diff options
27 files changed, 539 insertions, 324 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..5fafec43 --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +Language: Cpp +Standard: Auto +IndentWidth: 4 +TabWidth: 4 +UseCRLF: false +SpacesInSquareBrackets: false +SpacesInParentheses: false +UseTab: Always +ColumnLimit: 140 +BreakBeforeBraces: Allman +AlignAfterOpenBracket: AlwaysBreak +IndentExternBlock: Indent +PointerAlignment: Left +SortIncludes: false
\ No newline at end of file diff --git a/LauncherInjector/resources.rc b/LauncherInjector/resources.rc index 8a1cde40..99fcc41e 100644 --- a/LauncherInjector/resources.rc +++ b/LauncherInjector/resources.rc @@ -61,8 +61,8 @@ IDI_ICON1 ICON "ns_icon.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,0,0 - PRODUCTVERSION 0,0,0,0 + FILEVERSION 0,0,0,1 + PRODUCTVERSION 0,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L diff --git a/NorthstarDedicatedTest/ExploitFixes.cpp b/NorthstarDedicatedTest/ExploitFixes.cpp index d36b4175..36bd36f4 100644 --- a/NorthstarDedicatedTest/ExploitFixes.cpp +++ b/NorthstarDedicatedTest/ExploitFixes.cpp @@ -3,39 +3,36 @@ #include "ExploitFixes.h" #include "ExploitFixes_UTF8Parser.h" #include "NSMem.h" +#include "cvar.h" // Make sure 3 or less floats are valid -bool ValidateFloats(float a, float b = 0, float c = 0) { - return !isnan(a) && !isnan(b) && !isnan(c); -} +bool ValidateFloats(float a, float b = 0, float c = 0) { return !isnan(a) && !isnan(b) && !isnan(c); } -struct Vector { +struct Vector +{ float x, y, z; Vector(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {} - bool IsValid() { - return ValidateFloats(x, y, z); - } + bool IsValid() { return ValidateFloats(x, y, z); } }; -struct Angle { +struct Angle +{ float pitch, yaw, roll; Angle(float pitch = 0, float yaw = 0, float roll = 0) : pitch(pitch), yaw(yaw), roll(roll) {} - bool IsInvalid() { + bool IsInvalid() + { if (!ValidateFloats(pitch, yaw, roll)) return false; - return - (pitch > 90 || pitch < -90) - || (yaw > 180 || yaw < -180) - || (roll > 180 || roll < -180); + return (pitch > 90 || pitch < -90) || (yaw > 180 || yaw < -180) || (roll > 180 || roll < -180); } }; -#define BLOCK_NETMSG_FUNC(name, pattern) \ +#define BLOCK_NETMSG_FUNC(name, pattern) \ KHOOK(name, ("engine.dll", pattern), bool, __fastcall, (void* thisptr, void* buffer)) { return false; } // Servers can literally request a screenshot from any client, yeah no @@ -45,9 +42,83 @@ BLOCK_NETMSG_FUNC(CLC_Screenshot_ReadFromBuffer, "48 89 5C 24 ? 48 89 6C 24 ? 48 // This is unused ingame and a big exploit vector BLOCK_NETMSG_FUNC(Base_CmdKeyValues_ReadFromBuffer, "40 55 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 5D 70"); +KHOOK(CClient_ProcessSetConVar, ("engine.dll", "48 8B D1 48 8B 49 18 48 8B 01 48 FF 60 10"), bool, __fastcall, (void* pMsg)) +{ + + 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; + + constexpr int SETCONVAR_SANITY_AMOUNT_LIMIT = 20; + if (msg->m_ConVars_count < 1 || msg->m_ConVars_count > SETCONVAR_SANITY_AMOUNT_LIMIT) + return false; // Nope + + for (int i = 0; i < msg->m_ConVars_count; i++) + { + auto entry = msg->m_ConVars + i; + + // Safety check for memory access + if (NSMem::IsMemoryReadable(entry, 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 false; // Missing null terminators + + auto realVar = g_pCVar->FindVar(entry->name); + + if (!realVar) + // Not an actual cvar, no thanks + return false; + + // Force name to match case + memcpy(entry->name, realVar->m_ConCommandBase.m_pszName, strlen(realVar->m_ConCommandBase.m_pszName) + 1); + + if (!ConVar::IsFlagSet(realVar, FCVAR_USERINFO) || ConVar::IsFlagSet(realVar, FCVAR_CHEAT)) + { + return false; + } + } + else + { + return false; // Not risking that one, they all gotta be readable + } + } + + return oCClient_ProcessSetConVar(msg); +} + // Purpose: prevent invalid user CMDs -KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __fastcall, (void* thisptr, void* pMsg)) { - struct __declspec(align(8)) CLC_Move { +KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __fastcall, (void* thisptr, void* pMsg)) +{ + struct CLC_Move + { BYTE gap0[24]; void* m_pMessageHandler; int m_nBackupCommands; @@ -72,7 +143,8 @@ KHOOK(CClient_ProcessUsercmds, ("engine.dll", "40 55 56 48 83 EC 58"), bool, __f return oCClient_ProcessUsercmds(thisptr, pMsg); } -KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) { +KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall, (void* buf, void* pCmd_move, void* pCmd_from)) +{ // Let normal usercmd read happen first, it's safe oReadUsercmd(buf, pCmd_move, pCmd_from); @@ -105,20 +177,18 @@ KHOOK(ReadUsercmd, ("server.dll", "4C 89 44 24 ? 53 55 56 57"), void, __fastcall }; auto cmd = (SV_CUserCmd*)pCmd_move; - if ( - cmd->worldViewAngles.IsInvalid() || - cmd->localViewAngles.IsInvalid() || - cmd->attackangles.IsInvalid() || - cmd->cameraAngles.IsInvalid()) { + if (cmd->worldViewAngles.IsInvalid() || cmd->localViewAngles.IsInvalid() || cmd->attackangles.IsInvalid() || + cmd->cameraAngles.IsInvalid()) + { goto INVALID_CMD; } if (cmd->frameTime <= 0 || cmd->tick_count == 0 || cmd->command_time <= 0) goto INVALID_CMD; // No simulation of bogus-timed cmds - if (!cmd->move.IsValid() || // Prevent player freeze (and even server crash) exploit + if (!cmd->move.IsValid() || // Prevent player freeze (and even server crash) exploit !cmd->cameraPos.IsValid()) // IIRC this can crash spectating clients or anyone watching replays - goto INVALID_CMD; + goto INVALID_CMD; if (!ValidateFloats(cmd->cameraPos.x, cmd->cameraPos.y, cmd->cameraPos.z)) goto INVALID_CMD; // IIRC this can crash spectating clients or anyone watching replays @@ -128,7 +198,7 @@ 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 = Angle(0,0,0); + cmd->worldViewAngles = cmd->localViewAngles = cmd->attackangles = cmd->cameraAngles = Angle(0, 0, 0); cmd->tick_count = cmd->frameTime = 0; cmd->move = cmd->cameraPos = Vector(0, 0, 0); cmd->buttons = 0; @@ -139,19 +209,24 @@ INVALID_CMD: // this is HORRIBLE for security, because it means servers can run arbitrary concommands on clients // especially since we have script commands this could theoretically be awful #include "gameutils.h" -KHOOK(IsValveMod, ("engine.dll", "48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63"), bool, __fastcall, ()) { +KHOOK(IsValveMod, ("engine.dll", "48 83 EC 28 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 85 C0 74 63"), bool, __fastcall, ()) +{ return !CommandLine()->CheckParm("-norestrictservercommands"); } // Fix respawn's crappy UTF8 parser so it doesn't crash -_- // This also means you can launch multiplayer with "communities_enabled 1" and not crash, you're welcome -KHOOK(CrashFunc_ParseUTF8, ("engine.dll", "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"), - bool, __fastcall, (INT64* a1, DWORD* a2, char* strData)) { +KHOOK( + CrashFunc_ParseUTF8, ("engine.dll", "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"), bool, + __fastcall, (INT64 * a1, DWORD* a2, char* strData)) +{ static void* targetRetAddr = NSMem::PatternScan("engine.dll", "84 C0 75 2C 49 8B 16"); - if (_ReturnAddress() == targetRetAddr) { - if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData)) { + if (_ReturnAddress() == targetRetAddr) + { + if (!ExploitFixes_UTF8Parser::CheckValid(a1, a2, strData)) + { spdlog::warn("ParseUTF8 Hook: Ignoring potentially-crashing utf8 string"); return false; } @@ -162,8 +237,10 @@ KHOOK(CrashFunc_ParseUTF8, ("engine.dll", "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 ////////////////////////////////////////////////// -void DoBytePatches() { +void DoBytePatches() +{ uintptr_t engineBase = (uintptr_t)GetModuleHandleA("engine.dll"); + uintptr_t serverBase = (uintptr_t)GetModuleHandleA("server.dll"); // patches to make commands run from client/ui script still work // note: this is likely preventable in a nicer way? test prolly @@ -176,17 +253,49 @@ void DoBytePatches() { int val = *(int*)addr | FCVAR_SERVER_CAN_EXECUTE; NSMem::BytePatch(addr, (BYTE*)&val, sizeof(int)); } + + { // Dumb ANTITAMPER patches (they negatively impact performance and security) + + constexpr const char* ANTITAMPER_EXPORTS[] = { + "ANTITAMPER_SPOTCHECK_CODEMARKER", + "ANTITAMPER_TESTVALUE_CODEMARKER", + "ANTITAMPER_TRIGGER_CODEMARKER", + }; + + // Prevent thesefrom actually doing anything + for (auto exportName : ANTITAMPER_EXPORTS) + { + + auto address = (uintptr_t)GetProcAddress(NULL, exportName); + if (!address) + { + spdlog::warn("Failed to find AntiTamper function export \"{}\"", exportName); + } + else + { + + // Just return, none of them have any args or are userpurge + NSMem::BytePatch(address, {0xC3}); + + spdlog::info("Patched AntiTamper function export \"{}\"", exportName); + } + } + } } -void ExploitFixes::LoadCallback(HMODULE unused) { +void ExploitFixes::LoadCallback(HMODULE unused) +{ spdlog::info("ExploitFixes::LoadCallback ..."); spdlog::info("\tByte patching..."); DoBytePatches(); - if (KHook::InitAllHooks()) { + if (KHook::InitAllHooks()) + { spdlog::info("\tInitialized " + std::to_string(KHook::_allHooks.size()) + " exploit-patch hooks."); - } else { + } + else + { spdlog::critical("\tFAILED to initialize all exploit patches."); // Force exit? diff --git a/NorthstarDedicatedTest/ExploitFixes.h b/NorthstarDedicatedTest/ExploitFixes.h index 49928640..7a407a3d 100644 --- a/NorthstarDedicatedTest/ExploitFixes.h +++ b/NorthstarDedicatedTest/ExploitFixes.h @@ -3,6 +3,7 @@ #pragma once #include "pch.h" -namespace ExploitFixes { +namespace ExploitFixes +{ void LoadCallback(HMODULE unused); -} +}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h index 6b767a0c..b06d442b 100644 --- a/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h +++ b/NorthstarDedicatedTest/ExploitFixes_UTF8Parser.h @@ -8,55 +8,55 @@ namespace ExploitFixes_UTF8Parser { bool __fastcall CheckValid(INT64* a1, DWORD* a2, char* strData) { - static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2)) NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A"); + static auto sub_F1320 = (INT64(__fastcall*)(DWORD a1, char* a2))NSMem::PatternScan("engine.dll", "83 F9 7F 77 08 88 0A"); - 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 + 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); @@ -71,7 +71,7 @@ namespace ExploitFixes_UTF8Parser { while (1) { - + if (!NSMem::IsMemoryReadable(v4, 1)) return false; // INVALID @@ -128,7 +128,7 @@ namespace ExploitFixes_UTF8Parser return true; if (*v4 != 92 || v4[1] != 117) return true; - + v27 = v4[2] | 0x20; v28 = v4[3] | 0x20; v29 = v4[4] | 0x20; @@ -172,4 +172,4 @@ namespace ExploitFixes_UTF8Parser LABEL_48: return true; } -}
\ No newline at end of file +} // namespace ExploitFixes_UTF8Parser
\ No newline at end of file diff --git a/NorthstarDedicatedTest/NSMem.h b/NorthstarDedicatedTest/NSMem.h index a6ddf033..a5bbd42f 100644 --- a/NorthstarDedicatedTest/NSMem.h +++ b/NorthstarDedicatedTest/NSMem.h @@ -4,8 +4,10 @@ // KittenPopo's memory stuff, made for northstar (because I really can't handle working with northstar's original memory stuff tbh) #pragma region Pattern Scanning -namespace NSMem { - inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) { +namespace NSMem +{ + inline void* PatternScan(void* module, const int* pattern, int patternSize, int offset) + { if (!module) return NULL; @@ -16,16 +18,20 @@ namespace NSMem { auto scanBytes = (BYTE*)module; - for (auto i = 0; i < sizeOfImage - patternSize; ++i) { + for (auto i = 0; i < sizeOfImage - patternSize; ++i) + { bool found = true; - for (auto j = 0; j < patternSize; ++j) { - if (scanBytes[i + j] != pattern[j] && pattern[j] != -1) { + for (auto j = 0; j < patternSize; ++j) + { + if (scanBytes[i + j] != pattern[j] && pattern[j] != -1) + { found = false; break; } } - if (found) { + if (found) + { uintptr_t addressInt = (uintptr_t)(&scanBytes[i]) + offset; return (uint8_t*)addressInt; } @@ -34,31 +40,42 @@ namespace NSMem { return nullptr; } - inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) { + inline void* PatternScan(const char* moduleName, const char* pattern, int offset = 0) + { std::vector<int> patternNums; bool lastChar = 0; int size = strlen(pattern); - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) + { char c = pattern[i]; // If this is a space character, ignore it if (c == ' ' || c == '\t') continue; - if (c == '?') { + if (c == '?') + { // Add a wildcard (-1) patternNums.push_back(-1); - } else if (i < size - 1) { + } + else if (i < size - 1) + { BYTE result = 0; - for (int j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) + { int val = 0; char c = (pattern + i + j)[0]; - if (c >= 'a') { + if (c >= 'a') + { val = c - 'a' + 0xA; - } else if (c >= 'A') { + } + else if (c >= 'A') + { val = c - 'A' + 0xA; - } else { + } + else + { val = c - '0'; } @@ -72,24 +89,28 @@ namespace NSMem { return PatternScan(GetModuleHandleA(moduleName), &patternNums[0], patternNums.size(), offset); } - inline void BytePatch(uintptr_t address, const BYTE* vals, int size) { + inline void BytePatch(uintptr_t address, const BYTE* vals, int size) + { WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, vals, size, NULL); } - inline void BytePatch(uintptr_t address, std::initializer_list<BYTE> vals) { + inline void BytePatch(uintptr_t address, std::initializer_list<BYTE> vals) + { std::vector<BYTE> bytes = vals; if (!bytes.empty()) BytePatch(address, &bytes[0], bytes.size()); } - inline void NOP(uintptr_t address, int size) { + inline void NOP(uintptr_t address, int size) + { BYTE* buf = (BYTE*)malloc(size); memset(buf, 0x90, size); BytePatch(address, buf, size); free(buf); } - inline bool IsMemoryReadable(void* ptr, size_t size) { + inline bool IsMemoryReadable(void* ptr, size_t size) + { BYTE* buffer = (BYTE*)malloc(size); size_t numWritten = 0; @@ -98,18 +119,21 @@ namespace NSMem { return numWritten == size; } -} +} // namespace NSMem #pragma region KHOOK -struct KHookPatternInfo { - const char* moduleName, *pattern; +struct KHookPatternInfo +{ + const char *moduleName, *pattern; int offset = 0; - KHookPatternInfo(const char* moduleName, const char* pattern, int offset = 0) - : moduleName(moduleName), pattern(pattern), offset(offset) {} + KHookPatternInfo(const char* moduleName, const char* pattern, int offset = 0) : moduleName(moduleName), pattern(pattern), offset(offset) + { + } }; -struct KHook { +struct KHook +{ KHookPatternInfo targetFunc; void* targetFuncAddr; void* hookFunc; @@ -117,13 +141,15 @@ struct KHook { static inline std::vector<KHook*> _allHooks; - KHook(KHookPatternInfo targetFunc, void* hookFunc, void** original) : targetFunc(targetFunc) { + KHook(KHookPatternInfo targetFunc, void* hookFunc, void** original) : targetFunc(targetFunc) + { this->hookFunc = hookFunc; this->original = original; _allHooks.push_back(this); } - bool Setup() { + bool Setup() + { targetFuncAddr = NSMem::PatternScan(targetFunc.moduleName, targetFunc.pattern, targetFunc.offset); if (!targetFuncAddr) return false; @@ -132,11 +158,16 @@ struct KHook { } // Returns true if succeeded - static bool InitAllHooks() { - for (KHook* hook : _allHooks) { - if (hook->Setup()) { + static bool InitAllHooks() + { + for (KHook* hook : _allHooks) + { + if (hook->Setup()) + { spdlog::info("KHook hooked at {}", hook->targetFuncAddr); - } else { + } + else + { return false; } } @@ -144,9 +175,9 @@ struct KHook { return MH_EnableHook(MH_ALL_HOOKS) == MH_OK; } }; -#define KHOOK(name, funcPatternInfo, returnType, convention, args) \ +#define KHOOK(name, funcPatternInfo, returnType, convention, args) \ returnType convention hk##name args; \ auto o##name = (returnType(convention*) args)0; \ - KHook k##name = KHook(KHookPatternInfo funcPatternInfo, &hk##name, (void**)&o##name); \ + KHook k##name = KHook(KHookPatternInfo funcPatternInfo, &hk##name, (void**)&o##name); \ returnType convention hk##name args #pragma endregion
\ No newline at end of file diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index d006eca3..8c417112 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -66,7 +66,7 @@ <SubSystem>Windows</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;$(SolutionDir)$(Platform)\$(Configuration)\libprotobuf_x64.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;$(SolutionDir)$(Platform)\$(Configuration)\libprotobuf_x64.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies> <ForceSymbolReferences> </ForceSymbolReferences> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> @@ -96,7 +96,7 @@ <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;$(SolutionDir)$(Platform)\$(Configuration)\libprotobuf_x64.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;$(SolutionDir)$(Platform)\$(Configuration)\libprotobuf_x64.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies> <ForceSymbolReferences> </ForceSymbolReferences> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> @@ -573,6 +573,7 @@ <ClInclude Include="ExploitFixes.h" /> <ClInclude Include="ExploitFixes_UTF8Parser.h" /> <ClInclude Include="NSMem.h" /> + <ClInclude Include="version.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="audio.cpp" /> @@ -646,6 +647,7 @@ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader> </ClCompile> <ClCompile Include="ExploitFixes.cpp" /> + <ClCompile Include="version.cpp" /> </ItemGroup> <ItemGroup> <None Include="include\crypto\bn_conf.h.in" /> @@ -684,4 +686,4 @@ <ImportGroup Label="ExtensionTargets"> <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" /> </ImportGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index cd9a7cf6..67137947 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1533,6 +1533,9 @@ <ClInclude Include="ExploitFixes_UTF8Parser.h"> <Filter>Source Files\Shared\Exploit Fixes\UTF8Parser</Filter> </ClInclude> + <ClInclude Include="version.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp"> @@ -1721,6 +1724,9 @@ <ClCompile Include="ExploitFixes.cpp"> <Filter>Source Files\Shared\Exploit Fixes</Filter> </ClCompile> + <ClCompile Include="version.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <MASM Include="audio_asm.asm"> diff --git a/NorthstarDedicatedTest/concommand.h b/NorthstarDedicatedTest/concommand.h index b1342163..eb812c32 100644 --- a/NorthstarDedicatedTest/concommand.h +++ b/NorthstarDedicatedTest/concommand.h @@ -104,4 +104,7 @@ class ConCommand : public ConCommandBase }; // Size: 0x0060 void RegisterConCommand(const char* name, void (*callback)(const CCommand&), const char* helpString, int flags); -void InitialiseConCommands(HMODULE baseAddress);
\ No newline at end of file +void InitialiseConCommands(HMODULE baseAddress); + +#define MAKE_CONCMD(name, helpStr, flags, fn) \ +RegisterConCommand(name, fn, helpStr, flags);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp index 7023e13c..3f2728d0 100644 --- a/NorthstarDedicatedTest/convar.cpp +++ b/NorthstarDedicatedTest/convar.cpp @@ -6,10 +6,6 @@ #include "gameutils.h" #include "sourceinterface.h" -// should this be in modmanager? -std::unordered_map<std::string, ConVar*> - g_CustomConvars; // this is used in modloading code to determine whether we've registered a mod convar already - typedef void (*ConVarRegisterType)( ConVar* pConVar, const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback); @@ -40,6 +36,8 @@ void InitialiseConVars(HMODULE baseAddress) HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &ConVar::IsFlagSet, reinterpret_cast<LPVOID*>(&CvarIsFlagSet)); + + } //----------------------------------------------------------------------------- @@ -54,8 +52,6 @@ ConVar::ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, con conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar. conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, 0, 0, 0, 0, 0); - - g_CustomConvars.emplace(pszName, this); } //----------------------------------------------------------------------------- @@ -72,8 +68,6 @@ ConVar::ConVar( conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar. conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, bMin, fMin, bMax, fMax, pCallback); - - g_CustomConvars.emplace(pszName, this); } //----------------------------------------------------------------------------- @@ -82,10 +76,7 @@ ConVar::ConVar( ConVar::~ConVar(void) { if (m_Value.m_pszString) - { delete[] m_Value.m_pszString; - m_Value.m_pszString = NULL; - } } //----------------------------------------------------------------------------- diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h index a00c7f0e..fc5b0eb9 100644 --- a/NorthstarDedicatedTest/convar.h +++ b/NorthstarDedicatedTest/convar.h @@ -129,7 +129,7 @@ class ConVar }; ConCommandBase m_ConCommandBase{}; // 0x0000 - ConVar* m_pParent{}; // 0x0040 + const char* defaultVal{}; // 0x0040 CVValue_t m_Value{}; // 0x0048 bool m_bHasMin{}; // 0x005C float m_fMinVal{}; // 0x0060 @@ -139,5 +139,4 @@ class ConVar char m_pPad80[10]{}; // 0x0080 }; // Size: 0x0080 -void InitialiseConVars(HMODULE baseAddress); -extern std::unordered_map<std::string, ConVar*> g_CustomConvars;
\ No newline at end of file +void InitialiseConVars(HMODULE baseAddress);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/cvar.cpp b/NorthstarDedicatedTest/cvar.cpp index 7f8c35d7..23d767fd 100644 --- a/NorthstarDedicatedTest/cvar.cpp +++ b/NorthstarDedicatedTest/cvar.cpp @@ -4,45 +4,6 @@ #include "concommand.h" //----------------------------------------------------------------------------- -// Purpose: finds base commands. -// Input : *pszCommandName - -//----------------------------------------------------------------------------- -ConCommandBase* CCvar::FindCommandBase(const char* pszCommandName) -{ - static int index = 14; - return CallVFunc<ConCommandBase*>(index, this, pszCommandName); -} - -//----------------------------------------------------------------------------- -// Purpose: finds ConVars. -// Input : *pszVarName - -//----------------------------------------------------------------------------- -ConVar* CCvar::FindVar(const char* pszVarName) -{ - static int index = 16; - return CallVFunc<ConVar*>(index, this, pszVarName); -} - -//----------------------------------------------------------------------------- -// Purpose: finds ConCommands. -// Input : *pszCommandName - -//----------------------------------------------------------------------------- -ConCommand* CCvar::FindCommand(const char* pszCommandName) -{ - static int index = 18; - return CallVFunc<ConCommand*>(index, this, pszCommandName); -} - -//----------------------------------------------------------------------------- -// Purpose: iterates over all ConVars -//----------------------------------------------------------------------------- -CCVarIteratorInternal* CCvar::FactoryInternalIterator() -{ - static int index = 41; - return CallVFunc<CCVarIteratorInternal*>(index, this); -} - -//----------------------------------------------------------------------------- // Purpose: returns all ConVars //----------------------------------------------------------------------------- std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap() @@ -61,5 +22,6 @@ std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap() return allConVars; } + SourceInterface<CCvar>* g_pCVarInterface; CCvar* g_pCVar;
\ No newline at end of file diff --git a/NorthstarDedicatedTest/cvar.h b/NorthstarDedicatedTest/cvar.h index 43b13bfb..e254af4e 100644 --- a/NorthstarDedicatedTest/cvar.h +++ b/NorthstarDedicatedTest/cvar.h @@ -1,5 +1,6 @@ #pragma once #include "convar.h" +#include "pch.h" //----------------------------------------------------------------------------- // Forward declarations @@ -25,11 +26,13 @@ class CCVarIteratorInternal // Fully reversed table, just look at the virtual fu //----------------------------------------------------------------------------- class CCvar { - public: - ConCommandBase* FindCommandBase(const char* pszCommandName); - ConVar* FindVar(const char* pszVarName); - ConCommand* FindCommand(const char* pszCommandName); - CCVarIteratorInternal* FactoryInternalIterator(); + public: + + M_VMETHOD(ConCommandBase*, FindCommandBase, 14, (const char* pszCommandName), (this, pszCommandName)); + M_VMETHOD(ConVar*, FindVar, 16, (const char* pszVarName), (this, pszVarName)); + M_VMETHOD(ConCommand*, FindCommand, 18, (const char* pszCommandName), (this, pszCommandName)); + M_VMETHOD(CCVarIteratorInternal*, FactoryInternalIterator, 41, (), (this)); + std::unordered_map<std::string, ConCommandBase*> DumpToMap(); }; diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index da232eef..06878b1e 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -193,8 +193,11 @@ bool InitialiseNorthstar() initialised = true; parseConfigurables(); + InitialiseVersion(); + // Fix some users' failure to connect to respawn datacenters SetEnvironmentVariableA("OPENSSL_ia32cap", "~0x200000200000000"); + curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base); InitialiseLogging(); diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp index 4c403872..055fe4df 100644 --- a/NorthstarDedicatedTest/hooks.cpp +++ b/NorthstarDedicatedTest/hooks.cpp @@ -36,7 +36,7 @@ LoadLibraryWType LoadLibraryWOriginal; void InstallInitialHooks() { if (MH_Initialize() != MH_OK) - spdlog::error("MH_Initialize failed"); + spdlog::error("MH_Initialize (minhook initialization) failed"); HookEnabler hook; ENABLER_CREATEHOOK(hook, &GetCommandLineA, &GetCommandLineAHook, reinterpret_cast<LPVOID*>(&GetCommandLineAOriginal)); diff --git a/NorthstarDedicatedTest/keyvalues.cpp b/NorthstarDedicatedTest/keyvalues.cpp index 08c85dc1..2063be62 100644 --- a/NorthstarDedicatedTest/keyvalues.cpp +++ b/NorthstarDedicatedTest/keyvalues.cpp @@ -59,7 +59,7 @@ void ModManager::TryBuildKeyValues(const char* filename) if (!m_loadedMods[i].Enabled) continue; - size_t fileHash = std::hash<std::string>{}(normalisedPath); + size_t fileHash = STR_HASH(normalisedPath); auto modKv = m_loadedMods[i].KeyValues.find(fileHash); if (modKv != m_loadedMods[i].KeyValues.end()) { diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp index 95638ef2..8d60ca22 100644 --- a/NorthstarDedicatedTest/languagehooks.cpp +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -15,7 +15,7 @@ GetGameLanguageType GetGameLanguageOriginal; bool CheckLangAudioExists(char* lang) { - std::string path{"r2\\sound\\general_"}; + std::string path {"r2\\sound\\general_"}; path += lang; path += ".mstr"; return fs::exists(path); @@ -31,7 +31,7 @@ std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern) using iterator = fs::directory_iterator; const iterator end; - for (iterator iter{dir}; iter != end; ++iter) + for (iterator iter {dir}; iter != end; ++iter) { const std::string filename = iter->path().filename().string(); std::smatch matches; diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp index 623ac06b..4ac1c760 100644 --- a/NorthstarDedicatedTest/latencyflex.cpp +++ b/NorthstarDedicatedTest/latencyflex.cpp @@ -8,9 +8,9 @@ OnRenderStartType OnRenderStart; ConVar* Cvar_r_latencyflex; -HMODULE m_lfxModule{}; +HMODULE m_lfxModule {}; typedef void (*PFN_winelfx_WaitAndBeginFrame)(); -PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{}; +PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame {}; void OnRenderStartHook() { diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index b9922ba5..8508e61c 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -54,74 +54,77 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) return EXCEPTION_CONTINUE_SEARCH; std::stringstream exceptionCause; + exceptionCause << "Cause: "; switch (exceptionCode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_IN_PAGE_ERROR: { - exceptionCause << "Cause: Access Violation" << std::endl; + exceptionCause << "Access Violation" << std::endl; auto exceptionInfo0 = exceptionInfo->ExceptionRecord->ExceptionInformation[0]; auto exceptionInfo1 = exceptionInfo->ExceptionRecord->ExceptionInformation[1]; if (!exceptionInfo0) - exceptionCause << "Attempted to read from: 0x" << std::setw(8) << std::setfill('0') << std::hex << exceptionInfo1; + exceptionCause << "Attempted to read from: 0x" << (void*)exceptionInfo1; else if (exceptionInfo0 == 1) - exceptionCause << "Attempted to write to: 0x" << std::setw(8) << std::setfill('0') << std::hex << exceptionInfo1; + exceptionCause << "Attempted to write to: 0x" << (void*)exceptionInfo1; else if (exceptionInfo0 == 8) - exceptionCause << "Data Execution Prevention (DEP) at: 0x" << std::setw(8) << std::setfill('0') << std::hex + exceptionCause << "Data Execution Prevention (DEP) at: 0x" << (void*)std::hex << exceptionInfo1; else - exceptionCause << "Unknown access violation at: 0x" << std::setw(8) << std::setfill('0') << std::hex << exceptionInfo1; + exceptionCause << "Unknown access violation at: 0x" << (void*)exceptionInfo1; break; } case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - exceptionCause << "Cause: Array bounds exceeded"; + exceptionCause << "Array bounds exceeded"; break; case EXCEPTION_DATATYPE_MISALIGNMENT: - exceptionCause << "Cause: Datatype misalignment"; + exceptionCause << "Datatype misalignment"; break; case EXCEPTION_FLT_DENORMAL_OPERAND: - exceptionCause << "Cause: Denormal operand"; + exceptionCause << "Denormal operand"; break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: + exceptionCause << "Divide by zero (float)"; + break; case EXCEPTION_INT_DIVIDE_BY_ZERO: - exceptionCause << "Cause: Divide by zero"; + exceptionCause << "Divide by zero (int)"; break; case EXCEPTION_FLT_INEXACT_RESULT: - exceptionCause << "Cause: Inexact result"; + exceptionCause << "Inexact result"; break; case EXCEPTION_FLT_INVALID_OPERATION: - exceptionCause << "Cause: invalid operation"; + exceptionCause << "Invalid operation"; break; case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_INT_OVERFLOW: - exceptionCause << "Cause: Numeric overflow"; + exceptionCause << "Numeric overflow"; break; case EXCEPTION_FLT_UNDERFLOW: - exceptionCause << "Cause: Numeric underflow"; + exceptionCause << "Numeric underflow"; break; case EXCEPTION_FLT_STACK_CHECK: - exceptionCause << "Cause: Stack check"; + exceptionCause << "Stack check"; break; case EXCEPTION_ILLEGAL_INSTRUCTION: - exceptionCause << "Cause: Illegal instruction"; + exceptionCause << "Illegal instruction"; break; case EXCEPTION_INVALID_DISPOSITION: - exceptionCause << "Cause: Invalid disposition"; + exceptionCause << "Invalid disposition"; break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: - exceptionCause << "Cause: Noncontinuable exception"; + exceptionCause << "Noncontinuable exception"; break; case EXCEPTION_PRIV_INSTRUCTION: - exceptionCause << "Cause: Priv instruction"; + exceptionCause << "Priviledged instruction"; break; case EXCEPTION_STACK_OVERFLOW: - exceptionCause << "Cause: Stack overflow"; + exceptionCause << "Stack overflow"; break; default: - exceptionCause << "Cause: Unknown"; + exceptionCause << "Unknown"; break; } @@ -208,14 +211,33 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) return EXCEPTION_EXECUTE_HANDLER; } +HANDLE hExceptionFilter; + +BOOL WINAPI ConsoleHandlerRoutine(DWORD eventCode) +{ + switch (eventCode) + { + case CTRL_CLOSE_EVENT: + // User closed console, shut everything down + spdlog::info("Exiting due to console close..."); + RemoveVectoredExceptionHandler(hExceptionFilter); + exit(EXIT_SUCCESS); + return FALSE; + } + + return TRUE; +} + void InitialiseLogging() { - AddVectoredExceptionHandler(TRUE, ExceptionFilter); + hExceptionFilter = AddVectoredExceptionHandler(TRUE, ExceptionFilter); AllocConsole(); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); + + SetConsoleCtrlHandler(ConsoleHandlerRoutine, true); } ConVar* Cvar_spewlog_enable; @@ -223,10 +245,12 @@ ConVar* Cvar_spewlog_enable; enum SpewType_t { SPEW_MESSAGE = 0, + SPEW_WARNING, SPEW_ASSERT, SPEW_ERROR, SPEW_LOG, + SPEW_TYPE_COUNT }; diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp index 210cc290..ca1ca96d 100644 --- a/NorthstarDedicatedTest/masterserver.cpp +++ b/NorthstarDedicatedTest/masterserver.cpp @@ -168,6 +168,7 @@ void MasterServerManager::SetCommonHttpClientOptions(CURL* curl) { curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_USERAGENT, &NSUserAgent); // curl_easy_setopt(curl, CURLOPT_STDERR, stdout); if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work { @@ -209,7 +210,6 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or CURL* curl = curl_easy_init(); SetCommonHttpClientOptions(curl); - std::string readBuffer; curl_easy_setopt( curl, CURLOPT_URL, @@ -555,7 +555,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken curl_easy_setopt( curl, CURLOPT_URL, fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr) - .c_str()); + .c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -697,7 +697,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c fmt::format( "{}/client/auth_with_server?id={}&playerToken={}&server={}&password={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr, serverIdStr, escapedPassword) - .c_str()); + .c_str()); curl_free(escapedPassword); } @@ -831,7 +831,7 @@ void MasterServerManager::AddSelfToServerList( "{}/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", Cvar_ns_masterserver_hostname->GetString(), port, authPort, nameEscaped, descEscaped, mapEscaped, playlistEscaped, maxPlayers, passwordEscaped) - .c_str()); + .c_str()); curl_free(nameEscaped); curl_free(descEscaped); @@ -935,7 +935,7 @@ void MasterServerManager::AddSelfToServerList( Cvar_ns_player_auth_port->GetInt(), escapedNameNew, escapedDescNew, escapedMapNew, escapedPlaylistNew, g_ServerAuthenticationManager->m_additionalPlayerData.size(), maxPlayers, escapedPasswordNew) - .c_str()); + .c_str()); curl_free(escapedNameNew); curl_free(escapedDescNew); @@ -1036,7 +1036,7 @@ void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist, fmt::format( "{}/server/update_values?id={}&map={}&playlist={}&maxPlayers={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, mapEscaped, playlistEscaped, maxPlayers) - .c_str()); + .c_str()); curl_free(mapEscaped); curl_free(playlistEscaped); @@ -1075,7 +1075,7 @@ void MasterServerManager::UpdateServerPlayerCount(int playerCount) curl, CURLOPT_URL, fmt::format( "{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, playerCount) - .c_str()); + .c_str()); CURLcode result = curl_easy_perform(curl); @@ -1115,7 +1115,7 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata, fmt::format( "{}/accounts/write_persistence?id={}&serverId={}", Cvar_ns_masterserver_hostname->GetString(), strPlayerId, m_ownServerId) - .c_str()); + .c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -1260,7 +1260,7 @@ void CHostState__State_GameShutdownHook(CHostState* hostState) CHostState__State_GameShutdown(hostState); } -MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownServerId{""}, m_ownClientAuthToken{""} {} +MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownServerId{ "" }, m_ownClientAuthToken{ "" } {} void InitialiseSharedMasterServer(HMODULE baseAddress) { diff --git a/NorthstarDedicatedTest/misccommands.cpp b/NorthstarDedicatedTest/misccommands.cpp index c3c9cd99..44f0dee8 100644 --- a/NorthstarDedicatedTest/misccommands.cpp +++ b/NorthstarDedicatedTest/misccommands.cpp @@ -7,50 +7,43 @@ #include "squirrel.h" #include "host_state.h" -void ForceLoadMapCommand(const CCommand& arg) +void AddMiscConCommands() { - if (arg.ArgC() < 2) - return; - - g_pHostState->m_iNextState = HS_NEW_GAME; - strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName)); -} + MAKE_CONCMD( + "force_newgame", "forces a map load through directly setting g_pHostState->m_iNextState to HS_NEW_GAME", FCVAR_NONE, + [](const CCommand& arg) { + if (arg.ArgC() < 2) + return; -void SelfAuthAndLeaveToLobbyCommand(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_pHostState->m_iNextState = HS_NEW_GAME; + strncpy(g_pHostState->m_levelName, arg.Arg(1), sizeof(g_pHostState->m_levelName)); + }); - g_MasterServerManager->m_bNewgameAfterSelfAuth = true; - g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken); -} + MAKE_CONCMD( + "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, [](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_MasterServerManager->m_bNewgameAfterSelfAuth = true; + g_MasterServerManager->AuthenticateWithOwnServer(g_LocalPlayerUserID, g_MasterServerManager->m_ownClientAuthToken); + }); -void EndSelfAuthAndLeaveToLobbyCommand(const CCommand& arg) -{ - Cbuf_AddText( - Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(), - cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); + // this is a concommand because we make a deferred call to it from another thread + MAKE_CONCMD("ns_end_reauth_and_leave_to_lobby", "", FCVAR_NONE, [](const CCommand& arg) { + Cbuf_AddText( + Cbuf_GetCurrentPlayer(), fmt::format("serverfilter {}", g_ServerAuthenticationManager->m_authData.begin()->first).c_str(), + cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); - // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this - if (g_ClientSquirrelManager->sqvm) - { - g_ServerAuthenticationManager->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? - SetCurrentPlaylist("tdm"); - strcpy(g_pHostState->m_levelName, "mp_lobby"); - g_pHostState->m_iNextState = HS_NEW_GAME; - } -} + // weird way of checking, but check if client script vm is initialised, mainly just to allow players to cancel this + if (g_ClientSquirrelManager->sqvm) + { + g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = true; -void AddMiscConCommands() -{ - RegisterConCommand( - "force_newgame", ForceLoadMapCommand, "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", SelfAuthAndLeaveToLobbyCommand, - "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", EndSelfAuthAndLeaveToLobbyCommand, "", FCVAR_NONE); + // 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? + SetCurrentPlaylist("tdm"); + strcpy(g_pHostState->m_levelName, "mp_lobby"); + g_pHostState->m_iNextState = HS_NEW_GAME; + } + }); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/miscserverfixes.cpp b/NorthstarDedicatedTest/miscserverfixes.cpp index 0b9a12db..7d977d64 100644 --- a/NorthstarDedicatedTest/miscserverfixes.cpp +++ b/NorthstarDedicatedTest/miscserverfixes.cpp @@ -25,7 +25,4 @@ void InitialiseMiscServerFixes(HMODULE baseAddress) } } -void InitialiseMiscEngineServerFixes(HMODULE baseAddress) -{ - -}
\ No newline at end of file +void InitialiseMiscEngineServerFixes(HMODULE baseAddress) {}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 79529c99..99bb5fdb 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -191,9 +191,10 @@ ModManager::ModManager() { // precaculated string hashes // note: use backslashes for these, since we use lexically_normal for file paths which uses them - m_hScriptsRsonHash = std::hash<std::string>{}("scripts\\vscripts\\scripts.rson"); - m_hPdefHash = std::hash<std::string>{}( - "cfg\\server\\persistent_player_data_version_231.pdef"); // this can have multiple versions, but we use 231 so that's what we hash + m_hScriptsRsonHash = STR_HASH("scripts\\vscripts\\scripts.rson"); + m_hPdefHash = STR_HASH( + "cfg\\server\\persistent_player_data_version_231.pdef" // this can have multiple versions, but we use 231 so that's what we hash + ); LoadMods(); } @@ -282,9 +283,7 @@ void ModManager::LoadMods() // preexisting convars note: we don't delete convars if they already exist because they're used for script stuff, unfortunately this // causes us to leak memory on reload, but not much, potentially find a way to not do this at some point for (ModConVar* convar : mod.ConVars) - if (g_CustomConvars.find(convar->Name) == - g_CustomConvars.end()) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining - // same convar multiple times + if (!g_pCVar->FindVar(convar->Name.c_str())) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining same convar multiple times new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str()); // read vpk paths @@ -396,7 +395,7 @@ void ModManager::LoadMods() if (fs::is_regular_file(file)) { std::string kvStr = file.path().lexically_relative(mod.ModDirectory / "keyvalues").lexically_normal().string(); - mod.KeyValues.emplace(std::hash<std::string>{}(kvStr), kvStr); + mod.KeyValues.emplace(STR_HASH(kvStr), kvStr); } } } @@ -537,7 +536,7 @@ void ModManager::UnloadMods() void ModManager::CompileAssetsForFile(const char* filename) { - size_t fileHash = std::hash<std::string>{}(fs::path(filename).lexically_normal().string()); + size_t fileHash = STR_HASH(fs::path(filename).lexically_normal().string()); if (fileHash == m_hScriptsRsonHash) BuildScriptsRson(); diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h index a6410b07..bcf2f53c 100644 --- a/NorthstarDedicatedTest/pch.h +++ b/NorthstarDedicatedTest/pch.h @@ -23,6 +23,7 @@ #include <Windows.h> #include <Psapi.h> +#include "version.h" #include <comdef.h> #include <filesystem> #include <iostream> @@ -31,6 +32,7 @@ #include <cstring> #include <regex> #include <thread> +#include <map> #include <set> #include "logging.h" @@ -44,4 +46,17 @@ template <typename ReturnType, typename... Args> ReturnType CallVFunc(int index, return (*reinterpret_cast<ReturnType(__fastcall***)(void*, Args...)>(thisPtr))[index](thisPtr, args...); } +template <typename T, size_t index, typename ...Args> constexpr T CallVFunc_Alt(void* classBase, Args... args) noexcept { + return ((*(T(__thiscall***)(void*, Args...))(classBase))[index])(classBase, args...); +} + + +#define STR_HASH(s) (std::hash<std::string>()(s)) + +// Example usage: M_VMETHOD(int, GetEntityIndex, 8, (CBaseEntity* ent), (this, ent)) +#define M_VMETHOD(returnType, name, index, args, argsRaw) \ +FORCEINLINE returnType name args noexcept { \ + return CallVFunc_Alt<returnType, index> argsRaw; \ +} + #endif
\ No newline at end of file diff --git a/NorthstarDedicatedTest/version.cpp b/NorthstarDedicatedTest/version.cpp new file mode 100644 index 00000000..dfeb9670 --- /dev/null +++ b/NorthstarDedicatedTest/version.cpp @@ -0,0 +1,59 @@ +#include "version.h" +#include "pch.h" + +char version[16]; +char NSUserAgent[32]; + +void InitialiseVersion() { + HRSRC hResInfo; + DWORD dwSize; + HGLOBAL hResData; + LPVOID pRes, pResCopy; + UINT uLen = 0; + VS_FIXEDFILEINFO* lpFfi = NULL; + HINSTANCE hInst = ::GetModuleHandle(NULL); + + hResInfo = FindResourceW(hInst, MAKEINTRESOURCE(1), RT_VERSION); + if (hResInfo != NULL) { + dwSize = SizeofResource(hInst, hResInfo); + hResData = LoadResource(hInst, hResInfo); + if (hResData != NULL) { + pRes = LockResource(hResData); + pResCopy = LocalAlloc(LMEM_FIXED, dwSize); + if (pResCopy != 0) { + CopyMemory(pResCopy, pRes, dwSize); + VerQueryValueW(pResCopy, L"\\", (LPVOID*)&lpFfi, &uLen); + + DWORD dwFileVersionMS = lpFfi->dwFileVersionMS; + DWORD dwFileVersionLS = lpFfi->dwFileVersionLS; + + DWORD dwLeftMost = HIWORD(dwFileVersionMS); + DWORD dwSecondLeft = LOWORD(dwFileVersionMS); + DWORD dwSecondRight = HIWORD(dwFileVersionLS); + DWORD dwRightMost = LOWORD(dwFileVersionLS); + + // We actually use the rightmost integer do determine whether or not we're a debug/dev build + // If it is set to 1 (as in resources.rc), we are a dev build + // On github CI, we set this 1 to a 0 automatically as we replace the 0.0.0.1 with the real version number + if (dwRightMost == 1) { + sprintf(version, "%d.%d.%d.%d+dev", dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); + sprintf(NSUserAgent, "R2Northstar/%d.%d.%d+dev", dwLeftMost, dwSecondLeft, dwSecondRight); + } + else { + sprintf(version, "%d.%d.%d.%d", dwLeftMost, dwSecondLeft, dwSecondRight, dwRightMost); + sprintf(NSUserAgent, "R2Northstar/%d.%d.%d", dwLeftMost, dwSecondLeft, dwSecondRight); + } + UnlockResource(hResData); + FreeResource(hResData); + LocalFree(pResCopy); + return; + } + UnlockResource(hResData); + FreeResource(hResData); + LocalFree(pResCopy); + } + } + // Could not locate version info for whatever reason + spdlog::error("Failed to load version info:\n{}", std::system_category().message(GetLastError())); + sprintf(NSUserAgent, "R2Northstar/0.0.0"); +} diff --git a/NorthstarDedicatedTest/version.h b/NorthstarDedicatedTest/version.h new file mode 100644 index 00000000..d9d29fec --- /dev/null +++ b/NorthstarDedicatedTest/version.h @@ -0,0 +1,6 @@ +#pragma once + +extern char version[16]; +extern char NSUserAgent[32]; + +void InitialiseVersion();
\ No newline at end of file diff --git a/loader_launcher_proxy/Memory.cpp b/loader_launcher_proxy/Memory.cpp index f00c4d96..200246eb 100644 --- a/loader_launcher_proxy/Memory.cpp +++ b/loader_launcher_proxy/Memory.cpp @@ -5,10 +5,12 @@ IMemAlloc** g_ppMemAllocSingleton; void LoadTier0Handle() { - if (!hTier0Module) hTier0Module = GetModuleHandleA("tier0.dll"); - if (!hTier0Module) return; + if (!hTier0Module) + hTier0Module = GetModuleHandleA("tier0.dll"); + if (!hTier0Module) + return; - g_ppMemAllocSingleton = (IMemAlloc**)GetProcAddress(hTier0Module, "g_pMemAllocSingleton"); + g_ppMemAllocSingleton = (IMemAlloc**)GetProcAddress(hTier0Module, "g_pMemAllocSingleton"); } const int STATIC_ALLOC_SIZE = 4096; @@ -21,64 +23,60 @@ char pStaticAllocBuf[STATIC_ALLOC_SIZE]; void* malloc(size_t n) { - // allocate into static buffer - if (g_iStaticAllocated + n <= STATIC_ALLOC_SIZE) - { - void* ret = pStaticAllocBuf + g_iStaticAllocated; - g_iStaticAllocated += n; - return ret; - } - else - { - // try to fallback to g_pMemAllocSingleton - if (!hTier0Module || !g_ppMemAllocSingleton) LoadTier0Handle(); - if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) - return (*g_ppMemAllocSingleton)->m_vtable->Alloc(*g_ppMemAllocSingleton, n); - else - throw "Cannot allocate"; - } + // allocate into static buffer + if (g_iStaticAllocated + n <= STATIC_ALLOC_SIZE) + { + void* ret = pStaticAllocBuf + g_iStaticAllocated; + g_iStaticAllocated += n; + return ret; + } + else + { + // try to fallback to g_pMemAllocSingleton + if (!hTier0Module || !g_ppMemAllocSingleton) + LoadTier0Handle(); + if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) + return (*g_ppMemAllocSingleton)->m_vtable->Alloc(*g_ppMemAllocSingleton, n); + else + throw "Cannot allocate"; + } } void free(void* p) { - // if it was allocated into the static buffer, just do nothing, safest way to deal with it - if (p >= pStaticAllocBuf && p <= pStaticAllocBuf + STATIC_ALLOC_SIZE) - return; + // if it was allocated into the static buffer, just do nothing, safest way to deal with it + if (p >= pStaticAllocBuf && p <= pStaticAllocBuf + STATIC_ALLOC_SIZE) + return; - if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) - (*g_ppMemAllocSingleton)->m_vtable->Free(*g_ppMemAllocSingleton, p); + if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) + (*g_ppMemAllocSingleton)->m_vtable->Free(*g_ppMemAllocSingleton, p); } -void* realloc(void* old_ptr, size_t size) { - // it was allocated into the static buffer - if (old_ptr >= pStaticAllocBuf && old_ptr <= pStaticAllocBuf + STATIC_ALLOC_SIZE) - { - if (g_pLastAllocated == old_ptr) - { - // nothing was allocated after this - size_t old_size = g_iStaticAllocated - ((size_t)g_pLastAllocated - (size_t)pStaticAllocBuf); - size_t diff = size - old_size; - if (diff > 0) - g_iStaticAllocated += diff; - return old_ptr; - } - else - { - return malloc(size); - } - } +void* realloc(void* old_ptr, size_t size) +{ + // it was allocated into the static buffer + if (old_ptr >= pStaticAllocBuf && old_ptr <= pStaticAllocBuf + STATIC_ALLOC_SIZE) + { + if (g_pLastAllocated == old_ptr) + { + // nothing was allocated after this + size_t old_size = g_iStaticAllocated - ((size_t)g_pLastAllocated - (size_t)pStaticAllocBuf); + size_t diff = size - old_size; + if (diff > 0) + g_iStaticAllocated += diff; + return old_ptr; + } + else + { + return malloc(size); + } + } - if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) - return (*g_ppMemAllocSingleton)->m_vtable->Realloc(*g_ppMemAllocSingleton, old_ptr, size); - return nullptr; + if (g_ppMemAllocSingleton && *g_ppMemAllocSingleton) + return (*g_ppMemAllocSingleton)->m_vtable->Realloc(*g_ppMemAllocSingleton, old_ptr, size); + return nullptr; } -void* operator new(size_t n) -{ - return malloc(n); -} +void* operator new(size_t n) { return malloc(n); } -void operator delete(void* p) -{ - return free(p); -} +void operator delete(void* p) { return free(p); } |