PDA

Просмотр полной версии : [Мануал] Команды, работающие независимо от наличия cmd процессора



Nexius_Tailer
03.04.2016, 13:29
Приветствую. Сегодня представлю систему, которая будет полезна при написании каких-либо скриптов для сервера.

Суть её вот в чём: многие скрипты, например include, которые имеют в себе команды, требуют либо подключения cmd процессора, либо команды в них работают только на стандартном.
Лично моя политика написания скриптов (не модов) всегда была такой, чтобы конечный скрипт имел как можно меньше зависимостей (а по возможности не имел вообще), ибо в противном случае пользователю нужно следить не только за обновлениями моего скрипта, но и за обновлениями всех тех, подключение которых необходимо, что не очень удобно.
Либо же, если пользователь никогда бы не использовал тот скрипт, который требуется в моём, но ему приходится (хороший пример - античит на FlyHack с использованием MapAndreas).

В общем, с помощью этой системы можно делать команды внутри своих модулей (фс, инклудов), которые не будут требовать zcmd, но при его подключении всё также будет работать как и прежде.

Работает это со всеми cmd процессорами, кроме rCmd, ибо синтаксис команд там абсолютно другой.

Для этого придётся создать как бы свой cmd процессор в своём скрипте, но работать он будет немного иначе.

Если вам нужна такая фича в вашем fs/include, код ниже к вашим услугам:


#if !defined CMD
#define CMD:%0(%1) forward cmd_%0(%1); public cmd_%0(%1)
#endif

//YCMD и izcmd не блокируют вызов OnPlayerCommandText, поэтому их игнорируем, чтобы команды не дублировались
#if !defined YCMD && !defined _I_ZCMD_INCLUDED
public OnPlayerCommandText(playerid, cmdtext[])
{
new cmd[32], params[126], pos;
while(cmdtext[pos] > ' ')
{
cmd[pos] = cmdtext[pos];
pos++;
}
while(cmdtext[pos] == ' ')
{
pos++;
if(!cmdtext[pos])
{
params = "\1";
break;
}
}
strcat(params, cmdtext[pos]);
//Здесь уже команды, шапку делаем по методу стандартных
//Только вместо каких-либо действий вызываем паблик команды, в котором уже будет весь её код
//if(!strcmp(cmd, "/help", true)) return my_cmd_help(playerid, params); <- Как пример, команда "/help"
return 0;
}
#endif


Теперь объяснения, что за чем и зачем.

Дефайн "CMD:" нужен для совместимости с YCMD и прочими командными процессорами, которые не поддерживают объявление команд по типу "cmd_", но в них есть своя совместимость с командами, объявленными через "CMD:".

Выделение памяти, объяснять подробнее не вижу нужды:

new cmd[32], params[126], pos;

Единственное, что cmd в себе позже будет хранить текст "/НашейКоманды", а params - её параметры.

Далее, вот это:

while(cmdtext[pos] > ' ')
{
cmd[pos] = cmdtext[pos];
pos++;
}

Как раз и записывает весь текст команды в массив "cmd" до первого пробела либо конца строки, если команда введена без параметров. Т.е. по сути результат этого равносилен strtok, который мы пишем первый раз в начале OnPlayerCommandText, если команды на strcmp.

Вот теперь уже в:

while(cmdtext[pos] == ' ')
{
pos++;
if(!cmdtext[pos])
{
params = "\1";
break;
}
}

Идёт отделение параметров. Ну т.е. пока только мы считаем, с какого символа они начинаются, если начинаются вообще (проверка "!cmdtext[pos]" говорит о том, что если следующий символ в цикле будет равен концу строки, а параметры ещё не обнаружены, то команда введена без параметров).

И вот тут:

strcat(params, cmdtext[pos]);

Записываем в отдельную переменную params весь текст параметров, начиная с первого символа после пробела(ов).

А далее, то что под комментариями - это уже объявление своих команд.

if(!strcmp(cmd, "/help", true)) return my_cmd_help(playerid, params); //Как пример, команда "/help"

