PDA

Просмотр полной версии : [Урок] Определение типа скрипта (FS/GM) используя state



Nexius_Tailer
12.12.2016, 16:16
Всем привет :)

Предисловие:
Недавно нашёл тему обсуждений "как лучше структурировать античит", откуда выяснил, что лучшим способом для меня было бы реализовать в инклуде, которую можно было-бы подключать как к фильтрскрипту, так и к гейммоду. Но стояла небольшая проблема: как написал Y_less, не всегда можно наверняка знать, задефайнит ли пользователь в своём фильтрскрипте "FILTERSCRIPT" (из чего и можно было узнать, что это FS), или нет.
Тем не менее, мне нужен был способ, который не будет задействовать ни два разных файла, ни предоставленный выше не очень надёжный вариант.
Упомянутый выше Y_less предложил такой метод:


new bool:AC_FILTERSCRIPT = false;

public OnFilterScriptInit()
{
AC_FILTERSCRIPT = true;
AC_OnScriptInit();
}

public OnGameModeInit()
{
if (!AC_FILTERSCRIPT) AC_OnScriptInit();
}

Но проанализировав его я понял, что вызывать каллбэки, типо OnPlayerUpdate придётся и в FS и в моде, тем самым каждый раз в каждом каллбэке будет идти проверка условия "фильтрскрипт ли это или нет" (с данным же способом эти проверки также будут, но неявными, т.е. самому их делать каждый раз не придётся).

В итоге на основе его идеи и кода, я сделал свой вариант с использованием state, что проверено мной на работоспособность в данный момент.

Итак, идея кода выглядит следующим образом:


public OnFilterScriptInit()
{
state ScriptType:FilterScript;
return 1;
}

//Все последующие паблики

public OnGameModeInit() <ScriptType:FilterScript> //Если фильтрскрипт
{
print("Это FilterScript!");
return 1;
}

public OnGameModeInit() <> //В остальных случаях (гейммод)
{
print("Это GameMode!");
return 1;
}

Ок, написав такой скрипт, его можно загрузить как FS, либо как GM, при этом в зависимости от вашего выбора, при включении сервера нам напишет одно из двух сообщений (и соответственно выполнится только одно из двух OnGameModeInit). Ну а т.к. это всего лишь пример, то на практике работать это будет со всеми пабликами.

Как же нам всё это адаптировать к инклуде, чтобы при включении мода/FS, нам соответственно это определяло?
Просто! :D Для этого мы будем использовать основу кода выше + Hook Method 7, а именно:


public OnFilterScriptInit()
{
state ScriptType:FilterScript;
//Hook Method 7
#if defined MyLib_OnFilterScriptInit
return MyLib_OnFilterScriptInit();
#else
return 1;
#endif
}

//Hook Method 7
#if defined _ALS_OnFilterScriptInit
#undef OnFilterScriptInit
#else
#define _ALS_OnFilterScriptInit
#endif
#define OnFilterScriptInit MyLib_OnFilterScriptInit
#if defined MyLib_OnFilterScriptInit
forward MyLib_OnFilterScriptInit();
#endif

public OnGameModeInit() <ScriptType:FilterScript>
{
print("[Inc] Это FilterScript!");
//Hook Method 7
#if defined MyLib_OnGameModeInit
return MyLib_OnGameModeInit();
#else
return 1;
#endif
}

public OnGameModeInit() <>
{
print("[Inc] Это GameMode!");
//Hook Method 7
#if defined MyLib_OnGameModeInit
return MyLib_OnGameModeInit();
#else
return 1;
#endif
}

//Hook Method 7
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit MyLib_OnGameModeInit
#if defined MyLib_OnGameModeInit
forward MyLib_OnGameModeInit();
#endif

При этом вызов конечного паблика будет таким-же, как будто он был вызван сразу.
Заметьте, важно учитывать то, что будучи вызванным при разных состояниях, OnGameModeInit это один паблик, поэтому код


#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit MyLib_OnGameModeInit
#if defined MyLib_OnGameModeInit
forward MyLib_OnGameModeInit();
#endif

Мы используем только один раз, и в конце.
Опять-же, как и с OnGameModeInit можно работать и с любыми другими каллбэками.

В дополнение: стоит указывать свой тег для любой подмены (хука, перехвата) во избежание каких-либо совпадений между ними. Тег "MyLib_" является примером.


Если вы хотите вызвать паблик только для одного случая - эти макросы будут полезны (спасибо Y_less'у):




#define fspublic%0(%1) public%0(%1) <> {} public%0(%1) <ScriptType:FilterScript>
#define gmpublic%0(%1) public%0(%1) <ScriptType:FilterScript> {} public%0(%1) <>


Then you just have:



fspublic OnPlayerUpdate()
{
}


Итог:
Данный метод будет в любом случае полезен, т.к. определяется тип скрипта прямо во время его выполнения.

P.S. Хорошо, но чтобы увидеть все на практике, вы можете скачать готовый скрипт с инклудом (ссылка на .pwn (https://pastebin.com/Qsq1ZJDx) + ссылка на .inc (https://pastebin.com/cV59WSCE)).
Можно скомпилировать, а затем загрузить основной скрипт и как GM, и как FS - в любом случае, когда запустится сервер, в консоли будет написан ваш выбор, а когда вы подключаетесь к серверу - в чате :)

Если есть предложения, буду рад их услышать.
Спасибо.

Пельмень
23.12.2016, 15:59
И всё же для создания античита лучше использовать разработку того самого (уже 10 раз упомянутого) Y_Less под названием y_master

Daniel_Cortez
23.12.2016, 17:31
И всё же для создания античита лучше использовать разработку того самого (уже 10 раз упомянутого) Y_Less под названием y_master
Очень развёрнутый и аргументированный ответ (нет).

Daniel_Cortez
26.12.2016, 12:17
Ок, пока что я переместил тему в раздел "Добавить урок", т.к. ещё нужно уточнить несколько фактов - например, вот этот:


Но проанализировав его я понял, что вызывать каллбэки, типо OnPlayerUpdate придётся и в FS и в моде, тем самым каждый раз в каждом каллбэке будет идти проверка условия "фильтрскрипт ли это или нет".

Эта проверка и в варианте со state тоже будет. Отличие лишь в том, что для проверки состояния автоматона перед телом функции (т.е. непосредственно перед инструкцией PROC, которая обычно означает начало функции) создаётся конструкция switch, которая перебирает все состояния автоматона, встречающиеся в скрипте.

Nexius_Tailer
26.12.2016, 12:21
По крайней мере это условие не нужно делать самому скриптеру, что как минимум удобнее (особенно если ещё и использовать макросы в конце).

Daniel_Cortez
26.12.2016, 12:51
По крайней мере это условие не нужно делать самому скриптеру, что как минимум удобнее (особенно если ещё и использовать макросы в конце).
В любом случае было бы не лишним упомянуть об этом в статье. А заодно и поменять MyLib на какой-нибудь свой префикс, чтобы код перехватов заранее был готов к использованию.

Nexius_Tailer
26.12.2016, 13:52
В любом случае было бы не лишним упомянуть об этом в статье. А заодно и поменять MyLib на какой-нибудь свой префикс, чтобы код перехватов заранее был готов к использованию.
Первое уточнил. Второе, думаю, пусть будет как есть, т.к. это пример.