Cawfee

Принцип написания античитов. Написание античита на деньги для наглядности

В теме 3 сообщения

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

 

Как работает античит? Рассмотрим такую ситуацию. При помощи запрещенных программных обеспечений Вы изменяете количество денег у своего игрока. Но спустя несколько секунд (а обычно даже секунду) это количество денег возвращается в прежнее состояние. Можно предположить, что существует какой-то таймер с интервалом, например, в секунду, внутри которого идет проверка: соответствует ли количество денег игрока сейчас тем цифрам, что были во время прежней проверки.

 

Итак, мы можем получить деньги игрока в данный момент при помощи функции GetPlayerMoney. А с чем нам сверять полученное количество средств? Следует создать новую переменную, в которую уже стоит заносить количество средств, которое должно быть у игрока. Следовательно, нам нужно перехватить функции, связанные с изменением денег, в которых уже производить изменения в значении переменной.

 

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

 

Начинаем писать античит.

Мое мнение таково, что античиты следует выносить в отдельные файлы (инклуды). Для этого предлагаю создать в папке "include" новую папку - "anticheats". Внутри новой папки создаем файл "ac_money.inc". Открываем файл.

 

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

#if defined INC_AC_MONEY
	#endinput
#endif
#define INC_AC_MONEY

Далее мы объявим переменные, в которых будем хранить количество денег игрока (сколько у него должно быть) и ID таймера для игрока, чтобы могли удалять его после того, как игрок отключился.

static
	gPlayerMoney[MAX_PLAYERS], // количество денег, которое должно быть у игрока
	gPlayerTimer[MAX_PLAYERS]; // ID таймера для игрока, в котором будет идти проверка его баланса

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

public OnPlayerConnect(playerid)
{
	// создаем таймер, где будем проверять игрока на пользование читами
	gPlayerTimer[playerid] = SetTimerEx("ac_money_OnPlayerSecondTimer", 1000*1, true, "i", playerid);
	
	#if defined ac_money_OnPlayerConnect
		return ac_money_OnPlayerConnect(playerid);
	#else
		return 1;
	#endif
}
#if defined _ALS_OnPlayerConnect
	#undef OnPlayerConnect
#else
	#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect ac_money_OnPlayerConnect
#if defined ac_money_OnPlayerConnect
	forward ac_money_OnPlayerConnect(playerid);
#endif

public OnPlayerDisconnect(playerid, reason)
{
	KillTimer(ac_timerid[playerid]); // удалим таймер для этого игрока
	
	#if defined ac_money_OnPlayerDisconnect
		return ac_money_OnPlayerDisconnect(playerid, reason);
	#else
		return 1;
	#endif
}
#if defined _ALS_OnPlayerDisconnect
	#undef OnPlayerDisconnect
#else
	#define _ALS_OnPlayerDisconnect
#endif
#define OnPlayerDisconnect ac_money_OnPlayerDisconnect
#if defined ac_money_OnPlayerDisconnect
	forward ac_money_OnPlayerDisconnect(playerid);
#endif

Мы перехватили две функции - OnPlayerConnect и OnPlayerDisconnect, в которых будем создавать и удалять таймер для проверки игрока на пользование читами. Далее мы должны перехватить функции изменения денег (ResetPlayerMoney, GivePlayerMoney), чтобы в них происходило изменение значения переменной, где записано количество денежных средств данного игрока:

stock ac_money_GivePlayerMoney(playerid, money) // перехватываем функцию GivePlayerMoney
{
	gPlayerMoney[playerid] += money;
	return GivePlayerMoney(playerid, money);
}
#if defined _ALS_GivePlayerMoney
	#undef GivePlayerMoney
#else
	#define _ALS_GivePlayerMoney
#endif
#define GivePlayerMoney ac_money_GivePlayerMoney

stock ac_money_ResetPlayerMoney(playerid) // перехватим функцию ResetPlayerMoney
{
	gPlayerMoney[playerid] = 0;
	return ResetPlayerMoney(playerid);
}
#if defined _ALS_ResetPlayerMoney
	#undef ResetPlayerMoney
#else
	#define _ALS_ResetPlayerMoney
