From f5ab6fb5e8be7b73e6003d4145081d5e0c0ce287 Mon Sep 17 00:00:00 2001 From: Jack <66967891+ASpoonPlaysGames@users.noreply.github.com> Date: Wed, 27 Dec 2023 00:32:01 +0000 Subject: Folder restructuring from primedev (#624) Copies of over the primedev folder structure for easier cherry-picking of further changes Co-authored-by: F1F7Y --- NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp | 461 ------- .../shared/exploit_fixes/exploitfixes_lzss.cpp | 78 -- .../exploit_fixes/exploitfixes_utf8parser.cpp | 199 --- NorthstarDLL/shared/exploit_fixes/ns_limits.cpp | 298 ----- NorthstarDLL/shared/exploit_fixes/ns_limits.h | 52 - NorthstarDLL/shared/keyvalues.cpp | 1322 -------------------- NorthstarDLL/shared/keyvalues.h | 134 -- NorthstarDLL/shared/maxplayers.cpp | 640 ---------- NorthstarDLL/shared/maxplayers.h | 3 - NorthstarDLL/shared/misccommands.cpp | 391 ------ NorthstarDLL/shared/misccommands.h | 3 - NorthstarDLL/shared/playlist.cpp | 125 -- NorthstarDLL/shared/playlist.h | 10 - 13 files changed, 3716 deletions(-) delete mode 100644 NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp delete mode 100644 NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp delete mode 100644 NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp delete mode 100644 NorthstarDLL/shared/exploit_fixes/ns_limits.cpp delete mode 100644 NorthstarDLL/shared/exploit_fixes/ns_limits.h delete mode 100644 NorthstarDLL/shared/keyvalues.cpp delete mode 100644 NorthstarDLL/shared/keyvalues.h delete mode 100644 NorthstarDLL/shared/maxplayers.cpp delete mode 100644 NorthstarDLL/shared/maxplayers.h delete mode 100644 NorthstarDLL/shared/misccommands.cpp delete mode 100644 NorthstarDLL/shared/misccommands.h delete mode 100644 NorthstarDLL/shared/playlist.cpp delete mode 100644 NorthstarDLL/shared/playlist.h (limited to 'NorthstarDLL/shared') diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp deleted file mode 100644 index 8064d5ac..00000000 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp +++ /dev/null @@ -1,461 +0,0 @@ -#include "core/convar/cvar.h" -#include "ns_limits.h" -#include "dedicated/dedicated.h" -#include "core/tier0.h" -#include "engine/r2engine.h" -#include "client/r2client.h" -#include "core/math/vector.h" -#include "core/vanilla.h" - -AUTOHOOK_INIT() - -ConVar* Cvar_ns_exploitfixes_log; -ConVar* Cvar_ns_should_log_all_clientcommands; - -ConVar* Cvar_sv_cheats; - -#define BLOCKED_INFO(s) \ - ( \ - [=]() -> bool \ - { \ - if (Cvar_ns_exploitfixes_log->GetBool()) \ - { \ - std::stringstream stream; \ - stream << "ExploitFixes.cpp: " << BLOCK_PREFIX << s; \ - spdlog::error(stream.str()); \ - } \ - return false; \ - }()) - -// block bad netmessages -// Servers can literally request a screenshot from any client, yeah no -// clang-format off -AUTOHOOK(CLC_Screenshot_WriteToBuffer, engine.dll + 0x22AF20, -bool, __fastcall, (void* thisptr, void* buffer)) // 48 89 5C 24 ? 57 48 83 EC 20 8B 42 10 -// clang-format on -{ - if (g_pVanillaCompatibility->GetVanillaCompatibility()) - return CLC_Screenshot_WriteToBuffer(thisptr, buffer); - return false; -} - -// clang-format off -AUTOHOOK(CLC_Screenshot_ReadFromBuffer, engine.dll + 0x221F00, -bool, __fastcall, (void* thisptr, void* buffer)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B DA 48 8B 52 38 -// clang-format on -{ - if (g_pVanillaCompatibility->GetVanillaCompatibility()) - return CLC_Screenshot_ReadFromBuffer(thisptr, buffer); - return false; -} - -// This is unused ingame and a big client=>server=>client exploit vector -// clang-format off -AUTOHOOK(Base_CmdKeyValues_ReadFromBuffer, engine.dll + 0x220040, -bool, __fastcall, (void* thisptr, void* buffer)) // 40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70 -// clang-format on -{ - return false; -} - -// clang-format off -AUTOHOOK(CClient_ProcessSetConVar, engine.dll + 0x75CF0, -bool, __fastcall, (void* pMsg)) // 48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10 -// clang-format on -{ - - constexpr int ENTRY_STR_LEN = 260; - struct SetConVarEntry - { - char name[ENTRY_STR_LEN]; - char val[ENTRY_STR_LEN]; - }; - - struct NET_SetConVar - { - void* vtable; - void* unk1; - void* unk2; - void* m_pMessageHandler; - SetConVarEntry* m_ConVars; // convar entry array - void* unk5; // these 2 unks are just vector capacity or whatever - void* unk6; - int m_ConVars_count; // amount of cvar entries in array (this will not be out of bounds) - }; - - auto msg = (NET_SetConVar*)pMsg; - bool bIsServerFrame = ThreadInServerFrameThread(); - - std::string BLOCK_PREFIX = - std::string {"NET_SetConVar ("} + (bIsServerFrame ? "server" : "client") + "): Blocked dangerous/invalid msg: "; - - if (bIsServerFrame) - { - constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 69; - if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT) - { - return BLOCKED_INFO("Invalid m_ConVars_count (" << msg->m_ConVars_count << ")"); - } - } - - for (int i = 0; i < msg->m_ConVars_count; i++) - { - auto entry = msg->m_ConVars + i; - - // Safety check for memory access - if (CMemoryAddress(entry).IsMemoryReadable(sizeof(*entry))) - { - // Find null terminators - bool nameValid = false, valValid = false; - for (int i = 0; i < ENTRY_STR_LEN; i++) - { - if (!entry->name[i]) - nameValid = true; - if (!entry->val[i]) - valValid = true; - } - - if (!nameValid || !valValid) - return BLOCKED_INFO("Missing null terminators"); - - ConVar* pVar = g_pCVar->FindVar(entry->name); - - if (pVar) - { - memcpy( - entry->name, - pVar->m_ConCommandBase.m_pszName, - strlen(pVar->m_ConCommandBase.m_pszName) + 1); // Force name to match case - - int iFlags = bIsServerFrame ? FCVAR_USERINFO : FCVAR_REPLICATED; - if (!pVar->IsFlagSet(iFlags)) - return BLOCKED_INFO( - "Invalid flags (" << std::hex << "0x" << pVar->m_ConCommandBase.m_nFlags << "), var is " << entry->name); - } - } - else - { - return BLOCKED_INFO("Unreadable memory at " << (void*)entry); // Not risking that one, they all gotta be readable - } - } - - return CClient_ProcessSetConVar(msg); -} - -// prevent invalid user CMDs -// clang-format off -AUTOHOOK(CClient_ProcessUsercmds, engine.dll + 0x1040F0, -bool, __fastcall, (void* thisptr, void* pMsg)) // 40 55 56 48 83 EC 58 -// clang-format on -{ - struct CLC_Move - { - BYTE gap0[24]; - void* m_pMessageHandler; - int m_nBackupCommands; - int m_nNewCommands; - int m_nLength; - // bf_read m_DataIn; - // bf_write m_DataOut; - }; - - auto msg = (CLC_Move*)pMsg; - - const char* BLOCK_PREFIX = "ProcessUserCmds: "; - - if (msg->m_nBackupCommands < 0) - { - return BLOCKED_INFO("Invalid m_nBackupCommands (" << msg->m_nBackupCommands << ")"); - } - - if (msg->m_nNewCommands < 0) - { - return BLOCKED_INFO("Invalid m_nNewCommands (" << msg->m_nNewCommands << ")"); - } - - if (msg->m_nLength <= 0) - return BLOCKED_INFO("Invalid message length (" << msg->m_nLength << ")"); - - return CClient_ProcessUsercmds(thisptr, pMsg); -} - -// clang-format off -AUTOHOOK(ReadUsercmd, server.dll + 0x2603F0, -void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) // 4C 89 44 24 ? 53 55 56 57 -// clang-format on -{ - // Let normal usercmd read happen first, it's safe - ReadUsercmd(buf, pCmd_move, pCmd_from); - - // Now let's make sure the CMD we read isnt messed up to prevent numerous exploits (including server crashing) - struct alignas(4) SV_CUserCmd - { - DWORD command_number; - DWORD tick_count; - float command_time; - Vector3 worldViewAngles; - BYTE gap18[4]; - Vector3 localViewAngles; - Vector3 attackangles; - Vector3 move; - DWORD buttons; - BYTE impulse; - short weaponselect; - DWORD meleetarget; - BYTE gap4C[24]; - char headoffset; - BYTE gap65[11]; - Vector3 cameraPos; - Vector3 cameraAngles; - BYTE gap88[4]; - int tickSomething; - DWORD dword90; - DWORD predictedServerEventAck; - DWORD dword98; - float frameTime; - }; - - auto cmd = (SV_CUserCmd*)pCmd_move; - auto fromCmd = (SV_CUserCmd*)pCmd_from; - - std::string BLOCK_PREFIX = - "ReadUsercmd (command_number delta: " + std::to_string(cmd->command_number - fromCmd->command_number) + "): "; - - // fix invalid player angles - cmd->worldViewAngles.MakeValid(); - cmd->attackangles.MakeValid(); - cmd->localViewAngles.MakeValid(); - - // Fix invalid camera angles - cmd->cameraPos.MakeValid(); - cmd->cameraAngles.MakeValid(); - - // Fix invaid movement vector - cmd->move.MakeValid(); - - if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) - { - BLOCKED_INFO( - "Bogus cmd timing (tick_count: " << cmd->tick_count << ", frameTime: " << cmd->frameTime - << ", commandTime : " << cmd->command_time << ")"); - goto INVALID_CMD; // No simulation of bogus-timed cmds - } - - return; - -INVALID_CMD: - - // Fix any gameplay-affecting cmd properties - // NOTE: Currently tickcount/frametime is set to 0, this ~shouldn't~ cause any problems - cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = {0, 0, 0}; - cmd->tick_count = cmd->frameTime = 0; - cmd->move = cmd->cameraPos = {0, 0, 0}; - cmd->buttons = 0; - cmd->meleetarget = 0; -} - -// ensure that GetLocalBaseClient().m_bRestrictServerCommands is set correctly, which the return value of this function controls -// this is IsValveMod in source, but we're making it IsRespawnMod now since valve didn't make this one -// clang-format off -AUTOHOOK(IsRespawnMod, engine.dll + 0x1C6360, -bool, __fastcall, (const char* pModName)) // 48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63 -// clang-format on -{ - // somewhat temp, store the modname here, since we don't have a proper ptr in engine to it rn - int iSize = strlen(pModName); - g_pModName = new char[iSize + 1]; - strcpy(g_pModName, pModName); - - if (g_pVanillaCompatibility->GetVanillaCompatibility()) - return false; - - return (!strcmp("r2", pModName) || !strcmp("r1", pModName)) && !CommandLine()->CheckParm("-norestrictservercommands"); -} - -// ratelimit stringcmds, and prevent remote clients from calling commands that they shouldn't -// clang-format off -AUTOHOOK(CGameClient__ExecuteStringCommand, engine.dll + 0x1022E0, -bool, __fastcall, (CBaseClient* self, uint32_t unknown, const char* pCommandString)) -// clang-format on -{ - if (Cvar_ns_should_log_all_clientcommands->GetBool()) - spdlog::info("player {} (UID: {}) sent command: \"{}\"", self->m_Name, self->m_UID, pCommandString); - - if (!g_pServerLimits->CheckStringCommandLimits(self)) - { - CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands"); - return false; - } - - // verify the command we're trying to execute is FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS, if it's a concommand - char* commandBuf[1040]; // assumedly this is the size of CCommand since we don't have an actual constructor - memset(commandBuf, 0, sizeof(commandBuf)); - CCommand tempCommand = *(CCommand*)&commandBuf; - - if (!CCommand__Tokenize(tempCommand, pCommandString, cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) - return false; - - ConCommand* command = g_pCVar->FindCommand(tempCommand.Arg(0)); - - // if the command doesn't exist pass it on to ExecuteStringCommand for script clientcommands and stuff - if (command && !command->IsFlagSet(FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS)) - { - // ensure FCVAR_GAMEDLL concommands without FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS can't be executed by remote clients - if (IsDedicatedServer()) - return false; - - if (strcmp(self->m_UID, g_pLocalPlayerUserID)) - return false; - } - - // check for and block abusable legacy portal 2 commands - // these aren't actually concommands weirdly enough, they seem to just be hardcoded - if (!Cvar_sv_cheats->GetBool()) - { - constexpr const char* blockedCommands[] = { - "emit", // Sound-playing exploit (likely for Portal 2 coop devs testing splitscreen sound or something) - - // These both execute a command for every single entity for some reason, nice one valve - "pre_go_to_hub", - "pre_go_to_calibration", - - "end_movie", // Calls "__MovieFinished" script function, not sure exactly what this does but it certainly isn't needed - "load_recent_checkpoint" // This is the instant-respawn exploit, literally just calls RespawnPlayer() - }; - - int iCmdLength = strlen(tempCommand.Arg(0)); - - bool bIsBadCommand = false; - for (auto& blockedCommand : blockedCommands) - { - if (iCmdLength != strlen(blockedCommand)) - continue; - - for (int i = 0; tempCommand.Arg(0)[i]; i++) - if (tolower(tempCommand.Arg(0)[i]) != blockedCommand[i]) - goto NEXT_COMMAND; // break out of this loop, then go to next command - - // this is a command we need to block - return false; - NEXT_COMMAND:; - } - } - - return CGameClient__ExecuteStringCommand(self, unknown, pCommandString); -} - -// prevent clients from crashing servers through overflowing CNetworkStringTableContainer::WriteBaselines -bool bWasWritingStringTableSuccessful; - -// clang-format off -AUTOHOOK(CBaseClient__SendServerInfo, engine.dll + 0x104FB0, -void, __fastcall, (void* self)) -// clang-format on -{ - bWasWritingStringTableSuccessful = true; - CBaseClient__SendServerInfo(self); - if (!bWasWritingStringTableSuccessful) - CBaseClient__Disconnect( - self, 1, "Overflowed CNetworkStringTableContainer::WriteBaselines, try restarting your client and reconnecting"); -} - -// return null when GetEntByIndex is passed an index >= 0x4000 -// this is called from exactly 1 script clientcommand that can be given an arbitrary index, and going above 0x4000 crashes -// clang-format off -AUTOHOOK(GetEntByIndex, server.dll + 0x2A8A50, -void*, __fastcall, (int i)) -// clang-format on -{ - const int MAX_ENT_IDX = 0x4000; - - if (i >= MAX_ENT_IDX) - { - spdlog::warn("GetEntByIndex {} is out of bounds (max {})", i, MAX_ENT_IDX); - return nullptr; - } - - return GetEntByIndex(i); -} -// clang-format off -AUTOHOOK(CL_CopyExistingEntity, engine.dll + 0x6F940, -bool, __fastcall, (void* a1)) -// clang-format on -{ - struct CEntityReadInfo - { - BYTE gap[40]; - int nNewEntity; - }; - - CEntityReadInfo* pReadInfo = (CEntityReadInfo*)a1; - if (pReadInfo->nNewEntity >= 0x1000 || pReadInfo->nNewEntity < 0) - { - // Value isn't sanitized in release builds for - // every game powered by the Source Engine 1 - // causing read/write outside of array bounds. - // This defect has let to the achievement of a - // full-chain RCE exploit. We hook and perform - // sanity checks for the value of m_nNewEntity - // here to prevent this behavior from happening. - return false; - } - - return CL_CopyExistingEntity(a1); -} - -ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module)) -{ - AUTOHOOK_DISPATCH_MODULE(engine.dll) - - // allow client/ui to run clientcommands despite restricting servercommands - module.Offset(0x4FB65).Patch("EB 11"); - module.Offset(0x4FBAC).Patch("EB 16"); - - // patch to set bWasWritingStringTableSuccessful in CNetworkStringTableContainer::WriteBaselines if it fails - { - CMemoryAddress writeAddress(&bWasWritingStringTableSuccessful - module.Offset(0x234EDC).m_nAddress); - - CMemoryAddress addr = module.Offset(0x234ED2); - addr.Patch("C7 05"); - addr.Offset(2).Patch((BYTE*)&writeAddress, sizeof(writeAddress)); - - addr.Offset(6).Patch("00 00 00 00"); - - addr.Offset(10).NOP(5); - } -} - -ON_DLL_LOAD_RELIESON("server.dll", ServerExploitFixes, ConVar, (CModule module)) -{ - AUTOHOOK_DISPATCH_MODULE(server.dll) - - // ret at the start of CServerGameClients::ClientCommandKeyValues as it has no benefit and is forwarded to client (i.e. security issue) - // this prevents the attack vector of client=>server=>client, however server=>client also has clientside patches - module.Offset(0x153920).Patch("C3"); - - // Dumb ANTITAMPER patches (they negatively impact performance and security) - constexpr const char* ANTITAMPER_EXPORTS[] = { - "ANTITAMPER_SPOTCHECK_CODEMARKER", - "ANTITAMPER_TESTVALUE_CODEMARKER", - "ANTITAMPER_TRIGGER_CODEMARKER", - }; - - // Prevent these from actually doing anything - for (auto exportName : ANTITAMPER_EXPORTS) - { - CMemoryAddress exportAddr = module.GetExport(exportName); - if (exportAddr) - { - // Just return, none of them have any args or are userpurge - exportAddr.Patch("C3"); - spdlog::info("Patched AntiTamper function export \"{}\"", exportName); - } - } - - Cvar_ns_exploitfixes_log = - new ConVar("ns_exploitfixes_log", "1", FCVAR_GAMEDLL, "Whether to log whenever ExploitFixes.cpp blocks/corrects something"); - Cvar_ns_should_log_all_clientcommands = - new ConVar("ns_should_log_all_clientcommands", "0", FCVAR_NONE, "Whether to log all clientcommands"); - - Cvar_sv_cheats = g_pCVar->FindVar("sv_cheats"); -} diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp deleted file mode 100644 index ccb6ac18..00000000 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes_lzss.cpp +++ /dev/null @@ -1,78 +0,0 @@ - -AUTOHOOK_INIT() - -static constexpr int LZSS_LOOKSHIFT = 4; - -struct lzss_header_t -{ - unsigned int id; - unsigned int actualSize; -}; - -// Rewrite of CLZSS::SafeUncompress to fix a vulnerability where malicious compressed payloads could cause the decompressor to try to read -// out of the bounds of the output buffer. -// clang-format off -AUTOHOOK(CLZSS__SafeDecompress, engine.dll + 0x432A10, -unsigned int, __fastcall, (void* self, const unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize)) -// clang-format on -{ - unsigned int totalBytes = 0; - int getCmdByte = 0; - int cmdByte = 0; - - lzss_header_t header = *(lzss_header_t*)pInput; - - if (!pInput || !header.actualSize || header.id != 0x53535A4C || header.actualSize > unBufSize) - return 0; - - pInput += sizeof(lzss_header_t); - - for (;;) - { - if (!getCmdByte) - cmdByte = *pInput++; - - getCmdByte = (getCmdByte + 1) & 0x07; - - if (cmdByte & 0x01) - { - int position = *pInput++ << LZSS_LOOKSHIFT; - position |= (*pInput >> LZSS_LOOKSHIFT); - position += 1; - int count = (*pInput++ & 0x0F) + 1; - if (count == 1) - break; - - // Ensure reference chunk exists entirely within our buffer - if (position > totalBytes) - return 0; - - totalBytes += count; - if (totalBytes > unBufSize) - return 0; - - unsigned char* pSource = pOutput - position; - for (int i = 0; i < count; i++) - *pOutput++ = *pSource++; - } - else - { - totalBytes++; - if (totalBytes > unBufSize) - return 0; - - *pOutput++ = *pInput++; - } - cmdByte = cmdByte >> 1; - } - - if (totalBytes != header.actualSize) - return 0; - - return totalBytes; -} - -ON_DLL_LOAD("engine.dll", ExploitFixes_LZSS, (CModule module)) -{ - AUTOHOOK_DISPATCH() -} diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp deleted file mode 100644 index 3d97f750..00000000 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes_utf8parser.cpp +++ /dev/null @@ -1,199 +0,0 @@ - -AUTOHOOK_INIT() - -INT64(__fastcall* sub_F1320)(DWORD a1, char* a2); - -// Reimplementation of an exploitable UTF decoding function in titanfall -bool __fastcall CheckUTF8Valid(INT64* a1, DWORD* a2, char* strData) -{ - DWORD v3; // eax - char* v4; // rbx - char v5; // si - char* _strData; // rdi - char* v7; // rbp - char v11; // al - DWORD v12; // er9 - DWORD v13; // ecx - DWORD v14; // edx - DWORD v15; // er8 - int v16; // eax - DWORD v17; // er9 - int v18; // eax - DWORD v19; // er9 - DWORD v20; // ecx - int v21; // eax - int v22; // er9 - DWORD v23; // edx - int v24; // eax - int v25; // er9 - DWORD v26; // er9 - DWORD v27; // er10 - DWORD v28; // ecx - DWORD v29; // edx - DWORD v30; // er8 - int v31; // eax - DWORD v32; // er10 - int v33; // eax - DWORD v34; // er10 - DWORD v35; // ecx - int v36; // eax - int v37; // er10 - DWORD v38; // edx - int v39; // eax - int v40; // er10 - DWORD v41; // er10 - INT64 v43; // r8 - INT64 v44; // rdx - INT64 v45; // rcx - INT64 v46; // rax - INT64 v47; // rax - char v48; // al - INT64 v49; // r8 - INT64 v50; // rdx - INT64 v51; // rcx - INT64 v52; // rax - INT64 v53; // rax - - v3 = a2[2]; - v4 = (char*)(a1[1] + *a2); - v5 = 0; - _strData = strData; - v7 = &v4[*((UINT16*)a2 + 2)]; - if (v3 >= 2) - { - ++v4; - --v7; - if (v3 != 2) - { - while (1) - { - if (!CMemoryAddress(v4).IsMemoryReadable(1)) - return false; // INVALID - - v11 = *v4++; // crash potential - if (v11 != 92) - goto LABEL_6; - v11 = *v4++; - if (v11 == 110) - break; - switch (v11) - { - case 't': - v11 = 9; - goto LABEL_6; - case 'r': - v11 = 13; - goto LABEL_6; - case 'b': - v11 = 8; - goto LABEL_6; - case 'f': - v11 = 12; - goto LABEL_6; - } - if (v11 != 117) - goto LABEL_6; - v12 = *v4 | 0x20; - v13 = v4[1] | 0x20; - v14 = v4[2] | 0x20; - v15 = v4[3] | 0x20; - v16 = 87; - if (v12 <= 0x39) - v16 = 48; - v17 = v12 - v16; - v18 = 87; - v19 = v17 << 12; - if (v13 <= 0x39) - v18 = 48; - v20 = v13 - v18; - v21 = 87; - v22 = (v20 << 8) | v19; - if (v14 <= 0x39) - v21 = 48; - v23 = v14 - v21; - v24 = 87; - v25 = (16 * v23) | v22; - if (v15 <= 0x39) - v24 = 48; - v4 += 4; - v26 = (v15 - v24) | v25; - if (v26 - 55296 <= 0x7FF) - { - if (v26 >= 0xDC00) - return true; - if (*v4 != 92 || v4[1] != 117) - return true; - - v27 = v4[2] | 0x20; - v28 = v4[3] | 0x20; - v29 = v4[4] | 0x20; - v30 = v4[5] | 0x20; - v31 = 87; - if (v27 <= 0x39) - v31 = 48; - v32 = v27 - v31; - v33 = 87; - v34 = v32 << 12; - if (v28 <= 0x39) - v33 = 48; - v35 = v28 - v33; - v36 = 87; - v37 = (v35 << 8) | v34; - if (v29 <= 0x39) - v36 = 48; - v38 = v29 - v36; - v39 = 87; - v40 = (16 * v38) | v37; - if (v30 <= 0x39) - v39 = 48; - v4 += 6; - v41 = ((v30 - v39) | v40) - 56320; - if (v41 > 0x3FF) - return true; - v26 = v41 | ((v26 - 55296) << 10); - } - _strData += (DWORD)sub_F1320(v26, _strData); - LABEL_7: - if (v4 == v7) - goto LABEL_48; - } - v11 = 10; - LABEL_6: - v5 |= v11; - *_strData++ = v11; - goto LABEL_7; - } - } -LABEL_48: - return true; -} - -// prevent utf8 parser from crashing when provided bad data, which can be sent through user-controlled openinvites -// clang-format off -AUTOHOOK(Rson_ParseUTF8, engine.dll + 0xEF670, -bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) // 48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 1A -// clang-format on -{ - static void* targetRetAddr = CModule("engine.dll").FindPattern("84 C0 75 2C 49 8B 16"); - - // only call if we're parsing utf8 data from the network (i.e. communities), otherwise we get perf issues - void* pReturnAddress = -#ifdef _MSC_VER - _ReturnAddress() -#else - __builtin_return_address(0) -#endif - ; - - if (pReturnAddress == targetRetAddr && !CheckUTF8Valid(a1, a2, strData)) - return false; - - return Rson_ParseUTF8(a1, a2, strData); -} - -ON_DLL_LOAD("engine.dll", EngineExploitFixes_UTF8Parser, (CModule module)) -{ - AUTOHOOK_DISPATCH() - - sub_F1320 = module.FindPattern("83 F9 7F 77 08 88 0A").RCast(); -} diff --git a/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp b/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp deleted file mode 100644 index bd855ee4..00000000 --- a/NorthstarDLL/shared/exploit_fixes/ns_limits.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "ns_limits.h" -#include "engine/hoststate.h" -#include "client/r2client.h" -#include "engine/r2engine.h" -#include "server/r2server.h" -#include "core/tier0.h" -#include "core/math/vector.h" -#include "server/auth/serverauthentication.h" - -AUTOHOOK_INIT() - -ServerLimitsManager* g_pServerLimits; - -float (*CEngineServer__GetTimescale)(); - -// todo: make this work on higher timescales, also possibly disable when sv_cheats is set -void ServerLimitsManager::RunFrame(double flCurrentTime, float flFrameTime) -{ - if (Cvar_sv_antispeedhack_enable->GetBool()) - { - // for each player, set their usercmd processing budget for the frame to the last frametime for the server - for (int i = 0; i < g_pGlobals->m_nMaxClients; i++) - { - CBaseClient* player = &g_pClientArray[i]; - - if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end()) - { - PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[player]; - if (pLimitData->flFrameUserCmdBudget < g_pGlobals->m_flTickInterval * Cvar_sv_antispeedhack_maxtickbudget->GetFloat()) - { - pLimitData->flFrameUserCmdBudget += g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier->GetFloat() * - fmax(flFrameTime, g_pGlobals->m_flFrameTime * CEngineServer__GetTimescale()); - } - } - } - } -} - -void ServerLimitsManager::AddPlayer(CBaseClient* player) -{ - PlayerLimitData limitData; - limitData.flFrameUserCmdBudget = - g_pGlobals->m_flTickInterval * CEngineServer__GetTimescale() * Cvar_sv_antispeedhack_maxtickbudget->GetFloat(); - - m_PlayerLimitData.insert(std::make_pair(player, limitData)); -} - -void ServerLimitsManager::RemovePlayer(CBaseClient* player) -{ - if (m_PlayerLimitData.find(player) != m_PlayerLimitData.end()) - m_PlayerLimitData.erase(player); -} - -bool ServerLimitsManager::CheckStringCommandLimits(CBaseClient* player) -{ - if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1) - { - // note: this isn't super perfect, legit clients can trigger it in lobby if they try, mostly good enough tho imo - if (Plat_FloatTime() - m_PlayerLimitData[player].lastClientCommandQuotaStart >= 1.0) - { - // reset quota - m_PlayerLimitData[player].lastClientCommandQuotaStart = Plat_FloatTime(); - m_PlayerLimitData[player].numClientCommandsInQuota = 0; - } - - m_PlayerLimitData[player].numClientCommandsInQuota++; - if (m_PlayerLimitData[player].numClientCommandsInQuota > CVar_sv_quota_stringcmdspersecond->GetInt()) - { - // too many stringcmds, dc player - return false; - } - } - - return true; -} - -bool ServerLimitsManager::CheckChatLimits(CBaseClient* player) -{ - if (Plat_FloatTime() - m_PlayerLimitData[player].lastSayTextLimitStart >= 1.0) - { - m_PlayerLimitData[player].lastSayTextLimitStart = Plat_FloatTime(); - m_PlayerLimitData[player].sayTextLimitCount = 0; - } - - if (m_PlayerLimitData[player].sayTextLimitCount >= Cvar_sv_max_chat_messages_per_sec->GetInt()) - return false; - - m_PlayerLimitData[player].sayTextLimitCount++; - return true; -} - -// clang-format off -AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0, -char, __fastcall, (void* self, void* buf)) -// clang-format on -{ - enum eNetChanLimitMode - { - NETCHANLIMIT_WARN, - NETCHANLIMIT_KICK - }; - - double startTime = Plat_FloatTime(); - char ret = CNetChan__ProcessMessages(self, buf); - - // check processing limits, unless we're in a level transition - if (g_pHostState->m_iCurrentState == HostState_t::HS_RUN && ThreadInServerFrameThread()) - { - // player that sent the message - CBaseClient* sender = *(CBaseClient**)((char*)self + 368); - - // if no sender, return - // relatively certain this is fine? - if (!sender || !g_pServerLimits->m_PlayerLimitData.count(sender)) - return ret; - - // reset every second - if (startTime - g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart >= 1.0 || - g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart == -1.0) - { - g_pServerLimits->m_PlayerLimitData[sender].lastNetChanProcessingLimitStart = startTime; - g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime = 0.0; - } - g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime += (Plat_FloatTime() * 1000) - (startTime * 1000); - - if (g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime >= - g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt()) - { - spdlog::warn( - "Client {} hit netchan processing limit with {}ms of processing time this second (max is {})", - (char*)sender + 0x16, - g_pServerLimits->m_PlayerLimitData[sender].netChanProcessingLimitTime, - g_pServerLimits->Cvar_net_chan_limit_msec_per_sec->GetInt()); - - // never kick local player - if (g_pServerLimits->Cvar_net_chan_limit_mode->GetInt() != NETCHANLIMIT_WARN && strcmp(g_pLocalPlayerUserID, sender->m_UID)) - { - CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit"); - return false; - } - } - } - - return ret; -} - -bool ServerLimitsManager::CheckConnectionlessPacketLimits(netpacket_t* packet) -{ - static const ConVar* Cvar_net_data_block_enabled = g_pCVar->FindVar("net_data_block_enabled"); - - // don't ratelimit datablock packets as long as datablock is enabled - if (packet->adr.type == NA_IP && - (!(packet->data[4] == 'N' && Cvar_net_data_block_enabled->GetBool()) || !Cvar_net_data_block_enabled->GetBool())) - { - // bad lookup: optimise later tm - UnconnectedPlayerLimitData* sendData = nullptr; - for (UnconnectedPlayerLimitData& foundSendData : g_pServerLimits->m_UnconnectedPlayerLimitData) - { - if (!memcmp(packet->adr.ip, foundSendData.ip, 16)) - { - sendData = &foundSendData; - break; - } - } - - if (!sendData) - { - sendData = &g_pServerLimits->m_UnconnectedPlayerLimitData.emplace_back(); - memcpy(sendData->ip, packet->adr.ip, 16); - } - - if (Plat_FloatTime() < sendData->timeoutEnd) - return false; - - if (Plat_FloatTime() - sendData->lastQuotaStart >= 1.0) - { - sendData->lastQuotaStart = Plat_FloatTime(); - sendData->packetCount = 0; - } - - sendData->packetCount++; - - if (sendData->packetCount >= g_pServerLimits->Cvar_sv_querylimit_per_sec->GetInt()) - { - spdlog::warn( - "Client went over connectionless ratelimit of {} per sec with packet of type {}", - g_pServerLimits->Cvar_sv_querylimit_per_sec->GetInt(), - packet->data[4]); - - // timeout for a minute - sendData->timeoutEnd = Plat_FloatTime() + 60.0; - return false; - } - } - - return true; -} - -// this is weird and i'm not sure if it's correct, so not using for now -/*AUTOHOOK(CBasePlayer__PhysicsSimulate, server.dll + 0x5A6E50, bool, __fastcall, (void* self, int a2, char a3)) -{ - spdlog::info("CBasePlayer::PhysicsSimulate"); - return CBasePlayer__PhysicsSimulate(self, a2, a3); -}*/ - -struct alignas(4) SV_CUserCmd -{ - DWORD command_number; - DWORD tick_count; - float command_time; - Vector3 worldViewAngles; - BYTE gap18[4]; - Vector3 localViewAngles; - Vector3 attackangles; - Vector3 move; - DWORD buttons; - BYTE impulse; - short weaponselect; - DWORD meleetarget; - BYTE gap4C[24]; - char headoffset; - BYTE gap65[11]; - Vector3 cameraPos; - Vector3 cameraAngles; - BYTE gap88[4]; - int tickSomething; - DWORD dword90; - DWORD predictedServerEventAck; - DWORD dword98; - float frameTime; -}; - -// clang-format off -AUTOHOOK(CPlayerMove__RunCommand, server.dll + 0x5B8100, -void, __fastcall, (void* self, CBasePlayer* player, SV_CUserCmd* pUserCmd, uint64_t a4)) -// clang-format on -{ - if (g_pServerLimits->Cvar_sv_antispeedhack_enable->GetBool()) - { - CBaseClient* pClient = &g_pClientArray[player->m_nPlayerIndex - 1]; - - if (g_pServerLimits->m_PlayerLimitData.find(pClient) != g_pServerLimits->m_PlayerLimitData.end()) - { - PlayerLimitData* pLimitData = &g_pServerLimits->m_PlayerLimitData[pClient]; - - pLimitData->flFrameUserCmdBudget = fmax(0.0, pLimitData->flFrameUserCmdBudget - pUserCmd->frameTime); - - if (pLimitData->flFrameUserCmdBudget <= 0.0) - { - spdlog::warn("player {} went over usercmd budget ({})", pClient->m_Name, pLimitData->flFrameUserCmdBudget); - return; - } - } - } - - CPlayerMove__RunCommand(self, player, pUserCmd, a4); -} - -ON_DLL_LOAD_RELIESON("engine.dll", ServerLimits, ConVar, (CModule module)) -{ - AUTOHOOK_DISPATCH_MODULE(engine.dll) - - g_pServerLimits = new ServerLimitsManager; - - g_pServerLimits->CVar_sv_quota_stringcmdspersecond = new ConVar( - "sv_quota_stringcmdspersecond", - "60", - FCVAR_GAMEDLL, - "How many string commands per second clients are allowed to submit, 0 to disallow all string commands, -1 to disable"); - g_pServerLimits->Cvar_net_chan_limit_mode = - new ConVar("net_chan_limit_mode", "0", FCVAR_GAMEDLL, "The mode for netchan processing limits: 0 = warn, 1 = kick"); - g_pServerLimits->Cvar_net_chan_limit_msec_per_sec = new ConVar( - "net_chan_limit_msec_per_sec", - "100", - FCVAR_GAMEDLL, - "Netchannel processing is limited to so many milliseconds, abort connection if exceeding budget"); - g_pServerLimits->Cvar_sv_querylimit_per_sec = new ConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, ""); - g_pServerLimits->Cvar_sv_max_chat_messages_per_sec = new ConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, ""); - g_pServerLimits->Cvar_sv_antispeedhack_enable = - new ConVar("sv_antispeedhack_enable", "0", FCVAR_NONE, "whether to enable antispeedhack protections"); - g_pServerLimits->Cvar_sv_antispeedhack_maxtickbudget = new ConVar( - "sv_antispeedhack_maxtickbudget", - "64", - FCVAR_GAMEDLL, - "Maximum number of client-issued usercmd ticks that can be replayed in packet loss conditions"); - g_pServerLimits->Cvar_sv_antispeedhack_budgetincreasemultiplier = new ConVar( - "sv_antispeedhack_budgetincreasemultiplier", - "1", - FCVAR_GAMEDLL, - "Increase usercmd processing budget by tickinterval * value per tick"); - - CEngineServer__GetTimescale = module.Offset(0x240840).RCast(); -} - -ON_DLL_LOAD("server.dll", ServerLimitsServer, (CModule module)) -{ - AUTOHOOK_DISPATCH_MODULE(server.dll) -} diff --git a/NorthstarDLL/shared/exploit_fixes/ns_limits.h b/NorthstarDLL/shared/exploit_fixes/ns_limits.h deleted file mode 100644 index 546fec6f..00000000 --- a/NorthstarDLL/shared/exploit_fixes/ns_limits.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -#include "engine/r2engine.h" -#include "core/convar/convar.h" -#include - -struct PlayerLimitData -{ - double lastClientCommandQuotaStart = -1.0; - int numClientCommandsInQuota = 0; - - double lastNetChanProcessingLimitStart = -1.0; - double netChanProcessingLimitTime = 0.0; - - double lastSayTextLimitStart = -1.0; - int sayTextLimitCount = 0; - - float flFrameUserCmdBudget = 0.0; -}; - -struct UnconnectedPlayerLimitData -{ - char ip[16]; - double lastQuotaStart = 0.0; - int packetCount = 0; - double timeoutEnd = -1.0; -}; - -class ServerLimitsManager -{ -public: - ConVar* CVar_sv_quota_stringcmdspersecond; - ConVar* Cvar_net_chan_limit_mode; - ConVar* Cvar_net_chan_limit_msec_per_sec; - ConVar* Cvar_sv_querylimit_per_sec; - ConVar* Cvar_sv_max_chat_messages_per_sec; - ConVar* Cvar_sv_antispeedhack_enable; - ConVar* Cvar_sv_antispeedhack_maxtickbudget; - ConVar* Cvar_sv_antispeedhack_budgetincreasemultiplier; - - std::unordered_map m_PlayerLimitData; - std::vector m_UnconnectedPlayerLimitData; - -public: - void RunFrame(double flCurrentTime, float flFrameTime); - void AddPlayer(CBaseClient* player); - void RemovePlayer(CBaseClient* player); - bool CheckStringCommandLimits(CBaseClient* player); - bool CheckChatLimits(CBaseClient* player); - bool CheckConnectionlessPacketLimits(netpacket_t* packet); -}; - -extern ServerLimitsManager* g_pServerLimits; diff --git a/NorthstarDLL/shared/keyvalues.cpp b/NorthstarDLL/shared/keyvalues.cpp deleted file mode 100644 index 88753723..00000000 --- a/NorthstarDLL/shared/keyvalues.cpp +++ /dev/null @@ -1,1322 +0,0 @@ -#include "keyvalues.h" -#include - -// implementation of the ConVar class -// heavily based on https://github.com/Mauler125/r5sdk/blob/master/r5dev/vpc/keyvalues.cpp - -typedef int HKeySymbol; -#define INVALID_KEY_SYMBOL (-1) - -#define MAKE_3_BYTES_FROM_1_AND_2(x1, x2) ((((uint16_t)x2) << 8) | (uint8_t)(x1)) -#define SPLIT_3_BYTES_INTO_1_AND_2(x1, x2, x3) \ - do \ - { \ - x1 = (uint8_t)(x3); \ - x2 = (uint16_t)((x3) >> 8); \ - } while (0) - -struct CKeyValuesSystem -{ -public: - struct __VTable - { - char pad0[8 * 3]; // 2 methods - HKeySymbol (*GetSymbolForString)(CKeyValuesSystem* self, const char* name, bool bCreate); - const char* (*GetStringForSymbol)(CKeyValuesSystem* self, HKeySymbol symbol); - char pad1[8 * 5]; - HKeySymbol (*GetSymbolForStringCaseSensitive)( - CKeyValuesSystem* self, HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate); - }; - - const __VTable* m_pVtable; -}; - -int (*V_UTF8ToUnicode)(const char* pUTF8, wchar_t* pwchDest, int cubDestSizeInBytes); -int (*V_UnicodeToUTF8)(const wchar_t* pUnicode, char* pUTF8, int cubDestSizeInBytes); -CKeyValuesSystem* (*KeyValuesSystem)(); - -KeyValues::KeyValues() {} // default constructor for copying and such - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *pszSetName - -//----------------------------------------------------------------------------- -KeyValues::KeyValues(const char* pszSetName) -{ - Init(); - SetName(pszSetName); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *pszSetName - -// *pszFirstKey - -// *pszFirstValue - -//----------------------------------------------------------------------------- -KeyValues::KeyValues(const char* pszSsetName, const char* pszFirstKey, const char* pszFirstValue) -{ - Init(); - SetName(pszSsetName); - SetString(pszFirstKey, pszFirstValue); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *pszSetName - -// *pszFirstKey - -// *pwszFirstValue - -//----------------------------------------------------------------------------- -KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, const wchar_t* pwszFirstValue) -{ - Init(); - SetName(pszSetName); - SetWString(pszFirstKey, pwszFirstValue); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *pszSetName - -// *pszFirstKey - -// iFirstValue - -//----------------------------------------------------------------------------- -KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue) -{ - Init(); - SetName(pszSetName); - SetInt(pszFirstKey, iFirstValue); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *pszSetName - -// *pszFirstKey - -// *pszFirstValue - -// *pszSecondKey - -// *pszSecondValue - -//----------------------------------------------------------------------------- -KeyValues::KeyValues( - const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue, const char* pszSecondKey, const char* pszSecondValue) -{ - Init(); - SetName(pszSetName); - SetString(pszFirstKey, pszFirstValue); - SetString(pszSecondKey, pszSecondValue); -} - -//----------------------------------------------------------------------------- -// Purpose: Constructor -// Input : *pszSetName - -// *pszFirstKey - -// iFirstValue - -// *pszSecondKey - -// iSecondValue - -//----------------------------------------------------------------------------- -KeyValues::KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue, const char* pszSecondKey, int iSecondValue) -{ - Init(); - SetName(pszSetName); - SetInt(pszFirstKey, iFirstValue); - SetInt(pszSecondKey, iSecondValue); -} - -//----------------------------------------------------------------------------- -// Purpose: Destructor -//----------------------------------------------------------------------------- -KeyValues::~KeyValues(void) -{ - RemoveEverything(); -} - -//----------------------------------------------------------------------------- -// Purpose: Initialize member variables -//----------------------------------------------------------------------------- -void KeyValues::Init(void) -{ - m_iKeyName = 0; - m_iKeyNameCaseSensitive1 = 0; - m_iKeyNameCaseSensitive2 = 0; - m_iDataType = TYPE_NONE; - - m_pSub = nullptr; - m_pPeer = nullptr; - m_pChain = nullptr; - - m_sValue = nullptr; - m_wsValue = nullptr; - m_pValue = nullptr; - - m_bHasEscapeSequences = 0; -} - -//----------------------------------------------------------------------------- -// Purpose: Clear out all subkeys, and the current value -//----------------------------------------------------------------------------- -void KeyValues::Clear(void) -{ - delete m_pSub; - m_pSub = nullptr; - m_iDataType = TYPE_NONE; -} - -//----------------------------------------------------------------------------- -// for backwards compat - we used to need this to force the free to run from the same DLL -// as the alloc -//----------------------------------------------------------------------------- -void KeyValues::DeleteThis(void) -{ - delete this; -} - -//----------------------------------------------------------------------------- -// Purpose: remove everything -//----------------------------------------------------------------------------- -void KeyValues::RemoveEverything(void) -{ - KeyValues* dat; - KeyValues* datNext = nullptr; - for (dat = m_pSub; dat != nullptr; dat = datNext) - { - datNext = dat->m_pPeer; - dat->m_pPeer = nullptr; - delete dat; - } - - for (dat = m_pPeer; dat && dat != this; dat = datNext) - { - datNext = dat->m_pPeer; - dat->m_pPeer = nullptr; - delete dat; - } - - delete[] m_sValue; - m_sValue = nullptr; - delete[] m_wsValue; - m_wsValue = nullptr; -} - -//----------------------------------------------------------------------------- -// Purpose: Find a keyValue, create it if it is not found. -// Set bCreate to true to create the key if it doesn't already exist -// (which ensures a valid pointer will be returned) -// Input : *pszKeyName - -// bCreate - -// Output : *KeyValues -//----------------------------------------------------------------------------- -KeyValues* KeyValues::FindKey(const char* pszKeyName, bool bCreate) -{ - assert_msg(this, "Member function called on NULL KeyValues"); - - if (!pszKeyName || !*pszKeyName) - return this; - - const char* pSubStr = strchr(pszKeyName, '/'); - const char* pSearchStr = pszKeyName; - if (pSubStr && !*(pSubStr + 1)) - { - // if key name is just '/', then use it as a key directly - pSearchStr = pSubStr; - pSubStr = nullptr; - } - - HKeySymbol iSearchStr = KeyValuesSystem()->m_pVtable->GetSymbolForString(KeyValuesSystem(), pSearchStr, bCreate); - if (iSearchStr == INVALID_KEY_SYMBOL) - { - // not found, couldn't possibly be in key value list - return nullptr; - } - - KeyValues* pLastKVs = nullptr; - KeyValues* pCurrentKVs; - // find the searchStr in the current peer list - for (pCurrentKVs = m_pSub; pCurrentKVs != nullptr; pCurrentKVs = pCurrentKVs->m_pPeer) - { - pLastKVs = pCurrentKVs; // record the last item looked at (for if we need to append to the end of the list) - - // symbol compare - if (pLastKVs->m_iKeyName == (uint32_t)iSearchStr) - break; - } - - if (!pCurrentKVs && m_pChain) - pCurrentKVs = m_pChain->FindKey(pSearchStr, false); - - // make sure a key was found - if (!pCurrentKVs) - { - if (bCreate) - { - // we need to create a new key - pCurrentKVs = new KeyValues(pSearchStr); - // Assert(dat != NULL); - - // insert new key at end of list - if (pLastKVs) - pLastKVs->m_pPeer = pCurrentKVs; - else - m_pSub = pCurrentKVs; - - pCurrentKVs->m_pPeer = nullptr; - - // a key graduates to be a submsg as soon as it's m_pSub is set - // this should be the only place m_pSub is set - m_iDataType = TYPE_NONE; - } - else - { - return nullptr; - } - } - - // if we've still got a subStr we need to keep looking deeper in the tree - if (pSubStr) - { - // recursively chain down through the paths in the string - return pCurrentKVs->FindKey(pSubStr + 1, bCreate); - } - - return pCurrentKVs; -} - -//----------------------------------------------------------------------------- -// Purpose: Locate last child. Returns NULL if we have no children -// Output : *KeyValues -//----------------------------------------------------------------------------- -KeyValues* KeyValues::FindLastSubKey(void) const -{ - // No children? - if (m_pSub == nullptr) - return nullptr; - - // Scan for the last one - KeyValues* pLastChild = m_pSub; - while (pLastChild->m_pPeer) - pLastChild = pLastChild->m_pPeer; - return pLastChild; -} - -//----------------------------------------------------------------------------- -// Purpose: Adds a subkey. Make sure the subkey isn't a child of some other keyvalues -// Input : *pSubKey - -//----------------------------------------------------------------------------- -void KeyValues::AddSubKey(KeyValues* pSubkey) -{ - // Make sure the subkey isn't a child of some other keyvalues - assert(pSubkey != nullptr); - assert(pSubkey->m_pPeer == nullptr); - - // add into subkey list - if (m_pSub == nullptr) - { - m_pSub = pSubkey; - } - else - { - KeyValues* pTempDat = m_pSub; - while (pTempDat->GetNextKey() != nullptr) - { - pTempDat = pTempDat->GetNextKey(); - } - - pTempDat->SetNextKey(pSubkey); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Remove a subkey from the list -// Input : *pSubKey - -//----------------------------------------------------------------------------- -void KeyValues::RemoveSubKey(KeyValues* pSubKey) -{ - if (!pSubKey) - return; - - // check the list pointer - if (m_pSub == pSubKey) - { - m_pSub = pSubKey->m_pPeer; - } - else - { - // look through the list - KeyValues* kv = m_pSub; - while (kv->m_pPeer) - { - if (kv->m_pPeer == pSubKey) - { - kv->m_pPeer = pSubKey->m_pPeer; - break; - } - - kv = kv->m_pPeer; - } - } - - pSubKey->m_pPeer = nullptr; -} - -//----------------------------------------------------------------------------- -// Purpose: Insert a subkey at index -// Input : nIndex - -// *pSubKey - -//----------------------------------------------------------------------------- -void KeyValues::InsertSubKey(int nIndex, KeyValues* pSubKey) -{ - // Sub key must be valid and not part of another chain - assert(pSubKey && pSubKey->m_pPeer == nullptr); - - if (nIndex == 0) - { - pSubKey->m_pPeer = m_pSub; - m_pSub = pSubKey; - return; - } - else - { - int nCurrentIndex = 0; - for (KeyValues* pIter = GetFirstSubKey(); pIter != nullptr; pIter = pIter->GetNextKey()) - { - ++nCurrentIndex; - if (nCurrentIndex == nIndex) - { - pSubKey->m_pPeer = pIter->m_pPeer; - pIter->m_pPeer = pSubKey; - return; - } - } - // Index is out of range if we get here - assert(0); - return; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Checks if key contains a subkey -// Input : *pSubKey - -// Output : true if contains, false otherwise -//----------------------------------------------------------------------------- -bool KeyValues::ContainsSubKey(KeyValues* pSubKey) -{ - for (KeyValues* pIter = GetFirstSubKey(); pIter != nullptr; pIter = pIter->GetNextKey()) - { - if (pSubKey == pIter) - { - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: Swaps existing subkey with another -// Input : *pExistingSubkey - -// *pNewSubKey - -//----------------------------------------------------------------------------- -void KeyValues::SwapSubKey(KeyValues* pExistingSubkey, KeyValues* pNewSubKey) -{ - assert(pExistingSubkey != nullptr && pNewSubKey != nullptr); - - // Make sure the new sub key isn't a child of some other keyvalues - assert(pNewSubKey->m_pPeer == nullptr); - - // Check the list pointer - if (m_pSub == pExistingSubkey) - { - pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; - pExistingSubkey->m_pPeer = nullptr; - m_pSub = pNewSubKey; - } - else - { - // Look through the list - KeyValues* kv = m_pSub; - while (kv->m_pPeer) - { - if (kv->m_pPeer == pExistingSubkey) - { - pNewSubKey->m_pPeer = pExistingSubkey->m_pPeer; - pExistingSubkey->m_pPeer = nullptr; - kv->m_pPeer = pNewSubKey; - break; - } - - kv = kv->m_pPeer; - } - // Existing sub key should always be found, otherwise it's a bug in the calling code. - assert(kv->m_pPeer != nullptr); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Elides subkey -// Input : *pSubKey - -//----------------------------------------------------------------------------- -void KeyValues::ElideSubKey(KeyValues* pSubKey) -{ - // This pointer's "next" pointer needs to be fixed up when we elide the key - KeyValues** ppPointerToFix = &m_pSub; - for (KeyValues* pKeyIter = m_pSub; pKeyIter != nullptr; ppPointerToFix = &pKeyIter->m_pPeer, pKeyIter = pKeyIter->GetNextKey()) - { - if (pKeyIter == pSubKey) - { - if (pSubKey->m_pSub == nullptr) - { - // No children, simply remove the key - *ppPointerToFix = pSubKey->m_pPeer; - delete pSubKey; - } - else - { - *ppPointerToFix = pSubKey->m_pSub; - // Attach the remainder of this chain to the last child of pSubKey - KeyValues* pChildIter = pSubKey->m_pSub; - while (pChildIter->m_pPeer != nullptr) - { - pChildIter = pChildIter->m_pPeer; - } - // Now points to the last child of pSubKey - pChildIter->m_pPeer = pSubKey->m_pPeer; - // Detach the node to be elided - pSubKey->m_pSub = nullptr; - pSubKey->m_pPeer = nullptr; - delete pSubKey; - } - return; - } - } - // Key not found; that's caller error. - assert(0); -} - -//----------------------------------------------------------------------------- -// Purpose: Check if a keyName has no value assigned to it. -// Input : *pszKeyName - -// Output : true on success, false otherwise -//----------------------------------------------------------------------------- -bool KeyValues::IsEmpty(const char* pszKeyName) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (!pKey) - return true; - - if (pKey->m_iDataType == TYPE_NONE && pKey->m_pSub == nullptr) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the first true sub key -// Output : *KeyValues -//----------------------------------------------------------------------------- -KeyValues* KeyValues::GetFirstTrueSubKey(void) const -{ - assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pSub : nullptr; - while (pRet && pRet->m_iDataType != TYPE_NONE) - pRet = pRet->m_pPeer; - - return pRet; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the next true sub key -// Output : *KeyValues -//----------------------------------------------------------------------------- -KeyValues* KeyValues::GetNextTrueSubKey(void) const -{ - assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pPeer : nullptr; - while (pRet && pRet->m_iDataType != TYPE_NONE) - pRet = pRet->m_pPeer; - - return pRet; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the first value -// Output : *KeyValues -//----------------------------------------------------------------------------- -KeyValues* KeyValues::GetFirstValue(void) const -{ - assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pSub : nullptr; - while (pRet && pRet->m_iDataType == TYPE_NONE) - pRet = pRet->m_pPeer; - - return pRet; -} - -//----------------------------------------------------------------------------- -// Purpose: gets the next value -// Output : *KeyValues -//----------------------------------------------------------------------------- -KeyValues* KeyValues::GetNextValue(void) const -{ - assert_msg(this, "Member function called on NULL KeyValues"); - KeyValues* pRet = this ? m_pPeer : nullptr; - while (pRet && pRet->m_iDataType == TYPE_NONE) - pRet = pRet->m_pPeer; - - return pRet; -} - -//----------------------------------------------------------------------------- -// Purpose: Return the first subkey in the list -//----------------------------------------------------------------------------- -KeyValues* KeyValues::GetFirstSubKey() const -{ - assert_msg(this, "Member function called on NULL KeyValues"); - return this ? m_pSub : nullptr; -} - -//----------------------------------------------------------------------------- -// Purpose: Return the next subkey -//----------------------------------------------------------------------------- -KeyValues* KeyValues::GetNextKey() const -{ - assert_msg(this, "Member function called on NULL KeyValues"); - return this ? m_pPeer : nullptr; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the name of the current key section -// Output : const char* -//----------------------------------------------------------------------------- -const char* KeyValues::GetName(void) const -{ - return KeyValuesSystem()->m_pVtable->GetStringForSymbol( - KeyValuesSystem(), MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2)); -} - -//----------------------------------------------------------------------------- -// Purpose: Get the integer value of a keyName. Default value is returned -// if the keyName can't be found. -// Input : *pszKeyName - -// nDefaultValue - -// Output : int -//----------------------------------------------------------------------------- -int KeyValues::GetInt(const char* pszKeyName, int iDefaultValue) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - switch (pKey->m_iDataType) - { - case TYPE_STRING: - return atoi(pKey->m_sValue); - case TYPE_WSTRING: - return _wtoi(pKey->m_wsValue); - case TYPE_FLOAT: - return static_cast(pKey->m_flValue); - case TYPE_UINT64: - // can't convert, since it would lose data - assert(0); - return 0; - case TYPE_INT: - case TYPE_PTR: - default: - return pKey->m_iValue; - }; - } - return iDefaultValue; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the integer value of a keyName. Default value is returned -// if the keyName can't be found. -// Input : *pszKeyName - -// nDefaultValue - -// Output : uint64_t -//----------------------------------------------------------------------------- -uint64_t KeyValues::GetUint64(const char* pszKeyName, uint64_t nDefaultValue) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - switch (pKey->m_iDataType) - { - case TYPE_STRING: - { - uint64_t uiResult = 0ull; - sscanf(pKey->m_sValue, "%lld", &uiResult); - return uiResult; - } - case TYPE_WSTRING: - { - uint64_t uiResult = 0ull; - swscanf(pKey->m_wsValue, L"%lld", &uiResult); - return uiResult; - } - case TYPE_FLOAT: - return static_cast(pKey->m_flValue); - case TYPE_UINT64: - return *reinterpret_cast(pKey->m_sValue); - case TYPE_PTR: - return static_cast(reinterpret_cast(pKey->m_pValue)); - case TYPE_INT: - default: - return pKey->m_iValue; - }; - } - return nDefaultValue; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the pointer value of a keyName. Default value is returned -// if the keyName can't be found. -// Input : *pszKeyName - -// pDefaultValue - -// Output : void* -//----------------------------------------------------------------------------- -void* KeyValues::GetPtr(const char* pszKeyName, void* pDefaultValue) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - switch (pKey->m_iDataType) - { - case TYPE_PTR: - return pKey->m_pValue; - - case TYPE_WSTRING: - case TYPE_STRING: - case TYPE_FLOAT: - case TYPE_INT: - case TYPE_UINT64: - default: - return nullptr; - }; - } - return pDefaultValue; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the float value of a keyName. Default value is returned -// if the keyName can't be found. -// Input : *pszKeyName - -// flDefaultValue - -// Output : float -//----------------------------------------------------------------------------- -float KeyValues::GetFloat(const char* pszKeyName, float flDefaultValue) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - switch (pKey->m_iDataType) - { - case TYPE_STRING: - return static_cast(atof(pKey->m_sValue)); - case TYPE_WSTRING: - return static_cast(_wtof(pKey->m_wsValue)); // no wtof - case TYPE_FLOAT: - return pKey->m_flValue; - case TYPE_INT: - return static_cast(pKey->m_iValue); - case TYPE_UINT64: - return static_cast((*(reinterpret_cast(pKey->m_sValue)))); - case TYPE_PTR: - default: - return 0.0f; - }; - } - return flDefaultValue; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the string pointer of a keyName. Default value is returned -// if the keyName can't be found. -// // Input : *pszKeyName - -// pszDefaultValue - -// Output : const char* -//----------------------------------------------------------------------------- -const char* KeyValues::GetString(const char* pszKeyName, const char* pszDefaultValue) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - // convert the data to string form then return it - char buf[64]; - switch (pKey->m_iDataType) - { - case TYPE_FLOAT: - snprintf(buf, sizeof(buf), "%f", pKey->m_flValue); - SetString(pszKeyName, buf); - break; - case TYPE_PTR: - snprintf(buf, sizeof(buf), "%lld", reinterpret_cast(pKey->m_pValue)); - SetString(pszKeyName, buf); - break; - case TYPE_INT: - snprintf(buf, sizeof(buf), "%d", pKey->m_iValue); - SetString(pszKeyName, buf); - break; - case TYPE_UINT64: - snprintf(buf, sizeof(buf), "%lld", *(reinterpret_cast(pKey->m_sValue))); - SetString(pszKeyName, buf); - break; - case TYPE_COLOR: - snprintf(buf, sizeof(buf), "%d %d %d %d", pKey->m_Color[0], pKey->m_Color[1], pKey->m_Color[2], pKey->m_Color[3]); - SetString(pszKeyName, buf); - break; - - case TYPE_WSTRING: - { - // convert the string to char *, set it for future use, and return it - char wideBuf[512]; - int result = V_UnicodeToUTF8(pKey->m_wsValue, wideBuf, 512); - if (result) - { - // note: this will copy wideBuf - SetString(pszKeyName, wideBuf); - } - else - { - return pszDefaultValue; - } - break; - } - case TYPE_STRING: - break; - default: - return pszDefaultValue; - }; - - return pKey->m_sValue; - } - return pszDefaultValue; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the wide string pointer of a keyName. Default value is returned -// if the keyName can't be found. -// // Input : *pszKeyName - -// pwszDefaultValue - -// Output : const wchar_t* -//----------------------------------------------------------------------------- -const wchar_t* KeyValues::GetWString(const char* pszKeyName, const wchar_t* pwszDefaultValue) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - wchar_t wbuf[64]; - switch (pKey->m_iDataType) - { - case TYPE_FLOAT: - swprintf(wbuf, ARRAYSIZE(wbuf), L"%f", pKey->m_flValue); - SetWString(pszKeyName, wbuf); - break; - case TYPE_PTR: - swprintf(wbuf, ARRAYSIZE(wbuf), L"%lld", static_cast(reinterpret_cast(pKey->m_pValue))); - SetWString(pszKeyName, wbuf); - break; - case TYPE_INT: - swprintf(wbuf, ARRAYSIZE(wbuf), L"%d", pKey->m_iValue); - SetWString(pszKeyName, wbuf); - break; - case TYPE_UINT64: - { - swprintf(wbuf, ARRAYSIZE(wbuf), L"%lld", *(reinterpret_cast(pKey->m_sValue))); - SetWString(pszKeyName, wbuf); - } - break; - case TYPE_COLOR: - swprintf(wbuf, ARRAYSIZE(wbuf), L"%d %d %d %d", pKey->m_Color[0], pKey->m_Color[1], pKey->m_Color[2], pKey->m_Color[3]); - SetWString(pszKeyName, wbuf); - break; - - case TYPE_WSTRING: - break; - case TYPE_STRING: - { - size_t bufSize = strlen(pKey->m_sValue) + 1; - wchar_t* pWBuf = new wchar_t[bufSize]; - int result = V_UTF8ToUnicode(pKey->m_sValue, pWBuf, static_cast(bufSize * sizeof(wchar_t))); - if (result >= 0) // may be a zero length string - { - SetWString(pszKeyName, pWBuf); - delete[] pWBuf; - } - else - { - delete[] pWBuf; - return pwszDefaultValue; - } - - break; - } - default: - return pwszDefaultValue; - }; - - return reinterpret_cast(pKey->m_wsValue); - } - return pwszDefaultValue; -} - -//----------------------------------------------------------------------------- -// Purpose: Gets a color -// Input : *pszKeyName - -// &defaultColor - -// Output : Color -//----------------------------------------------------------------------------- -Color KeyValues::GetColor(const char* pszKeyName, const Color& defaultColor) -{ - Color color = defaultColor; - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - { - if (pKey->m_iDataType == TYPE_COLOR) - { - color[0] = pKey->m_Color[0]; - color[1] = pKey->m_Color[1]; - color[2] = pKey->m_Color[2]; - color[3] = pKey->m_Color[3]; - } - else if (pKey->m_iDataType == TYPE_FLOAT) - { - color[0] = static_cast(pKey->m_flValue); - } - else if (pKey->m_iDataType == TYPE_INT) - { - color[0] = static_cast(pKey->m_iValue); - } - else if (pKey->m_iDataType == TYPE_STRING) - { - // parse the colors out of the string - float a = 0, b = 0, c = 0, d = 0; - sscanf(pKey->m_sValue, "%f %f %f %f", &a, &b, &c, &d); - color[0] = static_cast(a); - color[1] = static_cast(b); - color[2] = static_cast(c); - color[3] = static_cast(d); - } - } - return color; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the data type of the value stored in a keyName -// Input : *pszKeyName - -//----------------------------------------------------------------------------- -KeyValuesTypes_t KeyValues::GetDataType(const char* pszKeyName) -{ - KeyValues* pKey = FindKey(pszKeyName, false); - if (pKey) - return static_cast(pKey->m_iDataType); - - return TYPE_NONE; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the data type of the value stored in this keyName -//----------------------------------------------------------------------------- -KeyValuesTypes_t KeyValues::GetDataType(void) const -{ - return static_cast(m_iDataType); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the integer value of a keyName. -// Input : *pszKeyName - -// iValue - -//----------------------------------------------------------------------------- -void KeyValues::SetInt(const char* pszKeyName, int iValue) -{ - KeyValues* pKey = FindKey(pszKeyName, true); - if (pKey) - { - pKey->m_iValue = iValue; - pKey->m_iDataType = TYPE_INT; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the integer value of a keyName. -//----------------------------------------------------------------------------- -void KeyValues::SetUint64(const char* pszKeyName, uint64_t nValue) -{ - KeyValues* pKey = FindKey(pszKeyName, true); - - if (pKey) - { - // delete the old value - delete[] pKey->m_sValue; - // make sure we're not storing the WSTRING - as we're converting over to STRING - delete[] pKey->m_wsValue; - pKey->m_wsValue = nullptr; - - pKey->m_sValue = new char[sizeof(uint64_t)]; - *(reinterpret_cast(pKey->m_sValue)) = nValue; - pKey->m_iDataType = TYPE_UINT64; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the float value of a keyName. -// Input : *pszKeyName - -// flValue - -//----------------------------------------------------------------------------- -void KeyValues::SetFloat(const char* pszKeyName, float flValue) -{ - KeyValues* pKey = FindKey(pszKeyName, true); - if (pKey) - { - pKey->m_flValue = flValue; - pKey->m_iDataType = TYPE_FLOAT; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the name value of a keyName. -// Input : *pszSetName - -//----------------------------------------------------------------------------- -void KeyValues::SetName(const char* pszSetName) -{ - HKeySymbol hCaseSensitiveKeyName = INVALID_KEY_SYMBOL, hCaseInsensitiveKeyName = INVALID_KEY_SYMBOL; - hCaseSensitiveKeyName = - KeyValuesSystem()->m_pVtable->GetSymbolForStringCaseSensitive(KeyValuesSystem(), hCaseInsensitiveKeyName, pszSetName, false); - - m_iKeyName = hCaseInsensitiveKeyName; - SPLIT_3_BYTES_INTO_1_AND_2(m_iKeyNameCaseSensitive1, m_iKeyNameCaseSensitive2, hCaseSensitiveKeyName); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the pointer value of a keyName. -// Input : *pszKeyName - -// *pValue - -//----------------------------------------------------------------------------- -void KeyValues::SetPtr(const char* pszKeyName, void* pValue) -{ - KeyValues* pKey = FindKey(pszKeyName, true); - - if (pKey) - { - pKey->m_pValue = pValue; - pKey->m_iDataType = TYPE_PTR; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the string value (internal) -// Input : *pszValue - -//----------------------------------------------------------------------------- -void KeyValues::SetStringValue(char const* pszValue) -{ - // delete the old value - delete[] m_sValue; - // make sure we're not storing the WSTRING - as we're converting over to STRING - delete[] m_wsValue; - m_wsValue = nullptr; - - if (!pszValue) - { - // ensure a valid value - pszValue = ""; - } - - // allocate memory for the new value and copy it in - size_t len = strlen(pszValue); - m_sValue = new char[len + 1]; - memcpy(m_sValue, pszValue, len + 1); - - m_iDataType = TYPE_STRING; -} - -//----------------------------------------------------------------------------- -// Purpose: Sets this key's peer to the KeyValues passed in -// Input : *pDat - -//----------------------------------------------------------------------------- -void KeyValues::SetNextKey(KeyValues* pDat) -{ - m_pPeer = pDat; -} - -//----------------------------------------------------------------------------- -// Purpose: Set the string value of a keyName. -// Input : *pszKeyName - -// *pszValue - -//----------------------------------------------------------------------------- -void KeyValues::SetString(const char* pszKeyName, const char* pszValue) -{ - if (KeyValues* pKey = FindKey(pszKeyName, true)) - { - pKey->SetStringValue(pszValue); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Set the string value of a keyName. -// Input : *pszKeyName - -// *pwszValue - -//----------------------------------------------------------------------------- -void KeyValues::SetWString(const char* pszKeyName, const wchar_t* pwszValue) -{ - KeyValues* pKey = FindKey(pszKeyName, true); - if (pKey) - { - // delete the old value - delete[] pKey->m_wsValue; - // make sure we're not storing the STRING - as we're converting over to WSTRING - delete[] pKey->m_sValue; - pKey->m_sValue = nullptr; - - if (!pwszValue) - { - // ensure a valid value - pwszValue = L""; - } - - // allocate memory for the new value and copy it in - size_t len = wcslen(pwszValue); - pKey->m_wsValue = new wchar_t[len + 1]; - memcpy(pKey->m_wsValue, pwszValue, (len + 1) * sizeof(wchar_t)); - - pKey->m_iDataType = TYPE_WSTRING; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Sets a color -// Input : *pszKeyName - -// color - -//----------------------------------------------------------------------------- -void KeyValues::SetColor(const char* pszKeyName, Color color) -{ - KeyValues* pKey = FindKey(pszKeyName, true); - - if (pKey) - { - pKey->m_iDataType = TYPE_COLOR; - pKey->m_Color[0] = color[0]; - pKey->m_Color[1] = color[1]; - pKey->m_Color[2] = color[2]; - pKey->m_Color[3] = color[3]; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &src - -//----------------------------------------------------------------------------- -void KeyValues::RecursiveCopyKeyValues(KeyValues& src) -{ - // garymcthack - need to check this code for possible buffer overruns. - - m_iKeyName = src.m_iKeyName; - m_iKeyNameCaseSensitive1 = src.m_iKeyNameCaseSensitive1; - m_iKeyNameCaseSensitive2 = src.m_iKeyNameCaseSensitive2; - - if (!src.m_pSub) - { - m_iDataType = src.m_iDataType; - char buf[256]; - switch (src.m_iDataType) - { - case TYPE_NONE: - break; - case TYPE_STRING: - if (src.m_sValue) - { - size_t len = strlen(src.m_sValue) + 1; - m_sValue = new char[len]; - strncpy(m_sValue, src.m_sValue, len); - } - break; - case TYPE_INT: - { - m_iValue = src.m_iValue; - snprintf(buf, sizeof(buf), "%d", m_iValue); - size_t len = strlen(buf) + 1; - m_sValue = new char[len]; - strncpy(m_sValue, buf, len); - } - break; - case TYPE_FLOAT: - { - m_flValue = src.m_flValue; - snprintf(buf, sizeof(buf), "%f", m_flValue); - size_t len = strlen(buf) + 1; - m_sValue = new char[len]; - strncpy(m_sValue, buf, len); - } - break; - case TYPE_PTR: - { - m_pValue = src.m_pValue; - } - break; - case TYPE_UINT64: - { - m_sValue = new char[sizeof(uint64_t)]; - memcpy(m_sValue, src.m_sValue, sizeof(uint64_t)); - } - break; - case TYPE_COLOR: - { - m_Color[0] = src.m_Color[0]; - m_Color[1] = src.m_Color[1]; - m_Color[2] = src.m_Color[2]; - m_Color[3] = src.m_Color[3]; - } - break; - - default: - { - // do nothing . .what the heck is this? - assert(0); - } - break; - } - } - - // Handle the immediate child - if (src.m_pSub) - { - m_pSub = new KeyValues; - - m_pSub->Init(); - m_pSub->SetName(nullptr); - - m_pSub->RecursiveCopyKeyValues(*src.m_pSub); - } - - // Handle the immediate peer - if (src.m_pPeer) - { - m_pPeer = new KeyValues; - - m_pPeer->Init(); - m_pPeer->SetName(nullptr); - - m_pPeer->RecursiveCopyKeyValues(*src.m_pPeer); - } -} - -//----------------------------------------------------------------------------- -// Purpose: Make a new copy of all subkeys, add them all to the passed-in keyvalues -// Input : *pParent - -//----------------------------------------------------------------------------- -void KeyValues::CopySubkeys(KeyValues* pParent) const -{ - // recursively copy subkeys - // Also maintain ordering.... - KeyValues* pPrev = nullptr; - for (KeyValues* pSub = m_pSub; pSub != nullptr; pSub = pSub->m_pPeer) - { - // take a copy of the subkey - KeyValues* pKey = pSub->MakeCopy(); - - // add into subkey list - if (pPrev) - { - pPrev->m_pPeer = pKey; - } - else - { - pParent->m_pSub = pKey; - } - pKey->m_pPeer = nullptr; - pPrev = pKey; - } -} - -//----------------------------------------------------------------------------- -// Purpose: Makes a copy of the whole key-value pair set -// Output : KeyValues* -//----------------------------------------------------------------------------- -KeyValues* KeyValues::MakeCopy(void) const -{ - KeyValues* pNewKeyValue = new KeyValues; - - pNewKeyValue->Init(); - pNewKeyValue->SetName(GetName()); - - // copy data - pNewKeyValue->m_iDataType = m_iDataType; - switch (m_iDataType) - { - case TYPE_STRING: - { - if (m_sValue) - { - size_t len = strlen(m_sValue); - assert(!pNewKeyValue->m_sValue); - pNewKeyValue->m_sValue = new char[len + 1]; - memcpy(pNewKeyValue->m_sValue, m_sValue, len + 1); - } - } - break; - case TYPE_WSTRING: - { - if (m_wsValue) - { - size_t len = wcslen(m_wsValue); - pNewKeyValue->m_wsValue = new wchar_t[len + 1]; - memcpy(pNewKeyValue->m_wsValue, m_wsValue, len + 1 * sizeof(wchar_t)); - } - } - break; - - case TYPE_INT: - pNewKeyValue->m_iValue = m_iValue; - break; - - case TYPE_FLOAT: - pNewKeyValue->m_flValue = m_flValue; - break; - - case TYPE_PTR: - pNewKeyValue->m_pValue = m_pValue; - break; - - case TYPE_COLOR: - pNewKeyValue->m_Color[0] = m_Color[0]; - pNewKeyValue->m_Color[1] = m_Color[1]; - pNewKeyValue->m_Color[2] = m_Color[2]; - pNewKeyValue->m_Color[3] = m_Color[3]; - break; - - case TYPE_UINT64: - pNewKeyValue->m_sValue = new char[sizeof(uint64_t)]; - memcpy(pNewKeyValue->m_sValue, m_sValue, sizeof(uint64_t)); - break; - }; - - // recursively copy subkeys - CopySubkeys(pNewKeyValue); - return pNewKeyValue; -} - -ON_DLL_LOAD("vstdlib.dll", KeyValues, (CModule module)) -{ - V_UTF8ToUnicode = module.GetExport("V_UTF8ToUnicode").RCast(); - V_UnicodeToUTF8 = module.GetExport("V_UnicodeToUTF8").RCast(); - KeyValuesSystem = module.GetExport("KeyValuesSystem").RCast(); -} - -AUTOHOOK_INIT() - -// clang-format off -AUTOHOOK(KeyValues__LoadFromBuffer, engine.dll + 0x426C30, -char, __fastcall, (KeyValues* self, const char* pResourceName, const char* pBuffer, void* pFileSystem, void* a5, void* a6, int a7)) -// clang-format on -{ - static void* pSavedFilesystemPtr = nullptr; - - // this is just to allow playlists to get a valid pFileSystem ptr for kv building, other functions that call this particular overload of - // LoadFromBuffer seem to get called on network stuff exclusively not exactly sure what the address wanted here is, so just taking it - // from a function call that always happens before playlists is loaded - - // note: would be better if we could serialize this to disk for playlists, as this method breaks saving playlists in demos - if (pFileSystem != nullptr) - pSavedFilesystemPtr = pFileSystem; - if (!pFileSystem && !strcmp(pResourceName, "playlists")) - pFileSystem = pSavedFilesystemPtr; - - return KeyValues__LoadFromBuffer(self, pResourceName, pBuffer, pFileSystem, a5, a6, a7); -} - -ON_DLL_LOAD("engine.dll", EngineKeyValues, (CModule module)) -{ - AUTOHOOK_DISPATCH() -} diff --git a/NorthstarDLL/shared/keyvalues.h b/NorthstarDLL/shared/keyvalues.h deleted file mode 100644 index bd62797e..00000000 --- a/NorthstarDLL/shared/keyvalues.h +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once -#include "core/math/color.h" - -enum KeyValuesTypes_t : char -{ - TYPE_NONE = 0x0, - TYPE_STRING = 0x1, - TYPE_INT = 0x2, - TYPE_FLOAT = 0x3, - TYPE_PTR = 0x4, - TYPE_WSTRING = 0x5, - TYPE_COLOR = 0x6, - TYPE_UINT64 = 0x7, - TYPE_COMPILED_INT_BYTE = 0x8, - TYPE_COMPILED_INT_0 = 0x9, - TYPE_COMPILED_INT_1 = 0xA, - TYPE_NUMTYPES = 0xB, -}; - -enum MergeKeyValuesOp_t -{ - MERGE_KV_ALL, - MERGE_KV_UPDATE, // update values are copied into storage, adding new keys to storage or updating existing ones - MERGE_KV_DELETE, // update values specify keys that get deleted from storage - MERGE_KV_BORROW, // update values only update existing keys in storage, keys in update that do not exist in storage are discarded -}; - -//----------------------------------------------------------------------------- -// Purpose: Simple recursive data access class -// Used in vgui for message parameters and resource files -// Destructor deletes all child KeyValues nodes -// Data is stored in key (string names) - (string/int/float)value pairs called nodes. -// -// About KeyValues Text File Format: - -// It has 3 control characters '{', '}' and '"'. Names and values may be quoted or -// not. The quote '"' character must not be used within name or values, only for -// quoting whole tokens. You may use escape sequences wile parsing and add within a -// quoted token a \" to add quotes within your name or token. When using Escape -// Sequence the parser must now that by setting KeyValues::UsesEscapeSequences( true ), -// which it's off by default. Non-quoted tokens ends with a whitespace, '{', '}' and '"'. -// So you may use '{' and '}' within quoted tokens, but not for non-quoted tokens. -// An open bracket '{' after a key name indicates a list of subkeys which is finished -// with a closing bracket '}'. Subkeys use the same definitions recursively. -// Whitespaces are space, return, newline and tabulator. Allowed Escape sequences -// are \n, \t, \\, \n and \". The number character '#' is used for macro purposes -// (eg #include), don't use it as first character in key names. -//----------------------------------------------------------------------------- -class KeyValues -{ -private: - KeyValues(); // for internal use only - -public: - // Constructors/destructors - KeyValues(const char* pszSetName); - KeyValues(const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue); - KeyValues(const char* pszSetName, const char* pszFirstKey, const wchar_t* pwszFirstValue); - KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue); - KeyValues( - const char* pszSetName, const char* pszFirstKey, const char* pszFirstValue, const char* pszSecondKey, const char* pszSecondValue); - KeyValues(const char* pszSetName, const char* pszFirstKey, int iFirstValue, const char* pszSecondKey, int iSecondValue); - ~KeyValues(void); - - void Init(void); - void Clear(void); - void DeleteThis(void); - void RemoveEverything(); - - KeyValues* FindKey(const char* pKeyName, bool bCreate = false); - KeyValues* FindLastSubKey(void) const; - - void AddSubKey(KeyValues* pSubkey); - void RemoveSubKey(KeyValues* pSubKey); - void InsertSubKey(int nIndex, KeyValues* pSubKey); - bool ContainsSubKey(KeyValues* pSubKey); - void SwapSubKey(KeyValues* pExistingSubkey, KeyValues* pNewSubKey); - void ElideSubKey(KeyValues* pSubKey); - - // Data access - bool IsEmpty(const char* pszKeyName); - KeyValues* GetFirstTrueSubKey(void) const; - KeyValues* GetNextTrueSubKey(void) const; - KeyValues* GetFirstValue(void) const; - KeyValues* GetNextValue(void) const; - KeyValues* GetFirstSubKey() const; - KeyValues* GetNextKey() const; - const char* GetName(void) const; - int GetInt(const char* pszKeyName, int iDefaultValue); - uint64_t GetUint64(const char* pszKeyName, uint64_t nDefaultValue); - void* GetPtr(const char* pszKeyName, void* pDefaultValue); - float GetFloat(const char* pszKeyName, float flDefaultValue); - const char* GetString(const char* pszKeyName = nullptr, const char* pszDefaultValue = ""); - const wchar_t* GetWString(const char* pszKeyName = nullptr, const wchar_t* pwszDefaultValue = L""); - Color GetColor(const char* pszKeyName, const Color& defaultColor); - KeyValuesTypes_t GetDataType(const char* pszKeyName); - KeyValuesTypes_t GetDataType(void) const; - - // Key writing - void SetInt(const char* pszKeyName, int iValue); - void SetUint64(const char* pszKeyName, uint64_t nValue); - void SetPtr(const char* pszKeyName, void* pValue); - void SetNextKey(KeyValues* pDat); - void SetName(const char* pszName); - void SetString(const char* pszKeyName, const char* pszValue); - void SetWString(const char* pszKeyName, const wchar_t* pwszValue); - void SetStringValue(char const* pszValue); - void SetColor(const char* pszKeyName, Color color); - void SetFloat(const char* pszKeyName, float flValue); - - void RecursiveCopyKeyValues(KeyValues& src); - void CopySubkeys(KeyValues* pParent) const; - KeyValues* MakeCopy(void) const; - -public: - uint32_t m_iKeyName : 24; // 0x0000 - uint32_t m_iKeyNameCaseSensitive1 : 8; // 0x0003 - char* m_sValue; // 0x0008 - wchar_t* m_wsValue; // 0x0010 - union // 0x0018 - { - int m_iValue; - float m_flValue; - void* m_pValue; - unsigned char m_Color[4]; - }; - char m_szShortName[8]; // 0x0020 - char m_iDataType; // 0x0028 - char m_bHasEscapeSequences; // 0x0029 - uint16_t m_iKeyNameCaseSensitive2; // 0x002A - KeyValues* m_pPeer; // 0x0030 - KeyValues* m_pSub; // 0x0038 - KeyValues* m_pChain; // 0x0040 -}; diff --git a/NorthstarDLL/shared/maxplayers.cpp b/NorthstarDLL/shared/maxplayers.cpp deleted file mode 100644 index 711193d4..00000000 --- a/NorthstarDLL/shared/maxplayers.cpp +++ /dev/null @@ -1,640 +0,0 @@ -#include "core/tier0.h" -#include "maxplayers.h" - -AUTOHOOK_INIT() - -// never set this to anything below 32 -#define NEW_MAX_PLAYERS 64 -// dg note: the theoretical limit is actually 100, 76 works without entity issues, and 64 works without clientside prediction issues. - -#define PAD_NUMBER(number, boundary) (((number) + ((boundary)-1)) / (boundary)) * (boundary) - -// this is horrible -constexpr int PlayerResource_Name_Start = 0; // Start of modded allocated space. -constexpr int PlayerResource_Name_Size = ((NEW_MAX_PLAYERS + 1) * 8); // const char* m_szName[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_Ping_Start = PlayerResource_Name_Start + PlayerResource_Name_Size; -constexpr int PlayerResource_Ping_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int m_iPing[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_Team_Start = PlayerResource_Ping_Start + PlayerResource_Ping_Size; -constexpr int PlayerResource_Team_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int m_iTeam[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_PRHealth_Start = PlayerResource_Team_Start + PlayerResource_Team_Size; -constexpr int PlayerResource_PRHealth_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int m_iPRHealth[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_Connected_Start = PlayerResource_PRHealth_Start + PlayerResource_PRHealth_Size; -constexpr int PlayerResource_Connected_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int (used as a bool) m_bConnected[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_Alive_Start = PlayerResource_Connected_Start + PlayerResource_Connected_Size; -constexpr int PlayerResource_Alive_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int (used as a bool) m_bAlive[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_BoolStats_Start = PlayerResource_Alive_Start + PlayerResource_Alive_Size; -constexpr int PlayerResource_BoolStats_Size = ((NEW_MAX_PLAYERS + 1) * 4); // int (used as a bool idk) m_boolStats[MAX_PLAYERS + 1]; - -constexpr int PlayerResource_KillStats_Start = PlayerResource_BoolStats_Start + PlayerResource_BoolStats_Size; -constexpr int PlayerResource_KillStats_Length = PAD_NUMBER((NEW_MAX_PLAYERS + 1) * 6, 4); -constexpr int PlayerResource_KillStats_Size = (PlayerResource_KillStats_Length * 6); // int m_killStats[MAX_PLAYERS + 1][6]; - -constexpr int PlayerResource_ScoreStats_Start = PlayerResource_KillStats_Start + PlayerResource_KillStats_Size; -constexpr int PlayerResource_ScoreStats_Length = PAD_NUMBER((NEW_MAX_PLAYERS + 1) * 5, 4); -constexpr int PlayerResource_ScoreStats_Size = (PlayerResource_ScoreStats_Length * 4); // int m_scoreStats[MAX_PLAYERS + 1][5]; - -// must be the usage of the last field to account for any possible paddings -constexpr int PlayerResource_TotalSize = PlayerResource_ScoreStats_Start + PlayerResource_ScoreStats_Size; - -constexpr int Team_PlayerArray_AddedLength = NEW_MAX_PLAYERS - 32; -constexpr int Team_PlayerArray_AddedSize = PAD_NUMBER(Team_PlayerArray_AddedLength * 8, 4); -constexpr int Team_AddedSize = Team_PlayerArray_AddedSize; - -bool MaxPlayersIncreaseEnabled() -{ - static bool bMaxPlayersIncreaseEnabled = CommandLine()->CheckParm("-experimentalmaxplayersincrease"); - return bMaxPlayersIncreaseEnabled; -} - -int GetMaxPlayers() -{ - if (MaxPlayersIncreaseEnabled()) - return NEW_MAX_PLAYERS; - - return 32; -} - -template void ChangeOffset(CMemoryAddress addr, unsigned int offset) -{ - addr.Patch((BYTE*)&offset, sizeof(T)); -} - -// clang-format off -AUTOHOOK(StringTables_CreateStringTable, engine.dll + 0x22E220, -void*,, (void* thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags)) -// clang-format on -{ - // Change the amount of entries to account for a bigger player amount - if (!strcmp(name, "userinfo")) - { - int maxPlayersPowerOf2 = 1; - while (maxPlayersPowerOf2 < NEW_MAX_PLAYERS) - maxPlayersPowerOf2 <<= 1; - - maxentries = maxPlayersPowerOf2; - } - - return StringTables_CreateStringTable(thisptr, name, maxentries, userdatafixedsize, userdatanetworkbits, flags); -} - -ON_DLL_LOAD("engine.dll", MaxPlayersOverride_Engine, (CModule module)) -{ - if (!MaxPlayersIncreaseEnabled()) - return; - - AUTOHOOK_DISPATCH_MODULE(engine.dll) - - // patch GetPlayerLimits to ignore the boundary limit - module.Offset(0x116458).Patch("0xEB"); // jle => jmp - - // patch ED_Alloc to change nFirstIndex - ChangeOffset(module.Offset(0x18F46C + 1), NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1) - - // patch CGameServer::SpawnServer to change GetMaxClients inline - ChangeOffset(module.Offset(0x119543 + 2), NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1) - - // patch CGameServer::SpawnServer to change for loop - ChangeOffset(module.Offset(0x11957F + 2), NEW_MAX_PLAYERS); // original: 32 - - // patch CGameServer::SpawnServer to change for loop (there are two) - ChangeOffset(module.Offset(0x119586 + 2), NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1) - - // patch max players somewhere in CClientState - ChangeOffset(module.Offset(0x1A162C + 2), NEW_MAX_PLAYERS - 1); // original: 31 (32 - 1) - - // patch max players in userinfo stringtable creation - /*{ - int maxPlayersPowerOf2 = 1; - while (maxPlayersPowerOf2 < NEW_MAX_PLAYERS) - maxPlayersPowerOf2 <<= 1; - ChangeOffset((char*)baseAddress + 0x114B79 + 3, maxPlayersPowerOf2); // original: 32 - }*/ - // this is not supposed to work at all but it does on 64 players (how) - // proper fix below - - // patch max players in userinfo stringtable creation loop - ChangeOffset(module.Offset(0x114C48 + 2), NEW_MAX_PLAYERS); // original: 32 - - // do not load prebaked SendTable message list - module.Offset(0x75859).Patch("EB"); // jnz -> jmp -} - -typedef void (*RunUserCmds_Type)(bool a1, float a2); -RunUserCmds_Type RunUserCmds_Original; - -HMODULE serverBase = 0; -auto RandomIntZeroMax = (__int64(__fastcall*)())0; - -// lazy rebuild -// clang-format off -AUTOHOOK(RunUserCmds, server.dll + 0x483D10, -void,, (bool a1, float a2)) -// clang-format on -{ - unsigned char v3; // bl - int v5; // er14 - int i; // edi - __int64 v7; // rax - DWORD* v8; // rbx - int v9; // edi - __int64* v10; // rsi - __int64 v11; // rax - int v12; // er12 - __int64 v13; // rdi - int v14; // ebx - int v15; // eax - __int64 v16; // r8 - int v17; // edx - char v18; // r15 - char v19; // bp - int v20; // esi - __int64* v21; // rdi - __int64 v22; // rcx - bool v23; // al - __int64 v24; // rax - __int64 v25[NEW_MAX_PLAYERS]; // [rsp+20h] [rbp-138h] BYREF - - uintptr_t base = (__int64)serverBase; - auto g_pGlobals = *(__int64*)(base + 0xBFBE08); - __int64 globals = g_pGlobals; - - auto g_pEngineServer = *(__int64*)(base + 0xBFBD98); - - auto qword_1814D9648 = *(__int64*)(base + 0x14D9648); - auto qword_1814DA408 = *(__int64*)(base + 0x14DA408); - auto qword_1812107E8 = *(__int64*)(base + 0x12107E8); - auto qword_1812105A8 = *(__int64*)(base + 0x12105A8); - - auto UTIL_PlayerByIndex = (__int64(__fastcall*)(int index))(base + 0x26AA10); - auto sub_180485590 = (void(__fastcall*)(__int64))(base + 0x485590); - auto sub_18058CD80 = (void(__fastcall*)(__int64))(base + 0x58CD80); - auto sub_1805A6D90 = (void(__fastcall*)(__int64))(base + 0x5A6D90); - auto sub_1805A6E50 = (bool(__fastcall*)(__int64, int, char))(base + 0x5A6E50); - auto sub_1805A6C20 = (void(__fastcall*)(__int64))(base + 0x5A6C20); - - v3 = *(unsigned char*)(g_pGlobals + 73); - if (*(DWORD*)(qword_1814D9648 + 92) && - ((*(unsigned __int8(__fastcall**)(__int64))(*(__int64*)g_pEngineServer + 32))(g_pEngineServer) || - !*(DWORD*)(qword_1814DA408 + 92)) && - v3) - { - globals = g_pGlobals; - v5 = 1; - for (i = 1; i <= *(DWORD*)(g_pGlobals + 52); ++i) - { - v7 = UTIL_PlayerByIndex(i); - v8 = (DWORD*)v7; - if (v7) - { - *(__int64*)(base + 0x1210420) = v7; - *(float*)(g_pGlobals + 16) = a2; - if (!a1) - sub_18058CD80(v7); - sub_1805A6D90((__int64)v8); - } - globals = g_pGlobals; - } - memset(v25, 0, sizeof(v25)); - v9 = 0; - if (*(int*)(globals + 52) > 0) - { - v10 = v25; - do - { - v11 = UTIL_PlayerByIndex(++v9); - globals = g_pGlobals; - *v10++ = v11; - } while (v9 < *(DWORD*)(globals + 52)); - } - v12 = *(DWORD*)(qword_1812107E8 + 92); - if (*(DWORD*)(qword_1812105A8 + 92)) - { - v13 = *(DWORD*)(globals + 52) - 1; - if (v13 >= 1) - { - v14 = *(DWORD*)(globals + 52); - do - { - v15 = RandomIntZeroMax(); - v16 = v25[v13--]; - v17 = v15 % v14--; - v25[v13 + 1] = v25[v17]; - v25[v17] = v16; - } while (v13 >= 1); - globals = g_pGlobals; - } - } - v18 = 1; - do - { - v19 = 0; - v20 = 0; - if (*(int*)(globals + 52) > 0) - { - v21 = v25; - do - { - v22 = *v21; - if (*v21) - { - *(__int64*)(base + 0x1210420) = *v21; - *(float*)(globals + 16) = a2; - v23 = sub_1805A6E50(v22, v12, v18); - globals = g_pGlobals; - if (v23) - v19 = 1; - else - *v21 = 0; - } - ++v20; - ++v21; - } while (v20 < *(DWORD*)(globals + 52)); - } - v18 = 0; - } while (v19); - if (*(int*)(globals + 52) >= 1) - { - do - { - v24 = UTIL_PlayerByIndex(v5); - if (v24) - { - *(__int64*)(base + 0x1210420) = v24; - *(float*)(g_pGlobals + 16) = a2; - sub_1805A6C20(v24); - } - ++v5; - } while (v5 <= *(DWORD*)(g_pGlobals + 52)); - } - sub_180485590(*(__int64*)(base + 0xB7B2D8)); - } -} - -// clang-format off -AUTOHOOK(SendPropArray2, server.dll + 0x12B130, -__int64,, (__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1)) -// clang-format on -{ - // Change the amount of elements to account for a bigger player amount - if (!strcmp(name, "\"player_array\"")) - elements = NEW_MAX_PLAYERS; - - return SendPropArray2(recvProp, elements, flags, name, proxyFn, unk1); -} - -ON_DLL_LOAD("server.dll", MaxPlayersOverride_Server, (CModule module)) -{ - if (!MaxPlayersIncreaseEnabled()) - return; - - AUTOHOOK_DISPATCH_MODULE(server.dll) - - // get required data - serverBase = (HMODULE)module.m_nAddress; - RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax")); - - // patch max players amount - ChangeOffset(module.Offset(0x9A44D + 3), NEW_MAX_PLAYERS); // 0x20 (32) => 0x80 (128) - - // patch SpawnGlobalNonRewinding to change forced edict index - ChangeOffset(module.Offset(0x2BC403 + 2), NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1) - - constexpr int CPlayerResource_OriginalSize = 4776; - constexpr int CPlayerResource_AddedSize = PlayerResource_TotalSize; - constexpr int CPlayerResource_ModifiedSize = CPlayerResource_OriginalSize + CPlayerResource_AddedSize; - - // CPlayerResource class allocation function - allocate a bigger amount to fit all new max player data - ChangeOffset(module.Offset(0x5C560A + 1), CPlayerResource_ModifiedSize); - - // DT_PlayerResource::m_iPing SendProp - ChangeOffset(module.Offset(0x5C5059 + 2), CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0x5C50A8 + 2), CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0x5C50E2 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_iPing DataMap - ChangeOffset(module.Offset(0xB94598), CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0xB9459C), NEW_MAX_PLAYERS + 1); - ChangeOffset(module.Offset(0xB945C0), PlayerResource_Ping_Size); - - // DT_PlayerResource::m_iTeam SendProp - ChangeOffset(module.Offset(0x5C5110 + 2), CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0x5C519C + 2), CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0x5C517E + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_iTeam DataMap - ChangeOffset(module.Offset(0xB94600), CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0xB94604), NEW_MAX_PLAYERS + 1); - ChangeOffset(module.Offset(0xB94628), PlayerResource_Team_Size); - - // DT_PlayerResource::m_iPRHealth SendProp - ChangeOffset(module.Offset(0x5C51C0 + 2), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0x5C5204 + 2), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0x5C523E + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_iPRHealth DataMap - ChangeOffset(module.Offset(0xB94668), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0xB9466C), NEW_MAX_PLAYERS + 1); - ChangeOffset(module.Offset(0xB94690), PlayerResource_PRHealth_Size); - - // DT_PlayerResource::m_bConnected SendProp - ChangeOffset(module.Offset(0x5C526C + 2), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0x5C52B4 + 2), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0x5C52EE + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_bConnected DataMap - ChangeOffset(module.Offset(0xB946D0), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0xB946D4), NEW_MAX_PLAYERS + 1); - ChangeOffset(module.Offset(0xB946F8), PlayerResource_Connected_Size); - - // DT_PlayerResource::m_bAlive SendProp - ChangeOffset(module.Offset(0x5C5321 + 2), CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0x5C5364 + 2), CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0x5C539E + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_bAlive DataMap - ChangeOffset(module.Offset(0xB94738), CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0xB9473C), NEW_MAX_PLAYERS + 1); - ChangeOffset(module.Offset(0xB94760), PlayerResource_Alive_Size); - - // DT_PlayerResource::m_boolStats SendProp - ChangeOffset(module.Offset(0x5C53CC + 2), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0x5C5414 + 2), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0x5C544E + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_boolStats DataMap - ChangeOffset(module.Offset(0xB947A0), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0xB947A4), NEW_MAX_PLAYERS + 1); - ChangeOffset(module.Offset(0xB947C8), PlayerResource_BoolStats_Size); - - // DT_PlayerResource::m_killStats SendProp - ChangeOffset(module.Offset(0x5C547C + 2), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0x5C54E2 + 2), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0x5C54FE + 4), PlayerResource_KillStats_Length); - - // DT_PlayerResource::m_killStats DataMap - ChangeOffset(module.Offset(0xB94808), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0xB9480C), PlayerResource_KillStats_Length); - ChangeOffset(module.Offset(0xB94830), PlayerResource_KillStats_Size); - - // DT_PlayerResource::m_scoreStats SendProp - ChangeOffset(module.Offset(0x5C5528 + 2), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0x5C5576 + 2), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0x5C5584 + 4), PlayerResource_ScoreStats_Length); - - // DT_PlayerResource::m_scoreStats DataMap - ChangeOffset(module.Offset(0xB94870), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0xB94874), PlayerResource_ScoreStats_Length); - ChangeOffset(module.Offset(0xB94898), PlayerResource_ScoreStats_Size); - - // CPlayerResource::UpdatePlayerData - m_bConnected - ChangeOffset(module.Offset(0x5C66EE + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0x5C672E + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - - // CPlayerResource::UpdatePlayerData - m_iPing - ChangeOffset(module.Offset(0x5C6394 + 4), CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0x5C63DB + 4), CPlayerResource_OriginalSize + PlayerResource_Ping_Start); - - // CPlayerResource::UpdatePlayerData - m_iTeam - ChangeOffset(module.Offset(0x5C63FD + 4), CPlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0x5C6442 + 4), CPlayerResource_OriginalSize + PlayerResource_Team_Start); - - // CPlayerResource::UpdatePlayerData - m_iPRHealth - ChangeOffset(module.Offset(0x5C645B + 4), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0x5C64A0 + 4), CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - - // CPlayerResource::UpdatePlayerData - m_bConnected - ChangeOffset(module.Offset(0x5C64AA + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0x5C64F0 + 4), CPlayerResource_OriginalSize + PlayerResource_Connected_Start); - - // CPlayerResource::UpdatePlayerData - m_bAlive - ChangeOffset(module.Offset(0x5C650A + 4), CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0x5C654F + 4), CPlayerResource_OriginalSize + PlayerResource_Alive_Start); - - // CPlayerResource::UpdatePlayerData - m_boolStats - ChangeOffset(module.Offset(0x5C6557 + 4), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0x5C65A5 + 4), CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - - // CPlayerResource::UpdatePlayerData - m_scoreStats - ChangeOffset(module.Offset(0x5C65C2 + 3), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0x5C65E3 + 4), CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - - // CPlayerResource::UpdatePlayerData - m_killStats - ChangeOffset(module.Offset(0x5C6654 + 3), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0x5C665B + 3), CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); - - *module.Offset(0x14E7390).RCast() = 0; - auto DT_PlayerResource_Construct = module.Offset(0x5C4FE0).RCast<__int64(__fastcall*)()>(); - DT_PlayerResource_Construct(); - - constexpr int CTeam_OriginalSize = 3336; - constexpr int CTeam_AddedSize = Team_AddedSize; - constexpr int CTeam_ModifiedSize = CTeam_OriginalSize + CTeam_AddedSize; - - // CTeam class allocation function - allocate a bigger amount to fit all new team player data - ChangeOffset(module.Offset(0x23924A + 1), CTeam_ModifiedSize); - - // CTeam::CTeam - increase memset length to clean newly allocated data - ChangeOffset(module.Offset(0x2395AE + 2), 256 + CTeam_AddedSize); - - *module.Offset(0xC945A0).RCast() = 0; - auto DT_Team_Construct = module.Offset(0x238F50).RCast<__int64(__fastcall*)()>(); - DT_Team_Construct(); -} - -// clang-format off -AUTOHOOK(RecvPropArray2, client.dll + 0x1CEDA0, -__int64,, (__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn)) -// clang-format on -{ - // Change the amount of elements to account for a bigger player amount - if (!strcmp(name, "\"player_array\"")) - elements = NEW_MAX_PLAYERS; - - return RecvPropArray2(recvProp, elements, flags, name, proxyFn); -} - -ON_DLL_LOAD("client.dll", MaxPlayersOverride_Client, (CModule module)) -{ - if (!MaxPlayersIncreaseEnabled()) - return; - - AUTOHOOK_DISPATCH_MODULE(client.dll) - - constexpr int C_PlayerResource_OriginalSize = 5768; - constexpr int C_PlayerResource_AddedSize = PlayerResource_TotalSize; - constexpr int C_PlayerResource_ModifiedSize = C_PlayerResource_OriginalSize + C_PlayerResource_AddedSize; - - // C_PlayerResource class allocation function - allocate a bigger amount to fit all new max player data - ChangeOffset(module.Offset(0x164C41 + 1), C_PlayerResource_ModifiedSize); - - // C_PlayerResource::C_PlayerResource - change loop end value - ChangeOffset(module.Offset(0x1640C4 + 2), NEW_MAX_PLAYERS - 32); - - // C_PlayerResource::C_PlayerResource - change m_szName address - ChangeOffset( - module.Offset(0x1640D0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class - - // C_PlayerResource::C_PlayerResource - change m_szName address - ChangeOffset( - module.Offset(0x1640D0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class - - // C_PlayerResource::C_PlayerResource - increase memset length to clean newly allocated data - ChangeOffset(module.Offset(0x1640D0 + 3), 2244 + C_PlayerResource_AddedSize); - - // C_PlayerResource::UpdatePlayerName - change m_szName address - ChangeOffset(module.Offset(0x16431F + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName - change m_szName address 1 - ChangeOffset(module.Offset(0x1645B1 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName - change m_szName address 2 - ChangeOffset(module.Offset(0x1645C0 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName - change m_szName address 3 - ChangeOffset(module.Offset(0x1645DD + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName internal func - change m_szName address 1 - ChangeOffset(module.Offset(0x164B71 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName internal func - change m_szName address 2 - ChangeOffset(module.Offset(0x164B9B + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 1 - ChangeOffset(module.Offset(0x164641 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 2 - ChangeOffset(module.Offset(0x164650 + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 3 - ChangeOffset(module.Offset(0x16466D + 3), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 1 - ChangeOffset(module.Offset(0x164BA3 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 2 - ChangeOffset(module.Offset(0x164BCE + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 3 - ChangeOffset(module.Offset(0x164BE7 + 4), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - - // C_PlayerResource::m_szName - ChangeOffset(module.Offset(0xc350f8), C_PlayerResource_OriginalSize + PlayerResource_Name_Start); - ChangeOffset(module.Offset(0xc350f8 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource size - ChangeOffset(module.Offset(0x163415 + 6), C_PlayerResource_ModifiedSize); - - // DT_PlayerResource::m_iPing RecvProp - ChangeOffset(module.Offset(0x163492 + 2), C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0x1634D6 + 2), C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0x163515 + 5), NEW_MAX_PLAYERS + 1); - - // C_PlayerResource::m_iPing - ChangeOffset(module.Offset(0xc35170), C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); - ChangeOffset(module.Offset(0xc35170 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_iTeam RecvProp - ChangeOffset(module.Offset(0x163549 + 2), C_PlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0x1635C8 + 2), C_PlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0x1635AD + 5), NEW_MAX_PLAYERS + 1); - - // C_PlayerResource::m_iTeam - ChangeOffset(module.Offset(0xc351e8), C_PlayerResource_OriginalSize + PlayerResource_Team_Start); - ChangeOffset(module.Offset(0xc351e8 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_iPRHealth RecvProp - ChangeOffset(module.Offset(0x1635F9 + 2), C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0x163625 + 2), C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0x163675 + 5), NEW_MAX_PLAYERS + 1); - - // C_PlayerResource::m_iPRHealth - ChangeOffset(module.Offset(0xc35260), C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); - ChangeOffset(module.Offset(0xc35260 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_bConnected RecvProp - ChangeOffset(module.Offset(0x1636A9 + 2), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0x1636D5 + 2), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0x163725 + 5), NEW_MAX_PLAYERS + 1); - - // C_PlayerResource::m_bConnected - ChangeOffset(module.Offset(0xc352d8), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - ChangeOffset(module.Offset(0xc352d8 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_bAlive RecvProp - ChangeOffset(module.Offset(0x163759 + 2), C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0x163785 + 2), C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0x1637D5 + 5), NEW_MAX_PLAYERS + 1); - - // C_PlayerResource::m_bAlive - ChangeOffset(module.Offset(0xc35350), C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); - ChangeOffset(module.Offset(0xc35350 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_boolStats RecvProp - ChangeOffset(module.Offset(0x163809 + 2), C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0x163835 + 2), C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0x163885 + 5), NEW_MAX_PLAYERS + 1); - - // C_PlayerResource::m_boolStats - ChangeOffset(module.Offset(0xc353c8), C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); - ChangeOffset(module.Offset(0xc353c8 + 4), NEW_MAX_PLAYERS + 1); - - // DT_PlayerResource::m_killStats RecvProp - ChangeOffset(module.Offset(0x1638B3 + 2), C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0x1638E5 + 2), C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0x163935 + 5), PlayerResource_KillStats_Length); - - // C_PlayerResource::m_killStats - ChangeOffset(module.Offset(0xc35440), C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); - ChangeOffset(module.Offset(0xc35440 + 4), PlayerResource_KillStats_Length); - - // DT_PlayerResource::m_scoreStats RecvProp - ChangeOffset(module.Offset(0x163969 + 2), C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0x163995 + 2), C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0x1639E5 + 5), PlayerResource_ScoreStats_Length); - - // C_PlayerResource::m_scoreStats - ChangeOffset(module.Offset(0xc354b8), C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); - ChangeOffset(module.Offset(0xc354b8 + 4), PlayerResource_ScoreStats_Length); - - // C_PlayerResource::GetPlayerName - change m_bConnected address - ChangeOffset(module.Offset(0x164599 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - - // C_PlayerResource::GetPlayerName2 (?) - change m_bConnected address - ChangeOffset(module.Offset(0x164629 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - - // C_PlayerResource::GetPlayerName internal func - change m_bConnected address - ChangeOffset(module.Offset(0x164B13 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - - // Some other get name func (that seems to be unused) - change m_bConnected address - ChangeOffset(module.Offset(0x164860 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - - // Some other get name func 2 (that seems to be unused too) - change m_bConnected address - ChangeOffset(module.Offset(0x164834 + 3), C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); - - *module.Offset(0xC35068).RCast() = 0; - auto DT_PlayerResource_Construct = module.Offset(0x163400).RCast<__int64(__fastcall*)()>(); - DT_PlayerResource_Construct(); - - constexpr int C_Team_OriginalSize = 3200; - constexpr int C_Team_AddedSize = Team_AddedSize; - constexpr int C_Team_ModifiedSize = C_Team_OriginalSize + C_Team_AddedSize; - - // C_Team class allocation function - allocate a bigger amount to fit all new team player data - ChangeOffset(module.Offset(0x182321 + 1), C_Team_ModifiedSize); - - // C_Team::C_Team - increase memset length to clean newly allocated data - ChangeOffset(module.Offset(0x1804A2 + 2), 256 + C_Team_AddedSize); - - // DT_Team size - ChangeOffset(module.Offset(0xC3AA0C), C_Team_ModifiedSize); - - *module.Offset(0xC3AFF8).RCast() = 0; - auto DT_Team_Construct = module.Offset(0x17F950).RCast<__int64(__fastcall*)()>(); - DT_Team_Construct(); -} diff --git a/NorthstarDLL/shared/maxplayers.h b/NorthstarDLL/shared/maxplayers.h deleted file mode 100644 index 40a3ac58..00000000 --- a/NorthstarDLL/shared/maxplayers.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -int GetMaxPlayers(); diff --git a/NorthstarDLL/shared/misccommands.cpp b/NorthstarDLL/shared/misccommands.cpp deleted file mode 100644 index 15da6767..00000000 --- a/NorthstarDLL/shared/misccommands.cpp +++ /dev/null @@ -1,391 +0,0 @@ -#include "misccommands.h" -#include "core/convar/concommand.h" -#include "shared/playlist.h" -#include "engine/r2engine.h" -#include "client/r2client.h" -#include "core/tier0.h" -#include "engine/hoststate.h" -#include "masterserver/masterserver.h" -#include "mods/modmanager.h" -#include "server/auth/serverauthentication.h" -#include "squirrel/squirrel.h" - -void ConCommand_force_newgame(const CCommand& arg) -{ - if (arg.ArgC() < 2) - return; - - g_pHostState->m_iNextState = HostState_t::HS_NEW_GAME; - strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName)); -} - -void ConCommand_ns_start_reauth_and_leave_to_lobby(const CCommand& arg) -{ - // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect - g_pMasterServerManager->m_bNewgameAfterSelfAuth = true; - g_pMasterServerManager->AuthenticateWithOwnServer(g_pLocalPlayerUserID, g_pMasterServerManager->m_sOwnClientAuthToken); -} - -void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) -{ - if (g_pServerAuthentication->m_RemoteAuthenticationData.size()) - g_pCVar->FindVar("serverfilter")->SetValue(g_pServerAuthentication->m_RemoteAuthenticationData.begin()->first.c_str()); - - // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this - if (g_pSquirrel->m_pSQVM) - { - g_pServerAuthentication->m_bNeedLocalAuthForNewgame = true; - - // this won't set playlist correctly on remote clients, don't think they can set playlist until they've left which sorta - // fucks things should maybe set this in HostState_NewGame? - R2::SetCurrentPlaylist("tdm"); - strcpy(g_pHostState->m_levelName, "mp_lobby"); - g_pHostState->m_iNextState = HostState_t::HS_NEW_GAME; - } -} - -void ConCommand_cvar_setdefaultvalue(const CCommand& arg) -{ - if (arg.ArgC() < 3) - { - spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm"); - return; - } - - ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1)); - if (!pCvar) - { - spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm"); - return; - } - - // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default - int nLen = strlen(arg.Arg(2)); - char* pBuf = new char[nLen + 1]; - strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen); - - pCvar->m_pszDefaultValue = pBuf; -} - -void ConCommand_cvar_setvalueanddefaultvalue(const CCommand& arg) -{ - if (arg.ArgC() < 3) - { - spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm"); - return; - } - - ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1)); - if (!pCvar) - { - spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm"); - return; - } - - // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default - int nLen = strlen(arg.Arg(2)); - char* pBuf = new char[nLen + 1]; - strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen); - - pCvar->m_pszDefaultValue = pBuf; - pCvar->SetValue(pCvar->m_pszDefaultValue); -} - -void ConCommand_cvar_reset(const CCommand& arg) -{ - if (arg.ArgC() < 2) - { - spdlog::info("usage: cvar_reset mp_gamemode"); - return; - } - - ConVar* pCvar = g_pCVar->FindVar(arg.Arg(1)); - if (!pCvar) - { - spdlog::info("usage: cvar_reset mp_gamemode"); - return; - } - - // reset cvar - pCvar->SetValue(pCvar->m_pszDefaultValue); -} - -void AddMiscConCommands() -{ - RegisterConCommand( - "force_newgame", - ConCommand_force_newgame, - "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME", - FCVAR_NONE); - - RegisterConCommand( - "ns_start_reauth_and_leave_to_lobby", - ConCommand_ns_start_reauth_and_leave_to_lobby, - "called by the server, used to reauth and return the player to lobby when leaving a game", - FCVAR_SERVER_CAN_EXECUTE); - - // this is a concommand because we make a deferred call to it from another thread - RegisterConCommand("ns_end_reauth_and_leave_to_lobby", ConCommand_ns_end_reauth_and_leave_to_lobby, "", FCVAR_NONE); - - RegisterConCommand( - "cvar_setdefaultvalue", - ConCommand_cvar_setdefaultvalue, - "overwrites the default value of a cvar, for use with script and cvar_reset", - FCVAR_NONE); - RegisterConCommand( - "cvar_setvalueanddefaultvalue", - ConCommand_cvar_setvalueanddefaultvalue, - "overwrites the current value and default value of a cvar, for use with script and cvar_reset", - FCVAR_NONE); - RegisterConCommand("cvar_reset", ConCommand_cvar_reset, "resets a cvar's value to its default value", FCVAR_NONE); -} - -// fixes up various cvar flags to have more sane values -void FixupCvarFlags() -{ - if (CommandLine()->CheckParm("-allowdevcvars")) - { - // strip hidden and devonly cvar flags - int iNumCvarsAltered = 0; - for (auto& pair : g_pCVar->DumpToMap()) - { - // strip flags - int flags = pair.second->GetFlags(); - if (flags & FCVAR_DEVELOPMENTONLY) - { - flags &= ~FCVAR_DEVELOPMENTONLY; - iNumCvarsAltered++; - } - - if (flags & FCVAR_HIDDEN) - { - flags &= ~FCVAR_HIDDEN; - iNumCvarsAltered++; - } - - pair.second->m_nFlags = flags; - } - - spdlog::info("Removed {} hidden/devonly cvar flags", iNumCvarsAltered); - } - - // make all engine client commands FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS - // these are usually checked through CGameClient::IsEngineClientCommand, but we get more control over this if we just do it through - // cvar flags - const char** ppEngineClientCommands = CModule("engine.dll").Offset(0x7C5EF0).RCast(); - - int i = 0; - do - { - ConCommandBase* pCommand = g_pCVar->FindCommandBase(ppEngineClientCommands[i]); - if (pCommand) // not all the commands in this array actually exist in respawn source - pCommand->m_nFlags |= FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS; - } while (ppEngineClientCommands[++i]); - - // array of cvars and the flags we want to add to them - const std::vector> CVAR_FIXUP_ADD_FLAGS = { - // system commands (i.e. necessary for proper functionality) - // servers need to be able to disconnect - {"disconnect", FCVAR_SERVER_CAN_EXECUTE}, - - // cheat commands - {"give", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"give_server", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"givecurrentammo", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"takecurrentammo", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - - {"switchclass", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"set", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"_setClassVarServer", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - - {"ent_create", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"ent_throw", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"ent_setname", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"ent_teleport", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"ent_remove", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"ent_remove_all", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"ent_fire", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - - {"particle_create", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"particle_recreate", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"particle_kill", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - - {"test_setteam", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"melee_lunge_ent", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - - // fcvars that should be cheats - {"net_ignoreAllSnapshots", FCVAR_CHEAT}, - {"highlight_draw", FCVAR_CHEAT}, - // these should potentially be replicated rather than cheat, like sv_footsteps is - // however they're defined on client, so can't make replicated atm sadly - {"cl_footstep_event_max_dist", FCVAR_CHEAT}, - {"cl_footstep_event_max_dist_titan", FCVAR_CHEAT}, - }; - - // array of cvars and the flags we want to remove from them - const std::vector> CVAR_FIXUP_REMOVE_FLAGS = { - // unsure how this command works, not even sure it's used on retail servers, deffo shouldn't be used on northstar - {"migrateme", FCVAR_SERVER_CAN_EXECUTE | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"recheck", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, // we don't need this on northstar servers, it's for communities - - // unsure how these work exactly (rpt system likely somewhat stripped?), removing anyway since they won't be used - {"rpt_client_enable", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - {"rpt_password", FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS}, - - // these are devonly by default but should be modifyable - // NOTE: not all of these may actually do anything or work properly in practice - // network settings - {"cl_updaterate_mp", FCVAR_DEVELOPMENTONLY}, - {"cl_updaterate_sp", FCVAR_DEVELOPMENTONLY}, - {"clock_bias_sp", FCVAR_DEVELOPMENTONLY}, - {"clock_bias_mp", FCVAR_DEVELOPMENTONLY}, - {"cl_interpolate", FCVAR_DEVELOPMENTONLY}, // super duper ultra fucks anims if changed - {"cl_interpolateSoAllAnimsLoop", FCVAR_DEVELOPMENTONLY}, - {"cl_cmdrate", FCVAR_DEVELOPMENTONLY}, - {"cl_cmdbackup", FCVAR_DEVELOPMENTONLY}, - {"rate", FCVAR_DEVELOPMENTONLY}, - {"net_minroutable", FCVAR_DEVELOPMENTONLY}, - {"net_maxroutable", FCVAR_DEVELOPMENTONLY}, - {"net_lerpFields", FCVAR_DEVELOPMENTONLY}, - {"net_ignoreAllSnapshots", FCVAR_DEVELOPMENTONLY}, - {"net_chokeloop", FCVAR_DEVELOPMENTONLY}, - {"sv_unlag", FCVAR_DEVELOPMENTONLY}, - {"sv_maxunlag", FCVAR_DEVELOPMENTONLY}, - {"sv_lagpushticks", FCVAR_DEVELOPMENTONLY}, - {"sv_instancebaselines", FCVAR_DEVELOPMENTONLY}, - {"sv_voiceEcho", FCVAR_DEVELOPMENTONLY}, - {"net_compresspackets", FCVAR_DEVELOPMENTONLY}, - {"net_compresspackets_minsize", FCVAR_DEVELOPMENTONLY}, - {"net_verifyEncryption", FCVAR_DEVELOPMENTONLY}, // unsure if functional in retail - - // gameplay settings - {"vel_samples", FCVAR_DEVELOPMENTONLY}, - {"vel_sampleFrequency", FCVAR_DEVELOPMENTONLY}, - {"sv_friction", FCVAR_DEVELOPMENTONLY}, - {"sv_stopspeed", FCVAR_DEVELOPMENTONLY}, - {"sv_airaccelerate", FCVAR_DEVELOPMENTONLY}, - {"sv_forceGrapplesToFail", FCVAR_DEVELOPMENTONLY}, - {"sv_maxvelocity", FCVAR_DEVELOPMENTONLY}, - {"sv_footsteps", FCVAR_DEVELOPMENTONLY}, - // these 2 are flagged as CHEAT above, could be made REPLICATED later potentially - {"cl_footstep_event_max_dist", FCVAR_DEVELOPMENTONLY}, - {"cl_footstep_event_max_dist_titan", FCVAR_DEVELOPMENTONLY}, - {"sv_balanceTeams", FCVAR_DEVELOPMENTONLY}, - {"rodeo_enable", FCVAR_DEVELOPMENTONLY}, - {"sv_forceRodeoToFail", FCVAR_DEVELOPMENTONLY}, - {"player_find_rodeo_target_per_cmd", FCVAR_DEVELOPMENTONLY}, // todo test before merge - {"hud_takesshots", FCVAR_DEVELOPMENTONLY}, // very likely does not work but would be cool if it did - - {"cam_collision", FCVAR_DEVELOPMENTONLY}, - {"cam_idealdelta", FCVAR_DEVELOPMENTONLY}, - {"cam_ideallag", FCVAR_DEVELOPMENTONLY}, - - // graphics/visual settings - {"mat_colorcorrection", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoRadius", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoDepthMax", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoBlurSharpness", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoIntensity", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoBias", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoDistanceLerp", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoBlurRadius", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoExponent", FCVAR_DEVELOPMENTONLY}, - {"r_hbaoDepthFadePctDefault", FCVAR_DEVELOPMENTONLY}, - {"r_drawscreenspaceparticles", FCVAR_DEVELOPMENTONLY}, - {"ui_loadingscreen_fadeout_time", FCVAR_DEVELOPMENTONLY}, - {"ui_loadingscreen_fadein_time", FCVAR_DEVELOPMENTONLY}, - {"ui_loadingscreen_transition_time", FCVAR_DEVELOPMENTONLY}, - {"ui_loadingscreen_mintransition_time", FCVAR_DEVELOPMENTONLY}, - // these 2 could be FCVAR_CHEAT, i guess? - {"cl_draw_player_model", FCVAR_DEVELOPMENTONLY}, - {"cl_always_draw_3p_player", FCVAR_DEVELOPMENTONLY}, - {"idcolor_neutral", FCVAR_DEVELOPMENTONLY}, - {"idcolor_ally", FCVAR_DEVELOPMENTONLY}, - {"idcolor_ally_cb1", FCVAR_DEVELOPMENTONLY}, - {"idcolor_ally_cb2", FCVAR_DEVELOPMENTONLY}, - {"idcolor_ally_cb3", FCVAR_DEVELOPMENTONLY}, - {"idcolor_enemy", FCVAR_DEVELOPMENTONLY}, - {"idcolor_enemy_cb1", FCVAR_DEVELOPMENTONLY}, - {"idcolor_enemy_cb2", FCVAR_DEVELOPMENTONLY}, - {"idcolor_enemy_cb3", FCVAR_DEVELOPMENTONLY}, - {"playerListPartyColorR", FCVAR_DEVELOPMENTONLY}, - {"playerListPartyColorG", FCVAR_DEVELOPMENTONLY}, - {"playerListPartyColorB", FCVAR_DEVELOPMENTONLY}, - {"playerListUseFriendColor", FCVAR_DEVELOPMENTONLY}, - {"fx_impact_neutral", FCVAR_DEVELOPMENTONLY}, - {"fx_impact_ally", FCVAR_DEVELOPMENTONLY}, - {"fx_impact_enemy", FCVAR_DEVELOPMENTONLY}, - {"hitch_alert_color", FCVAR_DEVELOPMENTONLY}, - {"particles_cull_all", FCVAR_DEVELOPMENTONLY}, - {"particles_cull_dlights", FCVAR_DEVELOPMENTONLY}, - {"map_settings_override", FCVAR_DEVELOPMENTONLY}, - {"highlight_draw", FCVAR_DEVELOPMENTONLY}, - - // sys/engine settings - {"sleep_when_meeting_framerate", FCVAR_DEVELOPMENTONLY}, - {"sleep_when_meeting_framerate_headroom_ms", FCVAR_DEVELOPMENTONLY}, - {"not_focus_sleep", FCVAR_DEVELOPMENTONLY}, - {"sp_not_focus_pause", FCVAR_DEVELOPMENTONLY}, - {"joy_requireFocus", FCVAR_DEVELOPMENTONLY}, - - {"host_thread_mode", FCVAR_DEVELOPMENTONLY}, - {"phys_enable_simd_optimizations", FCVAR_DEVELOPMENTONLY}, - {"phys_enable_experimental_optimizations", FCVAR_DEVELOPMENTONLY}, - - {"community_frame_run", FCVAR_DEVELOPMENTONLY}, - {"sv_single_core_dedi", FCVAR_DEVELOPMENTONLY}, - {"sv_stressbots", FCVAR_DEVELOPMENTONLY}, - - {"fatal_script_errors", FCVAR_DEVELOPMENTONLY}, - {"fatal_script_errors_client", FCVAR_DEVELOPMENTONLY}, - {"fatal_script_errors_server", FCVAR_DEVELOPMENTONLY}, - {"script_error_on_midgame_load", FCVAR_DEVELOPMENTONLY}, // idk what this is - - {"ai_ainRebuildOnMapStart", FCVAR_DEVELOPMENTONLY}, - - {"save_enable", FCVAR_DEVELOPMENTONLY}, - - // cheat commands - {"switchclass", FCVAR_DEVELOPMENTONLY}, - {"set", FCVAR_DEVELOPMENTONLY}, - {"_setClassVarServer", FCVAR_DEVELOPMENTONLY}, - - // reparse commands - {"aisettings_reparse", FCVAR_DEVELOPMENTONLY}, - {"aisettings_reparse_client", FCVAR_DEVELOPMENTONLY}, - {"damagedefs_reparse", FCVAR_DEVELOPMENTONLY}, - {"damagedefs_reparse_client", FCVAR_DEVELOPMENTONLY}, - {"playerSettings_reparse", FCVAR_DEVELOPMENTONLY}, - {"_playerSettings_reparse_Server", FCVAR_DEVELOPMENTONLY}, - - }; - - const std::vector> CVAR_FIXUP_DEFAULT_VALUES = { - {"sv_stressbots", "0"}, // not currently used but this is probably a bad default if we get bots working - {"cl_pred_optimize", "0"} // fixes issues with animation prediction in thirdperson - }; - - for (auto& fixup : CVAR_FIXUP_ADD_FLAGS) - { - ConCommandBase* command = g_pCVar->FindCommandBase(std::get<0>(fixup)); - if (command) - command->m_nFlags |= std::get<1>(fixup); - } - - for (auto& fixup : CVAR_FIXUP_REMOVE_FLAGS) - { - ConCommandBase* command = g_pCVar->FindCommandBase(std::get<0>(fixup)); - if (command) - command->m_nFlags &= ~std::get<1>(fixup); - } - - for (auto& fixup : CVAR_FIXUP_DEFAULT_VALUES) - { - ConVar* cvar = g_pCVar->FindVar(std::get<0>(fixup)); - if (cvar && !strcmp(cvar->GetString(), cvar->m_pszDefaultValue)) - { - cvar->SetValue(std::get<1>(fixup)); - cvar->m_pszDefaultValue = std::get<1>(fixup); - } - } -} diff --git a/NorthstarDLL/shared/misccommands.h b/NorthstarDLL/shared/misccommands.h deleted file mode 100644 index 07a07fb3..00000000 --- a/NorthstarDLL/shared/misccommands.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once -void AddMiscConCommands(); -void FixupCvarFlags(); diff --git a/NorthstarDLL/shared/playlist.cpp b/NorthstarDLL/shared/playlist.cpp deleted file mode 100644 index 2b9ad979..00000000 --- a/NorthstarDLL/shared/playlist.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "playlist.h" -#include "core/convar/concommand.h" -#include "core/convar/convar.h" -#include "squirrel/squirrel.h" -#include "engine/hoststate.h" -#include "engine/r2engine.h" -#include "server/serverpresence.h" - -AUTOHOOK_INIT() - -// use the R2 namespace for game funcs -namespace R2 -{ - DEFINED_VAR_AT(engine.dll + 0x18C640, GetCurrentPlaylistName); - DEFINED_VAR_AT(engine.dll + 0x18EB20, SetCurrentPlaylist); - DEFINED_VAR_AT(engine.dll + 0x18ED00, SetPlaylistVarOverride); - DEFINED_VAR_AT(engine.dll + 0x18C680, GetCurrentPlaylistVar); -} // namespace R2 - -ConVar* Cvar_ns_use_clc_SetPlaylistVarOverride; - -// clang-format off -AUTOHOOK(clc_SetPlaylistVarOverride__Process, engine.dll + 0x222180, -char, __fastcall, (void* a1, void* a2)) -// clang-format on -{ - // the private_match playlist on mp_lobby is the only situation where there should be any legitimate sending of this netmessage - if (!Cvar_ns_use_clc_SetPlaylistVarOverride->GetBool() || strcmp(R2::GetCurrentPlaylistName(), "private_match") || - strcmp(g_pGlobals->m_pMapName, "mp_lobby")) - return 1; - - return clc_SetPlaylistVarOverride__Process(a1, a2); -} - -// clang-format off -AUTOHOOK(SetCurrentPlaylist, engine.dll + 0x18EB20, -bool, __fastcall, (const char* pPlaylistName)) -// clang-format on -{ - bool bSuccess = SetCurrentPlaylist(pPlaylistName); - - if (bSuccess) - { - spdlog::info("Set playlist to {}", R2::GetCurrentPlaylistName()); - g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName()); - } - - return bSuccess; -} - -// clang-format off -AUTOHOOK(SetPlaylistVarOverride, engine.dll + 0x18ED00, -void, __fastcall, (const char* pVarName, const char* pValue)) -// clang-format on -{ - if (strlen(pValue) >= 64) - return; - - SetPlaylistVarOverride(pVarName, pValue); -} - -// clang-format off -AUTOHOOK(GetCurrentPlaylistVar, engine.dll + 0x18C680, -const char*, __fastcall, (const char* pVarName, bool bUseOverrides)) -// clang-format on -{ - if (!bUseOverrides && !strcmp(pVarName, "max_players")) - bUseOverrides = true; - - return GetCurrentPlaylistVar(pVarName, bUseOverrides); -} - -// clang-format off -AUTOHOOK(GetCurrentGamemodeMaxPlayers, engine.dll + 0x18C430, -int, __fastcall, ()) -// clang-format on -{ - const char* pMaxPlayers = R2::GetCurrentPlaylistVar("max_players", 0); - if (!pMaxPlayers) - return GetCurrentGamemodeMaxPlayers(); - - int iMaxPlayers = atoi(pMaxPlayers); - return iMaxPlayers; -} - -void ConCommand_playlist(const CCommand& args) -{ - if (args.ArgC() < 2) - return; - - R2::SetCurrentPlaylist(args.Arg(1)); -} - -void ConCommand_setplaylistvaroverride(const CCommand& args) -{ - if (args.ArgC() < 3) - return; - - for (int i = 1; i < args.ArgC(); i += 2) - R2::SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1)); -} - -ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, (ConCommand, ConVar), (CModule module)) -{ - AUTOHOOK_DISPATCH() - - // playlist is the name of the command on respawn servers, but we already use setplaylist so can't get rid of it - RegisterConCommand("playlist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); - RegisterConCommand("setplaylist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); - RegisterConCommand("setplaylistvaroverrides", ConCommand_setplaylistvaroverride, "sets a playlist var override", FCVAR_NONE); - - // note: clc_SetPlaylistVarOverride is pretty insecure, since it allows for entirely arbitrary playlist var overrides to be sent to the - // server, this is somewhat restricted on custom servers to prevent it being done outside of private matches, but ideally it should be - // disabled altogether, since the custom menus won't use it anyway this should only really be accepted if you want vanilla client - // compatibility - Cvar_ns_use_clc_SetPlaylistVarOverride = new ConVar( - "ns_use_clc_SetPlaylistVarOverride", "0", FCVAR_GAMEDLL, "Whether the server should accept clc_SetPlaylistVarOverride messages"); - - // patch to prevent clc_SetPlaylistVarOverride from being able to crash servers if we reach max overrides due to a call to Error (why is - // this possible respawn, wtf) todo: add a warning for this - module.Offset(0x18ED8D).Patch("C3"); - - // patch to allow setplaylistvaroverride to be called before map init on dedicated and private match launched through the game - module.Offset(0x18ED17).NOP(6); -} diff --git a/NorthstarDLL/shared/playlist.h b/NorthstarDLL/shared/playlist.h deleted file mode 100644 index e56fdf96..00000000 --- a/NorthstarDLL/shared/playlist.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -// use the R2 namespace for game funcs -namespace R2 -{ - inline const char* (*GetCurrentPlaylistName)(); - inline void (*SetCurrentPlaylist)(const char* pPlaylistName); - inline void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue); - inline const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides); -} // namespace R2 -- cgit v1.2.3