SprayExploitFixer: initial commit
This commit is contained in:
parent
470d8f9780
commit
b50c8b56af
148
spray_exploit_fixer/gamedata/spray_exploit_fixer.txt
Normal file
148
spray_exploit_fixer/gamedata/spray_exploit_fixer.txt
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
"Games"
|
||||||
|
{
|
||||||
|
"#default"
|
||||||
|
{
|
||||||
|
"Functions"
|
||||||
|
{
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"signature" "CGameClient::FileReceived"
|
||||||
|
"callconv" "thiscall"
|
||||||
|
"return" "int"
|
||||||
|
"this" "address"
|
||||||
|
"arguments"
|
||||||
|
{
|
||||||
|
"a1"
|
||||||
|
{
|
||||||
|
"type" "charptr"
|
||||||
|
}
|
||||||
|
"a2"
|
||||||
|
{
|
||||||
|
"type" "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"#default"
|
||||||
|
{
|
||||||
|
"#supported"
|
||||||
|
{
|
||||||
|
"engine" "css"
|
||||||
|
"engine" "hl2dm"
|
||||||
|
"engine" "tf2"
|
||||||
|
"engine" "zps"
|
||||||
|
}
|
||||||
|
|
||||||
|
"Offsets"
|
||||||
|
{
|
||||||
|
"clients"
|
||||||
|
{
|
||||||
|
"windows" "12"
|
||||||
|
"linux" "12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Signatures"
|
||||||
|
{
|
||||||
|
/* Search: "CGameClient::FileReceived" */
|
||||||
|
/* CGameClient::FileReceived(char const*, unsigned int, bool) */
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"library" "engine"
|
||||||
|
"linux" "@_ZN11CGameClient12FileReceivedEPKcj"
|
||||||
|
"windows" "\x55\x8B\x2A\x56\x8B\x2A\x2A\x33\x2A\x57\x8B\x2A\x8D\x2A\x2A\x2A\x2A\x2A\x39"
|
||||||
|
/* 55 8B ? 56 8B ? ? 33 ? 57 8B ? 8D ? ? ? ? ? 39 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"tf2"
|
||||||
|
{
|
||||||
|
/* TF2 function is void. */
|
||||||
|
"Functions"
|
||||||
|
{
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"return" "void"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"left4dead"
|
||||||
|
{
|
||||||
|
/* L4D has an extra argument. */
|
||||||
|
"Functions"
|
||||||
|
{
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"arguments"
|
||||||
|
{
|
||||||
|
"a3"
|
||||||
|
{
|
||||||
|
"type" "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Offsets"
|
||||||
|
{
|
||||||
|
"clients"
|
||||||
|
{
|
||||||
|
"windows" "48"
|
||||||
|
"linux" "48"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Signatures"
|
||||||
|
{
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"library" "engine"
|
||||||
|
"linux" "@_ZN11CGameClient12FileReceivedEPKcjb"
|
||||||
|
"windows" "\x56\x8B\x2A\x2A\x2A\x33\x2A\x8D\x2A\x2A\x2A\x2A\x2A\x8D"
|
||||||
|
/* 56 8B ? ? ? 33 ? 8D ? ? ? ? ? 8D */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"left4dead2"
|
||||||
|
{
|
||||||
|
/* L4D2 has an extra argument. */
|
||||||
|
"Functions"
|
||||||
|
{
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"arguments"
|
||||||
|
{
|
||||||
|
"a3"
|
||||||
|
{
|
||||||
|
"type" "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Offsets"
|
||||||
|
{
|
||||||
|
"clients"
|
||||||
|
{
|
||||||
|
"windows" "48"
|
||||||
|
"linux" "48"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"Signatures"
|
||||||
|
{
|
||||||
|
"CGameClient::FileReceived"
|
||||||
|
{
|
||||||
|
"library" "engine"
|
||||||
|
"linux" "@_ZN11CGameClient12FileReceivedEPKcjb"
|
||||||
|
"windows" "\x55\x8B\x2A\x56\x8B\x2A\x2A\x33\x2A\x8D\x2A\x2A\x2A\x2A\x2A\x90"
|
||||||
|
/* 55 8B ? 56 8B ? ? 33 ? 8D ? ? ? ? ? 90 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
403
spray_exploit_fixer/scripting/spray_exploit_fixer.sp
Normal file
403
spray_exploit_fixer/scripting/spray_exploit_fixer.sp
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
#define PLUGIN_VERSION "1377"
|
||||||
|
|
||||||
|
/*=======================================================================================
|
||||||
|
Plugin Info:
|
||||||
|
|
||||||
|
* Name : [ANY] Spray Exploit Fixer
|
||||||
|
* Author : SilverShot
|
||||||
|
* Descrp : Deletes bad sprays and prevents them from crashing clients.
|
||||||
|
* Link : https://forums.alliedmods.net/showthread.php?t=323447
|
||||||
|
* Plugins : https://sourcemod.net/plugins.php?exact=exact&sortby=title&search=1&author=Silvers
|
||||||
|
|
||||||
|
========================================================================================
|
||||||
|
Change Log:
|
||||||
|
|
||||||
|
1.5 (14-May-2020)
|
||||||
|
- Added better error log message when gamedata file is missing.
|
||||||
|
- Fixed gamedata for HL2:DM. Thanks to "CliptonHeist" for reporting and "asherkin" for explaining engine != game.
|
||||||
|
- (Info: the gamedata "engine" key for HL2:DM uses "hl2dm" (the engine name) while the "game" part uses "hl2mp" (game name) e.g. for offsets).
|
||||||
|
|
||||||
|
1.4 (10-May-2020)
|
||||||
|
- Added support for "Zombie Panic! Source" game. Requires gamedata update.
|
||||||
|
- Fixed "sm_spray_test" timing out when checking many sprays. Thanks to "Sreaper" for reporting and testing.
|
||||||
|
- Now checks 50 files and waits 0.1 seconds before checking the next batch.
|
||||||
|
- TF2 updated to fix clients crashing, but this plugin is still recommended to delete the other randomly uploaded user files.
|
||||||
|
|
||||||
|
1.3 (26-Apr-2020)
|
||||||
|
- Changed cvar "spray_exploit_fixer_log" to log all files or only invalid sprays.
|
||||||
|
- Logging now saves to "sourcemod/logs/spray_downloads.log" file.
|
||||||
|
|
||||||
|
1.2 (23-Apr-2020)
|
||||||
|
- Added better checks to detect more bad sprays.
|
||||||
|
- Added better checks for TF2 and other games to avoid false positives.
|
||||||
|
- Prevented banning people in TF2 since many random invalid files are sent, not just sprays.
|
||||||
|
|
||||||
|
1.1 (21-Apr-2020)
|
||||||
|
- Added better checks to prevent false positives.
|
||||||
|
- Added ability to detect the users uploading sprays or other files.
|
||||||
|
- Added cvar "spray_exploit_fixer_ban" to ban players with invalid sprays.
|
||||||
|
- Added cvar "spray_exploit_fixer_log" to log players and files they uploaded.
|
||||||
|
- Changed "sm_spray_test" to allow recursive searching the downloads directory.
|
||||||
|
- Fixed plugin crashing TF2.
|
||||||
|
- Updated GameData required.
|
||||||
|
|
||||||
|
1.0 (20-Apr-2020)
|
||||||
|
- Initial release.
|
||||||
|
|
||||||
|
======================================================================================*/
|
||||||
|
|
||||||
|
#pragma semicolon 1
|
||||||
|
#pragma newdecls required
|
||||||
|
|
||||||
|
#include <sourcemod>
|
||||||
|
#include <sdktools>
|
||||||
|
#include <dhooks>
|
||||||
|
|
||||||
|
#define GAMEDATA "spray_exploit_fixer"
|
||||||
|
#define MAX_READ 50
|
||||||
|
|
||||||
|
int g_iVal[] = {86,84,70,0,7,0,0,0,42,0,0,0,42,0,0,0,42,42,42,42,42,42,42,42,42,42,42,0};
|
||||||
|
char g_sFilename[PLATFORM_MAX_PATH];
|
||||||
|
EngineVersion g_iEngine;
|
||||||
|
|
||||||
|
public Plugin myinfo =
|
||||||
|
{
|
||||||
|
name = "[ANY] Spray Exploit Fixer",
|
||||||
|
author = "SilverShot + Neon",
|
||||||
|
description = "Deletes bad sprays and prevents them from crashing clients.",
|
||||||
|
version = PLUGIN_VERSION,
|
||||||
|
url = "https://forums.alliedmods.net/showthread.php?t=323447"
|
||||||
|
}
|
||||||
|
|
||||||
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
||||||
|
{
|
||||||
|
g_iEngine = GetEngineVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPluginStart()
|
||||||
|
{
|
||||||
|
char sPath[PLATFORM_MAX_PATH];
|
||||||
|
BuildPath(Path_SM, sPath, sizeof(sPath), "gamedata/%s.txt", GAMEDATA);
|
||||||
|
if( FileExists(sPath) == false ) SetFailState("\n==========\nMissing required file: \"%s\".\nRead installation instructions again.\n==========", sPath);
|
||||||
|
|
||||||
|
Handle hGameData = LoadGameConfigFile(GAMEDATA);
|
||||||
|
if( hGameData == null ) SetFailState("Failed to load \"%s.txt\" gamedata.", GAMEDATA);
|
||||||
|
|
||||||
|
Handle hDetour = DHookCreateFromConf(hGameData, "CGameClient::FileReceived");
|
||||||
|
|
||||||
|
if( !hDetour )
|
||||||
|
SetFailState("Failed to find \"CGameClient::FileReceived\" signature.");
|
||||||
|
if( !DHookEnableDetour(hDetour, false, FileReceived) )
|
||||||
|
SetFailState("Failed to detour \"CGameClient::FileReceived\".");
|
||||||
|
if( !DHookEnableDetour(hDetour, true, FileReceivedPost) )
|
||||||
|
SetFailState("Failed to detour \"CGameClient::FileReceived\" post.");
|
||||||
|
|
||||||
|
delete hDetour;
|
||||||
|
delete hGameData;
|
||||||
|
|
||||||
|
RegAdminCmd("sm_spray_test", CmdSprays, ADMFLAG_RCON, "Tests all sprays in the games downloads folder, listing bad ones.");
|
||||||
|
|
||||||
|
CreateConVar( "spray_exploit_fixer", PLUGIN_VERSION, "Spray Exploit Fixer plugin version.", FCVAR_DONTRECORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
float g_fTime;
|
||||||
|
public Action CmdSprays(int client, int a)
|
||||||
|
{
|
||||||
|
bool recurse = g_iEngine != Engine_Left4Dead && g_iEngine != Engine_Left4Dead2;
|
||||||
|
int count, counts;
|
||||||
|
|
||||||
|
g_fTime = GetEngineTime();
|
||||||
|
RecursiveSearchDirs(client, recurse, recurse ? "download" : "downloads", count, counts, 0, null);
|
||||||
|
|
||||||
|
return Plugin_Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action TimerNext(Handle timer, DataPack dPack)
|
||||||
|
{
|
||||||
|
DirectoryListing hDir;
|
||||||
|
char sDir[PLATFORM_MAX_PATH];
|
||||||
|
int client, count, counts, level;
|
||||||
|
bool recurse;
|
||||||
|
|
||||||
|
dPack.Reset();
|
||||||
|
client = dPack.ReadCell();
|
||||||
|
recurse = dPack.ReadCell();
|
||||||
|
dPack.ReadString(sDir, sizeof(sDir));
|
||||||
|
count = dPack.ReadCell();
|
||||||
|
counts = dPack.ReadCell();
|
||||||
|
level = dPack.ReadCell();
|
||||||
|
hDir = dPack.ReadCell();
|
||||||
|
|
||||||
|
RecursiveSearchDirs(client, recurse, sDir, count, counts, level, hDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecursiveSearchDirs(int client, bool recurse, const char[] sDir, int &count, int &counts, int level, DirectoryListing hDir)
|
||||||
|
{
|
||||||
|
char sPath[PLATFORM_MAX_PATH];
|
||||||
|
FileType type;
|
||||||
|
File hFile;
|
||||||
|
int iRead[sizeof(g_iVal)];
|
||||||
|
int total;
|
||||||
|
level++;
|
||||||
|
|
||||||
|
if( hDir == null )
|
||||||
|
hDir = OpenDirectory(sDir, true);
|
||||||
|
|
||||||
|
while( hDir.GetNext(sPath, sizeof(sPath), type) )
|
||||||
|
{
|
||||||
|
if( strcmp(sPath, ".") && strcmp(sPath, "..") )
|
||||||
|
{
|
||||||
|
if( type == FileType_Directory && recurse )
|
||||||
|
{
|
||||||
|
Format(sPath, sizeof(sPath), "%s/%s", sDir, sPath);
|
||||||
|
RecursiveSearchDirs(client, recurse, sPath, count, counts, level, null);
|
||||||
|
}
|
||||||
|
else if( type == FileType_File )
|
||||||
|
{
|
||||||
|
int len = strlen(sPath);
|
||||||
|
if( len > 4 && strcmp(sPath[len - 4], ".dat") == 0 )
|
||||||
|
{
|
||||||
|
counts++;
|
||||||
|
Format(sPath, sizeof(sPath), "%s/%s", sDir, sPath);
|
||||||
|
|
||||||
|
hFile = OpenFile(sPath, "rb");
|
||||||
|
hFile.Read(iRead, sizeof(iRead), 1);
|
||||||
|
delete hFile;
|
||||||
|
|
||||||
|
int i = ValFile(iRead);
|
||||||
|
if( i != -1 )
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
PrintToServer("Invalid file: %s: %02d (%02X <> %02X)", sPath, i, iRead[i], g_iVal[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( total++ > MAX_READ )
|
||||||
|
{
|
||||||
|
level--;
|
||||||
|
DataPack dPack;
|
||||||
|
CreateDataTimer(0.1, TimerNext, dPack);
|
||||||
|
dPack.WriteCell(client);
|
||||||
|
dPack.WriteCell(recurse);
|
||||||
|
dPack.WriteString(sDir);
|
||||||
|
dPack.WriteCell(count);
|
||||||
|
dPack.WriteCell(counts);
|
||||||
|
dPack.WriteCell(level);
|
||||||
|
dPack.WriteCell(hDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete hDir;
|
||||||
|
level--;
|
||||||
|
|
||||||
|
if( level == 0 )
|
||||||
|
ReplyToCommand(client, "Sprays checked. Found %d of %d invalid. Took %0.1f seconds.", count, counts, GetEngineTime() - g_fTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MRESReturn FileReceived(int pThis, Handle hReturn, Handle hParams)
|
||||||
|
{
|
||||||
|
char sTemp[PLATFORM_MAX_PATH - 10];
|
||||||
|
DHookGetParamString(hParams, 1, sTemp, sizeof(sTemp));
|
||||||
|
Format(g_sFilename, sizeof(g_sFilename), "download/%s", sTemp);
|
||||||
|
|
||||||
|
if( FileExists(g_sFilename) )
|
||||||
|
{
|
||||||
|
int len = strlen(g_sFilename);
|
||||||
|
if( len > 4 && strcmp(g_sFilename[len - 4], ".dat") == 0 )
|
||||||
|
{
|
||||||
|
int iRead[sizeof(g_iVal)];
|
||||||
|
File hFile = OpenFile(g_sFilename, "rb", false);
|
||||||
|
hFile.Read(iRead, sizeof(iRead), 1);
|
||||||
|
delete hFile;
|
||||||
|
|
||||||
|
int i = ValFile(iRead);
|
||||||
|
if( i != -1 )
|
||||||
|
{
|
||||||
|
LogCustom("Deleted invalid spray: %s", g_sFilename);
|
||||||
|
PrintToServer("Invalid file: %s: %02d (%02X <> %02X)", g_sFilename, i, iRead[i], g_iVal[i]);
|
||||||
|
|
||||||
|
DeleteFile(g_sFilename);
|
||||||
|
|
||||||
|
for (int j = 1; j <= MaxClients; j++)
|
||||||
|
{
|
||||||
|
if (IsValidClient(j))
|
||||||
|
{
|
||||||
|
char sTemp2[PLATFORM_MAX_PATH - 4];
|
||||||
|
GetPlayerDecalFile(j, sTemp2, sizeof(sTemp2));
|
||||||
|
Format(sTemp2, sizeof(sTemp2), "%s.dat", sTemp2);
|
||||||
|
if (StrEqual(sTemp2, g_sFilename[24], false))
|
||||||
|
{
|
||||||
|
char auth[20];
|
||||||
|
GetClientAuthId(j, AuthId_Steam2, auth, sizeof(auth));
|
||||||
|
KickClient(j, "Please change your spray");
|
||||||
|
LogAction(j, -1, "\"%L\" is possibly using a bad spray. Client got kicked and spray got deleted.", j);
|
||||||
|
LogCustom("Deleted invalid spray: %s from (%N) [%s]", sTemp2, j, auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DHookSetReturn(hReturn, 0);
|
||||||
|
return MRES_Supercede;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MRES_Ignored;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MRESReturn FileReceivedPost(int pThis, Handle hReturn, Handle hParams)
|
||||||
|
{
|
||||||
|
/*int client;
|
||||||
|
|
||||||
|
if( FileExists(g_sFilename) )
|
||||||
|
{
|
||||||
|
int len = strlen(g_sFilename);
|
||||||
|
if( len > 4 && strcmp(g_sFilename[len - 4], ".dat") == 0 )
|
||||||
|
{
|
||||||
|
int iRead[sizeof(g_iVal)];
|
||||||
|
File hFile = OpenFile(g_sFilename, "rb", false);
|
||||||
|
hFile.Read(iRead, sizeof(iRead), 1);
|
||||||
|
delete hFile;
|
||||||
|
|
||||||
|
int i = ValFile(iRead);
|
||||||
|
if( i != -1 )
|
||||||
|
{
|
||||||
|
if( !client && g_iOff != -1 )
|
||||||
|
{
|
||||||
|
client = LoadFromAddress(view_as<Address>(pThis + g_iOff), NumberType_Int8);
|
||||||
|
if( client < 1 || client > MaxClients || !IsClientInGame(client) ) client = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( client )
|
||||||
|
{
|
||||||
|
char auth[20];
|
||||||
|
GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth));
|
||||||
|
LogCustom("Deleted invalid spray: %s from (%N) [%s]", g_sFilename, client, auth);
|
||||||
|
PrintToServer("Invalid file: %s: %02d (%02X <> %02X) from (%N) [%s]", g_sFilename, i, iRead[i], g_iVal[i], client, auth);
|
||||||
|
} else {
|
||||||
|
LogCustom("Deleted invalid spray: %s", g_sFilename);
|
||||||
|
PrintToServer("Invalid file: %s: %02d (%02X <> %02X)", g_sFilename, i, iRead[i], g_iVal[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteFile(g_sFilename);
|
||||||
|
|
||||||
|
for (int j = 1; j <= MaxClients; j++)
|
||||||
|
{
|
||||||
|
if (IsValidClient(j))
|
||||||
|
{
|
||||||
|
char sTemp[PLATFORM_MAX_PATH - 4];
|
||||||
|
GetPlayerDecalFile(j, sTemp, sizeof(sTemp));
|
||||||
|
Format(sTemp, sizeof(sTemp), "%s.dat", sTemp);
|
||||||
|
if (StrEqual(sTemp, g_sFilename[24], false))
|
||||||
|
{
|
||||||
|
char auth[20];
|
||||||
|
GetClientAuthId(j, AuthId_Steam2, auth, sizeof(auth));
|
||||||
|
KickClient(j, "Please change your spray");
|
||||||
|
LogAction(j, -1, "\"%L\" is possibly using a bad spray. Client got kicked and spray got deleted.", j);
|
||||||
|
LogCustom("Deleted invalid spray: %s from (%N) [%s]", sTemp, j, auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DHookSetReturn(hReturn, 0);
|
||||||
|
return MRES_Supercede;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MRES_Ignored;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
int ValFile(int iRead[sizeof(g_iVal)])
|
||||||
|
{
|
||||||
|
char bytes[10];
|
||||||
|
bool read = true;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
for( int i = 0; i < sizeof(g_iVal); i++ )
|
||||||
|
{
|
||||||
|
if( i == 0 && g_iEngine == Engine_TF2 && iRead[i] == 82 && iRead[i+1] == 73 && iRead[i+2] == 70 && iRead[i+3] == 70 && iRead[i+8] == 87 && iRead[i+9] == 65 && iRead[i+10] == 86 && iRead[i+11] == 69 && iRead[i+12] == 102 && iRead[i+13] == 109 && iRead[i+14] == 116 )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if( g_iVal[i] == 42 )
|
||||||
|
{
|
||||||
|
switch( i )
|
||||||
|
{
|
||||||
|
case 8: read = iRead[i] <= 5;
|
||||||
|
case 16, 18:
|
||||||
|
{
|
||||||
|
Format(bytes, sizeof(bytes), "%02X%02X", iRead[i+1], iRead[i]);
|
||||||
|
n = HtD(bytes);
|
||||||
|
if( n < 0 || n > 8192 ) read = false;
|
||||||
|
}
|
||||||
|
case 20:
|
||||||
|
{
|
||||||
|
Format(bytes, sizeof(bytes), "%02X%02X%02X%02X", iRead[i+3], iRead[i+2], iRead[i+1], iRead[i]);
|
||||||
|
n = HtD(bytes);
|
||||||
|
if( n & (0x8000|0x10000|0x800000) ) read = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
read = iRead[i] == g_iVal[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !read ) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HtD(char[] bytes)
|
||||||
|
{
|
||||||
|
int len = strlen(bytes);
|
||||||
|
int base = 1;
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
for( int i = len - 1; i >= 0; i-- )
|
||||||
|
{
|
||||||
|
if( bytes[i] >= '0' && bytes[i] <= '9' )
|
||||||
|
{
|
||||||
|
value += (bytes[i] - 48) * base;
|
||||||
|
base = base * 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if( bytes[i] >= 'A' && bytes[i] <= 'F' )
|
||||||
|
{
|
||||||
|
value += (bytes[i] - 55) * base;
|
||||||
|
base = base * 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogCustom(const char[] format, any ...)
|
||||||
|
{
|
||||||
|
char buffer[512];
|
||||||
|
VFormat(buffer, sizeof(buffer), format, 2);
|
||||||
|
|
||||||
|
char FileName[PLATFORM_MAX_PATH], sTime[32];
|
||||||
|
BuildPath(Path_SM, FileName, sizeof(FileName), "logs/spray_detector.log");
|
||||||
|
File file = OpenFile(FileName, "a+");
|
||||||
|
FormatTime(sTime, sizeof(sTime), "%d-%b-%Y - %H:%M:%S");
|
||||||
|
file.WriteLine("%s: %s", sTime, buffer);
|
||||||
|
FlushFile(file);
|
||||||
|
delete file;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
// Purpose:
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
stock int IsValidClient(int client, bool nobots = true)
|
||||||
|
{
|
||||||
|
if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsClientInGame(client);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user