PDA

Просмотр полной версии : [Урок] О командных процессорах



Daniel_Cortez
29.01.2017, 19:21
Всем привет.

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


Одной из основных проблем в обработке команд является линейная зависимость времени отклика от названия команды или её расположения в коде.
Что представляет из себя эта зависимость:
Без командного процессора производятся лишние вызовы strcmp, чтобы найти нужную команду.
Пример:


public OnPlayerCommandText(playerid, cmdtext[])
{
if (0 == strcmp(cmdtext, "/command1"))
{
return SendClientMessage(playerid, -1, "Вызвана 1-я команда.");
}
if (0 == strcmp(cmdtext, "/command2"))
{
return SendClientMessage(playerid, -1, "Вызвана 2-я команда.");
}
if (0 == strcmp(cmdtext, "/command3"))
{
return SendClientMessage(playerid, -1, "Вызвана 3-я команда.");
}
return SendClientMessage(playerid, -1, "Ошибка: Неизвестная команда.");
}

Самое малое время отклика будет у команды "/command1", т.к. она реализована в OnPlayerCommandText самой первой и при её вызове будет совершён всего 1 вызов функции strcmp.
В то же время, команда "/command3" находится в самом невыгодном положении, т.к. при её вызове строка в cmdtext будет сравниваться сначала с "/command1" и "/command2", и только потом с "/command3", т.е. произойдут 2 лишних вызова strcmp.
В итоге чем больше команд находится перед вызванной командой, тем больше будет "холостых" срабатываний strcmp и тем больше времени уйдёт на поиск. Это и есть линейная зависимость времени отклика команды от её расположения в OPCT.

Во многих командных движках наподобие ZCMD тоже есть линейная зависимость, но только от названия команды.
Команды в них реализуются в виде public-функций. В скомпилированном скрипте (*.amx) названия таких функций сохраняются в таблице экспортируемых функций, чтобы их можно было найти и вызвать извне (т.е. со стороны сервера, а не только из кода на Pawn).
Пример:


CMD:abc(playerid, params[])
{
return SendClientMessage(playerid, -1, "abc");
}
CMD:def(playerid, params[])
{
return SendClientMessage(playerid, -1, "def");
}
CMD:ghi(playerid, params[])
{
return SendClientMessage(playerid, -1, "ghi");
}
CMD:jkl(playerid, params[])
{
return SendClientMessage(playerid, -1, "jkl");
}

При раскрытии макроса "CMD:" к названию public-функции добавляется префикс "cmd_". Например, для "CMD:abc" функция будет называться "cmd_abc".
Без префикса командный процессор мог бы вызвать любую public-функцию. Например, любой игрок мог бы ввести "/OnGameModeInit", инициировать "перезагрузку" сервера - загрузку домов, бизнесов и всего прочего, без сохранения того, что было у игроков до перезагрузки.
Записи в таблице экспортируемых функций будут располагаться в алфавитном порядке:


abc
def
ghi
jkl

Чтобы вызвать функцию на Pawn по её имени, нужно сначала найти её ID, т.е. её порядковый номер в таблице функций.
Эта задача осуществляется с помощью amx_FindNative, если для обработки команд используется плагин, или funcidx на стороне Pawn (она сама вызывает amx_FindNative).
Функция amx_FindNative производит линейный поиск по таблице экспортируемых функций, поочерёдно сравнивая указанное имя с каждым именем в таблице, пока не найдёт совпадение.
При таком поиске название abc попадётся самым первым, а для jkl будут совершены 3 лишних сравнения с предыдущими именами в таблице.
Как результат, время отклика при такой схеме зависит от названия самой команды.

В теории наличие линейной зависимости во времени отклика открывает для злоумышленников вектор атаки: флудить вызовом команды, которая либо самая последняя по алфавиту (например, что-нибудь на букву Z), либо реализована самой последней в OnPlayerCommandText (это можно узнать из слитых исходников или исходников паблик-мода, на котором основан мод сервера).
Если на сервере достаточно много команд и атакующий удачно подобрал команду для флуда, он сможет спровоцировать максимальное использование ресурсов и, возможно, отключение сервера хостером, чтобы не влиять на работу других серверов, находящихся на той же машине.

