<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.multitheftauto.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=DColeman</id>
	<title>Multi Theft Auto: Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.multitheftauto.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=DColeman"/>
	<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/wiki/Special:Contributions/DColeman"/>
	<updated>2026-04-26T09:53:42Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=OnExplosion&amp;diff=82642</id>
		<title>OnExplosion</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=OnExplosion&amp;diff=82642"/>
		<updated>2025-12-17T09:25:22Z</updated>

		<summary type="html">&lt;p&gt;DColeman: Add explosion sync information&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{Server event}}&lt;br /&gt;
{{Added feature/item|1.6.1|1.6.0|21914|This event is triggered every time an explosion is created either by server-side [[createExplosion]], or when reported by [[player]].}}&lt;br /&gt;
==Parameters==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
float x, float y, float z, int theType&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*'''x:''' X coordinate of where the explosion was created&lt;br /&gt;
*'''y:''' Y coordinate of where the explosion was created&lt;br /&gt;
*'''z:''' Z coordinate of where the explosion was created&lt;br /&gt;
*'''theType:''' the type of explosion created, see: [[Explosion types]]&lt;br /&gt;
==Source==&lt;br /&gt;
The [[event system#Event source|source]] of this event is the [[player]] who notified server about explosion, or [[root]] if explosion was created server-side along without specifying creator in [[createExplosion]].&lt;br /&gt;
&lt;br /&gt;
===Canceling===&lt;br /&gt;
If this event is [[Event system #Canceling|canceled]], the explosion will not occur.&lt;br /&gt;
If an explosion is notified by a player, that explosion will still be visible to this player.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
This example outputs information about occuring explosion.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local debugMsgLevel = 4&lt;br /&gt;
local debugMsgR = 255&lt;br /&gt;
local debugMsgG = 127&lt;br /&gt;
local debugMsgB = 0&lt;br /&gt;
local explosionTypes = {&lt;br /&gt;
	[0] = &amp;quot;Grenade&amp;quot;,&lt;br /&gt;
	[1] = &amp;quot;Molotov&amp;quot;,&lt;br /&gt;
	[2] = &amp;quot;Rocket&amp;quot;,&lt;br /&gt;
	[3] = &amp;quot;Rocket Weak&amp;quot;,&lt;br /&gt;
	[4] = &amp;quot;Car&amp;quot;,&lt;br /&gt;
	[5] = &amp;quot;Car Quick&amp;quot;,&lt;br /&gt;
	[6] = &amp;quot;Boat&amp;quot;,&lt;br /&gt;
	[7] = &amp;quot;Aircraft&amp;quot;,&lt;br /&gt;
	[8] = &amp;quot;Mine&amp;quot;,&lt;br /&gt;
	[9] = &amp;quot;Object&amp;quot;,&lt;br /&gt;
	[10] = &amp;quot;Tank Grenade&amp;quot;,&lt;br /&gt;
	[11] = &amp;quot;Small&amp;quot;,&lt;br /&gt;
	[12] = &amp;quot;Tiny&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onExplosion(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local explosionPos = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionTypeName = explosionTypes[explosionType]&lt;br /&gt;
	local explosionSource = inspect(source)&lt;br /&gt;
	local debugMsg = explosionTypeName..&amp;quot; explosion has occured at &amp;quot;..explosionPos..&amp;quot; (source: &amp;quot;..explosionSource..&amp;quot;)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(debugMsg, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosion)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{See also/Server event|Server events}}&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=AddDebugHook&amp;diff=82476</id>
		<title>AddDebugHook</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=AddDebugHook&amp;diff=82476"/>
		<updated>2025-09-16T09:42:24Z</updated>

		<summary type="html">&lt;p&gt;DColeman: /* Required Arguments */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{Server client function}}&lt;br /&gt;
{{New feature/item|3.0136|1.3|5939|&lt;br /&gt;
This function allows tracing of MTA functions and events. It should only be used when debugging scripts as it may degrade script performance.&lt;br /&gt;
&lt;br /&gt;
Debug hooks are not recursive, so functions and events triggered inside the hook callback will not be traced.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool addDebugHook ( string hookType, function callbackFunction [, table nameList ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Required Arguments=== &lt;br /&gt;
*'''hookType:''' The type of hook to add. This can be:&lt;br /&gt;
** preEvent&lt;br /&gt;
** postEvent&lt;br /&gt;
** preFunction&lt;br /&gt;
** postFunction&lt;br /&gt;
** preEventFunction&lt;br /&gt;
** postEventFunction&lt;br /&gt;
*'''callbackFunction:''' The function to call&lt;br /&gt;
** Returning the string &amp;quot;skip&amp;quot; from the callback function will cause the original function/event to be skipped&lt;br /&gt;
&lt;br /&gt;
===Optional Arguments=== &lt;br /&gt;
*'''nameList:''' Table of strings for restricting which functions and events the hook will be triggered on&lt;br /&gt;
** addDebugHook and removeDebugHook will only be hooked if they are specified in the name list&lt;br /&gt;
&lt;br /&gt;
===Returns===&lt;br /&gt;
Returns ''true'' if the hook was successfully added, or ''false'' otherwise.&lt;br /&gt;
&lt;br /&gt;
==Callback parameters==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
string preFunction( resource sourceResource, string functionName, bool isAllowedByACL, string luaFilename, int luaLineNumber, ...functionArguments )&lt;br /&gt;
       postFunction( resource sourceResource, string functionName, bool isAllowedByACL, string luaFilename, int luaLineNumber, ...functionArguments )&lt;br /&gt;
string preEvent( resource sourceResource, string eventName, element eventSource, element eventClient, string luaFilename, int luaLineNumber, ...eventArguments )&lt;br /&gt;
       postEvent( resource sourceResource, string eventName, element eventSource, element eventClient, string luaFilename, int luaLineNumber, ...eventArguments )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{New feature/item|3.0158|1.5.5|11856|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
string preEventFunction ( resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, resource functionResource, string functionFilename, int functionLineNumber, ...eventArgs )&lt;br /&gt;
       postEventFunction ( resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, resource functionResource, string functionFilename, int functionLineNumber, ...eventArgs )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
This example will dump info about all triggered events:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onPreEvent( sourceResource, eventName, eventSource, eventClient, luaFilename, luaLineNumber, ... )&lt;br /&gt;
    local args = { ... }&lt;br /&gt;
    local srctype = eventSource and getElementType(eventSource)&lt;br /&gt;
    local resname = sourceResource and getResourceName(sourceResource)&lt;br /&gt;
    local plrname = eventClient and getPlayerName(eventClient)&lt;br /&gt;
    outputDebugString( &amp;quot;preEvent&amp;quot;&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(resname)&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(eventName)&lt;br /&gt;
        .. &amp;quot; source:&amp;quot; .. tostring(srctype)&lt;br /&gt;
        .. &amp;quot; player:&amp;quot; .. tostring(plrname)&lt;br /&gt;
        .. &amp;quot; file:&amp;quot; .. tostring(luaFilename)&lt;br /&gt;
        .. &amp;quot;(&amp;quot; .. tostring(luaLineNumber) .. &amp;quot;)&amp;quot;&lt;br /&gt;
        .. &amp;quot; numArgs:&amp;quot; .. tostring(#args)&lt;br /&gt;
        .. &amp;quot; arg1:&amp;quot; .. tostring(args[1])&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
addDebugHook( &amp;quot;preEvent&amp;quot;, onPreEvent )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This example will dump info about all called MTA functions:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onPreFunction( sourceResource, functionName, isAllowedByACL, luaFilename, luaLineNumber, ... )&lt;br /&gt;
    local args = { ... }&lt;br /&gt;
    local resname = sourceResource and getResourceName(sourceResource)&lt;br /&gt;
    outputDebugString( &amp;quot;preFunction&amp;quot;&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(resname)&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(functionName)&lt;br /&gt;
        .. &amp;quot; allowed:&amp;quot; .. tostring(isAllowedByACL)&lt;br /&gt;
        .. &amp;quot; file:&amp;quot; .. tostring(luaFilename)&lt;br /&gt;
        .. &amp;quot;(&amp;quot; .. tostring(luaLineNumber) .. &amp;quot;)&amp;quot;&lt;br /&gt;
        .. &amp;quot; numArgs:&amp;quot; .. tostring(#args)&lt;br /&gt;
        .. &amp;quot; arg1:&amp;quot; .. tostring(args[1])&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
addDebugHook( &amp;quot;preFunction&amp;quot;, onPreFunction)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example adds a hook which will only be triggered for the named functions&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addDebugHook( &amp;quot;preFunction&amp;quot;, onPreFunction, {&amp;quot;setElementPosition&amp;quot;,&amp;quot;loadstring&amp;quot;} )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Changelog==&lt;br /&gt;
{{ChangelogHeader}}&lt;br /&gt;
{{ChangelogItem|1.3.5-9.06054|Added clientside}}&lt;br /&gt;
{{ChangelogItem|1.3.5-9.06142|Added option to restrict to specified functions and events }}&lt;br /&gt;
{{ChangelogItem|1.5.2-9.07957|Added option to skip original function/event &amp;lt;br/&amp;gt;Added ability to hook addDebugHook and removeDebugHook }}&lt;br /&gt;
{{ChangelogItem|1.5.5-9.11856|Added pre/postEventFunction hooks }}&lt;br /&gt;
{{ChangelogItem|1.6.0-9.22741|Removed ability to skip 'addDebugHook' }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Utility functions}}&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=82010</id>
		<title>User:DColeman/RU:Безопасность скриптов</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=82010"/>
		<updated>2025-05-23T14:26:25Z</updated>

		<summary type="html">&lt;p&gt;DColeman: /* Обнаружение и обезвреживание бэкдоров и читов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Информация по памяти клиента==&lt;br /&gt;
&lt;br /&gt;
Начиная с самых основ:&lt;br /&gt;
* Вы должны понимать, что все, что вы храните на стороне клиента, включая .lua файлы, находится под риском. Любая конфиденциальная или критическая информация, которая как-либо оказывается на клиентской стороне (ПК игрока), может быть прочитана и/или изменена чит-клиентами.&lt;br /&gt;
* Чтобы сохранить конфиденциальную информацию и логику скрипта в секрете - используйте серверную сторону.&lt;br /&gt;
* Обратите внимание, что скрипты, отмеченные как общие ('''shared''') также действуют как '''клиентские''', что означает, что все вышеперечисленное также применяется и к ним. Например, объявление скрипта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;shared&amp;quot;/&amp;gt; &amp;lt;!-- этот скрипт будет запущен и на сервере и на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое, что и объявление:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;client&amp;quot;/&amp;gt; &amp;lt;!-- объявляем скрипт на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;server&amp;quot;/&amp;gt; &amp;lt;!-- делаем то же самое, но на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Дополнительный слой защиты==&lt;br /&gt;
&lt;br /&gt;
Чтобы значительно ''усложнить жизнь*'' тем, у кого есть плохие намерения по отношению к вашему серверу, вы можете использовать аттрибут cache (и/или [https://luac.mtasa.com/ скомпилированные lua скрипты (также известные как Luac) с '''3''' уровнем дополнительной обфускации] - [https://wiki.multitheftauto.com/wiki/Lua_compilation_API API]), доступный в [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], вместе с настройкой встроенного античита МТА с включением SD (специальными кодами обнаружения), для дополнительной информации смотрите [https://wiki.multitheftauto.com/wiki/Anti-cheat_guide гайд по античиту].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;shared.lua&amp;quot; type=&amp;quot;shared&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''Усложнить жизнь'' '''не означает &amp;quot;сделать невозможным&amp;quot;''' получение вашего клиентского Lua кода, это означает, что '''большинство''' людей не смогут просмотреть ваши .lua файлы - тех, кто ищет логические ошибки (баги) или отсутствующие/некорректные проверки безопасности.&lt;br /&gt;
* Может использоваться на '''client''' и '''shared''' типах скриптов (не имеет эффекта на серверных скриптах).&lt;br /&gt;
* Это не удалит Lua файлы, которые были загружены ранее.&lt;br /&gt;
&lt;br /&gt;
==Обнаружение и обезвреживание бэкдоров и читов==&lt;br /&gt;
'''Чтобы минимизировать (или исключить) урон, причиненный Lua скриптами:'''&lt;br /&gt;
* Всегда обновляйте ваш сервер до самой актуальной версии, вы можете [https://nightly.multitheftauto.com/ загрузить новейшие сборки сервера отсюда.] С информацией по определенным сборкам можно ознакомиться [https://buildinfo.multitheftauto.com/ здесь.]&lt;br /&gt;
* Всегда обновляйте ресурсы, установленные на сервере, до самой актуальной версии, вы можете [https://github.com/multitheftauto/mtasa-resources загрузить новейшие (стандартные) ресурсы из репозитория GitHub.] Они часто содержат обновления безопасности, которые влияют на устойчивость вашего сервера к атакам.&lt;br /&gt;
* Убедитесь, что [https://wiki.multitheftauto.com/wiki/Access%20Control%20List ACL (список контроля доступа)] правильно сконфигурирован - он поможет заблокировать ресурсам некоторые потенциально опасные функции.&lt;br /&gt;
* Никогда не выдавайте админ-права ресурсам (включая карты), полученным из неизвестных источников.&lt;br /&gt;
* Перед запуском недоверенного ресурса, изучите:&lt;br /&gt;
** Его [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], на наличие возможных скрытых скриптов, которые скрываются под видом файлов с другими расширениями.&lt;br /&gt;
** Его исходный код, на наличие вредоносной логики.&lt;br /&gt;
* Не запускайте и не используйте скомпилированные ресурсы (скрипты), в легитимности которых вы не уверены.&lt;br /&gt;
&lt;br /&gt;
'''Чтобы минимизировать урон, причиненный читером, зашедшим на сервер:'''&lt;br /&gt;
* При создании скриптов '''никогда не доверяйте данным, полученным со стороны клиента'''.&lt;br /&gt;
* При просмотре скриптов на наличие дыр безопасности, сверяйтесь с данными, поступающими от клиента, которому можно доверять.&lt;br /&gt;
* Отправлены могут быть любые данные, поэтому любые серверные скрипты, которые коммуницируют с клиентскими и получают информацию от игроков, '''должны проверять информацию на корректность''' перед дальнейшим использованием. Подделка данных чаще всего происходит с помощью [[setElementData]] и [[triggerServerEvent]].&lt;br /&gt;
* Вы не должны полагаться только на [https://wiki.multitheftauto.com/wiki/Serial серийный номер] игрока, когда он используется для критических действий (авто-входа/администраторских действий). '''Не гарантируется, что серийный номер игрока уникален и не подделан'''. Это причина, почему вы должны '''поместить его за''' системой аккаунтов, в качестве '''важного аутентификационного фактора'''.&lt;br /&gt;
* Серверная логика '''не может быть обойдена''' (если только сервер не взломан или в коде нет ошибки, но это совсем другой сценарий.) - '''используйте это в своих интересах'''. В большинстве случаев, вы можете реализовать проверки безопасности только на серверной стороне, не привлекая клиент.&lt;br /&gt;
* Следование аксиоме '''''“Все параметры, включая source могут быть подделаны и им нельзя доверять. Глобальной переменной client можно доверять.”''''' дает вам уверенность в том, что игрок, к которому вы обращаетесь (через переменную '''client''') '''именно тот, кто вызвал событие'''. Это защищает вас от тех ситуаций, когда читер может вызывать, например, админские события (например, кик/бан игрока), указывая реального администратора ('''второй''' аргумент '''[[triggerServerEvent]]'''), или вызывать события за других игроков (будто они вызвали их, но в реальности это сделал читер) - всего лишь из-за использования некорректной переменной. Чтобы убедиться, что вы это поняли, обратимся к коду:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЭТОТ КОД - ОН АБСОЛЮТНО НЕВЕРНЫЙ И НЕБЕЗОПАСНЫЙ&lt;br /&gt;
	ПРОБЛЕМА: ИСПОЛЬЗУЯ ПЕРЕМЕННУЮ 'source' В hasObjectPermissionTo ВЫ ОСТАВЛЯЕТЕ ДЫРУ ДЛЯ ЧИТЕРОВ&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerWrongAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(source, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- эксплойт находится здесь...&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerWrongAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerWrongAdminEvent&amp;quot;, root, onServerWrongAdminEvent)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	onServerCorrectAdminEvent ПРЕКРАСНО ЗАЩИЩЕН, ТАК, КАК ДОЛЖНО БЫТЬ&lt;br /&gt;
	ЗДЕСЬ НЕТ ПРОБЛЕМ: МЫ ИСПОЛЬЗУЕМ 'client' В hasObjectPermissionTo, ЧТО ЗАЩИЩАЕТ НАС ОТ ПОДМЕНЫ ИГРОКА, КОТОРЫЙ ВЫЗВАЛ СОБЫТИЕ&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
function onServerCorrectAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(client, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- если игрок имеет права&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerCorrectAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerCorrectAdminEvent&amp;quot;, root, onServerCorrectAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing setElementData==&lt;br /&gt;
&lt;br /&gt;
* You should refrain from using [[element data]] everywhere, it should be only used when really necessary. It is advised to replace it with [[triggerClientEvent]] instead.&lt;br /&gt;
* If you still insist on using it, it is recommended to set '''4th''' argument in [[setElementData]] to '''false''' (disabling sync for this, certain data) and use subscriber mode - [[addElementDataSubscriber]], for both security &amp;amp; performance reasons described in [https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent event section.]&lt;br /&gt;
{{Important Note|Disabling sync however, doesn't fully protect data key. It would be still vulnerable by default, but in this case you are not leaving it in plain sight. Using [[getAllElementData]] or digging in RAM wouldn't expose it, since it won't be synced to client in first place. Therefore, it needs to be added to anti-cheat as well.}} &lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of basic element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = tostring(sourceElement) -- element which received data&lt;br /&gt;
	local logOldValue = tostring(oldValue) -- old value&lt;br /&gt;
	local logNewValue = tostring(newValue) -- new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;=======================================\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;=======================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	local logVisibleTo = root -- specify who will see this log in console, in this case each player connected to server&lt;br /&gt;
	local hadData = (oldValue ~= nil) -- check if element had such data before&lt;br /&gt;
&lt;br /&gt;
	if (hadData) then -- if element had such data before&lt;br /&gt;
		setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
	else&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it completely&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	outputConsole(logText, logVisibleTo) -- print it to console&lt;br /&gt;
&lt;br /&gt;
	return true -- all success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeBasicAC(dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not go further&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkSpecialThing = (dataKey == &amp;quot;special_thing&amp;quot;) -- compare whether dataKey matches &amp;quot;special_thing&amp;quot;&lt;br /&gt;
	local checkFlagWaving = (dataKey == &amp;quot;flag_waving&amp;quot;) -- compare whether dataKey matches &amp;quot;flag_waving&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if (checkSpecialThing) then -- if it does, do our security checks&lt;br /&gt;
		local invalidElement = (client ~= source) -- verify whether source element is different from player which changed data&lt;br /&gt;
&lt;br /&gt;
		if (invalidElement) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;special_thing&amp;quot; can only be set for player himself&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkFlagWaving) then -- if it does, do our security checks&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(client) -- get player's current vehicle&lt;br /&gt;
		local invalidVehicle = (playerVehicle ~= source) -- verify whether source element is different from player's vehicle&lt;br /&gt;
&lt;br /&gt;
		if (invalidVehicle) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;flag_waving&amp;quot; can only be set for player's own vehicle&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeBasicAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of advanced element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan, allowOnlyProtectedKeys to true (as per default configuration)&lt;br /&gt;
	If allowOnlyProtectedKeys is enabled, do not forget to add every client-side element data key to protectedKeys table - otherwise you will face false-positives&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (handleDataChange) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	[&amp;quot;keyName&amp;quot;] = { -- name of key which would be protected&lt;br /&gt;
		onlyForPlayerHimself = true, -- enabling this (true) will make sure that this element data key can only be set on player who synced it (ignores onlyForOwnPlayerVeh and allowForElements), use false/nil to disable this&lt;br /&gt;
		onlyForOwnPlayerVeh = false, -- enabling this (true) will make sure that this element data key can only be set on player's current vehicle who synced it (ignores allowForElements), use false/nil to disable this&lt;br /&gt;
		allowForElements = { -- restrict this key for certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
			[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = { -- restrict this key for certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 32}, -- if value is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
		allowedTableLength = {1, 64}, -- if value is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
		allowedNumberRange = {1, 128}, -- if value is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
	}&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering element data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local allowOnlyProtectedKeys = true -- disallow (remove by using removeElementData) every element data besides those given in protectedKeys table; in case someone wanted to flood server with garbage keys, which would be kept in memory until server restart or manual remove - setElementData(source, key, nil) won't remove it; it has to be removeElementData&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
local protectedKeys = {&lt;br /&gt;
	[&amp;quot;vehicleNumber&amp;quot;] = { -- we want vehicleNumber to be set only on vehicles, with stricte numbers in range of 1-100&lt;br /&gt;
		allowForElements = {&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedNumberRange = {1, 100},&lt;br /&gt;
	},&lt;br /&gt;
	[&amp;quot;personalVehData&amp;quot;] = { -- we want be able to set personalVehData only on current vehicle, also it should be a string with length between 1-24&lt;br /&gt;
		onlyForOwnPlayerVeh = true,&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 24},&lt;br /&gt;
	},&lt;br /&gt;
	-- perform security checks on keys stored in this table, in a convenient way&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of element which received data&lt;br /&gt;
	local logOldValue = inspect(oldValue) -- in-depth view of old value&lt;br /&gt;
	local logNewValue = inspect(newValue) -- in-depth view of new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Fail reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (forceRemove) then -- we don't want this element data key to exist at all&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it&lt;br /&gt;
&lt;br /&gt;
		return true -- return success and stop here, we don't need further checks&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
&lt;br /&gt;
	return true -- return success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function handleDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from protectedKeys), false otherwise&lt;br /&gt;
	local protectedKey = protectedKeys[dataKey] -- look up whether key changed is stored in protectedKeys table&lt;br /&gt;
&lt;br /&gt;
	if (not protectedKey) then -- if it's not&lt;br /&gt;
&lt;br /&gt;
		if (allowOnlyProtectedKeys) then -- if we don't want garbage keys&lt;br /&gt;
			local failReason = &amp;quot;Key isn't present in protectedKeys&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = true -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return true -- this key isn't protected, let it through&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local onlyForPlayerHimself = protectedKey.onlyForPlayerHimself -- if key has &amp;quot;self-set&amp;quot; lock&lt;br /&gt;
	local onlyForOwnPlayerVeh = protectedKey.onlyForOwnPlayerVeh -- if key has &amp;quot;self-vehicle&amp;quot; lock&lt;br /&gt;
	local allowForElements = protectedKey.allowForElements -- if key has element type check&lt;br /&gt;
	local allowedDataTypes = protectedKey.allowedDataTypes -- if key has allowed data type check&lt;br /&gt;
&lt;br /&gt;
	if (onlyForPlayerHimself) then -- if &amp;quot;self-set&amp;quot; lock is active&lt;br /&gt;
		local matchingElement = (clientElement == sourceElement) -- verify whether player who set data is equal to element which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElement) then -- if it's not matching&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player himself&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (onlyForOwnPlayerVeh) then -- if &amp;quot;self-vehicle&amp;quot; lock is active&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(clientElement) -- get current vehicle of player which set data&lt;br /&gt;
		local matchingVehicle = (playerVehicle == sourceElement) -- check whether it matches the one which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingVehicle) then -- if it doesn't match&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player's own vehicle&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowForElements) then -- check if it's one of them&lt;br /&gt;
		local elementType = getElementType(sourceElement) -- get type of element whose data changed&lt;br /&gt;
		local matchingElementType = allowForElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElementType) then -- this isn't matching&lt;br /&gt;
			local failReason = &amp;quot;Invalid element type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowedDataTypes) then -- if there's allowed data types&lt;br /&gt;
		local valueType = type(newValue) -- get data type of value&lt;br /&gt;
		local matchingType = allowedDataTypes[valueType] -- check if it's one of allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingType) then -- if it's not then&lt;br /&gt;
			local failReason = &amp;quot;Invalid data type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedStringLength = protectedKey.allowedStringLength -- if key has specified string length check&lt;br /&gt;
		local dataString = (valueType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
		if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
			local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
			local stringLength = utf8.len(newValue) -- get length of data string&lt;br /&gt;
			local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedTableLength = protectedKey.allowedTableLength -- if key has table length check&lt;br /&gt;
		local dataTable = (valueType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
		if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
			local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
			local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
			local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
			local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
			for _, _ in pairs(newValue) do -- loop through whole table&lt;br /&gt;
				tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
				minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
				maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
				if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
					break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
				local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedNumberRange = protectedKey.allowedNumberRange -- if key has allowed number range check&lt;br /&gt;
		local dataNumber = (valueType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
		if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
			local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
			local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
			local matchingRange = (newValue &amp;gt;= minRange) and (newValue &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingRange) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this data key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeAdvancedAC(dataKey, oldValue, newValue) -- this event makes use of handleDataChange, the code was split for better readability&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not continue&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedChange = handleDataChange(client, source, dataKey, oldValue, newValue) -- run our security checks&lt;br /&gt;
&lt;br /&gt;
	if (approvedChange) then -- it's all cool and good&lt;br /&gt;
		return false -- we don't need further action&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(client, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(client, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeAdvancedAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing triggerServerEvent==&lt;br /&gt;
&lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player (client) '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
* Do '''not''' use the same name for your custom event as MTA native server events (which aren't remotely triggerable by default), e.g: '''onPlayerLogin'''; doing so would open door for cheaters manipulations.&lt;br /&gt;
* Be aware to which players event is sent via [[triggerClientEvent]]. For both security &amp;amp; performance reasons, admin like events should be received by admins only (to prevent confidential data being accessed), in the same time you shouldn't send each event to everyone on server (e.g: login success event which hides login panel for certain player). [[triggerClientEvent]] allows you to specify the event receiver as first (optional) argument. It defaults to [[root]], meaning if you don't specify it, it will be sent to everyone connected to server - even those who are still downloading server cache (which results in ''Server triggered clientside event eventName, but event is not added clientside.''). You can either pass player element or table with receivers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playersToReceiveEvent = {player1, player2, player3} -- each playerX is player element&lt;br /&gt;
&lt;br /&gt;
triggerClientEvent(playersToReceiveEvent, ...) -- do not forget to fill the latter part of arguments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of anti-cheat function designed for events, used for data validation for both normal, and admin events which are called from client-side.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan to true (as per default configuration)&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (processServerEventData) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	checkACLGroup = { -- check whether player who called event belongs to at least one group below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;Admin&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkPermissions = { -- check whether player who called event has permission to at least one thing below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;function.kickPlayer&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkEventData = {&lt;br /&gt;
		{&lt;br /&gt;
			debugData = &amp;quot;source&amp;quot;, -- optional details for report shown in debug message&lt;br /&gt;
			eventData = source, -- data we want to verify&lt;br /&gt;
			equalTo = client, -- compare whether eventData == equalTo&lt;br /&gt;
			allowedElements = { -- restrict eventData to be certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
				[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedDataTypes = { -- restrict eventData to be certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
				[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedStringLength = {1, 32}, -- if eventData is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
			allowedTableLength = {1, 64}, -- if eventData is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
			allowedNumberRange = {1, 128}, -- if eventData is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering server event data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
function processServerEventData(clientElement, sourceElement, serverEvent, securityChecks) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from securityChecks), false otherwise&lt;br /&gt;
	if (not securityChecks) then -- if we haven't passed any security checks&lt;br /&gt;
		return true -- nothing to check, let code go further&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (not clientElement) then -- if client variable isn't available for some reason (although it should never happen)&lt;br /&gt;
		local failReason = &amp;quot;Client variable not present&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkACLGroup = securityChecks.checkACLGroup -- if there's any ACL groups to check&lt;br /&gt;
	local checkPermissions = securityChecks.checkPermissions -- if there's any permissions to check&lt;br /&gt;
	local checkEventData = securityChecks.checkEventData -- if there's any data checks&lt;br /&gt;
&lt;br /&gt;
	if (checkACLGroup) then -- let's check player ACL groups&lt;br /&gt;
		local playerAccount = getPlayerAccount(clientElement) -- get current account of player&lt;br /&gt;
		local guestAccount = isGuestAccount(playerAccount) -- if account is guest (meaning player is not logged in)&lt;br /&gt;
&lt;br /&gt;
		if (guestAccount) then -- it's the case&lt;br /&gt;
			local failReason = &amp;quot;Can't retrieve player login - guest account&amp;quot; -- reason shown in report&lt;br /&gt;
			local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
			reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local accountName = getAccountName(playerAccount) -- get name of player's current account&lt;br /&gt;
		local aclString = &amp;quot;user.&amp;quot;..accountName -- format it for further use in isObjectInACLGroup function&lt;br /&gt;
&lt;br /&gt;
		for groupID = 1, #checkACLGroup do -- iterate over table of given groups&lt;br /&gt;
			local groupName = checkACLGroup[groupID] -- get each group name&lt;br /&gt;
			local aclGroup = aclGetGroup(groupName) -- check if such group exists&lt;br /&gt;
&lt;br /&gt;
			if (not aclGroup) then -- it doesn't&lt;br /&gt;
				local failReason = &amp;quot;ACL group '&amp;quot;..groupName..&amp;quot;' is missing&amp;quot; -- reason shown in report&lt;br /&gt;
				local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
				reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local playerInACLGroup = isObjectInACLGroup(aclString, aclGroup) -- check if player belong to the group&lt;br /&gt;
&lt;br /&gt;
			if (playerInACLGroup) then -- yep, it's the case&lt;br /&gt;
				return true -- so it's a success&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Player doesn't belong to any given ACL group&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkPermissions) then -- check if player has at least one desired permission&lt;br /&gt;
		local allowedByDefault = false -- does he have access by default&lt;br /&gt;
&lt;br /&gt;
		for permissionID = 1, #checkPermissions do -- iterate over all permissions&lt;br /&gt;
			local permissionName = checkPermissions[permissionID] -- get permission name&lt;br /&gt;
			local hasPermission = hasObjectPermissionTo(clientElement, permissionName, allowedByDefault) -- check whether player is allowed to perform certain action&lt;br /&gt;
&lt;br /&gt;
			if (hasPermission) then -- if player has access&lt;br /&gt;
				return true -- one is available (and enough), server won't bother to check others (as return keywords also breaks loop)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Not enough permissions&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkEventData) then -- if there is some data to verify&lt;br /&gt;
&lt;br /&gt;
		for dataID = 1, #checkEventData do -- iterate over each of data&lt;br /&gt;
			local dataToCheck = checkEventData[dataID] -- get each data set&lt;br /&gt;
			local eventData = dataToCheck.eventData -- this is the one we'll be verifying&lt;br /&gt;
			local equalTo = dataToCheck.equalTo -- we want to compare whether eventData == equalTo&lt;br /&gt;
			local allowedElements = dataToCheck.allowedElements -- check whether is element, and whether belongs to certain element types&lt;br /&gt;
			local allowedDataTypes = dataToCheck.allowedDataTypes -- do we restrict data to be certain type?&lt;br /&gt;
			local debugData = dataToCheck.debugData -- additional helper data&lt;br /&gt;
			local debugText = debugData and &amp;quot; (&amp;quot;..debugData..&amp;quot;)&amp;quot; or &amp;quot;&amp;quot; -- if it's present, format it nicely&lt;br /&gt;
&lt;br /&gt;
			if (equalTo) then -- equal check exists&lt;br /&gt;
				local matchingData = (eventData == equalTo) -- compare whether those two values are equal&lt;br /&gt;
&lt;br /&gt;
				if (not matchingData) then -- they aren't&lt;br /&gt;
					local failReason = &amp;quot;Data isn't equal @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedElements) then -- we do check whether is an element, and belongs to at least one given in the list&lt;br /&gt;
				local validElement = isElement(eventData) -- check if it's actual element&lt;br /&gt;
&lt;br /&gt;
				if (not validElement) then -- it's not&lt;br /&gt;
					local failReason = &amp;quot;Data isn't element @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local elementType = getElementType(eventData) -- it's element, so we want to know it's type&lt;br /&gt;
				local matchingElementType = allowedElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingElementType) then -- it's not allowed&lt;br /&gt;
					local failReason = &amp;quot;Invalid element type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedDataTypes) then -- let's check allowed data types&lt;br /&gt;
				local dataType = type(eventData) -- get data type&lt;br /&gt;
				local matchingType = allowedDataTypes[dataType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingType) then -- it isn't&lt;br /&gt;
					local failReason = &amp;quot;Invalid data type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedStringLength = dataToCheck.allowedStringLength -- if data has string length check&lt;br /&gt;
				local dataString = (dataType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
				if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
					local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
					local stringLength = utf8.len(eventData) -- get length of data string&lt;br /&gt;
					local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedTableLength = dataToCheck.allowedTableLength -- if data has table length check&lt;br /&gt;
				local dataTable = (dataType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
				if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
					local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
					local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
					local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
					local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
					for _, _ in pairs(eventData) do -- loop through whole table&lt;br /&gt;
						tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
						minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
						maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
						if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
							break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
						local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedNumberRange = dataToCheck.allowedNumberRange -- if data has number range check&lt;br /&gt;
				local dataNumber = (dataType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
				if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
					local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
					local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
					local matchingRange = (eventData &amp;gt;= minRange) and (eventData &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingRange) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this event call&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- helper function to log and handle accidents&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view player which called event&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of source element&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected event abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Event: &amp;quot;..serverEvent..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect or skipPunishment) then -- we don't want to punish player for some reason&lt;br /&gt;
		return true -- stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(clientElement, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(clientElement, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- all done, report success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onServerEvent(clientData)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local dataToPass = 10&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerEvent&amp;quot;, localPlayer, dataToPass)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = clientData, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;number&amp;quot;] = true, -- we want it to be only number&lt;br /&gt;
					},&lt;br /&gt;
					allowedNumberRange = {1, 100}, -- in range of 1 to 100&lt;br /&gt;
					debugData = &amp;quot;clientData&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerEvent&amp;quot;, root, onServerEvent)&lt;br /&gt;
&lt;br /&gt;
function onServerAdminEvent(playerToBan)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server admin event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local playerToBan = getPlayerFromName(&amp;quot;playerToBan&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerAdminEvent&amp;quot;, localPlayer, playerToBan)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerAdminEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkACLGroup = { -- we need to check whether player who called event belongs to ACL groups&lt;br /&gt;
				&amp;quot;Admin&amp;quot;, -- in this case admin group&lt;br /&gt;
			},&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = playerToBan, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;player&amp;quot;] = true, -- we want it to be player&lt;br /&gt;
					},&lt;br /&gt;
					debugData = &amp;quot;playerToBan&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerAdminEvent&amp;quot;, root, onServerAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing server-only events==&lt;br /&gt;
* It is very important to '''disable remote triggering''' ability in [https://wiki.multitheftauto.com/wiki/AddEvent addEvent], to prevent calling server-side only events from client-side.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
This example shows how you should make event server-side only.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onServerSideOnlyEvent()&lt;br /&gt;
	-- do some server-side stuff&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerSideOnlyEvent&amp;quot;, false) -- set second argument (allowRemoteTriger) to false, so it can't be called from client&lt;br /&gt;
addEventHandler(&amp;quot;onServerSideOnlyEvent&amp;quot;, root, onServerSideOnlyEvent) -- associate our event with function onServerSideOnlyEvent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing projectiles &amp;amp; explosions==&lt;br /&gt;
This section (and '''code''' is '''work in progress''') - '''use at your own risk'''.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile ammo tracker:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerProjectileAmmo = {} -- store player-held weapons ammo here&lt;br /&gt;
local playerWeaponsToTrack = { -- keep track of ammo for certain player-held weapon types, basically those which create projectile; [weaponID] = weaponSlot&lt;br /&gt;
	[16] = 8, -- grenade&lt;br /&gt;
	[17] = 8, -- teargas&lt;br /&gt;
	[18] = 8, -- molotov&lt;br /&gt;
	[35] = 7, -- rocket launcher&lt;br /&gt;
	[36] = 7, -- rocket launcher (heat-seeking)&lt;br /&gt;
	[39] = 8, -- satchel charge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for weaponID, weaponSlot in pairs(playerWeaponsToTrack) do&lt;br /&gt;
		local weaponInSlot = getPedWeapon(playerElement, weaponSlot)&lt;br /&gt;
		local weaponTotalAmmo = getPedTotalAmmo(playerElement, weaponSlot)&lt;br /&gt;
		local weaponHasAmmo = (weaponTotalAmmo &amp;gt; 0)&lt;br /&gt;
		local weaponMatching = (weaponInSlot == weaponID)&lt;br /&gt;
		local updateWeaponAmmo = (weaponMatching and weaponHasAmmo)&lt;br /&gt;
&lt;br /&gt;
		if (updateWeaponAmmo) then&lt;br /&gt;
			local storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			local newWeaponAmmo = (updateWeaponAmmo and weaponTotalAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
			if (not storedProjectileAmmo) then&lt;br /&gt;
				playerProjectileAmmo[playerElement] = {}&lt;br /&gt;
				storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			storedProjectileAmmo[weaponID] = newWeaponAmmo&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function hasPlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
	local projectileAmmo = (projectileData and projectileData[projectileWeapon])&lt;br /&gt;
&lt;br /&gt;
	return projectileAmmo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectileData) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileWeaponAmmo = playerProjectileData[projectileWeapon]&lt;br /&gt;
	local tempProjectileAmmo = (projectileWeaponAmmo - 1)&lt;br /&gt;
	local newProjectileAmmo = (tempProjectileAmmo &amp;gt; 0 and tempProjectileAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectileData[projectileWeapon] = newProjectileAmmo&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerWeaponSwitchAntiCheat(previousWeaponID, currentWeaponID)&lt;br /&gt;
	setTimer(updateProjectileAmmoForPlayer, 50, 1, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWeaponSwitch&amp;quot;, root, onPlayerWeaponSwitchAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onResourceStartAntiCheat()&lt;br /&gt;
	local playersTable = getElementsByType(&amp;quot;player&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	for playerID = 1, #playersTable do&lt;br /&gt;
		local playerElement = playersTable[playerID]&lt;br /&gt;
&lt;br /&gt;
		updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onResourceStart&amp;quot;, resourceRoot, onResourceStartAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerWastedQuitAntiCheat()&lt;br /&gt;
	playerProjectileAmmo[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerCreatedProjectiles = {} -- store count of legitimately created player projectiles; [playerElement] = {[projectileType] = activeProjectiles}&lt;br /&gt;
local projectileTypes = {&lt;br /&gt;
	[16] = { -- Grenade&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[17] = { -- Teargas&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[17] = true, -- teargas&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[18] = { -- Molotov&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[19] = { -- Rocket&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[425] = true, -- hunter&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[20] = { -- Rocket (heat-seeking)&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[21] = { -- Airbomb&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[39] = { -- Satchel charge&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[39] = true, -- satchel charge&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[58] = { -- Hydra flare&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportProjectileAbnormality(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	local projectileSyncer = inspect(playerElement)&lt;br /&gt;
	local projectilePosition = projectileX..&amp;quot;, &amp;quot;..projectileY..&amp;quot;, &amp;quot;..projectileZ&lt;br /&gt;
	local projectileZoneName = getZoneName(projectileX, projectileY, projectileZ, false)&lt;br /&gt;
	local projectileCityName = getZoneName(projectileX, projectileY, projectileZ, true)&lt;br /&gt;
	local projectileLog =&lt;br /&gt;
		&amp;quot;* Detected projectile abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile syncer: &amp;quot;..projectileSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile type: &amp;quot;..projectileType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile position: &amp;quot;..projectilePosition.. &amp;quot; (&amp;quot;..projectileZoneName..&amp;quot;, &amp;quot;..projectileCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(projectileLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processProjectileChecks(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
	local projectileAllowed = projectileData.projectileAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not projectileAllowed) then&lt;br /&gt;
		return false, &amp;quot;Projectile not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileAllowedWeapons = projectileData.projectileAllowedWeapons&lt;br /&gt;
&lt;br /&gt;
	if (projectileAllowedWeapons) then&lt;br /&gt;
		local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
		local allowedWeapon = projectileAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedWeapon) then&lt;br /&gt;
			return false, &amp;quot;Player is not holding correct weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
		local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
		if (not playerHasAmmo) then&lt;br /&gt;
			return false, &amp;quot;Player doesn't have ammo for weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerVehicle) then&lt;br /&gt;
		local projectileAllowedVehicles = projectileData.projectileAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
		if (projectileAllowedVehicles) then&lt;br /&gt;
			local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
			local allowedVehicle = projectileAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedVehicle) then&lt;br /&gt;
				return false, &amp;quot;Player is not inside allowed vehicles&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
			local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
			if (not playerDriver) then&lt;br /&gt;
				return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Player in vehicle&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxDistanceFromCreator = projectileData.projectileMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToProjectile = getDistanceBetweenPoints3D(playerX, playerY, playerZ, projectileX, projectileY, projectileZ)&lt;br /&gt;
		local matchingProjectileDistance = (distanceToProjectile &amp;lt;= projectileMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileDistance) then&lt;br /&gt;
			return false, &amp;quot;Projectile distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxVelocity = projectileData.projectileMaxVelocity&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxVelocity) then&lt;br /&gt;
		local projectileVelocity = (projectileVX ^ 2 + projectileVY ^ 2 + projectileVZ ^ 2)&lt;br /&gt;
		local projectileSpeed = math.sqrt(projectileVelocity)&lt;br /&gt;
		local matchingProjectileVelocity = (projectileSpeed &amp;lt;= projectileMaxVelocity)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileVelocity) then&lt;br /&gt;
			return false, &amp;quot;Projectile velocity mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trackPlayerProjectile(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		playerCreatedProjectiles[playerElement] = {}&lt;br /&gt;
		playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID] or 0&lt;br /&gt;
	local newProjectilesCount = (projectilesByType + 1)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
&lt;br /&gt;
	if (not projectileData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileBehaviour = projectileData.projectileOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not projectileBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = projectileBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = projectileBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = projectileBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID]&lt;br /&gt;
&lt;br /&gt;
	if (not projectilesByType) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local tempProjectilesCount = (projectilesByType - 1)&lt;br /&gt;
	local newProjectilesCount = (tempProjectilesCount &amp;gt; 0 and tempProjectilesCount or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerProjectileCreationAntiCheat(projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local approvedProjectile, detectionCode = processProjectileChecks(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedProjectile) then&lt;br /&gt;
		trackPlayerProjectile(source, projectileType)&lt;br /&gt;
&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportProjectileAbnormality(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerProjectileCreation&amp;quot;, root, onPlayerProjectileCreationAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerQuitAntiCheat()&lt;br /&gt;
	playerCreatedProjectiles[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Explosions handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local rocketInstantExplosionDistanceThreshold = 1.55 -- distance below which only explosion will be created (and not rocket projectile, hence not calling onPlayerProjectileCreation); do not change it&lt;br /&gt;
local explosionTypes = { -- more specific info on explosion, and whether it is allowed&lt;br /&gt;
	[0] = { -- Grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[1] = { -- Molotov&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[2] = { -- Rocket&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[3] = { -- Rocket weak&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[4] = { -- Car&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[5] = { -- Car quick&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[6] = { -- Boat&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[7] = { -- Heli&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[8] = { -- Mine&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[9] = { -- Object&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[10] = { -- Tank grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionMaxDistanceFromCreator = 80,&lt;br /&gt;
		explosionAllowedVehicles = {&lt;br /&gt;
			[432] = true,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	[11] = { -- Small&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[12] = { -- Tiny&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportExplosionAbnormality(playerElement, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	local explosionSyncer = inspect(playerElement)&lt;br /&gt;
	local explosionType = explosionType&lt;br /&gt;
	local explosionPosition = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionZoneName = getZoneName(explosionX, explosionY, explosionZ, false)&lt;br /&gt;
	local explosionCityName = getZoneName(explosionX, explosionY, explosionZ, true)&lt;br /&gt;
	local explosionLog =&lt;br /&gt;
		&amp;quot;* Detected explosion abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion syncer: &amp;quot;..explosionSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion type: &amp;quot;..explosionType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion position: &amp;quot;..explosionPosition.. &amp;quot; (&amp;quot;..explosionZoneName..&amp;quot;, &amp;quot;..explosionCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(explosionLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processExplosionChecks(playerElement, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
	local explosionAllowed = explosionData.explosionAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not explosionAllowed) then&lt;br /&gt;
		return false, &amp;quot;Explosion type not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rocketExplosion = (explosionType == 2)&lt;br /&gt;
&lt;br /&gt;
	if (rocketExplosion) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local instantExplosion = (distanceToExplosion &amp;lt; rocketInstantExplosionDistanceThreshold)&lt;br /&gt;
&lt;br /&gt;
		if (instantExplosion) then&lt;br /&gt;
			local explosionAllowedWeapons = explosionData.explosionAllowedWeapons&lt;br /&gt;
			local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
			local allowedWeapon = explosionAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedWeapon) then&lt;br /&gt;
				return false, &amp;quot;Player is not holding rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
			local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
			if (not playerHasAmmo) then&lt;br /&gt;
				return false, &amp;quot;Player doesn't have ammo for rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedProjectiles = explosionData.explosionAllowedProjectiles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedProjectiles) then&lt;br /&gt;
&lt;br /&gt;
		for projectileID, _ in pairs(explosionAllowedProjectiles) do&lt;br /&gt;
			local atleastOneProjectile = decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
&lt;br /&gt;
			if (atleastOneProjectile) then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Explosion created without respective projectile&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedVehicles = explosionData.explosionAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedVehicles) then&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
		if (not playerVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is not in vehicle&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
		local allowedVehicle = explosionAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is inside not allowed vehicles&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
		local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
		if (not playerDriver) then&lt;br /&gt;
			return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionMaxDistanceFromCreator = explosionData.explosionMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (explosionMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local matchingExplosionDistance = (distanceToExplosion &amp;lt; explosionMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingExplosionDistance) then&lt;br /&gt;
			return false, &amp;quot;Explosion distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
&lt;br /&gt;
	if (not explosionData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionBehaviour = explosionData.explosionOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not explosionBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = explosionBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = explosionBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = explosionBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onExplosionAntiCheat(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local serverSyncExplosion = (source == root)&lt;br /&gt;
&lt;br /&gt;
	if (serverSyncExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedExplosion, detectionCode = processExplosionChecks(source, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportExplosionAbnormality(source, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosionAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Handling non-registered events==&lt;br /&gt;
&lt;br /&gt;
See: [[onPlayerTriggerInvalidEvent]]&lt;br /&gt;
&lt;br /&gt;
==Handling events spam==&lt;br /&gt;
See: [[onPlayerTriggerEventThreshold]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=AddDebugHook&amp;diff=81990</id>
		<title>AddDebugHook</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=AddDebugHook&amp;diff=81990"/>
		<updated>2025-05-11T06:39:33Z</updated>

		<summary type="html">&lt;p&gt;DColeman: We cannot skip 'addDebugHook' anymore&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{Server client function}}&lt;br /&gt;
{{New feature/item|3.0136|1.3|5939|&lt;br /&gt;
This function allows tracing of MTA functions and events. It should only be used when debugging scripts as it may degrade script performance.&lt;br /&gt;
&lt;br /&gt;
Debug hooks are not recursive, so functions and events triggered inside the hook callback will not be traced.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool addDebugHook ( string hookType, function callbackFunction [, table nameList ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Required Arguments=== &lt;br /&gt;
*'''hookType:''' The type of hook to add. This can be:&lt;br /&gt;
** preEvent&lt;br /&gt;
** postEvent&lt;br /&gt;
** preFunction&lt;br /&gt;
** postFunction&lt;br /&gt;
{{New feature/item|3.0158|1.5.5|11856|&lt;br /&gt;
* preEventFunction&lt;br /&gt;
* postEventFunction&lt;br /&gt;
}}&lt;br /&gt;
*'''callbackFunction:''' The function to call&lt;br /&gt;
** Returning the string &amp;quot;skip&amp;quot; from the callback function will cause the original function/event to be skipped&lt;br /&gt;
&lt;br /&gt;
===Optional Arguments=== &lt;br /&gt;
*'''nameList:''' Table of strings for restricting which functions and events the hook will be triggered on&lt;br /&gt;
** addDebugHook and removeDebugHook will only be hooked if they are specified in the name list&lt;br /&gt;
&lt;br /&gt;
===Returns===&lt;br /&gt;
Returns ''true'' if the hook was successfully added, or ''false'' otherwise.&lt;br /&gt;
&lt;br /&gt;
==Callback parameters==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
string preFunction( resource sourceResource, string functionName, bool isAllowedByACL, string luaFilename, int luaLineNumber, ...functionArguments )&lt;br /&gt;
       postFunction( resource sourceResource, string functionName, bool isAllowedByACL, string luaFilename, int luaLineNumber, ...functionArguments )&lt;br /&gt;
string preEvent( resource sourceResource, string eventName, element eventSource, element eventClient, string luaFilename, int luaLineNumber, ...eventArguments )&lt;br /&gt;
       postEvent( resource sourceResource, string eventName, element eventSource, element eventClient, string luaFilename, int luaLineNumber, ...eventArguments )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{New feature/item|3.0158|1.5.5|11856|&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
string preEventFunction ( resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, resource functionResource, string functionFilename, int functionLineNumber, ...eventArgs )&lt;br /&gt;
       postEventFunction ( resource eventResource, string eventName, element eventSource, element eventClient, string eventFilename, int eventLineNumber, resource functionResource, string functionFilename, int functionLineNumber, ...eventArgs )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
This example will dump info about all triggered events:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onPreEvent( sourceResource, eventName, eventSource, eventClient, luaFilename, luaLineNumber, ... )&lt;br /&gt;
    local args = { ... }&lt;br /&gt;
    local srctype = eventSource and getElementType(eventSource)&lt;br /&gt;
    local resname = sourceResource and getResourceName(sourceResource)&lt;br /&gt;
    local plrname = eventClient and getPlayerName(eventClient)&lt;br /&gt;
    outputDebugString( &amp;quot;preEvent&amp;quot;&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(resname)&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(eventName)&lt;br /&gt;
        .. &amp;quot; source:&amp;quot; .. tostring(srctype)&lt;br /&gt;
        .. &amp;quot; player:&amp;quot; .. tostring(plrname)&lt;br /&gt;
        .. &amp;quot; file:&amp;quot; .. tostring(luaFilename)&lt;br /&gt;
        .. &amp;quot;(&amp;quot; .. tostring(luaLineNumber) .. &amp;quot;)&amp;quot;&lt;br /&gt;
        .. &amp;quot; numArgs:&amp;quot; .. tostring(#args)&lt;br /&gt;
        .. &amp;quot; arg1:&amp;quot; .. tostring(args[1])&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
addDebugHook( &amp;quot;preEvent&amp;quot;, onPreEvent )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This example will dump info about all called MTA functions:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onPreFunction( sourceResource, functionName, isAllowedByACL, luaFilename, luaLineNumber, ... )&lt;br /&gt;
    local args = { ... }&lt;br /&gt;
    local resname = sourceResource and getResourceName(sourceResource)&lt;br /&gt;
    outputDebugString( &amp;quot;preFunction&amp;quot;&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(resname)&lt;br /&gt;
        .. &amp;quot; &amp;quot; .. tostring(functionName)&lt;br /&gt;
        .. &amp;quot; allowed:&amp;quot; .. tostring(isAllowedByACL)&lt;br /&gt;
        .. &amp;quot; file:&amp;quot; .. tostring(luaFilename)&lt;br /&gt;
        .. &amp;quot;(&amp;quot; .. tostring(luaLineNumber) .. &amp;quot;)&amp;quot;&lt;br /&gt;
        .. &amp;quot; numArgs:&amp;quot; .. tostring(#args)&lt;br /&gt;
        .. &amp;quot; arg1:&amp;quot; .. tostring(args[1])&lt;br /&gt;
        )&lt;br /&gt;
end&lt;br /&gt;
addDebugHook( &amp;quot;preFunction&amp;quot;, onPreFunction)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example adds a hook which will only be triggered for the named functions&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addDebugHook( &amp;quot;preFunction&amp;quot;, onPreFunction, {&amp;quot;setElementPosition&amp;quot;,&amp;quot;loadstring&amp;quot;} )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Changelog==&lt;br /&gt;
{{ChangelogHeader}}&lt;br /&gt;
{{ChangelogItem|1.3.5-9.06054|Added clientside}}&lt;br /&gt;
{{ChangelogItem|1.3.5-9.06142|Added option to restrict to specified functions and events }}&lt;br /&gt;
{{ChangelogItem|1.5.2-9.07957|Added option to skip original function/event &amp;lt;br/&amp;gt;Added ability to hook addDebugHook and removeDebugHook }}&lt;br /&gt;
{{ChangelogItem|1.5.5-9.11856|Added pre/postEventFunction hooks }}&lt;br /&gt;
{{ChangelogItem|1.6.0-9.22741|Removed ability to skip 'addDebugHook' }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Utility functions}}&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81989</id>
		<title>User:DColeman/RU:Безопасность скриптов</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81989"/>
		<updated>2025-05-10T19:58:14Z</updated>

		<summary type="html">&lt;p&gt;DColeman: /* Дополнительный слой защиты */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Информация по памяти клиента==&lt;br /&gt;
&lt;br /&gt;
Начиная с самых основ:&lt;br /&gt;
* Вы должны понимать, что все, что вы храните на стороне клиента, включая .lua файлы, находится под риском. Любая конфиденциальная или критическая информация, которая как-либо оказывается на клиентской стороне (ПК игрока), может быть прочитана и/или изменена чит-клиентами.&lt;br /&gt;
* Чтобы сохранить конфиденциальную информацию и логику скрипта в секрете - используйте серверную сторону.&lt;br /&gt;
* Обратите внимание, что скрипты, отмеченные как общие ('''shared''') также действуют как '''клиентские''', что означает, что все вышеперечисленное также применяется и к ним. Например, объявление скрипта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;shared&amp;quot;/&amp;gt; &amp;lt;!-- этот скрипт будет запущен и на сервере и на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое, что и объявление:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;client&amp;quot;/&amp;gt; &amp;lt;!-- объявляем скрипт на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;server&amp;quot;/&amp;gt; &amp;lt;!-- делаем то же самое, но на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Дополнительный слой защиты==&lt;br /&gt;
&lt;br /&gt;
Чтобы значительно ''усложнить жизнь*'' тем, у кого есть плохие намерения по отношению к вашему серверу, вы можете использовать аттрибут cache (и/или [https://luac.mtasa.com/ скомпилированные lua скрипты (также известные как Luac) с '''3''' уровнем дополнительной обфускации] - [https://wiki.multitheftauto.com/wiki/Lua_compilation_API API]), доступный в [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], вместе с настройкой встроенного античита МТА с включением SD (специальными кодами обнаружения), для дополнительной информации смотрите [https://wiki.multitheftauto.com/wiki/Anti-cheat_guide гайд по античиту].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;shared.lua&amp;quot; type=&amp;quot;shared&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''Усложнить жизнь'' '''не означает &amp;quot;сделать невозможным&amp;quot;''' получение вашего клиентского Lua кода, это означает, что '''большинство''' людей не смогут просмотреть ваши .lua файлы - тех, кто ищет логические ошибки (баги) или отсутствующие/некорректные проверки безопасности.&lt;br /&gt;
* Может использоваться на '''client''' и '''shared''' типах скриптов (не имеет эффекта на серверных скриптах).&lt;br /&gt;
* Это не удалит Lua файлы, которые были загружены ранее.&lt;br /&gt;
&lt;br /&gt;
==Обнаружение и обезвреживание бэкдоров и читов==&lt;br /&gt;
'''Чтобы минимизировать (или исключить) урон, причиненный Lua скриптами:'''&lt;br /&gt;
* Всегда обновляйте ваш сервер до самой актуальной версии, вы можете [https://nightly.multitheftauto.com/ загрузить новейшие сборки сервера отсюда.] С информацией по определенным сборкам можно ознакомиться [https://buildinfo.multitheftauto.com/ здесь.]&lt;br /&gt;
* Всегда обновляйте ресурсы, установленные на сервере, до самой актуальной версии, вы можете [https://github.com/multitheftauto/mtasa-resources загрузить новейшие (стандартные) ресурсы из репозитория GitHub.] Они часто содержат обновления безопасности, которые влияют на устойчивость вашего сервера к атакам.&lt;br /&gt;
* Убедитесь, что [https://wiki.multitheftauto.com/wiki/Access%20Control%20List ACL (список контроля доступа)] правильно сконфигурирован - он поможет заблокировать ресурсам некоторые потенциально опасные функции.&lt;br /&gt;
* Никогда не выдавайте админ-права ресурсам (включая карты), полученным из неизвестных источников.&lt;br /&gt;
* Перед запуском недоверенного ресурса, изучите:&lt;br /&gt;
** Его [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], на наличие возможных скрытых скриптов, которые скрываются под видом файлов с другими расширениями.&lt;br /&gt;
** Его исходный код, на наличие вредоносной логики.&lt;br /&gt;
* Не запускайте и не используйте скомпилированные ресурсы (скрипты), в легитимности которых вы не уверены.&lt;br /&gt;
&lt;br /&gt;
'''Чтобы минимизировать урон, причиненный читером, зашедшим на сервер:'''&lt;br /&gt;
* При создании скриптов '''никогда не доверяйте данным, полученным со стороны клиента'''.&lt;br /&gt;
* При просмотре скриптов на наличие дыр безопасности, сверяйтесь с данными, поступающими от клиента, которому можно доверять.&lt;br /&gt;
* Отправлены могут быть любые данные, поэтому любые серверные скрипты, которые коммуницируют с клиентскими и получают информацию от игроков, '''должны проверять информацию на корректность''' перед дальнейшим использованием. Подделка данных чаще всего происходит с помощью [[setElementData]] и [[triggerServerEvent]].&lt;br /&gt;
* Вы не должны полагаться только на [https://wiki.multitheftauto.com/wiki/Serial серийный номер] игрока, когда они используются для критических действий (авто-входа/администраторских действий). '''Не гарантируется, что серийный номер игрока уникален и не подделан'''. Это причина, почему вы должны '''поместить его за''' системой аккаунтов, в качестве '''важного аутентификационного фактора'''.&lt;br /&gt;
* Серверная логика '''не может быть обойдена''' (если только сервер не взломан или в коде нет ошибки, но это совсем другой сценарий.) - '''используйте это в своих интересах'''. В большинстве случаев, вы можете реализовать проверки безопасности только на серверной стороне, не привлекая клиент.&lt;br /&gt;
* Следование аксиоме '''''“Все параметры, включая source могут быть подделаны и им нельзя доверять. Глобальной переменной client можно доверять.”''''' дает вам уверенность в том, что игрок, к которому вы обращаетесь (через переменную '''client''') '''именно тот, кто вызвал событие'''. Это защищает вас от тех ситуаций, когда читер может вызывать, например, админские события (например, кик/бан игрока), указывая реального администратора ('''второй''' аргумент '''[[triggerServerEvent]]'''), или вызывать события за других игроков (будто они вызвали их, но в реальности это сделал читер) - всего лишь из-за использования некорректной переменной. Чтобы убедиться, что вы это поняли, обратимся к коду:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЭТОТ КОД - ОН АБСОЛЮТНО НЕВЕРНЫЙ И НЕБЕЗОПАСНЫЙ&lt;br /&gt;
	ПРОБЛЕМА: ИСПОЛЬЗУЯ ПЕРЕМЕННУЮ 'source' В hasObjectPermissionTo ВЫ ОСТАВЛЯЕТЕ ДЫРУ ДЛЯ ЧИТЕРОВ&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerWrongAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(source, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- эксплойт находится здесь...&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerWrongAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerWrongAdminEvent&amp;quot;, root, onServerWrongAdminEvent)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	onServerCorrectAdminEvent ПРЕКРАСНО ЗАЩИЩЕН, ТАК, КАК ДОЛЖНО БЫТЬ&lt;br /&gt;
	ЗДЕСЬ НЕТ ПРОБЛЕМ: МЫ ИСПОЛЬЗУЕМ 'client' В hasObjectPermissionTo, ЧТО ЗАЩИЩАЕТ НАС ОТ ПОДМЕНЫ ИГРОКА, КОТОРЫЙ ВЫЗВАЛ СОБЫТИЕ&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
function onServerCorrectAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(client, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- если игрок имеет права&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerCorrectAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerCorrectAdminEvent&amp;quot;, root, onServerCorrectAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing setElementData==&lt;br /&gt;
&lt;br /&gt;
* You should refrain from using [[element data]] everywhere, it should be only used when really necessary. It is advised to replace it with [[triggerClientEvent]] instead.&lt;br /&gt;
* If you still insist on using it, it is recommended to set '''4th''' argument in [[setElementData]] to '''false''' (disabling sync for this, certain data) and use subscriber mode - [[addElementDataSubscriber]], for both security &amp;amp; performance reasons described in [https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent event section.]&lt;br /&gt;
{{Important Note|Disabling sync however, doesn't fully protect data key. It would be still vulnerable by default, but in this case you are not leaving it in plain sight. Using [[getAllElementData]] or digging in RAM wouldn't expose it, since it won't be synced to client in first place. Therefore, it needs to be added to anti-cheat as well.}} &lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of basic element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = tostring(sourceElement) -- element which received data&lt;br /&gt;
	local logOldValue = tostring(oldValue) -- old value&lt;br /&gt;
	local logNewValue = tostring(newValue) -- new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;=======================================\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;=======================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	local logVisibleTo = root -- specify who will see this log in console, in this case each player connected to server&lt;br /&gt;
	local hadData = (oldValue ~= nil) -- check if element had such data before&lt;br /&gt;
&lt;br /&gt;
	if (hadData) then -- if element had such data before&lt;br /&gt;
		setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
	else&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it completely&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	outputConsole(logText, logVisibleTo) -- print it to console&lt;br /&gt;
&lt;br /&gt;
	return true -- all success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeBasicAC(dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not go further&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkSpecialThing = (dataKey == &amp;quot;special_thing&amp;quot;) -- compare whether dataKey matches &amp;quot;special_thing&amp;quot;&lt;br /&gt;
	local checkFlagWaving = (dataKey == &amp;quot;flag_waving&amp;quot;) -- compare whether dataKey matches &amp;quot;flag_waving&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if (checkSpecialThing) then -- if it does, do our security checks&lt;br /&gt;
		local invalidElement = (client ~= source) -- verify whether source element is different from player which changed data&lt;br /&gt;
&lt;br /&gt;
		if (invalidElement) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;special_thing&amp;quot; can only be set for player himself&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkFlagWaving) then -- if it does, do our security checks&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(client) -- get player's current vehicle&lt;br /&gt;
		local invalidVehicle = (playerVehicle ~= source) -- verify whether source element is different from player's vehicle&lt;br /&gt;
&lt;br /&gt;
		if (invalidVehicle) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;flag_waving&amp;quot; can only be set for player's own vehicle&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeBasicAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of advanced element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan, allowOnlyProtectedKeys to true (as per default configuration)&lt;br /&gt;
	If allowOnlyProtectedKeys is enabled, do not forget to add every client-side element data key to protectedKeys table - otherwise you will face false-positives&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (handleDataChange) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	[&amp;quot;keyName&amp;quot;] = { -- name of key which would be protected&lt;br /&gt;
		onlyForPlayerHimself = true, -- enabling this (true) will make sure that this element data key can only be set on player who synced it (ignores onlyForOwnPlayerVeh and allowForElements), use false/nil to disable this&lt;br /&gt;
		onlyForOwnPlayerVeh = false, -- enabling this (true) will make sure that this element data key can only be set on player's current vehicle who synced it (ignores allowForElements), use false/nil to disable this&lt;br /&gt;
		allowForElements = { -- restrict this key for certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
			[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = { -- restrict this key for certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 32}, -- if value is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
		allowedTableLength = {1, 64}, -- if value is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
		allowedNumberRange = {1, 128}, -- if value is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
	}&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering element data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local allowOnlyProtectedKeys = true -- disallow (remove by using removeElementData) every element data besides those given in protectedKeys table; in case someone wanted to flood server with garbage keys, which would be kept in memory until server restart or manual remove - setElementData(source, key, nil) won't remove it; it has to be removeElementData&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
local protectedKeys = {&lt;br /&gt;
	[&amp;quot;vehicleNumber&amp;quot;] = { -- we want vehicleNumber to be set only on vehicles, with stricte numbers in range of 1-100&lt;br /&gt;
		allowForElements = {&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedNumberRange = {1, 100},&lt;br /&gt;
	},&lt;br /&gt;
	[&amp;quot;personalVehData&amp;quot;] = { -- we want be able to set personalVehData only on current vehicle, also it should be a string with length between 1-24&lt;br /&gt;
		onlyForOwnPlayerVeh = true,&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 24},&lt;br /&gt;
	},&lt;br /&gt;
	-- perform security checks on keys stored in this table, in a convenient way&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of element which received data&lt;br /&gt;
	local logOldValue = inspect(oldValue) -- in-depth view of old value&lt;br /&gt;
	local logNewValue = inspect(newValue) -- in-depth view of new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Fail reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (forceRemove) then -- we don't want this element data key to exist at all&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it&lt;br /&gt;
&lt;br /&gt;
		return true -- return success and stop here, we don't need further checks&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
&lt;br /&gt;
	return true -- return success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function handleDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from protectedKeys), false otherwise&lt;br /&gt;
	local protectedKey = protectedKeys[dataKey] -- look up whether key changed is stored in protectedKeys table&lt;br /&gt;
&lt;br /&gt;
	if (not protectedKey) then -- if it's not&lt;br /&gt;
&lt;br /&gt;
		if (allowOnlyProtectedKeys) then -- if we don't want garbage keys&lt;br /&gt;
			local failReason = &amp;quot;Key isn't present in protectedKeys&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = true -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return true -- this key isn't protected, let it through&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local onlyForPlayerHimself = protectedKey.onlyForPlayerHimself -- if key has &amp;quot;self-set&amp;quot; lock&lt;br /&gt;
	local onlyForOwnPlayerVeh = protectedKey.onlyForOwnPlayerVeh -- if key has &amp;quot;self-vehicle&amp;quot; lock&lt;br /&gt;
	local allowForElements = protectedKey.allowForElements -- if key has element type check&lt;br /&gt;
	local allowedDataTypes = protectedKey.allowedDataTypes -- if key has allowed data type check&lt;br /&gt;
&lt;br /&gt;
	if (onlyForPlayerHimself) then -- if &amp;quot;self-set&amp;quot; lock is active&lt;br /&gt;
		local matchingElement = (clientElement == sourceElement) -- verify whether player who set data is equal to element which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElement) then -- if it's not matching&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player himself&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (onlyForOwnPlayerVeh) then -- if &amp;quot;self-vehicle&amp;quot; lock is active&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(clientElement) -- get current vehicle of player which set data&lt;br /&gt;
		local matchingVehicle = (playerVehicle == sourceElement) -- check whether it matches the one which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingVehicle) then -- if it doesn't match&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player's own vehicle&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowForElements) then -- check if it's one of them&lt;br /&gt;
		local elementType = getElementType(sourceElement) -- get type of element whose data changed&lt;br /&gt;
		local matchingElementType = allowForElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElementType) then -- this isn't matching&lt;br /&gt;
			local failReason = &amp;quot;Invalid element type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowedDataTypes) then -- if there's allowed data types&lt;br /&gt;
		local valueType = type(newValue) -- get data type of value&lt;br /&gt;
		local matchingType = allowedDataTypes[valueType] -- check if it's one of allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingType) then -- if it's not then&lt;br /&gt;
			local failReason = &amp;quot;Invalid data type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedStringLength = protectedKey.allowedStringLength -- if key has specified string length check&lt;br /&gt;
		local dataString = (valueType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
		if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
			local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
			local stringLength = utf8.len(newValue) -- get length of data string&lt;br /&gt;
			local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedTableLength = protectedKey.allowedTableLength -- if key has table length check&lt;br /&gt;
		local dataTable = (valueType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
		if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
			local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
			local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
			local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
			local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
			for _, _ in pairs(newValue) do -- loop through whole table&lt;br /&gt;
				tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
				minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
				maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
				if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
					break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
				local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedNumberRange = protectedKey.allowedNumberRange -- if key has allowed number range check&lt;br /&gt;
		local dataNumber = (valueType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
		if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
			local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
			local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
			local matchingRange = (newValue &amp;gt;= minRange) and (newValue &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingRange) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this data key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeAdvancedAC(dataKey, oldValue, newValue) -- this event makes use of handleDataChange, the code was split for better readability&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not continue&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedChange = handleDataChange(client, source, dataKey, oldValue, newValue) -- run our security checks&lt;br /&gt;
&lt;br /&gt;
	if (approvedChange) then -- it's all cool and good&lt;br /&gt;
		return false -- we don't need further action&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(client, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(client, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeAdvancedAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing triggerServerEvent==&lt;br /&gt;
&lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player (client) '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
* Do '''not''' use the same name for your custom event as MTA native server events (which aren't remotely triggerable by default), e.g: '''onPlayerLogin'''; doing so would open door for cheaters manipulations.&lt;br /&gt;
* Be aware to which players event is sent via [[triggerClientEvent]]. For both security &amp;amp; performance reasons, admin like events should be received by admins only (to prevent confidential data being accessed), in the same time you shouldn't send each event to everyone on server (e.g: login success event which hides login panel for certain player). [[triggerClientEvent]] allows you to specify the event receiver as first (optional) argument. It defaults to [[root]], meaning if you don't specify it, it will be sent to everyone connected to server - even those who are still downloading server cache (which results in ''Server triggered clientside event eventName, but event is not added clientside.''). You can either pass player element or table with receivers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playersToReceiveEvent = {player1, player2, player3} -- each playerX is player element&lt;br /&gt;
&lt;br /&gt;
triggerClientEvent(playersToReceiveEvent, ...) -- do not forget to fill the latter part of arguments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of anti-cheat function designed for events, used for data validation for both normal, and admin events which are called from client-side.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan to true (as per default configuration)&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (processServerEventData) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	checkACLGroup = { -- check whether player who called event belongs to at least one group below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;Admin&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkPermissions = { -- check whether player who called event has permission to at least one thing below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;function.kickPlayer&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkEventData = {&lt;br /&gt;
		{&lt;br /&gt;
			debugData = &amp;quot;source&amp;quot;, -- optional details for report shown in debug message&lt;br /&gt;
			eventData = source, -- data we want to verify&lt;br /&gt;
			equalTo = client, -- compare whether eventData == equalTo&lt;br /&gt;
			allowedElements = { -- restrict eventData to be certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
				[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedDataTypes = { -- restrict eventData to be certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
				[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedStringLength = {1, 32}, -- if eventData is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
			allowedTableLength = {1, 64}, -- if eventData is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
			allowedNumberRange = {1, 128}, -- if eventData is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering server event data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
function processServerEventData(clientElement, sourceElement, serverEvent, securityChecks) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from securityChecks), false otherwise&lt;br /&gt;
	if (not securityChecks) then -- if we haven't passed any security checks&lt;br /&gt;
		return true -- nothing to check, let code go further&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (not clientElement) then -- if client variable isn't available for some reason (although it should never happen)&lt;br /&gt;
		local failReason = &amp;quot;Client variable not present&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkACLGroup = securityChecks.checkACLGroup -- if there's any ACL groups to check&lt;br /&gt;
	local checkPermissions = securityChecks.checkPermissions -- if there's any permissions to check&lt;br /&gt;
	local checkEventData = securityChecks.checkEventData -- if there's any data checks&lt;br /&gt;
&lt;br /&gt;
	if (checkACLGroup) then -- let's check player ACL groups&lt;br /&gt;
		local playerAccount = getPlayerAccount(clientElement) -- get current account of player&lt;br /&gt;
		local guestAccount = isGuestAccount(playerAccount) -- if account is guest (meaning player is not logged in)&lt;br /&gt;
&lt;br /&gt;
		if (guestAccount) then -- it's the case&lt;br /&gt;
			local failReason = &amp;quot;Can't retrieve player login - guest account&amp;quot; -- reason shown in report&lt;br /&gt;
			local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
			reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local accountName = getAccountName(playerAccount) -- get name of player's current account&lt;br /&gt;
		local aclString = &amp;quot;user.&amp;quot;..accountName -- format it for further use in isObjectInACLGroup function&lt;br /&gt;
&lt;br /&gt;
		for groupID = 1, #checkACLGroup do -- iterate over table of given groups&lt;br /&gt;
			local groupName = checkACLGroup[groupID] -- get each group name&lt;br /&gt;
			local aclGroup = aclGetGroup(groupName) -- check if such group exists&lt;br /&gt;
&lt;br /&gt;
			if (not aclGroup) then -- it doesn't&lt;br /&gt;
				local failReason = &amp;quot;ACL group '&amp;quot;..groupName..&amp;quot;' is missing&amp;quot; -- reason shown in report&lt;br /&gt;
				local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
				reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local playerInACLGroup = isObjectInACLGroup(aclString, aclGroup) -- check if player belong to the group&lt;br /&gt;
&lt;br /&gt;
			if (playerInACLGroup) then -- yep, it's the case&lt;br /&gt;
				return true -- so it's a success&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Player doesn't belong to any given ACL group&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkPermissions) then -- check if player has at least one desired permission&lt;br /&gt;
		local allowedByDefault = false -- does he have access by default&lt;br /&gt;
&lt;br /&gt;
		for permissionID = 1, #checkPermissions do -- iterate over all permissions&lt;br /&gt;
			local permissionName = checkPermissions[permissionID] -- get permission name&lt;br /&gt;
			local hasPermission = hasObjectPermissionTo(clientElement, permissionName, allowedByDefault) -- check whether player is allowed to perform certain action&lt;br /&gt;
&lt;br /&gt;
			if (hasPermission) then -- if player has access&lt;br /&gt;
				return true -- one is available (and enough), server won't bother to check others (as return keywords also breaks loop)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Not enough permissions&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkEventData) then -- if there is some data to verify&lt;br /&gt;
&lt;br /&gt;
		for dataID = 1, #checkEventData do -- iterate over each of data&lt;br /&gt;
			local dataToCheck = checkEventData[dataID] -- get each data set&lt;br /&gt;
			local eventData = dataToCheck.eventData -- this is the one we'll be verifying&lt;br /&gt;
			local equalTo = dataToCheck.equalTo -- we want to compare whether eventData == equalTo&lt;br /&gt;
			local allowedElements = dataToCheck.allowedElements -- check whether is element, and whether belongs to certain element types&lt;br /&gt;
			local allowedDataTypes = dataToCheck.allowedDataTypes -- do we restrict data to be certain type?&lt;br /&gt;
			local debugData = dataToCheck.debugData -- additional helper data&lt;br /&gt;
			local debugText = debugData and &amp;quot; (&amp;quot;..debugData..&amp;quot;)&amp;quot; or &amp;quot;&amp;quot; -- if it's present, format it nicely&lt;br /&gt;
&lt;br /&gt;
			if (equalTo) then -- equal check exists&lt;br /&gt;
				local matchingData = (eventData == equalTo) -- compare whether those two values are equal&lt;br /&gt;
&lt;br /&gt;
				if (not matchingData) then -- they aren't&lt;br /&gt;
					local failReason = &amp;quot;Data isn't equal @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedElements) then -- we do check whether is an element, and belongs to at least one given in the list&lt;br /&gt;
				local validElement = isElement(eventData) -- check if it's actual element&lt;br /&gt;
&lt;br /&gt;
				if (not validElement) then -- it's not&lt;br /&gt;
					local failReason = &amp;quot;Data isn't element @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local elementType = getElementType(eventData) -- it's element, so we want to know it's type&lt;br /&gt;
				local matchingElementType = allowedElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingElementType) then -- it's not allowed&lt;br /&gt;
					local failReason = &amp;quot;Invalid element type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedDataTypes) then -- let's check allowed data types&lt;br /&gt;
				local dataType = type(eventData) -- get data type&lt;br /&gt;
				local matchingType = allowedDataTypes[dataType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingType) then -- it isn't&lt;br /&gt;
					local failReason = &amp;quot;Invalid data type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedStringLength = dataToCheck.allowedStringLength -- if data has string length check&lt;br /&gt;
				local dataString = (dataType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
				if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
					local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
					local stringLength = utf8.len(eventData) -- get length of data string&lt;br /&gt;
					local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedTableLength = dataToCheck.allowedTableLength -- if data has table length check&lt;br /&gt;
				local dataTable = (dataType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
				if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
					local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
					local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
					local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
					local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
					for _, _ in pairs(eventData) do -- loop through whole table&lt;br /&gt;
						tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
						minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
						maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
						if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
							break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
						local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedNumberRange = dataToCheck.allowedNumberRange -- if data has number range check&lt;br /&gt;
				local dataNumber = (dataType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
				if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
					local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
					local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
					local matchingRange = (eventData &amp;gt;= minRange) and (eventData &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingRange) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this event call&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- helper function to log and handle accidents&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view player which called event&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of source element&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected event abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Event: &amp;quot;..serverEvent..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect or skipPunishment) then -- we don't want to punish player for some reason&lt;br /&gt;
		return true -- stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(clientElement, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(clientElement, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- all done, report success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onServerEvent(clientData)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local dataToPass = 10&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerEvent&amp;quot;, localPlayer, dataToPass)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = clientData, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;number&amp;quot;] = true, -- we want it to be only number&lt;br /&gt;
					},&lt;br /&gt;
					allowedNumberRange = {1, 100}, -- in range of 1 to 100&lt;br /&gt;
					debugData = &amp;quot;clientData&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerEvent&amp;quot;, root, onServerEvent)&lt;br /&gt;
&lt;br /&gt;
function onServerAdminEvent(playerToBan)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server admin event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local playerToBan = getPlayerFromName(&amp;quot;playerToBan&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerAdminEvent&amp;quot;, localPlayer, playerToBan)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerAdminEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkACLGroup = { -- we need to check whether player who called event belongs to ACL groups&lt;br /&gt;
				&amp;quot;Admin&amp;quot;, -- in this case admin group&lt;br /&gt;
			},&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = playerToBan, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;player&amp;quot;] = true, -- we want it to be player&lt;br /&gt;
					},&lt;br /&gt;
					debugData = &amp;quot;playerToBan&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerAdminEvent&amp;quot;, root, onServerAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing server-only events==&lt;br /&gt;
* It is very important to '''disable remote triggering''' ability in [https://wiki.multitheftauto.com/wiki/AddEvent addEvent], to prevent calling server-side only events from client-side.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
This example shows how you should make event server-side only.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onServerSideOnlyEvent()&lt;br /&gt;
	-- do some server-side stuff&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerSideOnlyEvent&amp;quot;, false) -- set second argument (allowRemoteTriger) to false, so it can't be called from client&lt;br /&gt;
addEventHandler(&amp;quot;onServerSideOnlyEvent&amp;quot;, root, onServerSideOnlyEvent) -- associate our event with function onServerSideOnlyEvent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing projectiles &amp;amp; explosions==&lt;br /&gt;
This section (and '''code''' is '''work in progress''') - '''use at your own risk'''.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile ammo tracker:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerProjectileAmmo = {} -- store player-held weapons ammo here&lt;br /&gt;
local playerWeaponsToTrack = { -- keep track of ammo for certain player-held weapon types, basically those which create projectile; [weaponID] = weaponSlot&lt;br /&gt;
	[16] = 8, -- grenade&lt;br /&gt;
	[17] = 8, -- teargas&lt;br /&gt;
	[18] = 8, -- molotov&lt;br /&gt;
	[35] = 7, -- rocket launcher&lt;br /&gt;
	[36] = 7, -- rocket launcher (heat-seeking)&lt;br /&gt;
	[39] = 8, -- satchel charge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for weaponID, weaponSlot in pairs(playerWeaponsToTrack) do&lt;br /&gt;
		local weaponInSlot = getPedWeapon(playerElement, weaponSlot)&lt;br /&gt;
		local weaponTotalAmmo = getPedTotalAmmo(playerElement, weaponSlot)&lt;br /&gt;
		local weaponHasAmmo = (weaponTotalAmmo &amp;gt; 0)&lt;br /&gt;
		local weaponMatching = (weaponInSlot == weaponID)&lt;br /&gt;
		local updateWeaponAmmo = (weaponMatching and weaponHasAmmo)&lt;br /&gt;
&lt;br /&gt;
		if (updateWeaponAmmo) then&lt;br /&gt;
			local storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			local newWeaponAmmo = (updateWeaponAmmo and weaponTotalAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
			if (not storedProjectileAmmo) then&lt;br /&gt;
				playerProjectileAmmo[playerElement] = {}&lt;br /&gt;
				storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			storedProjectileAmmo[weaponID] = newWeaponAmmo&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function hasPlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
	local projectileAmmo = (projectileData and projectileData[projectileWeapon])&lt;br /&gt;
&lt;br /&gt;
	return projectileAmmo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectileData) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileWeaponAmmo = playerProjectileData[projectileWeapon]&lt;br /&gt;
	local tempProjectileAmmo = (projectileWeaponAmmo - 1)&lt;br /&gt;
	local newProjectileAmmo = (tempProjectileAmmo &amp;gt; 0 and tempProjectileAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectileData[projectileWeapon] = newProjectileAmmo&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerWeaponSwitchAntiCheat(previousWeaponID, currentWeaponID)&lt;br /&gt;
	setTimer(updateProjectileAmmoForPlayer, 50, 1, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWeaponSwitch&amp;quot;, root, onPlayerWeaponSwitchAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onResourceStartAntiCheat()&lt;br /&gt;
	local playersTable = getElementsByType(&amp;quot;player&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	for playerID = 1, #playersTable do&lt;br /&gt;
		local playerElement = playersTable[playerID]&lt;br /&gt;
&lt;br /&gt;
		updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onResourceStart&amp;quot;, resourceRoot, onResourceStartAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerWastedQuitAntiCheat()&lt;br /&gt;
	playerProjectileAmmo[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerCreatedProjectiles = {} -- store count of legitimately created player projectiles; [playerElement] = {[projectileType] = activeProjectiles}&lt;br /&gt;
local projectileTypes = {&lt;br /&gt;
	[16] = { -- Grenade&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[17] = { -- Teargas&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[17] = true, -- teargas&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[18] = { -- Molotov&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[19] = { -- Rocket&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[425] = true, -- hunter&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[20] = { -- Rocket (heat-seeking)&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[21] = { -- Airbomb&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[39] = { -- Satchel charge&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[39] = true, -- satchel charge&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[58] = { -- Hydra flare&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportProjectileAbnormality(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	local projectileSyncer = inspect(playerElement)&lt;br /&gt;
	local projectilePosition = projectileX..&amp;quot;, &amp;quot;..projectileY..&amp;quot;, &amp;quot;..projectileZ&lt;br /&gt;
	local projectileZoneName = getZoneName(projectileX, projectileY, projectileZ, false)&lt;br /&gt;
	local projectileCityName = getZoneName(projectileX, projectileY, projectileZ, true)&lt;br /&gt;
	local projectileLog =&lt;br /&gt;
		&amp;quot;* Detected projectile abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile syncer: &amp;quot;..projectileSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile type: &amp;quot;..projectileType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile position: &amp;quot;..projectilePosition.. &amp;quot; (&amp;quot;..projectileZoneName..&amp;quot;, &amp;quot;..projectileCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(projectileLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processProjectileChecks(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
	local projectileAllowed = projectileData.projectileAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not projectileAllowed) then&lt;br /&gt;
		return false, &amp;quot;Projectile not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileAllowedWeapons = projectileData.projectileAllowedWeapons&lt;br /&gt;
&lt;br /&gt;
	if (projectileAllowedWeapons) then&lt;br /&gt;
		local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
		local allowedWeapon = projectileAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedWeapon) then&lt;br /&gt;
			return false, &amp;quot;Player is not holding correct weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
		local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
		if (not playerHasAmmo) then&lt;br /&gt;
			return false, &amp;quot;Player doesn't have ammo for weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerVehicle) then&lt;br /&gt;
		local projectileAllowedVehicles = projectileData.projectileAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
		if (projectileAllowedVehicles) then&lt;br /&gt;
			local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
			local allowedVehicle = projectileAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedVehicle) then&lt;br /&gt;
				return false, &amp;quot;Player is not inside allowed vehicles&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
			local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
			if (not playerDriver) then&lt;br /&gt;
				return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Player in vehicle&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxDistanceFromCreator = projectileData.projectileMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToProjectile = getDistanceBetweenPoints3D(playerX, playerY, playerZ, projectileX, projectileY, projectileZ)&lt;br /&gt;
		local matchingProjectileDistance = (distanceToProjectile &amp;lt;= projectileMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileDistance) then&lt;br /&gt;
			return false, &amp;quot;Projectile distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxVelocity = projectileData.projectileMaxVelocity&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxVelocity) then&lt;br /&gt;
		local projectileVelocity = (projectileVX ^ 2 + projectileVY ^ 2 + projectileVZ ^ 2)&lt;br /&gt;
		local projectileSpeed = math.sqrt(projectileVelocity)&lt;br /&gt;
		local matchingProjectileVelocity = (projectileSpeed &amp;lt;= projectileMaxVelocity)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileVelocity) then&lt;br /&gt;
			return false, &amp;quot;Projectile velocity mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trackPlayerProjectile(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		playerCreatedProjectiles[playerElement] = {}&lt;br /&gt;
		playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID] or 0&lt;br /&gt;
	local newProjectilesCount = (projectilesByType + 1)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
&lt;br /&gt;
	if (not projectileData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileBehaviour = projectileData.projectileOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not projectileBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = projectileBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = projectileBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = projectileBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID]&lt;br /&gt;
&lt;br /&gt;
	if (not projectilesByType) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local tempProjectilesCount = (projectilesByType - 1)&lt;br /&gt;
	local newProjectilesCount = (tempProjectilesCount &amp;gt; 0 and tempProjectilesCount or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerProjectileCreationAntiCheat(projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local approvedProjectile, detectionCode = processProjectileChecks(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedProjectile) then&lt;br /&gt;
		trackPlayerProjectile(source, projectileType)&lt;br /&gt;
&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportProjectileAbnormality(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerProjectileCreation&amp;quot;, root, onPlayerProjectileCreationAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerQuitAntiCheat()&lt;br /&gt;
	playerCreatedProjectiles[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Explosions handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local rocketInstantExplosionDistanceThreshold = 1.55 -- distance below which only explosion will be created (and not rocket projectile, hence not calling onPlayerProjectileCreation); do not change it&lt;br /&gt;
local explosionTypes = { -- more specific info on explosion, and whether it is allowed&lt;br /&gt;
	[0] = { -- Grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[1] = { -- Molotov&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[2] = { -- Rocket&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[3] = { -- Rocket weak&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[4] = { -- Car&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[5] = { -- Car quick&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[6] = { -- Boat&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[7] = { -- Heli&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[8] = { -- Mine&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[9] = { -- Object&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[10] = { -- Tank grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionMaxDistanceFromCreator = 80,&lt;br /&gt;
		explosionAllowedVehicles = {&lt;br /&gt;
			[432] = true,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	[11] = { -- Small&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[12] = { -- Tiny&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportExplosionAbnormality(playerElement, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	local explosionSyncer = inspect(playerElement)&lt;br /&gt;
	local explosionType = explosionType&lt;br /&gt;
	local explosionPosition = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionZoneName = getZoneName(explosionX, explosionY, explosionZ, false)&lt;br /&gt;
	local explosionCityName = getZoneName(explosionX, explosionY, explosionZ, true)&lt;br /&gt;
	local explosionLog =&lt;br /&gt;
		&amp;quot;* Detected explosion abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion syncer: &amp;quot;..explosionSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion type: &amp;quot;..explosionType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion position: &amp;quot;..explosionPosition.. &amp;quot; (&amp;quot;..explosionZoneName..&amp;quot;, &amp;quot;..explosionCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(explosionLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processExplosionChecks(playerElement, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
	local explosionAllowed = explosionData.explosionAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not explosionAllowed) then&lt;br /&gt;
		return false, &amp;quot;Explosion type not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rocketExplosion = (explosionType == 2)&lt;br /&gt;
&lt;br /&gt;
	if (rocketExplosion) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local instantExplosion = (distanceToExplosion &amp;lt; rocketInstantExplosionDistanceThreshold)&lt;br /&gt;
&lt;br /&gt;
		if (instantExplosion) then&lt;br /&gt;
			local explosionAllowedWeapons = explosionData.explosionAllowedWeapons&lt;br /&gt;
			local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
			local allowedWeapon = explosionAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedWeapon) then&lt;br /&gt;
				return false, &amp;quot;Player is not holding rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
			local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
			if (not playerHasAmmo) then&lt;br /&gt;
				return false, &amp;quot;Player doesn't have ammo for rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedProjectiles = explosionData.explosionAllowedProjectiles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedProjectiles) then&lt;br /&gt;
&lt;br /&gt;
		for projectileID, _ in pairs(explosionAllowedProjectiles) do&lt;br /&gt;
			local atleastOneProjectile = decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
&lt;br /&gt;
			if (atleastOneProjectile) then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Explosion created without respective projectile&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedVehicles = explosionData.explosionAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedVehicles) then&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
		if (not playerVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is not in vehicle&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
		local allowedVehicle = explosionAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is inside not allowed vehicles&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
		local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
		if (not playerDriver) then&lt;br /&gt;
			return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionMaxDistanceFromCreator = explosionData.explosionMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (explosionMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local matchingExplosionDistance = (distanceToExplosion &amp;lt; explosionMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingExplosionDistance) then&lt;br /&gt;
			return false, &amp;quot;Explosion distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
&lt;br /&gt;
	if (not explosionData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionBehaviour = explosionData.explosionOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not explosionBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = explosionBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = explosionBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = explosionBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onExplosionAntiCheat(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local serverSyncExplosion = (source == root)&lt;br /&gt;
&lt;br /&gt;
	if (serverSyncExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedExplosion, detectionCode = processExplosionChecks(source, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportExplosionAbnormality(source, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosionAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Handling non-registered events==&lt;br /&gt;
&lt;br /&gt;
See: [[onPlayerTriggerInvalidEvent]]&lt;br /&gt;
&lt;br /&gt;
==Handling events spam==&lt;br /&gt;
See: [[onPlayerTriggerEventThreshold]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=TakePlayerScreenShot&amp;diff=81988</id>
		<title>TakePlayerScreenShot</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=TakePlayerScreenShot&amp;diff=81988"/>
		<updated>2025-05-10T19:06:06Z</updated>

		<summary type="html">&lt;p&gt;DColeman: Use client instead of source in example&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{Server function}}&lt;br /&gt;
This function forces a client to capture the current screen output and send it back to the server. The image will contain the GTA HUD and the output of any dxDraw functions that are not flagged as 'post GUI'. The image specifically excludes the chat box and all GUI (including the client console). The result is received with the event [[onPlayerScreenShot]].&lt;br /&gt;
&lt;br /&gt;
==Syntax== &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool takePlayerScreenShot ( player thePlayer, int width, int height [, string tag = &amp;quot;&amp;quot;, int quality = 30, int maxBandwidth = 5000, int maxPacketSize = 500 ] )         &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; &lt;br /&gt;
{{OOP||[[player]]:takeScreenShot||}}&lt;br /&gt;
===Required Arguments=== &lt;br /&gt;
*'''thePlayer:''' the player to get the screen capture from.&lt;br /&gt;
*'''width:''' the width of the capture image.&lt;br /&gt;
*'''height:''' the height of the capture image.&lt;br /&gt;
&lt;br /&gt;
===Optional Arguments=== &lt;br /&gt;
*'''tag:''' A string to help identify the screen capture. The string is passed to the matching [[onPlayerScreenShot]] event for your personal convenience.&lt;br /&gt;
*'''quality:''' Quality of the final JPEG image from 0 to 100. A lower value can reduce the memory used by the image considerably which will result in faster and less intrusive uploads.&lt;br /&gt;
*'''maxBandwidth:''' The amount of client upload bandwidth to use (in bytes per second) when sending the image.&lt;br /&gt;
*'''maxPacketSize: ''' The maximum size of one packet.&lt;br /&gt;
&lt;br /&gt;
===Returns===&lt;br /&gt;
Returns ''true'' if the function was successfully, ''false'' if invalid arguments are specified.&lt;br /&gt;
&lt;br /&gt;
==Example==  &lt;br /&gt;
===Example 1===&lt;br /&gt;
This example will take a player screenshot whenever a player takes a picture with the camera weapon and then send the picture to all clients, which will render the latest screenshot in the bottom right corner of their screen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Client&amp;quot; class=&amp;quot;client&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local screenSizeX,screenSizeY = guiGetScreenSize() -- save the current screen dimensions&lt;br /&gt;
local imgtexture&lt;br /&gt;
local takenBy&lt;br /&gt;
&lt;br /&gt;
function wepFire(weapon)&lt;br /&gt;
	if weapon == 43 then -- if the weapon the player just fired is the camera&lt;br /&gt;
		triggerServerEvent(&amp;quot;onPlayerTakesPhoto&amp;quot;,localPlayer)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onClientPlayerWeaponFire&amp;quot;,localPlayer,wepFire)&lt;br /&gt;
&lt;br /&gt;
function updateLatestPhoto(img)&lt;br /&gt;
	if imgtexture then -- clean up the old dxTextrue if there is one&lt;br /&gt;
		destroyElement(imgtexture)&lt;br /&gt;
	end&lt;br /&gt;
	imgtexture = dxCreateTexture(img) -- create a new dxTexture from the image data so that we can render it using dxDrawImage&lt;br /&gt;
	takenBy = &amp;quot;taken by &amp;quot;..getPlayerName(source) -- let's also credit the photographer&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;updatePhoto&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;updatePhoto&amp;quot;,root,updateLatestPhoto)&lt;br /&gt;
&lt;br /&gt;
function renderPhoto()&lt;br /&gt;
	if imgtexture then&lt;br /&gt;
		local sizeX, sizeY = 320, 240&lt;br /&gt;
		dxDrawImage(screenSizeX-sizeX,screenSizeY-sizeY,sizeX,sizeY,imgtexture) -- render the picture as well as the name of the photographer in the bottom right corner of the screen&lt;br /&gt;
		dxDrawText(takenBy,screenSizeX-sizeX,screenSizeY-sizeY,screenSizeX,screenSizeY,tocolor(0,0,0))&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onClientRender&amp;quot;,root,renderPhoto)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function requestScreenshot()&lt;br /&gt;
	takePlayerScreenShot(client,320,240,&amp;quot;cameraphoto&amp;quot;,50) -- the player just took a picture with the camera, let's request a screenshot to see what they took a photo of&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onPlayerTakesPhoto&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerTakesPhoto&amp;quot;,root,requestScreenshot)&lt;br /&gt;
&lt;br /&gt;
function incomingPlayerScreenshot(res,status,img,timestamp,tag)&lt;br /&gt;
	if status == &amp;quot;ok&amp;quot; and tag == &amp;quot;cameraphoto&amp;quot; then -- make sure a picture was taken successfully and that we're the ones who requested this screenshot&lt;br /&gt;
		triggerClientEvent(&amp;quot;updatePhoto&amp;quot;,source,img)  -- trigger a client event to share the image with everyone on the server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerScreenShot&amp;quot;,root,incomingPlayerScreenshot)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Player functions|server}}&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81987</id>
		<title>User:DColeman/RU:Безопасность скриптов</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81987"/>
		<updated>2025-05-10T12:32:39Z</updated>

		<summary type="html">&lt;p&gt;DColeman: /* Обнаружение и обезвреживание бэкдоров и читов */ typo fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Информация по памяти клиента==&lt;br /&gt;
&lt;br /&gt;
Начиная с самых основ:&lt;br /&gt;
* Вы должны понимать, что все, что вы храните на стороне клиента, включая .lua файлы, находится под риском. Любая конфиденциальная или критическая информация, которая как-либо оказывается на клиентской стороне (ПК игрока), может быть прочитана и/или изменена чит-клиентами.&lt;br /&gt;
* Чтобы сохранить конфиденциальную информацию и логику скрипта в секрете - используйте серверную сторону.&lt;br /&gt;
* Обратите внимание, что скрипты, отмеченные как общие ('''shared''') также действуют как '''клиентские''', что означает, что все вышеперечисленное также применяется и к ним. Например, объявление скрипта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;shared&amp;quot;/&amp;gt; &amp;lt;!-- этот скрипт будет запущен и на сервере и на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое, что и объявление:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;client&amp;quot;/&amp;gt; &amp;lt;!-- объявляем скрипт на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;server&amp;quot;/&amp;gt; &amp;lt;!-- делаем то же самое, но на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Дополнительный слой защиты==&lt;br /&gt;
&lt;br /&gt;
Чтобы незначительно ''усложнить жизнь*'' тем, у кого есть плохие намерения по отношению к вашему серверу, вы можете использовать аттрибут cache (и/или [https://luac.mtasa.com/ скомпилированные lua скрипты (также известные как Luac) с '''3''' уровнем дополнительной обфускации] - [https://wiki.multitheftauto.com/wiki/Lua_compilation_API API]), доступный в [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], вместе с настройкой встроенного античита МТА с включением SD (специальными кодами обнаружения), для дополнительной информации смотрите [https://wiki.multitheftauto.com/wiki/Anti-cheat_guide гайд по античиту].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;shared.lua&amp;quot; type=&amp;quot;shared&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''Усложнить жизнь'' '''не означает &amp;quot;сделать невозможным&amp;quot;''' получение вашего клиентского Lua кода, это означает, что '''большинство''' людей не смогут просмотреть ваши .lua файлы - тех, кто ищет логические ошибки (баги) или отсутствующие/некорректные проверки безопасности.&lt;br /&gt;
* Может использоваться на '''client''' и '''shared''' типах скриптов (не имеет эффекта на серверных скриптах).&lt;br /&gt;
* Это не удалит Lua файлы, которые были загружены ранее.&lt;br /&gt;
&lt;br /&gt;
==Обнаружение и обезвреживание бэкдоров и читов==&lt;br /&gt;
'''Чтобы минимизировать (или исключить) урон, причиненный Lua скриптами:'''&lt;br /&gt;
* Всегда обновляйте ваш сервер до самой актуальной версии, вы можете [https://nightly.multitheftauto.com/ загрузить новейшие сборки сервера отсюда.] С информацией по определенным сборкам можно ознакомиться [https://buildinfo.multitheftauto.com/ здесь.]&lt;br /&gt;
* Всегда обновляйте ресурсы, установленные на сервере, до самой актуальной версии, вы можете [https://github.com/multitheftauto/mtasa-resources загрузить новейшие (стандартные) ресурсы из репозитория GitHub.] Они часто содержат обновления безопасности, которые влияют на устойчивость вашего сервера к атакам.&lt;br /&gt;
* Убедитесь, что [https://wiki.multitheftauto.com/wiki/Access%20Control%20List ACL (список контроля доступа)] правильно сконфигурирован - он поможет заблокировать ресурсам некоторые потенциально опасные функции.&lt;br /&gt;
* Никогда не выдавайте админ-права ресурсам (включая карты), полученным из неизвестных источников.&lt;br /&gt;
* Перед запуском недоверенного ресурса, изучите:&lt;br /&gt;
** Его [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], на наличие возможных скрытых скриптов, которые скрываются под видом файлов с другими расширениями.&lt;br /&gt;
** Его исходный код, на наличие вредоносной логики.&lt;br /&gt;
* Не запускайте и не используйте скомпилированные ресурсы (скрипты), в легитимности которых вы не уверены.&lt;br /&gt;
&lt;br /&gt;
'''Чтобы минимизировать урон, причиненный читером, зашедшим на сервер:'''&lt;br /&gt;
* При создании скриптов '''никогда не доверяйте данным, полученным со стороны клиента'''.&lt;br /&gt;
* При просмотре скриптов на наличие дыр безопасности, сверяйтесь с данными, поступающими от клиента, которому можно доверять.&lt;br /&gt;
* Отправлены могут быть любые данные, поэтому любые серверные скрипты, которые коммуницируют с клиентскими и получают информацию от игроков, '''должны проверять информацию на корректность''' перед дальнейшим использованием. Подделка данных чаще всего происходит с помощью [[setElementData]] и [[triggerServerEvent]].&lt;br /&gt;
* Вы не должны полагаться только на [https://wiki.multitheftauto.com/wiki/Serial серийный номер] игрока, когда они используются для критических действий (авто-входа/администраторских действий). '''Не гарантируется, что серийный номер игрока уникален и не подделан'''. Это причина, почему вы должны '''поместить его за''' системой аккаунтов, в качестве '''важного аутентификационного фактора'''.&lt;br /&gt;
* Серверная логика '''не может быть обойдена''' (если только сервер не взломан или в коде нет ошибки, но это совсем другой сценарий.) - '''используйте это в своих интересах'''. В большинстве случаев, вы можете реализовать проверки безопасности только на серверной стороне, не привлекая клиент.&lt;br /&gt;
* Следование аксиоме '''''“Все параметры, включая source могут быть подделаны и им нельзя доверять. Глобальной переменной client можно доверять.”''''' дает вам уверенность в том, что игрок, к которому вы обращаетесь (через переменную '''client''') '''именно тот, кто вызвал событие'''. Это защищает вас от тех ситуаций, когда читер может вызывать, например, админские события (например, кик/бан игрока), указывая реального администратора ('''второй''' аргумент '''[[triggerServerEvent]]'''), или вызывать события за других игроков (будто они вызвали их, но в реальности это сделал читер) - всего лишь из-за использования некорректной переменной. Чтобы убедиться, что вы это поняли, обратимся к коду:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЭТОТ КОД - ОН АБСОЛЮТНО НЕВЕРНЫЙ И НЕБЕЗОПАСНЫЙ&lt;br /&gt;
	ПРОБЛЕМА: ИСПОЛЬЗУЯ ПЕРЕМЕННУЮ 'source' В hasObjectPermissionTo ВЫ ОСТАВЛЯЕТЕ ДЫРУ ДЛЯ ЧИТЕРОВ&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerWrongAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(source, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- эксплойт находится здесь...&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerWrongAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerWrongAdminEvent&amp;quot;, root, onServerWrongAdminEvent)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	onServerCorrectAdminEvent ПРЕКРАСНО ЗАЩИЩЕН, ТАК, КАК ДОЛЖНО БЫТЬ&lt;br /&gt;
	ЗДЕСЬ НЕТ ПРОБЛЕМ: МЫ ИСПОЛЬЗУЕМ 'client' В hasObjectPermissionTo, ЧТО ЗАЩИЩАЕТ НАС ОТ ПОДМЕНЫ ИГРОКА, КОТОРЫЙ ВЫЗВАЛ СОБЫТИЕ&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
function onServerCorrectAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(client, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- если игрок имеет права&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerCorrectAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerCorrectAdminEvent&amp;quot;, root, onServerCorrectAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing setElementData==&lt;br /&gt;
&lt;br /&gt;
* You should refrain from using [[element data]] everywhere, it should be only used when really necessary. It is advised to replace it with [[triggerClientEvent]] instead.&lt;br /&gt;
* If you still insist on using it, it is recommended to set '''4th''' argument in [[setElementData]] to '''false''' (disabling sync for this, certain data) and use subscriber mode - [[addElementDataSubscriber]], for both security &amp;amp; performance reasons described in [https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent event section.]&lt;br /&gt;
{{Important Note|Disabling sync however, doesn't fully protect data key. It would be still vulnerable by default, but in this case you are not leaving it in plain sight. Using [[getAllElementData]] or digging in RAM wouldn't expose it, since it won't be synced to client in first place. Therefore, it needs to be added to anti-cheat as well.}} &lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of basic element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = tostring(sourceElement) -- element which received data&lt;br /&gt;
	local logOldValue = tostring(oldValue) -- old value&lt;br /&gt;
	local logNewValue = tostring(newValue) -- new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;=======================================\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;=======================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	local logVisibleTo = root -- specify who will see this log in console, in this case each player connected to server&lt;br /&gt;
	local hadData = (oldValue ~= nil) -- check if element had such data before&lt;br /&gt;
&lt;br /&gt;
	if (hadData) then -- if element had such data before&lt;br /&gt;
		setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
	else&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it completely&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	outputConsole(logText, logVisibleTo) -- print it to console&lt;br /&gt;
&lt;br /&gt;
	return true -- all success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeBasicAC(dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not go further&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkSpecialThing = (dataKey == &amp;quot;special_thing&amp;quot;) -- compare whether dataKey matches &amp;quot;special_thing&amp;quot;&lt;br /&gt;
	local checkFlagWaving = (dataKey == &amp;quot;flag_waving&amp;quot;) -- compare whether dataKey matches &amp;quot;flag_waving&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if (checkSpecialThing) then -- if it does, do our security checks&lt;br /&gt;
		local invalidElement = (client ~= source) -- verify whether source element is different from player which changed data&lt;br /&gt;
&lt;br /&gt;
		if (invalidElement) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;special_thing&amp;quot; can only be set for player himself&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkFlagWaving) then -- if it does, do our security checks&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(client) -- get player's current vehicle&lt;br /&gt;
		local invalidVehicle = (playerVehicle ~= source) -- verify whether source element is different from player's vehicle&lt;br /&gt;
&lt;br /&gt;
		if (invalidVehicle) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;flag_waving&amp;quot; can only be set for player's own vehicle&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeBasicAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of advanced element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan, allowOnlyProtectedKeys to true (as per default configuration)&lt;br /&gt;
	If allowOnlyProtectedKeys is enabled, do not forget to add every client-side element data key to protectedKeys table - otherwise you will face false-positives&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (handleDataChange) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	[&amp;quot;keyName&amp;quot;] = { -- name of key which would be protected&lt;br /&gt;
		onlyForPlayerHimself = true, -- enabling this (true) will make sure that this element data key can only be set on player who synced it (ignores onlyForOwnPlayerVeh and allowForElements), use false/nil to disable this&lt;br /&gt;
		onlyForOwnPlayerVeh = false, -- enabling this (true) will make sure that this element data key can only be set on player's current vehicle who synced it (ignores allowForElements), use false/nil to disable this&lt;br /&gt;
		allowForElements = { -- restrict this key for certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
			[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = { -- restrict this key for certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 32}, -- if value is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
		allowedTableLength = {1, 64}, -- if value is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
		allowedNumberRange = {1, 128}, -- if value is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
	}&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering element data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local allowOnlyProtectedKeys = true -- disallow (remove by using removeElementData) every element data besides those given in protectedKeys table; in case someone wanted to flood server with garbage keys, which would be kept in memory until server restart or manual remove - setElementData(source, key, nil) won't remove it; it has to be removeElementData&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
local protectedKeys = {&lt;br /&gt;
	[&amp;quot;vehicleNumber&amp;quot;] = { -- we want vehicleNumber to be set only on vehicles, with stricte numbers in range of 1-100&lt;br /&gt;
		allowForElements = {&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedNumberRange = {1, 100},&lt;br /&gt;
	},&lt;br /&gt;
	[&amp;quot;personalVehData&amp;quot;] = { -- we want be able to set personalVehData only on current vehicle, also it should be a string with length between 1-24&lt;br /&gt;
		onlyForOwnPlayerVeh = true,&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 24},&lt;br /&gt;
	},&lt;br /&gt;
	-- perform security checks on keys stored in this table, in a convenient way&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of element which received data&lt;br /&gt;
	local logOldValue = inspect(oldValue) -- in-depth view of old value&lt;br /&gt;
	local logNewValue = inspect(newValue) -- in-depth view of new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Fail reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (forceRemove) then -- we don't want this element data key to exist at all&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it&lt;br /&gt;
&lt;br /&gt;
		return true -- return success and stop here, we don't need further checks&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
&lt;br /&gt;
	return true -- return success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function handleDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from protectedKeys), false otherwise&lt;br /&gt;
	local protectedKey = protectedKeys[dataKey] -- look up whether key changed is stored in protectedKeys table&lt;br /&gt;
&lt;br /&gt;
	if (not protectedKey) then -- if it's not&lt;br /&gt;
&lt;br /&gt;
		if (allowOnlyProtectedKeys) then -- if we don't want garbage keys&lt;br /&gt;
			local failReason = &amp;quot;Key isn't present in protectedKeys&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = true -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return true -- this key isn't protected, let it through&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local onlyForPlayerHimself = protectedKey.onlyForPlayerHimself -- if key has &amp;quot;self-set&amp;quot; lock&lt;br /&gt;
	local onlyForOwnPlayerVeh = protectedKey.onlyForOwnPlayerVeh -- if key has &amp;quot;self-vehicle&amp;quot; lock&lt;br /&gt;
	local allowForElements = protectedKey.allowForElements -- if key has element type check&lt;br /&gt;
	local allowedDataTypes = protectedKey.allowedDataTypes -- if key has allowed data type check&lt;br /&gt;
&lt;br /&gt;
	if (onlyForPlayerHimself) then -- if &amp;quot;self-set&amp;quot; lock is active&lt;br /&gt;
		local matchingElement = (clientElement == sourceElement) -- verify whether player who set data is equal to element which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElement) then -- if it's not matching&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player himself&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (onlyForOwnPlayerVeh) then -- if &amp;quot;self-vehicle&amp;quot; lock is active&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(clientElement) -- get current vehicle of player which set data&lt;br /&gt;
		local matchingVehicle = (playerVehicle == sourceElement) -- check whether it matches the one which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingVehicle) then -- if it doesn't match&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player's own vehicle&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowForElements) then -- check if it's one of them&lt;br /&gt;
		local elementType = getElementType(sourceElement) -- get type of element whose data changed&lt;br /&gt;
		local matchingElementType = allowForElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElementType) then -- this isn't matching&lt;br /&gt;
			local failReason = &amp;quot;Invalid element type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowedDataTypes) then -- if there's allowed data types&lt;br /&gt;
		local valueType = type(newValue) -- get data type of value&lt;br /&gt;
		local matchingType = allowedDataTypes[valueType] -- check if it's one of allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingType) then -- if it's not then&lt;br /&gt;
			local failReason = &amp;quot;Invalid data type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedStringLength = protectedKey.allowedStringLength -- if key has specified string length check&lt;br /&gt;
		local dataString = (valueType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
		if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
			local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
			local stringLength = utf8.len(newValue) -- get length of data string&lt;br /&gt;
			local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedTableLength = protectedKey.allowedTableLength -- if key has table length check&lt;br /&gt;
		local dataTable = (valueType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
		if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
			local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
			local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
			local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
			local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
			for _, _ in pairs(newValue) do -- loop through whole table&lt;br /&gt;
				tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
				minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
				maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
				if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
					break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
				local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedNumberRange = protectedKey.allowedNumberRange -- if key has allowed number range check&lt;br /&gt;
		local dataNumber = (valueType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
		if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
			local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
			local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
			local matchingRange = (newValue &amp;gt;= minRange) and (newValue &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingRange) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this data key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeAdvancedAC(dataKey, oldValue, newValue) -- this event makes use of handleDataChange, the code was split for better readability&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not continue&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedChange = handleDataChange(client, source, dataKey, oldValue, newValue) -- run our security checks&lt;br /&gt;
&lt;br /&gt;
	if (approvedChange) then -- it's all cool and good&lt;br /&gt;
		return false -- we don't need further action&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(client, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(client, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeAdvancedAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing triggerServerEvent==&lt;br /&gt;
&lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player (client) '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
* Do '''not''' use the same name for your custom event as MTA native server events (which aren't remotely triggerable by default), e.g: '''onPlayerLogin'''; doing so would open door for cheaters manipulations.&lt;br /&gt;
* Be aware to which players event is sent via [[triggerClientEvent]]. For both security &amp;amp; performance reasons, admin like events should be received by admins only (to prevent confidential data being accessed), in the same time you shouldn't send each event to everyone on server (e.g: login success event which hides login panel for certain player). [[triggerClientEvent]] allows you to specify the event receiver as first (optional) argument. It defaults to [[root]], meaning if you don't specify it, it will be sent to everyone connected to server - even those who are still downloading server cache (which results in ''Server triggered clientside event eventName, but event is not added clientside.''). You can either pass player element or table with receivers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playersToReceiveEvent = {player1, player2, player3} -- each playerX is player element&lt;br /&gt;
&lt;br /&gt;
triggerClientEvent(playersToReceiveEvent, ...) -- do not forget to fill the latter part of arguments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of anti-cheat function designed for events, used for data validation for both normal, and admin events which are called from client-side.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan to true (as per default configuration)&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (processServerEventData) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	checkACLGroup = { -- check whether player who called event belongs to at least one group below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;Admin&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkPermissions = { -- check whether player who called event has permission to at least one thing below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;function.kickPlayer&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkEventData = {&lt;br /&gt;
		{&lt;br /&gt;
			debugData = &amp;quot;source&amp;quot;, -- optional details for report shown in debug message&lt;br /&gt;
			eventData = source, -- data we want to verify&lt;br /&gt;
			equalTo = client, -- compare whether eventData == equalTo&lt;br /&gt;
			allowedElements = { -- restrict eventData to be certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
				[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedDataTypes = { -- restrict eventData to be certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
				[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedStringLength = {1, 32}, -- if eventData is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
			allowedTableLength = {1, 64}, -- if eventData is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
			allowedNumberRange = {1, 128}, -- if eventData is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering server event data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
function processServerEventData(clientElement, sourceElement, serverEvent, securityChecks) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from securityChecks), false otherwise&lt;br /&gt;
	if (not securityChecks) then -- if we haven't passed any security checks&lt;br /&gt;
		return true -- nothing to check, let code go further&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (not clientElement) then -- if client variable isn't available for some reason (although it should never happen)&lt;br /&gt;
		local failReason = &amp;quot;Client variable not present&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkACLGroup = securityChecks.checkACLGroup -- if there's any ACL groups to check&lt;br /&gt;
	local checkPermissions = securityChecks.checkPermissions -- if there's any permissions to check&lt;br /&gt;
	local checkEventData = securityChecks.checkEventData -- if there's any data checks&lt;br /&gt;
&lt;br /&gt;
	if (checkACLGroup) then -- let's check player ACL groups&lt;br /&gt;
		local playerAccount = getPlayerAccount(clientElement) -- get current account of player&lt;br /&gt;
		local guestAccount = isGuestAccount(playerAccount) -- if account is guest (meaning player is not logged in)&lt;br /&gt;
&lt;br /&gt;
		if (guestAccount) then -- it's the case&lt;br /&gt;
			local failReason = &amp;quot;Can't retrieve player login - guest account&amp;quot; -- reason shown in report&lt;br /&gt;
			local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
			reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local accountName = getAccountName(playerAccount) -- get name of player's current account&lt;br /&gt;
		local aclString = &amp;quot;user.&amp;quot;..accountName -- format it for further use in isObjectInACLGroup function&lt;br /&gt;
&lt;br /&gt;
		for groupID = 1, #checkACLGroup do -- iterate over table of given groups&lt;br /&gt;
			local groupName = checkACLGroup[groupID] -- get each group name&lt;br /&gt;
			local aclGroup = aclGetGroup(groupName) -- check if such group exists&lt;br /&gt;
&lt;br /&gt;
			if (not aclGroup) then -- it doesn't&lt;br /&gt;
				local failReason = &amp;quot;ACL group '&amp;quot;..groupName..&amp;quot;' is missing&amp;quot; -- reason shown in report&lt;br /&gt;
				local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
				reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local playerInACLGroup = isObjectInACLGroup(aclString, aclGroup) -- check if player belong to the group&lt;br /&gt;
&lt;br /&gt;
			if (playerInACLGroup) then -- yep, it's the case&lt;br /&gt;
				return true -- so it's a success&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Player doesn't belong to any given ACL group&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkPermissions) then -- check if player has at least one desired permission&lt;br /&gt;
		local allowedByDefault = false -- does he have access by default&lt;br /&gt;
&lt;br /&gt;
		for permissionID = 1, #checkPermissions do -- iterate over all permissions&lt;br /&gt;
			local permissionName = checkPermissions[permissionID] -- get permission name&lt;br /&gt;
			local hasPermission = hasObjectPermissionTo(clientElement, permissionName, allowedByDefault) -- check whether player is allowed to perform certain action&lt;br /&gt;
&lt;br /&gt;
			if (hasPermission) then -- if player has access&lt;br /&gt;
				return true -- one is available (and enough), server won't bother to check others (as return keywords also breaks loop)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Not enough permissions&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkEventData) then -- if there is some data to verify&lt;br /&gt;
&lt;br /&gt;
		for dataID = 1, #checkEventData do -- iterate over each of data&lt;br /&gt;
			local dataToCheck = checkEventData[dataID] -- get each data set&lt;br /&gt;
			local eventData = dataToCheck.eventData -- this is the one we'll be verifying&lt;br /&gt;
			local equalTo = dataToCheck.equalTo -- we want to compare whether eventData == equalTo&lt;br /&gt;
			local allowedElements = dataToCheck.allowedElements -- check whether is element, and whether belongs to certain element types&lt;br /&gt;
			local allowedDataTypes = dataToCheck.allowedDataTypes -- do we restrict data to be certain type?&lt;br /&gt;
			local debugData = dataToCheck.debugData -- additional helper data&lt;br /&gt;
			local debugText = debugData and &amp;quot; (&amp;quot;..debugData..&amp;quot;)&amp;quot; or &amp;quot;&amp;quot; -- if it's present, format it nicely&lt;br /&gt;
&lt;br /&gt;
			if (equalTo) then -- equal check exists&lt;br /&gt;
				local matchingData = (eventData == equalTo) -- compare whether those two values are equal&lt;br /&gt;
&lt;br /&gt;
				if (not matchingData) then -- they aren't&lt;br /&gt;
					local failReason = &amp;quot;Data isn't equal @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedElements) then -- we do check whether is an element, and belongs to at least one given in the list&lt;br /&gt;
				local validElement = isElement(eventData) -- check if it's actual element&lt;br /&gt;
&lt;br /&gt;
				if (not validElement) then -- it's not&lt;br /&gt;
					local failReason = &amp;quot;Data isn't element @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local elementType = getElementType(eventData) -- it's element, so we want to know it's type&lt;br /&gt;
				local matchingElementType = allowedElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingElementType) then -- it's not allowed&lt;br /&gt;
					local failReason = &amp;quot;Invalid element type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedDataTypes) then -- let's check allowed data types&lt;br /&gt;
				local dataType = type(eventData) -- get data type&lt;br /&gt;
				local matchingType = allowedDataTypes[dataType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingType) then -- it isn't&lt;br /&gt;
					local failReason = &amp;quot;Invalid data type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedStringLength = dataToCheck.allowedStringLength -- if data has string length check&lt;br /&gt;
				local dataString = (dataType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
				if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
					local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
					local stringLength = utf8.len(eventData) -- get length of data string&lt;br /&gt;
					local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedTableLength = dataToCheck.allowedTableLength -- if data has table length check&lt;br /&gt;
				local dataTable = (dataType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
				if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
					local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
					local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
					local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
					local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
					for _, _ in pairs(eventData) do -- loop through whole table&lt;br /&gt;
						tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
						minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
						maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
						if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
							break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
						local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedNumberRange = dataToCheck.allowedNumberRange -- if data has number range check&lt;br /&gt;
				local dataNumber = (dataType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
				if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
					local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
					local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
					local matchingRange = (eventData &amp;gt;= minRange) and (eventData &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingRange) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this event call&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- helper function to log and handle accidents&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view player which called event&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of source element&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected event abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Event: &amp;quot;..serverEvent..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect or skipPunishment) then -- we don't want to punish player for some reason&lt;br /&gt;
		return true -- stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(clientElement, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(clientElement, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- all done, report success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onServerEvent(clientData)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local dataToPass = 10&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerEvent&amp;quot;, localPlayer, dataToPass)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = clientData, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;number&amp;quot;] = true, -- we want it to be only number&lt;br /&gt;
					},&lt;br /&gt;
					allowedNumberRange = {1, 100}, -- in range of 1 to 100&lt;br /&gt;
					debugData = &amp;quot;clientData&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerEvent&amp;quot;, root, onServerEvent)&lt;br /&gt;
&lt;br /&gt;
function onServerAdminEvent(playerToBan)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server admin event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local playerToBan = getPlayerFromName(&amp;quot;playerToBan&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerAdminEvent&amp;quot;, localPlayer, playerToBan)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerAdminEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkACLGroup = { -- we need to check whether player who called event belongs to ACL groups&lt;br /&gt;
				&amp;quot;Admin&amp;quot;, -- in this case admin group&lt;br /&gt;
			},&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = playerToBan, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;player&amp;quot;] = true, -- we want it to be player&lt;br /&gt;
					},&lt;br /&gt;
					debugData = &amp;quot;playerToBan&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerAdminEvent&amp;quot;, root, onServerAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing server-only events==&lt;br /&gt;
* It is very important to '''disable remote triggering''' ability in [https://wiki.multitheftauto.com/wiki/AddEvent addEvent], to prevent calling server-side only events from client-side.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
This example shows how you should make event server-side only.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onServerSideOnlyEvent()&lt;br /&gt;
	-- do some server-side stuff&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerSideOnlyEvent&amp;quot;, false) -- set second argument (allowRemoteTriger) to false, so it can't be called from client&lt;br /&gt;
addEventHandler(&amp;quot;onServerSideOnlyEvent&amp;quot;, root, onServerSideOnlyEvent) -- associate our event with function onServerSideOnlyEvent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing projectiles &amp;amp; explosions==&lt;br /&gt;
This section (and '''code''' is '''work in progress''') - '''use at your own risk'''.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile ammo tracker:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerProjectileAmmo = {} -- store player-held weapons ammo here&lt;br /&gt;
local playerWeaponsToTrack = { -- keep track of ammo for certain player-held weapon types, basically those which create projectile; [weaponID] = weaponSlot&lt;br /&gt;
	[16] = 8, -- grenade&lt;br /&gt;
	[17] = 8, -- teargas&lt;br /&gt;
	[18] = 8, -- molotov&lt;br /&gt;
	[35] = 7, -- rocket launcher&lt;br /&gt;
	[36] = 7, -- rocket launcher (heat-seeking)&lt;br /&gt;
	[39] = 8, -- satchel charge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for weaponID, weaponSlot in pairs(playerWeaponsToTrack) do&lt;br /&gt;
		local weaponInSlot = getPedWeapon(playerElement, weaponSlot)&lt;br /&gt;
		local weaponTotalAmmo = getPedTotalAmmo(playerElement, weaponSlot)&lt;br /&gt;
		local weaponHasAmmo = (weaponTotalAmmo &amp;gt; 0)&lt;br /&gt;
		local weaponMatching = (weaponInSlot == weaponID)&lt;br /&gt;
		local updateWeaponAmmo = (weaponMatching and weaponHasAmmo)&lt;br /&gt;
&lt;br /&gt;
		if (updateWeaponAmmo) then&lt;br /&gt;
			local storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			local newWeaponAmmo = (updateWeaponAmmo and weaponTotalAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
			if (not storedProjectileAmmo) then&lt;br /&gt;
				playerProjectileAmmo[playerElement] = {}&lt;br /&gt;
				storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			storedProjectileAmmo[weaponID] = newWeaponAmmo&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function hasPlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
	local projectileAmmo = (projectileData and projectileData[projectileWeapon])&lt;br /&gt;
&lt;br /&gt;
	return projectileAmmo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectileData) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileWeaponAmmo = playerProjectileData[projectileWeapon]&lt;br /&gt;
	local tempProjectileAmmo = (projectileWeaponAmmo - 1)&lt;br /&gt;
	local newProjectileAmmo = (tempProjectileAmmo &amp;gt; 0 and tempProjectileAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectileData[projectileWeapon] = newProjectileAmmo&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerWeaponSwitchAntiCheat(previousWeaponID, currentWeaponID)&lt;br /&gt;
	setTimer(updateProjectileAmmoForPlayer, 50, 1, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWeaponSwitch&amp;quot;, root, onPlayerWeaponSwitchAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onResourceStartAntiCheat()&lt;br /&gt;
	local playersTable = getElementsByType(&amp;quot;player&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	for playerID = 1, #playersTable do&lt;br /&gt;
		local playerElement = playersTable[playerID]&lt;br /&gt;
&lt;br /&gt;
		updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onResourceStart&amp;quot;, resourceRoot, onResourceStartAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerWastedQuitAntiCheat()&lt;br /&gt;
	playerProjectileAmmo[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerCreatedProjectiles = {} -- store count of legitimately created player projectiles; [playerElement] = {[projectileType] = activeProjectiles}&lt;br /&gt;
local projectileTypes = {&lt;br /&gt;
	[16] = { -- Grenade&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[17] = { -- Teargas&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[17] = true, -- teargas&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[18] = { -- Molotov&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[19] = { -- Rocket&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[425] = true, -- hunter&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[20] = { -- Rocket (heat-seeking)&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[21] = { -- Airbomb&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[39] = { -- Satchel charge&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[39] = true, -- satchel charge&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[58] = { -- Hydra flare&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportProjectileAbnormality(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	local projectileSyncer = inspect(playerElement)&lt;br /&gt;
	local projectilePosition = projectileX..&amp;quot;, &amp;quot;..projectileY..&amp;quot;, &amp;quot;..projectileZ&lt;br /&gt;
	local projectileZoneName = getZoneName(projectileX, projectileY, projectileZ, false)&lt;br /&gt;
	local projectileCityName = getZoneName(projectileX, projectileY, projectileZ, true)&lt;br /&gt;
	local projectileLog =&lt;br /&gt;
		&amp;quot;* Detected projectile abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile syncer: &amp;quot;..projectileSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile type: &amp;quot;..projectileType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile position: &amp;quot;..projectilePosition.. &amp;quot; (&amp;quot;..projectileZoneName..&amp;quot;, &amp;quot;..projectileCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(projectileLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processProjectileChecks(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
	local projectileAllowed = projectileData.projectileAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not projectileAllowed) then&lt;br /&gt;
		return false, &amp;quot;Projectile not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileAllowedWeapons = projectileData.projectileAllowedWeapons&lt;br /&gt;
&lt;br /&gt;
	if (projectileAllowedWeapons) then&lt;br /&gt;
		local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
		local allowedWeapon = projectileAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedWeapon) then&lt;br /&gt;
			return false, &amp;quot;Player is not holding correct weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
		local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
		if (not playerHasAmmo) then&lt;br /&gt;
			return false, &amp;quot;Player doesn't have ammo for weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerVehicle) then&lt;br /&gt;
		local projectileAllowedVehicles = projectileData.projectileAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
		if (projectileAllowedVehicles) then&lt;br /&gt;
			local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
			local allowedVehicle = projectileAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedVehicle) then&lt;br /&gt;
				return false, &amp;quot;Player is not inside allowed vehicles&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
			local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
			if (not playerDriver) then&lt;br /&gt;
				return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Player in vehicle&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxDistanceFromCreator = projectileData.projectileMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToProjectile = getDistanceBetweenPoints3D(playerX, playerY, playerZ, projectileX, projectileY, projectileZ)&lt;br /&gt;
		local matchingProjectileDistance = (distanceToProjectile &amp;lt;= projectileMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileDistance) then&lt;br /&gt;
			return false, &amp;quot;Projectile distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxVelocity = projectileData.projectileMaxVelocity&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxVelocity) then&lt;br /&gt;
		local projectileVelocity = (projectileVX ^ 2 + projectileVY ^ 2 + projectileVZ ^ 2)&lt;br /&gt;
		local projectileSpeed = math.sqrt(projectileVelocity)&lt;br /&gt;
		local matchingProjectileVelocity = (projectileSpeed &amp;lt;= projectileMaxVelocity)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileVelocity) then&lt;br /&gt;
			return false, &amp;quot;Projectile velocity mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trackPlayerProjectile(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		playerCreatedProjectiles[playerElement] = {}&lt;br /&gt;
		playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID] or 0&lt;br /&gt;
	local newProjectilesCount = (projectilesByType + 1)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
&lt;br /&gt;
	if (not projectileData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileBehaviour = projectileData.projectileOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not projectileBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = projectileBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = projectileBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = projectileBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID]&lt;br /&gt;
&lt;br /&gt;
	if (not projectilesByType) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local tempProjectilesCount = (projectilesByType - 1)&lt;br /&gt;
	local newProjectilesCount = (tempProjectilesCount &amp;gt; 0 and tempProjectilesCount or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerProjectileCreationAntiCheat(projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local approvedProjectile, detectionCode = processProjectileChecks(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedProjectile) then&lt;br /&gt;
		trackPlayerProjectile(source, projectileType)&lt;br /&gt;
&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportProjectileAbnormality(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerProjectileCreation&amp;quot;, root, onPlayerProjectileCreationAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerQuitAntiCheat()&lt;br /&gt;
	playerCreatedProjectiles[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Explosions handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local rocketInstantExplosionDistanceThreshold = 1.55 -- distance below which only explosion will be created (and not rocket projectile, hence not calling onPlayerProjectileCreation); do not change it&lt;br /&gt;
local explosionTypes = { -- more specific info on explosion, and whether it is allowed&lt;br /&gt;
	[0] = { -- Grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[1] = { -- Molotov&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[2] = { -- Rocket&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[3] = { -- Rocket weak&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[4] = { -- Car&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[5] = { -- Car quick&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[6] = { -- Boat&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[7] = { -- Heli&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[8] = { -- Mine&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[9] = { -- Object&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[10] = { -- Tank grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionMaxDistanceFromCreator = 80,&lt;br /&gt;
		explosionAllowedVehicles = {&lt;br /&gt;
			[432] = true,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	[11] = { -- Small&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[12] = { -- Tiny&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportExplosionAbnormality(playerElement, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	local explosionSyncer = inspect(playerElement)&lt;br /&gt;
	local explosionType = explosionType&lt;br /&gt;
	local explosionPosition = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionZoneName = getZoneName(explosionX, explosionY, explosionZ, false)&lt;br /&gt;
	local explosionCityName = getZoneName(explosionX, explosionY, explosionZ, true)&lt;br /&gt;
	local explosionLog =&lt;br /&gt;
		&amp;quot;* Detected explosion abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion syncer: &amp;quot;..explosionSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion type: &amp;quot;..explosionType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion position: &amp;quot;..explosionPosition.. &amp;quot; (&amp;quot;..explosionZoneName..&amp;quot;, &amp;quot;..explosionCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(explosionLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processExplosionChecks(playerElement, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
	local explosionAllowed = explosionData.explosionAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not explosionAllowed) then&lt;br /&gt;
		return false, &amp;quot;Explosion type not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rocketExplosion = (explosionType == 2)&lt;br /&gt;
&lt;br /&gt;
	if (rocketExplosion) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local instantExplosion = (distanceToExplosion &amp;lt; rocketInstantExplosionDistanceThreshold)&lt;br /&gt;
&lt;br /&gt;
		if (instantExplosion) then&lt;br /&gt;
			local explosionAllowedWeapons = explosionData.explosionAllowedWeapons&lt;br /&gt;
			local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
			local allowedWeapon = explosionAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedWeapon) then&lt;br /&gt;
				return false, &amp;quot;Player is not holding rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
			local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
			if (not playerHasAmmo) then&lt;br /&gt;
				return false, &amp;quot;Player doesn't have ammo for rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedProjectiles = explosionData.explosionAllowedProjectiles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedProjectiles) then&lt;br /&gt;
&lt;br /&gt;
		for projectileID, _ in pairs(explosionAllowedProjectiles) do&lt;br /&gt;
			local atleastOneProjectile = decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
&lt;br /&gt;
			if (atleastOneProjectile) then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Explosion created without respective projectile&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedVehicles = explosionData.explosionAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedVehicles) then&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
		if (not playerVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is not in vehicle&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
		local allowedVehicle = explosionAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is inside not allowed vehicles&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
		local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
		if (not playerDriver) then&lt;br /&gt;
			return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionMaxDistanceFromCreator = explosionData.explosionMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (explosionMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local matchingExplosionDistance = (distanceToExplosion &amp;lt; explosionMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingExplosionDistance) then&lt;br /&gt;
			return false, &amp;quot;Explosion distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
&lt;br /&gt;
	if (not explosionData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionBehaviour = explosionData.explosionOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not explosionBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = explosionBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = explosionBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = explosionBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onExplosionAntiCheat(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local serverSyncExplosion = (source == root)&lt;br /&gt;
&lt;br /&gt;
	if (serverSyncExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedExplosion, detectionCode = processExplosionChecks(source, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportExplosionAbnormality(source, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosionAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Handling non-registered events==&lt;br /&gt;
&lt;br /&gt;
See: [[onPlayerTriggerInvalidEvent]]&lt;br /&gt;
&lt;br /&gt;
==Handling events spam==&lt;br /&gt;
See: [[onPlayerTriggerEventThreshold]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81986</id>
		<title>User:DColeman/RU:Безопасность скриптов</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81986"/>
		<updated>2025-05-10T12:31:39Z</updated>

		<summary type="html">&lt;p&gt;DColeman: Translation progress&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Информация по памяти клиента==&lt;br /&gt;
&lt;br /&gt;
Начиная с самых основ:&lt;br /&gt;
* Вы должны понимать, что все, что вы храните на стороне клиента, включая .lua файлы, находится под риском. Любая конфиденциальная или критическая информация, которая как-либо оказывается на клиентской стороне (ПК игрока), может быть прочитана и/или изменена чит-клиентами.&lt;br /&gt;
* Чтобы сохранить конфиденциальную информацию и логику скрипта в секрете - используйте серверную сторону.&lt;br /&gt;
* Обратите внимание, что скрипты, отмеченные как общие ('''shared''') также действуют как '''клиентские''', что означает, что все вышеперечисленное также применяется и к ним. Например, объявление скрипта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;shared&amp;quot;/&amp;gt; &amp;lt;!-- этот скрипт будет запущен и на сервере и на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое, что и объявление:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;client&amp;quot;/&amp;gt; &amp;lt;!-- объявляем скрипт на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;server&amp;quot;/&amp;gt; &amp;lt;!-- делаем то же самое, но на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Дополнительный слой защиты==&lt;br /&gt;
&lt;br /&gt;
Чтобы незначительно ''усложнить жизнь*'' тем, у кого есть плохие намерения по отношению к вашему серверу, вы можете использовать аттрибут cache (и/или [https://luac.mtasa.com/ скомпилированные lua скрипты (также известные как Luac) с '''3''' уровнем дополнительной обфускации] - [https://wiki.multitheftauto.com/wiki/Lua_compilation_API API]), доступный в [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], вместе с настройкой встроенного античита МТА с включением SD (специальными кодами обнаружения), для дополнительной информации смотрите [https://wiki.multitheftauto.com/wiki/Anti-cheat_guide гайд по античиту].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;shared.lua&amp;quot; type=&amp;quot;shared&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''Усложнить жизнь'' '''не означает &amp;quot;сделать невозможным&amp;quot;''' получение вашего клиентского Lua кода, это означает, что '''большинство''' людей не смогут просмотреть ваши .lua файлы - тех, кто ищет логические ошибки (баги) или отсутствующие/некорректные проверки безопасности.&lt;br /&gt;
* Может использоваться на '''client''' и '''shared''' типах скриптов (не имеет эффекта на серверных скриптах).&lt;br /&gt;
* Это не удалит Lua файлы, которые были загружены ранее.&lt;br /&gt;
&lt;br /&gt;
==Обнаружение и обезвреживание бэкдоров и читов==&lt;br /&gt;
'''Чтобы минимизировать (или исключить) урон, причиненный Lua скриптами:'''&lt;br /&gt;
* Всегда обновляйте ваш сервер до самой актуальной версии, вы можете [https://nightly.multitheftauto.com/ загрузить новейшие сборки сервера отсюда.] С информацией по определенным сборкам можно ознакомиться [https://buildinfo.multitheftauto.com/ здесь.]&lt;br /&gt;
* Всегда обновляйте ресурсы, установленные на сервере, до самой актуальной версии, вы можете [https://github.com/multitheftauto/mtasa-resources загрузить новейшие (стандартные) ресурсы из репозитория GitHub.] Они часто содержат обновления безопасности, которые влияют на устойчивость вашего сервера к атакам.&lt;br /&gt;
* Убедитесь, что [https://wiki.multitheftauto.com/wiki/Access%20Control%20List ACL (список контроля доступа)] правильно сконфигурирован - он поможет заблокировать ресурсам некоторые потенциально опасные функции.&lt;br /&gt;
* Никогда не выдавайте админ-права ресурсам (включая карты), полученным из неизвестных источников.&lt;br /&gt;
* Перед запуском недоверенного ресурса, изучите:&lt;br /&gt;
** Его [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], на наличие возможных скрытых скриптов, которые скрываются под видом файлов с другими расширениями.&lt;br /&gt;
** Его исходный код, на наличие вредоносной логики.&lt;br /&gt;
* Не запускайте и не используйте скомпилированные ресурсы (скрипты), в легитимности которых вы не уверены.&lt;br /&gt;
&lt;br /&gt;
'''Чтобы минимизировать урон, причиненный читером, зашедшим на сервер:'''&lt;br /&gt;
* При создании скриптов '''никогда не доверяйте данным, полученным со стороны клиента'''.&lt;br /&gt;
* При просмотре скриптов на наличие дыр безопасности, сверяйтесь с данными, поступающими от клиента, которому можно доверять.&lt;br /&gt;
* Отправлены могут быть любые данные, поэтому любые серверные скрипты, которые коммуницируют с клиентскими и получают информацию от игроков, '''должны проверять информацию на корректность''' перед дальнейшим использованием. Подделка данных чаще всего происходит с помощью [[setElementData]] и [[triggerServerEvent]].&lt;br /&gt;
* Вы не должны полагаться только на [https://wiki.multitheftauto.com/wiki/Serial серийный номер] игрока, когда они используются для критических действий (авто-входа/администраторских действий). '''Не гарантируется, что серийный номер игрока уникален и не подделан'''. Это причина, почему вы должны '''поместить его за''' системой аккаунтов, в качестве '''важного аутентификационного фактора'''.&lt;br /&gt;
* Серверная логика '''не может быть обойдена''' (если только сервер не взломан или в коде нет ошибки, но это совсем другой сценарий.) - '''используйте это в своих интересах'''. В большинстве случаев, вы можете реализовать проверки безопасности только на серверной стороне, не привлекая клиент.&lt;br /&gt;
* Следование аксиоме '''''“Все параметры, включая source могут быть подделаны и им нельзя доверять. Глобальной переменной client можно доверять.”''''' дает вам уверенность в том, что игрок, к которому вы обращаетесь (через переменную '''client''') '''именно тот, кто вызвал событие'''. Это защищает вас от тех ситуаций, когда читер может вызывать, например, админские события (например, кик/бан игрока), указывая реального администратора ('''второй''' аргумент '''[[triggerServerEvent]]'''), или вызывать события за других игроков (будто они вызвали их, но в реальности это делает читер) - всего лишь из-за использования некорректной переменной. Чтобы убедиться, что вы это поняли, обратимся к коду:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	НИКОГДА НЕ ИСПОЛЬЗУЙТЕ ЭТОТ КОД - ОН АБСОЛЮТНО НЕВЕРНЫЙ И НЕБЕЗОПАСНЫЙ&lt;br /&gt;
	ПРОБЛЕМА: ИСПОЛЬЗУЯ ПЕРЕМЕННУЮ 'source' В hasObjectPermissionTo ВЫ ОСТАВЛЯЕТЕ ДЫРУ ДЛЯ ЧИТЕРОВ&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerWrongAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(source, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- эксплойт находится здесь...&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerWrongAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerWrongAdminEvent&amp;quot;, root, onServerWrongAdminEvent)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	onServerCorrectAdminEvent ПРЕКРАСНО ЗАЩИЩЕН, ТАК, КАК ДОЛЖНО БЫТЬ&lt;br /&gt;
	ЗДЕСЬ НЕТ ПРОБЛЕМ: МЫ ИСПОЛЬЗУЕМ 'client' В hasObjectPermissionTo, ЧТО ЗАЩИЩАЕТ НАС ОТ ПОДМЕНЫ ИГРОКА, КОТОРЫЙ ВЫЗВАЛ СОБЫТИЕ&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
function onServerCorrectAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' указывает на игрока, который вызвал событие, и должен использоваться в качестве проверки безопасности (для предотвращения подмены игрока)&lt;br /&gt;
		return false -- если эта переменная не существует (по неизвестной причине), останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- по стандарту не разрешаем действие, сморите: https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(client, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- если игрок имеет права&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- если у игрока нет прав&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- проверяем, что аргумент, переданный игроком, является элементом&lt;br /&gt;
	if (not validElement) then -- если нет...&lt;br /&gt;
		return false -- останавливаем выполнение кода&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- это элемент, получаем его тип&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- проверяем, что тип элемента - игрок&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- это не игрок&lt;br /&gt;
		return false -- останавливаемся&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- делаем, что нужно&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerCorrectAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerCorrectAdminEvent&amp;quot;, root, onServerCorrectAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing setElementData==&lt;br /&gt;
&lt;br /&gt;
* You should refrain from using [[element data]] everywhere, it should be only used when really necessary. It is advised to replace it with [[triggerClientEvent]] instead.&lt;br /&gt;
* If you still insist on using it, it is recommended to set '''4th''' argument in [[setElementData]] to '''false''' (disabling sync for this, certain data) and use subscriber mode - [[addElementDataSubscriber]], for both security &amp;amp; performance reasons described in [https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent event section.]&lt;br /&gt;
{{Important Note|Disabling sync however, doesn't fully protect data key. It would be still vulnerable by default, but in this case you are not leaving it in plain sight. Using [[getAllElementData]] or digging in RAM wouldn't expose it, since it won't be synced to client in first place. Therefore, it needs to be added to anti-cheat as well.}} &lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of basic element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = tostring(sourceElement) -- element which received data&lt;br /&gt;
	local logOldValue = tostring(oldValue) -- old value&lt;br /&gt;
	local logNewValue = tostring(newValue) -- new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;=======================================\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;=======================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	local logVisibleTo = root -- specify who will see this log in console, in this case each player connected to server&lt;br /&gt;
	local hadData = (oldValue ~= nil) -- check if element had such data before&lt;br /&gt;
&lt;br /&gt;
	if (hadData) then -- if element had such data before&lt;br /&gt;
		setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
	else&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it completely&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	outputConsole(logText, logVisibleTo) -- print it to console&lt;br /&gt;
&lt;br /&gt;
	return true -- all success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeBasicAC(dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not go further&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkSpecialThing = (dataKey == &amp;quot;special_thing&amp;quot;) -- compare whether dataKey matches &amp;quot;special_thing&amp;quot;&lt;br /&gt;
	local checkFlagWaving = (dataKey == &amp;quot;flag_waving&amp;quot;) -- compare whether dataKey matches &amp;quot;flag_waving&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if (checkSpecialThing) then -- if it does, do our security checks&lt;br /&gt;
		local invalidElement = (client ~= source) -- verify whether source element is different from player which changed data&lt;br /&gt;
&lt;br /&gt;
		if (invalidElement) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;special_thing&amp;quot; can only be set for player himself&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkFlagWaving) then -- if it does, do our security checks&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(client) -- get player's current vehicle&lt;br /&gt;
		local invalidVehicle = (playerVehicle ~= source) -- verify whether source element is different from player's vehicle&lt;br /&gt;
&lt;br /&gt;
		if (invalidVehicle) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;flag_waving&amp;quot; can only be set for player's own vehicle&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeBasicAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of advanced element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan, allowOnlyProtectedKeys to true (as per default configuration)&lt;br /&gt;
	If allowOnlyProtectedKeys is enabled, do not forget to add every client-side element data key to protectedKeys table - otherwise you will face false-positives&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (handleDataChange) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	[&amp;quot;keyName&amp;quot;] = { -- name of key which would be protected&lt;br /&gt;
		onlyForPlayerHimself = true, -- enabling this (true) will make sure that this element data key can only be set on player who synced it (ignores onlyForOwnPlayerVeh and allowForElements), use false/nil to disable this&lt;br /&gt;
		onlyForOwnPlayerVeh = false, -- enabling this (true) will make sure that this element data key can only be set on player's current vehicle who synced it (ignores allowForElements), use false/nil to disable this&lt;br /&gt;
		allowForElements = { -- restrict this key for certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
			[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = { -- restrict this key for certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 32}, -- if value is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
		allowedTableLength = {1, 64}, -- if value is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
		allowedNumberRange = {1, 128}, -- if value is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
	}&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering element data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local allowOnlyProtectedKeys = true -- disallow (remove by using removeElementData) every element data besides those given in protectedKeys table; in case someone wanted to flood server with garbage keys, which would be kept in memory until server restart or manual remove - setElementData(source, key, nil) won't remove it; it has to be removeElementData&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
local protectedKeys = {&lt;br /&gt;
	[&amp;quot;vehicleNumber&amp;quot;] = { -- we want vehicleNumber to be set only on vehicles, with stricte numbers in range of 1-100&lt;br /&gt;
		allowForElements = {&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedNumberRange = {1, 100},&lt;br /&gt;
	},&lt;br /&gt;
	[&amp;quot;personalVehData&amp;quot;] = { -- we want be able to set personalVehData only on current vehicle, also it should be a string with length between 1-24&lt;br /&gt;
		onlyForOwnPlayerVeh = true,&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 24},&lt;br /&gt;
	},&lt;br /&gt;
	-- perform security checks on keys stored in this table, in a convenient way&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of element which received data&lt;br /&gt;
	local logOldValue = inspect(oldValue) -- in-depth view of old value&lt;br /&gt;
	local logNewValue = inspect(newValue) -- in-depth view of new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Fail reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (forceRemove) then -- we don't want this element data key to exist at all&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it&lt;br /&gt;
&lt;br /&gt;
		return true -- return success and stop here, we don't need further checks&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
&lt;br /&gt;
	return true -- return success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function handleDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from protectedKeys), false otherwise&lt;br /&gt;
	local protectedKey = protectedKeys[dataKey] -- look up whether key changed is stored in protectedKeys table&lt;br /&gt;
&lt;br /&gt;
	if (not protectedKey) then -- if it's not&lt;br /&gt;
&lt;br /&gt;
		if (allowOnlyProtectedKeys) then -- if we don't want garbage keys&lt;br /&gt;
			local failReason = &amp;quot;Key isn't present in protectedKeys&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = true -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return true -- this key isn't protected, let it through&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local onlyForPlayerHimself = protectedKey.onlyForPlayerHimself -- if key has &amp;quot;self-set&amp;quot; lock&lt;br /&gt;
	local onlyForOwnPlayerVeh = protectedKey.onlyForOwnPlayerVeh -- if key has &amp;quot;self-vehicle&amp;quot; lock&lt;br /&gt;
	local allowForElements = protectedKey.allowForElements -- if key has element type check&lt;br /&gt;
	local allowedDataTypes = protectedKey.allowedDataTypes -- if key has allowed data type check&lt;br /&gt;
&lt;br /&gt;
	if (onlyForPlayerHimself) then -- if &amp;quot;self-set&amp;quot; lock is active&lt;br /&gt;
		local matchingElement = (clientElement == sourceElement) -- verify whether player who set data is equal to element which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElement) then -- if it's not matching&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player himself&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (onlyForOwnPlayerVeh) then -- if &amp;quot;self-vehicle&amp;quot; lock is active&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(clientElement) -- get current vehicle of player which set data&lt;br /&gt;
		local matchingVehicle = (playerVehicle == sourceElement) -- check whether it matches the one which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingVehicle) then -- if it doesn't match&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player's own vehicle&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowForElements) then -- check if it's one of them&lt;br /&gt;
		local elementType = getElementType(sourceElement) -- get type of element whose data changed&lt;br /&gt;
		local matchingElementType = allowForElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElementType) then -- this isn't matching&lt;br /&gt;
			local failReason = &amp;quot;Invalid element type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowedDataTypes) then -- if there's allowed data types&lt;br /&gt;
		local valueType = type(newValue) -- get data type of value&lt;br /&gt;
		local matchingType = allowedDataTypes[valueType] -- check if it's one of allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingType) then -- if it's not then&lt;br /&gt;
			local failReason = &amp;quot;Invalid data type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedStringLength = protectedKey.allowedStringLength -- if key has specified string length check&lt;br /&gt;
		local dataString = (valueType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
		if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
			local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
			local stringLength = utf8.len(newValue) -- get length of data string&lt;br /&gt;
			local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedTableLength = protectedKey.allowedTableLength -- if key has table length check&lt;br /&gt;
		local dataTable = (valueType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
		if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
			local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
			local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
			local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
			local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
			for _, _ in pairs(newValue) do -- loop through whole table&lt;br /&gt;
				tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
				minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
				maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
				if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
					break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
				local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedNumberRange = protectedKey.allowedNumberRange -- if key has allowed number range check&lt;br /&gt;
		local dataNumber = (valueType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
		if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
			local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
			local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
			local matchingRange = (newValue &amp;gt;= minRange) and (newValue &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingRange) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this data key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeAdvancedAC(dataKey, oldValue, newValue) -- this event makes use of handleDataChange, the code was split for better readability&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not continue&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedChange = handleDataChange(client, source, dataKey, oldValue, newValue) -- run our security checks&lt;br /&gt;
&lt;br /&gt;
	if (approvedChange) then -- it's all cool and good&lt;br /&gt;
		return false -- we don't need further action&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(client, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(client, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeAdvancedAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing triggerServerEvent==&lt;br /&gt;
&lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player (client) '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
* Do '''not''' use the same name for your custom event as MTA native server events (which aren't remotely triggerable by default), e.g: '''onPlayerLogin'''; doing so would open door for cheaters manipulations.&lt;br /&gt;
* Be aware to which players event is sent via [[triggerClientEvent]]. For both security &amp;amp; performance reasons, admin like events should be received by admins only (to prevent confidential data being accessed), in the same time you shouldn't send each event to everyone on server (e.g: login success event which hides login panel for certain player). [[triggerClientEvent]] allows you to specify the event receiver as first (optional) argument. It defaults to [[root]], meaning if you don't specify it, it will be sent to everyone connected to server - even those who are still downloading server cache (which results in ''Server triggered clientside event eventName, but event is not added clientside.''). You can either pass player element or table with receivers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playersToReceiveEvent = {player1, player2, player3} -- each playerX is player element&lt;br /&gt;
&lt;br /&gt;
triggerClientEvent(playersToReceiveEvent, ...) -- do not forget to fill the latter part of arguments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of anti-cheat function designed for events, used for data validation for both normal, and admin events which are called from client-side.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan to true (as per default configuration)&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (processServerEventData) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	checkACLGroup = { -- check whether player who called event belongs to at least one group below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;Admin&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkPermissions = { -- check whether player who called event has permission to at least one thing below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;function.kickPlayer&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkEventData = {&lt;br /&gt;
		{&lt;br /&gt;
			debugData = &amp;quot;source&amp;quot;, -- optional details for report shown in debug message&lt;br /&gt;
			eventData = source, -- data we want to verify&lt;br /&gt;
			equalTo = client, -- compare whether eventData == equalTo&lt;br /&gt;
			allowedElements = { -- restrict eventData to be certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
				[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedDataTypes = { -- restrict eventData to be certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
				[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedStringLength = {1, 32}, -- if eventData is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
			allowedTableLength = {1, 64}, -- if eventData is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
			allowedNumberRange = {1, 128}, -- if eventData is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering server event data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
function processServerEventData(clientElement, sourceElement, serverEvent, securityChecks) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from securityChecks), false otherwise&lt;br /&gt;
	if (not securityChecks) then -- if we haven't passed any security checks&lt;br /&gt;
		return true -- nothing to check, let code go further&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (not clientElement) then -- if client variable isn't available for some reason (although it should never happen)&lt;br /&gt;
		local failReason = &amp;quot;Client variable not present&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkACLGroup = securityChecks.checkACLGroup -- if there's any ACL groups to check&lt;br /&gt;
	local checkPermissions = securityChecks.checkPermissions -- if there's any permissions to check&lt;br /&gt;
	local checkEventData = securityChecks.checkEventData -- if there's any data checks&lt;br /&gt;
&lt;br /&gt;
	if (checkACLGroup) then -- let's check player ACL groups&lt;br /&gt;
		local playerAccount = getPlayerAccount(clientElement) -- get current account of player&lt;br /&gt;
		local guestAccount = isGuestAccount(playerAccount) -- if account is guest (meaning player is not logged in)&lt;br /&gt;
&lt;br /&gt;
		if (guestAccount) then -- it's the case&lt;br /&gt;
			local failReason = &amp;quot;Can't retrieve player login - guest account&amp;quot; -- reason shown in report&lt;br /&gt;
			local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
			reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local accountName = getAccountName(playerAccount) -- get name of player's current account&lt;br /&gt;
		local aclString = &amp;quot;user.&amp;quot;..accountName -- format it for further use in isObjectInACLGroup function&lt;br /&gt;
&lt;br /&gt;
		for groupID = 1, #checkACLGroup do -- iterate over table of given groups&lt;br /&gt;
			local groupName = checkACLGroup[groupID] -- get each group name&lt;br /&gt;
			local aclGroup = aclGetGroup(groupName) -- check if such group exists&lt;br /&gt;
&lt;br /&gt;
			if (not aclGroup) then -- it doesn't&lt;br /&gt;
				local failReason = &amp;quot;ACL group '&amp;quot;..groupName..&amp;quot;' is missing&amp;quot; -- reason shown in report&lt;br /&gt;
				local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
				reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local playerInACLGroup = isObjectInACLGroup(aclString, aclGroup) -- check if player belong to the group&lt;br /&gt;
&lt;br /&gt;
			if (playerInACLGroup) then -- yep, it's the case&lt;br /&gt;
				return true -- so it's a success&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Player doesn't belong to any given ACL group&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkPermissions) then -- check if player has at least one desired permission&lt;br /&gt;
		local allowedByDefault = false -- does he have access by default&lt;br /&gt;
&lt;br /&gt;
		for permissionID = 1, #checkPermissions do -- iterate over all permissions&lt;br /&gt;
			local permissionName = checkPermissions[permissionID] -- get permission name&lt;br /&gt;
			local hasPermission = hasObjectPermissionTo(clientElement, permissionName, allowedByDefault) -- check whether player is allowed to perform certain action&lt;br /&gt;
&lt;br /&gt;
			if (hasPermission) then -- if player has access&lt;br /&gt;
				return true -- one is available (and enough), server won't bother to check others (as return keywords also breaks loop)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Not enough permissions&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkEventData) then -- if there is some data to verify&lt;br /&gt;
&lt;br /&gt;
		for dataID = 1, #checkEventData do -- iterate over each of data&lt;br /&gt;
			local dataToCheck = checkEventData[dataID] -- get each data set&lt;br /&gt;
			local eventData = dataToCheck.eventData -- this is the one we'll be verifying&lt;br /&gt;
			local equalTo = dataToCheck.equalTo -- we want to compare whether eventData == equalTo&lt;br /&gt;
			local allowedElements = dataToCheck.allowedElements -- check whether is element, and whether belongs to certain element types&lt;br /&gt;
			local allowedDataTypes = dataToCheck.allowedDataTypes -- do we restrict data to be certain type?&lt;br /&gt;
			local debugData = dataToCheck.debugData -- additional helper data&lt;br /&gt;
			local debugText = debugData and &amp;quot; (&amp;quot;..debugData..&amp;quot;)&amp;quot; or &amp;quot;&amp;quot; -- if it's present, format it nicely&lt;br /&gt;
&lt;br /&gt;
			if (equalTo) then -- equal check exists&lt;br /&gt;
				local matchingData = (eventData == equalTo) -- compare whether those two values are equal&lt;br /&gt;
&lt;br /&gt;
				if (not matchingData) then -- they aren't&lt;br /&gt;
					local failReason = &amp;quot;Data isn't equal @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedElements) then -- we do check whether is an element, and belongs to at least one given in the list&lt;br /&gt;
				local validElement = isElement(eventData) -- check if it's actual element&lt;br /&gt;
&lt;br /&gt;
				if (not validElement) then -- it's not&lt;br /&gt;
					local failReason = &amp;quot;Data isn't element @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local elementType = getElementType(eventData) -- it's element, so we want to know it's type&lt;br /&gt;
				local matchingElementType = allowedElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingElementType) then -- it's not allowed&lt;br /&gt;
					local failReason = &amp;quot;Invalid element type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedDataTypes) then -- let's check allowed data types&lt;br /&gt;
				local dataType = type(eventData) -- get data type&lt;br /&gt;
				local matchingType = allowedDataTypes[dataType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingType) then -- it isn't&lt;br /&gt;
					local failReason = &amp;quot;Invalid data type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedStringLength = dataToCheck.allowedStringLength -- if data has string length check&lt;br /&gt;
				local dataString = (dataType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
				if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
					local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
					local stringLength = utf8.len(eventData) -- get length of data string&lt;br /&gt;
					local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedTableLength = dataToCheck.allowedTableLength -- if data has table length check&lt;br /&gt;
				local dataTable = (dataType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
				if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
					local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
					local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
					local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
					local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
					for _, _ in pairs(eventData) do -- loop through whole table&lt;br /&gt;
						tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
						minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
						maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
						if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
							break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
						local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedNumberRange = dataToCheck.allowedNumberRange -- if data has number range check&lt;br /&gt;
				local dataNumber = (dataType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
				if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
					local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
					local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
					local matchingRange = (eventData &amp;gt;= minRange) and (eventData &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingRange) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this event call&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- helper function to log and handle accidents&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view player which called event&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of source element&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected event abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Event: &amp;quot;..serverEvent..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect or skipPunishment) then -- we don't want to punish player for some reason&lt;br /&gt;
		return true -- stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(clientElement, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(clientElement, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- all done, report success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onServerEvent(clientData)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local dataToPass = 10&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerEvent&amp;quot;, localPlayer, dataToPass)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = clientData, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;number&amp;quot;] = true, -- we want it to be only number&lt;br /&gt;
					},&lt;br /&gt;
					allowedNumberRange = {1, 100}, -- in range of 1 to 100&lt;br /&gt;
					debugData = &amp;quot;clientData&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerEvent&amp;quot;, root, onServerEvent)&lt;br /&gt;
&lt;br /&gt;
function onServerAdminEvent(playerToBan)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server admin event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local playerToBan = getPlayerFromName(&amp;quot;playerToBan&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerAdminEvent&amp;quot;, localPlayer, playerToBan)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerAdminEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkACLGroup = { -- we need to check whether player who called event belongs to ACL groups&lt;br /&gt;
				&amp;quot;Admin&amp;quot;, -- in this case admin group&lt;br /&gt;
			},&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = playerToBan, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;player&amp;quot;] = true, -- we want it to be player&lt;br /&gt;
					},&lt;br /&gt;
					debugData = &amp;quot;playerToBan&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerAdminEvent&amp;quot;, root, onServerAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing server-only events==&lt;br /&gt;
* It is very important to '''disable remote triggering''' ability in [https://wiki.multitheftauto.com/wiki/AddEvent addEvent], to prevent calling server-side only events from client-side.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
This example shows how you should make event server-side only.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onServerSideOnlyEvent()&lt;br /&gt;
	-- do some server-side stuff&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerSideOnlyEvent&amp;quot;, false) -- set second argument (allowRemoteTriger) to false, so it can't be called from client&lt;br /&gt;
addEventHandler(&amp;quot;onServerSideOnlyEvent&amp;quot;, root, onServerSideOnlyEvent) -- associate our event with function onServerSideOnlyEvent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing projectiles &amp;amp; explosions==&lt;br /&gt;
This section (and '''code''' is '''work in progress''') - '''use at your own risk'''.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile ammo tracker:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerProjectileAmmo = {} -- store player-held weapons ammo here&lt;br /&gt;
local playerWeaponsToTrack = { -- keep track of ammo for certain player-held weapon types, basically those which create projectile; [weaponID] = weaponSlot&lt;br /&gt;
	[16] = 8, -- grenade&lt;br /&gt;
	[17] = 8, -- teargas&lt;br /&gt;
	[18] = 8, -- molotov&lt;br /&gt;
	[35] = 7, -- rocket launcher&lt;br /&gt;
	[36] = 7, -- rocket launcher (heat-seeking)&lt;br /&gt;
	[39] = 8, -- satchel charge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for weaponID, weaponSlot in pairs(playerWeaponsToTrack) do&lt;br /&gt;
		local weaponInSlot = getPedWeapon(playerElement, weaponSlot)&lt;br /&gt;
		local weaponTotalAmmo = getPedTotalAmmo(playerElement, weaponSlot)&lt;br /&gt;
		local weaponHasAmmo = (weaponTotalAmmo &amp;gt; 0)&lt;br /&gt;
		local weaponMatching = (weaponInSlot == weaponID)&lt;br /&gt;
		local updateWeaponAmmo = (weaponMatching and weaponHasAmmo)&lt;br /&gt;
&lt;br /&gt;
		if (updateWeaponAmmo) then&lt;br /&gt;
			local storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			local newWeaponAmmo = (updateWeaponAmmo and weaponTotalAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
			if (not storedProjectileAmmo) then&lt;br /&gt;
				playerProjectileAmmo[playerElement] = {}&lt;br /&gt;
				storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			storedProjectileAmmo[weaponID] = newWeaponAmmo&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function hasPlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
	local projectileAmmo = (projectileData and projectileData[projectileWeapon])&lt;br /&gt;
&lt;br /&gt;
	return projectileAmmo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectileData) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileWeaponAmmo = playerProjectileData[projectileWeapon]&lt;br /&gt;
	local tempProjectileAmmo = (projectileWeaponAmmo - 1)&lt;br /&gt;
	local newProjectileAmmo = (tempProjectileAmmo &amp;gt; 0 and tempProjectileAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectileData[projectileWeapon] = newProjectileAmmo&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerWeaponSwitchAntiCheat(previousWeaponID, currentWeaponID)&lt;br /&gt;
	setTimer(updateProjectileAmmoForPlayer, 50, 1, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWeaponSwitch&amp;quot;, root, onPlayerWeaponSwitchAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onResourceStartAntiCheat()&lt;br /&gt;
	local playersTable = getElementsByType(&amp;quot;player&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	for playerID = 1, #playersTable do&lt;br /&gt;
		local playerElement = playersTable[playerID]&lt;br /&gt;
&lt;br /&gt;
		updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onResourceStart&amp;quot;, resourceRoot, onResourceStartAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerWastedQuitAntiCheat()&lt;br /&gt;
	playerProjectileAmmo[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerCreatedProjectiles = {} -- store count of legitimately created player projectiles; [playerElement] = {[projectileType] = activeProjectiles}&lt;br /&gt;
local projectileTypes = {&lt;br /&gt;
	[16] = { -- Grenade&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[17] = { -- Teargas&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[17] = true, -- teargas&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[18] = { -- Molotov&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[19] = { -- Rocket&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[425] = true, -- hunter&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[20] = { -- Rocket (heat-seeking)&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[21] = { -- Airbomb&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[39] = { -- Satchel charge&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[39] = true, -- satchel charge&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[58] = { -- Hydra flare&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportProjectileAbnormality(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	local projectileSyncer = inspect(playerElement)&lt;br /&gt;
	local projectilePosition = projectileX..&amp;quot;, &amp;quot;..projectileY..&amp;quot;, &amp;quot;..projectileZ&lt;br /&gt;
	local projectileZoneName = getZoneName(projectileX, projectileY, projectileZ, false)&lt;br /&gt;
	local projectileCityName = getZoneName(projectileX, projectileY, projectileZ, true)&lt;br /&gt;
	local projectileLog =&lt;br /&gt;
		&amp;quot;* Detected projectile abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile syncer: &amp;quot;..projectileSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile type: &amp;quot;..projectileType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile position: &amp;quot;..projectilePosition.. &amp;quot; (&amp;quot;..projectileZoneName..&amp;quot;, &amp;quot;..projectileCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(projectileLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processProjectileChecks(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
	local projectileAllowed = projectileData.projectileAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not projectileAllowed) then&lt;br /&gt;
		return false, &amp;quot;Projectile not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileAllowedWeapons = projectileData.projectileAllowedWeapons&lt;br /&gt;
&lt;br /&gt;
	if (projectileAllowedWeapons) then&lt;br /&gt;
		local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
		local allowedWeapon = projectileAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedWeapon) then&lt;br /&gt;
			return false, &amp;quot;Player is not holding correct weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
		local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
		if (not playerHasAmmo) then&lt;br /&gt;
			return false, &amp;quot;Player doesn't have ammo for weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerVehicle) then&lt;br /&gt;
		local projectileAllowedVehicles = projectileData.projectileAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
		if (projectileAllowedVehicles) then&lt;br /&gt;
			local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
			local allowedVehicle = projectileAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedVehicle) then&lt;br /&gt;
				return false, &amp;quot;Player is not inside allowed vehicles&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
			local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
			if (not playerDriver) then&lt;br /&gt;
				return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Player in vehicle&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxDistanceFromCreator = projectileData.projectileMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToProjectile = getDistanceBetweenPoints3D(playerX, playerY, playerZ, projectileX, projectileY, projectileZ)&lt;br /&gt;
		local matchingProjectileDistance = (distanceToProjectile &amp;lt;= projectileMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileDistance) then&lt;br /&gt;
			return false, &amp;quot;Projectile distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxVelocity = projectileData.projectileMaxVelocity&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxVelocity) then&lt;br /&gt;
		local projectileVelocity = (projectileVX ^ 2 + projectileVY ^ 2 + projectileVZ ^ 2)&lt;br /&gt;
		local projectileSpeed = math.sqrt(projectileVelocity)&lt;br /&gt;
		local matchingProjectileVelocity = (projectileSpeed &amp;lt;= projectileMaxVelocity)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileVelocity) then&lt;br /&gt;
			return false, &amp;quot;Projectile velocity mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trackPlayerProjectile(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		playerCreatedProjectiles[playerElement] = {}&lt;br /&gt;
		playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID] or 0&lt;br /&gt;
	local newProjectilesCount = (projectilesByType + 1)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
&lt;br /&gt;
	if (not projectileData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileBehaviour = projectileData.projectileOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not projectileBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = projectileBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = projectileBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = projectileBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID]&lt;br /&gt;
&lt;br /&gt;
	if (not projectilesByType) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local tempProjectilesCount = (projectilesByType - 1)&lt;br /&gt;
	local newProjectilesCount = (tempProjectilesCount &amp;gt; 0 and tempProjectilesCount or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerProjectileCreationAntiCheat(projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local approvedProjectile, detectionCode = processProjectileChecks(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedProjectile) then&lt;br /&gt;
		trackPlayerProjectile(source, projectileType)&lt;br /&gt;
&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportProjectileAbnormality(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerProjectileCreation&amp;quot;, root, onPlayerProjectileCreationAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerQuitAntiCheat()&lt;br /&gt;
	playerCreatedProjectiles[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Explosions handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local rocketInstantExplosionDistanceThreshold = 1.55 -- distance below which only explosion will be created (and not rocket projectile, hence not calling onPlayerProjectileCreation); do not change it&lt;br /&gt;
local explosionTypes = { -- more specific info on explosion, and whether it is allowed&lt;br /&gt;
	[0] = { -- Grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[1] = { -- Molotov&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[2] = { -- Rocket&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[3] = { -- Rocket weak&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[4] = { -- Car&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[5] = { -- Car quick&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[6] = { -- Boat&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[7] = { -- Heli&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[8] = { -- Mine&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[9] = { -- Object&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[10] = { -- Tank grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionMaxDistanceFromCreator = 80,&lt;br /&gt;
		explosionAllowedVehicles = {&lt;br /&gt;
			[432] = true,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	[11] = { -- Small&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[12] = { -- Tiny&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportExplosionAbnormality(playerElement, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	local explosionSyncer = inspect(playerElement)&lt;br /&gt;
	local explosionType = explosionType&lt;br /&gt;
	local explosionPosition = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionZoneName = getZoneName(explosionX, explosionY, explosionZ, false)&lt;br /&gt;
	local explosionCityName = getZoneName(explosionX, explosionY, explosionZ, true)&lt;br /&gt;
	local explosionLog =&lt;br /&gt;
		&amp;quot;* Detected explosion abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion syncer: &amp;quot;..explosionSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion type: &amp;quot;..explosionType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion position: &amp;quot;..explosionPosition.. &amp;quot; (&amp;quot;..explosionZoneName..&amp;quot;, &amp;quot;..explosionCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(explosionLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processExplosionChecks(playerElement, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
	local explosionAllowed = explosionData.explosionAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not explosionAllowed) then&lt;br /&gt;
		return false, &amp;quot;Explosion type not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rocketExplosion = (explosionType == 2)&lt;br /&gt;
&lt;br /&gt;
	if (rocketExplosion) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local instantExplosion = (distanceToExplosion &amp;lt; rocketInstantExplosionDistanceThreshold)&lt;br /&gt;
&lt;br /&gt;
		if (instantExplosion) then&lt;br /&gt;
			local explosionAllowedWeapons = explosionData.explosionAllowedWeapons&lt;br /&gt;
			local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
			local allowedWeapon = explosionAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedWeapon) then&lt;br /&gt;
				return false, &amp;quot;Player is not holding rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
			local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
			if (not playerHasAmmo) then&lt;br /&gt;
				return false, &amp;quot;Player doesn't have ammo for rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedProjectiles = explosionData.explosionAllowedProjectiles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedProjectiles) then&lt;br /&gt;
&lt;br /&gt;
		for projectileID, _ in pairs(explosionAllowedProjectiles) do&lt;br /&gt;
			local atleastOneProjectile = decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
&lt;br /&gt;
			if (atleastOneProjectile) then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Explosion created without respective projectile&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedVehicles = explosionData.explosionAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedVehicles) then&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
		if (not playerVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is not in vehicle&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
		local allowedVehicle = explosionAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is inside not allowed vehicles&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
		local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
		if (not playerDriver) then&lt;br /&gt;
			return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionMaxDistanceFromCreator = explosionData.explosionMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (explosionMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local matchingExplosionDistance = (distanceToExplosion &amp;lt; explosionMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingExplosionDistance) then&lt;br /&gt;
			return false, &amp;quot;Explosion distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
&lt;br /&gt;
	if (not explosionData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionBehaviour = explosionData.explosionOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not explosionBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = explosionBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = explosionBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = explosionBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onExplosionAntiCheat(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local serverSyncExplosion = (source == root)&lt;br /&gt;
&lt;br /&gt;
	if (serverSyncExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedExplosion, detectionCode = processExplosionChecks(source, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportExplosionAbnormality(source, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosionAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Handling non-registered events==&lt;br /&gt;
&lt;br /&gt;
See: [[onPlayerTriggerInvalidEvent]]&lt;br /&gt;
&lt;br /&gt;
==Handling events spam==&lt;br /&gt;
See: [[onPlayerTriggerEventThreshold]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81985</id>
		<title>User:DColeman/RU:Безопасность скриптов</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81985"/>
		<updated>2025-05-10T11:53:10Z</updated>

		<summary type="html">&lt;p&gt;DColeman: /* Additional protection layer */ translation progress&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Информация по памяти клиента==&lt;br /&gt;
&lt;br /&gt;
Начиная с самых основ:&lt;br /&gt;
* Вы должны понимать, что все, что вы храните на стороне клиента, включая .lua файлы, находится под риском. Любая конфиденциальная или критическая информация, которая как-либо оказывается на клиентской стороне (ПК игрока), может быть прочитана и/или изменена чит-клиентами.&lt;br /&gt;
* Чтобы сохранить конфиденциальную информацию и логику скрипта в секрете - используйте серверную сторону.&lt;br /&gt;
* Обратите внимание, что скрипты, отмеченные как общие ('''shared''') также действуют как '''клиентские''', что означает, что все вышеперечисленное также применяется и к ним. Например, объявление скрипта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;shared&amp;quot;/&amp;gt; &amp;lt;!-- этот скрипт будет запущен и на сервере и на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое, что и объявление:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;client&amp;quot;/&amp;gt; &amp;lt;!-- объявляем скрипт на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;server&amp;quot;/&amp;gt; &amp;lt;!-- делаем то же самое, но на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Дополнительный слой защиты==&lt;br /&gt;
&lt;br /&gt;
Чтобы незначительно ''усложнить жизнь*'' тем, у кого есть плохие намерения по отношению к вашему серверу, вы можете использовать аттрибут cache (и/или [https://luac.mtasa.com/ скомпилированные lua скрипты (также известные как Luac) с '''3''' уровнем дополнительной обфускации] - [https://wiki.multitheftauto.com/wiki/Lua_compilation_API API]), доступный в [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], вместе с настройкой встроенного античита МТА с включением SD (специальными кодами обнаружения), для дополнительной информации смотрите [https://wiki.multitheftauto.com/wiki/Anti-cheat_guide гайд по античиту].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;shared.lua&amp;quot; type=&amp;quot;shared&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; означает, что этот Lua файл не будет сохранен на ПК игрока --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''Усложнить жизнь'' '''не означает &amp;quot;сделать невозможным&amp;quot;''' получение вашего клиентского Lua кода, это означает, что '''большинство''' людей не смогут просмотреть ваши .lua файлы - тех, кто ищет логические ошибки (баги) или отсутствующие/некорректные проверки безопасности.&lt;br /&gt;
* Может использоваться на '''client''' и '''shared''' типах скриптов (не имеет эффекта на серверных скриптах).&lt;br /&gt;
* Это не удалит Lua файлы, которые были загружены ранее.&lt;br /&gt;
&lt;br /&gt;
==Detecting and dealing with backdoors and cheats==&lt;br /&gt;
'''To ensure minimum (or no) damage resulting from Lua scripts:'''&lt;br /&gt;
* Keep your server up-to-date, you can [https://nightly.multitheftauto.com/ download latest server builds from MTA:SA nightly site.] Information on specific builds can be [https://buildinfo.multitheftauto.com/ found here.]&lt;br /&gt;
* Keep your resources up-to-date, you can [https://github.com/multitheftauto/mtasa-resources download latest (default) resources from GitHub repository.] Those often contain latest security fixes, which could mean difference between having your server resist from attack or not.&lt;br /&gt;
* Make sure to properly configure [https://wiki.multitheftauto.com/wiki/Access%20Control%20List ACL (Access Control List)] - which will block resources from using certain, potentially dangerous functions.&lt;br /&gt;
* Zero-trust with giving away admin rights for resources (including maps) coming from unknown sources.&lt;br /&gt;
* Before running any resource you don't trust, analyze:&lt;br /&gt;
** [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml] of it, for possible hidden scripts lurking beneath other file extensions.&lt;br /&gt;
** It's source code, for malicious logic.&lt;br /&gt;
* Do not run/keep using compiled resources (scripts) of which legitimacy you aren't sure.&lt;br /&gt;
&lt;br /&gt;
'''To ensure minimum damage when a cheater connects to your server:'''&lt;br /&gt;
* When making scripts, remember to never trust data coming from a client.&lt;br /&gt;
* While reviewing scripts for possible security holes. Look at any data coming from the client that is being trusted.&lt;br /&gt;
* Any kind of data could be sent, hence server scripts which communicate with client by receiving data sent by players should validate it, before further use in latter parts of code. Mostly, it will be done either by [[setElementData]] or [[triggerServerEvent]].&lt;br /&gt;
* You shouldn't rely only on player [https://wiki.multitheftauto.com/wiki/Serial serial], when it comes to processing crucial operations (auto-login/admin actions). '''Serials aren't guaranted to be unique or non-fakable'''. This is why you should '''put it behind''' account system, as '''important authentication factor''' (e.g: '''login &amp;amp; password''').&lt;br /&gt;
* Server-side logic '''can not be bypassed''' or '''tampered''' with (unless server is breached or when there is a bug in code, but that's whole different scenario) - '''use it to your advantage'''. In majority of cases, you will be able to perform security validations with no participation of client-side.&lt;br /&gt;
* Using concept of '''''“All parameters including source can be faked and should not be trusted. Global variable client can be trusted.”''''' - gives you reliable assurance that player to which you refer (via '''client''') '''is''' in fact, '''the one which really called event'''. This approach will protect you from situations where cheater can call &amp;amp; process for instance: admin events (e.g kick/ban player) by passing the actual admin ('''2nd''' argument in '''[[triggerServerEvent]]'''), or trigger events for other players (as if they were the ones who called them, but in reality it was forcefully done by cheater) - as a consequence of using wrong variable. To make sure you fully understood it, take a look at examples below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	DON'T EVER DO THAT - THAT IS COMPLETELY WRONG AND INSECURE&lt;br /&gt;
	THE ISSUE: BY USING 'source' IN hasObjectPermissionTo YOU ARE LEAVING DOOR STRAIGHT OPEN FOR CHEATERS&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerWrongAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' points to the player who triggered the event, and should be used as security measure (in order to prevent player faking)&lt;br /&gt;
		return false -- if this variable doesn't exists at the moment (for unknown reason, or it was the server who triggered this event), stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- do not allow action by default, see (defaultPermission): https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(source, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- the vulnerability lies here...&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- if player doesn't have permissions&lt;br /&gt;
		return false -- don't do it&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- check whether argument passed from client is an element&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then -- it is not&lt;br /&gt;
		return false -- stop code processing&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- it's an element, so get it's type&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- make sure that it's a player&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- it's not a player&lt;br /&gt;
		return false -- stop here&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- do what needs to be done&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerWrongAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerWrongAdminEvent&amp;quot;, root, onServerWrongAdminEvent)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	onServerCorrectAdminEvent IS PERFECTLY SECURED, AS IT SHOULD BE&lt;br /&gt;
	NO ISSUE HERE: WE'VE USED 'client' IN hasObjectPermissionTo WHICH MAKES IT SAFE FROM FAKING PLAYER WHO CALLED EVENT&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerCorrectAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' points to the player who triggered the event, and should be used as security measure (in order to prevent player faking)&lt;br /&gt;
		return false -- if this variable doesn't exists at the moment (for unknown reason, or it was the server who triggered this event), stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- do not allow action by default, see (defaultPermission): https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(client, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- is player allowed to do that?&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- if player doesn't have permissions&lt;br /&gt;
		return false -- don't do it&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- check whether argument passed from client is an element&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then -- it is not&lt;br /&gt;
		return false -- stop code processing&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- it's an element, so get it's type&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- make sure that it's a player&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- it's not a player&lt;br /&gt;
		return false -- stop here&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- do what needs to be done&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerCorrectAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerCorrectAdminEvent&amp;quot;, root, onServerCorrectAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing setElementData==&lt;br /&gt;
&lt;br /&gt;
* You should refrain from using [[element data]] everywhere, it should be only used when really necessary. It is advised to replace it with [[triggerClientEvent]] instead.&lt;br /&gt;
* If you still insist on using it, it is recommended to set '''4th''' argument in [[setElementData]] to '''false''' (disabling sync for this, certain data) and use subscriber mode - [[addElementDataSubscriber]], for both security &amp;amp; performance reasons described in [https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent event section.]&lt;br /&gt;
{{Important Note|Disabling sync however, doesn't fully protect data key. It would be still vulnerable by default, but in this case you are not leaving it in plain sight. Using [[getAllElementData]] or digging in RAM wouldn't expose it, since it won't be synced to client in first place. Therefore, it needs to be added to anti-cheat as well.}} &lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of basic element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = tostring(sourceElement) -- element which received data&lt;br /&gt;
	local logOldValue = tostring(oldValue) -- old value&lt;br /&gt;
	local logNewValue = tostring(newValue) -- new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;=======================================\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;=======================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	local logVisibleTo = root -- specify who will see this log in console, in this case each player connected to server&lt;br /&gt;
	local hadData = (oldValue ~= nil) -- check if element had such data before&lt;br /&gt;
&lt;br /&gt;
	if (hadData) then -- if element had such data before&lt;br /&gt;
		setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
	else&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it completely&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	outputConsole(logText, logVisibleTo) -- print it to console&lt;br /&gt;
&lt;br /&gt;
	return true -- all success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeBasicAC(dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not go further&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkSpecialThing = (dataKey == &amp;quot;special_thing&amp;quot;) -- compare whether dataKey matches &amp;quot;special_thing&amp;quot;&lt;br /&gt;
	local checkFlagWaving = (dataKey == &amp;quot;flag_waving&amp;quot;) -- compare whether dataKey matches &amp;quot;flag_waving&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if (checkSpecialThing) then -- if it does, do our security checks&lt;br /&gt;
		local invalidElement = (client ~= source) -- verify whether source element is different from player which changed data&lt;br /&gt;
&lt;br /&gt;
		if (invalidElement) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;special_thing&amp;quot; can only be set for player himself&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkFlagWaving) then -- if it does, do our security checks&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(client) -- get player's current vehicle&lt;br /&gt;
		local invalidVehicle = (playerVehicle ~= source) -- verify whether source element is different from player's vehicle&lt;br /&gt;
&lt;br /&gt;
		if (invalidVehicle) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;flag_waving&amp;quot; can only be set for player's own vehicle&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeBasicAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of advanced element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan, allowOnlyProtectedKeys to true (as per default configuration)&lt;br /&gt;
	If allowOnlyProtectedKeys is enabled, do not forget to add every client-side element data key to protectedKeys table - otherwise you will face false-positives&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (handleDataChange) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	[&amp;quot;keyName&amp;quot;] = { -- name of key which would be protected&lt;br /&gt;
		onlyForPlayerHimself = true, -- enabling this (true) will make sure that this element data key can only be set on player who synced it (ignores onlyForOwnPlayerVeh and allowForElements), use false/nil to disable this&lt;br /&gt;
		onlyForOwnPlayerVeh = false, -- enabling this (true) will make sure that this element data key can only be set on player's current vehicle who synced it (ignores allowForElements), use false/nil to disable this&lt;br /&gt;
		allowForElements = { -- restrict this key for certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
			[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = { -- restrict this key for certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 32}, -- if value is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
		allowedTableLength = {1, 64}, -- if value is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
		allowedNumberRange = {1, 128}, -- if value is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
	}&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering element data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local allowOnlyProtectedKeys = true -- disallow (remove by using removeElementData) every element data besides those given in protectedKeys table; in case someone wanted to flood server with garbage keys, which would be kept in memory until server restart or manual remove - setElementData(source, key, nil) won't remove it; it has to be removeElementData&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
local protectedKeys = {&lt;br /&gt;
	[&amp;quot;vehicleNumber&amp;quot;] = { -- we want vehicleNumber to be set only on vehicles, with stricte numbers in range of 1-100&lt;br /&gt;
		allowForElements = {&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedNumberRange = {1, 100},&lt;br /&gt;
	},&lt;br /&gt;
	[&amp;quot;personalVehData&amp;quot;] = { -- we want be able to set personalVehData only on current vehicle, also it should be a string with length between 1-24&lt;br /&gt;
		onlyForOwnPlayerVeh = true,&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 24},&lt;br /&gt;
	},&lt;br /&gt;
	-- perform security checks on keys stored in this table, in a convenient way&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of element which received data&lt;br /&gt;
	local logOldValue = inspect(oldValue) -- in-depth view of old value&lt;br /&gt;
	local logNewValue = inspect(newValue) -- in-depth view of new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Fail reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (forceRemove) then -- we don't want this element data key to exist at all&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it&lt;br /&gt;
&lt;br /&gt;
		return true -- return success and stop here, we don't need further checks&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
&lt;br /&gt;
	return true -- return success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function handleDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from protectedKeys), false otherwise&lt;br /&gt;
	local protectedKey = protectedKeys[dataKey] -- look up whether key changed is stored in protectedKeys table&lt;br /&gt;
&lt;br /&gt;
	if (not protectedKey) then -- if it's not&lt;br /&gt;
&lt;br /&gt;
		if (allowOnlyProtectedKeys) then -- if we don't want garbage keys&lt;br /&gt;
			local failReason = &amp;quot;Key isn't present in protectedKeys&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = true -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return true -- this key isn't protected, let it through&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local onlyForPlayerHimself = protectedKey.onlyForPlayerHimself -- if key has &amp;quot;self-set&amp;quot; lock&lt;br /&gt;
	local onlyForOwnPlayerVeh = protectedKey.onlyForOwnPlayerVeh -- if key has &amp;quot;self-vehicle&amp;quot; lock&lt;br /&gt;
	local allowForElements = protectedKey.allowForElements -- if key has element type check&lt;br /&gt;
	local allowedDataTypes = protectedKey.allowedDataTypes -- if key has allowed data type check&lt;br /&gt;
&lt;br /&gt;
	if (onlyForPlayerHimself) then -- if &amp;quot;self-set&amp;quot; lock is active&lt;br /&gt;
		local matchingElement = (clientElement == sourceElement) -- verify whether player who set data is equal to element which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElement) then -- if it's not matching&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player himself&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (onlyForOwnPlayerVeh) then -- if &amp;quot;self-vehicle&amp;quot; lock is active&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(clientElement) -- get current vehicle of player which set data&lt;br /&gt;
		local matchingVehicle = (playerVehicle == sourceElement) -- check whether it matches the one which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingVehicle) then -- if it doesn't match&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player's own vehicle&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowForElements) then -- check if it's one of them&lt;br /&gt;
		local elementType = getElementType(sourceElement) -- get type of element whose data changed&lt;br /&gt;
		local matchingElementType = allowForElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElementType) then -- this isn't matching&lt;br /&gt;
			local failReason = &amp;quot;Invalid element type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowedDataTypes) then -- if there's allowed data types&lt;br /&gt;
		local valueType = type(newValue) -- get data type of value&lt;br /&gt;
		local matchingType = allowedDataTypes[valueType] -- check if it's one of allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingType) then -- if it's not then&lt;br /&gt;
			local failReason = &amp;quot;Invalid data type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedStringLength = protectedKey.allowedStringLength -- if key has specified string length check&lt;br /&gt;
		local dataString = (valueType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
		if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
			local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
			local stringLength = utf8.len(newValue) -- get length of data string&lt;br /&gt;
			local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedTableLength = protectedKey.allowedTableLength -- if key has table length check&lt;br /&gt;
		local dataTable = (valueType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
		if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
			local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
			local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
			local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
			local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
			for _, _ in pairs(newValue) do -- loop through whole table&lt;br /&gt;
				tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
				minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
				maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
				if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
					break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
				local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedNumberRange = protectedKey.allowedNumberRange -- if key has allowed number range check&lt;br /&gt;
		local dataNumber = (valueType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
		if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
			local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
			local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
			local matchingRange = (newValue &amp;gt;= minRange) and (newValue &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingRange) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this data key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeAdvancedAC(dataKey, oldValue, newValue) -- this event makes use of handleDataChange, the code was split for better readability&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not continue&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedChange = handleDataChange(client, source, dataKey, oldValue, newValue) -- run our security checks&lt;br /&gt;
&lt;br /&gt;
	if (approvedChange) then -- it's all cool and good&lt;br /&gt;
		return false -- we don't need further action&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(client, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(client, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeAdvancedAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing triggerServerEvent==&lt;br /&gt;
&lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player (client) '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
* Do '''not''' use the same name for your custom event as MTA native server events (which aren't remotely triggerable by default), e.g: '''onPlayerLogin'''; doing so would open door for cheaters manipulations.&lt;br /&gt;
* Be aware to which players event is sent via [[triggerClientEvent]]. For both security &amp;amp; performance reasons, admin like events should be received by admins only (to prevent confidential data being accessed), in the same time you shouldn't send each event to everyone on server (e.g: login success event which hides login panel for certain player). [[triggerClientEvent]] allows you to specify the event receiver as first (optional) argument. It defaults to [[root]], meaning if you don't specify it, it will be sent to everyone connected to server - even those who are still downloading server cache (which results in ''Server triggered clientside event eventName, but event is not added clientside.''). You can either pass player element or table with receivers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playersToReceiveEvent = {player1, player2, player3} -- each playerX is player element&lt;br /&gt;
&lt;br /&gt;
triggerClientEvent(playersToReceiveEvent, ...) -- do not forget to fill the latter part of arguments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of anti-cheat function designed for events, used for data validation for both normal, and admin events which are called from client-side.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan to true (as per default configuration)&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (processServerEventData) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	checkACLGroup = { -- check whether player who called event belongs to at least one group below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;Admin&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkPermissions = { -- check whether player who called event has permission to at least one thing below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;function.kickPlayer&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkEventData = {&lt;br /&gt;
		{&lt;br /&gt;
			debugData = &amp;quot;source&amp;quot;, -- optional details for report shown in debug message&lt;br /&gt;
			eventData = source, -- data we want to verify&lt;br /&gt;
			equalTo = client, -- compare whether eventData == equalTo&lt;br /&gt;
			allowedElements = { -- restrict eventData to be certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
				[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedDataTypes = { -- restrict eventData to be certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
				[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedStringLength = {1, 32}, -- if eventData is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
			allowedTableLength = {1, 64}, -- if eventData is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
			allowedNumberRange = {1, 128}, -- if eventData is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering server event data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
function processServerEventData(clientElement, sourceElement, serverEvent, securityChecks) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from securityChecks), false otherwise&lt;br /&gt;
	if (not securityChecks) then -- if we haven't passed any security checks&lt;br /&gt;
		return true -- nothing to check, let code go further&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (not clientElement) then -- if client variable isn't available for some reason (although it should never happen)&lt;br /&gt;
		local failReason = &amp;quot;Client variable not present&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkACLGroup = securityChecks.checkACLGroup -- if there's any ACL groups to check&lt;br /&gt;
	local checkPermissions = securityChecks.checkPermissions -- if there's any permissions to check&lt;br /&gt;
	local checkEventData = securityChecks.checkEventData -- if there's any data checks&lt;br /&gt;
&lt;br /&gt;
	if (checkACLGroup) then -- let's check player ACL groups&lt;br /&gt;
		local playerAccount = getPlayerAccount(clientElement) -- get current account of player&lt;br /&gt;
		local guestAccount = isGuestAccount(playerAccount) -- if account is guest (meaning player is not logged in)&lt;br /&gt;
&lt;br /&gt;
		if (guestAccount) then -- it's the case&lt;br /&gt;
			local failReason = &amp;quot;Can't retrieve player login - guest account&amp;quot; -- reason shown in report&lt;br /&gt;
			local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
			reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local accountName = getAccountName(playerAccount) -- get name of player's current account&lt;br /&gt;
		local aclString = &amp;quot;user.&amp;quot;..accountName -- format it for further use in isObjectInACLGroup function&lt;br /&gt;
&lt;br /&gt;
		for groupID = 1, #checkACLGroup do -- iterate over table of given groups&lt;br /&gt;
			local groupName = checkACLGroup[groupID] -- get each group name&lt;br /&gt;
			local aclGroup = aclGetGroup(groupName) -- check if such group exists&lt;br /&gt;
&lt;br /&gt;
			if (not aclGroup) then -- it doesn't&lt;br /&gt;
				local failReason = &amp;quot;ACL group '&amp;quot;..groupName..&amp;quot;' is missing&amp;quot; -- reason shown in report&lt;br /&gt;
				local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
				reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local playerInACLGroup = isObjectInACLGroup(aclString, aclGroup) -- check if player belong to the group&lt;br /&gt;
&lt;br /&gt;
			if (playerInACLGroup) then -- yep, it's the case&lt;br /&gt;
				return true -- so it's a success&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Player doesn't belong to any given ACL group&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkPermissions) then -- check if player has at least one desired permission&lt;br /&gt;
		local allowedByDefault = false -- does he have access by default&lt;br /&gt;
&lt;br /&gt;
		for permissionID = 1, #checkPermissions do -- iterate over all permissions&lt;br /&gt;
			local permissionName = checkPermissions[permissionID] -- get permission name&lt;br /&gt;
			local hasPermission = hasObjectPermissionTo(clientElement, permissionName, allowedByDefault) -- check whether player is allowed to perform certain action&lt;br /&gt;
&lt;br /&gt;
			if (hasPermission) then -- if player has access&lt;br /&gt;
				return true -- one is available (and enough), server won't bother to check others (as return keywords also breaks loop)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Not enough permissions&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkEventData) then -- if there is some data to verify&lt;br /&gt;
&lt;br /&gt;
		for dataID = 1, #checkEventData do -- iterate over each of data&lt;br /&gt;
			local dataToCheck = checkEventData[dataID] -- get each data set&lt;br /&gt;
			local eventData = dataToCheck.eventData -- this is the one we'll be verifying&lt;br /&gt;
			local equalTo = dataToCheck.equalTo -- we want to compare whether eventData == equalTo&lt;br /&gt;
			local allowedElements = dataToCheck.allowedElements -- check whether is element, and whether belongs to certain element types&lt;br /&gt;
			local allowedDataTypes = dataToCheck.allowedDataTypes -- do we restrict data to be certain type?&lt;br /&gt;
			local debugData = dataToCheck.debugData -- additional helper data&lt;br /&gt;
			local debugText = debugData and &amp;quot; (&amp;quot;..debugData..&amp;quot;)&amp;quot; or &amp;quot;&amp;quot; -- if it's present, format it nicely&lt;br /&gt;
&lt;br /&gt;
			if (equalTo) then -- equal check exists&lt;br /&gt;
				local matchingData = (eventData == equalTo) -- compare whether those two values are equal&lt;br /&gt;
&lt;br /&gt;
				if (not matchingData) then -- they aren't&lt;br /&gt;
					local failReason = &amp;quot;Data isn't equal @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedElements) then -- we do check whether is an element, and belongs to at least one given in the list&lt;br /&gt;
				local validElement = isElement(eventData) -- check if it's actual element&lt;br /&gt;
&lt;br /&gt;
				if (not validElement) then -- it's not&lt;br /&gt;
					local failReason = &amp;quot;Data isn't element @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local elementType = getElementType(eventData) -- it's element, so we want to know it's type&lt;br /&gt;
				local matchingElementType = allowedElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingElementType) then -- it's not allowed&lt;br /&gt;
					local failReason = &amp;quot;Invalid element type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedDataTypes) then -- let's check allowed data types&lt;br /&gt;
				local dataType = type(eventData) -- get data type&lt;br /&gt;
				local matchingType = allowedDataTypes[dataType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingType) then -- it isn't&lt;br /&gt;
					local failReason = &amp;quot;Invalid data type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedStringLength = dataToCheck.allowedStringLength -- if data has string length check&lt;br /&gt;
				local dataString = (dataType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
				if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
					local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
					local stringLength = utf8.len(eventData) -- get length of data string&lt;br /&gt;
					local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedTableLength = dataToCheck.allowedTableLength -- if data has table length check&lt;br /&gt;
				local dataTable = (dataType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
				if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
					local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
					local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
					local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
					local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
					for _, _ in pairs(eventData) do -- loop through whole table&lt;br /&gt;
						tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
						minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
						maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
						if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
							break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
						local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedNumberRange = dataToCheck.allowedNumberRange -- if data has number range check&lt;br /&gt;
				local dataNumber = (dataType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
				if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
					local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
					local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
					local matchingRange = (eventData &amp;gt;= minRange) and (eventData &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingRange) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this event call&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- helper function to log and handle accidents&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view player which called event&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of source element&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected event abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Event: &amp;quot;..serverEvent..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect or skipPunishment) then -- we don't want to punish player for some reason&lt;br /&gt;
		return true -- stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(clientElement, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(clientElement, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- all done, report success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onServerEvent(clientData)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local dataToPass = 10&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerEvent&amp;quot;, localPlayer, dataToPass)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = clientData, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;number&amp;quot;] = true, -- we want it to be only number&lt;br /&gt;
					},&lt;br /&gt;
					allowedNumberRange = {1, 100}, -- in range of 1 to 100&lt;br /&gt;
					debugData = &amp;quot;clientData&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerEvent&amp;quot;, root, onServerEvent)&lt;br /&gt;
&lt;br /&gt;
function onServerAdminEvent(playerToBan)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server admin event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local playerToBan = getPlayerFromName(&amp;quot;playerToBan&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerAdminEvent&amp;quot;, localPlayer, playerToBan)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerAdminEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkACLGroup = { -- we need to check whether player who called event belongs to ACL groups&lt;br /&gt;
				&amp;quot;Admin&amp;quot;, -- in this case admin group&lt;br /&gt;
			},&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = playerToBan, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;player&amp;quot;] = true, -- we want it to be player&lt;br /&gt;
					},&lt;br /&gt;
					debugData = &amp;quot;playerToBan&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerAdminEvent&amp;quot;, root, onServerAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing server-only events==&lt;br /&gt;
* It is very important to '''disable remote triggering''' ability in [https://wiki.multitheftauto.com/wiki/AddEvent addEvent], to prevent calling server-side only events from client-side.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
This example shows how you should make event server-side only.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onServerSideOnlyEvent()&lt;br /&gt;
	-- do some server-side stuff&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerSideOnlyEvent&amp;quot;, false) -- set second argument (allowRemoteTriger) to false, so it can't be called from client&lt;br /&gt;
addEventHandler(&amp;quot;onServerSideOnlyEvent&amp;quot;, root, onServerSideOnlyEvent) -- associate our event with function onServerSideOnlyEvent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing projectiles &amp;amp; explosions==&lt;br /&gt;
This section (and '''code''' is '''work in progress''') - '''use at your own risk'''.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile ammo tracker:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerProjectileAmmo = {} -- store player-held weapons ammo here&lt;br /&gt;
local playerWeaponsToTrack = { -- keep track of ammo for certain player-held weapon types, basically those which create projectile; [weaponID] = weaponSlot&lt;br /&gt;
	[16] = 8, -- grenade&lt;br /&gt;
	[17] = 8, -- teargas&lt;br /&gt;
	[18] = 8, -- molotov&lt;br /&gt;
	[35] = 7, -- rocket launcher&lt;br /&gt;
	[36] = 7, -- rocket launcher (heat-seeking)&lt;br /&gt;
	[39] = 8, -- satchel charge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for weaponID, weaponSlot in pairs(playerWeaponsToTrack) do&lt;br /&gt;
		local weaponInSlot = getPedWeapon(playerElement, weaponSlot)&lt;br /&gt;
		local weaponTotalAmmo = getPedTotalAmmo(playerElement, weaponSlot)&lt;br /&gt;
		local weaponHasAmmo = (weaponTotalAmmo &amp;gt; 0)&lt;br /&gt;
		local weaponMatching = (weaponInSlot == weaponID)&lt;br /&gt;
		local updateWeaponAmmo = (weaponMatching and weaponHasAmmo)&lt;br /&gt;
&lt;br /&gt;
		if (updateWeaponAmmo) then&lt;br /&gt;
			local storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			local newWeaponAmmo = (updateWeaponAmmo and weaponTotalAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
			if (not storedProjectileAmmo) then&lt;br /&gt;
				playerProjectileAmmo[playerElement] = {}&lt;br /&gt;
				storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			storedProjectileAmmo[weaponID] = newWeaponAmmo&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function hasPlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
	local projectileAmmo = (projectileData and projectileData[projectileWeapon])&lt;br /&gt;
&lt;br /&gt;
	return projectileAmmo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectileData) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileWeaponAmmo = playerProjectileData[projectileWeapon]&lt;br /&gt;
	local tempProjectileAmmo = (projectileWeaponAmmo - 1)&lt;br /&gt;
	local newProjectileAmmo = (tempProjectileAmmo &amp;gt; 0 and tempProjectileAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectileData[projectileWeapon] = newProjectileAmmo&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerWeaponSwitchAntiCheat(previousWeaponID, currentWeaponID)&lt;br /&gt;
	setTimer(updateProjectileAmmoForPlayer, 50, 1, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWeaponSwitch&amp;quot;, root, onPlayerWeaponSwitchAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onResourceStartAntiCheat()&lt;br /&gt;
	local playersTable = getElementsByType(&amp;quot;player&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	for playerID = 1, #playersTable do&lt;br /&gt;
		local playerElement = playersTable[playerID]&lt;br /&gt;
&lt;br /&gt;
		updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onResourceStart&amp;quot;, resourceRoot, onResourceStartAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerWastedQuitAntiCheat()&lt;br /&gt;
	playerProjectileAmmo[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerCreatedProjectiles = {} -- store count of legitimately created player projectiles; [playerElement] = {[projectileType] = activeProjectiles}&lt;br /&gt;
local projectileTypes = {&lt;br /&gt;
	[16] = { -- Grenade&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[17] = { -- Teargas&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[17] = true, -- teargas&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[18] = { -- Molotov&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[19] = { -- Rocket&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[425] = true, -- hunter&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[20] = { -- Rocket (heat-seeking)&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[21] = { -- Airbomb&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[39] = { -- Satchel charge&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[39] = true, -- satchel charge&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[58] = { -- Hydra flare&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportProjectileAbnormality(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	local projectileSyncer = inspect(playerElement)&lt;br /&gt;
	local projectilePosition = projectileX..&amp;quot;, &amp;quot;..projectileY..&amp;quot;, &amp;quot;..projectileZ&lt;br /&gt;
	local projectileZoneName = getZoneName(projectileX, projectileY, projectileZ, false)&lt;br /&gt;
	local projectileCityName = getZoneName(projectileX, projectileY, projectileZ, true)&lt;br /&gt;
	local projectileLog =&lt;br /&gt;
		&amp;quot;* Detected projectile abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile syncer: &amp;quot;..projectileSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile type: &amp;quot;..projectileType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile position: &amp;quot;..projectilePosition.. &amp;quot; (&amp;quot;..projectileZoneName..&amp;quot;, &amp;quot;..projectileCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(projectileLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processProjectileChecks(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
	local projectileAllowed = projectileData.projectileAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not projectileAllowed) then&lt;br /&gt;
		return false, &amp;quot;Projectile not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileAllowedWeapons = projectileData.projectileAllowedWeapons&lt;br /&gt;
&lt;br /&gt;
	if (projectileAllowedWeapons) then&lt;br /&gt;
		local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
		local allowedWeapon = projectileAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedWeapon) then&lt;br /&gt;
			return false, &amp;quot;Player is not holding correct weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
		local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
		if (not playerHasAmmo) then&lt;br /&gt;
			return false, &amp;quot;Player doesn't have ammo for weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerVehicle) then&lt;br /&gt;
		local projectileAllowedVehicles = projectileData.projectileAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
		if (projectileAllowedVehicles) then&lt;br /&gt;
			local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
			local allowedVehicle = projectileAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedVehicle) then&lt;br /&gt;
				return false, &amp;quot;Player is not inside allowed vehicles&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
			local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
			if (not playerDriver) then&lt;br /&gt;
				return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Player in vehicle&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxDistanceFromCreator = projectileData.projectileMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToProjectile = getDistanceBetweenPoints3D(playerX, playerY, playerZ, projectileX, projectileY, projectileZ)&lt;br /&gt;
		local matchingProjectileDistance = (distanceToProjectile &amp;lt;= projectileMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileDistance) then&lt;br /&gt;
			return false, &amp;quot;Projectile distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxVelocity = projectileData.projectileMaxVelocity&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxVelocity) then&lt;br /&gt;
		local projectileVelocity = (projectileVX ^ 2 + projectileVY ^ 2 + projectileVZ ^ 2)&lt;br /&gt;
		local projectileSpeed = math.sqrt(projectileVelocity)&lt;br /&gt;
		local matchingProjectileVelocity = (projectileSpeed &amp;lt;= projectileMaxVelocity)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileVelocity) then&lt;br /&gt;
			return false, &amp;quot;Projectile velocity mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trackPlayerProjectile(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		playerCreatedProjectiles[playerElement] = {}&lt;br /&gt;
		playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID] or 0&lt;br /&gt;
	local newProjectilesCount = (projectilesByType + 1)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
&lt;br /&gt;
	if (not projectileData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileBehaviour = projectileData.projectileOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not projectileBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = projectileBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = projectileBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = projectileBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID]&lt;br /&gt;
&lt;br /&gt;
	if (not projectilesByType) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local tempProjectilesCount = (projectilesByType - 1)&lt;br /&gt;
	local newProjectilesCount = (tempProjectilesCount &amp;gt; 0 and tempProjectilesCount or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerProjectileCreationAntiCheat(projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local approvedProjectile, detectionCode = processProjectileChecks(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedProjectile) then&lt;br /&gt;
		trackPlayerProjectile(source, projectileType)&lt;br /&gt;
&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportProjectileAbnormality(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerProjectileCreation&amp;quot;, root, onPlayerProjectileCreationAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerQuitAntiCheat()&lt;br /&gt;
	playerCreatedProjectiles[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Explosions handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local rocketInstantExplosionDistanceThreshold = 1.55 -- distance below which only explosion will be created (and not rocket projectile, hence not calling onPlayerProjectileCreation); do not change it&lt;br /&gt;
local explosionTypes = { -- more specific info on explosion, and whether it is allowed&lt;br /&gt;
	[0] = { -- Grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[1] = { -- Molotov&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[2] = { -- Rocket&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[3] = { -- Rocket weak&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[4] = { -- Car&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[5] = { -- Car quick&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[6] = { -- Boat&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[7] = { -- Heli&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[8] = { -- Mine&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[9] = { -- Object&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[10] = { -- Tank grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionMaxDistanceFromCreator = 80,&lt;br /&gt;
		explosionAllowedVehicles = {&lt;br /&gt;
			[432] = true,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	[11] = { -- Small&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[12] = { -- Tiny&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportExplosionAbnormality(playerElement, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	local explosionSyncer = inspect(playerElement)&lt;br /&gt;
	local explosionType = explosionType&lt;br /&gt;
	local explosionPosition = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionZoneName = getZoneName(explosionX, explosionY, explosionZ, false)&lt;br /&gt;
	local explosionCityName = getZoneName(explosionX, explosionY, explosionZ, true)&lt;br /&gt;
	local explosionLog =&lt;br /&gt;
		&amp;quot;* Detected explosion abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion syncer: &amp;quot;..explosionSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion type: &amp;quot;..explosionType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion position: &amp;quot;..explosionPosition.. &amp;quot; (&amp;quot;..explosionZoneName..&amp;quot;, &amp;quot;..explosionCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(explosionLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processExplosionChecks(playerElement, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
	local explosionAllowed = explosionData.explosionAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not explosionAllowed) then&lt;br /&gt;
		return false, &amp;quot;Explosion type not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rocketExplosion = (explosionType == 2)&lt;br /&gt;
&lt;br /&gt;
	if (rocketExplosion) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local instantExplosion = (distanceToExplosion &amp;lt; rocketInstantExplosionDistanceThreshold)&lt;br /&gt;
&lt;br /&gt;
		if (instantExplosion) then&lt;br /&gt;
			local explosionAllowedWeapons = explosionData.explosionAllowedWeapons&lt;br /&gt;
			local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
			local allowedWeapon = explosionAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedWeapon) then&lt;br /&gt;
				return false, &amp;quot;Player is not holding rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
			local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
			if (not playerHasAmmo) then&lt;br /&gt;
				return false, &amp;quot;Player doesn't have ammo for rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedProjectiles = explosionData.explosionAllowedProjectiles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedProjectiles) then&lt;br /&gt;
&lt;br /&gt;
		for projectileID, _ in pairs(explosionAllowedProjectiles) do&lt;br /&gt;
			local atleastOneProjectile = decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
&lt;br /&gt;
			if (atleastOneProjectile) then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Explosion created without respective projectile&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedVehicles = explosionData.explosionAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedVehicles) then&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
		if (not playerVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is not in vehicle&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
		local allowedVehicle = explosionAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is inside not allowed vehicles&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
		local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
		if (not playerDriver) then&lt;br /&gt;
			return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionMaxDistanceFromCreator = explosionData.explosionMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (explosionMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local matchingExplosionDistance = (distanceToExplosion &amp;lt; explosionMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingExplosionDistance) then&lt;br /&gt;
			return false, &amp;quot;Explosion distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
&lt;br /&gt;
	if (not explosionData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionBehaviour = explosionData.explosionOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not explosionBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = explosionBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = explosionBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = explosionBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onExplosionAntiCheat(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local serverSyncExplosion = (source == root)&lt;br /&gt;
&lt;br /&gt;
	if (serverSyncExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedExplosion, detectionCode = processExplosionChecks(source, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportExplosionAbnormality(source, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosionAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Handling non-registered events==&lt;br /&gt;
&lt;br /&gt;
See: [[onPlayerTriggerInvalidEvent]]&lt;br /&gt;
&lt;br /&gt;
==Handling events spam==&lt;br /&gt;
See: [[onPlayerTriggerEventThreshold]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81984</id>
		<title>User:DColeman/RU:Безопасность скриптов</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=User:DColeman/RU:%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B2&amp;diff=81984"/>
		<updated>2025-05-10T11:33:33Z</updated>

		<summary type="html">&lt;p&gt;DColeman: Init&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Информация по памяти клиента==&lt;br /&gt;
&lt;br /&gt;
Начиная с самых основ:&lt;br /&gt;
* Вы должны понимать, что все, что вы храните на стороне клиента, включая .lua файлы, находится под риском. Любая конфиденциальная или критическая информация, которая как-либо оказывается на клиентской стороне (ПК игрока), может быть прочитана и/или изменена чит-клиентами.&lt;br /&gt;
* Чтобы сохранить конфиденциальную информацию и логику скрипта в секрете - используйте серверную сторону.&lt;br /&gt;
* Обратите внимание, что скрипты, отмеченные как общие ('''shared''') также действуют как '''клиентские''', что означает, что все вышеперечисленное также применяется и к ним. Например, объявление скрипта:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;shared&amp;quot;/&amp;gt; &amp;lt;!-- этот скрипт будет запущен и на сервере и на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
То же самое, что и объявление:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;client&amp;quot;/&amp;gt; &amp;lt;!-- объявляем скрипт на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;script.lua&amp;quot; type=&amp;quot;server&amp;quot;/&amp;gt; &amp;lt;!-- делаем то же самое, но на клиенте --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Additional protection layer==&lt;br /&gt;
&lt;br /&gt;
In order to make things ''slightly harder*'' for those having bad intentions towards your server, you can make use of cache attribute (and/or [https://luac.mtasa.com/ Lua compile (also known as Luac) with extra obfuscation set to level '''3'''] - [https://wiki.multitheftauto.com/wiki/Lua_compilation_API API]) available in [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml], along with configuring MTA's built-in AC by toggling SD (Special detections), see: [https://wiki.multitheftauto.com/wiki/Anti-cheat_guide Anti-cheat guide].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;shared.lua&amp;quot; type=&amp;quot;shared&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; indicates that this Lua file won't be saved on player's PC --&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; cache=&amp;quot;false&amp;quot;/&amp;gt; &amp;lt;!-- cache=&amp;quot;false&amp;quot; indicates that this Lua file won't be saved on player's PC --&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''Slightly harder'' '''but not impossible''' for some to obtain client code, yet does good job at keeping '''most''' people away from inspecting your .lua files - those looking for possible logic flaws (bugs) or missing/incorrect security based checks.&lt;br /&gt;
* Can be used on both '''client''' and '''shared''' script type (has no effect on server-sided Lua).&lt;br /&gt;
* It doesn't remove Lua files which were previously downloaded.&lt;br /&gt;
&lt;br /&gt;
==Detecting and dealing with backdoors and cheats==&lt;br /&gt;
'''To ensure minimum (or no) damage resulting from Lua scripts:'''&lt;br /&gt;
* Keep your server up-to-date, you can [https://nightly.multitheftauto.com/ download latest server builds from MTA:SA nightly site.] Information on specific builds can be [https://buildinfo.multitheftauto.com/ found here.]&lt;br /&gt;
* Keep your resources up-to-date, you can [https://github.com/multitheftauto/mtasa-resources download latest (default) resources from GitHub repository.] Those often contain latest security fixes, which could mean difference between having your server resist from attack or not.&lt;br /&gt;
* Make sure to properly configure [https://wiki.multitheftauto.com/wiki/Access%20Control%20List ACL (Access Control List)] - which will block resources from using certain, potentially dangerous functions.&lt;br /&gt;
* Zero-trust with giving away admin rights for resources (including maps) coming from unknown sources.&lt;br /&gt;
* Before running any resource you don't trust, analyze:&lt;br /&gt;
** [https://wiki.multitheftauto.com/wiki/Meta.xml meta.xml] of it, for possible hidden scripts lurking beneath other file extensions.&lt;br /&gt;
** It's source code, for malicious logic.&lt;br /&gt;
* Do not run/keep using compiled resources (scripts) of which legitimacy you aren't sure.&lt;br /&gt;
&lt;br /&gt;
'''To ensure minimum damage when a cheater connects to your server:'''&lt;br /&gt;
* When making scripts, remember to never trust data coming from a client.&lt;br /&gt;
* While reviewing scripts for possible security holes. Look at any data coming from the client that is being trusted.&lt;br /&gt;
* Any kind of data could be sent, hence server scripts which communicate with client by receiving data sent by players should validate it, before further use in latter parts of code. Mostly, it will be done either by [[setElementData]] or [[triggerServerEvent]].&lt;br /&gt;
* You shouldn't rely only on player [https://wiki.multitheftauto.com/wiki/Serial serial], when it comes to processing crucial operations (auto-login/admin actions). '''Serials aren't guaranted to be unique or non-fakable'''. This is why you should '''put it behind''' account system, as '''important authentication factor''' (e.g: '''login &amp;amp; password''').&lt;br /&gt;
* Server-side logic '''can not be bypassed''' or '''tampered''' with (unless server is breached or when there is a bug in code, but that's whole different scenario) - '''use it to your advantage'''. In majority of cases, you will be able to perform security validations with no participation of client-side.&lt;br /&gt;
* Using concept of '''''“All parameters including source can be faked and should not be trusted. Global variable client can be trusted.”''''' - gives you reliable assurance that player to which you refer (via '''client''') '''is''' in fact, '''the one which really called event'''. This approach will protect you from situations where cheater can call &amp;amp; process for instance: admin events (e.g kick/ban player) by passing the actual admin ('''2nd''' argument in '''[[triggerServerEvent]]'''), or trigger events for other players (as if they were the ones who called them, but in reality it was forcefully done by cheater) - as a consequence of using wrong variable. To make sure you fully understood it, take a look at examples below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	DON'T EVER DO THAT - THAT IS COMPLETELY WRONG AND INSECURE&lt;br /&gt;
	THE ISSUE: BY USING 'source' IN hasObjectPermissionTo YOU ARE LEAVING DOOR STRAIGHT OPEN FOR CHEATERS&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerWrongAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' points to the player who triggered the event, and should be used as security measure (in order to prevent player faking)&lt;br /&gt;
		return false -- if this variable doesn't exists at the moment (for unknown reason, or it was the server who triggered this event), stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- do not allow action by default, see (defaultPermission): https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(source, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- the vulnerability lies here...&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- if player doesn't have permissions&lt;br /&gt;
		return false -- don't do it&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- check whether argument passed from client is an element&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then -- it is not&lt;br /&gt;
		return false -- stop code processing&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- it's an element, so get it's type&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- make sure that it's a player&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- it's not a player&lt;br /&gt;
		return false -- stop here&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- do what needs to be done&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerWrongAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerWrongAdminEvent&amp;quot;, root, onServerWrongAdminEvent)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	onServerCorrectAdminEvent IS PERFECTLY SECURED, AS IT SHOULD BE&lt;br /&gt;
	NO ISSUE HERE: WE'VE USED 'client' IN hasObjectPermissionTo WHICH MAKES IT SAFE FROM FAKING PLAYER WHO CALLED EVENT&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
function onServerCorrectAdminEvent(playerToBan)&lt;br /&gt;
	if (not client) then -- 'client' points to the player who triggered the event, and should be used as security measure (in order to prevent player faking)&lt;br /&gt;
		return false -- if this variable doesn't exists at the moment (for unknown reason, or it was the server who triggered this event), stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultPermission = false -- do not allow action by default, see (defaultPermission): https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo&lt;br /&gt;
	local canAdminBanPlayer = hasObjectPermissionTo(client, &amp;quot;function.banPlayer&amp;quot;, defaultPermission) -- is player allowed to do that?&lt;br /&gt;
&lt;br /&gt;
	if (not canAdminBanPlayer) then -- if player doesn't have permissions&lt;br /&gt;
		return false -- don't do it&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local validElement = isElement(playerToBan) -- check whether argument passed from client is an element&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then -- it is not&lt;br /&gt;
		return false -- stop code processing&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local elementType = getElementType(playerToBan) -- it's an element, so get it's type&lt;br /&gt;
	local playerType = (elementType == &amp;quot;player&amp;quot;) -- make sure that it's a player&lt;br /&gt;
&lt;br /&gt;
	if (not playerType) then -- it's not a player&lt;br /&gt;
		return false -- stop here&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- banPlayer(...) -- do what needs to be done&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerCorrectAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerCorrectAdminEvent&amp;quot;, root, onServerCorrectAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing setElementData==&lt;br /&gt;
&lt;br /&gt;
* You should refrain from using [[element data]] everywhere, it should be only used when really necessary. It is advised to replace it with [[triggerClientEvent]] instead.&lt;br /&gt;
* If you still insist on using it, it is recommended to set '''4th''' argument in [[setElementData]] to '''false''' (disabling sync for this, certain data) and use subscriber mode - [[addElementDataSubscriber]], for both security &amp;amp; performance reasons described in [https://wiki.multitheftauto.com/wiki/Script_security#Securing_triggerServerEvent event section.]&lt;br /&gt;
{{Important Note|Disabling sync however, doesn't fully protect data key. It would be still vulnerable by default, but in this case you are not leaving it in plain sight. Using [[getAllElementData]] or digging in RAM wouldn't expose it, since it won't be synced to client in first place. Therefore, it needs to be added to anti-cheat as well.}} &lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of basic element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = tostring(sourceElement) -- element which received data&lt;br /&gt;
	local logOldValue = tostring(oldValue) -- old value&lt;br /&gt;
	local logNewValue = tostring(newValue) -- new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;=======================================\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;=======================================&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	local logVisibleTo = root -- specify who will see this log in console, in this case each player connected to server&lt;br /&gt;
	local hadData = (oldValue ~= nil) -- check if element had such data before&lt;br /&gt;
&lt;br /&gt;
	if (hadData) then -- if element had such data before&lt;br /&gt;
		setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
	else&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it completely&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	outputConsole(logText, logVisibleTo) -- print it to console&lt;br /&gt;
&lt;br /&gt;
	return true -- all success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeBasicAC(dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not go further&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkSpecialThing = (dataKey == &amp;quot;special_thing&amp;quot;) -- compare whether dataKey matches &amp;quot;special_thing&amp;quot;&lt;br /&gt;
	local checkFlagWaving = (dataKey == &amp;quot;flag_waving&amp;quot;) -- compare whether dataKey matches &amp;quot;flag_waving&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if (checkSpecialThing) then -- if it does, do our security checks&lt;br /&gt;
		local invalidElement = (client ~= source) -- verify whether source element is different from player which changed data&lt;br /&gt;
&lt;br /&gt;
		if (invalidElement) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;special_thing&amp;quot; can only be set for player himself&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkFlagWaving) then -- if it does, do our security checks&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(client) -- get player's current vehicle&lt;br /&gt;
		local invalidVehicle = (playerVehicle ~= source) -- verify whether source element is different from player's vehicle&lt;br /&gt;
&lt;br /&gt;
		if (invalidVehicle) then -- if it's so&lt;br /&gt;
			reportAndRevertDataChange(client, source, dataKey, oldValue, newValue) -- revert data change, because &amp;quot;flag_waving&amp;quot; can only be set for player's own vehicle&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeBasicAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of advanced element data anti-cheat.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan, allowOnlyProtectedKeys to true (as per default configuration)&lt;br /&gt;
	If allowOnlyProtectedKeys is enabled, do not forget to add every client-side element data key to protectedKeys table - otherwise you will face false-positives&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (handleDataChange) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	[&amp;quot;keyName&amp;quot;] = { -- name of key which would be protected&lt;br /&gt;
		onlyForPlayerHimself = true, -- enabling this (true) will make sure that this element data key can only be set on player who synced it (ignores onlyForOwnPlayerVeh and allowForElements), use false/nil to disable this&lt;br /&gt;
		onlyForOwnPlayerVeh = false, -- enabling this (true) will make sure that this element data key can only be set on player's current vehicle who synced it (ignores allowForElements), use false/nil to disable this&lt;br /&gt;
		allowForElements = { -- restrict this key for certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
			[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = { -- restrict this key for certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
			[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 32}, -- if value is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
		allowedTableLength = {1, 64}, -- if value is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
		allowedNumberRange = {1, 128}, -- if value is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
	}&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering element data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local allowOnlyProtectedKeys = true -- disallow (remove by using removeElementData) every element data besides those given in protectedKeys table; in case someone wanted to flood server with garbage keys, which would be kept in memory until server restart or manual remove - setElementData(source, key, nil) won't remove it; it has to be removeElementData&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
local protectedKeys = {&lt;br /&gt;
	[&amp;quot;vehicleNumber&amp;quot;] = { -- we want vehicleNumber to be set only on vehicles, with stricte numbers in range of 1-100&lt;br /&gt;
		allowForElements = {&lt;br /&gt;
			[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedNumberRange = {1, 100},&lt;br /&gt;
	},&lt;br /&gt;
	[&amp;quot;personalVehData&amp;quot;] = { -- we want be able to set personalVehData only on current vehicle, also it should be a string with length between 1-24&lt;br /&gt;
		onlyForOwnPlayerVeh = true,&lt;br /&gt;
		allowedDataTypes = {&lt;br /&gt;
			[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
		},&lt;br /&gt;
		allowedStringLength = {1, 24},&lt;br /&gt;
	},&lt;br /&gt;
	-- perform security checks on keys stored in this table, in a convenient way&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- helper function to log and revert changes&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view of player which forced element data sync&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of element which received data&lt;br /&gt;
	local logOldValue = inspect(oldValue) -- in-depth view of old value&lt;br /&gt;
	local logNewValue = inspect(newValue) -- in-depth view of new value&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected element data abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Data key: &amp;quot;..dataKey..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Old data value: &amp;quot;..logOldValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;New data value: &amp;quot;..logNewValue..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Fail reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (forceRemove) then -- we don't want this element data key to exist at all&lt;br /&gt;
		removeElementData(sourceElement, dataKey) -- remove it&lt;br /&gt;
&lt;br /&gt;
		return true -- return success and stop here, we don't need further checks&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	setElementData(sourceElement, dataKey, oldValue, true) -- revert changes, it will call onElementDataChange event, but will fail (stop) on first condition - because server (not client) forced change&lt;br /&gt;
&lt;br /&gt;
	return true -- return success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function handleDataChange(clientElement, sourceElement, dataKey, oldValue, newValue) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from protectedKeys), false otherwise&lt;br /&gt;
	local protectedKey = protectedKeys[dataKey] -- look up whether key changed is stored in protectedKeys table&lt;br /&gt;
&lt;br /&gt;
	if (not protectedKey) then -- if it's not&lt;br /&gt;
&lt;br /&gt;
		if (allowOnlyProtectedKeys) then -- if we don't want garbage keys&lt;br /&gt;
			local failReason = &amp;quot;Key isn't present in protectedKeys&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = true -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return true -- this key isn't protected, let it through&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local onlyForPlayerHimself = protectedKey.onlyForPlayerHimself -- if key has &amp;quot;self-set&amp;quot; lock&lt;br /&gt;
	local onlyForOwnPlayerVeh = protectedKey.onlyForOwnPlayerVeh -- if key has &amp;quot;self-vehicle&amp;quot; lock&lt;br /&gt;
	local allowForElements = protectedKey.allowForElements -- if key has element type check&lt;br /&gt;
	local allowedDataTypes = protectedKey.allowedDataTypes -- if key has allowed data type check&lt;br /&gt;
&lt;br /&gt;
	if (onlyForPlayerHimself) then -- if &amp;quot;self-set&amp;quot; lock is active&lt;br /&gt;
		local matchingElement = (clientElement == sourceElement) -- verify whether player who set data is equal to element which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElement) then -- if it's not matching&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player himself&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (onlyForOwnPlayerVeh) then -- if &amp;quot;self-vehicle&amp;quot; lock is active&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(clientElement) -- get current vehicle of player which set data&lt;br /&gt;
		local matchingVehicle = (playerVehicle == sourceElement) -- check whether it matches the one which received data&lt;br /&gt;
&lt;br /&gt;
		if (not matchingVehicle) then -- if it doesn't match&lt;br /&gt;
			local failReason = &amp;quot;Can only set on player's own vehicle&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowForElements) then -- check if it's one of them&lt;br /&gt;
		local elementType = getElementType(sourceElement) -- get type of element whose data changed&lt;br /&gt;
		local matchingElementType = allowForElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingElementType) then -- this isn't matching&lt;br /&gt;
			local failReason = &amp;quot;Invalid element type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (allowedDataTypes) then -- if there's allowed data types&lt;br /&gt;
		local valueType = type(newValue) -- get data type of value&lt;br /&gt;
		local matchingType = allowedDataTypes[valueType] -- check if it's one of allowed&lt;br /&gt;
&lt;br /&gt;
		if (not matchingType) then -- if it's not then&lt;br /&gt;
			local failReason = &amp;quot;Invalid data type&amp;quot; -- reason shown in report&lt;br /&gt;
			local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
			reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedStringLength = protectedKey.allowedStringLength -- if key has specified string length check&lt;br /&gt;
		local dataString = (valueType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
		if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
			local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
			local stringLength = utf8.len(newValue) -- get length of data string&lt;br /&gt;
			local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedTableLength = protectedKey.allowedTableLength -- if key has table length check&lt;br /&gt;
		local dataTable = (valueType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
		if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
			local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
			local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
			local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
			local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
			local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
			for _, _ in pairs(newValue) do -- loop through whole table&lt;br /&gt;
				tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
				minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
				maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
				if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
					break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
			if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
				local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local allowedNumberRange = protectedKey.allowedNumberRange -- if key has allowed number range check&lt;br /&gt;
		local dataNumber = (valueType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
		if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
			local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
			local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
			local matchingRange = (newValue &amp;gt;= minRange) and (newValue &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
			if (not matchingRange) then -- if it doesn't&lt;br /&gt;
				local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;)&amp;quot; -- reason shown in report&lt;br /&gt;
				local forceRemove = false -- should key be completely removed&lt;br /&gt;
&lt;br /&gt;
				reportAndRevertDataChange(clientElement, sourceElement, dataKey, oldValue, newValue, failReason, forceRemove) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this data key&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onElementDataChangeAdvancedAC(dataKey, oldValue, newValue) -- this event makes use of handleDataChange, the code was split for better readability&lt;br /&gt;
	if (not client) then -- check if data is coming from client&lt;br /&gt;
		return false -- if it's not, do not continue&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedChange = handleDataChange(client, source, dataKey, oldValue, newValue) -- run our security checks&lt;br /&gt;
&lt;br /&gt;
	if (approvedChange) then -- it's all cool and good&lt;br /&gt;
		return false -- we don't need further action&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(client, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(client, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onElementDataChange&amp;quot;, root, onElementDataChangeAdvancedAC)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing triggerServerEvent==&lt;br /&gt;
&lt;br /&gt;
* All parameters including '''source''' can be faked and should not be trusted.&lt;br /&gt;
* Global variable '''client''' can be trusted.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player (client) '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
* Do '''not''' use the same name for your custom event as MTA native server events (which aren't remotely triggerable by default), e.g: '''onPlayerLogin'''; doing so would open door for cheaters manipulations.&lt;br /&gt;
* Be aware to which players event is sent via [[triggerClientEvent]]. For both security &amp;amp; performance reasons, admin like events should be received by admins only (to prevent confidential data being accessed), in the same time you shouldn't send each event to everyone on server (e.g: login success event which hides login panel for certain player). [[triggerClientEvent]] allows you to specify the event receiver as first (optional) argument. It defaults to [[root]], meaning if you don't specify it, it will be sent to everyone connected to server - even those who are still downloading server cache (which results in ''Server triggered clientside event eventName, but event is not added clientside.''). You can either pass player element or table with receivers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playersToReceiveEvent = {player1, player2, player3} -- each playerX is player element&lt;br /&gt;
&lt;br /&gt;
triggerClientEvent(playersToReceiveEvent, ...) -- do not forget to fill the latter part of arguments&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
Example of anti-cheat function designed for events, used for data validation for both normal, and admin events which are called from client-side.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
--[[&lt;br /&gt;
	For maximum security set punishPlayerOnDetect, punishmentBan to true (as per default configuration)&lt;br /&gt;
&lt;br /&gt;
	Anti-cheat (processServerEventData) table structure and it's options:&lt;br /&gt;
&lt;br /&gt;
	checkACLGroup = { -- check whether player who called event belongs to at least one group below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;Admin&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkPermissions = { -- check whether player who called event has permission to at least one thing below, set to false/nil to not check this&lt;br /&gt;
		&amp;quot;function.kickPlayer&amp;quot;,&lt;br /&gt;
	},&lt;br /&gt;
	checkEventData = {&lt;br /&gt;
		{&lt;br /&gt;
			debugData = &amp;quot;source&amp;quot;, -- optional details for report shown in debug message&lt;br /&gt;
			eventData = source, -- data we want to verify&lt;br /&gt;
			equalTo = client, -- compare whether eventData == equalTo&lt;br /&gt;
			allowedElements = { -- restrict eventData to be certain element type(s), set to false/nil or leave it empty to not check this (full list of element types: https://wiki.multitheftauto.com/wiki/GetElementsByType)&lt;br /&gt;
				[&amp;quot;player&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;ped&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;vehicle&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;object&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedDataTypes = { -- restrict eventData to be certain value type(s), set to false/nil or leave it empty to not check this&lt;br /&gt;
				[&amp;quot;string&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;number&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;table&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;boolean&amp;quot;] = true,&lt;br /&gt;
				[&amp;quot;nil&amp;quot;] = true,&lt;br /&gt;
			},&lt;br /&gt;
			allowedStringLength = {1, 32}, -- if eventData is a string, then it's length must be in between (min-max), set it to false/nil to not check string length - do note that allowedDataTypes must contain [&amp;quot;string&amp;quot;] = true&lt;br /&gt;
			allowedTableLength = {1, 64}, -- if eventData is a table, then it's length must be in between (min-max), set it to false/nil to not check table length - do note that allowedDataTypes must contain [&amp;quot;table&amp;quot;] = true&lt;br /&gt;
			allowedNumberRange = {1, 128}, -- if eventData is a number, then it must be in between (min-max), set it to false/nil to not check number range - do note that allowedDataTypes must contain [&amp;quot;number&amp;quot;] = true&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punishPlayerOnDetect = true -- should player be punished upon detection (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Altering server event data&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugR = 255 -- debug message - red color&lt;br /&gt;
local debugG = 127 -- debug message - green color&lt;br /&gt;
local debugB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
function processServerEventData(clientElement, sourceElement, serverEvent, securityChecks) -- the heart of our anti-cheat, which does all the magic security measurements, returns true if there was no altering from client (based on data from securityChecks), false otherwise&lt;br /&gt;
	if (not securityChecks) then -- if we haven't passed any security checks&lt;br /&gt;
		return true -- nothing to check, let code go further&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (not clientElement) then -- if client variable isn't available for some reason (although it should never happen)&lt;br /&gt;
		local failReason = &amp;quot;Client variable not present&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local checkACLGroup = securityChecks.checkACLGroup -- if there's any ACL groups to check&lt;br /&gt;
	local checkPermissions = securityChecks.checkPermissions -- if there's any permissions to check&lt;br /&gt;
	local checkEventData = securityChecks.checkEventData -- if there's any data checks&lt;br /&gt;
&lt;br /&gt;
	if (checkACLGroup) then -- let's check player ACL groups&lt;br /&gt;
		local playerAccount = getPlayerAccount(clientElement) -- get current account of player&lt;br /&gt;
		local guestAccount = isGuestAccount(playerAccount) -- if account is guest (meaning player is not logged in)&lt;br /&gt;
&lt;br /&gt;
		if (guestAccount) then -- it's the case&lt;br /&gt;
			local failReason = &amp;quot;Can't retrieve player login - guest account&amp;quot; -- reason shown in report&lt;br /&gt;
			local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
			reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
			return false -- return failure&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local accountName = getAccountName(playerAccount) -- get name of player's current account&lt;br /&gt;
		local aclString = &amp;quot;user.&amp;quot;..accountName -- format it for further use in isObjectInACLGroup function&lt;br /&gt;
&lt;br /&gt;
		for groupID = 1, #checkACLGroup do -- iterate over table of given groups&lt;br /&gt;
			local groupName = checkACLGroup[groupID] -- get each group name&lt;br /&gt;
			local aclGroup = aclGetGroup(groupName) -- check if such group exists&lt;br /&gt;
&lt;br /&gt;
			if (not aclGroup) then -- it doesn't&lt;br /&gt;
				local failReason = &amp;quot;ACL group '&amp;quot;..groupName..&amp;quot;' is missing&amp;quot; -- reason shown in report&lt;br /&gt;
				local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
				reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
				return false -- return failure&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local playerInACLGroup = isObjectInACLGroup(aclString, aclGroup) -- check if player belong to the group&lt;br /&gt;
&lt;br /&gt;
			if (playerInACLGroup) then -- yep, it's the case&lt;br /&gt;
				return true -- so it's a success&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Player doesn't belong to any given ACL group&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkPermissions) then -- check if player has at least one desired permission&lt;br /&gt;
		local allowedByDefault = false -- does he have access by default&lt;br /&gt;
&lt;br /&gt;
		for permissionID = 1, #checkPermissions do -- iterate over all permissions&lt;br /&gt;
			local permissionName = checkPermissions[permissionID] -- get permission name&lt;br /&gt;
			local hasPermission = hasObjectPermissionTo(clientElement, permissionName, allowedByDefault) -- check whether player is allowed to perform certain action&lt;br /&gt;
&lt;br /&gt;
			if (hasPermission) then -- if player has access&lt;br /&gt;
				return true -- one is available (and enough), server won't bother to check others (as return keywords also breaks loop)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local failReason = &amp;quot;Not enough permissions&amp;quot; -- reason shown in report&lt;br /&gt;
		local skipPunishment = true -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
		reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
		return false -- return failure&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (checkEventData) then -- if there is some data to verify&lt;br /&gt;
&lt;br /&gt;
		for dataID = 1, #checkEventData do -- iterate over each of data&lt;br /&gt;
			local dataToCheck = checkEventData[dataID] -- get each data set&lt;br /&gt;
			local eventData = dataToCheck.eventData -- this is the one we'll be verifying&lt;br /&gt;
			local equalTo = dataToCheck.equalTo -- we want to compare whether eventData == equalTo&lt;br /&gt;
			local allowedElements = dataToCheck.allowedElements -- check whether is element, and whether belongs to certain element types&lt;br /&gt;
			local allowedDataTypes = dataToCheck.allowedDataTypes -- do we restrict data to be certain type?&lt;br /&gt;
			local debugData = dataToCheck.debugData -- additional helper data&lt;br /&gt;
			local debugText = debugData and &amp;quot; (&amp;quot;..debugData..&amp;quot;)&amp;quot; or &amp;quot;&amp;quot; -- if it's present, format it nicely&lt;br /&gt;
&lt;br /&gt;
			if (equalTo) then -- equal check exists&lt;br /&gt;
				local matchingData = (eventData == equalTo) -- compare whether those two values are equal&lt;br /&gt;
&lt;br /&gt;
				if (not matchingData) then -- they aren't&lt;br /&gt;
					local failReason = &amp;quot;Data isn't equal @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedElements) then -- we do check whether is an element, and belongs to at least one given in the list&lt;br /&gt;
				local validElement = isElement(eventData) -- check if it's actual element&lt;br /&gt;
&lt;br /&gt;
				if (not validElement) then -- it's not&lt;br /&gt;
					local failReason = &amp;quot;Data isn't element @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local elementType = getElementType(eventData) -- it's element, so we want to know it's type&lt;br /&gt;
				local matchingElementType = allowedElements[elementType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingElementType) then -- it's not allowed&lt;br /&gt;
					local failReason = &amp;quot;Invalid element type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if (allowedDataTypes) then -- let's check allowed data types&lt;br /&gt;
				local dataType = type(eventData) -- get data type&lt;br /&gt;
				local matchingType = allowedDataTypes[dataType] -- verify whether it's allowed&lt;br /&gt;
&lt;br /&gt;
				if (not matchingType) then -- it isn't&lt;br /&gt;
					local failReason = &amp;quot;Invalid data type @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
					local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
					reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
					return false -- return failure&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedStringLength = dataToCheck.allowedStringLength -- if data has string length check&lt;br /&gt;
				local dataString = (dataType == &amp;quot;string&amp;quot;) -- make sure it's a string&lt;br /&gt;
&lt;br /&gt;
				if (allowedStringLength and dataString) then -- if we should check string length&lt;br /&gt;
					local minLength = allowedStringLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedStringLength[2] -- retrieve max length&lt;br /&gt;
					local stringLength = utf8.len(eventData) -- get length of data string&lt;br /&gt;
					local matchingLength = (stringLength &amp;gt;= minLength) and (stringLength &amp;lt;= maxLength) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid string length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedTableLength = dataToCheck.allowedTableLength -- if data has table length check&lt;br /&gt;
				local dataTable = (dataType == &amp;quot;table&amp;quot;) -- make sure it's a table&lt;br /&gt;
&lt;br /&gt;
				if (allowedTableLength and dataTable) then -- if we should check table length&lt;br /&gt;
					local minLength = allowedTableLength[1] -- retrieve min length&lt;br /&gt;
					local maxLength = allowedTableLength[2] -- retrieve max length&lt;br /&gt;
					local minLengthAchieved = false -- variable which checks 'does minimum length was achieved'&lt;br /&gt;
					local maxLengthExceeded = false -- variable which checks 'does length has exceeds more than allowed maximum'&lt;br /&gt;
					local tableLength = 0 -- store initial table length&lt;br /&gt;
&lt;br /&gt;
					for _, _ in pairs(eventData) do -- loop through whole table&lt;br /&gt;
						tableLength = (tableLength + 1) -- add + 1 on each table entry&lt;br /&gt;
						minLengthAchieved = (tableLength &amp;gt;= minLength) -- is length bigger or at very minimum we require&lt;br /&gt;
						maxLengthExceeded = (tableLength &amp;gt; maxLength) -- does table exceeded more than max length?&lt;br /&gt;
&lt;br /&gt;
						if (maxLengthExceeded) then -- it is bigger than it should be&lt;br /&gt;
							break -- break the loop (due of condition above being worthy, it makes no point to count further and waste CPU, on a table which potentially could have huge amount of entries)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					local matchingLength = (minLengthAchieved and not maxLengthExceeded) -- check if min length has been achieved, and make sure that it doesn't go beyond max length&lt;br /&gt;
&lt;br /&gt;
					if (not matchingLength) then -- this table doesn't match requirements&lt;br /&gt;
						local failReason = &amp;quot;Invalid table length (must be between &amp;quot;..minLength..&amp;quot;-&amp;quot;..maxLength..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				local allowedNumberRange = dataToCheck.allowedNumberRange -- if data has number range check&lt;br /&gt;
				local dataNumber = (dataType == &amp;quot;number&amp;quot;) -- make sure it's a number&lt;br /&gt;
&lt;br /&gt;
				if (allowedNumberRange and dataNumber) then -- if we should check number range&lt;br /&gt;
					local minRange = allowedNumberRange[1] -- retrieve min number range&lt;br /&gt;
					local maxRange = allowedNumberRange[2] -- retrieve max number range&lt;br /&gt;
					local matchingRange = (eventData &amp;gt;= minRange) and (eventData &amp;lt;= maxRange) -- compare whether value fits in between&lt;br /&gt;
&lt;br /&gt;
					if (not matchingRange) then -- if it doesn't&lt;br /&gt;
						local failReason = &amp;quot;Invalid number range (must be between &amp;quot;..minRange..&amp;quot;-&amp;quot;..maxRange..&amp;quot;) @ argument &amp;quot;..dataID..debugText -- reason shown in report&lt;br /&gt;
						local skipPunishment = false -- should server skip punishment&lt;br /&gt;
&lt;br /&gt;
						reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- report accident, and handle (or not) this player&lt;br /&gt;
&lt;br /&gt;
						return false -- return failure&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- security checks passed, we are all clear with this event call&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function reportAndHandleEventAbnormality(clientElement, sourceElement, serverEvent, failReason, skipPunishment) -- helper function to log and handle accidents&lt;br /&gt;
	local logClient = inspect(clientElement) -- in-depth view player which called event&lt;br /&gt;
	local logSerial = getPlayerSerial(clientElement) or &amp;quot;N/A&amp;quot; -- client serial, or &amp;quot;N/A&amp;quot; if not possible, for some reason&lt;br /&gt;
	local logSource = inspect(sourceElement) -- in-depth view of source element&lt;br /&gt;
	local logText = -- fill our report with data&lt;br /&gt;
		&amp;quot;*\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Detected event abnormality:\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client: &amp;quot;..logClient..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Client serial: &amp;quot;..logSerial..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Source: &amp;quot;..logSource..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Event: &amp;quot;..serverEvent..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Reason: &amp;quot;..failReason..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;*&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(logText, debugLevel, debugR, debugG, debugB) -- print it to debug&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect or skipPunishment) then -- we don't want to punish player for some reason&lt;br /&gt;
		return true -- stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(clientElement, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(clientElement, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true -- all done, report success&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onServerEvent(clientData)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local dataToPass = 10&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerEvent&amp;quot;, localPlayer, dataToPass)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = clientData, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;number&amp;quot;] = true, -- we want it to be only number&lt;br /&gt;
					},&lt;br /&gt;
					allowedNumberRange = {1, 100}, -- in range of 1 to 100&lt;br /&gt;
					debugData = &amp;quot;clientData&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerEvent&amp;quot;, root, onServerEvent)&lt;br /&gt;
&lt;br /&gt;
function onServerAdminEvent(playerToBan)&lt;br /&gt;
	--[[&lt;br /&gt;
		Assume this server admin event (function) is called in such way:&lt;br /&gt;
&lt;br /&gt;
		local playerToBan = getPlayerFromName(&amp;quot;playerToBan&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		triggerServerEvent(&amp;quot;onServerAdminEvent&amp;quot;, localPlayer, playerToBan)&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local shouldProcessServerCode = processServerEventData(&lt;br /&gt;
		client, -- client element - responsible for calling event&lt;br /&gt;
		source, -- source element - passed in triggerServerEvent (as 2nd argument)&lt;br /&gt;
		eventName, -- name of event - in this case 'onServerAdminEvent'&lt;br /&gt;
		{&lt;br /&gt;
			checkACLGroup = { -- we need to check whether player who called event belongs to ACL groups&lt;br /&gt;
				&amp;quot;Admin&amp;quot;, -- in this case admin group&lt;br /&gt;
			},&lt;br /&gt;
			checkEventData = { -- we want to verify everything what comes from client&lt;br /&gt;
				{&lt;br /&gt;
					eventData = source, -- first to check, source variable&lt;br /&gt;
					equalTo = client, -- we want to check whether it matches player who called event&lt;br /&gt;
					debugData = &amp;quot;source&amp;quot;, -- helper details which would be shown in report&lt;br /&gt;
				},&lt;br /&gt;
				{&lt;br /&gt;
					eventData = playerToBan, -- let's check the data which client sent to us&lt;br /&gt;
					allowedDataTypes = {&lt;br /&gt;
						[&amp;quot;player&amp;quot;] = true, -- we want it to be player&lt;br /&gt;
					},&lt;br /&gt;
					debugData = &amp;quot;playerToBan&amp;quot;, -- if something goes wrong, let server know where (it will appear in debug report)&lt;br /&gt;
				},&lt;br /&gt;
			},&lt;br /&gt;
		}&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	if (not shouldProcessServerCode) then -- something isn't right, no green light for processing code behind this scope&lt;br /&gt;
		return false -- stop code execution&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- do code as usual&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerAdminEvent&amp;quot;, true)&lt;br /&gt;
addEventHandler(&amp;quot;onServerAdminEvent&amp;quot;, root, onServerAdminEvent)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing server-only events==&lt;br /&gt;
* It is very important to '''disable remote triggering''' ability in [https://wiki.multitheftauto.com/wiki/AddEvent addEvent], to prevent calling server-side only events from client-side.&lt;br /&gt;
* '''Admin''' styled '''events''' should be verifying player '''ACL rights''', either by [https://wiki.multitheftauto.com/wiki/IsObjectInACLGroup isObjectInACLGroup] or [https://wiki.multitheftauto.com/wiki/HasObjectPermissionTo hasObjectPermissionTo].&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&lt;br /&gt;
This example shows how you should make event server-side only.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function onServerSideOnlyEvent()&lt;br /&gt;
	-- do some server-side stuff&lt;br /&gt;
end&lt;br /&gt;
addEvent(&amp;quot;onServerSideOnlyEvent&amp;quot;, false) -- set second argument (allowRemoteTriger) to false, so it can't be called from client&lt;br /&gt;
addEventHandler(&amp;quot;onServerSideOnlyEvent&amp;quot;, root, onServerSideOnlyEvent) -- associate our event with function onServerSideOnlyEvent&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Securing projectiles &amp;amp; explosions==&lt;br /&gt;
This section (and '''code''' is '''work in progress''') - '''use at your own risk'''.&lt;br /&gt;
&lt;br /&gt;
----------&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile ammo tracker:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerProjectileAmmo = {} -- store player-held weapons ammo here&lt;br /&gt;
local playerWeaponsToTrack = { -- keep track of ammo for certain player-held weapon types, basically those which create projectile; [weaponID] = weaponSlot&lt;br /&gt;
	[16] = 8, -- grenade&lt;br /&gt;
	[17] = 8, -- teargas&lt;br /&gt;
	[18] = 8, -- molotov&lt;br /&gt;
	[35] = 7, -- rocket launcher&lt;br /&gt;
	[36] = 7, -- rocket launcher (heat-seeking)&lt;br /&gt;
	[39] = 8, -- satchel charge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for weaponID, weaponSlot in pairs(playerWeaponsToTrack) do&lt;br /&gt;
		local weaponInSlot = getPedWeapon(playerElement, weaponSlot)&lt;br /&gt;
		local weaponTotalAmmo = getPedTotalAmmo(playerElement, weaponSlot)&lt;br /&gt;
		local weaponHasAmmo = (weaponTotalAmmo &amp;gt; 0)&lt;br /&gt;
		local weaponMatching = (weaponInSlot == weaponID)&lt;br /&gt;
		local updateWeaponAmmo = (weaponMatching and weaponHasAmmo)&lt;br /&gt;
&lt;br /&gt;
		if (updateWeaponAmmo) then&lt;br /&gt;
			local storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			local newWeaponAmmo = (updateWeaponAmmo and weaponTotalAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
			if (not storedProjectileAmmo) then&lt;br /&gt;
				playerProjectileAmmo[playerElement] = {}&lt;br /&gt;
				storedProjectileAmmo = playerProjectileAmmo[playerElement]&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			storedProjectileAmmo[weaponID] = newWeaponAmmo&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function hasPlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
	local projectileAmmo = (projectileData and projectileData[projectileWeapon])&lt;br /&gt;
&lt;br /&gt;
	return projectileAmmo&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectileAmmo(playerElement, projectileWeapon)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerDead = isPedDead(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerDead) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectileData = playerProjectileAmmo[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectileData) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local projectileWeaponAmmo = playerProjectileData[projectileWeapon]&lt;br /&gt;
	local tempProjectileAmmo = (projectileWeaponAmmo - 1)&lt;br /&gt;
	local newProjectileAmmo = (tempProjectileAmmo &amp;gt; 0 and tempProjectileAmmo or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectileData[projectileWeapon] = newProjectileAmmo&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerWeaponSwitchAntiCheat(previousWeaponID, currentWeaponID)&lt;br /&gt;
	setTimer(updateProjectileAmmoForPlayer, 50, 1, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWeaponSwitch&amp;quot;, root, onPlayerWeaponSwitchAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onResourceStartAntiCheat()&lt;br /&gt;
	local playersTable = getElementsByType(&amp;quot;player&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	for playerID = 1, #playersTable do&lt;br /&gt;
		local playerElement = playersTable[playerID]&lt;br /&gt;
&lt;br /&gt;
		updateProjectileAmmoForPlayer(playerElement)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onResourceStart&amp;quot;, resourceRoot, onResourceStartAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerWastedQuitAntiCheat()&lt;br /&gt;
	playerProjectileAmmo[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerWastedQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Projectile handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local playerCreatedProjectiles = {} -- store count of legitimately created player projectiles; [playerElement] = {[projectileType] = activeProjectiles}&lt;br /&gt;
local projectileTypes = {&lt;br /&gt;
	[16] = { -- Grenade&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[17] = { -- Teargas&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[17] = true, -- teargas&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[18] = { -- Molotov&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[19] = { -- Rocket&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[425] = true, -- hunter&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[20] = { -- Rocket (heat-seeking)&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileMaxVelocity = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[21] = { -- Airbomb&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[39] = { -- Satchel charge&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 2,&lt;br /&gt;
		projectileAllowedWeapons = {&lt;br /&gt;
			[39] = true, -- satchel charge&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[58] = { -- Hydra flare&lt;br /&gt;
		projectileAllowed = true,&lt;br /&gt;
		projectileMaxDistanceFromCreator = 5,&lt;br /&gt;
		projectileAllowedVehicles = {&lt;br /&gt;
			[520] = true, -- hydra&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportProjectileAbnormality(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	local projectileSyncer = inspect(playerElement)&lt;br /&gt;
	local projectilePosition = projectileX..&amp;quot;, &amp;quot;..projectileY..&amp;quot;, &amp;quot;..projectileZ&lt;br /&gt;
	local projectileZoneName = getZoneName(projectileX, projectileY, projectileZ, false)&lt;br /&gt;
	local projectileCityName = getZoneName(projectileX, projectileY, projectileZ, true)&lt;br /&gt;
	local projectileLog =&lt;br /&gt;
		&amp;quot;* Detected projectile abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile syncer: &amp;quot;..projectileSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile type: &amp;quot;..projectileType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Projectile position: &amp;quot;..projectilePosition.. &amp;quot; (&amp;quot;..projectileZoneName..&amp;quot;, &amp;quot;..projectileCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(projectileLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processProjectileChecks(playerElement, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
	local projectileAllowed = projectileData.projectileAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not projectileAllowed) then&lt;br /&gt;
		return false, &amp;quot;Projectile not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileAllowedWeapons = projectileData.projectileAllowedWeapons&lt;br /&gt;
&lt;br /&gt;
	if (projectileAllowedWeapons) then&lt;br /&gt;
		local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
		local allowedWeapon = projectileAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedWeapon) then&lt;br /&gt;
			return false, &amp;quot;Player is not holding correct weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
		local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
		if (not playerHasAmmo) then&lt;br /&gt;
			return false, &amp;quot;Player doesn't have ammo for weapon&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (playerVehicle) then&lt;br /&gt;
		local projectileAllowedVehicles = projectileData.projectileAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
		if (projectileAllowedVehicles) then&lt;br /&gt;
			local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
			local allowedVehicle = projectileAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedVehicle) then&lt;br /&gt;
				return false, &amp;quot;Player is not inside allowed vehicles&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
			local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
			if (not playerDriver) then&lt;br /&gt;
				return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Player in vehicle&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxDistanceFromCreator = projectileData.projectileMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToProjectile = getDistanceBetweenPoints3D(playerX, playerY, playerZ, projectileX, projectileY, projectileZ)&lt;br /&gt;
		local matchingProjectileDistance = (distanceToProjectile &amp;lt;= projectileMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileDistance) then&lt;br /&gt;
			return false, &amp;quot;Projectile distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileMaxVelocity = projectileData.projectileMaxVelocity&lt;br /&gt;
&lt;br /&gt;
	if (projectileMaxVelocity) then&lt;br /&gt;
		local projectileVelocity = (projectileVX ^ 2 + projectileVY ^ 2 + projectileVZ ^ 2)&lt;br /&gt;
		local projectileSpeed = math.sqrt(projectileVelocity)&lt;br /&gt;
		local matchingProjectileVelocity = (projectileSpeed &amp;lt;= projectileMaxVelocity)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingProjectileVelocity) then&lt;br /&gt;
			return false, &amp;quot;Projectile velocity mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trackPlayerProjectile(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		playerCreatedProjectiles[playerElement] = {}&lt;br /&gt;
		playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID] or 0&lt;br /&gt;
	local newProjectilesCount = (projectilesByType + 1)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
	local projectileData = projectileTypes[projectileType]&lt;br /&gt;
&lt;br /&gt;
	if (not projectileData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectileBehaviour = projectileData.projectileOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not projectileBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = projectileBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = projectileBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = projectileBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
	local validElement = isElement(playerElement)&lt;br /&gt;
&lt;br /&gt;
	if (not validElement) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local playerProjectiles = playerCreatedProjectiles[playerElement]&lt;br /&gt;
&lt;br /&gt;
	if (not playerProjectiles) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local projectilesByType = playerProjectiles[projectileID]&lt;br /&gt;
&lt;br /&gt;
	if (not projectilesByType) then&lt;br /&gt;
		return false, true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local tempProjectilesCount = (projectilesByType - 1)&lt;br /&gt;
	local newProjectilesCount = (tempProjectilesCount &amp;gt; 0 and tempProjectilesCount or nil)&lt;br /&gt;
&lt;br /&gt;
	playerProjectiles[projectileID] = newProjectilesCount&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onPlayerProjectileCreationAntiCheat(projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
	local approvedProjectile, detectionCode = processProjectileChecks(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedProjectile) then&lt;br /&gt;
		trackPlayerProjectile(source, projectileType)&lt;br /&gt;
&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getProjectileDetectionOverwrittenBehavior(projectileType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportProjectileAbnormality(source, projectileType, projectileX, projectileY, projectileZ, projectileForce, projectileTarget, projectileRX, projectileRY, projectileRZ, projectileVX, projectileVY, projectileVZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerProjectileCreation&amp;quot;, root, onPlayerProjectileCreationAntiCheat)&lt;br /&gt;
&lt;br /&gt;
function onPlayerQuitAntiCheat()&lt;br /&gt;
	playerCreatedProjectiles[source] = nil&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, onPlayerQuitAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;
Explosions handler:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local rocketInstantExplosionDistanceThreshold = 1.55 -- distance below which only explosion will be created (and not rocket projectile, hence not calling onPlayerProjectileCreation); do not change it&lt;br /&gt;
local explosionTypes = { -- more specific info on explosion, and whether it is allowed&lt;br /&gt;
	[0] = { -- Grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[16] = true, -- grenade&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[1] = { -- Molotov&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[18] = true, -- molotov&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[2] = { -- Rocket&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedWeapons = {&lt;br /&gt;
			[35] = true, -- rocket launcher&lt;br /&gt;
			[36] = true, -- rocket launcher (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[3] = { -- Rocket weak&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionAllowedProjectiles = {&lt;br /&gt;
			[19] = true, -- rocket&lt;br /&gt;
			[20] = true, -- rocket (heat-seeking)&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	[4] = { -- Car&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[5] = { -- Car quick&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[6] = { -- Boat&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[7] = { -- Heli&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[8] = { -- Mine&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[9] = { -- Object&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[10] = { -- Tank grenade&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
		explosionMaxDistanceFromCreator = 80,&lt;br /&gt;
		explosionAllowedVehicles = {&lt;br /&gt;
			[432] = true,&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	[11] = { -- Small&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
	[12] = { -- Tiny&lt;br /&gt;
		explosionAllowed = true,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local reportAbnormality = true -- whether to report about abnormality in debug script - by default&lt;br /&gt;
local punishPlayerOnDetect = false -- should player be punished upon detection; true - yes, or false to not do anything (make sure that resource which runs this code has admin rights)&lt;br /&gt;
local punishmentBan = true -- only relevant if punishPlayerOnDetect is set to true; use true for ban or false for kick&lt;br /&gt;
local punishmentReason = &amp;quot;Projectile/explosion anti-cheat&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; reason which would be shown to punished player&lt;br /&gt;
local punishedBy = &amp;quot;Console&amp;quot; -- only relevant if punishPlayerOnDetect is set to true; who was responsible for punishing, as well shown to punished player&lt;br /&gt;
local banByIP = false -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; banning by IP nowadays is not recommended (...)&lt;br /&gt;
local banByUsername = false -- community username - legacy thing, hence is set to false and should stay like that&lt;br /&gt;
local banBySerial = true -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; (...) if there is a player serial to use instead&lt;br /&gt;
local banTime = 0 -- only relevant if punishPlayerOnDetect and punishmentBan is set to true; time in seconds, 0 for permanent&lt;br /&gt;
local debugMsgLevel = 4 -- this debug level allows to hide INFO: prefix, and use custom colors&lt;br /&gt;
local debugMsgR = 255 -- debug message - red color&lt;br /&gt;
local debugMsgG = 127 -- debug message - green color&lt;br /&gt;
local debugMsgB = 0 -- debug message - blue color&lt;br /&gt;
&lt;br /&gt;
local function reportExplosionAbnormality(playerElement, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	local explosionSyncer = inspect(playerElement)&lt;br /&gt;
	local explosionType = explosionType&lt;br /&gt;
	local explosionPosition = explosionX..&amp;quot;, &amp;quot;..explosionY..&amp;quot;, &amp;quot;..explosionZ&lt;br /&gt;
	local explosionZoneName = getZoneName(explosionX, explosionY, explosionZ, false)&lt;br /&gt;
	local explosionCityName = getZoneName(explosionX, explosionY, explosionZ, true)&lt;br /&gt;
	local explosionLog =&lt;br /&gt;
		&amp;quot;* Detected explosion abnormality - &amp;quot;..detectionCode..&amp;quot; *\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion syncer: &amp;quot;..explosionSyncer..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion type: &amp;quot;..explosionType..&amp;quot;\n&amp;quot;..&lt;br /&gt;
		&amp;quot;Explosion position: &amp;quot;..explosionPosition.. &amp;quot; (&amp;quot;..explosionZoneName..&amp;quot;, &amp;quot;..explosionCityName..&amp;quot;)\n&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	outputDebugString(explosionLog, debugMsgLevel, debugMsgR, debugMsgG, debugMsgB)&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function processExplosionChecks(playerElement, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
	local explosionAllowed = explosionData.explosionAllowed&lt;br /&gt;
&lt;br /&gt;
	if (not explosionAllowed) then&lt;br /&gt;
		return false, &amp;quot;Explosion type not allowed&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rocketExplosion = (explosionType == 2)&lt;br /&gt;
&lt;br /&gt;
	if (rocketExplosion) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local instantExplosion = (distanceToExplosion &amp;lt; rocketInstantExplosionDistanceThreshold)&lt;br /&gt;
&lt;br /&gt;
		if (instantExplosion) then&lt;br /&gt;
			local explosionAllowedWeapons = explosionData.explosionAllowedWeapons&lt;br /&gt;
			local playerWeapon = getPedWeapon(playerElement)&lt;br /&gt;
			local allowedWeapon = explosionAllowedWeapons[playerWeapon]&lt;br /&gt;
&lt;br /&gt;
			if (not allowedWeapon) then&lt;br /&gt;
				return false, &amp;quot;Player is not holding rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			local weaponAmmo = hasPlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
			local playerHasAmmo = (weaponAmmo &amp;gt; 0)&lt;br /&gt;
&lt;br /&gt;
			if (not playerHasAmmo) then&lt;br /&gt;
				return false, &amp;quot;Player doesn't have ammo for rocket launcher&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			decreasePlayerProjectileAmmo(playerElement, playerWeapon)&lt;br /&gt;
&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedProjectiles = explosionData.explosionAllowedProjectiles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedProjectiles) then&lt;br /&gt;
&lt;br /&gt;
		for projectileID, _ in pairs(explosionAllowedProjectiles) do&lt;br /&gt;
			local atleastOneProjectile = decreasePlayerProjectiles(playerElement, projectileID)&lt;br /&gt;
&lt;br /&gt;
			if (atleastOneProjectile) then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		return false, &amp;quot;Explosion created without respective projectile&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionAllowedVehicles = explosionData.explosionAllowedVehicles&lt;br /&gt;
&lt;br /&gt;
	if (explosionAllowedVehicles) then&lt;br /&gt;
		local playerVehicle = getPedOccupiedVehicle(playerElement)&lt;br /&gt;
&lt;br /&gt;
		if (not playerVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is not in vehicle&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleModel = getElementModel(playerVehicle)&lt;br /&gt;
		local allowedVehicle = explosionAllowedVehicles[vehicleModel]&lt;br /&gt;
&lt;br /&gt;
		if (not allowedVehicle) then&lt;br /&gt;
			return false, &amp;quot;Player is inside not allowed vehicles&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local vehicleDriver = getVehicleController(playerVehicle)&lt;br /&gt;
		local playerDriver = (playerElement == vehicleDriver)&lt;br /&gt;
&lt;br /&gt;
		if (not playerDriver) then&lt;br /&gt;
			return false, &amp;quot;Player is not vehicle driver&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionMaxDistanceFromCreator = explosionData.explosionMaxDistanceFromCreator&lt;br /&gt;
&lt;br /&gt;
	if (explosionMaxDistanceFromCreator) then&lt;br /&gt;
		local playerX, playerY, playerZ = getElementPosition(playerElement)&lt;br /&gt;
		local distanceToExplosion = getDistanceBetweenPoints3D(playerX, playerY, playerZ, explosionX, explosionY, explosionZ)&lt;br /&gt;
		local matchingExplosionDistance = (distanceToExplosion &amp;lt; explosionMaxDistanceFromCreator)&lt;br /&gt;
&lt;br /&gt;
		if (not matchingExplosionDistance) then&lt;br /&gt;
			return false, &amp;quot;Explosion distance mismatch&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
	local explosionData = explosionTypes[explosionType]&lt;br /&gt;
&lt;br /&gt;
	if (not explosionData) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local explosionBehaviour = explosionData.explosionOverwriteBehaviour&lt;br /&gt;
&lt;br /&gt;
	if (not explosionBehaviour) then&lt;br /&gt;
		return reportAbnormality, punishPlayerOnDetect, punishmentBan&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local overwriteReport = explosionBehaviour.reportAbnormality&lt;br /&gt;
	local overwritePunish = explosionBehaviour.punishPlayerOnDetect&lt;br /&gt;
	local overwriteBan = explosionBehaviour.punishmentBan&lt;br /&gt;
&lt;br /&gt;
	return overwriteReport, overwritePunish, overwriteBan&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function onExplosionAntiCheat(explosionX, explosionY, explosionZ, explosionType)&lt;br /&gt;
	local serverSyncExplosion = (source == root)&lt;br /&gt;
&lt;br /&gt;
	if (serverSyncExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local approvedExplosion, detectionCode = processExplosionChecks(source, explosionType, explosionX, explosionY, explosionZ)&lt;br /&gt;
&lt;br /&gt;
	if (approvedExplosion) then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local reportAbnormality, punishPlayerOnDetect, punishmentBan = getExplosionDetectionOverwrittenBehavior(explosionType)&lt;br /&gt;
&lt;br /&gt;
	if (reportAbnormality) then&lt;br /&gt;
		reportExplosionAbnormality(source, explosionType, explosionX, explosionY, explosionZ, detectionCode)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	cancelEvent()&lt;br /&gt;
&lt;br /&gt;
	if (not punishPlayerOnDetect) then -- we don't want to punish player for some reason&lt;br /&gt;
		return false -- so stop here&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	if (punishmentBan) then -- if it's ban&lt;br /&gt;
		banPlayer(source, banByIP, banByUsername, banBySerial, punishedBy, punishmentReason, banTime) -- remove his presence from server&lt;br /&gt;
	else -- otherwise&lt;br /&gt;
		kickPlayer(source, punishedBy, punishmentReason) -- simply kick player out of server&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onExplosion&amp;quot;, root, onExplosionAntiCheat)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Handling non-registered events==&lt;br /&gt;
&lt;br /&gt;
See: [[onPlayerTriggerInvalidEvent]]&lt;br /&gt;
&lt;br /&gt;
==Handling events spam==&lt;br /&gt;
See: [[onPlayerTriggerEventThreshold]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=Server_Commands&amp;diff=78775</id>
		<title>Server Commands</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=Server_Commands&amp;diff=78775"/>
		<updated>2024-01-13T15:53:26Z</updated>

		<summary type="html">&lt;p&gt;DColeman: Fix typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page lists all built in commands that the server can process. All these commands can be entered via the server console or the client console depending upon permissions unless otherwise stated.&lt;br /&gt;
&lt;br /&gt;
==Resource commands==&lt;br /&gt;
====check====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: check [ ''all'' | ''&amp;lt;resource-name&amp;gt;'' ]&amp;lt;br&amp;gt;&lt;br /&gt;
:Checks which files would be changed with [[Server_Commands#upgrade|upgrade]] command. Does not modify anything.&lt;br /&gt;
====info====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: info ''&amp;lt;resource-name&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Get info for a resource eg: info admin&lt;br /&gt;
====list====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Shows a list of resources&lt;br /&gt;
====refresh====&lt;br /&gt;
:Refresh resource list to find new resources&lt;br /&gt;
====refreshall====&lt;br /&gt;
:Refresh resources and restart any changed resources&lt;br /&gt;
====restart====&lt;br /&gt;
:Usage: restart ''&amp;lt;resource-name&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Restarts a running resource eg: restart admin&lt;br /&gt;
&lt;br /&gt;
====start====&lt;br /&gt;
:Usage: start ''&amp;lt;resource-name&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Start a loaded resource eg: start admin&lt;br /&gt;
====stop====&lt;br /&gt;
:Usage: stop ''&amp;lt;resource-name&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Stop a resource eg: stop admin&lt;br /&gt;
====stopall====&lt;br /&gt;
:Stop all running resources&lt;br /&gt;
====upgrade====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: upgrade [ ''all'' | ''&amp;lt;resource-name&amp;gt;'' ]&amp;lt;br&amp;gt;&lt;br /&gt;
:Perform a basic upgrade of all resources. The [[Server_Commands#check|check]] command shows the list of changes this command will make.&lt;br /&gt;
====aclrequest====&lt;br /&gt;
:Usage: aclrequest [ ''list'' | ''allow'' | ''deny'' ] ''&amp;lt;resource-name&amp;gt;'' [ ''&amp;lt;right&amp;gt;'' | ''all'' ]&amp;lt;br&amp;gt;&lt;br /&gt;
:Manage ACL requests from resources implementing &amp;lt;aclrequest&amp;gt; in their [[meta.xml]]&lt;br /&gt;
{{New feature/item|3.0156|1.5.5|11851|&lt;br /&gt;
====reloadacl====&lt;br /&gt;
:Perform a simple ACL reload.}}&lt;br /&gt;
&lt;br /&gt;
==Account commands==&lt;br /&gt;
====aexec====&lt;br /&gt;
:Usage: aexec ''&amp;lt;nick&amp;gt;'' ''&amp;lt;command&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Force a player to execute a command eg: aexec playername say hello&lt;br /&gt;
====addaccount====&lt;br /&gt;
:Usage: addaccount ''&amp;lt;accountname&amp;gt;'' ''&amp;lt;password&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Add an account eg: addaccount accountname password&lt;br /&gt;
====chgpass====&lt;br /&gt;
:Usage: chgpass ''&amp;lt;accountname&amp;gt;'' ''&amp;lt;password&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Change an accounts password eg: chgpass account newpw&lt;br /&gt;
====delaccount====&lt;br /&gt;
:Usage: delaccount ''&amp;lt;accountname&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Delete an account eg: delaccount accountname&lt;br /&gt;
====reloadbans====&lt;br /&gt;
:Reloads all the bans from ''banlist.xml''.&lt;br /&gt;
====authserial====&lt;br /&gt;
:Usage: authserial &amp;lt;account-name&amp;gt; [list|removelast|httppass]&lt;br /&gt;
:Manage serial authentication for an account.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Server commands==&lt;br /&gt;
====ase====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:See the amount of master server list queries&lt;br /&gt;
====debugdb====&lt;br /&gt;
:Usage: debugdb ''&amp;lt;''0-2''&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Set logging level for database functions. [0-Off &amp;amp;nbsp;1-Errors only &amp;amp;nbsp;2-All]&lt;br /&gt;
:By default, logging output is written to the file '''logs/db.log''' unless another file is declared in the [[Mtaserver.conf#dbfile|&amp;lt;dbfile&amp;gt; section of mtaserver.conf]]&lt;br /&gt;
====debugjoinflood====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Shows debug information regarding the join flood mitigation feature.&lt;br /&gt;
====debuguptime====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Shows how many days the server has been up and running.&lt;br /&gt;
====help====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Displays these list of commands&lt;br /&gt;
====loadmodule====&lt;br /&gt;
:Usage: loadmodule ''&amp;lt;module-filename&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Load a module eg: loadmodule ml_sockets.dll&lt;br /&gt;
&lt;br /&gt;
====unloadmodule====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: unloadmodule ''&amp;lt;module-filename&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Unload a module eg: unloadmodule ml_sockets.dll&lt;br /&gt;
====reloadmodule====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: reloadmodule ''&amp;lt;module-filename&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Reload a module eg: reloadmodule ml_sockets.dll&lt;br /&gt;
====openports====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Server console only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Test if server ports are open&lt;br /&gt;
====sfakelag====&lt;br /&gt;
{{Main|Command fakelag}}&lt;br /&gt;
:Usage: sfakelag ''&amp;lt;packet loss&amp;gt;'' ''&amp;lt;extra ping&amp;gt;'' ''&amp;lt;ping variance&amp;gt;'' [''&amp;lt;KBPS limit&amp;gt;'']&lt;br /&gt;
:'''Only available if enabled in the [[mtaserver.conf]] file.'''&lt;br /&gt;
:Adds artificial packet loss, ping, jitter and bandwidth limits to the server-client connections.&lt;br /&gt;
====shutdown====&lt;br /&gt;
:Usage: shutdown ''&amp;lt;reason&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Shutdown the server eg: shutdown put reason here&lt;br /&gt;
====sver====&lt;br /&gt;
:Get the server MTA version&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Other commands==&lt;br /&gt;
====say====&lt;br /&gt;
:Usage: say ''&amp;lt;text&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Show a message to all players on the server eg: say hello&lt;br /&gt;
====whois====&lt;br /&gt;
:Usage: whois ''&amp;lt;nick&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Get the IP of a player currently connected (use '''whowas''' for IP/serial/version)&lt;br /&gt;
{{Deprecated feature|3.0156|1.5.6|&lt;br /&gt;
====whowas====&lt;br /&gt;
:Usage: whowas ''&amp;lt;nick&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Get IP/Serial/username of a player that was previously connected to the server&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
====ver====&lt;br /&gt;
:Get the MTA version&lt;br /&gt;
&lt;br /&gt;
==Client relevant only==&lt;br /&gt;
====chgmypass====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: chgmypass ''&amp;lt;oldpass&amp;gt;'' ''&amp;lt;newpass&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Change your password eg: chgmypass oldpw newpw&lt;br /&gt;
====debugscript====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: debugscript ''&amp;lt;''0-3''&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Remove (This does not work &amp;quot;Incorrect client type for this command&amp;quot;)&lt;br /&gt;
====login====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: login ''&amp;lt;accountname&amp;gt;'' ''&amp;lt;password&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Login to an account eg: login accountname password&lt;br /&gt;
&lt;br /&gt;
====logout====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Log out of the current account&lt;br /&gt;
====me====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: me ''&amp;lt;text&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Show a message to all players on the server, with your nick prepended&lt;br /&gt;
====msg====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: msg ''&amp;lt;nick&amp;gt;'' ''&amp;lt;text&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Send a message to a player eg: msg playername hello&lt;br /&gt;
====nick====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: nick ''&amp;lt;old-nick&amp;gt;'' ''&amp;lt;new-nick&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Change your ingame nickname&lt;br /&gt;
====teamsay====&lt;br /&gt;
:&amp;lt;ins&amp;gt;Client  only&amp;lt;/ins&amp;gt;&lt;br /&gt;
:Usage: teamsay ''&amp;lt;text&amp;gt;''&amp;lt;br&amp;gt;&lt;br /&gt;
:Send a message to all players on the same team&lt;br /&gt;
&lt;br /&gt;
[[Category: Support]]&lt;br /&gt;
&lt;br /&gt;
[[hu:Server Commands]]&lt;br /&gt;
[[ru:Server Commands]]&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=GetGuestPlayers&amp;diff=77889</id>
		<title>GetGuestPlayers</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=GetGuestPlayers&amp;diff=77889"/>
		<updated>2023-10-05T16:55:31Z</updated>

		<summary type="html">&lt;p&gt;DColeman: Spelling correction. This function cannot return false&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Useful Function}}&lt;br /&gt;
__NOTOC__&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;table getGuestPlayers (  )&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Returns===&lt;br /&gt;
Returns all players who are not logged in or an empty table if there are no such players.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&amp;lt;section name=&amp;quot;Server&amp;quot; class=&amp;quot;server&amp;quot; show=&amp;quot;true&amp;quot;&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function getGuestPlayers (  )&lt;br /&gt;
    local Guest = { } ;&lt;br /&gt;
&lt;br /&gt;
    for _ , players_ in ipairs ( getElementsByType ( &amp;quot;player&amp;quot; ) ) do&lt;br /&gt;
        local playerAcc = getPlayerAccount ( players_ )&lt;br /&gt;
&lt;br /&gt;
        if isGuestAccount ( playerAcc ) then&lt;br /&gt;
            &lt;br /&gt;
            table.insert ( Guest , players_ )&lt;br /&gt;
&lt;br /&gt;
        end&lt;br /&gt;
    end &lt;br /&gt;
    return Guest&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;/section&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Example 1==&lt;br /&gt;
This example gets players Guest Account and show msg login or register&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Example 2==&lt;br /&gt;
This example gets players Guest Account and kill them&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addCommandHandler ( &amp;quot;getGuestPlayers_&amp;quot; , function ( )&lt;br /&gt;
&lt;br /&gt;
        local GuestPlayers = getGuestPlayers (  )&lt;br /&gt;
&lt;br /&gt;
        if ( #GuestPlayers ~= 0 ) then&lt;br /&gt;
&lt;br /&gt;
            for _ , v in ipairs ( GuestPlayers ) do&lt;br /&gt;
&lt;br /&gt;
                outputChatBox (  getPlayerName ( v ) : gsub ( &amp;quot;#%x%x%x%x%x%x&amp;quot;, &amp;quot;&amp;quot; ) .. &amp;quot;plz Login or Register&amp;quot; , v , 255 , 255 , 255 , true )&lt;br /&gt;
&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
) ;&lt;br /&gt;
&lt;br /&gt;
-- F8 Say : getGuestPlayers_&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Example 3==&lt;br /&gt;
This example show number players not login or Guest Account&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
outputChatBox ( #getGuestPlayers (  )  )&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Author: Abdul_KariM&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Useful_Functions}}&lt;/div&gt;</summary>
		<author><name>DColeman</name></author>
	</entry>
</feed>