PDA

Просмотр полной версии : [F.A.Q] .NET Plugin



Seregamil
18.12.2015, 20:01
Просьба не закидывать помидорками, я старался.

Подавая заявку в проф.лигу LightCode Cup возникла мысль, что, возможно, будет написание плагинов. Написать годный плагин на крестах за короткий промежуток времени я не способен, поэтому решил восстановить свою старую работу по какой-никакой попытке написания плагинов в среде .NET

И так. Данная разработка позволит вам писать плагины на C# и полноценно использовать их.

Функции на стороне сервера:


Вызывает метод плагина.
Параметры:

methodName - имя вызываемого метода
split[] - ключ для параметров
i - int32
f - float
s - string
c - char

{Float,_}:... - аргументы соответственно
Возвращаемые значения:


int32,
boolean,
float



Из-за неспособности AMX машины вернуть текстовый параметр пришлось создавать аналогичную предыдущей функцию.
Разница только в параметрах. Возвращается всегда 1.

Параметры:

methodName - имя вызываемого метода
split[] - ключ для параметров
i - int32
f - float
s - string
c - char

str[] - массив для записи текста
length - размер массива
{Float,_}:... - аргументы соответственно


Функции на стороне плагина:


Вызывает каллбэк по его названию и передает параметры.
Параметры:


string callback - название каллбэка
params object[] args - аргументы




Выводит текст в консоль
Параметры:


string text - текст =)






#include <a_samp>

native callDotnetMethod(methodName[], split[], {Float,_}:...);
native callDotnetMethodStr(methodName[], str[], len, split[], {Float,_}:...);

main(){

}

public OnGameModeInit() {
callDotnetMethod("onDotnetLoaded", "cf", 'v', 1.0);

new temp_int = callDotnetMethod("kernel.testINT", "ii", 10, 24);
printf("testINT returned: %i", temp_int);

new temp_bool = callDotnetMethod("kernel.testBOOL", "isi", 1, "boolean=)", 0);
printf("testBOOL returned: %i", temp_bool);

new temp_float = callDotnetMethod("kernel.testFLOAT", "ifcf", 10, 1.432, 'c', 242);
printf("testFLOAT returned: %f", temp_float);

new temp_str[ 16 ] ;
callDotnetMethodStr("kernel.testSTRING", temp_str, sizeof temp_str, "sss", "by", "Seregamil", "dotNET v1.0");
printf("testSTRING returned: %s", temp_str);
return true ;
}

forward onDotnetWasLoaded(str[]);
public onDotnetWasLoaded(str[]){
print(str);
}



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace c_sharp
{
public class kernel
{
public static object onDotnetLoaded(params object[] args)
{
cpp.logwrite("dotnet-> onDotnetLoaded was called. ARGS:");
foreach (object arg in args)
{
cpp.logwrite(arg.ToString());
}

cpp.callRemoteCallback("onDotnetWasLoaded", "Hello! =)");
return true;
}

public static object testINT(params object[] args)
{
cpp.logwrite("dotnet-> testINT was called");
int a = Convert.ToInt32(args[0]);
int b = Convert.ToInt32(args[1]);
return a + b ;
}

public static object testBOOL(params object[] args)
{
cpp.logwrite("dotnet-> testBOOL was called.");
return false;
}

public static object testSTRING(params object[] args)
{
cpp.logwrite("dotnet-> testSTRING was called." );

foreach (object arg in args)
{
cpp.logwrite(string.Format("{0}: {1}",arg.GetType(), arg.ToString()));
}

return "blackJack prod.";
}

public static object testFLOAT(params object[] args)
{
cpp.logwrite("dotnet-> testFLOAT was called. ARGS:");
float result = 13.228f;
return result;
}
}
}

http://i.imgur.com/hwxbI32.png


Решение содержит 2 проекта. Первый на C#, второй на C++. Это COM сборка. При компиляции образуется один *.dll файл в нужной директории. В моем случае это S:\gta-o\plugins\.
Я использую VS2012 Express. Путь для вывода меняется в свойствах проекта dotnet

http://i.imgur.com/B6Li3Zn.png
Второй этап - нужно указать местоположение файла c_sharp.tlb.
7 строка проекта dotnet
#import "C:\Users\Seregamil\Documents\GitHub\.NET-plugin\source\c_sharp\bin\Debug\c_sharp.tlb" raw_interfaces_only

ВНИМАНИЕ. КОМПИЛЯЦИЯ ПРОЕКТА ПРОИЗВОДИТСЯ В РЕЖИМЕ АДМИНИСТРАТОРА

Всё. Вы готовы к разработке плагина на C#.
Все примеры я делал в файле kernel.cs, если хотите - меняйте, делайте зависимость и т.д. у вас нет ограничений.
Файл macro.cs содержит значения MAX_PLAYERS, MAX_VEHICLES etc., использовалось для написания и синхронизации плагина с клиентской частью игрока.

* Я не стал прикручивать SAMPGDK, т.к. мне он не нужен. Основная задача - написание плагинов, а не гейммодов. На офф.форуме есть плагин, позволяющий писать мод на C#
* Я умышленно отключил mono. Хотите писать плагин под Linux - пожалуйста, прикручивается это легко, достаточно поставить библиотеки и перекомпилировать проект.

Ссылка на разработку. Примеры прилагаются https://github.com/Seregamil/.NET-plugin

Огромное спасибо пользователям SDraw, Disinterpreter, g3o0or, Fallen A. за помощь.