Помимо вызова функции каждой команды, эту функцию нужно ещё и объявить (а в ней ещё и код этой самой команды написать).

Опять же, на примере команды "/help". Объявляем паблик и функцию для команды (где-нибудь в начале или в конце, как вам удобнее):

CMD:help(playerid, params[]) return my_cmd_help(playerid, params);
my_cmd_help(playerid, params[])
{
#pragma unused params //Это добавлять если только команда не имеет параметров и вам выдаёт варнинг
//Проверки на залогиненность и т.д.
//И любые действия, которые должны происходить при вводе "/help"
return 1;
}


И да, русские команды добавлять можно, но название паблика команды должно быть на английском. Также работать русские команды будут только если zcmd не подключен (а если станет подключен, то такие команды уже будут вызываться только по альтернативным латинским именам, которые объявлены после "CMD:")!

Также, по моему, это не пригодится вам, если вы пишите мод, ибо там реально проще будет подключить/не подключить нужный cmd-процессор и нет надобности в гибкости.

Идею написания "независимых" команд взял у скрипта aview от автора SoNik)), но у него была реализована единственная команда и без параметров.


UPD: Другой вариант (более компактный)
Есть такая старая, но классная штука, как dcmd (https://team.sa-mp.com/wiki/Fast_Commands#dcmd). Так вот, немного её доработав, также можно достичь совместимости.. Правда только для тех zcmd-подобных процессоров, где команды можно вызывать через cmd_"cmdname" (т.е. такие реализации, как Pawn.CMD/YCMD, к сожалению, работать с этим не будут из-за того, что у каждого из них свои префиксы).

Для начала код оригинального dcmd и синтаксис его команд:

#define dcmd(%1,%2,%3) if (!strcmp((%3)[1], #%1, true, (%2)) && ((((%3)[(%2) + 1] == '\0') && (dcmd_%1(playerid, ""))) || (((%3)[(%2) + 1] == ' ') && (dcmd_%1(playerid, (%3)[(%2) + 2]))))) return 1

public OnPlayerCommandText(playerid, cmdtext[])
{
dcmd(help, 4, cmdtext); //4 = длина имени команды, равносильно strlen("help")
return 0;
}

dcmd_help(playerid, params[])
{
#pragma unused params //Избежание варнинга, если params не используется
//Проверки на залогиненность и т.д.
//И любые действия, которые должны происходить при вводе "/help"
return 1;
}


Собственно, немного изменив логику работы и пару названий мы можем сделать это средством создания почти универсальных команд:

#define dcmd2(%1,%2,%3) if(!strcmp((%3)[1], #%1, true, (%2)) && ((((%3)[(%2) + 1] == '\0') && (cmd_%1(playerid, ""))) || (((%3)[(%2) + 1] == ' ') && (cmd_%1(playerid, (%3)[(%2) + 2]))))) return 1
Теперь так будет выглядеть наш движок;
"dcmd2" для избежания путаницы, если вдруг уже имеем обычный dcmd.

Создание команд придётся сделать таким образом:

public OnPlayerCommandText(playerid, cmdtext[])
{
dcmd2(help, 4, cmdtext); //Здесь всё как у обычного dcmd, 4 = strlen("help")
return 0;
}

forward cmd_help(playerid, params[]); //А здесь уже синтаксис zcmd команды
public cmd_help(playerid, params[]) //Объявляем пабликом для того, чтобы команду видел zcmd
{
//Проверка на залогиненность и т.д.
//И любые действия, которые должны происходить при вводе "/help"
return 1;
}


Принцип работы этого варианта: когда ничего не подключено, dcmd2 вызывает нашу команду (а точнее сверяет через strcmp введённый текст с названием нужной команды, и, если таковая введена, вызывает cmd_"+введённый нами текст до пробела") из OnPlayerCommandText уже не как функцию, а как паблик.
Как только же мы подключаем zcmd, вызов паблика OnPlayerCommandText прекращается, т.е. dcmd2 перестаёт вызывать команды. Но, т.к. они объявлены синтаксисом zcmd команд и являются пабликами - zcmd их видит и уже вызывает сам.

Таким образом, это является некой золотой серединой, если вы просто хотите иметь команды, которые по скорости без zmcd работали бы как обычные, но при его подключении начинали бы выполняться им.

Надеюсь, это было полезно.

$continue$
03.04.2016, 13:35
а оно кому то нужно?
Поиграть с директивами препроцессора вообще западло?



ZCMD:


#if defined _zcmd_included

DC_CMD:


#if defined _Pawn && defined DC_CMD

Nexius_Tailer
03.04.2016, 13:37
а оно кому то нужно?
Лично мне да, в начале темы объяснил почему.


Поиграть с директивами препроцессора вообще западло?
Ну-ка, код пожалуйста.
Возможно твой вариант будет гораздо проще, тогда включу его в тему.


ZCMD:


#if defined _zcmd_included

DC_CMD:


#if defined _Pawn && defined DC_CMD

А если я использую izcmd, life-cmd, ещё что-то иное?

$continue$
03.04.2016, 13:47
Хотя я не продумал тот момент, что DC_CMD/ZCMD могут быть объявлены в любом месте мода. Тогда нужно продумывать другую реализацию.

Nexius_Tailer
03.04.2016, 13:47
Хотя я не продумал тот момент, что DC_CMD/ZCMD могут быть объявлены в любом месте мода. Тогда нужно продумывать другую реализацию.
И это тоже. Так вот и решение - первый пост :)