#endif
#define ResetPlayerMoney ac_money_ResetPlayerMoney

Последнее, что мы не сделали - функцию, в которой будем производить проверку: пользуется ли игрок запрещенными программными обеспечениями? Создаем ее (обратите внимание - я создаю функцию с модификатором public потому, что это будет таймер, а все таймеры следует объявлять с модификаторами public).

forward ac_money_OnPlayerSecond(playerid);
public ac_money_OnPlayerSecond(playerid)
{
	if(GetPlayerMoney(playerid) == gPlayerMoney[playerid])
		return 1; // все хорошо, деньги "тогда" и сейчас совпадают
		
	SendClientMessage(playerid, -1, "Система расценила Ваши действия как действия читера. Количество средств не совпадает.");
	Kick(playerid); // выкинем игрока с сервера за пользование сторонними ПО
	return 0;
}

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

 

Автор данного урока: @Jawn

AntiCheat money.inc

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Этот античит к любой игре подойдет ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@mat300 принцип во всех играх один - хранить данные, которые могут быть подделаны, на сервере. А код, естественно, будет отличаться. Данный подойдет к SAMP/CRMP. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

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

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!


Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.


Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

  • Похожий контент

    • Kutuzov
      От Kutuzov
      Автор данного канала снимает уроки по программированию на Pawn, также скоро будет на 20 подписчиков снимать мод с нуля в samp. 
      Приглашаем всех на канал, а также особенно новичков. Даже если вы уже знаете pawn, подпишитесь пожалуйста на его канал и поддержите его.
      Автор канала не требует денег!!! Он обучает всех бесплатно!!!
      Ссылка на канал: https://www.youtube.com/@prog_samp_easy
       
      Также у него есть свой тг канал, но начнет вести его, как только будет не менее 10 подписчиков.
       Ссылка на тг канал: https://t.me/pawndevelop
       
      Всем удачи
    • Sleash
      От Sleash
      Всем доброго времени суток. Сегодня мне бы хотелось вам рассказать от таких функциях в PAWN-языке, как SetTimer и SetTimerEx.
      Сразу сделаю такое примечание: Для SetTimer и SetTimerEx можно использовать ТОЛЬКО public!!!
      SetTimer
      Итак, для начала приступим к лёгкому и пойдём по нарастающей:
      Первое, это SetTimer. Функция включает в себя 3 параметра:
      native SetTimer(funcname[], interval, repeating); funcname[] - Это название функции, по сути - название вашего pablic в кавычках.
      interval - Интервалы от вызова таймера до срабатывания pablic
                     Так же если стоит повторение таймера, то будет работать как интервал между таймерами.
                     Измеряется в миллисекундах. 1000 миллисекунд равно 1 секунде
      repating - Тут всё просто: будет ли повторяться ваша функция. Может иметь значения: true - будет выполняться повторение ИЛИ false - вызывается 1 раз.
      Давайте разберём пример для наглядности:
      forward Info(); public Info() {     SendClientMessageToAll(0xFFFFFFFF, "Наш сайт: pawno-rus.ru"); } // И теперь давайте вызовем данный таймер при запуске мода public OngameModeInit() {     SetTimer("Info", 1000*60*20, true);     return true } Давайте разберёмся по интервалу: 1000*60*20. Так как я писал ранее, что 1000 млСек = 1 сек, то: 1 сек * 60 = 1 минута; 1 минута * 20 = 20 минут.
      Таким образом данный таймер будет выводить каждые 20 минут для всех игроков на сервере в чат: "pawno-rus.ru".
       
      SetTimerEx
       
      По сути с SetTimer - всё, теперь перейдём к следующей функции: SetTimerEx:
      native SetTimerEx(funcname[], interval, repeating, const format[], {Float,_}:...); Тут добавляются ещё 2 параметра:
      const format[] - формат данных для ввода в функцию. (Такие как: s, d, f);
      {Float,_}:... - переменные или значение для данных из const fromat[]
      Из данных объяснения мало что можно понять незнающему или начинающему скриптеру, поэтому давайте разберём на примере кода, который при входу даёт на авторизацию две минуты игроку:
      forward autorez(playerid); public autorez(playerid) {     if(/*проверка на то залогинился ли игрок*/)     {         SendClientMessage(playerid, 0xFFFFFFFF, "Вы были кикнуты по причине: Время на авторизацию истекло.");         Kick(playerid);         return true;     }     return true; } //Дальше уже добавлям сам таймер public OnPlayerConnect(playerid) {     SetTimerEx("autorez", 1000*60*2, false, "d", playerid);     return true; } Если первые три пункта мы уже разбирали. то начнём сразу с 4-го:
      4. Тут в кавычках надо вставлять тип данных. Наподобие функции format(output[], len, "%s %d %f", string, int, float), только без знака процента (%)
      5. Тут уже указывают сами данные, так же разберём как и предыдущий: format(output[], len, "%s %d %f ", string, int, float)
       
      Я надеюсь что кому-то помог, а если что непонятно, то спрашивайте, всегда помогу.
    • NoVate
      От NoVate
      Доброго времени суток 
       
      Многие новички задавались вопросом: "Как создать свой RP сервер?", "С чего начать разработку?", "Это сложно и я не справлюсь".
      После таких вопросов многие уходили с этой темы и оставляли её на последней полке. Некоторые брались и сталкивались с большими проблемами. И только часть из этих людей доходили до какого-то результата.
       
      Моя цель: сделать создание сервера в SA:MP проще. Чтобы каждый из тех, кто интересовался созданием сервера мог спокойно взять готовую, а что самое главное - базовую сборку и под неё уже создавать различные свои задумки. SA:MP на данный момент не особо актуален, а тем более самый обычный клиент, НО, как мне кажется, если сделать что-то невероятное, то аудитория заметит это и ей будет достаточно интересно "пощупать" данное новшество.
       
      Для тех, кто начнёт писать: "Ты видел свой код?", "Ты сделал не правильно в 777-ой строке" и так далее. Сразу отвечу, цитируя:
       
      Кому стало интересно и кто хотел бы ознакомиться со сборком - прошу в мой Github (не реклама).
      Буду делать коммиты по мере свободного времени и написании кода.
      Если есть какие-то идеи, то пишите в мой Discord сервера (не реклама), который можно будет найти на той же странице.
       
      Спасибо всем за прочтение и хорошего настроения 
    • xiemoniyaz
      От xiemoniyaz
      C:\Users\111\OneDrive\Ðàáî÷èé ñòîë\rrrr\gamemodes\arizona.pwn(78772) : error 017: undefined symbol "GEO_MAX_COUNTRY_NAME_LENGTH" C:\Users\111\OneDrive\Ðàáî÷èé ñòîë\rrrr\gamemodes\arizona.pwn(78772) : error 009: invalid array size (negative, zero or out of bounds) C:\Users\111\OneDrive\Ðàáî÷èé ñòîë\rrrr\gamemodes\arizona.pwn(78772) : error 036: empty statement C:\Users\111\OneDrive\Ðàáî÷èé ñòîë\rrrr\gamemodes\arizona.pwn(78772) : fatal error 107: too many error messages on one line Compilation aborted. Pawn compiler 3.2.3664          Copyright (c) 1997-2016, ITB CompuPhase 4 Errors.  
       
      cmd:get(playerid, params[]) {     extract params -> new player; else return SendClientMessage(playerid, -1, !"/get [playerid]");     new string[GEO_MAX_COUNTRY_NAME_LENGTH+GEO_MAX_REGION_NAME_LENGTH+GEO_MAX_CITY_NAME_LENGTH+GEO_MAX_PROVIDER_NAME_LENGTH+GEO_MAX_MOBILE_STATUS_LENGTH+GEO_MAX_PROXY_LENGTH-6+118];     format(string, sizeof(string),     "Страна: %s\n\     Регион: %s\n\     Город: %s\n\     Провайдер: %s\n\     Использует ли моб.сеть: %s\n\     Использует ли прокси: %s",     GetPlayerIpCountry(player),     GetPlayerIpRegion(player),     GetPlayerIpCity(player),     GetPlayerIpProvider(player),     GetPlayerIpMobileStatus(player),     GetPlayerIpProxyStatus(player));     ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, !" ", string, !"Закрыть", !"");     return true; }