В DC_CMD для обработки команд используется хеш-таблица (https://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88-%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0).
Во время запуска сервера плагин просматривает таблицу public-функций и для тех функций, названия которых начинаются с "cmd_", вычисляет из названия хеш-сумму, находит ID функции и полученные пары "хеш-ID" сохраняет в хеш-таблице.
При вводе команды от названия вычисляется хеш и по нему в таблице находится ID соответствующей public-функции. При такой схеме отсутствует линейная зависимость от названия функции, т.к. время поиска по хеш-таблице примерно равно для каждого элемента.

По такому же принципу работает плагин Pawn.CMD и движок y_commands.
Значительным недостатком y_commands является то, что он использует несколько других инклудов из YSI, состоящих из грязных хаков на основе багов Pawn 3.0, не говоря уже о реализации хеш-таблицы в YSI: вместо динамической аллокации памяти под её элементы используется очень большой массив, который всё время занимает место в памяти сервера. И хотя память не является особой проблемой, используемые в YSI практики только поощряют расточительство и использование багов вместо того, чтобы сделать адекватную реализацию через плагин.
Про минусы Pawn.CMD можно прочесть здесь (http://pro-pawn.ru/showthread.php?p=73718#post73718). Также есть информация, что под Linux этот плагин приводит к падению сервера с JIT, но эта информация пока что не подтверждена (буду очень благодарен, если кто-нибудь найдёт время, чтобы её подтвердить/опровергнуть).
Среди преимуществ Pawn.CMD и y_commands можно отметить поддержку отдельных реализаций команд для справки об аргументах команды и для разных групп игроков, но без этого синтаксического сахара можно легко обойтись, больше никаких проблем они не решают.


Как я уже сказал ранее, я не буду навязывать вам выбор. Это было бы неэтично с моей стороны, т.к. я, как разработчик DC_CMD, могу быть заинтересованным лицом (хоть я уже и несколькько лет практически не притрагивался к исходникам плагина).
Моей задачей в этой статье было всего лишь привести факты, т.к. я не видел, чтобы о них хоть где-то упоминали ранее.

Выбирайте с умом. Или без. Всё в ваших руках.


Статью подготовил: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)


Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Копирование данной статьи на других ресурсах без разрешения автора запрещено!

Nexius_Tailer
29.01.2017, 20:53
Интересно.
Хотя всё-же стоит учитывать, что перебор названий пабликов в любом случае будет быстрее, чем многократный вызов strcmp из pawn скрипта.
Юзать y_cmd единственной из YSI, при этом ещё и не используя ни одной из её фич, действительно было-бы малоразумно, так что такой вариант мало кому подходит.

И если говорить про обходимость без синтаксического сахара, то в этом плане вполне и сам zcmd/izcmd - вообще почти ничего лишнего)

VVWVV
29.01.2017, 21:02
Интересно.
Хотя всё-же стоит учитывать, что перебор названий пабликов в любом случае будет быстрее, чем многократный вызов strcmp из pawn скрипта.
Юзать y_cmd единственной из YSI, при этом ещё и не используя ни одной из её фич, действительно было-бы малоразумно, так что такой вариант мало кому подходит.

И если говорить про обходимость без синтаксического сахара, то в этом плане вполне и сам zcmd/izcmd - вообще почти ничего лишнего)

Вместо библиотеки Y_Less'а можно использовать библиотеку SmartCMD, которая мало чем отличается от y_commands. Более того, она должна быть быстрее y_commands.

Nexius_Tailer
29.01.2017, 21:08
Вместо библиотеки Y_Less'а можно использовать библиотеку SmartCMD, которая мало чем отличается от y_commands. Более того, она должна быть быстрее y_commands.
Ну да, в последнее время их много развелось (Smart-CMD, SickAttack's Command Processor (http://forum.sa-mp.com/showthread.php?t=618661) и ещё там какие-то). А кто-то когда-то говорил, что эра гонок cmd-процессоров закончилась :P
Надо будет как-нибудь каждый из них изучить получше

Daniel_Cortez
29.01.2017, 21:09
Интересно.
Хотя всё-же стоит учитывать, что перебор названий пабликов в любом случае будет быстрее, чем многократный вызов strcmp из pawn скрипта.

Да, я этого и не отрицал, но зависимость времени отклика от названия всё ещё остаётся.


И если говорить про обходимость без синтаксического сахара, то в этом плане вполне и сам zcmd/izcmd - вообще почти ничего лишнего)
... или писать все команды в OnPlayerCommandText - тогда даже никаких инклудов подключать не придётся!

Я не просто так упомянул в том предложении о решении других проблем. Если сами по себе движки y_commands, Pawn.CMD и DC_CMD устраняют линейную зависимость времени отклика от того или иного признака, избавляя от возможного вектора атаки, то синтаксический сахар... это просто сахар.


