diff --git a/ScoreboardUnlocker/gamedata/ScoreboardUnlocker.txt b/ScoreboardUnlocker/gamedata/ScoreboardUnlocker.txt new file mode 100644 index 0000000..85bcb96 --- /dev/null +++ b/ScoreboardUnlocker/gamedata/ScoreboardUnlocker.txt @@ -0,0 +1,97 @@ +"Games" +{ + "cstrike" + { + "Keys" + { + "INTERFACEVERSION_SERVERGAMEDLL" "ServerGameDLL012" + } + + "Signatures" + { + "CreateInterface_Server" + { + "library" "server" + "linux" "@CreateInterface" + "windows" "@CreateInterface" + } + + "g_SendTableCRC" + { + "library" "engine" + "linux" "@g_SendTableCRC" + // IDK + //"windows" "" + } + } + + "Offsets" + { + "IServerGameDLL::GetAllServerClasses" + { + "linux" "11" + "windows" "11" + } + + "ServerClass::m_pNext" + { + "linux" "8" + "windows" "8" + } + + "ServerClass::m_pTable" + { + "linux" "4" + "windows" "4" + } + + "SendTable::m_pNetTableName" + { + "linux" "8" + "windows" "8" + } + + "SendTable::m_nProps" + { + "linux" "4" + "windows" "4" + } + + "SendTable::m_pProps" + { + "linux" "0" + "windows" "0" + } + + "SendProp::m_Type" + { + "linux" "8" + "windows" "8" + } + + "SendProp::m_pDataTable" + { + "linux" "68" + "windows" "68" + } + + "SendProp::m_pVarName" + { + "linux" "48" + "windows" "48" + } + + "SendProp::m_nBits" + { + "linux" "12" + "windows" "12" + } + + "SendProp::sizeof" + { + "linux" "80" + "windows" "80" + } + } + } +} diff --git a/ScoreboardUnlocker/scripting/ScoreboardUnlocker.sp b/ScoreboardUnlocker/scripting/ScoreboardUnlocker.sp new file mode 100644 index 0000000..512a9f0 --- /dev/null +++ b/ScoreboardUnlocker/scripting/ScoreboardUnlocker.sp @@ -0,0 +1,267 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include + +#define PLUGIN_NAME "ScoreboardUnlocker" +#define PLUGIN_AUTHOR "Irineu" +#define PLUGIN_VERSION "1.0" + +#define DPT_DataTable 6 + +public Plugin myinfo = +{ + name = PLUGIN_NAME, + author = PLUGIN_AUTHOR, + description = "Increases the frags limit in the scoreboard", + version = PLUGIN_VERSION, + url = "" +}; + +bool g_bLate = false; +Address serverGameDLL = 0; +Address g_pSendTableCRC = 0; +int g_iServerClass_m_pNext = -1; +int g_iServerClass_m_pTable = -1; +int g_iSendTable_m_pNetTableName = -1; +int g_iSendTable_m_nProps = -1; +int g_iSendTable_m_pProps = -1; +int g_iSendProp_m_Type = -1; +int g_iSendProp_m_pDataTable = -1; +int g_iSendProp_m_pVarName = -1; +int g_iSendProp_m_nBits = -1; +int g_iSendProp_sizeof = -1; +Handle g_hGetAllServerClasses = INVALID_HANDLE; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() +{ + GameData gd = new GameData("ScoreboardUnlocker"); + if (!gd) + SetFailState("Missing gamedata file"); + + StartPrepSDKCall(SDKCall_Static); + if (!PrepSDKCall_SetFromConf(gd, SDKConf_Signature, "CreateInterface_Server")) + SetFailState("Failed to get CreateInterface_Server"); + + PrepSDKCall_AddParameter(SDKType_String, SDKPass_Pointer); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Pointer, VDECODE_FLAG_ALLOWNULL); + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + + Handle call = EndPrepSDKCall(); + + StartPrepSDKCall(SDKCall_Raw); + if (!PrepSDKCall_SetFromConf(gd, SDKConf_Virtual, "IServerGameDLL::GetAllServerClasses")) + SetFailState("Failed to get IServerGameDLL::GetAllServerClasses"); + + PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain); + + g_hGetAllServerClasses = EndPrepSDKCall(); + + char GameDLLInterfaceName[64]; + if (!gd.GetKeyValue("INTERFACEVERSION_SERVERGAMEDLL", GameDLLInterfaceName, sizeof(GameDLLInterfaceName))) + SetFailState("Failed to get gamedll interface name"); + + serverGameDLL = SDKCall(call, GameDLLInterfaceName, 0); + if (serverGameDLL == Address_Null) + SetFailState("Failed to get gamedll interface"); + + g_pSendTableCRC = gd.GetMemSig("g_SendTableCRC"); + if (g_pSendTableCRC == Address_Null) + SetFailState("Failed to get g_SendTableCRC"); + + g_iServerClass_m_pNext = GetRequiredOffset(gd, "ServerClass::m_pNext"); + g_iServerClass_m_pTable = GetRequiredOffset(gd, "ServerClass::m_pTable"); + g_iSendTable_m_pNetTableName = GetRequiredOffset(gd, "SendTable::m_pNetTableName"); + g_iSendTable_m_nProps = GetRequiredOffset(gd, "SendTable::m_nProps"); + g_iSendTable_m_pProps = GetRequiredOffset(gd, "SendTable::m_pProps"); + g_iSendProp_m_Type = GetRequiredOffset(gd, "SendProp::m_Type"); + g_iSendProp_m_pDataTable = GetRequiredOffset(gd, "SendProp::m_pDataTable"); + g_iSendProp_m_pVarName = GetRequiredOffset(gd, "SendProp::m_pVarName"); + g_iSendProp_m_nBits = GetRequiredOffset(gd, "SendProp::m_nBits"); + g_iSendProp_sizeof = GetRequiredOffset(gd, "SendProp::sizeof"); + + ConVar sv_sendtables = FindConVar("sv_sendtables"); + if (!sv_sendtables) + SetFailState("Failed to find sv_sendtables"); + sv_sendtables.BoolValue = true; + + if (!g_bLate) + FulfillMyPurpose(); + + CloseHandle(call); + CloseHandle(gd); +} + +public void OnMapInit(const char[] mapName) +{ + if (g_bLate) + { + FulfillMyPurpose(); + g_bLate = false; + } +} + +void FulfillMyPurpose() +{ + Address pTable = FindSendTable("DT_PlayerResource"); + if (pTable == Address_Null) + SetFailState("Failed to find DT_PlayerResource"); + + for (int i = 1; i <= MaxClients; i++) + { + char name[64]; + Format(name, sizeof(name), "m_iScore.%03d", i); + Address pProp = FindSendProp(pTable, name); + if (pProp != Address_Null) + StoreToAddress(pProp + view_as
(g_iSendProp_m_nBits), 32, NumberType_Int32, false); + else + SetFailState("Failed to find m_iScore.%03d", i); + } + + int crc = LoadFromAddress(g_pSendTableCRC, NumberType_Int32); + StoreToAddress(g_pSendTableCRC, crc + 1, NumberType_Int32, false); +} + +int GetRequiredOffset(GameData gd, const char[] name) +{ + int ret = gd.GetOffset(name); + if (ret == -1) + SetFailState("Missing offset %s from gamedata file", name); + + return ret; +} + +void LoadStringFromAddress(char[] buf, int bufSize, Address address) +{ + for (int i = 0; i < bufSize; i++) + { + char c = LoadFromAddress(address + view_as
(i), NumberType_Int8); + if (c == 0) + break; + + buf[i] = c; + } + + buf[bufSize - 1] = 0; +} + +enum struct bazinga +{ + Address pAddr; +} + +Address FindSendTable(const char[] name) +{ + ArrayList sendTables = new ArrayList(); + for (Address pServerClass = SDKCall(g_hGetAllServerClasses, serverGameDLL); + pServerClass != Address_Null; + pServerClass = LoadFromAddress(pServerClass + view_as
(g_iServerClass_m_pNext), NumberType_Int32)) + { + Address pTable = LoadFromAddress(pServerClass + view_as
(g_iServerClass_m_pTable), NumberType_Int32); + if (pTable != Address_Null) + { + bazinga thing; + thing.pAddr = pTable; + sendTables.PushArray(thing, sizeof(bazinga)); + } + } + + while (sendTables.Length > 0) + { + bazinga thing; + sendTables.GetArray(0, thing, sizeof(bazinga)); + Address pTable = thing.pAddr; + sendTables.Erase(0); + char tableName[64]; + Address pName = LoadFromAddress(pTable + view_as
(g_iSendTable_m_pNetTableName), NumberType_Int32); + LoadStringFromAddress(tableName, sizeof(tableName), pName); + if (strcmp(tableName, name) == 0) + { + delete sendTables; + return pTable; + } + else + { + int nProps = LoadFromAddress(pTable + view_as
(g_iSendTable_m_nProps), NumberType_Int32); + for (int i = 0; i < nProps; i++) + { + Address pProp = LoadFromAddress(pTable + view_as
(g_iSendTable_m_pProps), NumberType_Int32) + (i * g_iSendProp_sizeof); + Address pPropTable = LoadFromAddress(pProp + view_as
(g_iSendProp_m_pDataTable), NumberType_Int32); + int propType = LoadFromAddress(pProp + view_as
(g_iSendProp_m_Type), NumberType_Int32); + if (propType == DPT_DataTable && pPropTable != Address_Null) + { + thing.pAddr = pPropTable; + sendTables.PushArray(thing, sizeof(bazinga)); + } + } + } + } + + delete sendTables; + return Address_Null; +} + +Address FindInSendTable(Address pTable, const char[] name, int requiredType = -1) +{ + int nProps = LoadFromAddress(pTable + view_as
(g_iSendTable_m_nProps), NumberType_Int32); + for (int i = 0; i < nProps; i++) + { + Address pProp = LoadFromAddress(pTable + view_as
(g_iSendTable_m_pProps), NumberType_Int32) + (i * g_iSendProp_sizeof); + char propName[64]; + Address pName = LoadFromAddress(pProp + view_as
(g_iSendProp_m_pVarName), NumberType_Int32); + LoadStringFromAddress(propName, sizeof(propName), pName); + int propType = LoadFromAddress(pProp + view_as
(g_iSendProp_m_Type), NumberType_Int32); + if (strcmp(propName, name) == 0 && (propType == requiredType || requiredType == -1)) + return pProp; + } + + return Address_Null; +} + +Address FindSendProp(Address pTable, const char[] name, int requiredType = -1) +{ + char propNames[16][64]; + int nameCount = ExplodeString(name, ".", propNames, 16, 64); + Address pProp = Address_Null; + // Please don't ever reuse parameters for local variables in this stupid language again, future me. + Address pTable2 = pTable; + for (int i = 0; i < nameCount; ) + { + if (pProp != Address_Null) + { + Address pPropTable = LoadFromAddress(pProp + view_as
(g_iSendProp_m_pDataTable), NumberType_Int32); + if (pPropTable != Address_Null) + pTable2 = pPropTable; + } + + int type = i == (nameCount - 1) ? requiredType : DPT_DataTable; + pProp = FindInSendTable(pTable2, propNames[i], type); + + if (pProp == Address_Null) + { + if (i == 0) + { + pProp = FindInSendTable(pTable2, "baseclass", DPT_DataTable); + if (pProp == Address_Null) + break; + } + else + { + break; + } + } + else + { + i++; + } + } + + return pProp; +}