[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: ['https://steamcommunity.com/tradeoffer/new/?partner=883337522&token=D4Ku6oDJ']\n"
  },
  {
    "path": "Changelog",
    "content": "1.7.4\n- Removed the need to download other third party includes (SB, SB++, MA and Updater).\n- Removed Angle checks in L4D1&2 (Fix coming in 1.8).\n- Added define in lilac.sp for TF2Classic support.\n- Added support for the old version of SourceBans.\n- Added admin chat warnings for suspicious players (Aimbot, Aimlock and Bhop).\n- Removed custom warnings when compiling (some people mistook them for errors).\n- Fixed a bug where multiple aimbot checks are fired upon getting multiple kills from the same shot.\n- Updated sourcecode to compile in SM 1.11 without warnings.\n\n1.7.3\n- Removed a redundant function.\n- Added Air-Time check to Bhop detection (Default 0.3 seconds).\n- Fixed an issue where Bhop bans don't always log the amount of bhops done.\n- Updated default Bhop presets.\n- Updated Czech translations, thanks luk27official.\n\n1.7.2\n- Added experimental support for TF2Classic (Needs manual recompiling with `TF2C` defined in source).\n- Added Finnish translations, by Veeti.\n- Removed unused UTF-8 check flags.\n- Changed ConVars to use kidfearless's methodmaps version.\n- Fixed so aimbot isn't checked on dead players (Should fix grenades in CS:GO).\n\n1.7.1\n- Reorganized source code to be in separate files.\n- - The plugin itself is still a single file, making installation easier.\n- - But this should make code easier to follow.\n- Removed \"Randomized\" Backtrack patch method, as the \"Lock\" method is better.\n- Removed old Bhop detection code.\n- - Bhop detection now has pre-defined \"configs\", and a custom mode.\n- - \"lilac_bhop 1 & 2\" have been disabled, if your config already is using those modes, it will automatically swap to \"Medium\" mode.\n- Added SourceIRC support.\n- Added new ConVar \"lilac_sourceirc\" (Default 1), send logs to SourceIRC.\n- Added Database logging.\n- Added new ConVar \"lilac_database\" (Default \"\"). Send logs to MySQL or SQLite.\n- Added new Command (lilac_bhop_set), only available if \"lilac_bhop\" is set to 3 (custom mode).\n- Added compile warnings if Sourcebans++, Material-Admin or Updater includes fail.\n- - These are just warnings, not errors.\n- Added new ConVar \"lilac_ban_language\" (Default 1), which language should be used for ban reasons.\n- - 1 = Server.\n- - 2 = Client.\n- Added Counter-Strike:Source to the official supported list of games.\n- - People use it and don't report issues.\n- Added new Macro ConVar mode (\"lilac_macro 2\"), allows detecting macros without logging.\n- Added new ConVar \"lilac_macro\" (Default 0), detect macro usage.\n- Added new ConVar \"lilac_macro_warning\" (Default 1).\n- - 0 = Disabled.\n- - 1 = Warn player.\n- - 2 = Warn admins.\n- - 3 = Warn everyone.\n- Added new ConVar \"lilac_macro_method\" (Default 0).\n- - 0 = Kick.\n- - 1 = Ban (Default ban length is 15 minutes, min possible is 15, max is 60).\n- Added new ConVar \"lilac_macro_mode\" (Default 0), what types of macros to detect.\n- - 0 = All.\n- - 1 = Auto-Jump.\n- - 2 = Auto-Shoot.\n- Added new ConVar \"lilac_filter_name\" (Default 2).\n- - 0 = Disabled.\n- - 1 = Kick only.\n- - 2 = Ban cheaters with newlines in name.\n- Added new ConVar \"lilac_filter_chat\" (Default 1).\n- - Filters chat for invalid characters, also blocks Bismillah spam.\n- Fixed sm_basepath not being respected.\n- Fixed ban status message (lilac_ban_status) being spammed.\n- Fixed general code ordering to be more efficient.\n- Fixed Aimlock detection method being bloated and not running correctly.\n- Fixed map teleports causing issues for Aimbot&Aimlock detection and Backtrack patch.\n- Fixed false positive for NoLerp on servers which allow any interp ratio (sv_client_min_interp_ratio && sv_client_max_interp_ratio), thanks RoseTheFox!\n- Updated default ConVar value of \"lilac_noisemaker\" to be \"1\".\n- Updated all cheat detections to have a \"log only\" option.\n- - Negative values, example: \"lilac_angles -1\" will detect Angle-Cheats, but log only.\n- - Check ConVar descriptions for more info.\n- Updated NoLerp bans to no longer be displayed as ConVar bans.\n- - NoLerp bans have their own ban message now, need help updating all translations to reflect this.\n- Updated command \"lilac_ban_status\" to include Lilac's version number.\n- Updated outdated coding style.\n- Updated German translations, thanks freakexeuLow.\n- Updated Norwegian translations, thanks... Me...\n- Updated Spanish translations, thanks 4LEJ4NDRO.\n\n1.6.3\n- Fixed so SourceTV isn't considered a valid player.\n\n1.6.2 (Never officially released on AM)\n- Fixed rare case in TF2 where bumper carts used outside of cart areas in official halloween maps could cause false positives when stood on weird inclines.\n\n1.6.1\n- Fixed bug where angle-cheats would ban all players in L4D and L4D2.\n\n1.6.0\n - Removed redundant code.\n - Added new cheat detection feature for CS:GO (Only), Anti-Duck-Delay/FastDuck.\n - Added new BETA (May not work) TF2 cheat detection for Infinite Noisemaker Spam. Since it is in BETA, it WON'T ban, only log! If no false positives are reported, it will perma ban in the future.\n - Added BETA auto updater support.\n - Added new ConVar \"lilac_anti_duck_delay\" (Default 1), detect Anti-Duck-Delay/FastDuck in CS:GO.\n - Added new ConVar \"lilac_noisemaker\" (Default 1), detect infinite noisemaker in TF2.\n - Added new ConVar \"lilac_auto_update\" (Default 0), enable this to auto update (Requires updater plugin).\n - Added new ConVar \"lilac_max_ping_spec\" (Default 0), moves players with high ping into team spectator and warns them about potential kick after x many seconds.\n - Added Russian warning if MA wasn't included when compiled (command: lilac_ban_status).\n - Added new backtrack patch method, Lock. This patch method shouldn't affect laggy legit players much.\n - Added a delay for forwards so they won't get spammed to other plugins.\n - Fixed overly long ConVar description for \"lilac_max_lerp\".\n - Fixed typo in max ping ConVar description, Thanks 4LEJ4NDRO/ALEJANDRO!\n - Fixed a typo in code and translations files.\n - Updated Bhop to have a lower chance of false positives, thanks M4rkey and Thundy!\n - Updated Ping kicker to wait 100 seconds before kicking instead of 45 seconds.\n - Updated Ping kicker to skip testing players who have not been in game for more than 120 seconds.\n - Updated default ban length for Bhop to be 1 month instead of permanently, do \"lilac_set_ban_length bhop -1\" to use the ConVar value \"lilac_ban_length\" instead.\n - Updated Aimlock to check newly connected players for AimLock.\n - Updated command \"lilac_ban_status\" to tell you if bans will go through Sourcebans++, Material-Admin or Basebans.\n - Updated command \"lilac_ban_status\" to show if native ban functions are available.\n - Updated so ban status will be printed after all plugins are loaded along with startup message.\n - Updated how banning works through Sourcebans++/MaterialAdmin, it will now check if the native exists and not if the plugin by name is loaded.\n - Updated ConVar checker to be more basic and less CPU intensive.\n\n\n1.5.1 (Never officially released on AM)\n - Added new command \"lilac_ban_status\", which prints to server console the status of Sourcebans++ and Material-Admin.\n - Removed mat_fullbright comparison, despite it having been removed from queries.\n\n\n1.5.0\n - Added new ConVar \"lilac_aimbot_autoshoot\" (Default 1), enables autoshoot detection.\n - Added new command \"lilac_set_ban_length\", can be used to overwrite ban length for specific cheat detections.\n - Added German, Spanish, Portuguese, Turkish and Ukrainian translations.\n - Fixed false Angle-Cheat detections in L4D (Thanks finishlast).\n - Fixed false ConVar detection \"mat_fullbright\" on some community made maps. Lilac will no longer check for this ConVar.\n - Fixed some errors in Aimlock detections.\n\n\n1.4.0 (Never officially released on AM)\n - Added support for MateralAdmin (Thanks panikajo and CrazyHackGUT).\n - Added new ConVar \"lilac_ban_length\" (Default 0), sets ban length in minutes (0 = Forever).\n\n\n1.3.0\n - Fixed false Angle-Cheat detections in Left4Dead2 (Thanks larrybrains).\n - Updated ConVar \"lilac_max_lerp\" to be disabled if less than 105.\n - Updated where detection logs are stored, from \"{gamefolder}/lilac.log\" to \"{gamefolder}/addons/sourcemod/logs/lilac.log\".\n\n\n1.2.0 (Never officially released on AM)\n - Added new ConVar \"lilac_ban\" (Default 1), set to 0 to disable all banning (useful for those who want to test Lilac before fully trusting it).\n - Updated code syntax so older versions of sourcemod can compile and run Lilac.\n - Updated ConVar updates to be cleaner, thanks MAGNAT2645!\n - Updated ConVar checker to not kick unresponsive clients so quickly.\n - Updated the default config location to \"cfg/sourcemod\", if the old config file is still in the \"cfg/\" folder, the old file will still be used.\n\n\n1.1.0 (Never officially released on AM)\n - Added new forward for blocking cheat detections (Should be used by bhop/VIP plugins).\n - Fixed some false positives for Aimlock detections (Hopefully, still not sure what caused issues for others).\n - Fixed aimlock lightweight mode testing 6 players, not 5 (Typos are fun :D).\n - Updated backtrack patch to last 1 second instead of 5 (Laggy players should not get punished so harshly now).\n\n\n1.0.0\n - Rewrote large portions of the Anti-Cheat (A complete rewrite?).\n - Removed OnGameFrame check in TF2 for taunting players.\n - Added translation support, Lilac now supports French, Russian, Norwegian and English.\n - Added startup message when Little Anti-Cheat is loaded.\n - Added TF2 forward for checking when players are taunting.\n - Added new ConVar \"lilac_aimlock_light\" (Default 1), if enabled, won't check for aimlocks on all players constantly to prevent lag on some servers.\n - Added new ConVar \"lilac_welcome\" (Default 0) saying the server is protected.\n - Added new ConVar \"lilac_loss_fix\" (Default 1), if enabled, ignores some detections on laggy players (packet loss).\n - Added new ConVar \"lilac_log_misc\" (Default 0), if enabled, lilac will log when players are kicked for misc features (high ping, interp exploit and query failure).\n - Added new forward when players are banned (lilac_cheater_banned(int client, int cheat)).\n - Fixed plugin not loading in CS:GO (Thanks Bottiger).\n - Fixed extreme rare case where aimbot detector would look at the wrong victim.\n - Fixed cases where Lilac would look a little too far back at tick history.\n - Fixed so connecting players can't inherit angle history from previous players.\n - Fixed missing punctuation in NoLerp detection log message.\n - Fixed a bug where aimlock detections would not expire after 10 minutes, but aimbot detections would (Typos are fun).\n - Fixed sourcebans++ compatibility not working (Thanks foon).\n - Fixed so repeat tests (aimbot) aren't done between close players.\n - Updated interp exploit kicker to display the correct interp convar value.\n - Updated several comments and ConVar descriptions to be more clear.\n - Updated Aimlock detector to ignore players who are too close to each other.\n - Updated Aimlock detector to consider packet loss (if lilac_loss_fix is enabled).\n - Updated Aimbot detector to consider packet loss (if lilac_loss_fix is enabled, total_delta detection works regardless).\n - Updated Aimbot detector to check for things it previously wouldn't under certain circumstances.\n - Updated ConVar detector to query for ConVars every 5 seconds instead of every 2 seconds.\n - Updated Backtrack patch to last 5 seconds instead of 10.\n - Updated Backtrack patch to use correct random tick ranging from -200ms to max 200ms based on ping.\n - Updated \"lilac_log_extra\" to have an option to also log extra information on every detection, suspicions and kick.\n - Updated coding style somewhat, to make it easier to follow and understand.\n\n\n0.7.1\n - Fixed potential for false NoLerp ban if sv_maxupdaterate is updated mid-game and then plugin is loaded.\n - Changed high ping players getting kicked after 100 seconds to 45 seconds.\n - Changed Aimlock detection to increment after two snaps instead of three.\n - Changed so cheaters banned for Chat-Clear can't continue spamming chat.\n - Removed \"Full\" backtrack patch method, it was never used anyway (Old stuff from development/testing).\n - Changed backtrack patch to modify tickcount to a random value ranging from 400ms instead of 200ms.\n"
  },
  {
    "path": "README.md",
    "content": "# Closing notes\nI wish to thank everyone who participated in this project, it's been an interesting ride.\\\nAs fun as it has been, I think it's time to make it official: I quit.\n\nAs some can tell, I've become fairly inactive as of late,\\\nas fun and interesting as this project initially was to me years ago,\\\nI simply don't wish to work on it anymore. Frankly, games don't interest me anymore, I've moved on.\n\nOriginally, I was planning on handing this repo over to someone else to maintain,\\\nand handing them some of the private detection methods I developed years ago,\\\nand while I did start that process, I've come to change my mind.\n\nIt would be a decision that the users of Lilac had no say in,\\\nand given that the surface area of attack grows the more people are brought on,\\\nI decided it would just be best to leave this repo as it is.\\\nThus, I've decided to just archive this project.\n\nI've done my fair share, and fixed some wrongs.\\\nAt the end of the day, all projects come to an end at some point,\\\nand I think this project has served its purpose, at least as far as I've cared to carry it.\n\n## Going forward\nIf you are still in dire need of an anti-cheat for TF2, look to: github.com/sapphonie/StAC-tf2\n\nWhile it's not perfect (nothing is), and we have some ideological differences in how we write code - their project is good.\\\nOn the other hand, if you still wish to use Lilac, find a fork of the project that is active by people you trust.\\\nOr make one yourself, it's easier than you think.\n\nAs for the private detection methods?\\\nI wouldn't worry about it, developers are creative and will figure it out,\\\nand they'll figure new patterns out too.\n\nThanks for everything, and farewell.\n\n# Little Anti-Cheat\n\nLittle Anti-Cheat is a free and open source anti-cheat for source games, and runs on SourceMod.\\\nIt was originally developed for some secret servers I had back in the day.\\\nBut, as I quit cheating and quit having servers, I decided to release this project to help the community out.\\\nThis Anti-Cheat is by no means perfect, and it is bypassable to some extent, but it should still be helpful in dealing with cheaters :)\n\n### Current Cheat Detections:\n - Angle-Cheats (Legit Anti-Backstab (TF2), Basic Anti-Aims and Duckspeed).\n - Chat-Clear (When cheaters clear the chat).\n - Basic Invalid ConVar Detector (Checks if clients have sv_cheats turned on and such).\n - BunnyHop (Bhop).\n - Basic Projectile and Hitscan Aimbot.\n - Basic Aimlock.\n - Anti-Duck-Delay/FastDuck (CS:GO only).\n - Newlines in names.\n\n### Misc features:\n - Angle-Cheats Patch (Patches Angle-Cheats from working).\n - Max Interp Kicker (Kicks players for attempting to exploit interp (cl_interp 0.5)).\n - Max Ping Kicker (Kicks players for having too high ping (Disabled by default)).\n - Backtrack Patch (Patches backtrack cheats (Disabled by default)).\n - Macro detection.\n - Invalid name detection.\n - Invalid characters in chat patch (+ chat clear exploit fix).\n\n### Supported Games:\n - [TF2] Team Fortress 2\n - [CS:GO] Counter-Strike:Global Offensive\n - [CS:S] Counter-Strike:Source\n - [L4D2] Left 4 Dead 2\n - [L4D] Left 4 Dead\n - [DoD:S] Day of Defeat: Source\n\n### Untested, but should work in:\n - [HL2:DM] Half-Life 2:DeathMatch\n\n## FAQ\n**Q: What is Autoshoot?**\\\nA: Autoshoot is when a cheat fires a perfect 1-tick shot.\\\nIt's quite common for cheats to do this when using aimbot.\\\nAutoshoot detections work by detecting 1-tick perfect shots that lead to a kill twice in a row (Autoshoot will get logged if another aimbot type was detected tho).\n\nYou *can* get a false positive for Autoshoot, but that should be very rare.\\\nIt is possible to trigger a false positive if you use \"bind mwheeldown/up +attack\", as scroll (for some reason) does perfect 1-tick input.\\\nThat said, if someone has to go out of their way to do something stupid and abnormal to get a ban, they've basically asked for it.\\\nIf this is a problem for you, you can set `lilac_aimbot_autoshoot` to `0`.\n\nImportant thing to note about Autoshoot, because this feature shoots for you, you cannot tell if someone is using Autoshoot by spectating them, or through STV demos. Autoshoot isn't visible in demos or for spectators.\n\n**Q: What is Anti-Duck-Delay? There are so many bans for it, are they false positives?**\\\nA: Are they false positives? In short: **No.**\\\nAnti-Duck-Delay (Most commonly called **FastDuck**) is a cheat feature in CS:GO that is available in a LOT of cheats.\\\nIn fact, Anti-Duck-Delay is so commonly used by cheaters, that most bans issued by Lilac in CS:GO will be for this.\\\nAnti-Duck-Delay works by inputting a value into your usercmd buttons, that is impossible to input by legit players; only internal cheats can do this.\n\nI understand if this makes you anxious, since there are a LOT of bans for ADD, but this is completely normal.\\\nIf someone gets banned for this, they were cheating.\n\n**Q: What is NoLerp?**\\\nA: \"NoLerp\" is when cheats set their interpolation to 0ms (or lower than the minimum possible).\\\nThis is often done to increase their Aimbot accuracy.\n\n**Q: What are Angle-Cheats?**\\\nA: Angle-Cheats is when a player's view angles are set beyond the limits of the game.\\\nThis is often done to create a desync between their model and hitbox, making it harder to shoot them.\\\nIt can also be done to execute some other exploits; like in TF2 with Duckspeed.\n\nNote: Lilac currently does not check for yaw, so some desyncs are still possible and not detected.\n\n**Q: Are Macros cheats?**\\\nA: No.\\\nMacros are just when a player is using a script to input buttons for them (AutoHotKey for instance), or by using scroll to spam some input.\\\nThis is why Macro detections can only ban for 15 to 60 minutes, and no more.\\\nMacro detections are by default disabled, because most servers don't care about this.\n\n**Q: Does Lilac ban for high ping?**\\\nA: Not quite.\\\nThe optional high ping kicker (which is disabled by default) in Lilac bans players for 3 minutes, after that, they can reconnect.\\\nThe reason for this is simple, if you only kicked high ping players, they could instantly reconnect.\n\n## Non-Steam versions / CS:S v34 / CS:S v91 / ETC...\nNon-Steam versions (IE: Cracks) **ARE NOT SUPPORTED!**\\\nI am sorry to say, but non-steam versions aren't supported.\\\nThis is because of technical problems with cracks, as they tend to be of older versions of the game, which means they'll have bugs that can conflict with some cheat detections.\\\nAnd I just don't want to support piracy.\\\nI also just don't want to download sketchy unofficial cracked versions of games...\n\nSo Little Anti-Cheat may not work out of the box for cracked versions of games.\\\nThat said, I've decided to be a little helpful based on feedback from others.\n\nFor Non-Steam/Cracked version of CS:S (like v34 or v91), Angle-Cheat detections won't work.\\\nYou can fix this by updating these ConVars: `lilac_angles 0` and `lilac_angles_patch 0`.\\\nThese **HAVE** to be disabled.\n\n### Credits / Special Thanks to:\n - J_Tanzanite... Yeah I'm crediting myself for writing this AC...\n - Azalty, for being (rightly) stubborn regarding an issue and for contributing database logging.\n - foon, for fixing sourcebans++ not working (https://forums.alliedmods.net/showthread.php?p=2689297#post2689297).\n - Bottiger, for fixing this plugin not working in CS:GO and general criticisms.\n - MAGNAT2645 for suggesting a cleaner method of handling convar changes.\n - Larry/LarryBrains for informing me of false Angle-Cheat detections in L4D2.\n - VintagePC (https://github.com/vintagepc) for SourceIRC support and basepath fix.\n\n### Current languages supported:\n - Simplified Chinese (by RoyZ https://github.com/RoyZ-CSGO ^-^, and apples194)\n - Dutch (by snowy UwU OwO EwE).\n - Danish (by kS the Man / ksgoescoding c:).\n - Norwegian (by me, the translations could be better).\n - French (by Rasi / GreenGuyRasi).\n - Finnish (By [Veeti](https://forums.alliedmods.net/member.php?u=317665)).\n - English (by me lol duh hue hue hue).\n - Russian (by an awesome person c:).\n - Czech (by luk27official and someone else).\n - Brazilian Portuguese by SheepyChris (https://github.com/SheepyChris), Tiagoquix (https://github.com/Tiagoquix) and Crashzk (https://github.com/crashzk).\n - German (by two humble nice Germans c:).\n - Spanish (by ALEJANDRO ^-^).\n - Ukrainian (by panikajo ;D).\n - Polish (by https://github.com/qawery-just-sad).\n - Turkish (by ShiroNje and R3nzTheCodeGOD).\n - Hungarian (by The Solid Lad).\n - Swedish (by Teamkiller324).\n - Latvian (by rcon420).\n - Romanian (by rigE08).\n\n\nI do hope to add more languages in the future.\\\nBut at least you can add or improve on the translations already provided.\\\nMy friends who did some of the translations were told by me that the translations don't have to be perfect.\\\nJust understandable to those who don't speak English too well.\n\n### Optional:\n - Sourcebans++\n - MaterialAdmin\n - Updater\n"
  },
  {
    "path": "scripting/include/convar_class.inc",
    "content": "#if defined _convar_class_included\n #endinput\n#endif\n#define _convar_class_included\n\n// todo: track previous default values \n\nstatic ArrayList _ConvarList;\n\nenum struct convar_t\n{\n\tConVar cvar;\n\tchar description[512];\n\tchar defValue[64];\n\tchar name[255];\n\n\tbool GetMin(float& input)\n\t{\n\t\treturn this.cvar.GetBounds(ConVarBound_Lower, input);\n\t}\n\tbool GetMax(float& input)\n\t{\n\t\treturn this.cvar.GetBounds(ConVarBound_Upper, input);\n\t}\n\tvoid SetMin(bool set, float& input = 0.0)\n\t{\n\t\tthis.cvar.SetBounds(ConVarBound_Lower, set, input);\n\t}\n\tvoid SetMax(bool set, float& input = 0.0)\n\t{\n\t\tthis.cvar.SetBounds(ConVarBound_Upper, set, input);\n\t}\n}\n\nmethodmap Convar < ConVar\n{\n\tpublic Convar(const char[] name, const char[] defaultValue, const char[] description = \"\",\n\t\t\t\t\t int flags = 0, bool hasMin = false, float min = 0.0, bool hasMax = false, float max = 0.0, convar_t convar = {})\n\t{\n\t\tif(_ConvarList == null)\n\t\t{\n\t\t\t_ConvarList = new ArrayList(sizeof(convar_t));\n\t\t}\n\n\t\tConVar cvar = CreateConVar(name, defaultValue, description, flags, hasMin, min, hasMax, max);\n\n\t\tconvar_t savedValue;\n\t\tsavedValue.cvar = cvar;\n\t\tstrcopy(savedValue.description, 512, description);\n\t\tstrcopy(savedValue.defValue, 64, defaultValue);\n\t\t\n\t\t// Can't set default values :T\n\t\tsavedValue.SetMin(hasMin, min);\n\t\tsavedValue.SetMax(hasMax, max);\n\n\t\tconvar = savedValue;\n\n\t\t_ConvarList.PushArray(savedValue);\n\n\t\treturn view_as<Convar>(cvar);\n\t}\n\n\tpublic static bool CreateConfig(const char[] fileName = \"\", const char[] folder = \"sourcemod\", const bool clearWhenDone = true)\n\t{\n\t\tchar localFolder[PLATFORM_MAX_PATH];\n\t\tFormatEx(localFolder, PLATFORM_MAX_PATH, \"cfg/%s\", folder);\n\t\tif(!DirExists(localFolder))\n\t\t{\n\t\t\tif(!CreateDirectory(localFolder, 755))\n\t\t\t{\n\t\t\t\tLogError(\"Error: Failed to create folder '%s'\", localFolder);\n\t\t\t}\n\t\t}\n\n\t\tif(_ConvarList == null)\n\t\t{\n\t\t\tLogError(\"Error: No convars found. did you run .CreateConfig() before adding convars?\");\n\t\t\treturn false;\n\t\t}\n\n\n\t\t// Check if the file exists.\n\t\tchar local[PLATFORM_MAX_PATH];\n\t\tif(fileName[0] == 0)\n\t\t{\n\t\t\tchar pluginName[PLATFORM_MAX_PATH];\n\t\t\tGetPluginFilename(GetMyHandle(), pluginName, PLATFORM_MAX_PATH);\n\n\t\t\tReplaceString(pluginName, PLATFORM_MAX_PATH, \".smx\", \"\");\n\n\t\t\tint start = FindCharInString(pluginName, '/', true);\n\n\t\t\tFormatEx(local, PLATFORM_MAX_PATH, \"cfg/%s/plugin.%s.cfg\", folder, pluginName[start+1]);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFormatEx(local, sizeof(local), \"cfg/%s/%s.cfg\", folder, fileName);\n\t\t}\n\t\tbool fileExists = FileExists(local);\n\t\tif (!fileExists)\n\t\t{\n\t\t\t// Create first time file\n\t\t\tFile file = OpenFile(local, \"wt\");\n\t\t\tif (file != null)\n\t\t\t{\n\t\t\t\tfileExists = true;\n\n\t\t\t\t// get the plugin name\n\t\t\t\tchar pluginName[64];\n\t\t\t\tGetPluginFilename(GetMyHandle(), pluginName, 64);\n\n\t\t\t\t// Write warning\n\t\t\t\tfile.WriteLine(\"// This file was auto-generated by KiD's Convar Class. Only plugin convars are allowed.\");\n\t\t\t\tfile.WriteLine(\"// ConVars for plugin \\\"%s\\\"\\n\\n\", pluginName);\n\n\t\t\t\t// Loop through all of our convars\n\t\t\t\tfor (int i = 0; i < _ConvarList.Length; ++i)\n\t\t\t\t{\n\t\t\t\t\t// get the current convar and description\n\t\t\t\t\tconvar_t convar;\n\t\t\t\t\t_ConvarList.GetArray(i, convar);\n\n\t\t\t\t\t// don't write to file if flag is set\n\t\t\t\t\tif (convar.cvar.Flags & FCVAR_DONTRECORD != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// make a copy of our description\n\t\t\t\t\tchar descr[512];\n\t\t\t\t\tdescr = convar.description;\n\n\t\t\t\t\t// format newlines as comments\n\t\t\t\t\tReplaceString(descr, 512, \"\\n\", \"\\n// \");\n\n\t\t\t\t\t// write the values and bounds to the file if they exist\n\t\t\t\t\tfile.WriteLine(\"// %s\", descr);\n\n\t\t\t\t\t// get the convar name and default value to write to file\n\t\t\t\t\tchar name[64];\n\t\t\t\t\tconvar.cvar.GetName(name, 64);\n\n\t\t\t\t\tfile.WriteLine(\"// -\");\n\t\t\t\t\tfile.WriteLine(\"// Default: \\\"%s\\\"\", convar.defValue);\n\t\t\t\t\tfloat x;\n\t\t\t\t\tif (convar.GetMin(x))\n\t\t\t\t\t{\n\t\t\t\t\t\tfile.WriteLine(\"// Minimum: \\\"%02f\\\"\", x);\n\t\t\t\t\t}\n\t\t\t\t\tif (convar.GetMax(x))\n\t\t\t\t\t{\n\t\t\t\t\t\tfile.WriteLine(\"// Maximum: \\\"%02f\\\"\", x);\n\t\t\t\t\t}\n\t\t\t\t\tfile.WriteLine(\"%s \\\"%s\\\"\\n\", name, convar.defValue);\n\t\t\t\t}\n\t\t\t\t// end with newline\n\t\t\t\tfile.WriteLine(\"\");\n\t\t\t\tdelete file;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// writing failed, notify developer.\n\t\t\t\tchar pluginName[64];\n\t\t\t\tGetPluginFilename(GetMyHandle(), pluginName, 64);\n\t\t\t\tLogError(\"Failed to auto generate config for %s at '%s', make sure the directory has write permission.\", pluginName, local);\n\t\t\t\tif(clearWhenDone)\n\t\t\t\t{\n\t\t\t\t\tdelete _ConvarList;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// file already exists, just update the description and defaults\n\t\telse\n\t\t{\n\t\t\t// open the file for reading\n\t\t\tFile file = OpenFile(local, \"r\");\n\t\t\t// create a stringmap to hold our current convars.\n\t\t\tStringMap convars = new StringMap();\n\n\t\t\tchar line[512];\n\t\t\tint currentLine = 0;\n\t\t\twhile(!file.EndOfFile() && file.ReadLine(line, 512))\n\t\t\t{\n\t\t\t\t++currentLine;\n\t\t\t\t// check if the line contains a valid statement\n\t\t\t\tif(line[0] != '/' && line[0] != '\\n' && line[0] != 0)\n\t\t\t\t{\n\t\t\t\t\tchar buffers[2][512];\n\t\t\t\t\t// only convars should be in here. which should contain convar [space] value.\n\t\t\t\t\tif(ExplodeString(line, \" \", buffers, 2, 512, true) == 2)\n\t\t\t\t\t{\n\t\t\t\t\t\t// remove any trailing whitespace. should be none\n\t\t\t\t\t\tTrimString(buffers[0]);\n\t\t\t\t\t\tTrimString(buffers[1]);\n\t\t\t\t\t\t// remove the quotes from the values\n\t\t\t\t\t\tStripQuotes(buffers[0]);\n\t\t\t\t\t\tStripQuotes(buffers[1]);\n\n\t\t\t\t\t\t// since convars are only ever strings we store it as strings.\n\t\t\t\t\t\tconvars.SetString(buffers[0], buffers[1]);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// someone put something in there that shouldn't be. Yell at the dev for doing stupid stuff.\n\t\t\t\t\t\tLogError(\"Error exploding convar string: '%s' on line: %i\", line, currentLine);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// close our file\n\t\t\tdelete file;\n\t\t\t// yay duplicate code\n\t\t\t// rewrite the cfg with old convars removed and new convars added.\n\t\t\t/* Attempt to recreate it */\n\t\t\tDeleteFile(local);\n\t\t\tfile = OpenFile(local, \"wt\");\n\t\t\tif (file != null)\n\t\t\t{\n\t\t\t\tfileExists = true;\n\n\t\t\t\tchar pluginName[64];\n\t\t\t\tGetPluginFilename(GetMyHandle(), pluginName, 64);\n\n\t\t\t\tfile.WriteLine(\"// This file was auto-generated by KiD's Convar Class. Only plugin convars are allowed.\");\n\t\t\t\tfile.WriteLine(\"// ConVars for plugin \\\"%s\\\"\\n\\n\", pluginName);\n\n\t\t\t\tfloat x;\n\t\t\t\tfor (int i = 0; i < _ConvarList.Length; ++i)\n\t\t\t\t{\n\t\t\t\t\tconvar_t convar;\n\t\t\t\t\t_ConvarList.GetArray(i, convar);\n\n\t\t\t\t\tif (convar.cvar.Flags & FCVAR_DONTRECORD != 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tchar descr[512];\n\t\t\t\t\tdescr = convar.description;\n\n\t\t\t\t\tReplaceString(descr, 512, \"\\n\", \"\\n// \");\n\t\t\t\t\tfile.WriteLine(\"// %s\", descr);\n\n\t\t\t\t\tchar name[64];\n\t\t\t\t\tconvar.cvar.GetName(name, 64);\n\n\t\t\t\t\tfile.WriteLine(\"// -\");\n\t\t\t\t\tfile.WriteLine(\"// Default: \\\"%s\\\"\", convar.defValue);\n\t\t\t\t\tif (convar.GetMin(x))\n\t\t\t\t\t{\n\t\t\t\t\t\tfile.WriteLine(\"// Minimum: \\\"%02f\\\"\", x);\n\t\t\t\t\t}\n\t\t\t\t\tif (convar.GetMax(x))\n\t\t\t\t\t{\n\t\t\t\t\t\tfile.WriteLine(\"// Maximum: \\\"%02f\\\"\", x);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// only difference is that now we check for a stored value.\n\t\t\t\t\tchar storedValue[512];\n\t\t\t\t\tif(convars.GetString(name, storedValue, 512))\n\t\t\t\t\t{\n\t\t\t\t\t\tfile.WriteLine(\"%s \\\"%s\\\"\\n\", name, storedValue);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tfile.WriteLine(\"%s \\\"%s\\\"\\n\", name, convar.defValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfile.WriteLine(\"\");\n\t\t\t\tdelete file;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar pluginName[64];\n\t\t\t\tGetPluginFilename(GetMyHandle(), pluginName, 64);\n\t\t\t\tLogError(\"Failed to auto generate config for %s, make sure the directory has write permission.\", pluginName);\n\t\t\t\t\n\t\t\t\tif(clearWhenDone)\n\t\t\t\t{\n\t\t\t\t\tdelete _ConvarList;\n\t\t\t\t}\n\n\t\t\t\tdelete convars;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\n\t\t\tdelete convars;\n\t\t}\n\t\tif(fileExists)\n\t\t{\n\t\t\tServerCommand(\"exec \\\"%s\\\"\", local[4]);\n\t\t}\n\t\tif(clearWhenDone)\n\t\t{\n\t\t\tdelete _ConvarList;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static void AutoExecConfig(const char[] fileName = \"\", const char[] folder = \"sourcemod\", const bool clearWhenDone = true)\n\t{\n\t\tif(Convar.CreateConfig(fileName, folder, clearWhenDone))\n\t\t{\n\t\t\tAutoExecConfig(false, fileName, folder);\n\t\t}\n\t}\n\n\tpublic void Close()\n\t{\n\t\tdelete _ConvarList;\n\t}\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_aimbot.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nstatic int aimbot_detection[MAXPLAYERS + 1];\nstatic int aimbot_autoshoot[MAXPLAYERS + 1];\nstatic int aimbot_timertick[MAXPLAYERS + 1];\n\nvoid lilac_aimbot_reset_client(int client)\n{\n\taimbot_detection[client] = 0;\n\taimbot_autoshoot[client] = 0;\n\taimbot_timertick[client] = 0;\n}\n\nint lilac_aimbot_get_client_detections(int client)\n{\n\treturn aimbot_detection[client];\n}\n\npublic Action event_player_death(Event event, const char[] name, bool dontBroadcast)\n{\n\tint attackerid;\n\tint victimid;\n\tint client;\n\t\n\tif (!icvar[CVAR_ENABLE])\n\t\treturn Plugin_Continue;\n\t\n\tattackerid = GetEventInt(event, \"attacker\", -1);\n\tvictimid = GetEventInt(event, \"userid\", -1);\n\tclient = GetClientOfUserId(victimid);\n\t\n\tif (!is_player_valid(client))\n\t\treturn Plugin_Continue;\n\t\n\t/* This prevents running multiple aimbot checks on the same tick.\n\t * This can happen with explosives, like some projectiles.\n\t * This variable gets set in the \"shared event\" function. */\n\tif (aimbot_timertick[client] == GetGameTickCount())\n\t\treturn Plugin_Continue;\n\t\n\tevent_death_shared(attackerid,\n\t\tGetClientOfUserId(attackerid),\n\t\tclient, false);\n\t\n\treturn Plugin_Continue;\n}\n\npublic Action event_player_death_tf2(Event event, const char[] name, bool dontBroadcast)\n{\n\tchar wep[64];\n\tint userid, client, victim, killtype;\n\n\tif (!icvar[CVAR_ENABLE])\n\t\treturn Plugin_Continue;\n\n\n\tvictim = GetClientOfUserId(GetEventInt(event, \"userid\", -1));\n\n\tif (!is_player_valid(victim))\n\t\treturn Plugin_Continue;\n\t\n\t/* Same as above, prevent multiple aimbot checks on the same tick. */\n\tif (aimbot_timertick[victim] == GetGameTickCount())\n\t\treturn Plugin_Continue;\n\n\tGetEventString(event, \"weapon_logclassname\", wep, sizeof(wep), \"\");\n\n\t/* Ignore sentries & world in TF2. */\n\tif (!strncmp(wep, \"obj_\", 4, false) || !strncmp(wep, \"world\", 5, false))\n\t\treturn Plugin_Continue;\n\n\tuserid = GetEventInt(event, \"attacker\", -1);\n\tclient = GetClientOfUserId(userid);\n\tkilltype = GetEventInt(event, \"customkill\", 0);\n\n\t/* killtype 3 == flamethrower,\n\t * ignore snap detections as pyros sometimes,\n\t * sway their aim around. */\n\tevent_death_shared(userid, client, victim, ((killtype == 3) ? true : false));\n\n\treturn Plugin_Continue;\n}\n\nvoid event_death_shared(int userid, int client, int victim, bool skip_delta)\n{\n\tDataPack pack;\n\tfloat killpos[3], deathpos[3];\n\tint skip_snap = 0;\n\n\tif (client == victim\n\t\t|| !is_player_valid(client)\n\t\t/* || !is_player_valid(victim) Already checked. */\n\t\t|| IsFakeClient(client)\n\t\t|| !IsPlayerAlive(client)\n\t\t|| playerinfo_banned_flags[client][CHEAT_AIMBOT]\n\t\t|| GetClientTime(client) < 10.1)\n\t\treturn;\n\n\tif (icvar[CVAR_AIMLOCK_LIGHT])\n\t\tlilac_aimlock_light_test(client);\n\n\tif (!icvar[CVAR_AIMBOT])\n\t\treturn;\n\n\t/* Prevent multiple aimbot timer checks on the same tick. */\n\taimbot_timertick[client] = GetGameTickCount();\n\n\tGetClientEyePosition(client, killpos);\n\tGetClientEyePosition(victim, deathpos);\n\n\t/* Killer and victim are too close to each other, skip some detections. */\n\tif (GetVectorDistance(killpos, deathpos) < 350.0 || skip_delta)\n\t\tskip_snap = 1;\n\n\tCreateDataTimer(0.5, timer_check_aimbot, pack);\n\tpack.WriteCell(userid);\n\tpack.WriteCell(skip_snap);\n\tpack.WriteCell(playerinfo_index[client]); /* Fallback to this tick if the shot isn't found. */\n\tpack.WriteFloat(killpos[0]);\n\tpack.WriteFloat(killpos[1]);\n\tpack.WriteFloat(killpos[2]);\n\tpack.WriteFloat(deathpos[0]);\n\tpack.WriteFloat(deathpos[1]);\n\tpack.WriteFloat(deathpos[2]);\n}\n\npublic Action timer_check_aimbot(Handle timer, DataPack pack)\n{\n\tint ind;\n\tint client;\n\tint fallback;\n\tint shotindex = -1;\n\tint detected = 0;\n\tfloat delta = 0.0;\n\tfloat tdelta = 0.0;\n\tfloat total_delta = 0.0;\n\tfloat aimdist, laimdist;\n\tfloat ideal[3], ang[3], lang[3];\n\tfloat killpos[3], deathpos[3];\n\tbool skip_snap = false;\n\tbool skip_autoshoot = false;\n\tbool skip_repeat = false;\n\n\tpack.Reset();\n\tclient = GetClientOfUserId(pack.ReadCell());\n\tskip_snap = pack.ReadCell();\n\tfallback = pack.ReadCell();\n\tkillpos[0] = pack.ReadFloat();\n\tkillpos[1] = pack.ReadFloat();\n\tkillpos[2] = pack.ReadFloat();\n\tdeathpos[0] = pack.ReadFloat();\n\tdeathpos[1] = pack.ReadFloat();\n\tdeathpos[2] = pack.ReadFloat();\n\n\t/* Killer may have left the game, cancel. */\n\tif (!is_player_valid(client))\n\t\treturn Plugin_Continue;\n\n\t/* Locate when the shot was fired. */\n\tind = playerinfo_index[client];\n\t/* 0.5 (datapacktimer delay) + 0.5 (snap test) + 0.1 (buffer).\n\t * We are looking this far back in case of a projectile aimbot shot,\n\t * as the death event happens way later after the shot. */\n\tfor (int i = 0; i < CMD_LENGTH - time_to_ticks(0.5 + 0.5 + 0.1); i++) {\n\t\tif (--ind < 0)\n\t\t\tind += CMD_LENGTH;\n\n\t\t/* The shot needs to have happened at least 0.3 seconds ago. */\n\t\tif (GetGameTime() - playerinfo_time_usercmd[client][ind] < 0.3)\n\t\t\tcontinue;\n\n\t\tif ((playerinfo_actions[client][ind] & ACTION_SHOT)) {\n\t\t\tshotindex = ind;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Shot not found, use fallback. */\n\tif (shotindex == -1) {\n\t\tshotindex = fallback;\n\n\t\t/* If the latest index is the same as the fallback, then no\n\t\t * more usercmds have been processed since the death event.\n\t\t * These detections are thus unstable and will be ignored\n\t\t * (They require at least one tick after the shot to work). */\n\t\tif (playerinfo_index[client] == fallback) {\n\t\t\tskip_autoshoot = true;\n\t\t\tskip_repeat = true;\n\t\t}\n\t}\n\telse {\n\t\t/* Don't detect the same shot twice. */\n\t\tplayerinfo_actions[client][shotindex] = 0;\n\t}\n\n\t/* Skip repeat detections if players are too close to each other. */\n\tif (skip_snap)\n\t\tskip_repeat = true;\n\n\t/* Player taunted within 0.5 seconds\n\t * of taking a shot leading to a kill.\n\t * Ignore snap detections. */\n\tif (-0.1 < playerinfo_time_usercmd[client][shotindex] - playerinfo_time_teleported[client] < 0.5 + 0.1)\n\t\tskip_snap = true;\n\n\t/* Aimsnap and total delta test. */\n\tif (skip_snap == false) {\n\t\taim_at_point(killpos, deathpos, ideal);\n\n\t\tind = shotindex;\n\t\t/* Check angle history 0.5 seconds prior to a shot. */\n\t\tfor (int i = 0; i < time_to_ticks(0.5); i++) {\n\t\t\tif (ind < 0)\n\t\t\t\tind += CMD_LENGTH;\n\n\t\t\t/* We're looking back further than 0.5 seconds prior to the shot, abort. */\n\t\t\tif (playerinfo_time_usercmd[client][shotindex] - playerinfo_time_usercmd[client][ind] > 0.5)\n\t\t\t\tbreak;\n\n\t\t\tlaimdist = angle_delta(playerinfo_angles[client][ind], ideal);\n\t\t\tget_player_log_angles(client, ind, false, ang);\n\n\t\t\t/* Skip first iteration as we need angle deltas. */\n\t\t\tif (i) {\n\t\t\t\ttdelta = angle_delta(lang, ang);\n\n\t\t\t\t/* Store largest delta. */\n\t\t\t\tif (tdelta > delta)\n\t\t\t\t\tdelta = tdelta;\n\n\t\t\t\ttotal_delta += tdelta;\n\n\t\t\t\tif (aimdist < laimdist * 0.2 && tdelta > 10.0)\n\t\t\t\t\tdetected |= AIMBOT_FLAG_SNAP;\n\n\t\t\t\tif (aimdist < laimdist * 0.1 && tdelta > 5.0)\n\t\t\t\t\tdetected |= AIMBOT_FLAG_SNAP2;\n\t\t\t}\n\n\t\t\tlang = ang;\n\t\t\taimdist = laimdist;\n\t\t\tind--;\n\t\t}\n\t}\n\n\t/* Packetloss is too high, skip all detections but total_delta. */\n\tif (skip_due_to_loss(client)) {\n\t\tskip_autoshoot = true;\n\t\tskip_repeat = true;\n\t\tdetected = 0;\n\t}\n\n\t/* Angle-repeat test. */\n\tif (skip_repeat == false) {\n\t\tget_player_log_angles(client, shotindex - 1, false, ang);\n\t\tget_player_log_angles(client, shotindex + 1, false, lang);\n\t\ttdelta = angle_delta(ang, lang);\n\t\tget_player_log_angles(client, shotindex, false, lang);\n\n\t\tif (tdelta < 10.0 && angle_delta(ang, lang) > 0.5\n\t\t\t&& angle_delta(ang, lang) > tdelta * 5.0)\n\t\t\tdetected |= AIMBOT_FLAG_REPEAT;\n\t}\n\n\t/* Autoshoot test. */\n\tif (skip_autoshoot == false && icvar[CVAR_AIMBOT_AUTOSHOOT]) {\n\t\tint tmp = 0;\n\t\tind = shotindex + 1;\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tif (ind < 0)\n\t\t\t\tind += CMD_LENGTH;\n\t\t\telse if (ind >= CMD_LENGTH)\n\t\t\t\tind -= CMD_LENGTH;\n\n\t\t\tif ((playerinfo_buttons[client][ind] & IN_ATTACK))\n\t\t\t\ttmp++;\n\n\t\t\tind--;\n\t\t}\n\n\t\t/* Onetick perfect shot.\n\t\t * Players must get two of them in a row leading to a kill\n\t\t * or something else must have been detected to get this flag. */\n\t\tif (tmp == 1) {\n\t\t\tif (detected || ++aimbot_autoshoot[client] > 1)\n\t\t\t\tdetected |= AIMBOT_FLAG_AUTOSHOOT;\n\t\t}\n\t\telse {\n\t\t\taimbot_autoshoot[client] = 0;\n\t\t}\n\t}\n\n\tif (detected || total_delta > AIMBOT_MAX_TOTAL_DELTA)\n\t\tlilac_detected_aimbot(client, delta, total_delta, detected);\n\n\treturn Plugin_Continue;\n}\n\nstatic void lilac_detected_aimbot(int client, float delta, float td, int flags)\n{\n\tif (playerinfo_banned_flags[client][CHEAT_AIMBOT])\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_AIMBOT) == false)\n\t\treturn;\n\n\t/* Detection expires in 10 minutes. */\n\tCreateTimer(600.0, timer_decrement_aimbot, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);\n\n\tlilac_forward_client_cheat(client, CHEAT_AIMBOT);\n\n\t/* Don't log the first detection. */\n\tif (++aimbot_detection[client] < 2)\n\t\treturn;\n\n\tif (icvar[CVAR_CHEAT_WARN])\n\t\tlilac_warn_admins(client, CHEAT_AIMBOT, aimbot_detection[client]);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s is suspected of using an aimbot (Detection: %d | Delta: %.0f | TotalDelta: %.0f | Detected:%s%s%s%s%s).\",\n\t\t\tline_buffer, aimbot_detection[client], delta, td,\n\t\t\t((flags & AIMBOT_FLAG_SNAP)      ? \" Aim-Snap\"     : \"\"),\n\t\t\t((flags & AIMBOT_FLAG_SNAP2)     ? \" Aim-Snap2\"    : \"\"),\n\t\t\t((flags & AIMBOT_FLAG_AUTOSHOOT) ? \" Autoshoot\"    : \"\"),\n\t\t\t((flags & AIMBOT_FLAG_REPEAT)    ? \" Angle-Repeat\" : \"\"),\n\t\t\t((td > AIMBOT_MAX_TOTAL_DELTA)   ? \" Total-Delta\"  : \"\"));\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA] == 2)\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"aimbot\", aimbot_detection[client], float(flags), td);\n\n\tif (aimbot_detection[client] >= icvar[CVAR_AIMBOT]\n\t\t&& icvar[CVAR_AIMBOT] >= AIMBOT_BAN_MIN) {\n\n\t\tif (icvar[CVAR_LOG]) {\n\t\t\tlilac_log_setup_client(client);\n\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\"%s was banned for Aimbot.\", line_buffer);\n\n\t\t\tlilac_log(true);\n\n\t\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\t\tlilac_log_extra(client);\n\t\t}\n\t\tdatabase_log(client, \"aimbot\", DATABASE_BAN);\n\n\t\tplayerinfo_banned_flags[client][CHEAT_AIMBOT] = true;\n\t\tlilac_ban_client(client, CHEAT_AIMBOT);\n\t}\n}\n\npublic Action timer_decrement_aimbot(Handle timer, int userid)\n{\n\tint client = GetClientOfUserId(userid);\n\n\tif (!is_player_valid(client))\n\t\treturn Plugin_Continue;\n\n\tif (aimbot_detection[client] > 0)\n\t\taimbot_detection[client]--;\n\n\treturn Plugin_Continue;\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_aimlock.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nstatic bool aimlock_skip_player(int client)\n{\n\tif (!is_player_valid(client)\n\t\t|| IsFakeClient(client)\n\t\t|| !IsPlayerAlive(client)\n\t\t|| GetClientTeam(client) < 2 /* Not on a valid team. */\n\t\t|| GetGameTime() - playerinfo_time_teleported[client] < 2.0 /* Player recently teleported. */\n\t\t|| skip_due_to_loss(client)\n\t\t|| playerinfo_banned_flags[client][CHEAT_AIMLOCK]) /* Already banned/logged. */\n\t\treturn true;\n\n\t/* Lightweight mode is enabled, don't process players who aren't in que. */\n\tif (icvar[CVAR_AIMLOCK_LIGHT] == 1 && lilac_is_player_in_aimlock_que(client) == false)\n\t\treturn true;\n\n\treturn false;\n}\n\nstatic bool aimlock_skip_target(int client, int target)\n{\n\treturn (client == target\n\t\t|| !is_player_valid(target)\n\t\t|| GetClientTeam(client) == GetClientTeam(target)\n\t\t|| !IsPlayerAlive(target)\n\t\t|| GetClientTeam(target) < 2 /* Target isn't in a valid team. */\n\t\t|| GetGameTime() - playerinfo_time_teleported[target] < 2.0); /* Teleported. */\n}\n\npublic Action timer_check_aimlock(Handle timer)\n{\n\tfloat pos[3], pos2[3];\n\tint players_processed = 0;\n\tbool detected_aimlock[MAXPLAYERS + 1];\n\n\tif (!icvar[CVAR_ENABLE] || !icvar[CVAR_AIMLOCK])\n\t\treturn Plugin_Continue;\n\n\tfor (int client = 1; client <= MaxClients; client++) {\n\t\tdetected_aimlock[client] = false;\n\n\t\t/* Don't process more than 5 players.\n\t\t * Note: Don't use a \"break\" statement here!\n\t\t * We need to set detected_aimlock[...] to false\n\t\t * on every single player!\n\t\t * This will then also serve as a \"is_player_valid()\"\n\t\t * for players who need to be detected for Aimlock. */\n\t\tif (icvar[CVAR_AIMLOCK_LIGHT] == 1 && players_processed >= 5)\n\t\t\tcontinue;\n\n\t\tif (aimlock_skip_player(client))\n\t\t\tcontinue;\n\n\t\tGetClientEyePosition(client, pos);\n\n\t\tplayers_processed++;\n\n\t\tbool process = true;\n\t\tfor (int target = 1; process && target <= MaxClients; target++) {\n\t\t\tif (aimlock_skip_target(client, target))\n\t\t\t\tcontinue;\n\n\t\t\tGetClientEyePosition(target, pos2);\n\n\t\t\t/* Too close to an enemy, don't report aimlock\n\t\t\t * detections and stop processing this player. */\n\t\t\tif (GetVectorDistance(pos, pos2) < 300.0) {\n\t\t\t\tdetected_aimlock[client] = false;\n\t\t\t\tprocess = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* Player has already been detected of using aimlock,\n\t\t\t * don't check for aimlock again, only check\n\t\t\t * if the player is too close to other enemies. */\n\t\t\tif (detected_aimlock[client])\n\t\t\t\tcontinue;\n\n\t\t\tif (is_aimlocking(client, pos, pos2))\n\t\t\t\tdetected_aimlock[client] = true;\n\t\t}\n\t}\n\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tif (detected_aimlock[i])\n\t\t\tlilac_detected_aimlock(i);\n\t}\n\n\treturn Plugin_Continue;\n}\n\nstatic bool is_aimlocking(int client, float pos[3], float pos2[3])\n{\n\tfloat ideal[3], lang[3], ang[3];\n\tfloat laimdist, aimdist;\n\tint lock = 0;\n\tint ind;\n\n\taim_at_point(pos, pos2, ideal);\n\n\tind = playerinfo_index[client];\n\tfor (int i = 0; i < time_to_ticks(0.5 + 0.1); i++) {\n\t\tif (ind < 0)\n\t\t\tind += CMD_LENGTH;\n\n\t\t/* Only process aimlock time. */\n\t\tif (GetGameTime() - playerinfo_time_usercmd[client][ind] < 0.5 + 0.1) {\n\t\t\tget_player_log_angles(client, ind, false, ang);\n\t\t\tlaimdist = angle_delta(ang, ideal);\n\n\t\t\tif (i) {\n\t\t\t\tif (aimdist < 5.0)\n\t\t\t\t\tlock++;\n\t\t\t\telse\n\t\t\t\t\tlock = 0;\n\n\t\t\t\tif (aimdist < laimdist * 0.1\n\t\t\t\t\t&& angle_delta(ang, lang) > 20.0\n\t\t\t\t\t&& lock > time_to_ticks(0.1))\n\t\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tlang = ang;\n\t\t\taimdist = laimdist;\n\t\t}\n\n\t\tind--;\n\t}\n\n\treturn false;\n}\n\nstatic void lilac_detected_aimlock(int client)\n{\n\tif (playerinfo_banned_flags[client][CHEAT_AIMLOCK])\n\t\treturn;\n\n\t/* Suspicions reset after 3 minutes.\n\t * This means you need to get two aimlocks within\n\t * three minutes of each other to get a single detection. */\n\tif (GetGameTime() - playerinfo_time_aimlock[client] < 180.0)\n\t\tplayerinfo_aimlock_sus[client]++;\n\telse\n\t\tplayerinfo_aimlock_sus[client] = 1;\n\n\tplayerinfo_time_aimlock[client] = GetGameTime();\n\n\tif (playerinfo_aimlock_sus[client] < 2)\n\t\treturn;\n\n\tplayerinfo_aimlock_sus[client] = 0;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_AIMLOCK) == false)\n\t\treturn;\n\n\t/* Detection expires in 10 minutes. */\n\tCreateTimer(600.0, timer_decrement_aimlock, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);\n\n\tlilac_forward_client_cheat(client, CHEAT_AIMLOCK);\n\n\t/* Don't log the first detection. */\n\tif (++playerinfo_aimlock[client] < 2)\n\t\treturn;\n\n\tif (icvar[CVAR_CHEAT_WARN])\n\t\tlilac_warn_admins(client, CHEAT_AIMLOCK, playerinfo_aimlock[client]);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s is suspected of using an aimlock (Detection: %d).\",\n\t\t\tline_buffer, playerinfo_aimlock[client]);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA] == 2)\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"aimlock\", playerinfo_aimlock[client]);\n\n\tif (playerinfo_aimlock[client] >= icvar[CVAR_AIMLOCK]\n\t\t&& icvar[CVAR_AIMLOCK] >= AIMLOCK_BAN_MIN) {\n\t\tplayerinfo_banned_flags[client][CHEAT_AIMLOCK] = true;\n\n\t\tif (icvar[CVAR_LOG]) {\n\t\t\tlilac_log_setup_client(client);\n\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\"%s was banned for Aimlock.\", line_buffer);\n\n\t\t\tlilac_log(true);\n\n\t\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\t\tlilac_log_extra(client);\n\t\t}\n\t\tdatabase_log(client, \"aimlock\", DATABASE_BAN);\n\n\t\tlilac_ban_client(client, CHEAT_AIMLOCK);\n\t}\n}\n\nvoid lilac_aimlock_light_test(int client)\n{\n\tint ind;\n\tfloat lastang[3], ang[3];\n\n\t/* Player recently teleported, spawned or taunted. Ignore. */\n\tif (GetGameTime() - playerinfo_time_teleported[client] < 3.0)\n\t\treturn;\n\n\tind = playerinfo_index[client];\n\tfor (int i = 0; i < time_to_ticks(0.5); i++) {\n\t\tif (ind < 0)\n\t\t\tind += CMD_LENGTH;\n\n\t\tget_player_log_angles(client, ind, false, ang);\n\n\t\tif (i) {\n\t\t\t/* This player has a somewhat big delta,\n\t\t\t * test this player for aimlock for 200 seconds.\n\t\t\t * Even if we end up flagging more than 5 players\n\t\t\t * for this, that's fine as only 5 players\n\t\t\t * can be processed in the aimlock check timer. */\n\t\t\tif (angle_delta(lastang, ang) > 20.0) {\n\t\t\t\tplayerinfo_time_process_aimlock[client] = GetGameTime() + 200.0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlastang = ang;\n\t\tind--;\n\t}\n}\n\nstatic bool lilac_is_player_in_aimlock_que(int client)\n{\n\t/* Test for aimlock on players who: */\n\treturn (GetGameTime() < playerinfo_time_process_aimlock[client] /* Are in the que. */\n\t\t|| playerinfo_aimlock[client] /* Already has a detection. */\n\t\t|| lilac_aimbot_get_client_detections(client) > 1 /* Already have been detected for aimbot twice. */\n\t\t|| GetClientTime(client) < 240.0 /* Client just joined the game. */\n\t\t|| (GetGameTime() - playerinfo_time_aimlock[client] < 180.0\n\t\t\t&& playerinfo_time_aimlock[client] > 1.0)); /* Had one aimlock the past three minutes. */\n}\n\npublic Action timer_decrement_aimlock(Handle timer, int userid)\n{\n\tint client = GetClientOfUserId(userid);\n\n\tif (!is_player_valid(client))\n\t\treturn Plugin_Continue;\n\n\tif (playerinfo_aimlock[client] > 0)\n\t\tplayerinfo_aimlock[client]--;\n\n\treturn Plugin_Continue;\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_angles.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nvoid lilac_angles_check(int client, float angles[3])\n{\n\t/* Angles in L4D1&2 aren't always normalized properly. */\n\tif (ggame == GAME_L4D2 || ggame == GAME_L4D)\n\t\treturn;\n\n\tif (!IsPlayerAlive(client)\n\t\t|| playerinfo_time_teleported[client] + 5.0 > GetGameTime())\n\t\treturn;\n\n\t/* In TF2, if players use the bumpercarts outside of\n\t * official halloween map areas while standing on\n\t * weird inclines, you can trigger a false positive.\n\t * Yes... It's weird... Yes, this is rare and only happens\n\t * on community servers where they provide carts outside\n\t * of official halloween map areas...\n\t * Anyway, thanks WOLFA22 for reporting this! */\n#if !defined TF2C\n\tif (ggame == GAME_TF2) {\n\t\tif (TF2_IsPlayerInCondition(client, TFCond_HalloweenKart)) {\n\t\t\tplayerinfo_time_bumpercart[client] = GetGameTime();\n\t\t\treturn;\n\t\t}\n\t\telse if (GetGameTime() - playerinfo_time_bumpercart[client] < 5.0) {\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tif ((FloatAbs(angles[0]) > max_angles[0] && max_angles[0])\n\t\t|| (FloatAbs(angles[2]) > max_angles[2] && max_angles[2]))\n\t\tlilac_detected_angles(client, angles);\n}\n\nvoid lilac_angles_patch(float angles[3])\n{\n\t/* Patch Pitch. */\n\tif (max_angles[0] != 0.0) {\n\t\tif (angles[0] > max_angles[0])\n\t\t\tangles[0] = max_angles[0];\n\t\telse if (angles[0] < (max_angles[0] * -1.0))\n\t\t\tangles[0] = (max_angles[0] * -1.0);\n\t}\n\n\t/* Patch roll. */\n\tangles[2] = 0.0;\n}\n\nstatic void lilac_detected_angles(int client, float ang[3])\n{\n\tif (playerinfo_banned_flags[client][CHEAT_ANGLES])\n\t\treturn;\n\n\t/* Spam prevention. */\n\tif (playerinfo_time_forward[client][CHEAT_ANGLES] > GetGameTime())\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_ANGLES) == false) {\n\t\t/* Don't spam this forward again for the next 20 seconds. */\n\t\tplayerinfo_time_forward[client][CHEAT_ANGLES] = GetGameTime() + 20.0;\n\t\treturn;\n\t}\n\n\tplayerinfo_banned_flags[client][CHEAT_ANGLES] = true;\n\n\tlilac_forward_client_cheat(client, CHEAT_ANGLES);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s was detected and banned for Angle-Cheats (Pitch: %.2f, Yaw: %.2f, Roll: %.2f).\",\n\t\t\tline_buffer, ang[0], ang[1], ang[2]);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\n\t/* no need to add more data, these 3 angles are already included. */\n\tdatabase_log(client, \"angles\", DATABASE_BAN);\n\n\tlilac_ban_client(client, CHEAT_ANGLES);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_anti_duck_delay.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/* Delet dis! */\n#if defined TF2C\n\t#endinput\n#endif\n\nvoid lilac_anti_duck_delay_check(int client, const int buttons)\n{\n\tif (!(buttons & IN_BULLRUSH))\n\t\treturn;\n\n\tif (playerinfo_banned_flags[client][CHEAT_ANTI_DUCK_DELAY])\n\t\treturn;\n\n\t/* Spam prevention. */\n\tif (playerinfo_time_forward[client][CHEAT_ANTI_DUCK_DELAY] > GetGameTime())\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_ANTI_DUCK_DELAY) == false) {\n\t\t/* Don't spam this forward again for the next 10 seconds. */\n\t\tplayerinfo_time_forward[client][CHEAT_ANTI_DUCK_DELAY] = GetGameTime() + 10.0;\n\t\treturn;\n\t}\n\n\tplayerinfo_banned_flags[client][CHEAT_ANTI_DUCK_DELAY] = true;\n\n\tlilac_forward_client_cheat(client, CHEAT_ANTI_DUCK_DELAY);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer), \"%s was detected and banned for Anti-Duck-Delay.\", line_buffer);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"anti_duck_delay\", DATABASE_BAN);\n\n\tlilac_ban_client(client, CHEAT_ANTI_DUCK_DELAY);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_backtrack.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nstatic int prev_tickcount[MAXPLAYERS + 1];\nstatic int diff_tickcount[MAXPLAYERS + 1];\nstatic float time_timeout[MAXPLAYERS + 1];\n\nvoid lilac_backtrack_reset_client(int client)\n{\n\tprev_tickcount[client] = 0;\n\tdiff_tickcount[client] = 0;\n\ttime_timeout[client] = 0.0;\n}\n\nvoid lilac_backtrack_store_tickcount(int client, int tickcount)\n{\n\tstatic int tmp[MAXPLAYERS + 1];\n\n\tprev_tickcount[client] = tmp[client];\n\ttmp[client] = tickcount;\n}\n\nint lilac_backtrack_patch(int client, int tickcount)\n{\n\t/* Skip players who recently teleported. */\n\tif (playerinfo_time_teleported[client] + 2.0 > GetGameTime())\n\t\treturn tickcount;\n\n\tif (lilac_valid_tickcount(client, tickcount) == false\n\t\t&& lilac_is_player_in_backtrack_timeout(client) == false)\n\t\tlilac_set_client_in_backtrack_timeout(client);\n\n\tif (lilac_is_player_in_backtrack_timeout(client)\n\t\t&& icvar[CVAR_BACKTRACK_PATCH])\n\t\treturn lilac_lock_tickcount(client);\n\n\treturn tickcount;\n}\n\nstatic int lilac_lock_tickcount(int client)\n{\n\tint ping, tick;\n\n\tping = RoundToNearest(GetClientAvgLatency(client, NetFlow_Outgoing) / GetTickInterval());\n\ttick = diff_tickcount[client] + (GetGameTickCount() - ping);\n\n\t/* Never return higher than server tickcount.\n\t * Other than that, lock the tickcount to the player's\n\t * previous value for the durration of the patch.\n\t * This patch method shouldn't affect legit laggy players as much. */\n\treturn ((tick > GetGameTickCount()) ? GetGameTickCount() : tick);\n}\n\nstatic bool lilac_valid_tickcount(int client, int tickcount)\n{\n\treturn (intabs((prev_tickcount[client] + 1) - tickcount) <= icvar[CVAR_BACKTRACK_TOLERANCE]);\n}\n\nstatic void lilac_set_client_in_backtrack_timeout(int client)\n{\n\t/* Set the player in backtrack timeout for 1.1 seconds. */\n\ttime_timeout[client] = GetGameTime() + 1.1;\n\n\t/* Lock value. */\n\tdiff_tickcount[client] = (prev_tickcount[client] - (GetGameTickCount() - RoundToNearest(GetClientAvgLatency(client, NetFlow_Outgoing) / GetTickInterval()))) + 1;\n\n\t/* Clamp the value due to floating point errors and network variability. */\n\tif (diff_tickcount[client] > time_to_ticks(0.2) - 3)\n\t\tdiff_tickcount[client] = time_to_ticks(0.2) - 3;\n\telse if (diff_tickcount[client] < ((time_to_ticks(0.2) * -1) + 3))\n\t\tdiff_tickcount[client] = (time_to_ticks(0.2) * -1) + 3;\n}\n\nstatic bool lilac_is_player_in_backtrack_timeout(int client)\n{\n\treturn (GetGameTime() < time_timeout[client]);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_bhop.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nstatic int jump_ticks[MAXPLAYERS + 1];\nstatic int perfect_bhops[MAXPLAYERS + 1];\nstatic int next_bhop[MAXPLAYERS + 1];\nstatic int detections[MAXPLAYERS + 1];\n\n\nstatic void bhop_reset(int client)\n{\n\t/* -1 because the initial jump doesn't count. */\n\tjump_ticks[client] = -1;\n\tperfect_bhops[client] = -1;\n\tnext_bhop[client] = GetGameTickCount();\n}\n\nvoid lilac_bhop_reset_client(int client)\n{\n\tbhop_reset(client);\n\tdetections[client] = 0;\n}\n\nvoid lilac_bhop_check(int client, const int buttons, int last_buttons)\n{\n\t/* Player already banned / logged enough, no need to test anything. */\n\tif (playerinfo_banned_flags[client][CHEAT_BHOP])\n\t\treturn;\n\n\tif ((buttons & IN_JUMP))\n\t\tjump_ticks[client]++;\n\n\tint flags = GetEntityFlags(client);\n\tif ((buttons & IN_JUMP) && !(last_buttons & IN_JUMP)) {\n\t\tif ((flags & FL_ONGROUND)) {\n\t\t\tif (GetGameTickCount() > next_bhop[client]) {\n\t\t\t\tnext_bhop[client] = GetGameTickCount() + bhop_settings[BHOP_INDEX_AIR];\n\t\t\t\tperfect_bhops[client]++;\n\t\t\t\tcheck_bhop_max(client);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbhop_reset(client);\n\t\t\t}\n\t\t}\n\t}\n\telse if ((flags & FL_ONGROUND)) {\n\t\tcheck_bhop_min(client);\n\t\tbhop_reset(client);\n\t}\n}\n\nstatic void check_bhop_max(int client)\n{\n\t/* Invalid max, disable max bhop bans. */\n\tif (bhop_settings[BHOP_INDEX_MAX] < bhop_settings_min[BHOP_INDEX_MAX])\n\t\treturn;\n\n\tif (perfect_bhops[client] < bhop_settings[BHOP_INDEX_MAX])\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_BHOP) == false)\n\t\treturn;\n\n\t/* Client just hit the max threshhold, insta ban. */\n\tlilac_detected_bhop(client, true, true);\n\tlilac_ban_bhop(client);\n}\n\nstatic void check_bhop_min(int client)\n{\n\t/* Invalid min-Bhop settings. */\n\tif (bhop_settings[BHOP_INDEX_MIN] < bhop_settings_min[BHOP_INDEX_MIN])\n\t\treturn;\n\n\tif (perfect_bhops[client] < bhop_settings[BHOP_INDEX_MIN])\n\t\treturn;\n\n\t/* Jump ticks buffer is set and jump ticks is higher than max, ignore. */\n\tif (bhop_settings[BHOP_INDEX_JUMP] > -1\n\t\t&& jump_ticks[client] > bhop_settings[BHOP_INDEX_JUMP]\n\t\t+ bhop_settings[BHOP_INDEX_MIN])\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_BHOP) == false)\n\t\treturn;\n\n\tlilac_detected_bhop(client, false, false);\n}\n\nstatic void lilac_detected_bhop(int client, bool force_log, bool banning)\n{\n\tlilac_forward_client_cheat(client, CHEAT_BHOP);\n\n\t/* Detection expires in 10 minutes. */\n\tCreateTimer(600.0, timer_decrement_bhop, GetClientUserId(client));\n\n\t/* Don't log the first detection. */\n\tif (++detections[client] < 2 && force_log == false)\n\t\treturn;\n\n\tif (icvar[CVAR_CHEAT_WARN]\n\t\t&& !banning\n\t\t&& detections[client] < bhop_settings[BHOP_INDEX_TOTAL])\n\t\tlilac_warn_admins(client, CHEAT_BHOP, detections[client]);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s is suspected of using Bhop (Detection: %d | Bhops: %d | JumpTicks: %d).\",\n\t\t\tline_buffer, detections[client], perfect_bhops[client],\n\t\t\tjump_ticks[client]);\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA] == 2)\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"bhop\", detections[client], float(perfect_bhops[client]), float(jump_ticks[client]));\n\n\tif (detections[client] >= bhop_settings[BHOP_INDEX_TOTAL])\n\t\tlilac_ban_bhop(client);\n}\n\nstatic void lilac_ban_bhop(int client)\n{\n\t/* Already been banned, ignore. */\n\tif (playerinfo_banned_flags[client][CHEAT_BHOP])\n\t\treturn;\n\n\tplayerinfo_banned_flags[client][CHEAT_BHOP] = true;\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s was detected and banned for Bhop.\",\n\t\t\tline_buffer);\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"bhop\", DATABASE_BAN);\n\n\tlilac_ban_client(client, CHEAT_BHOP);\n}\n\npublic Action timer_decrement_bhop(Handle timer, int userid)\n{\n\tint client;\n\n\tclient = GetClientOfUserId(userid);\n\n\tif (!is_player_valid(client))\n\t\treturn Plugin_Continue;\n\n\tif (detections[client] > 0)\n\t\tdetections[client]--;\n\n\treturn Plugin_Continue;\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_config.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nvoid lilac_config_setup()\n{\n\tConVar tcvar;\n\n\thcvar[CVAR_ENABLE] = new Convar(\"lilac_enable\", \"1\",\n\t\t\"Enable Little Anti-Cheat.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_WELCOME] = new Convar(\"lilac_welcome\", \"0\",\n\t\t\"Welcome connecting players saying that the server is protected.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_SB] = new Convar(\"lilac_sourcebans\", \"1\",\n\t\t\"Ban players via sourcebans++ (If it isn't installed, it will default to basebans).\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_MA] = new Convar(\"lilac_materialadmin\", \"1\",\n\t\t\"Ban players via Material-Admin (Fork of Sourcebans++. If it isn't installed, will default to sourcebans++ or basebans).\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_SOURCEIRC] = new Convar(\"lilac_sourceirc\", \"1\",\n\t\t\"Enable reflecting log messages to SourceIRC channels flagged with 'lilac', if SourceIRC is available.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_LOG] = new Convar(\"lilac_log\", \"1\",\n\t\t\"Enable cheat logging.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_LOG_EXTRA] = new Convar(\"lilac_log_extra\", \"1\",\n\t\t\"0 = Disabled.\\n1 = Log extra information on player banned.\\n2 = Log extra information on everything.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 2.0);\n\thcvar[CVAR_LOG_MISC] = new Convar(\"lilac_log_misc\", \"0\",\n\t\t\"Log when players are kicked for misc features, like interp exploits, too high ping and on convar response failure.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_LOG_DATE] = new Convar(\"lilac_log_date\", \"{year}/{month}/{day} {hour}:{minute}:{second}\",\n\t\t\"Which date & time format to use when logging. Type: \\\"lilac_date_list\\\" for more info.\",\n\t\tFCVAR_PROTECTED, false, 0.0, false, 0.0);\n\thcvar[CVAR_BAN] = new Convar(\"lilac_ban\", \"1\",\n\t\t\"Enable banning of cheaters, set to 0 if you want to test Lilac before fully trusting it with bans.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_BAN_LENGTH] = new Convar(\"lilac_ban_length\", \"0\",\n\t\t\"How long bans should last in minutes (0 = forever).\",\n\t\tFCVAR_PROTECTED, true, 0.0, false, 0.0);\n\thcvar[CVAR_BAN_LANGUAGE] = new Convar(\"lilac_ban_language\", \"1\",\n\t\t\"Ban reason language.\\n0 = Use server's language.\\n1 = Use the language of the cheater.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_CHEAT_WARN] = new Convar(\"lilac_cheat_warning\", \"1\",\n\t\t\"Alert admins in chat about potential cheaters.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_ANGLES] = new Convar(\"lilac_angles\", \"1\",\n\t\t\"Detect Angle-Cheats (Basic Anti-Aim, Legit Anti-Backstab and Duckspeed).\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 1.0);\n\thcvar[CVAR_PATCH_ANGLES] = new Convar(\"lilac_angles_patch\", \"1\",\n\t\t\"Patch Angle-Cheats (Basic Anti-Aim, Legit Anti-Backstab and Duckspeed).\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_CHAT] = new Convar(\"lilac_chatclear\", \"1\",\n\t\t\"Detect Chat-Clear.\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 1.0);\n\thcvar[CVAR_CONVAR] = new Convar(\"lilac_convar\", \"1\",\n\t\t\"Detect basic invalid ConVars.\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 1.0);\n\thcvar[CVAR_NOLERP] = new Convar(\"lilac_nolerp\", \"1\",\n\t\t\"Detect NoLerp.\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 1.0);\n\thcvar[CVAR_BHOP] = new Convar(\"lilac_bhop\", \"5\",\n\t\t\"Bhop detection mode (Negative values = log-only).\\n0 = Disabled.\\n1&2 = Reserved (Invalid).\\n3 = Custom (unlocks lilac_bhop_set command).\\n4 = Low.\\n5 = Medium.\\n6 = High.\",\n\t\tFCVAR_PROTECTED, true, -6.0, true, 6.0);\n\thcvar[CVAR_AIMBOT] = new Convar(\"lilac_aimbot\", \"5\",\n\t\t\"Detect basic Aimbots.\\n0 = Disabled.\\n1 = Log only.\\n5 or more = ban on n'th detection (Minimum possible is 5)\",\n\t\tFCVAR_PROTECTED, true, 0.0, false, 0.0);\n\thcvar[CVAR_AIMBOT_AUTOSHOOT] = new Convar(\"lilac_aimbot_autoshoot\", \"1\",\n\t\t\"Detect Autoshoot.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_AIMLOCK] = new Convar(\"lilac_aimlock\", \"10\",\n\t\t\"Detect Aimlock.\\n0 = Disabled.\\n1 = Log only.\\n5 or more = ban on n'th detection (Minimum possible is 5).\",\n\t\tFCVAR_PROTECTED, true, 0.0, false, 0.0);\n\thcvar[CVAR_AIMLOCK_LIGHT] = new Convar(\"lilac_aimlock_light\", \"1\",\n\t\t\"Only process at most 5 suspicious players for aimlock.\\nDO NOT DISABLE THIS UNLESS YOUR SERVER CAN HANDLE IT!\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_ANTI_DUCK_DELAY] = new Convar(\"lilac_anti_duck_delay\", \"1\",\n\t\t\"CS:GO Only, detect Anti-Duck-Delay/FastDuck.\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 1.0);\n\thcvar[CVAR_NOISEMAKER_SPAM] = new Convar(\"lilac_noisemaker\", \"1\",\n\t\t\"TF2 Only, detect infinite noisemaker spam. STILL IN BETA, DOES NOT BAN, ONLY LOGS! MAY HAVE SOME ISSUES!\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 1.0);\n\thcvar[CVAR_BACKTRACK_PATCH] = new Convar(\"lilac_backtrack_patch\", \"0\",\n\t\t\"Patch Backtrack.\\n0 = Disabled (Recommended setting for SMAC compatibility).\\n1 = Enabled.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_BACKTRACK_TOLERANCE] = new Convar(\"lilac_backtrack_tolerance\", \"0\",\n\t\t\"How tolerant the backtrack patch will be of tickcount changes.\\n0 = No tolerance (recommended).\\n1+ = n ticks tolerant.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 3.0);\n\thcvar[CVAR_MAX_PING] = new Convar(\"lilac_max_ping\", \"0\",\n\t\t\"Ban players with too high of a ping for 3 minutes.\\nThis is meant to deal with fakelatency, the ban length is just to prevent instant reconnects.\\n0 = no ping limit, minimum possible is 100.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1000.0);\n\thcvar[CVAR_MAX_PING_SPEC] = new Convar(\"lilac_max_ping_spec\", \"0\",\n\t\t\"Move players with a high ping to spectator and warn them after this many seconds (Minimum possible is 30).\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 90.0);\n\thcvar[CVAR_MAX_LERP] = new Convar(\"lilac_max_lerp\", \"105\",\n\t\t\"Kicks players attempting to exploit interpolation, any interp higher than this value = kick.\\nMinimum value possible = 105 (Default interp in games = 100).\\n0 or less than 105 = Disabled.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 510.0); /* 500 is max possible. */\n\thcvar[CVAR_MACRO] = new Convar(\"lilac_macro\", \"0\",\n\t\t\"Detect macros.\\n-1 = Log only.\\n0 = Disabled.\\n1 = Enabled.\\n2 = Enabled, but no logging.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 2.0);\n\thcvar[CVAR_MACRO_WARNING] = new Convar(\"lilac_macro_warning\", \"1\",\n\t\t\"Warning mode for Macro detection:\\n0 = No warning.\\n1 = Warn player using macro.\\n2 = Warn admins on server.\\n3 = Warn everyone.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 3.0);\n\thcvar[CVAR_MACRO_DEAL_METHOD] = new Convar(\"lilac_macro_method\", \"0\",\n\t\t\"What to do with players detected of using macros:\\n0 = Kick.\\n1 = Ban.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_MACRO_MODE] = new Convar(\"lilac_macro_mode\", \"0\",\n\t\t\"What types of macros to detect:\\n0 = All.\\n1 = Auto-Jump.\\n2 = Auto-Shoot.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 3.0);\n\thcvar[CVAR_FILTER_NAME] = new Convar(\"lilac_filter_name\", \"2\",\n\t\t\"Filter invalid names (kicks players with invalid names).\\n-1 = Log-Only.\\n0 = Disabled.\\n1 = Enabled, kick only.\\n2 = Ban cheaters with newlines in names.\",\n\t\tFCVAR_PROTECTED, true, -1.0, true, 2.0);\n\thcvar[CVAR_FILTER_CHAT] = new Convar(\"lilac_filter_chat\", \"1\",\n\t\t\"Filter invalid characters in chat (block chat messages).\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_LOSS_FIX] = new Convar(\"lilac_loss_fix\", \"1\",\n\t\t\"Ignore some cheat detections for players who have too much packet loss (bad connection to the server).\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_AUTO_UPDATE] = new Convar(\"lilac_auto_update\", \"0\",\n\t\t\"Automatically update Little Anti-Cheat.\",\n\t\tFCVAR_PROTECTED, true, 0.0, true, 1.0);\n\thcvar[CVAR_DATABASE] = new Convar(\"lilac_database\", \"\",\n\t\t\"Database to log detections to.\\nempty = don't log to database\\ndatabase name = log to this database (MySQL & SQLite supported)\",\n\t\tFCVAR_PROTECTED);\n\n\tfor (int i = 0; i < CVAR_MAX; i++) {\n\t\tif (i != CVAR_LOG_DATE)\n\t\t\ticvar[i] = hcvar[i].IntValue;\n\n\t\thcvar[i].AddChangeHook(cvar_change);\n\t}\n\n\tif ((tcvar = FindConVar(\"sv_maxupdaterate\")) != null) {\n\t\ttcvar.AddChangeHook(cvar_change);\n\t\tlilac_lerp_maxupdaterate_changed(tcvar.IntValue);\n\t}\n\telse {\n\t\t/* Assume the value is 0 if we can't fetch it. */\n\t\tlilac_lerp_maxupdaterate_changed(0);\n\t}\n\n\t/* If the server allows clients to set the interp ratio to\n\t *     whatever they want, then false positives become possible.\n\t * Thanks RoseTheFox / Bud for reporting this :) */\n\tif ((tcvar = FindConVar(\"sv_client_min_interp_ratio\")) != null) {\n\t\ttcvar.AddChangeHook(cvar_change);\n\t\tlilac_lerp_ratio_changed(tcvar.IntValue);\n\n\t\tif ((tcvar = FindConVar(\"sv_client_max_interp_ratio\")) != null) {\n\t\t\ttcvar.AddChangeHook(cvar_change);\n\t\t\tlilac_lerp_ratio_changed(tcvar.IntValue);\n\t\t}\n\t\telse {\n\t\t\tlilac_lerp_ratio_changed(0);\n\t\t}\n\t}\n\telse {\n\t\tlilac_lerp_ratio_changed(0);\n\t}\n\n\tif ((tcvar = FindConVar(\"sv_cheats\")) != null) {\n\t\ttcvar.AddChangeHook(cvar_change);\n\t\tsv_cheats = tcvar.IntValue;\n\t}\n\telse {\n\t\tsv_cheats = 1;\n\t}\n\n\tRegServerCmd(\"lilac_date_list\", lilac_date_list, \"Lists date formatting options\", 0);\n\tRegServerCmd(\"lilac_set_ban_length\", lilac_set_ban_length, \"Sets custom ban lengths for specific cheats.\", 0);\n\tRegServerCmd(\"lilac_ban_status\", lilac_ban_status, \"Prints banning status to server console.\", 0);\n\tRegServerCmd(\"lilac_bhop_set\", lilac_bhop_set, \"Sets Custom Bhop settings\", 0);\n\n\t/* Legacy check, execute old config location.\n\t * Uses github.com/kidfearless/Auto-Exec-Config-Class */\n\tif (FileExists(\"cfg/lilac_config.cfg\", false, NULL_STRING))\n\t\tConvar.CreateConfig(\"lilac_config\", \"\");\n\telse\n\t\tConvar.CreateConfig(\"lilac_config\", \"sourcemod\");\n}\n\npublic void OnConfigsExecuted()\n{\n\tstatic bool run_status_ban = true;\n\n\tif (run_status_ban)\n\t\tlilac_ban_status(0);\n\n\t/* We need to call this here, in case of a plugin reload. */\n\tlilac_bhop_set_preset();\n\n\trun_status_ban = false;\n\n\tDatabase_OnConfigExecuted();\n}\n\npublic Action lilac_bhop_set(int args)\n{\n\tchar buffer[16];\n\tfloat fl = 0.0;\n\tint value = 0;\n\tint index = 0;\n\n\tif (intabs(icvar[CVAR_BHOP]) != BHOP_MODE_CUSTOM) {\n\t\tPrintToServer(\"Error: Must be in custom mode, set 'lilac_bhop' to %d.\",\n\t\t\tBHOP_MODE_CUSTOM);\n\t\treturn Plugin_Handled;\n\t}\n\telse if (args < 2) {\n\t\tPrintToServer(\"Error: too few arguments.\\nUsage: lilac_bhop_set <type> <value>\");\n\t\tPrintToServer(\"Examples:\");\n\t\tPrintToServer(\"\\tlilac_bhop_set min 7\");\n\t\tPrintToServer(\"\\tlilac_bhop_set ticks -1\");\n\t\tPrintToServer(\"\\tlilac_bhop_set max 15\");\n\t\tPrintToServer(\"\\tlilac_bhop_set air 0.3\");\n\t\tPrintToServer(\"\\tlilac_bhop_set total 4\");\n\t\tPrintToServer(\"\\nType list:\");\n\t\tPrintToServer(\"\\tmin   - Minimum consecutive perfect bhops to trigger a detection.\");\n\t\tPrintToServer(\"\\tticks - Jump tick buffer before min is ignored.\");\n\t\tPrintToServer(\"\\tmax   - Maximum consecutive perfect bhops to trigger an instant ban.\");\n\t\tPrintToServer(\"\\tair   - Minimum air-time between bhops in seconds.\");\n\t\tPrintToServer(\"\\ttotal - How many detections before banning.\");\n\t\tPrintToServer(\"\\n\");\n\t\tprint_current_bhop_settings();\n\n\t\treturn Plugin_Handled;\n\t}\n\n\tGetCmdArg(2, buffer, sizeof(buffer));\n\tfl = StringToFloat(buffer);\n\tGetCmdArg(1, buffer, sizeof(buffer));\n\n\t/* Working with strings is always the least fun part ): */\n\tif (StrEqual(buffer, \"min\", false)\n\t\t|| StrEqual(buffer, \"minimal\", false)\n\t\t|| StrEqual(buffer, \"minimum\", false)) {\n\t\tindex = BHOP_INDEX_MIN;\n\t}\n\telse if (StrEqual(buffer, \"jump\", false)\n\t\t|| StrEqual(buffer, \"jumps\", false)\n\t\t|| StrEqual(buffer, \"tick\", false)\n\t\t|| StrEqual(buffer, \"ticks\", false)\n\t\t|| StrEqual(buffer, \"buf\", false)\n\t\t|| StrEqual(buffer, \"buff\", false)\n\t\t|| StrEqual(buffer, \"buffer\", false)\n\t\t|| StrEqual(buffer, \"jump_tick\", false)\n\t\t|| StrEqual(buffer, \"jump_ticks\", false)\n\t\t|| StrEqual(buffer, \"jumptick\", false)\n\t\t|| StrEqual(buffer, \"jumpticks\", false)) {\n\t\tindex = BHOP_INDEX_JUMP;\n\t}\n\telse if (StrEqual(buffer, \"max\", false)\n\t\t|| StrEqual(buffer, \"maximal\", false)\n\t\t|| StrEqual(buffer, \"maximum\", false)) {\n\t\tindex = BHOP_INDEX_MAX;\n\t}\n\telse if (StrEqual(buffer, \"air\", false)\n\t\t|| StrEqual(buffer, \"air-time\", false)\n\t\t|| StrEqual(buffer, \"airtime\", false)) {\n\t\tindex = BHOP_INDEX_AIR;\n\t}\n\telse if (StrEqual(buffer, \"tot\", false)\n\t\t|| StrEqual(buffer, \"total\", false)\n\t\t|| StrEqual(buffer, \"all\", false)\n\t\t|| StrEqual(buffer, \"detection\", false)\n\t\t|| StrEqual(buffer, \"detections\", false)) {\n\t\tindex = BHOP_INDEX_TOTAL;\n\t}\n\telse {\n\t\tPrintToServer(\"Error: Unknown type \\\"%s\\\"\", buffer);\n\t\treturn Plugin_Handled;\n\t}\n\n\tif (index == BHOP_INDEX_AIR) {\n\t\tif (fl > 1.0)\n\t\t\tfl = 1.0;\n\t\tvalue = RoundToCeil(tick_rate * fl);\n\t}\n\telse {\n\t\tvalue = RoundToNearest(fl);\n\t}\n\n\tif (value < bhop_settings_min[index]) {\n\t\t/* Total setting CANNOT be set to be\n\t\t * lower than the min, no matter what! */\n\t\tif (index == BHOP_INDEX_TOTAL) {\n\t\t\tvalue = bhop_settings_min[index];\n\t\t}\n\t\telse if (value != 0) {\n\t\t\tPrintToServer(\"Warning: Minimum value is %d, use '0' to disable feature.\",\n\t\t\t\tbhop_settings_min[index]);\n\t\t\tvalue = bhop_settings_min[index];\n\t\t}\n\t}\n\n\tif (index == BHOP_INDEX_AIR)\n\t\tPrintToServer(\"[Lilac] Changed Bhop setting \\\"%s\\\" to %d ticks.\",\n\t\t\tbuffer, value);\n\telse\n\t\tPrintToServer(\"[Lilac] Changed Bhop setting \\\"%s\\\" to %d.\",\n\t\t\tbuffer, value);\n\n\tbhop_settings[index] = value;\n\tprint_current_bhop_settings();\n\n\t/* Both being 0 means we won't detect anything, lol.\n\t * So let's warn the admin :) */\n\tif (!bhop_settings[BHOP_INDEX_MIN] && !bhop_settings[BHOP_INDEX_MAX])\n\t\tPrintToServer(\"Warning: Min and Max are set to 0, bhop detection is now disabled!\");\n\n\treturn Plugin_Handled;\n}\n\nstatic void print_current_bhop_settings()\n{\n\t/* Yeah, a little messy... */\n\tPrintToServer(\"Current Bhop values:\");\n\tPrintToServer(\"    Type  : Min possible : Current\");\n\tPrintToServer(\"    Min   : %2d           : %d\", bhop_settings_min[BHOP_INDEX_MIN], bhop_settings[BHOP_INDEX_MIN]);\n\tif (bhop_settings[BHOP_INDEX_JUMP] == -1)\n\t\tPrintToServer(\"    Ticks : %2d           : %d\", bhop_settings_min[BHOP_INDEX_JUMP], bhop_settings[BHOP_INDEX_JUMP]);\n\telse\n\t\tPrintToServer(\"    Ticks : %2d           : %d (+%d = %d total)\", bhop_settings_min[BHOP_INDEX_JUMP], bhop_settings[BHOP_INDEX_JUMP], bhop_settings[BHOP_INDEX_MIN], bhop_settings[BHOP_INDEX_JUMP] + bhop_settings[BHOP_INDEX_MIN]);\n\tPrintToServer(\"    Max   : %2d           : %d\", bhop_settings_min[BHOP_INDEX_MAX], bhop_settings[BHOP_INDEX_MAX]);\n\tPrintToServer(\"    Air   : %2d           : %d\", bhop_settings_min[BHOP_INDEX_AIR], bhop_settings[BHOP_INDEX_AIR]);\n\tPrintToServer(\"    Total : %2d           : %d\", bhop_settings_min[BHOP_INDEX_TOTAL], bhop_settings[BHOP_INDEX_TOTAL]);\n}\n\npublic Action lilac_ban_status(int args)\n{\n\tint ban_type = 0;\n\tchar tmp[24];\n\n\tPrintToServer(\"====[Little Anti-Cheat %s - Ban Status]====\", PLUGIN_VERSION);\n\tPrintToServer(\"Checking ban plugins and third party plugins:\");\n\n\tPrintToServer(\"Material-Admin:\");\n\tPrintToServer(\"\\tLoaded: %s\", ((materialadmin_exist) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tNative Exists: %s\", ((NATIVE_EXISTS(\"MABanPlayer\")) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tConVar: lilac_materialadmin = %d\", icvar[CVAR_MA]);\n\n\tPrintToServer(\"Sourcebans++:\");\n\tPrintToServer(\"\\tLoaded: %s\", ((sourcebanspp_exist) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tNative Exists: %s\", ((NATIVE_EXISTS(\"SBPP_BanPlayer\")) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tConVar: lilac_sourcebans = %d\", icvar[CVAR_SB]);\n\n\tPrintToServer(\"Sourcebans (Old):\");\n\tPrintToServer(\"\\tLoaded: %s\", ((sourcebans_exist) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tNative Exists: %s\", ((NATIVE_EXISTS(\"SBBanPlayer\")) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tConVar: lilac_sourcebans = %d\", icvar[CVAR_SB]);\n\n\tPrintToServer(\"SourceIRC:\");\n\tPrintToServer(\"\\tNative Exists: %s\", ((NATIVE_EXISTS(\"IRC_MsgFlaggedChannels\")) ? \"Yes\" : \"No\"));\n\tPrintToServer(\"\\tConVar: lilac_sourceirc = %d\", icvar[CVAR_SOURCEIRC]);\n\tif (icvar[CVAR_SOURCEIRC] && NATIVE_EXISTS(\"IRC_MsgFlaggedChannels\"))\n\t\tIRC_MsgFlaggedChannels(\"lilac\", \"[LILAC] is active and logging to SourceIRC!\");\n\n\tban_type = ((icvar[CVAR_MA] && NATIVE_EXISTS(\"MABanPlayer\")) ? 3 : 0);\n\tif (!ban_type)\n\t\tban_type = ((icvar[CVAR_SB] && NATIVE_EXISTS(\"SBPP_BanPlayer\")) ? 2 : 0);\n\tif (!ban_type)\n\t\tban_type = (icvar[CVAR_SB] && NATIVE_EXISTS(\"SBBanPlayer\"));\n\n\tswitch (ban_type) {\n\tcase 0: { strcopy(tmp, sizeof(tmp), \"BaseBans\"); }\n\tcase 1: { strcopy(tmp, sizeof(tmp), \"SourceBans (Old)\"); }\n\tcase 2: { strcopy(tmp, sizeof(tmp), \"SourceBans++\"); }\n\tcase 3: { strcopy(tmp, sizeof(tmp), \"Material-Admin\"); }\n\tdefault: return Plugin_Handled;\n\t}\n\n\tPrintToServer(\"\\nBanning will go though %s.\\n\", tmp);\n\n\treturn Plugin_Handled;\n}\n\npublic Action lilac_set_ban_length(int args)\n{\n\tchar feature[32], length[32];\n\tint index = -1;\n\tint time;\n\n\tif (args < 2) {\n\t\tPrintToServer(\"Error: Too few arguments.\\nUsage: lilac_set_ban_length <cheat> <minutes>\");\n\t\tPrintToServer(\"Example:\\n\\tlilac_set_ban_length bhop 15\\n\\t(Sets bhop ban to 15 minutes)\");\n\t\tPrintToServer(\"\\nIf ban length is -1, then the ban length will be the ConVar lilac_ban_length's value.\");\n\t\tPrintToServer(\"\\nPossible cheat arguments:\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length angles <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length chatclear <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length convar <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length nolerp <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length bhop <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length aimbot <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length aimlock <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length antiduckdelay <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length noisemaker <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length macro <minutes>\");\n\t\tPrintToServer(\"\\tlilac_set_ban_length name <minutes>\\n\");\n\n\t\treturn Plugin_Handled;\n\t}\n\n\tGetCmdArg(1, feature, sizeof(feature));\n\n\tif (StrEqual(feature, \"angles\", false) || StrEqual(feature, \"angle\", false)) {\n\t\tindex = CHEAT_ANGLES;\n\t}\n\telse if (StrEqual(feature, \"chat\", false) || StrEqual(feature, \"chatclear\", false)) {\n\t\tindex = CHEAT_CHATCLEAR;\n\t}\n\telse if (StrEqual(feature, \"convar\", false) || StrEqual(feature, \"cvar\", false)) {\n\t\tindex = CHEAT_CONVAR;\n\t}\n\telse if (StrEqual(feature, \"nolerp\", false)) {\n\t\tindex = CHEAT_NOLERP;\n\t}\n\telse if (StrEqual(feature, \"bhop\", false) || StrEqual(feature, \"bunnyhop\", false)) {\n\t\tindex = CHEAT_BHOP;\n\t}\n\telse if (StrEqual(feature, \"aimbot\", false) || StrEqual(feature, \"aim\", false)) {\n\t\tindex = CHEAT_AIMBOT;\n\t}\n\telse if (StrEqual(feature, \"aimlock\", false) || StrEqual(feature, \"lock\", false)) {\n\t\tindex = CHEAT_AIMLOCK;\n\t}\n\t/* ( @~@) Bruh.... This is... B R U H */\n\telse if (StrEqual(feature, \"duck\", false) || StrEqual(feature, \"crouch\", false)\n\t\t|| StrEqual(feature, \"antiduck\", false) || StrEqual(feature, \"antiduckdelay\", false)\n\t\t|| StrEqual(feature, \"fastduck\", false)) {\n\t\tindex = CHEAT_ANTI_DUCK_DELAY;\n\t}\n\telse if (StrEqual(feature, \"noisemaker\", false) || StrEqual(feature, \"noise\", false)) {\n\t\tindex = CHEAT_NOISEMAKER_SPAM;\n\t}\n\telse if (StrEqual(feature, \"macro\", false)) {\n\t\tindex = CHEAT_MACRO;\n\t}\n\telse if (StrEqual(feature, \"name\", false) || StrEqual(feature, \"filter\", false)) {\n\t\tindex = CHEAT_NEWLINE_NAME;\n\t}\n\telse {\n\t\tPrintToServer(\"Error: Unknown cheat feature \\\"%s\\\"\", feature);\n\t\treturn Plugin_Handled;\n\t}\n\n\tGetCmdArg(2, length, sizeof(length));\n\ttime = StringToInt(length, 10);\n\n\t/* Macro exception. */\n\tif (index == CHEAT_MACRO) {\n\t\tif (time > 60)\n\t\t\ttime = 60;\n\t\telse if (time < 15)\n\t\t\ttime = 15;\n\t}\n\telse if (time < -1)\n\t\ttime = -1;\n\n\tban_length_overwrite[index] = time;\n\n\t/* Todo: Add message showing new times? Like the bhop set command :) */\n\n\treturn Plugin_Handled;\n}\n\npublic Action lilac_date_list(int args)\n{\n\tPrintToServer(\"=======[Lilac Date Formatting]=======\");\n\tPrintToServer(\"Manual formatting:\");\n\tPrintToServer(\"\\t{raw} = Skips the special formatting listed here\");\n\tPrintToServer(\"\\t        and lets you insert your own formatting\");\n\tPrintToServer(\"\\t        (see: http://www.cplusplus.com/reference/ctime/strftime/).\");\n\tPrintToServer(\"Example:\\n\\t{raw}%%Y/%%m/%%d %%H:%%M:%%S\");\n\tPrintToServer(\"Dates:\");\n\tPrintToServer(\"\\t{year}    = Numerical year  (2020).\");\n\tPrintToServer(\"\\t{month}   = Numerical month   (12).\");\n\tPrintToServer(\"\\t{day}     = Numerical day     (28).\");\n\tPrintToServer(\"Time:\");\n\tPrintToServer(\"\\t{hour}    = 24 hour format.\");\n\tPrintToServer(\"\\t{hours}   = 24 hour format.\");\n\tPrintToServer(\"\\t{24hour}  = 24 hour format.\");\n\tPrintToServer(\"\\t{24hours} = 24 hour format.\");\n\tPrintToServer(\"\\t{12hour}  = 12 hour format.\");\n\tPrintToServer(\"\\t{12hours} = 12 hour format.\");\n\tPrintToServer(\"\\t{pm}      = Insert AM/PM.\");\n\tPrintToServer(\"\\t{am}      = Insert AM/PM.\");\n\tPrintToServer(\"\\t{minute}  = Minute.\");\n\tPrintToServer(\"\\t{minutes} = Minute.\");\n\tPrintToServer(\"\\t{second}  = Second.\");\n\tPrintToServer(\"\\t{seconds} = Second.\");\n\tPrintToServer(\"Using flags example: {year}/{month}/{day} {hour}:{minute}:{second}\");\n\n\treturn Plugin_Continue;\n}\n\npublic void cvar_change(ConVar convar, const char[] oldValue, const char[] newValue)\n{\n\tchar cvarname[64];\n\tchar testdate[512];\n\n\t/* Thanks to MAGNAT2645 for informing me I could do this! */\n\tif (convar == hcvar[CVAR_ENABLE]) {\n\t\ticvar[CVAR_ENABLE] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_WELCOME]) {\n\t\ticvar[CVAR_WELCOME] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_SB]) {\n\t\ticvar[CVAR_SB] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_MA]) {\n\t\ticvar[CVAR_MA] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_SOURCEIRC]) {\n\t\ticvar[CVAR_SOURCEIRC] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_LOG]) {\n\t\ticvar[CVAR_LOG] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_LOG_EXTRA]) {\n\t\ticvar[CVAR_LOG_EXTRA] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_LOG_MISC]) {\n\t\ticvar[CVAR_LOG_MISC] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_LOG_DATE]) {\n\t\tlilac_setup_date_format(newValue);\n\t\t\n\t\tFormatTime(testdate, sizeof(testdate), dateformat, GetTime());\n\t\tPrintToServer(\"Date Format Preview: %s\", testdate);\n\t}\n\telse if (convar == hcvar[CVAR_BAN]) {\n\t\ticvar[CVAR_BAN] = StringToInt(newValue, 10);\n\n\t\tif (!icvar[CVAR_BAN])\n\t\t\tPrintToServer(\"[Little Anti-Cheat %s] WARNING: 'lilac_ban' has been set to 0, banning of cheaters has been disabled.\", PLUGIN_VERSION);\n\t}\n\telse if (convar == hcvar[CVAR_BAN_LENGTH]) {\n\t\ticvar[CVAR_BAN_LENGTH] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_BAN_LANGUAGE]) {\n\t\ticvar[CVAR_BAN_LANGUAGE] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_CHEAT_WARN]) {\n\t\ticvar[CVAR_CHEAT_WARN] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_ANGLES]) {\n\t\ticvar[CVAR_ANGLES] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_PATCH_ANGLES]) {\n\t\ticvar[CVAR_PATCH_ANGLES] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_CHAT]) {\n\t\ticvar[CVAR_CHAT] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_CONVAR]) {\n\t\ticvar[CVAR_CONVAR] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_NOLERP]) {\n\t\ticvar[CVAR_NOLERP] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_BHOP]) {\n\t\ticvar[CVAR_BHOP] = StringToInt(newValue, 10);\n\t\tlilac_bhop_set_preset();\n\t}\n\telse if (convar == hcvar[CVAR_AIMBOT]) {\n\t\ticvar[CVAR_AIMBOT] = StringToInt(newValue, 10);\n\t\t\n\t\tif (icvar[CVAR_AIMBOT] > 1 &&\n\t\t\ticvar[CVAR_AIMBOT] < AIMBOT_BAN_MIN)\n\t\t\ticvar[CVAR_AIMBOT] = 5;\n\t}\n\telse if (convar == hcvar[CVAR_AIMBOT_AUTOSHOOT]) {\n\t\ticvar[CVAR_AIMBOT_AUTOSHOOT] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_AIMLOCK]) {\n\t\ticvar[CVAR_AIMLOCK] = StringToInt(newValue, 10);\n\t\t\n\t\tif (icvar[CVAR_AIMLOCK] > 1\n\t\t\t&& icvar[CVAR_AIMLOCK] < AIMLOCK_BAN_MIN)\n\t\t\ticvar[CVAR_AIMLOCK] = 5;\n\t}\n\telse if (convar == hcvar[CVAR_AIMLOCK_LIGHT]) {\n\t\ticvar[CVAR_AIMLOCK_LIGHT] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_ANTI_DUCK_DELAY]) {\n\t\ticvar[CVAR_ANTI_DUCK_DELAY] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_NOISEMAKER_SPAM]) {\n\t\ticvar[CVAR_NOISEMAKER_SPAM] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_BACKTRACK_PATCH]) {\n\t\ticvar[CVAR_BACKTRACK_PATCH] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_BACKTRACK_TOLERANCE]) {\n\t\ticvar[CVAR_BACKTRACK_TOLERANCE] = StringToInt(newValue, 10);\n\t\t\n\t\tif (icvar[CVAR_BACKTRACK_TOLERANCE] > 2)\n\t\t\tPrintToServer(\"[Little Anti-Cheat %s] WARNING: It is not recommeded to set backtrack tolerance above 2, only do this if you understand what this means.\", PLUGIN_VERSION);\n\t}\n\telse if (convar == hcvar[CVAR_MAX_PING]) {\n\t\ticvar[CVAR_MAX_PING] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_MAX_PING_SPEC]) {\n\t\ticvar[CVAR_MAX_PING_SPEC] = StringToInt(newValue, 10);\n\t\t\n\t\tif (icvar[CVAR_MAX_PING_SPEC] < 30)\n\t\t\ticvar[CVAR_MAX_PING_SPEC] = 0;\n\t}\n\telse if (convar == hcvar[CVAR_MAX_LERP]) {\n\t\ticvar[CVAR_MAX_LERP] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_MACRO]) {\n\t\ticvar[CVAR_MACRO] = StringToInt(newValue, 10);\n\n\t\tif (icvar[CVAR_MACRO] > 0)\n\t\t\tPrintToServer(\"[Little Anti-Cheat %s] WARNING: It's recommended to use log-only method for now.\", PLUGIN_VERSION);\n\n\t\t/* Settings changed, reset counters. */\n\t\tfor (int i = 1; i <= MaxClients; i++)\n\t\t\tlilac_macro_reset_client(i);\n\t}\n\telse if (convar == hcvar[CVAR_MACRO_WARNING]) {\n\t\ticvar[CVAR_MACRO_WARNING] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_MACRO_DEAL_METHOD]) {\n\t\ticvar[CVAR_MACRO_DEAL_METHOD] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_MACRO_MODE]) {\n\t\ticvar[CVAR_MACRO_MODE] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_FILTER_NAME]) {\n\t\ticvar[CVAR_FILTER_NAME] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_FILTER_CHAT]) {\n\t\ticvar[CVAR_FILTER_CHAT] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_LOSS_FIX]) {\n\t\ticvar[CVAR_LOSS_FIX] = StringToInt(newValue, 10);\n\t}\n\telse if (convar == hcvar[CVAR_AUTO_UPDATE]) {\n\t\ticvar[CVAR_AUTO_UPDATE] = StringToInt(newValue, 10);\n\t\t\n\t\tlilac_update_url();\n\t}\n\telse if (convar == hcvar[CVAR_DATABASE]) {\n\t\tstrcopy(db_name, sizeof(db_name), newValue);\n\t}\n\telse {\n\t\tconvar.GetName(cvarname, sizeof(cvarname));\n\t\t\n\t\tif (StrEqual(cvarname, \"sv_autobunnyhopping\", false)) {\n\t\t\tforce_disable_bhop = StringToInt(newValue, 10);\n\t\t}\n\t\telse if (StrEqual(cvarname, \"sv_maxupdaterate\", false)) {\n\t\t\t/* NoLerp checks need to know this value. */\n\t\t\tlilac_lerp_maxupdaterate_changed(StringToInt(newValue));\n\n\t\t\t/* Changing this convar mid-game can cause false positives.\n\t\t\t * Ignore players already in-game. */\n\t\t\tfor (int i = 1; i <= MaxClients; i++)\n\t\t\t\tlilac_lerp_ignore_nolerp_client(i);\n\t\t}\n\t\telse if (StrEqual(cvarname, \"sv_client_min_interp_ratio\", false)) {\n\t\t\tlilac_lerp_ratio_changed(StringToInt(newValue));\n\t\t}\n\t\telse if (StrEqual(cvarname, \"sv_client_max_interp_ratio\", false)) {\n\t\t\tlilac_lerp_ratio_changed(StringToInt(newValue));\n\t\t}\n\t\telse if (StrEqual(cvarname, \"sv_cheats\", false)) {\n\t\t\tsv_cheats = StringToInt(newValue);\n\t\t\t\n\t\t\t/* Delay convar checks for 30 seconds. */\n\t\t\ttime_sv_cheats = GetTime() + QUERY_TIMEOUT;\n\t\t}\n\t}\n}\n\nstatic int bclamp(int n, int idx)\n{\n\treturn ((n < bhop_settings_min[idx]) ? bhop_settings_min[idx] : n);\n}\n\nstatic void lilac_bhop_set_preset()\n{\n\tint mode = intabs(icvar[CVAR_BHOP]);\n\n\tswitch (mode) {\n\t/* Backwards compatibility, mode 1 & 2 don't exist anymore.\n\t * If a config is already set to use these, change mode to medium. */\n\tcase BHOP_MODE_RESERVED_1, BHOP_MODE_RESERVED_2: {\n\t\tPrintToServer(\"[Lilac] Warning: Bhop mode 1 & 2 has been disabled, setting Bhop mode to %d (Medium).\",\n\t\t\tBHOP_MODE_MEDIUM);\n\t\thcvar[CVAR_BHOP].SetInt(BHOP_MODE_MEDIUM, false, false);\n\t}\n\tcase BHOP_MODE_LOW, BHOP_MODE_CUSTOM: {\n\t\t/* Can't do switch fall-through in SourcePawn.\n\t\t * Makes me miss C :( */\n\t\tif (mode == BHOP_MODE_CUSTOM) {\n\t\t\tPrintToServer(\"[Lilac] WARNING: DO NOT USE CUSTOM BHOP MODE UNLESS YOU KNOW WHAT YOU ARE DOING!\");\n\t\t\tPrintToServer(\"[Lilac] ВНИМАНИЕ: НЕ ИСПОЛЬЗУЙТЕ ПОЛЬЗОВАТЕЛЬСКИЙ РЕЖИМ BHOP, ЕСЛИ ВЫ НЕ ЗНАЕТЕ, ЧТО ВЫ ДЕЛАЕТЕ!\");\n\t\t}\n\n\t\t/* Most of these clamps aren't necessary,\n\t\t * but in-case I change the presets in the future,\n\t\t * these are nice :) */\n\t\tbhop_settings[BHOP_INDEX_MIN] = bclamp(10, BHOP_INDEX_MIN);\n\t\tbhop_settings[BHOP_INDEX_JUMP] = 5;\n\t\tbhop_settings[BHOP_INDEX_MAX] = bclamp(25, BHOP_INDEX_MAX);\n\t\tbhop_settings[BHOP_INDEX_AIR] = RoundToCeil(tick_rate * 0.3);\n\t\tbhop_settings[BHOP_INDEX_TOTAL] = bclamp(5, BHOP_INDEX_TOTAL);\n\t}\n\tcase BHOP_MODE_MEDIUM: {\n\t\tbhop_settings[BHOP_INDEX_MIN] = bclamp(7, BHOP_INDEX_MIN);\n\t\tbhop_settings[BHOP_INDEX_JUMP] = -1;\n\t\tbhop_settings[BHOP_INDEX_MAX] = bclamp(20, BHOP_INDEX_MAX);\n\t\tbhop_settings[BHOP_INDEX_AIR] = RoundToCeil(tick_rate * 0.3);\n\t\tbhop_settings[BHOP_INDEX_TOTAL] = bclamp(3, BHOP_INDEX_TOTAL);\n\t}\n\tcase BHOP_MODE_HIGH: {\n\t\tbhop_settings[BHOP_INDEX_MIN] = bclamp(5, BHOP_INDEX_MIN);\n\t\tbhop_settings[BHOP_INDEX_JUMP] = 8; /* 5 for SMAC bypass, and 3 as buffer. */\n\t\tbhop_settings[BHOP_INDEX_MAX] = bclamp(20, BHOP_INDEX_MAX);\n\t\tbhop_settings[BHOP_INDEX_AIR] = RoundToCeil(tick_rate * 0.3);\n\t\tbhop_settings[BHOP_INDEX_TOTAL] = bclamp(1, BHOP_INDEX_TOTAL);\n\t}\n\t}\n\n\tprint_current_bhop_settings();\n}\n\nstatic void lilac_setup_date_format(const char []format)\n{\n\tstrcopy(dateformat, sizeof(dateformat), format);\n\n\tif (ReplaceString(dateformat, sizeof(dateformat), \"{raw}\", \"\", false))\n\t\treturn;\n\n\tReplaceString(dateformat, sizeof(dateformat), \"%%\", \"%%%%\", false);\n\n\tReplaceString(dateformat, sizeof(dateformat), \"{year}\", \"%Y\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{month}\", \"%m\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{day}\", \"%d\", false);\n\n\tReplaceString(dateformat, sizeof(dateformat), \"{hour}\", \"%H\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{hours}\", \"%H\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{24hour}\", \"%H\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{24hours}\", \"%H\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{12hour}\", \"%I\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{12hours}\", \"%I\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{pm}\", \"%p\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{am}\", \"%p\", false);\n\n\tReplaceString(dateformat, sizeof(dateformat), \"{minute}\", \"%M\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{minutes}\", \"%M\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{second}\", \"%S\", false);\n\tReplaceString(dateformat, sizeof(dateformat), \"{seconds}\", \"%S\", false);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_convar.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n\n/* Basic query list. */\nstatic char query_list[][] = {\n\t\"sv_cheats\",\n\t\"r_drawothermodels\",\n\t\"mat_wireframe\",\n\t\"snd_show\",\n\t\"snd_visualize\",\n\t\"mat_proxy\",\n\t\"r_drawmodelstatsoverlay\",\n\t\"r_shadowwireframe\",\n\t\"r_showenvcubemap\",\n\t\"r_drawrenderboxes\",\n\t\"r_modelwireframedecal\"\n};\n\nstatic int query_index[MAXPLAYERS + 1];\nstatic int query_failed[MAXPLAYERS + 1];\n\nvoid lilac_convar_reset_client(int client)\n{\n\tquery_index[client] = 0;\n\tquery_failed[client] = 0;\n}\n\npublic Action timer_query(Handle timer)\n{\n\tif (!icvar[CVAR_ENABLE] || !icvar[CVAR_CONVAR])\n\t\treturn Plugin_Continue;\n\n\t/* sv_cheats recently changed or is set to 1, abort. */\n\tif (GetTime() < time_sv_cheats || sv_cheats)\n\t\treturn Plugin_Continue;\n\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tif (!is_player_valid(i) || IsFakeClient(i))\n\t\t\tcontinue;\n\n\t\t/* Player recently joined, wait before querying. */\n\t\tif (GetClientTime(i) < 60.0)\n\t\t\tcontinue;\n\n\t\t/* Don't query already banned players. */\n\t\tif (playerinfo_banned_flags[i][CHEAT_CONVAR])\n\t\t\tcontinue;\n\n\t\t/* Only increments query index if the player\n\t\t * has responded to the last one. */\n\t\tif (!query_failed[i]) {\n\t\t\tif (++query_index[i] >= 11)\n\t\t\t\tquery_index[i] = 0;\n\t\t}\n\n\t\tQueryClientConVar(i, query_list[query_index[i]], query_reply, 0);\n\n\t\tif (++query_failed[i] > QUERY_MAX_FAILURES) {\n\t\t\tif (icvar[CVAR_LOG_MISC]) {\n\t\t\t\tlilac_log_setup_client(i);\n\t\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\t\"%s was kicked for failing to respond to %d queries in %.0f seconds.\",\n\t\t\t\t\tline_buffer, QUERY_MAX_FAILURES,\n\t\t\t\t\tQUERY_TIMER * QUERY_MAX_FAILURES);\n\n\t\t\t\tlilac_log(true);\n\n\t\t\t\tif (icvar[CVAR_LOG_EXTRA] == 2)\n\t\t\t\t\tlilac_log_extra(i);\n\t\t\t}\n\t\t\tdatabase_log(i, \"cvar_query_failure\", DATABASE_KICK, float(QUERY_MAX_FAILURES), QUERY_TIMER * QUERY_MAX_FAILURES);\n\n\t\t\tKickClient(i, \"[Lilac] %T\", \"kick_query_failure\", i);\n\t\t}\n\t}\n\n\treturn Plugin_Continue;\n}\n\npublic void query_reply(QueryCookie cookie, int client, ConVarQueryResult result,\n\t\t\tconst char[] cvarName, const char[] cvarValue, any value)\n{\n\t/* Player NEEDS to answer the query. */\n\tif (result != ConVarQuery_Okay)\n\t\treturn;\n\n\t/* Client did respond to the query request, move on to the next convar. */\n\tquery_failed[client] = 0;\n\n\t/* Any response the server may recieve may also be faulty, ignore. */\n\tif (GetTime() < time_sv_cheats || sv_cheats)\n\t\treturn;\n\n\t/* Already banned. */\n\tif (playerinfo_banned_flags[client][CHEAT_CONVAR])\n\t\treturn;\n\n\tint val = StringToInt(cvarValue);\n\n\t/* Check for invalid convar responses.\n\t * Other than drawothermodels, a value of non-zero is invalid. */\n\tif (StrEqual(\"r_drawothermodels\", cvarName, false) && val == 1)\n\t\treturn;\n\telse if (val == 0)\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_CONVAR) == false)\n\t\treturn;\n\n\tlilac_forward_client_cheat(client, CHEAT_CONVAR);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s was detected and banned for an invalid ConVar (%s %s).\",\n\t\t\tline_buffer, cvarName, cvarValue);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"cvar_invalid\", DATABASE_BAN);\n\n\tplayerinfo_banned_flags[client][CHEAT_CONVAR] = true;\n\tlilac_ban_client(client, CHEAT_CONVAR);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_database.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2021-2023 azalty\n\tCopyright (C) 2023-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nvoid Database_OnConfigExecuted()\n{\n\tif (lil_db)\n\t\treturn;\n\t\n\tstatic bool first_load = true;\n\t\n\t/* Since db_name is only updated when the config name is CHANGED,\n\t * we need to retrieve it on the first load, since the execution\n\t * of the config file doesn't count like a change. */\n\tif (first_load) {\n\t\thcvar[CVAR_DATABASE].GetString(db_name, sizeof(db_name));\n\t\tfirst_load = false;\n\t}\n\t\n\tif (db_name[0] == '\\0' || IsCharSpace(db_name[0])) /* The config name is empty */\n\t\treturn;\n\t\n\tif (!SQL_CheckConfig(db_name)) {\n\t\tLogError(\"Database config '%s' doesn't exist in databases.cfg\", db_name);\n\t\treturn;\n\t}\n\tDatabase.Connect(OnDatabaseConnected, db_name);\n}\n\npublic void OnDatabaseConnected(Database db, const char[] error, any data)\n{\n\tif (!db) {\n\t\tLogError(\"Couldn't connect to the database. Please verify your config.\");\n\t\treturn;\n\t}\n\tlil_db = db;\n\n\tInitDatabase();\n}\n\nvoid InitDatabase()\n{\n\t/* SQLite syntax, but seems valid for MySQL too */\n\tlil_db.Query(OnDatabaseInit, \"CREATE TABLE IF NOT EXISTS lilac_detections(\"\n\t\t... \"name varchar(128) NOT NULL, \" /* Honestly, you can deal with less bytes. 32 is fine too, but since you shouldn't have a ton of detections, that should be okay. */\n\t\t... \"steamid varchar(32) NOT NULL, \"\n\t\t... \"ip varchar(16) NOT NULL, \"\n\t\t... \"cheat varchar(50) NOT NULL, \"\n\t\t... \"timestamp INTEGER NOT NULL, \"\n\t\t... \"detection INTEGER NOT NULL, \"\n\t\t... \"pos1 FLOAT NOT NULL, \"\n\t\t... \"pos2 FLOAT NOT NULL, \"\n\t\t... \"pos3 FLOAT NOT NULL, \"\n\t\t... \"ang1 FLOAT NOT NULL, \"\n\t\t... \"ang2 FLOAT NOT NULL, \"\n\t\t... \"ang3 FLOAT NOT NULL, \"\n\t\t... \"map varchar(128) NOT NULL, \"\n\t\t... \"team INTEGER NOT NULL, \"\n\t\t... \"weapon varchar(64) NOT NULL, \"\n\t\t... \"data1 FLOAT NOT NULL, \"\n\t\t... \"data2 FLOAT NOT NULL, \"\n\t\t... \"latency_inc FLOAT NOT NULL, \"\n\t\t... \"latency_out FLOAT NOT NULL, \"\n\t\t... \"loss_inc FLOAT NOT NULL, \"\n\t\t... \"loss_out FLOAT NOT NULL, \"\n\t\t... \"choke_inc FLOAT NOT NULL, \"\n\t\t... \"choke_out FLOAT NOT NULL, \"\n\t\t... \"connection_ticktime FLOAT NOT NULL, \"\n\t\t... \"game_ticktime FLOAT NOT NULL, \"\n\t\t... \"lilac_version varchar(20) NOT NULL)\");\n\t\n\t/* Sets the right charset to store player names, in case you don't know how to configure your DB (lol).\n\t * This only sets the connection charset. */\n\tlil_db.SetCharset(\"utf8mb4\");\n}\n\npublic void OnDatabaseInit(Database db, DBResultSet results, const char[] error, any data)\n{\n\tif (!results) {\n\t\tLogError(\"Database initation query failed (%s)\", error);\n\t\tdelete lil_db;\n\t}\n}\n\n/* Logs a row to the LilAC's database if it exists.\n * @param client      Client index.\n * @param cheat       Cheat name.\n * @param detection   Detection number or sanction: DATABASE_BAN, DATABASE_KICK, DATABASE_LOG_ONLY\n * @param data1       Additional data to pass as a float. You can convert integers to float.\n * @param data2       Additional data to pass as a float. You can convert integers to float. */\nvoid database_log(int client, char[] cheat, int detection=DATABASE_BAN, float data1=0.0, float data2=0.0)\n{\n\tif (!lil_db)\n\t\treturn;\n\n\tchar steamid[32], ip[16], map[128], weapon[64];\n\tfloat pos[3], ang[3];\n\n\tchar name[MAX_NAME_LENGTH];\n\tchar safe_name[(sizeof(name)*2)+1];\n\tif (!GetClientName(client, name, sizeof(name)))\n\t\tstrcopy(safe_name, sizeof(safe_name), \"<no name>\");\n\telse {\n\t\tTrimString(name);\n\t\tlil_db.Escape(name, safe_name, sizeof(safe_name));\n\t\tif (strlen(safe_name) >= 128) /* prevents exploits: don't exceed 127 characters else somes names could break the query */\n\t\t\tstrcopy(safe_name, sizeof(safe_name), \"<no name>\");\n\t}\n\n\tGetClientAuthId(client, AuthId_Steam2, steamid, sizeof(steamid), true);\n\tGetClientIP(client, ip, sizeof(ip), true);\n\n\tGetClientAbsOrigin(client, pos);\n\tGetCurrentMap(map, sizeof(map));\n\tGetClientWeapon(client, weapon, sizeof(weapon));\n\n\tget_player_log_angles(client, 0, true, ang);\n\n\tFormatEx(sql_buffer, sizeof(sql_buffer), \"INSERT INTO lilac_detections(\"\n\t\t... \"name, \"\n\t\t... \"steamid, \"\n\t\t... \"ip, \"\n\t\t... \"cheat, \"\n\t\t... \"timestamp, \"\n\t\t... \"detection, \" /* detection '0' means a ban, '-1' means a kick. Everything else is a detection number. */\n\t\t... \"pos1, \"\n\t\t... \"pos2, \"\n\t\t... \"pos3, \"\n\t\t... \"ang1, \"\n\t\t... \"ang2, \"\n\t\t... \"ang3, \"\n\t\t... \"map, \"\n\t\t... \"team, \"\n\t\t... \"weapon, \"\n\t\t... \"data1, \"\n\t\t... \"data2, \"\n\t\t... \"latency_inc, \"\n\t\t... \"latency_out, \"\n\t\t... \"loss_inc, \"\n\t\t... \"loss_out, \"\n\t\t... \"choke_inc, \"\n\t\t... \"choke_out, \"\n\t\t... \"connection_ticktime, \"\n\t\t... \"game_ticktime, \"\n\t\t... \"lilac_version) \"\n\t\t... \"VALUES(\"\n\t\t... \"'%s', \"\n\t\t... \"'%s', \"\n\t\t... \"'%s', \"\n\t\t... \"'%s', \"\n\t\t... \"'%i', \"\n\t\t... \"'%i', \"\n\t\t... \"'%.0f', \"\n\t\t... \"'%.0f', \"\n\t\t... \"'%.0f', \"\n\t\t... \"'%.5f', \"\n\t\t... \"'%.5f', \"\n\t\t... \"'%.5f', \"\n\t\t... \"'%s', \"\n\t\t... \"'%i', \"\n\t\t... \"'%s', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%f', \"\n\t\t... \"'%s')\",\n\t\tsafe_name,\n\t\tsteamid,\n\t\tip,\n\t\tcheat,\n\t\tGetTime(),\n\t\tdetection,\n\t\tpos[0],\n\t\tpos[1],\n\t\tpos[2],\n\t\tang[0],\n\t\tang[1],\n\t\tang[2],\n\t\tmap,\n\t\tGetClientTeam(client),\n\t\tweapon,\n\t\tdata1,\n\t\tdata2,\n\t\tGetClientAvgLatency(client, NetFlow_Incoming),\n\t\tGetClientAvgLatency(client, NetFlow_Outgoing),\n\t\tGetClientAvgLoss(client, NetFlow_Incoming),\n\t\tGetClientAvgLoss(client, NetFlow_Outgoing),\n\t\tGetClientAvgChoke(client, NetFlow_Incoming),\n\t\tGetClientAvgChoke(client, NetFlow_Outgoing),\n\t\tGetClientTime(client),\n\t\tGetGameTime(),\n\t\tPLUGIN_VERSION);\n\tlil_db.Query(OnDetectionInserted, sql_buffer);\n}\n\npublic void OnDetectionInserted(Database db, DBResultSet results, const char[] error, any data)\n{\n\tif (!results)\n\t\tLogError(\"Detection insertion query failed (%s)\", error);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_globals.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n#define NATIVE_EXISTS(%0)   (GetFeatureStatus(FeatureType_Native, %0) == FeatureStatus_Available)\n#define UPDATE_URL          \"https://raw.githubusercontent.com/J-Tanzanite/Little-Anti-Cheat/master/updatefile.txt\"\n\n#define CMD_LENGTH   330\n\n#define GAME_UNKNOWN   0\n#define GAME_TF2       1\n#define GAME_CSS       2\n#define GAME_CSGO      3\n#define GAME_DODS      4\n#define GAME_L4D2      5\n#define GAME_L4D       6\n\n/* In case anyone wants to change this later on in a pull request or whatever,\n * DON'T DON'T DON'T DON'T DON'T DON'T DON'T DON'T DON'T DON'T DON'T!!!\n * ...  DON'T...\n * These values cannot be changed due to forwards,\n *     changing them will cause issues for other plugins.\n * You can add new stuff, but not change the number of anything here. */\n#define CHEAT_ANGLES             0\n#define CHEAT_CHATCLEAR          1\n#define CHEAT_CONVAR             2\n#define CHEAT_NOLERP             3\n#define CHEAT_BHOP               4\n#define CHEAT_AIMBOT             5\n#define CHEAT_AIMLOCK            6\n#define CHEAT_ANTI_DUCK_DELAY    7\n#define CHEAT_NOISEMAKER_SPAM    8\n#define CHEAT_MACRO              9 /* Macros aren't actually cheats, but are forwarded as such. */\n#define CHEAT_NEWLINE_NAME      10\n#define CHEAT_MAX               11\n\n#define CVAR_ENABLE                 0\n#define CVAR_WELCOME                1\n#define CVAR_SB                     2\n#define CVAR_MA                     3\n#define CVAR_LOG                    4\n#define CVAR_LOG_EXTRA              5\n#define CVAR_LOG_MISC               6\n#define CVAR_LOG_DATE               7\n#define CVAR_BAN                    8\n#define CVAR_BAN_LENGTH             9\n#define CVAR_BAN_LANGUAGE          10\n#define CVAR_CHEAT_WARN            11\n#define CVAR_ANGLES                12\n#define CVAR_PATCH_ANGLES          13\n#define CVAR_CHAT                  14\n#define CVAR_CONVAR                15\n#define CVAR_NOLERP                16\n#define CVAR_BHOP                  17\n#define CVAR_AIMBOT                18\n#define CVAR_AIMBOT_AUTOSHOOT      19\n#define CVAR_AIMLOCK               20\n#define CVAR_AIMLOCK_LIGHT         21\n#define CVAR_ANTI_DUCK_DELAY       22\n#define CVAR_NOISEMAKER_SPAM       23\n#define CVAR_BACKTRACK_PATCH       24\n#define CVAR_BACKTRACK_TOLERANCE   25\n#define CVAR_MAX_PING              26\n#define CVAR_MAX_PING_SPEC         27\n#define CVAR_MAX_LERP              28\n#define CVAR_MACRO                 29\n#define CVAR_MACRO_WARNING         30\n#define CVAR_MACRO_DEAL_METHOD     31\n#define CVAR_MACRO_MODE            32\n#define CVAR_FILTER_NAME           33\n#define CVAR_FILTER_CHAT           34\n#define CVAR_LOSS_FIX              35\n#define CVAR_AUTO_UPDATE           36\n#define CVAR_SOURCEIRC             37\n#define CVAR_DATABASE              38\n#define CVAR_MAX                   39\n\n#define BHOP_INDEX_MIN     0\n#define BHOP_INDEX_JUMP    1\n#define BHOP_INDEX_MAX     2\n#define BHOP_INDEX_TOTAL   3\n#define BHOP_INDEX_AIR     4\n#define BHOP_MAX           5\n\n#define BHOP_MODE_DISABLED     0\n#define BHOP_MODE_RESERVED_1   1\n#define BHOP_MODE_RESERVED_2   2\n#define BHOP_MODE_CUSTOM       3\n#define BHOP_MODE_LOW          4\n#define BHOP_MODE_MEDIUM       5\n#define BHOP_MODE_HIGH         6\n\n#define NOISEMAKER_TYPE_NONE        0\n#define NOISEMAKER_TYPE_LIMITED     1\n#define NOISEMAKER_TYPE_UNLIMITED   2\n\n#define MACRO_LOG_LENGTH   200\n\n#define MACRO_AUTOJUMP    0\n#define MACRO_AUTOSHOOT   1\n#define MACRO_ARRAY       2\n\n#define ACTION_SHOT   1\n\n#define QUERY_MAX_FAILURES   24\n#define QUERY_TIMEOUT        30\n#define QUERY_TIMER          5.0\n\n#define AIMLOCK_BAN_MIN   5\n\n#define AIMBOT_BAN_MIN           5\n#define AIMBOT_MAX_TOTAL_DELTA   (180.0 * 2.5)\n#define AIMBOT_FLAG_REPEAT       (1 << 0)\n#define AIMBOT_FLAG_AUTOSHOOT    (1 << 1)\n#define AIMBOT_FLAG_SNAP         (1 << 2)\n#define AIMBOT_FLAG_SNAP2        (1 << 3)\n\n#define STRFLAG_NEWLINE          (1 << 0) /* Carriage return or Newline. */\n#define STRFLAG_WIDE_CHAR_SPAM   (1 << 1) /* Lots of wide character spam. */\n\n#define DATABASE_BAN 0\n#define DATABASE_KICK -1\n#define DATABASE_LOG_ONLY -2\n\n#define PLUGIN_NAME      \"[Lilac] Little Anti-Cheat\"\n#define PLUGIN_AUTHOR    \"J_Tanzanite\"\n#define PLUGIN_DESC      \"An opensource Anti-Cheat\"\n#define PLUGIN_VERSION   \"1.7.4\"\n#define PLUGIN_URL       \"https://github.com/J-Tanzanite/Little-Anti-Cheat\"\n\n/* Convars. */\nConvar hcvar[CVAR_MAX]; /* ConVar = built in SourceMod  |  Convar = kidfearless's convar_class */\nint icvar[CVAR_MAX];\nint sv_cheats = 0;\nint time_sv_cheats = 0;\nint force_disable_bhop = 0;\n\n/* Banlength overwrite. */\nint ban_length_overwrite[CHEAT_MAX];\n\n/* Database. */\nDatabase lil_db;\nchar sql_buffer[1500]; /* It's probably bigger than what you need, but better be safe than sorry I guess. */\nchar db_name[64]; /* Database config name from hcvar[CVAR_DATABASE]. */\n\n/* Misc. */\nint ggame;\nint tick_rate;\nint macro_max;\nint bhop_settings[BHOP_MAX];\nint bhop_settings_min[BHOP_MAX];\n\nchar line_buffer[2048];\nchar dateformat[512] = \"%Y/%m/%d %H:%M:%S\";\nchar log_file[PLATFORM_MAX_PATH];\nfloat max_angles[3] = {89.01, 0.0, 50.01};\nHandle forwardhandle = INVALID_HANDLE;\nHandle forwardhandleban = INVALID_HANDLE;\nHandle forwardhandleallow = INVALID_HANDLE;\n\n/* External plugins. */\nbool sourcebans_exist = false;\nbool sourcebanspp_exist = false;\nbool materialadmin_exist = false;\n\n/* Logging.\n * Todo: Might wanna move a lot of this variables to\n * their own files if they are only used there.\n * Just so the code gets a lot cleaner. */\nint playerinfo_index[MAXPLAYERS + 1];\nint playerinfo_buttons[MAXPLAYERS + 1][CMD_LENGTH];\nint playerinfo_actions[MAXPLAYERS + 1][CMD_LENGTH];\nint playerinfo_aimlock_sus[MAXPLAYERS + 1];\nint playerinfo_aimlock[MAXPLAYERS + 1];\nfloat playerinfo_time_bumpercart[MAXPLAYERS + 1];\nfloat playerinfo_time_teleported[MAXPLAYERS + 1];\nfloat playerinfo_time_aimlock[MAXPLAYERS + 1];\nfloat playerinfo_time_process_aimlock[MAXPLAYERS + 1];\nfloat playerinfo_angles[MAXPLAYERS + 1][CMD_LENGTH][3];\nfloat playerinfo_time_usercmd[MAXPLAYERS + 1][CMD_LENGTH];\nfloat playerinfo_time_forward[MAXPLAYERS + 1][CHEAT_MAX];\nbool playerinfo_banned_flags[MAXPLAYERS + 1][CHEAT_MAX];\n\n\n/* Forward declarations so we don't need third-party include files. */\n\n#define MA_BAN_STEAM  1\n\nnative Function IRC_MsgFlaggedChannels(const char[] flag, const char[] format, any:...);\nnative Function MABanPlayer(int iClient, int iTarget, int iType, int iTime, char[] sReason);\nnative Function SBBanPlayer(int client, int target, int time, const char[] reason);\nnative Function SBPP_BanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason);\nnative Function Updater_AddPlugin(const char[] url);\nnative Function Updater_RemovePlugin();"
  },
  {
    "path": "scripting/lilac/lilac_lerp.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nstatic float min_lerp_possible = 0.0;\nstatic bool ignore_nolerp[MAXPLAYERS + 1];\nstatic bool ignore_nolerp_all = false;\n\nvoid lilac_lerp_reset_client(int client)\n{\n\tignore_nolerp[client] = false;\n}\n\nvoid lilac_lerp_ignore_nolerp_client(int client)\n{\n\tignore_nolerp[client] = true;\n}\n\nvoid lilac_lerp_ratio_changed(int value)\n{\n\t/* Permamently disable nolerp checks.\n\t * Yes, this is a little harsh, but if servers allow\n\t * any interp ratio, they are unlikely to change\n\t * it back to any restrictive value. */\n\tif (value < 1)\n\t\tignore_nolerp_all = true;\n}\n\nvoid lilac_lerp_maxupdaterate_changed(int value)\n{\n\tif (ignore_nolerp_all)\n\t\treturn;\n\n\tmin_lerp_possible = ((value > 0) ? 1.0 / float(value) : 0.0);\n}\n\npublic Action timer_check_lerp(Handle timer)\n{\n\tif (!icvar[CVAR_ENABLE])\n\t\treturn Plugin_Continue;\n\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tif (!is_player_valid(i) || IsFakeClient(i))\n\t\t\tcontinue;\n\n\t\tfloat lerp = GetEntPropFloat(i, Prop_Data, \"m_fLerpTime\");\n\n\t\tif (lerp * 1000.0 > float(icvar[CVAR_MAX_LERP]) && icvar[CVAR_MAX_LERP] >= 105) {\n\t\t\tdetected_lerp_exploit(i, lerp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!icvar[CVAR_NOLERP]\n\t\t\t|| ignore_nolerp_all\n\t\t\t|| ignore_nolerp[i]\n\t\t\t|| playerinfo_banned_flags[i][CHEAT_NOLERP]\n\t\t\t|| min_lerp_possible < 0.005) /* Minvalue invalid or too low. */\n\t\t\tcontinue;\n\n\t\tif (lerp > min_lerp_possible * 0.95 /* buffer */)\n\t\t\tcontinue;\n\n\t\tdetected_nolerp(i, lerp);\n\t}\n\n\treturn Plugin_Continue;\n}\n\nstatic void detected_lerp_exploit(int client, float lerp)\n{\n\tif (icvar[CVAR_LOG_MISC]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s was kicked for exploiting interpolation (%.3fms / %dms max).\",\n\t\t\tline_buffer, lerp * 1000.0, icvar[CVAR_MAX_LERP]);\n\n\t\tlilac_log(true);\n\t\tif (icvar[CVAR_LOG_EXTRA] == 2)\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"lerp_exploit\", DATABASE_KICK, lerp * 1000.0, float(icvar[CVAR_MAX_LERP]));\n\n\tKickClient(client, \"[Lilac] %T\", \"kick_interp_exploit\", client,\n\t\tlerp * 1000.0, icvar[CVAR_MAX_LERP],\n\t\tfloat(icvar[CVAR_MAX_LERP]) / 999.9);\n}\n\nstatic void detected_nolerp(int client, float lerp)\n{\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_NOLERP) == false)\n\t\treturn;\n\n\tplayerinfo_banned_flags[client][CHEAT_NOLERP] = true;\n\n\tlilac_forward_client_cheat(client, CHEAT_NOLERP);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer), \"%s was detected and banned for NoLerp (%fms).\",\n\t\t\tline_buffer, lerp * 1000.0);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"nolerp\", DATABASE_BAN, lerp * 1000.0);\n\n\tlilac_ban_client(client, CHEAT_NOLERP);\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_macro.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nstatic int macro_history[MAXPLAYERS + 1][MACRO_ARRAY][MACRO_LOG_LENGTH];\nstatic int macro_detected[MAXPLAYERS + 1][MACRO_ARRAY];\n\nvoid lilac_macro_reset_client(int client)\n{\n\tfor (int i = 0; i < MACRO_ARRAY; i++) {\n\t\tmacro_detected[client][i] = 0;\n\t\tlilac_macro_reset_client_history(client, i);\n\t}\n}\n\nstatic void lilac_macro_reset_client_history(int client, int type)\n{\n\tif (type < 0 || type >= MACRO_ARRAY)\n\t\treturn;\n\n\tfor (int i = 0; i < MACRO_LOG_LENGTH; i++)\n\t\tmacro_history[client][type][i] = false;\n}\n\nvoid lilac_macro_check(int client, const int buttons, int last_buttons)\n{\n\tstatic int index[MAXPLAYERS + 1];\n\n\tif (++index[client] >= tick_rate)\n\t\tindex[client] = 0;\n\n\tfor (int i = 0; i < MACRO_ARRAY; i++) {\n\t\t/* Skip macros we aren't checking for. */\n\t\tif (!process_macro_type(i))\n\t\t\tcontinue;\n\n\t\tint input = get_macro_input(i);\n\n\t\t/* Player pressed the key. */\n\t\tbool key = (!(last_buttons & input) && (buttons & input)) ? true : false;\n\t\tmacro_history[client][i][index[client]] = key;\n\n\t\t/* Only check for spam when the key is pressed. */\n\t\tif (!key)\n\t\t\tcontinue;\n\n\t\tint acc = 0;\n\n\t\tfor (int k = 0; k < tick_rate; k++) {\n\t\t\tif (macro_history[client][i][k])\n\t\t\t\tacc++;\n\t\t}\n\n\t\tif (acc >= macro_max)\n\t\t\tlilac_detected_macro(client, i);\n\t}\n}\n\nstatic bool process_macro_type(int macro_type)\n{\n\t/* Invalid macro type. */\n\tif (macro_type >= MACRO_ARRAY || macro_type < 0)\n\t\treturn false;\n\n\t/* 0 == Test for all types. */\n\tif (!icvar[CVAR_MACRO_MODE])\n\t\treturn true;\n\n\t/* Check bit. */\n\treturn (icvar[CVAR_MACRO_MODE] & (1 << macro_type)) ? true : false;\n}\n\nstatic int get_macro_input(int macro_type)\n{\n\tswitch (macro_type) {\n\tcase MACRO_AUTOJUMP: return IN_JUMP;\n\tcase MACRO_AUTOSHOOT: return IN_ATTACK;\n\tdefault: return 0;\n\t}\n}\n\nstatic void lilac_detected_macro(int client, int type)\n{\n\tchar string[20];\n\n\t/* Clear history, prevents overlap. */\n\tlilac_macro_reset_client_history(client, type);\n\n\t/* Already been logged once, ignore. */\n\tif (playerinfo_banned_flags[client][CHEAT_MACRO])\n\t\treturn;\n\n\t/* Spam prevention. */\n\tif (playerinfo_time_forward[client][CHEAT_MACRO] > GetGameTime())\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_MACRO) == false) {\n\t\tplayerinfo_time_forward[client][CHEAT_MACRO] = GetGameTime() + 5.0;\n\t\treturn;\n\t}\n\n\tswitch (type) {\n\tcase MACRO_AUTOJUMP: { strcopy(string, sizeof(string), \"Auto-Jump\"); }\n\tcase MACRO_AUTOSHOOT: { strcopy(string, sizeof(string), \"Auto-Shoot\"); }\n\tdefault: { return; } /* Invalid type. */\n\t}\n\n\tlilac_forward_client_cheat(client, CHEAT_MACRO);\n\n\t/* Ignore the first detection. */\n\tif (++macro_detected[client][type] < 2)\n\t\treturn;\n\n\t/* Log (2 == detect, but no logging). */\n\tif (icvar[CVAR_LOG] && icvar[CVAR_MACRO] < 2) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\"%s was detected of using Macro %s (Detection: %d | Max presses: %d).\",\n\t\t\tline_buffer, string, macro_detected[client][type], macro_max);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\n\tint char_index;\n\twhile (string[char_index]) {\n\t\tstring[char_index] = CharToLower(string[char_index]); /* lower each character */\n\t\tchar_index++;\n\t}\n\tFormat(string, sizeof(string), \"macro_%s\", string);\n\tdatabase_log(client, string, macro_detected[client][type], float(macro_max));\n\n\t/* If we are using log-only, then don't warn, there's no point. */\n\tif (icvar[CVAR_MACRO] > -1) {\n\t\t/* Warnings. */\n\t\tswitch (icvar[CVAR_MACRO_WARNING]) {\n\t\tcase 1: {\n\t\t\tPrintCenterText(client, \"[Little Anti-Cheat] Warning: Macro usage isn't allowed!\");\n\t\t\tPrintToChat(client, \"[Little Anti-Cheat] Warning: Macro usage isn't allowed!\");\n\t\t}\n\t\tcase 2: {\n\t\t\tfor (int i = 1; macro_detected[client][type] == 2 && i <= MaxClients; i++) {\n\t\t\t\tif (!is_player_valid(i) || IsFakeClient(i))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tif (!is_player_admin(i))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tPrintToChat(i, \"[Little Anti-Cheat] %N was detected of using Macro %s.\",\n\t\t\t\t\tclient, string);\n\t\t\t}\n\t\t}\n\t\tcase 3: {\n\t\t\t/* Warn everyone once... */\n\t\t\tif (macro_detected[client][type] == 2)\n\t\t\t\tPrintToChatAll(\"[Little Anti-Cheat] %N was detected of using Macro %s.\",\n\t\t\t\t\tclient, string);\n\t\t}\n\t\t}\n\t}\n\n\tif (macro_detected[client][type] < 5)\n\t\treturn;\n\n\tplayerinfo_banned_flags[client][CHEAT_MACRO] = true;\n\n\tif (icvar[CVAR_MACRO] == -1)\n\t\treturn;\n\n\tif (icvar[CVAR_MACRO_DEAL_METHOD] == 0)\n\t\tKickClient(client, \"[Lilac] %T\", \"kick_macro\", client, string);\n\telse\n\t\tlilac_ban_client(client, CHEAT_MACRO);\n}\n\n/* Macro detections decrement every 5 minutes.\n * Todo: Might wanna make it more frequent? */\npublic Action timer_decrement_macro(Handle timer)\n{\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tfor (int k = 0; k < MACRO_ARRAY; k++) {\n\t\t\tif (macro_detected[i][k] > 0)\n\t\t\t\tmacro_detected[i][k]--;\n\t\t}\n\t}\n\n\treturn Plugin_Continue;\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_noisemaker.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/* Delet dis! */\n#if defined TF2C\n\t#endinput\n#endif\n\nstatic int noisemaker_type[MAXPLAYERS + 1];\nstatic int noisemaker_entity[MAXPLAYERS + 1];\nstatic int noisemaker_entity_prev[MAXPLAYERS + 1];\nstatic int noisemaker_detection[MAXPLAYERS + 1];\n\nvoid lilac_noisemaker_reset_client(int client)\n{\n\tnoisemaker_type[client] = 0;\n\tnoisemaker_entity[client] = 0;\n\tnoisemaker_entity_prev[client] = 0;\n\tnoisemaker_detection[client] = 0;\n}\n\npublic Action event_inventoryupdate(Event event, const char[] name, bool dontBroadcast)\n{\n\tint client;\n\n\tclient = GetClientOfUserId(GetEventInt(event, \"userid\", 0));\n\tcheck_inventory_for_noisemaker(client);\n\n\treturn Plugin_Continue;\n}\n\nvoid check_inventory_for_noisemaker(int client)\n{\n\tchar classname[32];\n\tint type;\n\n\tif (!is_player_valid(client))\n\t\treturn;\n\n\tnoisemaker_type[client] = NOISEMAKER_TYPE_NONE;\n\tnoisemaker_entity_prev[client] = noisemaker_entity[client];\n\tnoisemaker_entity[client] = 0;\n\n\tfor (int i = MaxClients + 1; i < GetEntityCount(); i++) {\n\t\tif (!IsValidEdict(i))\n\t\t\tcontinue;\n\n\t\tif (GetEntPropEnt(i, Prop_Data, \"m_hOwnerEntity\") != client)\n\t\t\tcontinue;\n\n\t\tGetEntityClassname(i, classname, sizeof(classname));\n\n\t\tif (!StrEqual(classname, \"tf_wearable\", false))\n\t\t\tcontinue;\n\n\t\ttype = get_entity_noisemaker_type(GetEntProp(i, Prop_Send, \"m_iItemDefinitionIndex\"));\n\n\t\tif (type) {\n\t\t\tnoisemaker_type[client] = type;\n\t\t\tnoisemaker_entity[client] = i;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic int get_entity_noisemaker_type(int itemindex)\n{\n\tswitch (itemindex) {\n\tcase 280: return NOISEMAKER_TYPE_LIMITED; /* Black cat. */\n\tcase 281: return NOISEMAKER_TYPE_LIMITED; /* Gremlin. */\n\tcase 282: return NOISEMAKER_TYPE_LIMITED; /* Werewolf. */\n\tcase 283: return NOISEMAKER_TYPE_LIMITED; /* Witch. */\n\tcase 284: return NOISEMAKER_TYPE_LIMITED; /* Banshee. */\n\tcase 286: return NOISEMAKER_TYPE_LIMITED; /* Crazy Laugh. */\n\tcase 288: return NOISEMAKER_TYPE_LIMITED; /* Stabby. */\n\tcase 362: return NOISEMAKER_TYPE_LIMITED; /* Bell. */\n\tcase 364: return NOISEMAKER_TYPE_LIMITED; /* Gong. */\n\tcase 365: return NOISEMAKER_TYPE_LIMITED; /* Koto. */\n\tcase 493: return NOISEMAKER_TYPE_LIMITED; /* Fireworks. */\n\tcase 542: return NOISEMAKER_TYPE_LIMITED; /* Vuvuzela. */\n\n\tcase 536: return NOISEMAKER_TYPE_UNLIMITED; /* Birthday. */\n\tcase 673: return NOISEMAKER_TYPE_UNLIMITED; /* Winter 2011. */\n\t}\n\n\treturn NOISEMAKER_TYPE_NONE;\n}\n\npublic Action OnClientCommandKeyValues(int client, KeyValues kv)\n{\n\tchar command[64];\n\tKvGetSectionName(kv, command, sizeof(command));\n\n\tif (ggame != GAME_TF2)\n\t\treturn Plugin_Continue;\n\n\tif (!icvar[CVAR_ENABLE] || !icvar[CVAR_NOISEMAKER_SPAM])\n\t\treturn Plugin_Continue;\n\n\tif (noisemaker_type[client] != NOISEMAKER_TYPE_LIMITED)\n\t\treturn Plugin_Continue;\n\n\tif (noisemaker_entity_prev[client] != noisemaker_entity[client]) {\n\t\tnoisemaker_entity_prev[client] = noisemaker_entity[client];\n\t\tnoisemaker_detection[client] = 0;\n\t}\n\n\tif (StrEqual(command, \"+use_action_slot_item_server\", false)\n\t\t|| StrEqual(command, \"-use_action_slot_item_server\", false)) {\n\n\t\t/* Since this reacts to both + and -,\n\t\t * and the maximum is 25 uses per noisemaker,\n\t\t * detect the double of that + a buffer of 10. */\n\t\tif (++noisemaker_detection[client] > 60)\n\t\t\tlilac_detected_noisemaker(client);\n\t}\n\n\treturn Plugin_Continue;\n}\n\nstatic void lilac_detected_noisemaker(int client)\n{\n\tif (playerinfo_banned_flags[client][CHEAT_NOISEMAKER_SPAM])\n\t\treturn;\n\n\tif (lilac_forward_allow_cheat_detection(client, CHEAT_NOISEMAKER_SPAM) == false)\n\t\treturn;\n\n\tplayerinfo_banned_flags[client][CHEAT_NOISEMAKER_SPAM] = true;\n\n\tlilac_forward_client_cheat(client, CHEAT_NOISEMAKER_SPAM);\n\n\tif (icvar[CVAR_LOG]) {\n\t\tlilac_log_setup_client(client);\n\t\tFormat(line_buffer, sizeof(line_buffer), \"%s is suspected of using unlimited noisemaker cheats.\", line_buffer);\n\n\t\tlilac_log(true);\n\n\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\tlilac_log_extra(client);\n\t}\n\tdatabase_log(client, \"noisemaker\", DATABASE_LOG_ONLY);\n\n\t/* Enable this later if no false positives are reported. */\n\t/* lilac_ban_client(client, CHEAT_NOISEMAKER_SPAM); */\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_ping.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n\nstatic int ping_high[MAXPLAYERS + 1];\nstatic int ping_warn[MAXPLAYERS + 1];\n\nvoid lilac_ping_reset_client(int client)\n{\n\tping_high[client] = 0;\n\tping_warn[client] = 0;\n}\n\npublic Action timer_check_ping(Handle timer)\n{\n\tstatic bool toggle = true;\n\tchar reason[128];\n\tfloat ping;\n\n\tif (!icvar[CVAR_ENABLE] || icvar[CVAR_MAX_PING] < 100)\n\t\treturn Plugin_Continue;\n\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tif (!is_player_valid(i) || IsFakeClient(i))\n\t\t\tcontinue;\n\n\t\t/* Player recently joined, don't check ping yet. */\n\t\tif (GetClientTime(i) < 120.0)\n\t\t\tcontinue;\n\n\t\tping = GetClientAvgLatency(i, NetFlow_Outgoing) * 1000.0;\n\n\t\tif (ping < float(icvar[CVAR_MAX_PING])) {\n\t\t\tif (toggle && ping_high[i] > 0)\n\t\t\t\tping_high[i]--;\n\n\t\t\tif (ping_high[i] < ping_warn[i] - 2 && ping_warn[i] > 0) {\n\n\t\t\t\tping_warn[i] = 0;\n\t\t\t\tPrintToChat(i, \"[Lilac] Your ping appears to be fine again, it is safe to rejoin a team and play.\");\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (++ping_high[i] >= icvar[CVAR_MAX_PING_SPEC] / 5\n\t\t\t&& icvar[CVAR_MAX_PING_SPEC] >= 30) {\n\t\t\tChangeClientTeam(i, 1); /* Move this player to spectators. */\n\n\t\t\tping_warn[i] = ping_high[i];\n\n\t\t\tPrintToChat(i, \"[Lilac] WARNING: You will be kicked in %d seconds if your ping stays too high! (%.0f / %d max)\",\n\t\t\t\t100 - (ping_high[i] * 5),\n\t\t\t\tping, icvar[CVAR_MAX_PING]);\n\t\t}\n\n\t\t/* Player has a higher ping than maximum for 100 seconds. */\n\t\tif (ping_high[i] < 20)\n\t\t\tcontinue;\n\n\t\tif (icvar[CVAR_LOG_MISC]) {\n\t\t\tlilac_log_setup_client(i);\n\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\"%s was kicked for having too high ping (%.3fms / %dms max).\",\n\t\t\t\tline_buffer, ping, icvar[CVAR_MAX_PING]);\n\n\t\t\tlilac_log(true);\n\n\t\t\tif (icvar[CVAR_LOG_EXTRA] == 2)\n\t\t\t\tlilac_log_extra(i);\n\t\t}\n\t\tdatabase_log(i, \"high_ping\", DATABASE_KICK);\n\n\t\tFormat(reason, sizeof(reason), \"[Lilac] %T\", \"tban_ping_high\", i,\n\t\t\tping, icvar[CVAR_MAX_PING]);\n\n\t\t/* Ban the client for three minutes to avoid instant reconnects. */\n\t\tBanClient(i, 3, BANFLAG_AUTHID, reason, reason, \"lilac\", 0);\n\t}\n\n\ttoggle = !toggle;\n\n\treturn Plugin_Continue;\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_stock.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\nvoid lilac_warn_admins(int client, int cheat, int detections)\n{\n\tchar name[MAX_NAME_LENGTH];\n\tchar type[16];\n\tint admins[MAXPLAYERS + 1];\n\tint n = 0;\n\t\n\t/* We'll assume the player is valid, as this function\n\t * should never be called on invalid clients in the first place. */\n\t/* if (!is_player_valid(client))\n\t\treturn; */\n\t\n\t/* Setup a list of admins. */\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tif (!is_player_valid(i))\n\t\t\tcontinue;\n\t\t\n\t\tif (IsFakeClient(i))\n\t\t\tcontinue;\n\t\t\n\t\tif (is_player_admin(i))\n\t\t\tadmins[n++] = i;\n\t}\n\t\n\t/* No admins are on. */\n\tif (!n)\n\t\treturn;\n\t\n\tswitch (cheat) {\n\tcase CHEAT_BHOP: { strcopy(type, sizeof(type), \"Bhop\"); }\n\tcase CHEAT_AIMBOT: { strcopy(type, sizeof(type), \"Aimbot\"); }\n\tcase CHEAT_AIMLOCK: { strcopy(type, sizeof(type), \"Aimlock\"); }\n\t/* Macros have their own warning system. */\n\tdefault: { return; }\n\t}\n\t\n\tif (!GetClientName(client, name, sizeof(name)))\n\t\tstrcopy(name, sizeof(name), \"[NAME_ERROR]\");\n\t\n\tfor (int i = 0; i < n; i++)\n\t\tPrintToChat(admins[i],\n\t\t\t\"[Lilac] %T\", \"admin_chat_warning_generic\",\n\t\t\tadmins[i], name, type, detections);\n}\n\n/* Useless Todo: I should update this soon... But I won't :P */\nbool bullettime_can_shoot(int client)\n{\n\tint weapon;\n\n\tif (!IsPlayerAlive(client))\n\t\treturn false;\n\n\tweapon = GetEntPropEnt(client, Prop_Data, \"m_hActiveWeapon\");\n\n\tif (!IsValidEntity(weapon))\n\t\treturn false;\n\n\tif (GetEntPropFloat(client, Prop_Data, \"m_flSimulationTime\") + GetTickInterval()\n\t\t>= GetEntPropFloat(weapon, Prop_Data, \"m_flNextPrimaryAttack\"))\n\t\treturn true;\n\n\treturn false;\n}\n\nvoid lilac_reset_client(int client)\n{\n\tlilac_backtrack_reset_client(client);\n\tlilac_bhop_reset_client(client);\n\tlilac_macro_reset_client(client);\n#if !defined TF2C\n\t/* Noise maker file is empty if compiled for TF2Classic. */\n\tlilac_noisemaker_reset_client(client);\n#endif\n\tlilac_aimbot_reset_client(client);\n\tlilac_ping_reset_client(client);\n\tlilac_convar_reset_client(client);\n\tlilac_lerp_reset_client(client);\n\n\tplayerinfo_index[client] = 0;\n\tplayerinfo_aimlock_sus[client] = 0;\n\tplayerinfo_aimlock[client] = 0;\n\tplayerinfo_time_bumpercart[client] = 0.0;\n\tplayerinfo_time_teleported[client] = 0.0;\n\tplayerinfo_time_aimlock[client] = 0.0;\n\tplayerinfo_time_process_aimlock[client] = 0.0;\n\n\tfor (int i = 0; i < CHEAT_MAX; i++) {\n\t\tplayerinfo_time_forward[client][i] = 0.0;\n\t\tplayerinfo_banned_flags[client][i] = false;\n\t}\n\n\tfor (int i = 0; i < CMD_LENGTH; i++) {\n\t\tplayerinfo_buttons[client][i] = 0;\n\t\tplayerinfo_actions[client][i] = 0;\n\t\tplayerinfo_time_usercmd[client][i] = 0.0;\n\n\t\tset_player_log_angles(client, view_as<float>({0.0, 0.0, 0.0}), i);\n\t}\n}\n\nvoid lilac_log_setup_client(int client)\n{\n\tchar date[512], steamid[64], ip[64];\n\n\tFormatTime(date, sizeof(date), dateformat, GetTime());\n\n\tGetClientAuthId(client, AuthId_Steam2, steamid, sizeof(steamid), true);\n\tGetClientIP(client, ip, sizeof(ip), true);\n\n\tFormatEx(line_buffer, sizeof(line_buffer),\n\t\t\"%s [Version %s] {Name: \\\"%N\\\" | SteamID: %s | IP: %s}\",\n\t\tdate, PLUGIN_VERSION, client, steamid, ip);\n}\n\nvoid lilac_log_extra(int client)\n{\n\tchar map[128], weapon[64];\n\tfloat pos[3], ang[3];\n\n\tGetClientAbsOrigin(client, pos);\n\tGetCurrentMap(map, sizeof(map));\n\tGetClientWeapon(client, weapon, sizeof(weapon));\n\n\tget_player_log_angles(client, 0, true, ang);\n\n\tFormatEx(line_buffer, sizeof(line_buffer),\n\t\t\"\\tPos={%.0f,%.0f,%.0f}, Angles={%.5f,%.5f,%.5f}, Map=\\\"%s\\\", Team={%d}, Weapon=\\\"%s\\\", Latency={Inc:%f,Out:%f}, Loss={Inc:%f,Out:%f}, Choke={Inc:%f,Out:%f}, ConnectionTime={%f seconds}, GameTime={%f seconds}\",\n\t\tpos[0], pos[1], pos[2],\n\t\tang[0], ang[1], ang[2],\n\t\tmap, GetClientTeam(client), weapon,\n\t\tGetClientAvgLatency(client, NetFlow_Incoming),\n\t\tGetClientAvgLatency(client, NetFlow_Outgoing),\n\t\tGetClientAvgLoss(client, NetFlow_Incoming),\n\t\tGetClientAvgLoss(client, NetFlow_Outgoing),\n\t\tGetClientAvgChoke(client, NetFlow_Incoming),\n\t\tGetClientAvgChoke(client, NetFlow_Outgoing),\n\t\tGetClientTime(client), GetGameTime());\n\n\tlilac_log(false);\n}\n\nvoid lilac_log(bool cleanup)\n{\n\tHandle file = OpenFile(log_file, \"a\");\n\n\tif (file == null) {\n\t\tPrintToServer(\"[Lilac] Cannot open log file.\");\n\t\treturn;\n\t}\n\n\t/* Remove invalid characters.\n\t * This doesn't care about invalid utf-8 formatting,\n\t * only ASCII control characters. */\n\tif (cleanup) {\n\t\tfor (int i = 0; line_buffer[i]; i++) {\n\t\t\tif (line_buffer[i] == '\\n' || line_buffer[i] == 0x0d)\n\t\t\t\tline_buffer[i] = '*';\n\t\t\telse if (line_buffer[i] < 32)\n\t\t\t\tline_buffer[i] = '#';\n\t\t}\n\t}\n\n\tWriteFileLine(file, \"%s\", line_buffer);\n\t/* Just echo log lines to SourceIRC */\n\tif (icvar[CVAR_SOURCEIRC] && NATIVE_EXISTS(\"IRC_MsgFlaggedChannels\")) {\n\t\t/* Note- SourceIRC Expects messages to be clean with no \\r or \\n, so clean it if not already done. */\n\t\tif (!cleanup) {\n\t\t\tfor (int i = 0; line_buffer[i]; i++) {\n\t\t\t\tif (line_buffer[i] == '\\n' || line_buffer[i] == 0x0d)\n\t\t\t\t\tline_buffer[i] = '*';\n\t\t\t\telse if (line_buffer[i] < 32)\n\t\t\t\t\tline_buffer[i] = '#';\n\t\t\t}\n\t\t}\n\t\tIRC_MsgFlaggedChannels(\"lilac\", \"[LILAC] %s\", line_buffer);\n\t}\n\tCloseHandle(file);\n}\n\nvoid lilac_log_first_time_setup()\n{\n\t/* Some admins may not understand how to interpret cheat logs\n\t * correctly, thus, we should warn them so they don't panic\n\t * over trivial stuff. */\n\tif (!FileExists(log_file, false, NULL_STRING)) {\n\t\tFormatEx(line_buffer, sizeof(line_buffer),\n\"=========[Notice]=========\\n\\\nThank you for installing Little Anti-Cheat %s!\\n\\\nJust a few notes about this Anti-Cheat:\\n\\n\\\nIf a player is logged as \\\"suspected\\\" of using cheats, they are not necessarily cheating.\\n\\\nIf the suspicions logged are few and rare, they are likely false positives.\\n\\\nAn automatic ban is triggered by 5 or more \\\"suspicions\\\" or by one \\\"detection\\\".\\n\\\nIf you think a ban may be incorrect, please do not hesitate to let me know.\\n\\n\\\nThat is all, have a wonderful day~\\n\\n\\n\", PLUGIN_VERSION);\n\t\tlilac_log(false);\n\t}\n}\n\nvoid lilac_ban_client(int client, int cheat)\n{\n\tchar reason[128];\n\tint lang = LANG_SERVER;\n\tbool log_only = false;\n\n\t/* Banning has been disabled, don't forward the ban and don't ban. */\n\tif (!icvar[CVAR_BAN])\n\t\treturn;\n\n\t/* Check if log only mode has been enabled, in which case, don't ban. */\n\tswitch (cheat) {\n\tcase CHEAT_ANGLES: { log_only = icvar[CVAR_ANGLES] < 0; }\n\tcase CHEAT_CHATCLEAR: { log_only = icvar[CVAR_CHAT] < 0; }\n\tcase CHEAT_CONVAR: { log_only = icvar[CVAR_CONVAR] < 0; }\n\tcase CHEAT_NOLERP: { log_only = icvar[CVAR_NOLERP] < 0; }\n\tcase CHEAT_BHOP: { log_only = icvar[CVAR_BHOP] < 0; }\n\t/* Aimbot and Aimlock have their own dedicated log-only mode. */\n\tcase CHEAT_ANTI_DUCK_DELAY: { log_only = icvar[CVAR_ANTI_DUCK_DELAY] < 0; }\n\tcase CHEAT_NOISEMAKER_SPAM: { log_only = icvar[CVAR_NOISEMAKER_SPAM] < 0; }\n\tcase CHEAT_MACRO: { log_only = icvar[CVAR_MACRO] < 0; }\n\tcase CHEAT_NEWLINE_NAME: { log_only = icvar[CVAR_FILTER_NAME] < 0; }\n\t}\n\n\tif (log_only)\n\t\treturn;\n\n\tif (icvar[CVAR_BAN_LANGUAGE])\n\t\tlang = client;\n\n\tswitch (cheat) {\n\tcase CHEAT_ANGLES: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_angle\", lang); }\n\tcase CHEAT_CHATCLEAR: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_chat_clear\", lang); }\n\tcase CHEAT_CONVAR: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_convar\", lang); }\n\tcase CHEAT_NOLERP: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_nolerp\", lang); }\n\tcase CHEAT_BHOP: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_bhop\", lang); }\n\tcase CHEAT_AIMBOT: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_aimbot\", lang); }\n\tcase CHEAT_AIMLOCK: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_aimlock\", lang); }\n\tcase CHEAT_ANTI_DUCK_DELAY: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_anti_duck_delay\", lang); }\n\tcase CHEAT_NOISEMAKER_SPAM: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_noisemaker\", lang); }\n\tcase CHEAT_MACRO: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_macro\", lang); }\n\tcase CHEAT_NEWLINE_NAME: { Format(reason, sizeof(reason),\n\t\t\"[Little Anti-Cheat %s] %T\", PLUGIN_VERSION, \"ban_name_newline\", lang); }\n\tdefault: return;\n\t}\n\n\tlilac_forward_client_ban(client, cheat);\n\n\n\t/* Try to ban with MateralAdmin first,\n\t * if that fails, proceed to SourceBans, then SourceBans++,\n\t * And lastly, BaseBans. */\n\n\n\tif (icvar[CVAR_MA] && NATIVE_EXISTS(\"MABanPlayer\")) {\n\t\tMABanPlayer(0, client, MA_BAN_STEAM, get_ban_length(cheat), reason);\n\t\tCreateTimer(5.0, timer_kick, GetClientUserId(client));\n\t\treturn;\n\t}\n\n\n\tif (icvar[CVAR_SB]) {\n\t\tif (NATIVE_EXISTS(\"SBPP_BanPlayer\")) {\n\t\t\tSBPP_BanPlayer(0, client, get_ban_length(cheat), reason);\n\t\t\tCreateTimer(5.0, timer_kick, GetClientUserId(client));\n\t\t\treturn;\n\t\t}\n\t\telse if (NATIVE_EXISTS(\"SBBanPlayer\")) {\n\t\t\tSBBanPlayer(0, client, get_ban_length(cheat), reason);\n\t\t\tCreateTimer(5.0, timer_kick, GetClientUserId(client));\n\t\t\treturn;\n\t\t}\n\t}\n\n\n\tBanClient(client, get_ban_length(cheat), BANFLAG_AUTO, reason, reason, \"lilac\", 0);\n\tCreateTimer(5.0, timer_kick, GetClientUserId(client));\n}\n\npublic Action timer_kick(Handle timer, int userid)\n{\n\tint client = GetClientOfUserId(userid);\n\n\tif (is_player_valid(client))\n\t\tKickClient(client, \"%T\", \"kick_ban_generic\", client);\n\t\t\n\treturn Plugin_Continue;\n}\n\nint get_ban_length(int cheat)\n{\n\treturn ((ban_length_overwrite[cheat] <= -1) ? icvar[CVAR_BAN_LENGTH] : ban_length_overwrite[cheat]);\n}\n\nvoid get_player_log_angles(int client, int tick, bool latest, float writeto[3])\n{\n\tint i = tick;\n\n\tif (latest) {\n\t\ti = playerinfo_index[client];\n\t}\n\telse {\n\t\twhile (i < 0)\n\t\t\ti += CMD_LENGTH;\n\t\twhile (i >= CMD_LENGTH)\n\t\t\ti -= CMD_LENGTH;\n\t}\n\n\twriteto[0] = playerinfo_angles[client][i][0];\n\twriteto[1] = playerinfo_angles[client][i][1];\n\twriteto[2] = playerinfo_angles[client][i][2];\n}\n\nvoid set_player_log_angles(int client, float ang[3], int tick)\n{\n\tint i = tick;\n\n\t/* Normalize tick. */\n\twhile (i < 0)\n\t\ti += CMD_LENGTH;\n\twhile (i >= CMD_LENGTH)\n\t\ti -= CMD_LENGTH;\n\n\tplayerinfo_angles[client][i][0] = ang[0];\n\tplayerinfo_angles[client][i][1] = ang[1];\n\tplayerinfo_angles[client][i][2] = ang[2];\n}\n\nvoid aim_at_point(const float p1[3], const float p2[3], float writeto[3])\n{\n\tSubtractVectors(p2, p1, writeto);\n\tGetVectorAngles(writeto, writeto);\n\n\twhile (writeto[0] > 90.0)\n\t\twriteto[0] -= 360.0;\n\twhile (writeto[0] < -90.0)\n\t\twriteto[0] += 360.0;\n\twhile (writeto[1] > 180.0)\n\t\twriteto[1] -= 360.0;\n\twhile (writeto[1] < -180.0)\n\t\twriteto[1] += 360.0;\n\n\twriteto[2] = 0.0;\n}\n\nfloat angle_delta(float []a1, float []a2)\n{\n\tint normal = 5;\n\tfloat p1[3], p2[3], delta;\n\n\tp1[0] = a1[0];\n\tp2[0] = a2[0];\n\tp2[1] = a2[1];\n\tp1[1] = a1[1];\n\n\t/* We don't care about roll. */\n\tp1[2] = 0.0;\n\tp2[2] = 0.0;\n\n\tdelta = GetVectorDistance(p1, p2);\n\n\t/* Normalize maximum 5 times, yaw can sometimes be odd. */\n\twhile (delta > 180.0 && normal > 0) {\n\t\tnormal--;\n\t\tdelta = FloatAbs(delta - 360.0);\n\t}\n\n\treturn delta;\n}\n\nbool skip_due_to_loss(int client)\n{\n\t/* Debate: What percentage should this be at?\n\t * Skip detection if the loss is more than 50% */\n\tif (icvar[CVAR_LOSS_FIX])\n\t\treturn GetClientAvgLoss(client, NetFlow_Both) > 0.5;\n\n\treturn false;\n}\n\nint time_to_ticks(float time)\n{\n\tif (time > 0.0)\n\t\treturn RoundToNearest(time / GetTickInterval());\n\n\treturn 0;\n}\n\nint intabs(int num)\n{\n\treturn ((num < 0) ? num * -1 : num);\n}\n\nbool is_player_admin(int client)\n{\n\t/* Todo: I don't know if this is correct. */\n\treturn CheckCommandAccess(client, \"\", ADMFLAG_GENERIC | ADMFLAG_KICK | ADMFLAG_SLAY, true);\n}\n\nbool is_player_valid(int client)\n{\n\treturn (client >= 1 && client <= MaxClients\n\t\t&& IsClientConnected(client) && IsClientInGame(client)\n\t\t&& !IsClientSourceTV(client));\n}\n\nvoid lilac_forward_client_cheat(int client, int cheat)\n{\n\tint dummy;\n\n\tif (forwardhandle == null)\n\t\treturn;\n\n\tCall_StartForward(forwardhandle);\n\tCall_PushCell(client);\n\tCall_PushCell(cheat);\n\tCall_Finish(dummy);\n}\n\nvoid lilac_forward_client_ban(int client, int cheat)\n{\n\tint dummy;\n\n\tif (forwardhandleban == null)\n\t\treturn;\n\n\tCall_StartForward(forwardhandleban);\n\tCall_PushCell(client);\n\tCall_PushCell(cheat);\n\tCall_Finish(dummy);\n}\n\nbool lilac_forward_allow_cheat_detection(int client, int cheat)\n{\n\tAction result = Plugin_Continue;\n\n\tif (forwardhandleallow == null)\n\t\treturn true;\n\n\tCall_StartForward(forwardhandleallow);\n\tCall_PushCell(client);\n\tCall_PushCell(cheat);\n\tCall_Finish(result);\n\n\tif (result == Plugin_Continue)\n\t\treturn true;\n\n\treturn false;\n}\n"
  },
  {
    "path": "scripting/lilac/lilac_string.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\npublic Action OnClientSayCommand(int client, const char[] command, const char[] sArgs)\n{\n\tint flags;\n\n\tif (!icvar[CVAR_ENABLE])\n\t\treturn Plugin_Continue;\n\n\t/* Prevent players banned for Chat-Clear from spamming chat.\n\t * Helps legit players see the cheater was banned. */\n\tif (playerinfo_banned_flags[client][CHEAT_CHATCLEAR])\n\t\treturn Plugin_Stop;\n\n\tif (!icvar[CVAR_FILTER_CHAT])\n\t\treturn Plugin_Continue;\n\n\t/* Just for future reference, because it may be unclear\n\t * how \"is_string_valid()\" works.\n\t * Normally, it will set the \"flags\" variable with bits\n\t * telling you what's wrong with a string and return false.\n\t * But the wide-char-spam bit is an exception,\n\t * it will set the bit, but won't return false.\n\t * This is because wide-char spam gets its own message\n\t * when blocking the chat.\n\t * Plus, it's still technically a valid string. */\n\n\t/* Invalid string and no newlines/carriage returns.\n\t * Newlines in chat will we dealt with in post.\n\t * This is just so people can see that the player did indeed\n\t * clear the chat before banning, otherwise people\n\t * would be confused and think the ban was an error. */\n\tif (!(is_string_valid(sArgs, flags)) && !(flags & STRFLAG_NEWLINE)) {\n\t\tPrintToChat(client, \"[Lilac] %T\", \"chat_invalid_characters\", client);\n\t\treturn Plugin_Stop;\n\t}\n\telse if ((flags & STRFLAG_WIDE_CHAR_SPAM)) {\n\t\t/* Wide char spam (Example: Bismillah spam),\n\t\t * as explained by 3kliksphilip here: https://youtu.be/hP1N1YRitlM?t=94\n\t\t * this clears the chat and is annoying.\n\t\t * Block this exploit. */\n\t\tPrintToChat(client, \"[Lilac] %T\", \"chat_wide_char_spam\", client);\n\t\treturn Plugin_Stop;\n\t}\n\n\treturn Plugin_Continue;\n}\n\npublic void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs)\n{\n\t/* Chat-Clear doesn't work in CS:GO. */\n\tif (ggame == GAME_CSGO)\n\t\treturn;\n\n\t/* Todo: CVAR_CHAT is... Now an outdated name... */\n\tif (!icvar[CVAR_ENABLE] || !icvar[CVAR_CHAT])\n\t\treturn;\n\n\t/* Don't log chat-clear more than once. */\n\tif (playerinfo_banned_flags[client][CHEAT_CHATCLEAR])\n\t\treturn;\n\n\tif (does_string_contain_newline(sArgs)) {\n\t\tif (lilac_forward_allow_cheat_detection(client, CHEAT_CHATCLEAR) == false)\n\t\t\treturn;\n\n\t\tplayerinfo_banned_flags[client][CHEAT_CHATCLEAR] = true;\n\t\tlilac_forward_client_cheat(client, CHEAT_CHATCLEAR);\n\n\t\tif (icvar[CVAR_LOG]) {\n\t\t\tlilac_log_setup_client(client);\n\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\"%s was detected and banned for Chat-Clear (Chat message: %s)\",\n\t\t\t\tline_buffer, sArgs);\n\n\t\t\tlilac_log(true);\n\n\t\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\t\tlilac_log_extra(client);\n\t\t}\n\t\tdatabase_log(client, \"chat_clear\", DATABASE_BAN);\n\n\t\tlilac_ban_client(client, CHEAT_CHATCLEAR);\n\t}\n}\n\n/* Quick and fast, better to use this than validate the entire string twice. */\nstatic bool does_string_contain_newline(const char []string)\n{\n\tfor (int i = 0; string[i]; i++) {\n\t\t/* Newline or carriage return. */\n\t\tif (string[i] == '\\n' || string[i] == '\\r')\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\npublic Action event_namechange(Event event, const char[] name, bool dontBroadcast)\n{\n\tint client;\n\tchar client_name[MAX_NAME_LENGTH];\n\n\tclient = GetClientOfUserId(GetEventInt(event, \"userid\", 0));\n\n\tif (skip_name_check(client))\n\t\treturn Plugin_Continue;\n\n\tGetEventString(event, \"newname\", client_name, sizeof(client_name), \"\");\n\tcheck_name(client, client_name);\n\n\treturn Plugin_Continue;\n}\n\nvoid lilac_string_check_name(int client)\n{\n\tchar name[MAX_NAME_LENGTH];\n\n\tif (skip_name_check(client))\n\t\treturn;\n\n\tif (!GetClientName(client, name, sizeof(name)))\n\t\treturn;\n\n\tcheck_name(client, name);\n}\n\nstatic bool skip_name_check(int client)\n{\n\tif (!icvar[CVAR_ENABLE]\n\t\t|| !icvar[CVAR_FILTER_NAME]\n\t\t|| !is_player_valid(client)\n\t\t|| IsFakeClient(client))\n\t\treturn true;\n\n\treturn false;\n}\n\nstatic void check_name(int client, const char []name)\n{\n\tint flags;\n\n\tif (is_string_valid(name, flags))\n\t\treturn;\n\n\t/* Todo: Currently logging \"const char []name\" because on the\n\t * event name_change, we don't actually log the player's\n\t * most recent name, rather their previous name.\n\t * I should fix that, but for now, lets do this instead (tmp fix). */\n\n\t/* Player was detected of having a newline or carriage return in their name, which is a cheat feature... */\n\tif (icvar[CVAR_FILTER_NAME] == 2 && (flags & STRFLAG_NEWLINE)) {\n\t\tif (playerinfo_banned_flags[client][CHEAT_NEWLINE_NAME])\n\t\t\treturn;\n\n\t\tif (lilac_forward_allow_cheat_detection(client, CHEAT_NEWLINE_NAME) == false)\n\t\t\treturn;\n\n\t\tplayerinfo_banned_flags[client][CHEAT_NEWLINE_NAME] = true;\n\t\tlilac_forward_client_cheat(client, CHEAT_NEWLINE_NAME);\n\n\t\tif (icvar[CVAR_LOG]) {\n\t\t\tlilac_log_setup_client(client);\n\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\"%s was banned of having newline characters in their name (%s).\", line_buffer, name);\n\n\t\t\tlilac_log(true);\n\n\t\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\t\tlilac_log_extra(client);\n\t\t}\n\t\tdatabase_log(client, \"name_newline\", DATABASE_BAN);\n\n\t\tlilac_ban_client(client, CHEAT_NEWLINE_NAME);\n\t}\n\telse {\n\t\t/* Invalid name. */\n\t\tif (icvar[CVAR_LOG_MISC]) {\n\t\t\tlilac_log_setup_client(client);\n\t\t\tFormat(line_buffer, sizeof(line_buffer),\n\t\t\t\t\"%s was kicked for having invalid characters in their name (%s).\", line_buffer, name);\n\n\t\t\tlilac_log(true);\n\n\t\t\tif (icvar[CVAR_LOG_EXTRA])\n\t\t\t\tlilac_log_extra(client);\n\t\t}\n\t\tdatabase_log(client, \"name_invalid\", DATABASE_KICK);\n\n\t\t/* Log only. */\n\t\tif (icvar[CVAR_FILTER_NAME] > 0)\n\t\t\tKickClient(client, \"[Lilac] %T\", \"kick_bad_name\", client);\n\t}\n}\n\n\n\n/*\n\tUTF-8 validater and Blacklist...\n\tMy old UTF-8 checker was soooo ugly, I had to change it.\n\n\tNote: 5 and 6 byte encodings aren't valid in UTF-8 anymore.\n\tOriginally, UTF-8 could have up to 6 byte long encodings, but in\n\t2003 it was changed to be maximum 4 bytes, to match the limitations\n\tof UTF-16.\n\n\t2003 was long ago, and so the 4 byte maximum is used here.\n*/\n\n\nstatic bool is_string_valid(const char []string, int &flags)\n{\n\tint widechars = 0;\n\tint i = 0;\n\tflags = 0;\n\n\twhile (string[i]) {\n\t\tint codepoint = 0;\n\t\tint len = utf8_decode(string[i], codepoint); /* SPawn pointer logic sucks :( */\n\n\t\t/* Invalid UTF-8 encoding. */\n\t\tif (len == 0)\n\t\t\treturn false;\n\n\t\tswitch (codepoint) {\n\t\tcase '\\n', '\\r': {\n\t\t\tflags = STRFLAG_NEWLINE;\n\t\t\treturn false;\n\t\t}\n\t\tcase 0xfdfd /* Bismillah. */ : {\n\t\t\tif (++widechars > 3)\n\t\t\t\tflags |= STRFLAG_WIDE_CHAR_SPAM;\n\t\t}\n\t\t}\n\n\t\t/* Other than the UTF-16 ranges and unicode limit,\n\t\t * these are just blacklisted codepoints and are valid UTF-8.\n\t\t * Private Use Areas and Control Characters are not allowed. */\n\n\t\t/* UTF-16 reserved surgate halves, not valid codepoint. */\n\t\tif (codepoint >= 0xd800 && codepoint <= 0xdfff)\n\t\t\treturn false;\n\t\telse if (codepoint > 0x10ffff) /* Unicode limit. */\n\t\t\treturn false;\n\t\telse if (codepoint < 0x20 && codepoint != '\\t') /* C0 control chars.*/\n\t\t\treturn false;\n\t\telse if (codepoint == 0x7f) /* Not really C0, but will count as one. */\n\t\t\treturn false;\n\t\telse if (codepoint >= 0x80 && codepoint <= 0x9f) /* C1 control chars. */\n\t\t\treturn false;\n\t\telse if (codepoint >= 0xe000 && codepoint <= 0xf8ff) /* PUA. */\n\t\t\treturn false;\n\t\telse if (codepoint >= 0xf0000 && codepoint <= 0xfffff) /* PUA. */\n\t\t\treturn false;\n\t\telse if (codepoint >= 0x100000 && codepoint <= 0x10fffd) /* PUA. */\n\t\t\treturn false;\n\n\t\ti += len;\n\t}\n\n\t/* No invalid encodings or codepoints found :) */\n\treturn true;\n}\n\n/* This function does not check for valid codepoints.\n * However, this will check for overlong encodings.\n * Returns the length of the encoding, 0 on error. */\nstatic int utf8_decode(const char []ptr, int &codepoint)\n{\n\tstatic const int mask[] = {0, 0, 0x1f, 0x0f, 0x07};\n\n\tint len = utf8_header_length(ptr[0]);\n\tif (len == 0) {\n\t\treturn 0;\n\t}\n\telse if (len == 1) {\n\t\tcodepoint = ptr[0];\n\t\treturn 1;\n\t}\n\n\tcodepoint = ptr[0] & mask[len];\n\tfor (int i = 1; i < len; i++) {\n\t\tif ((ptr[i] & 0xc0) != 0x80)\n\t\t\treturn 0;\n\n\t\tcodepoint = (codepoint << 6) | (ptr[i] & 0x3f);\n\t}\n\n\tif (len != codepoint_to_utf8_length(codepoint))\n\t\treturn 0;\n\n\treturn len;\n}\n\nstatic int utf8_header_length(char c)\n{\n\t/* Codepoint will always be above the U+10ffff limit. */\n\tif (c >= 0xf5)\n\t\treturn 0;\n\t/* Can only be invalid codepoints; two byte ASCII. */\n\telse if (c == 0xc0 || c == 0xc1)\n\t\treturn 0;\n\n\tswitch ((c & 0xf0)) {\n\t/* Masking can include an extra bit for two byte encodings. */\n\tcase 0xc0, 0xd0: return 2;\n\tcase 0xe0: return 3;\n\tcase 0xf0: return 4;\n\tdefault: return 1;\n\t}\n}\n\nstatic int codepoint_to_utf8_length(int codepoint)\n{\n\tif (codepoint < 0x80)\n\t\treturn 1;\n\telse if (codepoint < 0x800)\n\t\treturn 2;\n\telse if (codepoint < 0x10000)\n\t\treturn 3;\n\telse if (codepoint < 0x110000) /* U+10ffff + 1 */\n\t\treturn 4;\n\telse\n\t\treturn 0;\n}\n"
  },
  {
    "path": "scripting/lilac.sp",
    "content": "/*\n\tLittle Anti-Cheat\n\tCopyright (C) 2018-2023 J_Tanzanite\n\n\tThis program is free software: you can redistribute it and/or modify\n\tit under the terms of the GNU General Public License as published by\n\tthe Free Software Foundation, either version 3 of the License, or\n\t(at your option) any later version.\n\n\tThis program is distributed in the hope that it will be useful,\n\tbut WITHOUT ANY WARRANTY; without even the implied warranty of\n\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\tGNU General Public License for more details.\n\n\tYou should have received a copy of the GNU General Public License\n\talong with this program.  If not, see <https://www.gnu.org/licenses/>.\n*/\n\n/* Uncomment below line to compile for Team Fortress 2 Classic.\n * You'll need SourceMod 1.10 and SM-TF2Clasic-Tools to compile, if you decide to.\n * Note: Doing so means the compiled plugin won't work correctly\n * for other source games. \n * SourceMod 1.10: https://www.sourcemod.net/downloads.php?branch=1.10-dev\n * SM-TF2Clasic-Tools: https://github.com/tf2classic/SM-TF2Classic-Tools */\n//#define TF2C\n\n#include <sourcemod>\n#include <sdktools_engine>\n#include <sdktools_entoutput>\n#include <convar_class>\n#undef REQUIRE_PLUGIN /* ... */\n#undef REQUIRE_EXTENSIONS\n#if defined TF2C\n\t#include <tf2c>\n#else\n\t#include <tf2>\n\t#include <tf2_stocks>\n#endif\n#define REQUIRE_PLUGIN\n#define REQUIRE_EXTENSIONS\n\n#pragma semicolon 1\n#pragma newdecls required\n\n#include \"lilac/lilac_globals.sp\" /* Must be at top, contains defines. */\n\n#include \"lilac/lilac_aimbot.sp\"\n#include \"lilac/lilac_aimlock.sp\"\n#include \"lilac/lilac_angles.sp\"\n#include \"lilac/lilac_anti_duck_delay.sp\"\n#include \"lilac/lilac_backtrack.sp\"\n#include \"lilac/lilac_bhop.sp\"\n#include \"lilac/lilac_config.sp\"\n#include \"lilac/lilac_convar.sp\"\n#include \"lilac/lilac_database.sp\"\n#include \"lilac/lilac_lerp.sp\"\n#include \"lilac/lilac_macro.sp\"\n#include \"lilac/lilac_noisemaker.sp\"\n#include \"lilac/lilac_ping.sp\"\n#include \"lilac/lilac_stock.sp\"\n#include \"lilac/lilac_string.sp\" /* String takes care of chat and names. */\n\n\npublic Plugin myinfo = {\n\tname = PLUGIN_NAME,\n\tauthor = PLUGIN_AUTHOR,\n\tdescription = PLUGIN_DESC,\n\tversion = PLUGIN_VERSION,\n\turl = PLUGIN_URL\n};\n\n\npublic void OnPluginStart()\n{\n\tLoadTranslations(\"lilac.phrases.txt\");\n\n#if defined TF2C\n\tggame = GAME_TF2;\n\t/* Post inventory check isn't needed,\n\t * as noisemaker spam isn't a thing in TF2Classic. */\n\tHookEvent(\"player_teleported\", event_teleported, EventHookMode_Post);\n\tHookEvent(\"player_death\", event_player_death_tf2, EventHookMode_Pre);\n#else\n\tchar gamefolder[32];\n\tGetGameFolderName(gamefolder, sizeof(gamefolder));\n\tif (StrEqual(gamefolder, \"tf\", false)) {\n\t\tggame = GAME_TF2;\n\n\t\tHookEvent(\"post_inventory_application\", event_inventoryupdate, EventHookMode_Post);\n\t\tHookEvent(\"player_teleported\", event_teleported, EventHookMode_Post);\n\t}\n\telse if (StrEqual(gamefolder, \"cstrike\", false)) {\n\t\tggame = GAME_CSS;\n\t}\n\telse if (StrEqual(gamefolder, \"csgo\", false)) {\n\t\tConVar tvar;\n\t\tggame = GAME_CSGO;\n\n\t\tif ((tvar = FindConVar(\"sv_autobunnyhopping\")) != null) {\n\t\t\tforce_disable_bhop = tvar.IntValue;\n\t\t\ttvar.AddChangeHook(cvar_change);\n\t\t}\n\t\telse {\n\t\t\t/* We weren't able to get the cvar,\n\t\t\t * disable bhop checks just in case. */\n\t\t\tforce_disable_bhop = 1;\n\n\t\t\tPrintToServer(\"[Lilac] Unable to to find convar \\\"sv_autobunnyhopping\\\", bhop checks have been forcefully disabled.\");\n\t\t}\n\t}\n\telse if (StrEqual(gamefolder, \"left4dead2\", false)) {\n\t\tggame = GAME_L4D2;\n\t}\n\telse if (StrEqual(gamefolder, \"left4dead\", false)) {\n\t\tggame = GAME_L4D;\n\t}\n\telse if (StrEqual(gamefolder, \"dod\", false)) {\n\t\tggame = GAME_DODS;\n\t}\n\telse {\n\t\tggame = GAME_UNKNOWN;\n\t\tPrintToServer(\"[Lilac] This game currently isn't supported, Little Anti-Cheat will still run, but expect some bugs and false positives/bans!\");\n\t}\n\n\tif (ggame == GAME_TF2)\n\t\tHookEvent(\"player_death\", event_player_death_tf2, EventHookMode_Pre);\n\telse\n\t\tHookEvent(\"player_death\", event_player_death, EventHookMode_Pre);\n#endif /* TF2C check. */\n\n\tHookEvent(\"player_spawn\", event_teleported, EventHookMode_Post);\n\tHookEvent(\"player_changename\", event_namechange, EventHookMode_Post);\n\n\tHookEntityOutput(\"trigger_teleport\", \"OnEndTouch\", map_teleport);\n\n\t/* Default ban lengths are -1. (Global ConVar). */\n\tfor (int i = 0; i < CHEAT_MAX; i++)\n\t\tban_length_overwrite[i] = -1;\n\n\t/* Bans for Bhop last 1 month by default. */\n\tban_length_overwrite[CHEAT_BHOP] = 24 * 30 * 60;\n\n\t/* Bans for Macros are 15 minutes by default. */\n\tban_length_overwrite[CHEAT_MACRO] = 15;\n\n\t/* If sv_maxupdaterate is changed mid-game and then this plugin\n\t * is loaded, then it could lead to false positives.\n\t * Reset all stats on all players already in-game, but ignore lerp.\n\t * Also check players already in-game for noisemaker. */\n\tfor (int i = 1; i <= MaxClients; i++) {\n\t\tlilac_reset_client(i);\n\t\tlilac_lerp_ignore_nolerp_client(i);\n#if !defined TF2C\n\t\tcheck_inventory_for_noisemaker(i);\n#endif\n\t}\n\n\tforwardhandle = CreateGlobalForward(\"lilac_cheater_detected\",\n\t\tET_Ignore, Param_Cell, Param_Cell);\n\tforwardhandleban = CreateGlobalForward(\"lilac_cheater_banned\",\n\t\tET_Ignore, Param_Cell, Param_Cell);\n\tforwardhandleallow = CreateGlobalForward(\"lilac_allow_cheat_detection\",\n\t\tET_Event, Param_Cell, Param_Cell);\n\n\tCreateTimer(QUERY_TIMER, timer_query, _, TIMER_REPEAT);\n\tCreateTimer(5.0, timer_check_ping, _, TIMER_REPEAT);\n\tCreateTimer(5.0, timer_check_lerp, _, TIMER_REPEAT);\n\tCreateTimer(0.5, timer_check_aimlock, _, TIMER_REPEAT);\n\tCreateTimer(60.0 * 5.0, timer_decrement_macro, _, TIMER_REPEAT);\n\n\ttick_rate = RoundToNearest(1.0 / GetTickInterval());\n\n\t/* Ignore low tickrates. */\n\tmacro_max = (tick_rate >= 60 && tick_rate <= MACRO_LOG_LENGTH) ? 20 : 0;\n\n\tif (tick_rate > 50) {\n\t\tbhop_settings_min[BHOP_INDEX_MIN] = 5;\n\t\tbhop_settings_min[BHOP_INDEX_MAX] = 10;\n\t\tbhop_settings_min[BHOP_INDEX_TOTAL] = 1;\n\t}\n\telse {\n\t\tbhop_settings_min[BHOP_INDEX_MIN] = 10;\n\t\tbhop_settings_min[BHOP_INDEX_MAX] = 20;\n\t\tbhop_settings_min[BHOP_INDEX_TOTAL] = 3;\n\t}\n\tbhop_settings_min[BHOP_INDEX_JUMP] = -1;\n\tbhop_settings_min[BHOP_INDEX_AIR] = 0;\n\n\t/* This sets up convars and such. */\n\tlilac_config_setup();\n\n\tif (icvar[CVAR_LOG])\n\t\tlilac_log_first_time_setup();\n}\n\npublic void OnAllPluginsLoaded()\n{\n\tsourcebanspp_exist = LibraryExists(\"sourcebans++\");\n\tsourcebans_exist = LibraryExists(\"sourcebans\");\n\tmaterialadmin_exist = LibraryExists(\"materialadmin\");\n\n\tif (LibraryExists(\"updater\"))\n\t\tlilac_update_url();\n\n\t/* Startup message. */\n\tPrintToServer(\"[Little Anti-Cheat %s] Successfully loaded!\", PLUGIN_VERSION);\n}\n\npublic APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int err_max)\n{\n\t/* Been told this isn't needed, but just in case. */\n\tMarkNativeAsOptional(\"SBBanPlayer\");\n\tMarkNativeAsOptional(\"SBPP_BanPlayer\");\n\tMarkNativeAsOptional(\"MABanPlayer\");\n\tMarkNativeAsOptional(\"Updater_AddPlugin\");\n\tMarkNativeAsOptional(\"Updater_RemovePlugin\");\n\tMarkNativeAsOptional(\"IRC_MsgFlaggedChannels\");\n\n\t/* Build the log path for the file in case the user has overridden sm_basepath. */\n\tBuildPath(Path_SM, log_file, sizeof(log_file), \"logs/lilac.log\");\n\treturn APLRes_Success;\n}\n\npublic void OnLibraryAdded(const char []name)\n{\n\tif (StrEqual(name, \"sourcebans++\"))\n\t\tsourcebanspp_exist = true;\n\telse if (StrEqual(name, \"sourcebans\"))\n\t\tsourcebans_exist = true;\n\telse if (StrEqual(name, \"materialadmin\"))\n\t\tmaterialadmin_exist = true;\n\telse if (StrEqual(name, \"updater\"))\n\t\tlilac_update_url();\n}\n\npublic void OnLibraryRemoved(const char []name)\n{\n\tif (StrEqual(name, \"sourcebans++\"))\n\t\tsourcebanspp_exist = false;\n\telse if (StrEqual(name, \"sourcebans\"))\n\t\tsourcebans_exist = false;\n\telse if (StrEqual(name, \"materialadmin\"))\n\t\tmaterialadmin_exist = false;\n}\n\nvoid lilac_update_url()\n{\n\tif (icvar[CVAR_AUTO_UPDATE]) {\n\t\tif (!NATIVE_EXISTS(\"Updater_AddPlugin\")) {\n\t\t\tPrintToServer(\"Error: Native Updater_AddPlugin() not found! Check if updater plugin is installed.\");\n\t\t\treturn;\n\t\t}\n\n\t\tUpdater_AddPlugin(UPDATE_URL);\n\t}\n\telse {\n\t\tif (!NATIVE_EXISTS(\"Updater_RemovePlugin\")) {\n\t\t\tPrintToServer(\"Error: Native Updater_RemovePlugin() not found! Check if updater plugin is installed.\");\n\t\t\treturn;\n\t\t}\n\n\t\tUpdater_RemovePlugin();\n\t}\n}\n\npublic void OnClientPutInServer(int client)\n{\n\tlilac_reset_client(client);\n\tlilac_string_check_name(client);\n\n\tCreateTimer(30.0, timer_welcome, GetClientUserId(client));\n}\n\npublic void TF2_OnConditionRemoved(int client, TFCond condition)\n{\n\tif (condition == TFCond_Taunting)\n\t\tplayerinfo_time_teleported[client] = GetGameTime();\n\telse if (condition == TFCond_HalloweenKart)\n\t\tplayerinfo_time_bumpercart[client] = GetGameTime();\n}\n\npublic Action event_teleported(Event event, const char[] name, bool dontBroadcast)\n{\n\tint client = GetClientOfUserId(GetEventInt(event, \"userid\", -1));\n\n\tif (is_player_valid(client))\n\t\tplayerinfo_time_teleported[client] = GetGameTime();\n\n\treturn Plugin_Continue;\n}\n\npublic void map_teleport(const char[] output, int caller, int activator, float delay)\n{\n\tif (!is_player_valid(activator) || IsFakeClient(activator))\n\t\treturn;\n\n\tplayerinfo_time_teleported[activator] = GetGameTime();\n}\n\npublic Action timer_welcome(Handle timer, int userid)\n{\n\tint client = GetClientOfUserId(userid);\n\n\t/* Todo: Considering there are log-only options now...\n\t * Perhaps I should check if ANYTHING can ban at all. */\n\tif (is_player_valid(client) && icvar[CVAR_WELCOME]\n\t\t&& icvar[CVAR_ENABLE] && icvar[CVAR_BAN])\n\t\tPrintToChat(client, \"[Lilac] %T\", \"welcome_msg\", client, PLUGIN_VERSION);\n\n\treturn Plugin_Continue;\n}\n\npublic Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3],\n\t\t\t\tfloat angles[3], int& weapon, int& subtype, int& cmdnum,\n\t\t\t\tint& tickcount, int& seed, int mouse[2])\n{\n\tstatic int lbuttons[MAXPLAYERS + 1];\n\n\tif (!is_player_valid(client) || IsFakeClient(client))\n\t\treturn Plugin_Continue;\n\n\t/* Increment the index. */\n\tif (++playerinfo_index[client] >= CMD_LENGTH)\n\t\tplayerinfo_index[client] = 0;\n\n\t/* Store when the tick was processed. */\n\tplayerinfo_time_usercmd[client][playerinfo_index[client]] = GetGameTime();\n\n\t/* Store information. */\n\tlilac_backtrack_store_tickcount(client, tickcount);\n\tset_player_log_angles(client, angles, playerinfo_index[client]);\n\tplayerinfo_buttons[client][playerinfo_index[client]] = buttons;\n\tplayerinfo_actions[client][playerinfo_index[client]] = 0;\n\n\tif ((buttons & IN_ATTACK) && bullettime_can_shoot(client))\n\t\tplayerinfo_actions[client][playerinfo_index[client]] |= ACTION_SHOT;\n\n\tif (icvar[CVAR_ENABLE]) {\n#if !defined TF2C\n\t\t/* Detect Anti-Duck-Delay. */\n\t\tif (ggame == GAME_CSGO && icvar[CVAR_ANTI_DUCK_DELAY])\n\t\t\tlilac_anti_duck_delay_check(client, buttons);\n#endif\n\t\t/* Detect Angle-Cheats. */\n\t\tif (icvar[CVAR_ANGLES])\n\t\t\tlilac_angles_check(client, angles);\n\n\t\t/* Detect Macros. */\n\t\tif (macro_max && icvar[CVAR_MACRO])\n\t\t\tlilac_macro_check(client, buttons, lbuttons[client]);\n\n\t\t/* Detect bhop. */\n\t\tif (!force_disable_bhop && icvar[CVAR_BHOP])\n\t\t\tlilac_bhop_check(client, buttons, lbuttons[client]);\n\n\t\t/* Patch Angle-Cheats. */\n\t\tif (icvar[CVAR_PATCH_ANGLES])\n\t\t\tlilac_angles_patch(angles);\n\n\t\t/* Patch Backtracking. */\n\t\tif (icvar[CVAR_BACKTRACK_PATCH])\n\t\t\ttickcount = lilac_backtrack_patch(client, tickcount);\n\t}\n\n\tlbuttons[client] = buttons;\n\n\treturn Plugin_Continue;\n}\n"
  },
  {
    "path": "translations/chi/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"chi\"\t\t\"本服务器受到LAC反作弊系统保护 {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"chi\"\t\t\"错误：查询未响应，如果此问题仍然存在，请重新启动游戏\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"chi\"\t\t\"检测到参数利用：您的interp过高({1}ms / {2}ms 最高)。请设置您的 cl_interp 参数至 {3} 或更低。\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"chi\"\t\t\"您的Ping值过高！ ({1} / {2} 最高)\"\n\t}\n\n\t\"kick_ban_genetic\"\n\t{\n\t\t\"chi\"\t\t\"您已经被此服务器封禁。\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"chi\"\t\t\"检测到异常角度\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"chi\"\t\t\"检测到清屏作弊\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"chi\"\t\t\"检测到非法变量\"\n\t}\n\t\"ban_nolerp\"\n\t{\n\t\t\"chi\"\t\t\"检测到非法Lerp值\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"chi\"\t\t\"检测到连跳脚本\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"chi\"\t\t\"检测到自瞄\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"chi\"\t\t\"检测到暴力锁头\"\n\t}\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"chi\"\t\t\"检测到下蹲辅助\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"chi\"\t\t\"检测到刷屏\"\n\t}\n\t\"ban_macro\"\n\t{\n\t\t\"chi\"\t\t\"检测到辅助宏\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"chi\"\t\t\"此服务器不允许使用{1}辅助宏 \"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"chi\" \t\t\"你的聊天信息已被屏蔽 (原因: 无效字符)\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"chi\"\t\t\"你的聊天信息已被屏蔽 (原因: 宽字符垃圾信息)\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"chi\"\t\t\"你的游戏名称含有非法字符, 请更改你的游戏名称\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"chi\"\t\t\"检测到名称含有换行符\"\n\t}\n\n\t\"admin_chat_warning_generic\"\n\t{\n\t\t\"#format\"\t\"{1:s},{2:s},{3:d}\"\n\t\t\"chi\"\t\t\"{1} 可能使用了 '{2}' (检测次数: {3})\"\n\t}\n}\n"
  },
  {
    "path": "translations/cze/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"cze\"\t\t\"Tento server je chráněn Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"cze\"\t\t\"Error: Selhnání odpovědi na dotaz, restartujte hru pokud problém nadále přetrvává\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"cze\"\t\t\"Detekováno neférové zneužití: Interp je příliš velký. ({1}ms / {2}ms max). Prosím nastavte svůj cl_interp na hodnotu {3} či méně\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"cze\"\t\t\"Váš ping je příliš vysoký ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"cze\"\t\t\"Byl jste zabanován z tohoto serveru\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"cze\"\t\t\"Angle-Cheats detekován\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"cze\"\t\t\"Chat-Clear detekován\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"cze\"\t\t\"Nalezen špatný ConVar\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"cze\"\t\t\"Bhop detekován\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"cze\"\t\t\"Aimbot detekován\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"cze\"\t\t\"Aimlock detekován\"\n\t}\n}\n"
  },
  {
    "path": "translations/da/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"da\"\t\t\"Denne server er beskyttet af Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"da\"\t\t\"Fejl: Forespørgelsen fejlede, genstart dit spil hvis dette problem opstår igen\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"da\"\t\t\"Exploit opdaget: Din interp er for høj ({1}ms / {2}ms max). Venlist sæt din cl_interp tilbage til {3} eller lavere\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"da\"\t\t\"Din ping er for høj ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"da\"\t\t\"Du er blevet banned fra denne server\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"da\"\t\t\"Vinkel-Cheats Opdaget\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"da\"\t\t\"Chat-Clear Opdaget\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"da\"\t\t\"Ugyldig ConVar Opdaget\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"da\"\t\t\"NoLerp Opdaget\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"da\"\t\t\"Bhop Opdaget\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"da\"\t\t\"Aimbot Opdaget\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"da\"\t\t\"Aimlock Opdaget\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"da\" \t\t\"Anti-Duck-Forsinkelse Opdaget\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"da\" \t\t\"Uendelig Støj Generator Opdaget\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"da\"\t\t\"Macro Opdaget\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"da\"\t\t\"{1} Macro er ikke tilladt på denne server\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"da\" \t\t\"Din chat besked er blevet blokeret (Årsag: Ugyldige karakterer)\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"da\"\t\t\"Din chat besked er blevet blokeret (Årsag: Wide-Char Spam)\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"da\"\t\t\"Dit navn indeholder ugyldige karakterer, venligst ændre dit navn\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"da\"\t\t\"Navn Newlines Opdaget\"\n\t}\n}\n"
  },
  {
    "path": "translations/de/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n        \"welcome_msg\"\n        {\n                \"#format\"        \"{1:s}\"\n                \"de\"             \"Dieser Server wird von Little Anti-Cheat geschützt {1}\"\n        }\n\n        \"kick_query_failure\"\n        {\n                \"de\"             \"Fehler: Anfrage konnte nicht bearbeitet werden, bitte starten Sie Ihr Spiel neu, falls dieses Problem weiterhin besteht\"\n        }\n\n        \"kick_interp_exploit\"\n        {\n                \"#format\"       \"{1:.0f},{2:d},{3:.3f}\"\n                \"de\"            \"Exploit entdeckt: Ihr Interp ist zu hoch ({1}ms / {2}ms max). Bitte stellen Sie Ihre cl_interp zurück auf {3} oder niedriger ein\"\n        }\n\n        \"tban_ping_high\"\n        {\n                \"#format\"       \"{1:.0f},{2:d}\"\n                \"de\"            \"Ihr Ping ist zu hoch ({1} / {2} max)\"\n        }\n\n        \"kick_ban_generic\"\n        {\n                \"de\"            \"Sie wurden von diesem Server ausgeschlossen\"\n        }\n\n        \"ban_angle\"\n        {\n                \"de\"            \"Angle-Cheats entdeckt\"\n        }\n\n        \"ban_chat_clear\"\n        {\n                \"de\"            \"Chat-Cleaner entdeckt\"\n        }\n\n        \"ban_convar\"\n        {\n                \"de\"            \"Manipulierte ConVar entdeckt\"\n        }\n\n        \"ban_bhop\"\n        {\n                \"de\"            \"BHop Script entdeckt\"\n        }\n\n        \"ban_aimbot\"\n        {\n                \"de\"            \"Aimbot entdeckt\"\n        }\n\n        \"ban_aimlock\"\n        {\n                \"de\"            \"Aimlock entdeckt\"\n        }\n}\n"
  },
  {
    "path": "translations/es/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"es\"\t\t\"El servidor está protegido por Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"es\"\t\t\"Error: No hay respuesta en la consulta, reinicia el juego si el problema continúa.\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"es\"\t\t\"Detectado Exploit: Tu Interp es demasiado alto ({1}ms / {2}ms máx). Por favor, ajusta el comando cl_interp a {3} o por debajo\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"es\"\t\t\"Tu Ping es demasiado alto ({1} / {2} máx)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"es\"\t\t\"Has sido prohibido en este servidor\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"es\"\t\t\"Detectado Angle-Cheats\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"es\"\t\t\"Detectado Chat-Cleaner\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"es\"\t\t\"Detectado ConVar no permitido\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"es\"\t\t\"Detectado NoLerp\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"es\"\t\t\"Detectado Bhop\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"es\"\t\t\"Detectado Aimbot\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"es\"\t\t\"Detectado Aimlock\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"es\" \t\t\"Detectado Anti-Duck-Delay\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"es\" \t\t\"Detectado Noisemaker infinito\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"es\"\t\t\"Detectada Macro\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"es\"\t\t\"{1} las macros no están permitidas en este servidor\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"es\" \t\t\"Mensaje bloqueado en el chat, detectados caracteres inválidos\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"es\"\t\t\"Mensaje bloqueado en el chat, detectado spam de caracteres\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"es\"\t\t\"Tu nombre contiene caracteres inválidos, por favor cámbiate nombre\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"es\"\t\t\"Detectado Newlines en el nombre\"\n\t}\n}\n"
  },
  {
    "path": "translations/fi/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"fi\"\t\t\"Tämä palvelin on suojattu Little Anti-Cheatilla {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"fi\"\t\t\"Error: Query response häiriö. Käynnistä pelisi uudelleen, jos ongelma jatkuu\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"fi\"\t\t\"Exploit havaittu: sinun interp on liian korkea ({1}ms / {2}ms max). Aseta cl_interp takaisin {3} tai matalemmaksi\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"fi\"\t\t\"Viiveesi on liian korkea ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"fi\"\t\t\"Sinut on bannattu tältä palvelimelta\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"fi\"\t\t\"Angle-Cheats havaittu\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"fi\"\t\t\"Chat-Clear havaittu\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"fi\"\t\t\"Invalid ConVar havaittu\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"fi\"\t\t\"NoLerp havaittu\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"fi\"\t\t\"Bhop havaittu\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"fi\"\t\t\"Aimbot havaittu\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"fi\"\t\t\"Aimlock havaittu\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"fi\" \t\t\"Anti-Duck-Delay havaittu\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"fi\" \t\t\"Infinite Noise Maker havaittu\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"fi\"\t\t\"Macro havaittu\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"fi\"\t\t\"{1} Macro ei ole sallittu tällä palvelimella\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"fi\" \t\t\"Sinun chat-viestisi on estetty (Syy: Tuntemattomia merkkejä).\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"fi\"\t\t\"Sinun chat-viestisi on estetty (Syy: Wide-Char Spam).\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"fi\"\t\t\"Sinun nimesi sisältää tuntemattomia merkkejä. Vaihda nimesi\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"fi\"\t\t\"Nimen Newlineja havaittu\"\n\t}\n}\n"
  },
  {
    "path": "translations/fr/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"fr\"\t\t\"Ce serveur est protégé par Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"fr\"\t\t\"Erreur: Echec de la réponse à une requète, redémarrez votre jeu si cette erreur persiste\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"fr\"\t\t\"Exploit détecté: Votre interp est trop élevé ({1}ms / {2}ms max). Fixez votre cl_interp à {3} ou moins\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"fr\"\t\t\"Votre ping est trop élevé ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"fr\"\t\t\"Vous avez été bannis du serveur\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"fr\"\t\t\"Angle-Cheats détecté\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"fr\"\t\t\"Suppression du chat détecté (chat-clear)\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"fr\"\t\t\"ConVar invalide détecté\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"fr\"\t\t\"Bhop détecté\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"fr\"\t\t\"Aimbot détecté\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"fr\"\t\t\"Aimlock détecté\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"fr\" \t\t\"Anti délai d'accroupissement détecté\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"fr\" \t\t\"Spam deGénérateur de bruit infini détecté\"\n\t}\n}\n"
  },
  {
    "path": "translations/hu/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"hu\"\t\t\"Ezen szerver a Little Anti-Cheat {1} védelme alatt áll.\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"hu\"\t\t\"Lekérdezés válasz hiba. Kérlek indítsd újra a játékot, amennyiben ez nem szűnik meg.\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"hu\"\t\t\"Túl magas az interp-ed ({1}ms), a maximum megengedett érték: {2}ms. Kérlek állítsd át {3}-ra vagy kevesebbre. (Pl.: cl_interp 0)\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"hu\"\t\t\"A pinged túl magas. ({1}) A maximális megengedett érték: {2}.\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"hu\"\t\t\"Banolva lettél erről a szerverről.\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"hu\"\t\t\"Angle-Cheat Detektálva.\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"hu\"\t\t\"Chat-Clear Detektálva.\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"hu\"\t\t\"Invalid ConVar Detektálva.\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"hu\"\t\t\"Bhop Detektálva.\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"hu\"\t\t\"Aimbot Detektálva.\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"hu\"\t\t\"Aimlock Detektálva.\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"hu\" \t\t\"Anti-Duck-Delay Detektálva.\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"hu\" \t\t\"Infinite Noisemaker Detektálva.\"\n\t}\n}\n"
  },
  {
    "path": "translations/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"en\"\t\t\"This server is protected by Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"en\"\t\t\"Error: Query response failure, please restart your game if this issue persists\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"en\"\t\t\"Exploit detected: Your interp is too high ({1}ms / {2}ms max). Please set your cl_interp back to {3} or lower\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"en\"\t\t\"Your ping is too high ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"en\"\t\t\"You have been banned from this server\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"en\"\t\t\"Angle-Cheats Detected\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"en\"\t\t\"Chat-Clear Detected\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"en\"\t\t\"Invalid ConVar Detected\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"en\"\t\t\"NoLerp Detected\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"en\"\t\t\"Bhop Detected\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"en\"\t\t\"Aimbot Detected\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"en\"\t\t\"Aimlock Detected\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"en\" \t\t\"Anti-Duck-Delay Detected\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"en\" \t\t\"Infinite Noise Maker Detected\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"en\"\t\t\"Macro Detected\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"en\"\t\t\"{1} Macro is not allowed on this server\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"en\" \t\t\"Your chat message has been blocked (Reason: Invalid Characters).\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"en\"\t\t\"Your chat message has been blocked (Reason: Wide-Char Spam).\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"en\"\t\t\"Your name contains invalid characters, please change your name\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"en\"\t\t\"Name Newlines Detected\"\n\t}\n\n\t\"admin_chat_warning_generic\"\n\t{\n\t\t\"#format\"\t\"{1:s},{2:s},{3:d}\"\n\t\t\"en\"\t\t\"{1} is suspected of using '{2}' (Detections: {3})\"\n\t}\n}\n"
  },
  {
    "path": "translations/lv/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"lv\"\t\t\"Šis serveris izmanto Little Anti-Cheat aizsardzību {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"lv\"\t\t\"Kļūda: Query response failure, Restartē spēli ja šī problēma turpinās\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"lv\"\t\t\"Exploits atklāts: Tavs interp ir pārāk liels ({1}ms / {2}ms max). Lūdzu uzstādi cl_interp atpakaļ uz {3} vai zemāk\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"lv\"\t\t\"Tavs pings ir pārāk liels ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"lv\"\t\t\"Tu tiki banots no šī servera\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"lv\"\t\t\"Angle-Cheats Atklāts\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"lv\"\t\t\"Chat-Clear Atklāts\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"lv\"\t\t\"Invalid ConVar Atklāts\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"lv\"\t\t\"NoLerp Atklāts\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"lv\"\t\t\"Bhop Atklāts\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"lv\"\t\t\"Aimbot Atklāts\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"lv\"\t\t\"Aimlock Atklāts\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"lv\" \t\t\"Anti-Duck-Delay Atklāts\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"lv\" \t\t\"Infinite Noise Maker Atklāts\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"lv\"\t\t\"Macro Atklāts\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"lv\"\t\t\"{1} Macro skriptus aizliegts lietot šajā serverī\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"lv\" \t\t\"Tavas čata ziņas tika bloķētas (Iemesls: Invalid Characters).\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"lv\"\t\t\"Tavas čata ziņas tika bloķētas (Iemesls: Wide-Char Spam).\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"lv\"\t\t\"Tavs nickname satur invalid characters, lūdzu nomaini to\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"lv\"\t\t\"Name Newlines Atklāts\"\n\t}\n\n\t\"admin_chat_warning_generic\"\n\t{\n\t\t\"#format\"\t\"{1:s},{2:s},{3:d}\"\n\t\t\"lv\"\t\t\"{1} Tiek turēts aizdomās par '{2}' (Detections: {3})\"\n\t}\n}\n"
  },
  {
    "path": "translations/nl/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"nl\"\t\t\"Deze server is beveiligd met Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"nl\"\t\t\"Error: Fout bij opvragen van antwoord, start je game opnieuw als dit probleem zich blijft voordoen\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"nl\"\t\t\"Exploit gedetecteerd: Je interp is te hoog ({1}ms / {2}ms max). Zet cl_interp terug naar {3} of lager\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"nl\"\t\t\"Je ping is te hoog ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"nl\"\t\t\"Je bent verbannen van deze server\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"nl\"\t\t\"Ongeldige kijkrichting gedetecteerd\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"nl\"\t\t\"Verwijdering van chat gedetecteerd\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"nl\"\t\t\"Ongeldige ConVar gedetecteerd\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"nl\"\t\t\"Bhop gedetecteerd\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"nl\"\t\t\"Aimbot gedetecteerd\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"nl\"\t\t\"Aimlock gedetecteerd\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"nl\"\t\t\"Snelle squat gedetecteerd\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"nl\"\t\t\"Oneindige noisemaker gedetecteerd\"\n\t}\n}\n"
  },
  {
    "path": "translations/no/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"no\"\t\t\"Denne serveren er beskyttet av Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"no\"\t\t\"Error: Spillet svarte ikke på forespørsler i tide. Start spillet på nytt om problemet fortsetter\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"no\"\t\t\"Exploit oppdaget: Interpen din er for høy ({1}ms / {2}ms maks). Sett ned din cl_interp til {3} eller lavere\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"no\"\t\t\"Nettverk forsinkelsen (Ping) din er for høy ({1} / {2} maks)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"no\"\t\t\"Du har blitt utestengt fra denne serveren på grunn av juksing\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"no\"\t\t\"Ugyldig synsrettning oppdaget\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"no\"\t\t\"Fjerning av chat oppdaget\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"no\"\t\t\"Ugyldig console verdi oppdaget\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"no\"\t\t\"Bhop oppdaget\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"no\"\t\t\"Aimbot oppdaget\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"no\"\t\t\"Aimlock oppdaget\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"no\" \t\t\"Antidukk-forsinkelse oppdaget\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"no\" \t\t\"Uendelig Noisemaker-bruk oppdaget\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"no\"\t\t\"Macro oppdaget\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"no\"\t\t\"{1} Macro er ikke tillatt på denne serveren\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"no\" \t\t\"Chat melding blokkert, fjern ugyldige bokstaver og prøv igjen\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"no\"\t\t\"Chat melding blokkert, lang-bokstav spam oppdaget\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"no\"\t\t\"Navnet ditt har ugyldige bokstaver, vennligst endre navnet ditt\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"no\"\t\t\"Ny-linje bokstav i navnet oppdaget\"\n\t}\n\n\t\"admin_chat_warning_generic\"\n\t{\n\t\t\"#format\"\t\"{1:s},{2:s},{3:d}\"\n\t\t\"no\"\t\t\"{1} mistenkes for å bruke '{2}' (Oppdagelser: {3})\"\n\t}\n}\n"
  },
  {
    "path": "translations/pl/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"pl\"\t\t\"Ten serwer jest chroniony przez Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"pl\"\t\t\"Błąd: Błąd odpowiedzi na zapytanie, zrestartuj grę jeśli problem będzie się powtarzał\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto exploita: Twój interp jest za wysoki ({1}ms / {2}ms maks). Ustaw cl_interp z powrotem na {3} lub niższy\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"pl\"\t\t\"Twój ping jest za wysoki ({1} / {2} maks)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"pl\"\t\t\"Zostałeś zbanowany na tym serwerze\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Angle-Cheats\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Chat-Clear\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Nieprawidłowy ConVar\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Bhop\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Aimbot\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Aimlock\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Anti-Duck-Delay\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"pl\"\t\t\"Wykryto Nieskończony Zagłuszacz\"\n\t}\n}\n"
  },
  {
    "path": "translations/pt/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"pt\"\t\t\"Este servidor é protegido pelo Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"pt\"\t\t\"Erro: o seu cliente falhou ao responder o pedido. Reinicie o seu jogo caso este problema persista\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"pt\"\t\t\"Brecha detectada: a sua interpolação está muito alta ({1}ms / máx. {2}ms). Defina a variável do console \\\"cl_interp\\\" de volta para {3} ou um valor menor\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"pt\"\t\t\"A sua latência está muito alta ({1} / máx. {2})\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"pt\"\t\t\"Você foi banido(a) deste servidor\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"pt\"\t\t\"Trapaças relacionadas a ângulos detectadas\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"pt\"\t\t\"Limpador da janela de conversa detectado\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"pt\"\t\t\"Variável do console inválida detectada\"\n\t}\n\t\n\t\"ban_nolerp\"\n\t{\n\t\t\"pt\"\t\t\"Anulação de interpolação detectada\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"pt\"\t\t\"Bunny hop detectado\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"pt\"\t\t\"Auxílio de mira (aimbot) detectado\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"pt\"\t\t\"Auxílio de mira (aimlock) detectado\"\n\t}\n\t\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"pt\" \t\t\"Remoção do atraso para agachar detectado\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"pt\" \t\t\"Brinquedo Barulhento infinito detectado\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"pt\"\t\t\"Macro detectado\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"pt\"\t\t\"O macro do tipo \\\"{1}\\\" não é permitido neste servidor\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"pt\" \t\t\"A sua mensagem na janela de conversa foi bloqueada por conter caracteres inválidos.\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"pt\"\t\t\"A sua mensagem na janela de conversa foi bloqueada por conter caracteres de spam espaçosos.\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"pt\"\t\t\"O seu nome contém caracteres inválidos. Altere-o\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"pt\"\t\t\"Nome com quebras de linha detectado\"\n\t}\n\n\t\"admin_chat_warning_generic\"\n\t{\n\t\t\"pt\"\t\t\"{1} possivelmente está usando \\\"{2}\\\". (Nº de detecções: {3}.)\"\n\t}\n}\n"
  },
  {
    "path": "translations/ro/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"ro\"\t\t\"Acest server este protejat de Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"ro\"\t\t\"Eroare: Interogare eșuată, te rog sa repornești jocul dacă această problemă persistă\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"ro\"\t\t\"Exploit detectat: interp-ul tău este prea mare ({1}ms / {2}ms max). Te rog sa îți setezi cl_interp înapoi la {3} sau mai mic\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"ro\"\t\t\"Ping-ul tău este prea mare ({1} / {2} max)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"ro\"\t\t\"Ai fost banat pe acest server\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"ro\"\t\t\"Angle-Cheats Detectat\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"ro\"\t\t\"Chat-Clear Detectat\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"ro\"\t\t\"Invalid ConVar Detectat\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"ro\"\t\t\"NoLerp Detectat\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"ro\"\t\t\"Bhop Detectat\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"ro\"\t\t\"Aimbot Detectat\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"ro\"\t\t\"Aimlock Detectat\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"ro\" \t\t\"Anti-Duck-Delay Detectat\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"ro\" \t\t\"Infinite Noise Maker Detectat\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"ro\"\t\t\"Macro Detectat\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"ro\"\t\t\"{1} Macro nu este permis pe acest server\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"ro\" \t\t\"Mesajul tău din chat a fost blocat (Motiv: Caractere Invalide).\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"ro\"\t\t\"Mesajul tău din chat a fost blocat (Motiv: Spam cu caractere largi).\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"ro\"\t\t\"Numele tău conține caractere invalide, te rog să iți schimbi numele.\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"ro\"\t\t\"Au fost detectate noi linii în nume\"\n\t}\n\n\t\"admin_chat_warning_generic\"\n\t{\n\t\t\"#format\"\t\"{1:s},{2:s},{3:d}\"\n\t\t\"ro\"\t\t\"{1} este suspect de folosire  '{2}' (Detecții: {3})\"\n\t}\n}\n"
  },
  {
    "path": "translations/ru/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"ru\"\t\t\"Этот сервер защищён Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"ru\"\t\t\"Ошибка: клиент не отвечает на запросы сервера, перезапустите игру если проблема продолжается\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"ru\"\t\t\"Обнаружен эксплоит: Слишком большое значение interp ({1}ms / {2}ms max). Пожалуйста установите cl_interp на {3} или ниже\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"ru\"\t\t\"Ваш пинг слишком большой ({1} / {2} макс)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"ru\"\t\t\"Вы были забанены на этом сервере\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"ru\"\t\t\"Обнаружены недопустимые углы обзора\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"ru\"\t\t\"Обнаружено очищение чата\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"ru\"\t\t\"Обнаружено недопустимое значение консольной переменной\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"ru\"\t\t\"Обнаружен Bhop\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"ru\"\t\t\"Обнаружен Aimbot\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"ru\"\t\t\"Обнаружен Aimlock\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"ru\" \t\t\"Обнаружен обход задержки приседания\"\n\t}\n}\n"
  },
  {
    "path": "translations/sv/lilac.phrases.txt",
    "content": "\"Phrases\"\r\n{\r\n\t\"welcome_msg\"\r\n\t{\r\n\t\t\"se\"\t\t\"Denna server är säkrad av Little Anti-Cheat {1}\"\r\n\t}\r\n\r\n\t\"kick_query_failure\"\r\n\t{\r\n\t\t\"se\"\t\t\"Fel: Frågesvar misslyckades, var god starta om ditt spel om detta problem fortsätter.\"\r\n\t}\r\n\r\n\t\"kick_interp_exploit\"\r\n\t{\r\n\t\t\"se\"\t\t\"Exploit Upptäckt: Din interp är för hög ({1}ms / {2}ms max). Var god sätt din cl_interp tillbaka till {3} eller lägre\"\r\n\t}\r\n\r\n\t\"tban_ping_high\"\r\n\t{\r\n\t\t\"se\"\t\t\"Din ping är för hög ({1} / {2} max)\"\r\n\t}\r\n\r\n\t\"kick_ban_generic\"\r\n\t{\r\n\t\t\"se\"\t\t\"Du har blivit bannlyst från denna server\"\r\n\t}\r\n\r\n\t\"ban_angle\"\r\n\t{\r\n\t\t\"se\"\t\t\"Vinkel-Fusk Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_chat_clear\"\r\n\t{\r\n\t\t\"se\"\t\t\"Chatt-Rensning Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_convar\"\r\n\t{\r\n\t\t\"se\"\t\t\"Ogiltig ConVar Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_nolerp\"\r\n\t{\r\n\t\t\"se\"\t\t\"NoLerp Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_bhop\"\r\n\t{\r\n\t\t\"se\"\t\t\"Bhop Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_aimbot\"\r\n\t{\r\n\t\t\"se\"\t\t\"Aimbot Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_aimlock\"\r\n\t{\r\n\t\t\"se\"\t\t\"Aimlock Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_anti_duck_delay\"\r\n\t{\r\n\t\t\"se\" \t\t\"Anti-Duck-Fördröjning Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_noisemaker\"\r\n\t{\r\n\t\t\"se\" \t\t\"Oändlig Noisemaker Upptäckt\"\r\n\t}\r\n\r\n\t\"ban_macro\"\r\n\t{\r\n\t\t\"se\"\t\t\"Macro Upptäckt\"\r\n\t}\r\n\r\n\t\"kick_macro\"\r\n\t{\r\n\t\t\"#format\"\t\"{1:s}\"\r\n\t\t\"se\"\t\t\"{1} Macro är inte tillåtet i denna server.\"\r\n\t}\r\n\r\n\t\"chat_invalid_characters\"\r\n\t{\r\n\t\t\"se\" \t\t\"Chatt meddelande blockerad, ogiltiga karaktärer upptäckt.\"\r\n\t}\r\n\r\n\t\"chat_wide_char_spam\"\r\n\t{\r\n\t\t\"se\"\t\t\"Chatt meddelande blockerad, bred-karaktär spam upptäckt.\"\r\n\t}\r\n\r\n\t\"kick_bad_name\"\r\n\t{\r\n\t\t\"se\"\t\t\"Ditt namn innehåller ogiltiga karaktärer, var god ändra ditt namn.\"\r\n\t}\r\n\r\n\t\"ban_name_newline\"\r\n\t{\r\n\t\t\"se\"\t\t\"Namn-radbrytningar Upptäckt\"\r\n\t}\r\n}\r\n"
  },
  {
    "path": "translations/tr/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"tr\"\t\t\"Bu sunucu Little Anti-Cheat {1} tarafından korunmaktadır\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"tr\"\t\t\"Hata: Query yanıtı hatası, bu sorun devam ederse lütfen oyununuzu yeniden başlatın\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"tr\"\t\t\"Exploit Algılandı: Interp çok yüksek (en fazla {1}ms / {2}ms). Lütfen cl_interp değerinizi {3} veya daha düşük bir değere ayarlayın\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"tr\"\t\t\"Pinginiz çok yüksek (en fazla {1} / {2})\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"tr\"\t\t\"Bu sunucudan yasaklandınız\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"tr\"\t\t\"Angle-Cheats Algılandı\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"tr\"\t\t\"Chat-Clear Algılandı\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"tr\"\t\t\"Geçersiz ConVar Tespit Edildi\"\n\t}\n\n\t\"ban_nolerp\"\n\t{\n\t\t\"tr\"\t\t\"NoLerp Algılandı\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"tr\"\t\t\"Bhop Algılandı\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"tr\"\t\t\"Aimbot Algılandı\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"tr\"\t\t\"Aimlock Algılandı\"\n\t}\n\n\t\"ban_anti_duck_delay\"\n\t{\n\t\t\"tr\" \t\t\"FastDuck Algılandı\"\n\t}\n\n\t\"ban_noisemaker\"\n\t{\n\t\t\"tr\" \t\t\"Sonsuz Gürültücü Tespit Edildi\"\n\t}\n\n\t\"ban_macro\"\n\t{\n\t\t\"tr\"\t\t\"Makro Algılandı\"\n\t}\n\n\t\"kick_macro\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"tr\"\t\t\"{1} Bu sunucuda makroya izin verilmiyor\"\n\t}\n\n\t\"chat_invalid_characters\"\n\t{\n\t\t\"tr\" \t\t\"Sohbet mesajınız engellendi (Nedeni: Geçersiz Karakterler).\"\n\t}\n\n\t\"chat_wide_char_spam\"\n\t{\n\t\t\"tr\"\t\t\"Sohbet mesajınız engellendi (Nedeni: Geniş Karakterli Spam).\"\n\t}\n\n\t\"kick_bad_name\"\n\t{\n\t\t\"tr\"\t\t\"Adınız geçersiz karakterler içeriyor, lütfen adınızı değiştirin\"\n\t}\n\n\t\"ban_name_newline\"\n\t{\n\t\t\"tr\"\t\t\"Adınızda Yeni Satır Algılandı\"\n\t}\n}\n"
  },
  {
    "path": "translations/ua/lilac.phrases.txt",
    "content": "\"Phrases\"\n{\n\t\"welcome_msg\"\n\t{\n\t\t\"#format\"\t\"{1:s}\"\n\t\t\"ua\"\t\t\"Цей сервер захищений Little Anti-Cheat {1}\"\n\t}\n\n\t\"kick_query_failure\"\n\t{\n\t\t\"ua\"\t\t\"Помилка: клієнт не відповідає на запити сервера, перезапустіть гру якщо це буде продовжуватись\"\n\t}\n\n\t\"kick_interp_exploit\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d},{3:.3f}\"\n\t\t\"ua\"\t\t\"виявлено експлоїт: Занадто велике значення interp ({1}ms / {2}ms max). Будь ласка встановіть cl_interp на {3} або нижче\"\n\t}\n\n\t\"tban_ping_high\"\n\t{\n\t\t\"#format\"\t\"{1:.0f},{2:d}\"\n\t\t\"ua\"\t\t\"Ваш пінг занадто великий ({1} / {2} макс)\"\n\t}\n\n\t\"kick_ban_generic\"\n\t{\n\t\t\"ua\"\t\t\"Ви були забанені на цьому сервері\"\n\t}\n\n\t\"ban_angle\"\n\t{\n\t\t\"ua\"\t\t\"Виявлено неприпустимі кути огляду\"\n\t}\n\n\t\"ban_chat_clear\"\n\t{\n\t\t\"ua\"\t\t\"Виявлено очищення чату\"\n\t}\n\n\t\"ban_convar\"\n\t{\n\t\t\"ua\"\t\t\"Виявлено неприпустиме значення консольної змінної\"\n\t}\n\n\t\"ban_bhop\"\n\t{\n\t\t\"ua\"\t\t\"Виявлено Bhop\"\n\t}\n\n\t\"ban_aimbot\"\n\t{\n\t\t\"ua\"\t\t\"Виявлено Aimbot\"\n\t}\n\n\t\"ban_aimlock\"\n\t{\n\t\t\"ua\"\t\t\"Виявлено Aimlock\"\n\t}\n}\n"
  },
  {
    "path": "updatefile.txt",
    "content": "\"Updater\"\r\n{\r\n\t\"Information\"\r\n\t{\r\n\t\t\"Version\"\r\n\t\t{\r\n\t\t\t\"Latest\"\t\"1.7.4\"\r\n\t\t}\r\n\r\n\t\t\"Notes\" \"Removed the need to download other third party includes (SB, SB++, MA and Updater).\"\r\n\t\t\"Notes\" \"Removed Angle checks in L4D1&2.\"\r\n\t\t\"Notes\" \"Added define in lilac.sp for TF2Classic support.\"\r\n\t\t\"Notes\" \"Added support for the old version of SourceBans.\"\r\n\t\t\"Notes\" \"Added admin chat warnings for suspicious players (Aimbot, Aimlock and Bhop).\"\r\n\t\t\"Notes\" \"Fixed a bug where multiple aimbot checks are fired upon getting multiple kills from the same shot.\"\r\n\t\t\"Notes\" \"Removed custom warnings when compiling.\"\r\n\t\t\"Notes\" \"Updated sourcecode to compile in SM 1.11 without warnings.\"\r\n\t}\r\n\r\n\t\"Files\"\r\n\t{\r\n\t\t\"Plugin\"\t\"Path_SM/plugins/lilac.smx\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/chi/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/cze/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/de/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/es/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/fi/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/fr/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/nl/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/no/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/pt/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/ru/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/tr/lilac.phrases.txt\"\r\n\t\t\"Plugin\"\t\"Path_SM/translations/ua/lilac.phrases.txt\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_globals.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_aimbot.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_aimlock.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_angles.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_anti_duck_delay.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_backtrack.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_bhop.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_config.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_convar.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_lerp.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_macro.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_noisemaker.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_ping.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_stock.sp\"\r\n\t\t\"Source\"\t\"Path_SM/scripting/lilac/lilac_string.sp\"\r\n\t}\r\n}\r\n"
  }
]