Daniel_Cortez
18.12.2015, 20:52
В текущем виде, чтобы юзать функции на C#, нужно копипастить таблицу из исходника на C# в .pwn. Крайне ненадёжно. Если захочешь добавить в плагин новую функцию (которую ещё не факт, что будешь использовать в скрипте на Pawn), придётся заново копипастить таблицу. ИЧСХ, рано или поздно забудешь это сделать и в лучшем случае лицезреешь какой-нибудь сообщение об ошибке или краш.
Хотя, можно было с самого начала добавить в плагин таблицу из названий и адресов функций (как в плагинах на C/C++) и сделать из Pawn вызов функции на C# по имени, а не по ID.

Seregamil
18.12.2015, 21:40
В текущем виде, чтобы юзать функции на C#, нужно копипастить таблицу из исходника на C# в .pwn. Крайне ненадёжно. Если захочешь добавить в плагин новую функцию (которую ещё не факт, что будешь использовать в скрипте на Pawn), придётся заново копипастить таблицу. ИЧСХ, рано или поздно забудешь это сделать и в лучшем случае лицезреешь какой-нибудь сообщение об ошибке или краш.
Хотя, можно было с самого начала добавить в плагин таблицу из названий и адресов функций (как в плагинах на C/C++) и сделать из Pawn вызов функции на C# по имени, а не по ID.

Во всем этом деле копипастится одно -
enum c_sharp {
onDotnetLoaded,
testINT,
testBOOL,
testSTRING,
testFLOAT
};

Изначально было так, что вызывалось по названию, но всё-равно придется делать таблицу, ибо вызвать метод по названию невозможно.

$continue$
18.12.2015, 22:33
Каким образом если не секрет (.cpp) файл используется в С#?

Seregamil
19.12.2015, 08:06
Каким образом если не секрет (.cpp) файл используется в С#?

*.cpp файлы в C# не используются. Я просто связал 2 проекта в один.

Seregamil
19.12.2015, 08:58
Обновил библиотеку. Нашел способ вызывать метод по его названию не прибегая к использованию таблицы.

native callDotnetMethod(methodName[], split[], {Float,_}:...);
native callDotnetMethodStr(methodName[], str[], len, split[], {Float,_}:...);

Обновил тему. Сейчас займусь обновлением примеров.

$continue$
20.12.2015, 16:48
*.cpp файлы в C# не используются. Я просто связал 2 проекта в один.

Окей. Задам вопрос по другому: У тебя функции из С++ используются в C#?
Если да, то как? Динамическая библиотека?

Seregamil
20.12.2015, 17:43
Окей. Задам вопрос по другому: У тебя функции из С++ используются в C#?
Если да, то как? Динамическая библиотека?

https://ru.wikipedia.org/wiki/Component_Object_Model

Seregamil
21.12.2015, 17:14
Теперь можно вызывать метод из любого класса. Т.е. если раньше было ограничение на нахождение метода в классе, то сейчас - вообще всё-равно.
Пример:

callDotnetMethod( "player.GetName" );
callDotnetMethod( "vehicle.SetPos" );
На мой взгляд это удобнее и позволит разделять проект на множество различных классов, а не держать всё в куче.

А, да, добавлен .gitignore, дабы не засирать проект.

Seregamil
23.12.2015, 21:49
Добавил пример для работы с регулярными выражениями.

$continue$
15.01.2016, 00:30
Не хочешь рассказать о компиляции плагина в mono?

Seregamil
15.01.2016, 10:46
Хорошая мысль. Расскажу скоро.

Seregamil
11.02.2016, 20:07
Переделал класс для регулярок.

Функции на данный момент:

regex.IsMatch - указывает, обнаружено ли в указанной входной строке соответствие регулярке
regex.Match - Ищет первое вхождение и возвращает его позицию в строке
regex.Replace - Ищет все вхождения в строке и заменяет

Для удобства названия функций нагло слизаны с плагина пользователя Fro1sha ( http://forum.sa-mp.com/showthread.php?t=247893 )

Макросы:


#define regex_match(%0,%1) callDotnetMethod("regex.Match", "ss", %0, %1)
#define regex_is_match(%0,%1) callDotnetMethod("regex.IsMatch", "ss", %0, %1)
#define regex_replace(%0,%1,%2) callDotnetMethodStr("regex.Replace", %0, sizeof(%0), "sss", %0, %1, %2)



Класс regex: https://github.com/Seregamil/.NET-plugin/blob/master/source/c_sharp/regex.cs
Тестовый скрипт: https://github.com/Seregamil/.NET-plugin/blob/master/examples/regex/regex.pwn

Bib
23.08.2017, 15:02
Как это чудо скомпилировать на linux?

$continue$
23.08.2017, 15:08
Mono (https://ru.wikipedia.org/wiki/Mono)

Как это чудо скомпилировать на linux?

Seregamil
23.08.2017, 18:50
Mono (https://ru.wikipedia.org/wiki/Mono)

Хорошее предложение, работать оно, конечно, не будет.

Bib
24.08.2017, 11:04
Хорошее предложение, работать оно, конечно, не будет.

Кажется догадываюсь почему. Из-за COM сборки?

Seregamil
24.08.2017, 12:52
Кажется догадываюсь почему. Из-за COM сборки?

В точку. Можно переделать, но мне немного лень. В C++ можно подкрутить загрузку библиотеки mono и работать с этим.

Fallen A.
24.08.2017, 18:17
В точку. Можно переделать, но мне немного лень. В C++ можно подкрутить загрузку библиотеки mono и работать с этим.

Изи, но я, конечно же, делать этого не буду.

Seregamil
24.08.2017, 18:55
Изи, но я, конечно же, делать этого не буду.

Потому что, кхм, не умеешь, но это не важно. =)

Fallen A.
24.08.2017, 22:54
Потому что, кхм, не умеешь, но это не важно. =)
Ну прям уж таки и не умею :)