ziggi
03.04.2016, 14:03
Зачем заново изобретать dcmd (http://wiki.sa-mp.com/wiki/Fast_Commands#dcmd)?

DeimoS
03.04.2016, 14:16
Зачем заново изобретать dcmd (http://wiki.sa-mp.com/wiki/Fast_Commands#dcmd)?

Потому что крутые программисты не тратят время на такую чушь, как ознакомление с чужими наработками? Кто же, если не они, будут изобретать велосипеды? Стране нужны велосипеды

Nexius_Tailer
03.04.2016, 14:20
Зачем заново изобретать dcmd (http://wiki.sa-mp.com/wiki/Fast_Commands#dcmd)?
Хороший вопрос. А работают ли команды, созданные через dcmd, с zcmd к примеру?

TheMallard
03.04.2016, 16:25
Хороший вопрос. А работают ли команды, созданные через dcmd, с zcmd к примеру?

Конечно же работает, DCMD по сути просто дефайн и логика у него как у обычных команд, ведь работает все на strcmp.
Чисто ради удобства и отсутствия необходимости использовать strtok/strrest.

$continue$
03.04.2016, 16:27
https://u.teknik.io/56ekx.mp3

Потому что крутые программисты не тратят время на такую чушь, как ознакомление с чужими наработками? Кто же, если не они, будут изобретать велосипеды? Стране нужны велосипеды

P.S: Добавьте тэг !

Nexius_Tailer
03.04.2016, 19:41
Конечно же работает, DCMD по сути просто дефайн и логика у него как у обычных команд, ведь работает все на strcmp.
Чисто ради удобства и отсутствия необходимости использовать strtok/strrest.
А вот и не работает. Проверил. Так что сначала нужно проверить, а потом уже писать.
Да и почти уверен был и без теста, ибо вызывает dcmd функции команд с префиксом "dcmd_", а не "cmd_", как zcmd-подобные процессоры.
Так что, велосипедами вроде не пахнет, разобрались.


Добавьте тэг !
На этом форуме есть раздел предложений? Вот туда и пишите

DeimoS
03.04.2016, 19:47
А вот и не работает. Проверил. Так что сначала нужно проверить, а потом уже писать.
Да и почти уверен был и без теста, ибо вызывает dcmd функции команд с префиксом "dcmd_", а не "cmd_", как zcmd-подобные процессоры.
Так что, велосипедами вроде не пахнет, разобрались.


На этом форуме есть раздел предложений? Вот туда и пишите

А с Y_CMD свой способ проверял?

Nexius_Tailer
03.04.2016, 20:21
А с Y_CMD свой способ проверял?
А вот с y_commands немножко конфликтует. По моему из-за доп. параметра "help".
Если будет необходимость, попробую в ближайшее время поправить.
Хотя забавно, команды с ycmd вызываются также хорошо, проблема лишь в параметрах (что бы не ввёл - думает, что параметров нет).

Daniel_Cortez
03.04.2016, 20:37
Помню, проворачивал что-то подобное в dc_anims. Тогда я ещё не раз пожалел о той затее, т.к. пришлось попутно сделать удаление лишних пробелов перед и после параметров и весь этот код стало трудно поддерживать.
ИМХО, проще требовать в зависимостях sscanf2 и DC_CMD/ZCMD/y_commands (можно сделать комбинацию из #tryinclude с поиском одного из командных процессоров), чем изобретать ещё один велосипед.
Насколько я понял, перечисленными выше инклудами/плагинами не пользуются разве что новички, которые вообще едва знают Pawn и ковыряются в каком-нибудь старом RLS или GodFather.
Таким "скриптерам" всё равно не нужны мои работы (да и вообще чьи-либо ещё, кроме тех, которые уже используются в моде), лишь бы просто мод скомпилировался, т.е. требуя в зависимостях DC_CMD/ZCMD и sscanf2, я почти ничего не теряю.
Впрочем, вполне возможно, что я плохо знаю свою аудиторию... В конце концов, мне никто не отчитывается об использовании моих работ.

Nexius_Tailer
03.04.2016, 20:44
Помню, проворачивал что-то подобное в dc_anims. Тогда я ещё не раз пожалел о той затее.
ИМХО, проще требовать в зависимостях sscanf2 и DC_CMD/ZCMD/y_commands (можно сделать комбинацию из #tryinclude с поиском одного из командных процессоров), чем изобретать ещё один велосипед.
Насколько я понял, перечисленными выше инклудами/плагинами не пользуются разве что новички, которые вообще едва знают Pawn и ковыряются в каком-нибудь старом RLS или GodFather.
Таким "скриптерам" всё равно не нужны мои работы (да и вообще чьи-либо ещё, кроме тех, которые уже используются в моде), лишь бы просто мод скомпилировался, т.е. требуя в зависимостях DC_CMD/ZCMD и sscanf2, я почти ничего не теряю.
Впрочем, вполне возможно, что я плохо знаю свою аудиторию... В конце концов, мне никто не отчитывается об использовании моих работ.
Тут ещё дело в том, какая аудитория пользуется. Мне, например, нравится многое выкладывать в первую очередь на официальный форум, ибо и английский свой улучшаю, все дела, и аудитории в целом больше чем где-либо, и много действительно среди них толковых людей. Так вот, например там сейчас в тренде izcmd. У нас о нём, очевидно, мало кто знает, а тем более и пользуется. Поэтому, это ещё одна причина делать такое в своих скриптах. Но опять же я говорю про себя. Возможно просто, пока что не наткнулись те люди, которым это нужно на эту тему.

Daniel_Cortez
03.04.2016, 21:17
Тут ещё дело в том, какая аудитория пользуется. Мне, например, нравится многое выкладывать в первую очередь на официальный форум, ибо и английский свой улучшаю, все дела, и аудитории в целом больше чем где-либо, и много действительно среди них толковых людей. Так вот, например там сейчас в тренде izcmd. У нас о нём, очевидно, мало кто знает, а тем более и пользуется. Поэтому, это ещё одна причина делать такое в своих скриптах. Но опять же я говорю про себя. Возможно просто, пока что не наткнулись те люди, которым это нужно на эту тему.
В таком случае вполне хватит вот этого:


#if !defined CMD
#tryinclude "../include/dc_cmd.inc"
#endif
#if !defined CMD
#tryinclude <dc_cmd>
#endif
#if !defined CMD
#tryinclude "../include/zcmd.inc"
#endif
#if !defined CMD
#tryinclude <zcmd>
#endif
#if !defined CMD
#error This include requires DC_CMD/ZCMD to work.
#endif

Это и есть та комбинация из #tryinclude, про которую я писал в посте выше. Просто добавьте туда свой izcmd и готово.

Nexius_Tailer
03.04.2016, 21:41
Это и есть та комбинация из #tryinclude, про которую я писал в посте выше. Просто добавьте туда свой izcmd и готово.
Ну а через пол года появится ещё какой-нибудь "super-cmd", который также нужно будет учитывать. Такая реализация гораздо проще, но она учитывает лишь самые "популярные" реализации. Я добавлю такой вариант в первый пост, хотя по моему всё-же лучше иметь единый алгоритм, нежели кучу исключений.

Daniel_Cortez
03.04.2016, 22:13
Ну а через пол года появится ещё какой-нибудь "super-cmd", который также нужно будет учитывать.
Вряд ли. Командные процессоры уже подобрались если не вплотную, то очень близко к пределу производительности, после которого развиваться уже некуда.
Среди инклудов это ZCMD и iZCMD, среди плагинов - DC_CMD. По функционалу тоже есть лидер: y_commands.
ИМХО, улучшаться здесь уже некуда и вряд ли кто-то сможет сделать что-либо такое, чем все внезапно начнут пользоваться, так что по поводу поддержки новых командных процессоров можно не беспокоиться.

Nexius_Tailer
03.04.2016, 22:23
Вряд ли. Командные процессоры уже подобрались если не вплотную, то очень близко к пределу производительности, после которого развиваться уже некуда.
Среди инклудов это ZCMD и iZCMD, среди плагинов - DC_CMD. По функционалу тоже есть лидер: y_commands.
ИМХО, улучшаться здесь уже некуда и вряд ли кто-то сможет сделать что-либо такое, чем все внезапно начнут пользоваться, так что по поводу поддержки новых командных процессоров можно не беспокоиться.
Кстати, только что проверил, обрезает ли zcmd пробелы после аргументов - нет, не обрезает. Так что мороки не так много на самом деле, единственный реальный пока что минус моего варианта - нет поддержки (и не будет) rCmd, ибо им я уж более чем уверен, не каждый второй пользуется. И ещё небольшие спрыги с ycmd, что, наверное, поправлю.

И кстати, если говорить о плагинах, есть ещё и остальные, типо life-cmd или mcmd (by Mellnik). Но тут уже так дотошно до некультурности выяснять, чем пользуются больше, думаю, не следует. Просто опять к тому, что выбор их велик, и всё это добавлять в проверки я вижу крайне неудобным.

Nexius_Tailer
09.05.2016, 12:40
Обновлено: добавлена совместимость с YCMD!

Nexius_Tailer
31.10.2016, 18:24
UPD: Третий вариант

Nexius_Tailer
26.12.2016, 00:17
Немного обновлен первый вариант под совместимость с последним izcmd

Nexius_Tailer
12.02.2019, 17:07
Обновлено:
Подправлены ошибки, добавлены некоторые комментарии в коде и убран второй вариант из-за невозможности его использования без cmd-процессора, что, собстно, не соответствует названию топика.

seriu
12.02.2019, 21:44
А не проще ли использовать Pawn.Cmd
Ведь там уже реализована такая функция.

https://forum.sa-mp.com/showthread.php?t=608474


3.1.4:
- Added support for OPCT

Nexius_Tailer
13.02.2019, 00:36
А не проще ли использовать Pawn.Cmd
Ведь там уже реализована такая функция.

https://forum.sa-mp.com/showthread.php?t=608474


3.1.4:
- Added support for OPCT
В том то и дело, что это рассчитано на тех, кто может использовать любой zcmd-подобный процессор или не использовать его вообще. Да и к слову, Pawn.CMD просто позволяет вызывать обычные команды наряду с CMD: командами (т.е. если OPCT объявлен, то любые "обычные" команды уже 100% будут выполняться из OPCT), в то время как это рассчитано ещё и на то, что команды остаются одни и те же и подхватываются именно cmd-процессором тогда, когда он есть.

Nexius_Tailer
10.04.2020, 23:01
Обновлено:
Подправил размер массива, выделяемый под параметры команды в первом варианте