Вместо библиотеки Y_Less'а можно использовать библиотеку SmartCMD, которая мало чем отличается от y_commands. Более того, она должна быть быстрее y_commands.
В ней, как и в ZCMD (и вообще во всех остальных, поиск функций в которых производится не по хеш-таблице), время отклика зависит от названия команды. Да и при нормальных условиях на сервере разница в производительности вряд ли будет заметна.

Nexius_Tailer
29.01.2017, 21:30
... или писать все команды в OnPlayerCommandText - тогда даже никаких инклудов подключать не придётся!
Ну так там при большом количестве команд разница в скорости будет уже гораздо заметнее.


Я не просто так упомянул в том предложении о решении других проблем. Если сами по себе движки y_commands, Pawn.CMD и DC_CMD устраняют линейную зависимость времени отклика от того или иного признака, избавляя от возможного вектора атаки, то синтаксический сахар... это просто сахар.
От возможного вектора атаки обычно избавляет анти-флуд. А синтаксический сахар кому-то также предоставляет и более удобный скриптинг, если тот в нём нуждается. Это обычно отдельная категория людей, которые и скоростью ради этого жертвовать не против были, хотя с появлением pawn.cmd, получается, для них это вообще идеальный вариант.

- - - Добавлено - - -


Да и при нормальных условиях на сервере разница в производительности вряд ли будет заметна.
В принципе это и говорит о том, что проблем как таковых и нет. С нормальным кодом внутри команды будут нормально работать и на zcmd

ziggi
29.01.2017, 22:32
SickAttack's Command Processor (http://forum.sa-mp.com/showthread.php?t=618661)

Этот особо повеселил: https://github.com/Kevin-Reinke/Command_Processor/blob/master/command_processor.inc#L58-L516

Redsan
29.01.2017, 23:50
Этот особо повеселил: https://github.com/Kevin-Reinke/Command_Processor/blob/master/command_processor.inc#L58-L516

Лучший :lol::clapping:

Daniel_Cortez
30.01.2017, 07:53
Ну так там при большом количестве команд разница в скорости будет уже гораздо заметнее.
Странно, я думал, это будет очевидно из моего ответа. Видимо, нет -_-



От возможного вектора атаки обычно избавляет анти-флуд.
Атака может выполняться не одним игроком. И даже если она не вызовет полного отказа, повышение нагрузки может отразиться на геймплее.



А синтаксический сахар кому-то также предоставляет и более удобный скриптинг, если тот в нём нуждается. Это обычно отдельная категория людей, которые и скоростью ради этого жертвовать не против были, хотя с появлением pawn.cmd, получается, для них это вообще идеальный вариант.
Не спорю, хоть в сообществе такой сахар пока что не сильно популярен (по крайней мере, в русскоязычной части). В принципе, в плане производительности "гоняться" здесь уже давно незачем. Пожалуй, именно поэтому я так давно не работал над новыми версиями DC_CMD. При последних тенденциях есть лишь смысл работать над удобством.



В принципе это и говорит о том, что проблем как таковых и нет. С нормальным кодом внутри команды будут нормально работать и на zcmd
Под "нормальными условиями" имелось в виду отсутствие атак флудом.

Nexius_Tailer
30.01.2017, 09:47
Странно, я думал, это будет очевидно из моего ответа. Видимо, нет -_-
Действительно, видимо кому-то не очевидно. Тогда стоит посмотреть на любые тесты скорости cmd-процессоров, где участвует strcmp. И сразу станет видно, что если между zcmd-подобными разница, пусть даже в разы, то с strcmp с каждым из них в десятки раз, что наверное о чём-то, да должно говорить.


Атака может выполняться не одним игроком. И даже если она не вызовет полного отказа, повышение нагрузки может отразиться на геймплее.
В принципе оно так, но если с другой стороны, флудить можно чем угодно, где есть перебор: пикапами, чекпоинтами, диалогами и прочим. Если у злоумышленников цель будет зафлудить сервер, и они будут иметь при этом достаточные ресурсы - они это сделают.

Пельмень
30.01.2017, 21:06
Почему бы не мемкэш какой ни будь?

Daniel_Cortez
30.01.2017, 21:17
Почему бы не мемкэш какой ни будь?
Смотря, что ты имеешь в виду под "мемкэшем".

Пельмень
30.01.2017, 21:18
Смотря, что ты имеешь в виду под "мемкэшем".
Что-то подобное (http://libmemcached.org/)

Daniel_Cortez
31.01.2017, 16:03
Что-то подобное (http://libmemcached.org/)
Допустим, но чем это может быть лучше поиска по хеш-таблице, который уже используется в нескольких командных движках?