PDA

Просмотр полной версии : [Урок] Перехват функций, часть 2: практика - пишем античит на HP



Daniel_Cortez
13.10.2014, 20:53
Оглавление:
Часть 1: основы (http://pro-pawn.ru/showthread.php?10447-%D0%9F%D0%B5%D1%80%D0%B5%D1%85%D0%B2%D0%B0%D1%82-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B9-%D1%87%D0%B0%D1%81%D1%82%D1%8C-1-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D1%8B)
Часть 2: практика - пишем античит на HP



Sup.
В этой части мы рассмотрим создание простого античита на HP, подходящего к любому моду*.

* За исключением тех, на которых уже есть аналогичный античит,
в этом случае два разных античита могут конфликтовать друг с другом.


Прежде, чем начать разбор примера, давайте рассмотрим несколько советов, которые помогут избежать самых распространённых ошибок, связанных с перехватами функций, и улучшить качество вашего кода.
Внимание: данные советы ещё не рассматривались ни в одной из известных мне статей по перехватам (в.т.ч. в статье Y_Less'а на оффе). Поэтому, даже если Вы уже знакомы с техникой перехватов, эти советы достойны Вашего внимания.
Внутри инклуда задавайте переменным и функциям (в т.ч. перехватываемым) один и тот же префикс, связанный с названием инклуда.
Главное, чтобы этот префикс и названия переменных и функций в этом скрипте были уникальными, т.е. не повторялись в других инклудах и в основном скрипте.
Также название префикса должно быть как можно менее общим. Плохие примеры: "ac", "fix".
Античиты и багфиксы могут быть разные, поэтому старайтесь, отразить суть своего багфикса или античита. Например, "ac_spect__" (античит на невидимость, получаемую имитацией перехода в спект (http://pro-pawn.ru/showthread.php?3739)) (на самом деле, после "ac_hp" только 1 символ подчёркивания - это из-за ограничения названий до 31 символов в Pawn) или "ac_hp__" (античит на читерское HP).
Если префиксы будут повторяться в разных перехватах, то эти перехваты будут конфликтовать друг с другом, что, в свою очередь, помешает компиляции скрипта.
Пример: если есть инклуд "afk_system.inc", в котором реализована система AFK, можно использовать в нём префикс "afk_sys__" для перехватов, переменных, констант и функций (кроме тех функций, которые будут видны за пределами инклуда, например IsPlayerAFK).

Перехватчик не должен менять аргументы перехватываемой функции, её логику работы и предназначение.
Изменение возвращаемых значений допускается только в том случае, если это не нарушает совместимости с оригинальной функцией.
Например, в античите на HP перехватчик GetPlayerHealth возвращает не то значение, которое возвратит оригинальная функция, а кол-во HP игрока, хранящееся в античите. Но в то же время перехватчик должен возвращать именно HP, а не сумму HP и брони или ещё что-нибудь, что поменяет логику работы функции.
То же самое относится к изменению аргументов функции. Если с помощью перехвата добавить в какую-либо функцию дополнительные параметры, а потом убрать перехватчик, то компилятор будет выдавать ошибки и код не будет компилироваться без перехватчика, т.к. в оригинальной функции тех дополнительных параметров нет.
Иными словами, работа перехватчика должна быть незаметной для того кода, который использует перехватываемую функцию, как будто того перехватчика и нет.
Если же вам нужна функция, работающая по-другому - сделайте отдельную функцию, но не нужно путать её с оригиналом с помощью перехвата. Вмешательство в стандартную логику обычно приводит только к проблемам.

Если название перехватываемой функции (в случае с коллбэками) или перехватчика (для нативных функций) длиннее 31 символов, его следует сократить до этого лимита. Сокращение производится путём отсечения лишних символов справа (например, ac_veh_hp__OnVehicleDamageStatusUpdate -> ac_veh_hp__OnVehicleDamageStatu), строго до длины в 31 символ (не больше и не меньше!).
Пример:

public OnVehicleDamageStatusUpdate(vehicleid, playerid)
{
// ...
#if defined ac_veh_hp__OnVehicleDamageStatu // "урезаем" название до 31 символа
ac_veh_hp__OnVehicleDamageStatu(vehicleid, playerid);
#endif
return 1;
}
#if defined _ALS_OnVehicleDamageStatusUpdat // "урезаем" название до 31 символа
#undef OnVehicleDamageStatusUpdate
#else
#define _ALS_OnVehicleDamageStatusUpdat
#endif
#define OnVehicleDamageStatusUpdate ac_veh_hp__OnVehicleDamageStatu
#if defined ac_veh_hp__OnVehicleDamageStatu
forward ac_veh_hp__OnVehicleDamageStatu(vehicleid, playerid);
#endif

Всё, что должно использоваться только внутри инклуда (в данном уроке это переменные для записи HP - нельзя допустить, чтобы они изменялись из мода), должно иметь атрибут static.
Пример:

static some_var; // переменная не будет видна из мода

static stock DoSomething() // функция не будет видна из мода
{
// ...
}

Если перехватываемая функция не обязательно должна возвращать значение (пример: коллбэк OnPlayerConnect), вызывайте её из перехватчика следующим образом:

#if defined LibName__Func
LibName__Func();
#endif
return 1;

Благодаря этому удастся избежать варнингов, если перехватываемая функция ничего не возвращает.
Если в коде внутри перехватчика используются локальные переменные, ограничивайте этот код локальным блоком.

public Func(param1, param2)
{
// локальные переменные ограничены локальным блоком
{
new string[256];
// ...
}
#if defined LibName__Func
return Libname_Func(param1, param2);
#endif
}

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



Перейдём обратно к заданию - написанию античита на HP.
Сначала нужно определиться, как мы назовём инклуд с античитом и какой префикс будем использовать для переменных/функций.
Для примера подойдёт название "ac_health.inc" или "ac_hp.inc". Соответственно, будем использовать префикс "ac_hp__".

Дальше создадим массив, в который будем записывать здоровье игроков, устанавливаемое сервером:

static Float:ac_hp__health[MAX_PLAYERS];

Вместо new массив объявлен с помощью ключевого слова static - так, если перенести весь античит в отдельный инклуд, массив будет виден только внутри инклуда и не будет мешаться в моде.

Дальше можно было бы найти все вызовы SetPlayerHealth и под ними дублировать устанавливаемое кол-во HP в ac_health__health:

SetPlayerHealth(playerid, 100.0);
ac_hp__health[playerid] = 100.0;

НО для этого вам придётся:
Выискивать вручную все вызовы и точно так же вручную "прицеплять" к каждому из них дублирование HP в массив. И ни в коем случае не пропустить ни одного из них!
Всегда при использовании SetPlayerHealth держать в голове, что нужно добавить дублирование в ac_health.

Кроме того, стоит помнить, что чем больше сделано модификаций кода, тем больше вероятность того, что туда вкрадётся какая-нибудь ошибка. Человеческий фактор.
А может быть вы уже используете такой подход в своём RLS и даже не подозреваете, что в вашем античите на HP уже куча подобных ошибок, которые никогда не заметите ни вы, ни компилятор?
Причём одна такая ошибка - и в один прекрасный момент недоантичит начнёт банить ни в чём не повинных игроков, а репутация проекта будет испорчена. Если же попытаетесь отыскать источник ошибки, столкнётесь с новой проблемой: забаненный игрок может не помнить всех подробностей (да и не факт, что он вообще что-то захочет говорить скриптеру) - придётся перепроверять каждый вызов SetPlayerHealth и устраивать кучу тестов на локальном сервере.


Но ведь можно просто найти все вызовы SetPlayerHealth и под ними копировать устанавливаемое кол-во HP в ac_health!
И именно поэтому такой подход всегда был и будет считаться быдлокодерством - абсолютно никаких гарантий надёжности и куча проблем, причину которых очень трудно найти. От человеческого фактора нельзя полностью избавиться, лучшее, что можно сделать - минимизировать риск, избавившись от дублирования кода.

С техникой перехвата ситуация намного проще.
Поскольку перехватчик всего один, достаточно лишь адекватно протестировать хотя бы 1 случай его использования.
Затем во всех остальных случаях вызова SetPlayerHealth компилятор возьмёт всю рутинную работу по разбору перехватов на себя и перехватчик всегда будет вести себя так, как было запланировано.
В результате вероятность возникновения ошибки сводится к нулю. Такой код поддерживать намного проще.

Сделаем перехват функции SetPlayerHealth и в этом перехватчике осуществим дублирование выдаваемого кол-ва HP в массив:

stock ac_hp__SetPlayerHealth(playerid, &Float:health)
{
ac_hp__health[playerid] = health;
return SetPlayerHealth(playerid, health);
}
#if defined _ALS_SetPlayerHealth
#undef SetPlayerHealth
#else
#define _ALS_SetPlayerHealth
#endif
#define SetPlayerHealth ac_hp__SetPlayerHealth


Теперь нужно будет в функцию OnPlayerUpdate добавить проверку игрока на несанкционированное восстановление HP и последующую выдачу бана.
Всё это, как вы уже могли догадаться, будет сделано с помощью перехватов.

public OnPlayerUpdate(playerid)
{
// код перехвата вынесен в отдельный блок,
{ // чтобы после его выполнения переменные не занимали место в стеке
new Float:health;
GetPlayerHealth(playerid, health);
// если кол-во HP изменилось с момента предыдущего обновления
// сравниваемые значения трактуются, как целочисленные, чтобы избежать лишнего вызова floatcmp
// (внимание! такой оптимизационный приём можно применять только при сравнении
// с помощью знаков "==" и "!=", но ни в коем случае не с ">", "<", ">=" или "<=")
if(_:ac_hp__health[playerid] != _:health)
{
// если игрок потерял HP, упав с высоты или с помощью чита - запоминаем новое значение
// (пусть отнимает HP читами сколько угодно, всё равно восстановить его он уже не сможет)
if(ac_hp__health[playerid] > health)
{
ac_hp__health[playerid] = health;
}
// если HP больше, чем записано в античите - HAX detected!
else if(ac_hp__health[playerid] < health)
{
SetPlayerHealth(playerid, ac_hp__health[playerid]);
}
}
}
// на wiki.sa-mp.com написано, что возвращаемое коллбэком значение ни на что не влияет
// и может быть пропущено, поэтому не стоит "требовать" это значение у перехватываемой функции -
// вместо этого лучше использовать "return 1" отдельно от её вызова
#if defined ac_hp__OnPlayerUpdate
ac_hp__OnPlayerUpdate(playerid);
#endif
return 1;
}
#if defined _ALS_OnPlayerUpdate
#undef OnPlayerUpdate
#else
#define _ALS_OnPlayerUpdate
#endif
#define OnPlayerUpdate ac_hp__OnPlayerUpdate
#if defined ac_hp__OnPlayerUpdate
forward ac_hp__OnPlayerUpdate(playerid);
#endif


Теперь сделаем перехват GetPlayerHealth. По идее он не обязателен и никак не отразится на работе античита.
Но в случае с функцией GetPlayerHealth предоставляется возможность избавиться от вызова нативной функции, подменив её на функцию, написанную на Pawn - таким образом повысится производительность работы сервера при вызове функции.
При этом на возвращаемом функцией значении это никак не отразится, т.к. античит теперь имеет полный контроль над HP игрока.

stock ac_hp__GetPlayerHealth(playerid, &Float:health)
{
health = ac_hp__health[playerid];
return 1;
}
#if defined _ALS_GetPlayerHealth
#undef GetPlayerHealth
#else
#define _ALS_GetPlayerHealth
#endif
#define GetPlayerHealth ac_hp__GetPlayerHealth


Внимание! Перехват OnPlayerUpdate следует делать выше перехватов GetPlayerHealth/SetPlayerHealth, иначе они будут влиять на работу перехватчика в OnPlayerUpdate, если в нём используются те функции.

Но это ещё не всё. Если выдать игроку здоровье, оно выдастся ему не сразу из-за пинга.
Античит проверит здоровье со следующим вызовом OnPlayerUpdate ещё до того, как здоровье обновится у игрока, в результате сервер запишет то кол-во HP, которое было ещё до выдачи.
Как только у игрока обновится HP, в античите будет записано прежнее значение, и после ещё одной проверки в OnPlayerUpdate античит выдаст ложное срабатывание.
Нужно исправить эту ситуацию. Сделаем так, чтобы античит игнорировал уменьшение HP у игрока в течение 1 секунды после выдачи.

К переменным (под ac_hp__health):

static ac_hp__ignore_timestamp[MAX_PLAYERS];


Добавим перед переменными константу, в которой укажем время для игнорирования:

#if !defined AC_HP__IGNORE_TIME
#define AC_HP__IGNORE_TIME 1000
#endif

Почему вокруг объявления константы используется #if defined? Это мы разберём позже.
А пока что добавим игнорирование в перехвате OnPlayerUpdate.
Найдите строку:

if(ac_hp__health[playerid] > health)

и замените её на:

if((ac_hp__health[playerid] > health)
&& (GetTickCount() > ac_hp__ignore_timestamp[playerid]))


И остаётся лишь сделать запись времени, до которого античит будет игнорировать игрока.
В перехвате SetPlayerHealth найдите строки:

ac_hp__health[playerid] = health;
return SetPlayerHealth(playerid, health);

и замените их на:

ac_hp__ignore_timestamp[playerid] = GetTickCount()+AC_HP__IGNORE_TIME;
ac_hp__health[playerid] = health;
return SetPlayerHealth(playerid, health);


Теперь античит не будет выдавать ложных срабатываний при выдаче HP.
Но остаётся ещё одна проблема: игрок будет умирать во время спавна.
Суть в том, что при спавне у игрока всегда 100 HP, а в античите в это время записано 0.
Если сервер не выдаст игроку новое кол-во HP, античит выдаст ложное срабатывание и установит игроку 0 HP.
Чтобы исправить эту проблему, добавим под перехватом OnPlayerUpdate перехват OnPlayerSpawn, в котором запишем у игрока 100 HP.

public OnPlayerSpawn(playerid)
{
ac_hp__ignore_timestamp[playerid] = GetTickCount()+AC_HP__IGNORE_TIME;
ac_hp__health[playerid] = 100.0;
#if defined ac_hp__OnPlayerSpawn
ac_hp__OnPlayerSpawn(playerid);
#endif
return 1;
}
#if defined _ALS_OnPlayerSpawn
#undef OnPlayerSpawn
#else
#define _ALS_OnPlayerSpawn
#endif
#define OnPlayerSpawn ac_hp__OnPlayerSpawn
#if defined ac_hp__OnPlayerSpawn
forward ac_hp__OnPlayerSpawn(playerid);
#endif



Итак, если игрок накручивает HP читами, античит будет понижать здоровье обратно.
Но что, если нам нужно не только нейтрализовать читера, но и оповестить администрацию?
Можно добавить после понижения здоровья вызов какой-нибудь функции из мода (например, SendAdminMessage) для вывода модераторам сообщения о читере, НО такая функция есть не везде, а потому модуль не будет работать на всех модах.
Поэтому мы поступим иначе: добавим в мод коллбэк OnHPCheatDetected и в нём будем записывать весь код, который "привязан" к тому моду.
Здесь же сделаем вызов коллбэка. В перехвате OnPlayerUpdate находим строку:

SetPlayerHealth(playerid, ac_hp__health[playerid]);

и заменяем её на:

SetPlayerHealth(playerid, ac_hp__health[playerid]);
#if defined OnHPCheatDetected
OnHPCheatDetected(playerid, ac_hp__health[playerid], health);
#endif

И не забудем добавить опережающее объявление коллбэка для мода. В конец инклуда:

#if defined OnHPCheatDetected
forward OnHPCheatDetected(playerid, Float:hp_expected, Float:hp_got);
#endif

Обратите внимание: в обоих отрывках присутствует "#if defined" - это сделано для того, чтобы убедиться, что коллбэк OnHPCheatDetected реализован в моде.
Если его в моде нет, он не будет вызван из инклуда - иначе были бы ошибки из-за вызова несуществующей функции.


Наконец, составим весь код воедино и вынесем его в отдельный инклуд (например, "ac__health.inc", здесь "ac" - сокращение от "AntiCheat").
Результат должен выглядеть примерно так, как в этой теме (http://pro-pawn.ru/showthread.php?10586-dc_anti_hp_hack-%D0%B0%D0%BD%D1%82%D0%B8%D1%87%D0%B8%D1%82-%D0%BD%D0%B0-HP).

В итоге получается система, совершенно никак не привязанная к конкретному моду.
Поскольку она готова, остаётся лишь использовать её в вашем моде.
Сохраним инклуд в папке "pawno/include" и подключим его:

#include <dc_ac__health>

После этого добавим в мод коллбэк OnHPCheatDetected, который уже сделан в инклуде:

public OnHPCheatDetected(playerid, Float:hp_expected, Float:hp_got)
{
// здесь Ваш код для оповещения модераторов, бан игрока и т.д. и т.п.
}


Если у Вас получилось всё вышеперечисленное - поздравляю, Вы написали свой первый модуль на Pawn с использованием перехватов.
К следующей неделе придумаю ещё несколько примеров для 3-й части урока.

Автор: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)


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

Dima_Tushin
13.10.2014, 22:02
Сейчас попробую сделать!

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

а вот теперь все получилось и без ошибок! сейчас буду тестировать!

Dima_Tushin
14.10.2014, 12:56
вчера я сделал но у меня не ловило читерское хп жалко блин ладно еще раз попробую!

Dima_Tushin
14.10.2014, 13:36
Помоги Daniel посмотри что я не правильного сделал?


/*=============================================================================
Health Anticheat
by Daniel_Cortez

www.pro-pawn.ru

Copyright (c) 2014 Daniel_Cortez
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the
use of this software. Permission is granted to anyone to use this software for
any purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in
a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
=============================================================================*/
static Float: Health[MAX_PLAYERS];
/************************************************/
stock SetPlayerHealthAC(playerid, Float: healthe)
{
Health[playerid] = healthe;
return SetPlayerHealth(playerid, healthe);
}
#if defined _ASC_SetPlayerHealth
#undef SetPlayerHealth
#else
#define _ASC_SetPlayerHealth
#endif
#define SetPlayerHealth SetPlayerHealthAC
/************************************************/
public OnPlayerUpdate(playerid)
{
{
new Float: health;
GetPlayerHealth(playerid, health);
if((Health[playerid] != health))
{
if(Health[playerid] > health)
{
Health[playerid] = health;
#if defined OnHPCheatDetected
OnHPCheatDetected(playerid, Health[playerid], health);
#endif
}
else if(Health[playerid] < health)
{
Health[playerid] = health;
#if defined OnHPCheatDetected
OnHPCheatDetected(playerid, Health[playerid], health);
#endif
}
}
}
#if defined OnPlayerUpdateAC
OnPlayerUpdateAC(playerid);
#endif
return 1;
}
#if defined _ALS_OnPlayerUpdate
#undef OnPlayerUpdate
#else
#define _ALS_OnPlayerUpdate
#endif
#define OnPlayerUpdate OnPlayerUpdateAC
#if defined OnPlayerUpdateAC
forward OnPlayerUpdateAC(playerid);
#endif
/************************************************/
public OnPlayerSpawn(playerid)
{
Health[playerid] = 100;
#if defined OnPlayerSpawnAC
OnPlayerSpawnAC(playerid);
#endif
return 1;
}
#if defined _ALS_OnPlayerSpawn
#undef OnPlayerSpawn
#else
#define _ALS_OnPlayerSpawn
#endif
#define OnPlayerSpawn OnPlayerSpawnAC
#if defined OnPlayerSpawnAC
forward OnPlayerSpawnAC(playerid);
#endif
/************************************************/
stock GetPlayerHealthAC(playerid, &Float:healths)
{
healths = Health[playerid];
return 1;
}
#if defined _ALS_GetPlayerHealth
#undef GetPlayerHealth
#else
#define _ALS_GetPlayerHealth
#endif
#define GetPlayerHealth GetPlayerHealthAC
/************************************************/
#if defined OnHPCheatDetected
forward OnHPCheatDetected(playerid, Float:hp_expected, Float:hp_got);
#endif

GetPlayerHealth заменил GetPlayerHealthAC

SetPlayerHealth заменил SetPlayerHealthAC в моде не в Include
помоги что то не правильно сделал поправь, подскажи

L0ndl3m
14.10.2014, 17:01
Самая большая ошибка в данном случае это присвоение работы за свою. :/


/*=============================================================================
Health Anticheat
by Dima_Tuhin

www.pro-pawn.ru

Copyright (c) 2014 Dima_Tuhin

И перечитайте ещё раз первый пост, заменять функции не стоит.

Dima_Tushin
14.10.2014, 17:06
да это не важно я же не выкладываю и не говорю что я автор
просто спросил в чем проблема

да функции можно любые делать вот что обязательно в каждой переменной или функции делать пробелы вот такие ______

Osetin
14.10.2014, 17:38
да это не важно я же не выкладываю и не говорю что я автор
просто спросил в чем проблема

да функции можно любые делать вот что обязательно в каждой переменной или функции делать пробелы вот такие ______

Как раз таки это важно. Люди старались, описывали вам все, а вы взяли и тупо сменили ник автора на свой ник. Т.е вы присвоили чужую работу, которую вам преподнесли с подробным разъяснением. А автор сидел несколько дней и пытался написать статью. Не очень приятно бывает, когда так поступают.

Dima_Tushin
14.10.2014, 17:59
на конец то получилось :) теперь все античиты переведу на Includе

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


Как раз таки это важно. Люди старались, описывали вам все, а вы взяли и тупо сменили ник автора на свой ник. Т.е вы присвоили чужую работу, которую вам преподнесли с подробным разъяснением. А автор сидел несколько дней и пытался написать статью. Не очень приятно бывает, когда так поступают.

ладно понял я больше не буду присваивать авторство!!!

Seregamil
14.10.2014, 18:42
да это не важно я же не выкладываю и не говорю что я автор
просто спросил в чем проблема

да функции можно любые делать вот что обязательно в каждой переменной или функции делать пробелы вот такие ______
Ахринеть, а давайте я хакну библиотеки сампа, и вместо "SA-MP Team" напишу "Seregamil"?

$continue$
14.10.2014, 19:43
Ахринеть, а давайте я хакну библиотеки сампа, и вместо "SA-MP Team" напишу "Seregamil"?

go cho

Seregamil
15.10.2014, 05:16
go cho

Почитай, что такое авторское право.

MR_BEN
15.10.2014, 13:03
Ну поставили вы его "на место", а проблему так и не решили.

Avertus
25.10.2014, 12:18
Целесообразно ли будет весь мод разбить на модули, как проект GTO, только с использованием перехватов описанных в этой теме? В итоге получится набор систем в отдельных инклудах и один *.pwn файл где эти инклуды собираются:


#include "a_samp"



#include "lib/a_mysql"
#include "lib/dc_cmd"
#include "lib/sscanf2"
#include "lib/foreach"
#include "lib/showformatteddialog"
#include "lib/streamer"

// Мод:
#include "config.inc"
#include "db_connect.inc"
#include "reg_log.inc"
#include "ac_hc.inc"
#include "test.inc"
#include "maps.inc"
#include "vehicle.inc"

main(){}

DeimoS
25.10.2014, 12:35
Целесообразно ли будет весь мод разбить на модули, как проект GTO, только с использованием перехватов описанных в этой теме? В итоге получится набор систем в отдельных инклудах и один *.pwn файл где эти инклуды собираются:


#include "a_samp"



#include "lib/a_mysql"
#include "lib/dc_cmd"
#include "lib/sscanf2"
#include "lib/foreach"
#include "lib/showformatteddialog"
#include "lib/streamer"

// Мод:
#include "config.inc"
#include "db_connect.inc"
#include "reg_log.inc"
#include "ac_hc.inc"
#include "test.inc"
#include "maps.inc"
#include "vehicle.inc"

main(){}

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

Daniel_Cortez
25.10.2014, 15:32
Целесообразно ли будет весь мод разбить на модули, как проект GTO, только с использованием перехватов описанных в этой теме?
Слишком много всего придётся менять. По мне так проще будет написать новый мод с 0, заранее планируя модульную систему мода с использованием перехватов, чем долбиться над старой кучей кода.

codeo
28.10.2014, 12:28
if(_:ac_hp__health[playerid] != _:health) Я не совсем понял для чего это _: можно поподробнее

DeimoS
28.10.2014, 13:45
if(_:ac_hp__health[playerid] != _:health) Я не совсем понял для чего это _: можно поподробнее

Это указан тип данных. В данном случае он целочисленный. Это как "Float:health" и т.п. Для чего это делается? Это объяснено в самом коде:

// если кол-во HP изменилось с момента предыдущего обновления
// сравниваемые значения трактуются, как целочисленные, чтобы избежать лишнего вызова floatcmp
// (внимание! такой оптимизационный приём можно применять только при сравнении
// с помощью знаков "==" и "!=", но ни в коем случае не с ">", "<", ">=" или "<=")

Maranzalla
23.03.2015, 18:57
Я хочу использовать в своем моде замену

stock SetPlayerPos_protect(playerid, Float:x,Float:y,Float:z)
{
Player_Off_Protect{playerid} = 3;
Player_Pos[0][playerid] = x;
Player_Pos[1][playerid] = y;
Player_Pos[2][playerid] = z;
return SetPlayerPos(playerid, x,y,z);
}
#if defined _ALS_SetPlayerPos_protect
#undef SetPlayerPos
#else
#define _ALS_SetPlayerPos_protect
#endif
#define SetPlayerPos SetPlayerPos_protect
вот так будет правильно?

Desulaid
01.05.2015, 22:36
:to_take_umbrage: У тебя тут небольшая ошибочка.


stock ac_hp__GetPlayerHealth(playerid, Float:&health)
{
health = ac_hp__health[playerid];
return 1;
}
#if defined _ALS_GetPlayerHealth
#undef GetPlayerHealth
#else
#define _ALS_GetPlayerHealth
#endif
#define GetPlayerHealth ac_hp__GetPlayerHealth

А правильно было бы, если? (нашел при сравнении dc_anti_hp_hack.inc)


stock ac_hp__GetPlayerHealth(playerid, &Float:health)
{
health = ac_hp__health[playerid];
return 1;
}
#if defined _ALS_GetPlayerHealth
#undef GetPlayerHealth
#else
#define _ALS_GetPlayerHealth
#endif
#define GetPlayerHealth ac_hp__GetPlayerHealth


А то при первом варианте ошибочка вылазит, при втором ее нету.

Daniel_Cortez
01.05.2015, 22:45
:to_take_umbrage: У тебя тут небольшая ошибочка.


stock ac_hp__GetPlayerHealth(playerid, Float:&health)
{
health = ac_hp__health[playerid];
return 1;
}
#if defined _ALS_GetPlayerHealth
#undef GetPlayerHealth
#else
#define _ALS_GetPlayerHealth
#endif
#define GetPlayerHealth ac_hp__GetPlayerHealth

А правильно было бы, если? (нашел при сравнении dc_anti_hp_hack.inc)


stock ac_hp__GetPlayerHealth(playerid, &Float:health)
{
health = ac_hp__health[playerid];
return 1;
}
#if defined _ALS_GetPlayerHealth
#undef GetPlayerHealth
#else
#define _ALS_GetPlayerHealth
#endif
#define GetPlayerHealth ac_hp__GetPlayerHealth


А то при первом варианте ошибочка вылазит, при втором ее нету.
Исправил.

Desulaid
04.05.2015, 10:17
Что то у меня не совсем выходит написать по аналогии с этим античит на деньги. Проблема в параметрах функции, которая оповещает администраторов т.д.


error 004 function "OnMoneyCheatDetected" is not implemented
error 055: start of function body without function header
error 010: invalid function or declaration

Desulaid
26.05.2015, 00:30
Ура. Вышло. Я же теперь типа "супир" скриптер! :dirol:


/* * * * * * * * * * * * * * * * * * * * * * * * * * *
* Money Anticheat
* by Darge
* www.pro-pawn.ru \
* убрал другую ссылка :)
*
* Copyright (c) 2015 Darge
*
* ipsBruno's and Y_Less's technologies are used.
* separate gratitude to Daniel_Cortez
*/

#if !defined AC_MONEY__IGNORE_TIME
#define AC_MONEY__IGNORE_TIME 1000
#endif

static ac__money[MAX_PLAYERS];
static ac__money_ignore_timestamp[MAX_PLAYERS];

//------------------------------------------------------
public OnPlayerUpdate(playerid)
{
{
new money;
money = GetPlayerMoney(playerid);
if(ac__money[playerid] != money)
{
if((ac__money[playerid] > money)
&& (GetTickCount() > ac__money_ignore_timestamp[playerid]))
{
ac__money[playerid] = money;
}
else if(ac__money[playerid] < money)
{
GivePlayerMoney(playerid, ac__money[playerid]);
#if defined OnMoneyCheatDetected
OnMoneyCheatDetected(playerid, ac__money[playerid], money);
#endif
}
}
}
#if defined ac_hp__OnPlayerUpdate
ac_hp__OnPlayerUpdate(playerid);
#endif
return 1;
}
#if defined _ALS_OnPlayerUpdate
#undef OnPlayerUpdate
#else
#define _ALS_OnPlayerUpdate
#endif
#define OnPlayerUpdate ac_hp__OnPlayerUpdate
#if defined ac_hp__OnPlayerUpdate
forward ac_hp__OnPlayerUpdate(playerid);
#endif
//------------------------------------------------------
public OnPlayerSpawn(playerid)
{
ac__money_ignore_timestamp[playerid] = GetTickCount()+AC_MONEY__IGNORE_TIME;
ac__money[playerid] = 100;
#if defined ac_money__OnPlayerSpawn
ac_money__OnPlayerSpawn(playerid);
#endif
return 1;
}
#if defined _ALS_OnPlayerSpawn
#undef OnPlayerSpawn
#else
#define _ALS_OnPlayerSpawn
#endif
#define OnPlayerSpawn ac_money__OnPlayerSpawn
#if defined ac_money__OnPlayerSpawn
forward ac_money__OnPlayerSpawn(playerid);
#endif
//------------------------------------------------------
stock ac__GetPlayerMoney(playerid, money)
{
money = ac__money[playerid];
return 1;
}
#if defined _ALS_GetPlayerMoney
#undef GetPlayerMoney
#else
#define _ALS_GetPlayerMoney
#endif
#define GetPlayerMoney ac__GetPlayerMoney
//------------------------------------------------------
stock ac__GivePlayerMoney(playerid, money)
{
ac__money_ignore_timestamp[playerid] = GetTickCount()+AC_MONEY__IGNORE_TIME;
ac__money_ignore_timestamp[playerid] = money;
return GivePlayerMoney(playerid, money)
}
#if defined _ALS_GivePlayerMoney
#undef GivePlayerMoney
#else
#define _ALS_GivePlayerMoney
#endif
#define GivePlayerMoney ac__GivePlayerMoney
//------------------------------------------------------
#if defined OnMoneyCheatDetected
forward OnMoneyCheatDetected(playerid, money_ac, money_c);
#endif

Daniel_Cortez
26.05.2015, 06:49
1. В чём необходимость использования OnPlayerUpdate? Можно было просто перехватить OnGameModeInit и сделать свой односекундный таймер, а не грузить сервер проверками каждого игрока по 25 раз в секунду.
2. Если собираетесь публиковать работу - не пользуйтесь машинным переводом, это выглядит убого. Либо учите английский, либо лучше даже не пытайтесь писать на нём.
3. Это так и задумано, чтобы античит отбирал у игрока все деньги после смерти, оставляя всего 100$ ?



public OnPlayerSpawn(playerid)
{
ac__money_ignore_timestamp[playerid] = GetTickCount()+AC_MONEY__IGNORE_TIME;
ac__money[playerid] = 100;
#if defined ac_money__OnPlayerSpawn
ac_money__OnPlayerSpawn(playerid);
#endif
return 1;
}

4. Зачем каждый раз получать кол-во денег у игрока, когда можно сначала проверить время игнора, а уже потом, если нужно, сделать проверку на чит?
5. Определитесь уже с префиксом:



ac_money__OnPlayerSpawn

ac__money_ignore_timestamp
ac__money_OnPlayerSpawn

ac__GivePlayerMoney

ac_hp__OnPlayerUpdate // серьёзно!?

Daniel_Cortez
30.05.2015, 17:37
Не люблю даблпостинг, но всё же...



stock ac__GivePlayerMoney(playerid, money)
{
ac__money_ignore_timestamp[playerid] = GetTickCount()+AC_MONEY__IGNORE_TIME;
ac__money_ignore_timestamp[playerid] = money;
return GivePlayerMoney(playerid, money)
}

С такими ошибками скоро придётся реквестировать поддержку Pawn в PVS-Studio.

$continue$
04.06.2015, 11:12
Хм очень интересно, накидал фикс кика (Что бы выводились сообщения)


stock fix_KickPlayer(playerid)
{
print("fix_KickPlayer | Вызван");
SetTimerEx("OnPlayerKick", 1000, false, "i", playerid);
return 1;
}
#if defined _ALS_Kick
#undef Kick
#else
#define _ALS_Kick
#endif
#define Kick fix_KickPlayer



forward OnPlayerKick(playerid);
public OnPlayerKick(playerid) return print("OnPlayerKick | Вызван"), Kick(playerid);

Вызовы паблика и стока есть, но не кикает, в чем может быть причина?

Daniel_Cortez
04.06.2015, 14:37
Вызов оригинала должен быть до переобъявления.
Скорее всего, у вас паблик находится после переобъявления и вместо вызова оригинала Kick производится вызов перехватчика. В итоге получаем косвенную рекурсию: перехватчик вызывает сам себя через таймер.

Reim
23.06.2015, 21:44
Можно ли сделать вот так?



AC_GivePlayerMoney(playerid,summa)
{
PlayerInfo[playerid][pMoney] += summa;
GivePlayerMoney(playerid,summa);
return true;
}
#define GivePlayerMoney AC_GivePlayerMoney


И еще эти перехваты должны быть после a_samp или до, вот насчет этого момента?

Daniel_Cortez
24.06.2015, 10:38
Можно ли сделать вот так?



AC_GivePlayerMoney(playerid,summa)
{
PlayerInfo[playerid][pMoney] += summa;
GivePlayerMoney(playerid,summa);
return true;
}
#define GivePlayerMoney AC_GivePlayerMoney


И еще эти перехваты должны быть после a_samp или до, вот насчет этого момента?

Вы не первый, кто это спрашивает (и вряд ли последний). Поэтому я добавил послесловие в первой части урока.
Для удобства процитирую его здесь:


Вместо послесловия:


Как-то давно один "профессионал" спросил меня, мол зачем вся эта чепуха с перехватами, когда всё можно уместить в один #define?


stock my_AddStaticVehicle(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2)
{
print('Функция AddStaticVehicle перехвачена');
return AddStaticVehicle(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2);
}
#define AddStaticVehicle my_AddStaticVehicle

Так вот, этот метод в корне неправильный.

Почему? Очень просто, им нельзя сделать больше одного перехвата на одну и ту же функцию.
Не верите? Тогда попробуйте скомпилировать код (выведет ошибку на втором #define AddStaticVehicle):


// 2-й перехватчик для AddStaticVehicle
stock my2_AddStaticVehicle(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2)
{
print('Функция AddStaticVehicle перехвачена ещё раз');
return AddStaticVehicle(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2);
}
#define AddStaticVehicle my2_AddStaticVehicle

// 1-й перехватчик для AddStaticVehicle
stock my1_AddStaticVehicle(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2)
{
print('Функция AddStaticVehicle перехвачена');
return AddStaticVehicle(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2);
}
#define AddStaticVehicle my1_AddStaticVehicle

Не получилось? Я предупреждал.

Такой способ подойдёт только если вы используете его в своём моде и только если вы перехватываете функцию один раз (хоть я этого и не рекомендую).
Но если вы используете этот ленивый способ в выкладываемых на Pro-Pawn работах (да, я смотрю на вас, авторы уроков и мануалов), даже не надейтесь, что ваша работа будет одобрена.
Вы явно ошиблись порталом, если думаете, что можно одобрить полурабочий говнокод, который может нарушить совместимость с другими работами.

> Но ведь и так сойдёт! А кому не нравится, пусть сами переделывают!!
Отправляйтесь обратно н***й на govno-info и постите говнокод там.

Geebrox
24.08.2015, 17:05
А если я хочу сделать античит на деньги, в самом игровом моде отдельный переменный который хранит деньги и в античите отдельный. Как нормально сохранить и выдать деньги игроку? Или надо убрать из мода переменный, который хранит информацию о деньгах игрока и использовать для этого только переменную из античита?

Desulaid
25.08.2015, 13:08
А если я хочу сделать античит на деньги, в самом игровом моде отдельный переменный который хранит деньги и в античите отдельный. Как нормально сохранить и выдать деньги игроку? Или надо убрать из мода переменный, который хранит информацию о деньгах игрока и использовать для этого только переменную из античита?

На сколько я понял, то в обычном античите копируется вызываение суммы для античита. А тут мы просто должны перехватить значение > записать его в переменную и далее сравнивать.

Geebrox
25.08.2015, 14:07
Перехватить значение?? как если античит подключается перед тем как создается переменная для хранение денег. Или я не так тебя понял?

Desulaid
25.08.2015, 14:27
Ну что то типа


static ac_money[MAX_PLAYERS];

stock ac_money__GivePlayerMoney(playerid, money)
{
money = ac_money[playerid];
return 1;
}
#if defined _ALS_GivePlayerMoney
#undef GivePlayerMoney
#else
#define _ALS_GivePlayerMoney
#endif
#define GivePlayerMoney ac_money__GivePlayerMoney


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

И теперь с каждым вызовом функции GivePlayerMoney в переменную ac_money будет перезаписываться кол-во передаваемых денюжек.

Geebrox
25.08.2015, 14:29
:dash2::dash2::dash2: да я это знаю, кароче ты меня не понял
Ты даже про перехваты понял не до конца.
Вместо new массив объявлен с помощью ключевого слова static - так, если перенести весь античит в отдельный инклуд, массив будет виден только внутри инклуда и не будет мешаться в моде.

Я спросил как значение денег из античита перенести в переменную из энуменатора игрока в игровом моде

Daniel_Cortez
25.08.2015, 15:30
:dash2::dash2::dash2: да я это знаю, кароче ты меня не понял
Ты даже про перехваты понял не до конца.

Я спросил как значение денег из античита перенести в переменную из энуменатора игрока в игровом моде

player_info[playerid][pMoney] = GetPlayerMoney(playerid);
Если функция GetPlayerMoney перехватывается в античите (а она должна перехватываться, иначе какой же это античит?), то она должна будет вернуть именно то кол-во денег, которое записано в античите.
С функцией SetPlayerMoney то же самое.

Geebrox
25.08.2015, 15:42
а точно, спасибо за ответ) как я мог не додуматься об этом)

Redsan
15.09.2016, 15:55
Почему используется функция OnPlayerUpdate? В каких случаях стоит использовать секундный таймер для проверок античита?

vovandolg
15.09.2016, 20:47
Почему используется функция OnPlayerUpdate? В каких случаях стоит использовать секундный таймер для проверок античита?

Это просто пример перехватов, на деле так не делать лучше если сервер будет иметь большой онлайн(да это я про OnPlayerUpdate)

Nexius_Tailer
16.09.2016, 14:38
В каких случаях стоит использовать секундный таймер для проверок античита?
В тех, когда вещь, которую нужно детектить, приносит вред только в долгосрочной перспективе (т.е. это не крашер). Ну и когда чит важно просто словить без рассинхрона читера (который возможен как раз только в OnPlayerUpdate).

vasyok28
10.02.2017, 13:42
Как можно перехватить функцию если идет неизвестное количество параметров.
Допустим
func("iis", id, vehid, name);

VVWVV
10.02.2017, 13:45
Как можно перехватить функцию если идет неизвестное количество параметров.
Допустим
func("iis", id, vehid, name);

Придётся использовать #emit, либо же функции numargs, getarg.

vasyok28
10.02.2017, 13:47
Придётся использовать #emit, либо же функции numargs, getarg.

Можно пример?
Мне нужно перехватить функцию mysql_function_query

Redsan
14.02.2017, 13:35
Можно пример?
Мне нужно перехватить функцию mysql_function_query

Что вы хотите сделать?

$continue$
09.06.2021, 01:15
forawrd ac_veh_hp__OnVehicleDamageStatu(vehicleid, playerid);


to


forward ac_veh_hp__OnVehicleDamageStatu(vehicleid, playerid);