diff options
Diffstat (limited to 'NorthstarDedicatedTest')
31 files changed, 1423 insertions, 251 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index bd0d9eb6..276fbab8 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -1,14 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> @@ -27,19 +19,6 @@ <ProjectName>Northstar</ProjectName> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>DynamicLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v143</PlatformToolset> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> @@ -58,12 +37,6 @@ </ImportGroup> <ImportGroup Label="Shared"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> @@ -71,56 +44,12 @@ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> <PropertyGroup Label="UserMacros" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;_DEBUG;NORTHSTARDEDICATEDTEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <PrecompiledHeader>Use</PrecompiledHeader> - <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> - <LanguageStandard>stdcpp17</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableUAC>false</EnableUAC> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <ClCompile> - <WarningLevel>Level3</WarningLevel> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <SDLCheck>true</SDLCheck> - <PreprocessorDefinitions>WIN32;NDEBUG;NORTHSTARDEDICATEDTEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> - <ConformanceMode>true</ConformanceMode> - <PrecompiledHeader>Use</PrecompiledHeader> - <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> - <LanguageStandard>stdcpp17</LanguageStandard> - </ClCompile> - <Link> - <SubSystem>Windows</SubSystem> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableUAC>false</EnableUAC> - <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> - </Link> - </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> @@ -136,7 +65,7 @@ <SubSystem>Windows</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcrypto_static.lib;$(ProjectDir)include\libssl_static.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> <ForceSymbolReferences> </ForceSymbolReferences> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> @@ -158,6 +87,7 @@ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -165,7 +95,7 @@ <OptimizeReferences>true</OptimizeReferences> <GenerateDebugInformation>true</GenerateDebugInformation> <EnableUAC>false</EnableUAC> - <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcrypto_static.lib;$(ProjectDir)include\libssl_static.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>$(ProjectDir)include\MinHook.x64.lib;$(ProjectDir)include\libcurl\lib\libcurl_a.lib;dbghelp.lib;Wldap32.lib;Normaliz.lib;%(AdditionalDependencies)</AdditionalDependencies> <ForceSymbolReferences> </ForceSymbolReferences> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> @@ -586,9 +516,11 @@ <ClInclude Include="include\spdlog\tweakme.h" /> <ClInclude Include="include\spdlog\version.h" /> <ClInclude Include="keyvalues.h" /> + <ClInclude Include="languagehooks.h" /> <ClInclude Include="logging.h" /> <ClInclude Include="main.h" /> <ClInclude Include="masterserver.h" /> + <ClInclude Include="maxplayers.h" /> <ClInclude Include="memalloc.h" /> <ClInclude Include="miscclientfixes.h" /> <ClInclude Include="misccommands.h" /> @@ -627,6 +559,8 @@ <ClCompile Include="hooks.cpp" /> <ClCompile Include="hookutils.cpp" /> <ClCompile Include="keyvalues.cpp" /> + <ClCompile Include="maxplayers.cpp" /> + <ClCompile Include="languagehooks.cpp" /> <ClCompile Include="memalloc.cpp" /> <ClCompile Include="miscclientfixes.cpp" /> <ClCompile Include="misccommands.cpp" /> @@ -636,9 +570,7 @@ <ClCompile Include="masterserver.cpp" /> <ClCompile Include="modmanager.cpp" /> <ClCompile Include="pch.cpp"> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> - <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> </ClCompile> <ClCompile Include="pdef.cpp" /> @@ -687,7 +619,10 @@ <None Include="include\openssl\x509_vfy.h.in" /> <None Include="include\spdlog\fmt\bundled\LICENSE.rst" /> </ItemGroup> + <ItemGroup> + <None Include="..\Northstar-Legal.txt" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index 8d6bc246..aaa99a14 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1392,6 +1392,9 @@ <ClInclude Include="miscserverfixes.h"> <Filter>Header Files\Server</Filter> </ClInclude> + <ClInclude Include="maxplayers.h"> + <Filter>Header Files\Shared</Filter> + </ClInclude> <ClInclude Include="include\libcurl\include\curl\curl.h"> <Filter>Header Files\include\libcurl</Filter> </ClInclude> @@ -1428,6 +1431,9 @@ <ClInclude Include="bansystem.h"> <Filter>Header Files\Server\Authentication</Filter> </ClInclude> + <ClInclude Include="languagehooks.h"> + <Filter>Header Files\Client</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="dllmain.cpp"> @@ -1535,6 +1541,9 @@ <ClCompile Include="miscclientfixes.cpp"> <Filter>Source Files\Client</Filter> </ClCompile> + <ClCompile Include="maxplayers.cpp"> + <Filter>Source Files\Shared</Filter> + </ClCompile> <ClCompile Include="miscserverfixes.cpp"> <Filter>Source Files\Server</Filter> </ClCompile> @@ -1544,6 +1553,9 @@ <ClCompile Include="bansystem.cpp"> <Filter>Source Files\Server\Authentication</Filter> </ClCompile> + <ClCompile Include="languagehooks.cpp"> + <Filter>Source Files\Client</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="include\spdlog\fmt\bundled\LICENSE.rst"> diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp index ed7e8dac..de460662 100644 --- a/NorthstarDedicatedTest/convar.cpp +++ b/NorthstarDedicatedTest/convar.cpp @@ -20,7 +20,7 @@ ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, co ConVar* newVar = new ConVar; conVarConstructor(newVar, name, defaultValue, flags, helpString); - g_CustomConvars.insert(std::make_pair(name, newVar)); + g_CustomConvars.emplace(name, newVar); return newVar; } diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index 83a9950a..1359a33e 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -6,13 +6,14 @@ bool IsDedicated() { - return CommandLine()->CheckParm("-dedicated"); + //return CommandLine()->CheckParm("-dedicated"); + return strstr(GetCommandLineA(), "-dedicated"); } // CDedidcatedExports defs struct CDedicatedExports; // forward declare -typedef void (*DedicatedSys_PrintfType)(CDedicatedExports* dedicated, char* msg); +typedef void (*DedicatedSys_PrintfType)(CDedicatedExports* dedicated, const char* msg); typedef void (*DedicatedRunServerType)(CDedicatedExports* dedicated); // would've liked to just do this as a class but have not been able to get it to work @@ -26,7 +27,7 @@ struct CDedicatedExports DedicatedRunServerType RunServer; }; -void Sys_Printf(CDedicatedExports* dedicated, char* msg) +void Sys_Printf(CDedicatedExports* dedicated, const char* msg) { spdlog::info("[DEDICATED PRINT] {}", msg); } @@ -35,7 +36,7 @@ typedef void(*CHostState__InitType)(CHostState* self); void RunServer(CDedicatedExports* dedicated) { - Sys_Printf(dedicated, (char*)"CDedicatedExports::RunServer(): starting"); + Sys_Printf(dedicated, "CDedicatedExports::RunServer(): starting"); // init hoststate, if we don't do this, we get a crash later on CHostState__InitType CHostState__Init = (CHostState__InitType)((char*)GetModuleHandleA("engine.dll") + 0x16E110); @@ -76,6 +77,32 @@ bool IsGameActiveWindowHook() return true; } +HANDLE consoleInputThreadHandle = NULL; + +DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter) +{ + while (!g_pEngine || !g_pHostState || g_pHostState->m_iCurrentState != HostState_t::HS_RUN) + Sleep(1000); + + // Bind stdin to receive console input. + FILE* fp = nullptr; + freopen_s(&fp, "CONIN$", "r", stdin); + + spdlog::info("Ready to receive console commands."); + + { + // Process console input + std::string input; + while (g_pEngine && g_pEngine->m_nQuitting == EngineQuitState::QUIT_NOTQUITTING && std::getline(std::cin, input)) + { + input += "\n"; + Cbuf_AddText(Cbuf_GetCurrentPlayer(), input.c_str(), cmd_source_t::kCommandSrcCode); + } + } + + return 0; +} + void InitialiseDedicated(HMODULE engineAddress) { if (!IsDedicated()) @@ -209,7 +236,7 @@ void InitialiseDedicated(HMODULE engineAddress) TempReadWrite rw(ptr); // remove call to Shader_Connect - *ptr = 0x90; + *ptr = (char)0x90; *(ptr + 1) = (char)0x90; *(ptr + 2) = (char)0x90; *(ptr + 3) = (char)0x90; @@ -246,7 +273,7 @@ void InitialiseDedicated(HMODULE engineAddress) TempReadWrite rw(ptr); // remove call to ui loading stuff - *ptr = 0x90; + *ptr = (char)0x90; *(ptr + 1) = (char)0x90; *(ptr + 2) = (char)0x90; *(ptr + 3) = (char)0x90; @@ -380,25 +407,43 @@ void InitialiseDedicated(HMODULE engineAddress) CommandLine()->AppendParm("+host_preload_shaders", "0"); CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); CommandLine()->AppendParm("+exec", "autoexec_ns_server"); + + // Disable Quick Edit mode to reduce chance of user unintentionally hanging their server by selecting something. + if (!CommandLine()->CheckParm("-bringbackquickedit")) + { + HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode = 0; + + if (GetConsoleMode(stdIn, &mode)) { + if (mode & ENABLE_QUICK_EDIT_MODE) { + mode &= ~ENABLE_QUICK_EDIT_MODE; + mode &= ~ENABLE_MOUSE_INPUT; + + mode |= ENABLE_PROCESSED_INPUT; + + SetConsoleMode(stdIn, mode); + } + } + } else spdlog::info("Quick Edit enabled by user request"); + + // create console input thread + if (!CommandLine()->CheckParm("-noconsoleinput")) + consoleInputThreadHandle = CreateThread(0, 0, ConsoleInputThread, 0, 0, NULL); + else spdlog::info("Console input disabled by user request"); } -typedef void(*Tier0_InitOriginType)(); -Tier0_InitOriginType Tier0_InitOrigin; -void Tier0_InitOriginHook() +void InitialiseDedicatedOrigin(HMODULE baseAddress) { // disable origin on dedicated // for any big ea lawyers, this can't be used to play the game without origin, game will throw a fit if you try to do anything without an origin id as a client // for dedi it's fine though, game doesn't care if origin is disabled as long as there's only a server - Tier0_InitOrigin(); -} -void InitialiseDedicatedOrigin(HMODULE baseAddress) -{ if (!IsDedicated()) return; - HookEnabler hook; - ENABLER_CREATEHOOK(hook, GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"), &Tier0_InitOriginHook, reinterpret_cast<LPVOID*>(&Tier0_InitOrigin)); + char* ptr = (char*)GetProcAddress(GetModuleHandleA("tier0.dll"), "Tier0_InitOrigin"); + TempReadWrite rw(ptr); + *ptr = (char)0xC3; // ret } typedef void(*PrintFatalSquirrelErrorType)(void* sqvm); diff --git a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp index 7097e11a..1d0e13b6 100644 --- a/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp +++ b/NorthstarDedicatedTest/dedicatedmaterialsystem.cpp @@ -59,7 +59,7 @@ void InitialiseDedicatedMaterialSystem(HMODULE baseAddress) TempReadWrite rw(ptr); // make the game always use the error material - *ptr = 0xE9; + *ptr = (char)0xE9; *(ptr + 1) = (char)0x34; *(ptr + 2) = (char)0x03; *(ptr + 3) = (char)0x00; diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index c7cfce8b..83e78f4e 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -28,6 +28,8 @@ #include "rpakfilesystem.h" #include "bansystem.h" #include "memalloc.h" +#include "maxplayers.h" +#include "languagehooks.h" bool initialised = false; @@ -45,10 +47,6 @@ BOOL APIENTRY DllMain( HMODULE hModule, break; } - if (!initialised) - InitialiseNorthstar(); - initialised = true; - return TRUE; } @@ -58,39 +56,39 @@ void WaitForDebugger(HMODULE baseAddress) if (CommandLine()->CheckParm("-waitfordebugger")) { spdlog::info("waiting for debugger..."); - spdlog::info("{} bytes have been statically allocated", g_iStaticAllocated); while (!IsDebuggerPresent()) Sleep(100); } } -// in the future this will be called from launcher instead of dllmain -void InitialiseNorthstar() +bool InitialiseNorthstar() { if (initialised) { - spdlog::error("Called InitialiseNorthstar more than once!"); - return; + //spdlog::warn("Called InitialiseNorthstar more than once!"); // it's actually 100% fine for that to happen + return false; } + initialised = true; + curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base); + InitialiseLogging(); // apply initial hooks InstallInitialHooks(); InitialiseInterfaceCreationHooks(); - // adding a callback to tier0 won't work for some reason - AddDllLoadCallback("launcher.dll", InitialiseTier0GameUtilFunctions); + AddDllLoadCallback("tier0.dll", InitialiseTier0GameUtilFunctions); AddDllLoadCallback("engine.dll", WaitForDebugger); AddDllLoadCallback("engine.dll", InitialiseEngineGameUtilFunctions); AddDllLoadCallback("server.dll", InitialiseServerGameUtilFunctions); // dedi patches { + AddDllLoadCallback("tier0.dll", InitialiseDedicatedOrigin); AddDllLoadCallback("engine.dll", InitialiseDedicated); - AddDllLoadCallback("launcher.dll", InitialiseDedicatedOrigin); AddDllLoadCallback("server.dll", InitialiseDedicatedServerGameDLL); AddDllLoadCallback("materialsystem_dx11.dll", InitialiseDedicatedMaterialSystem); // this fucking sucks, but seemingly we somehow load after rtech_game???? unsure how, but because of this we have to apply patches here, not on rtech_game load @@ -102,6 +100,7 @@ void InitialiseNorthstar() // client-exclusive patches { + AddDllLoadCallback("tier0.dll", InitialiseTier0LanguageHooks); AddDllLoadCallback("engine.dll", InitialiseClientEngineSecurityPatches); AddDllLoadCallback("client.dll", InitialiseClientSquirrel); AddDllLoadCallback("client.dll", InitialiseSourceConsole); @@ -130,6 +129,16 @@ void InitialiseNorthstar() AddDllLoadCallback("engine.dll", InitialiseEngineRpakFilesystem); AddDllLoadCallback("engine.dll", InitialiseKeyValues); + // maxplayers increase + AddDllLoadCallback("engine.dll", InitialiseMaxPlayersOverride_Engine); + AddDllLoadCallback("client.dll", InitialiseMaxPlayersOverride_Client); + AddDllLoadCallback("server.dll", InitialiseMaxPlayersOverride_Server); + // mod manager after everything else AddDllLoadCallback("engine.dll", InitialiseModManager); + + // run callbacks for any libraries that are already loaded by now + CallAllPendingDLLLoadCallbacks(); + + return true; }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/filesystem.cpp b/NorthstarDedicatedTest/filesystem.cpp index 89eb9423..1fe3ec9d 100644 --- a/NorthstarDedicatedTest/filesystem.cpp +++ b/NorthstarDedicatedTest/filesystem.cpp @@ -57,7 +57,7 @@ std::string ReadVPKFile(const char* path) char data[4096]; do { - bytesRead = (*g_Filesystem)->m_vtable2->Read(&(*g_Filesystem)->m_vtable2, data, std::size(data), fileHandle); + bytesRead = (*g_Filesystem)->m_vtable2->Read(&(*g_Filesystem)->m_vtable2, data, (int)std::size(data), fileHandle); fileStream.write(data, bytesRead); } while (bytesRead == std::size(data)); diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp index 642c44e6..1cbd8648 100644 --- a/NorthstarDedicatedTest/gameutils.cpp +++ b/NorthstarDedicatedTest/gameutils.cpp @@ -78,12 +78,32 @@ void InitialiseServerGameUtilFunctions(HMODULE baseAddress) void InitialiseTier0GameUtilFunctions(HMODULE baseAddress) { - baseAddress = GetModuleHandleA("tier0.dll"); - - CreateGlobalMemAlloc = (CreateGlobalMemAllocType)GetProcAddress(baseAddress, "CreateGlobalMemAlloc"); - g_pMemAllocSingleton = CreateGlobalMemAlloc(); - - Error = (ErrorType)GetProcAddress(baseAddress, "Error"); - CommandLine = (CommandLineType)GetProcAddress(baseAddress, "CommandLine"); - Plat_FloatTime = (Plat_FloatTimeType)GetProcAddress(baseAddress, "Plat_FloatTime"); + if (!baseAddress) + { + spdlog::critical("tier0 base address is null, but it should be already loaded"); + throw "tier0 base address is null, but it should be already loaded"; + } + if (g_pMemAllocSingleton) + return; // seems this function was already called + CreateGlobalMemAlloc = reinterpret_cast<CreateGlobalMemAllocType>(GetProcAddress(baseAddress, "CreateGlobalMemAlloc")); + IMemAlloc** ppMemAllocSingleton = reinterpret_cast<IMemAlloc**>(GetProcAddress(baseAddress, "g_pMemAllocSingleton")); + if (!ppMemAllocSingleton) + { + spdlog::critical("Address of g_pMemAllocSingleton is a null pointer, this should never happen"); + throw "Address of g_pMemAllocSingleton is a null pointer, this should never happen"; + } + if (!*ppMemAllocSingleton) + { + g_pMemAllocSingleton = CreateGlobalMemAlloc(); + *ppMemAllocSingleton = g_pMemAllocSingleton; + spdlog::info("Created new g_pMemAllocSingleton"); + } + else + { + g_pMemAllocSingleton = *ppMemAllocSingleton; + } + + Error = reinterpret_cast<ErrorType>(GetProcAddress(baseAddress, "Error")); + CommandLine = reinterpret_cast<CommandLineType>(GetProcAddress(baseAddress, "CommandLine")); + Plat_FloatTime = reinterpret_cast<Plat_FloatTimeType>(GetProcAddress(baseAddress, "Plat_FloatTime")); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h index 8def57eb..43f387d1 100644 --- a/NorthstarDedicatedTest/gameutils.h +++ b/NorthstarDedicatedTest/gameutils.h @@ -7,10 +7,19 @@ class IMemAlloc public: struct VTable { - void* unknown[1]; - void* (*Alloc)(IMemAlloc* memAlloc, size_t nSize); - void* unknown2[3]; - void (*Free)(IMemAlloc* memAlloc, void* pMem); + void* unknown[1]; // alloc debug + void* (*Alloc) (IMemAlloc* memAlloc, size_t nSize); + void* unknown2[1]; // realloc debug + void* (*Realloc)(IMemAlloc* memAlloc, void* pMem, size_t nSize); + void* unknown3[1]; // free #1 + void (*Free) (IMemAlloc* memAlloc, void* pMem); + void* unknown4[2]; // nullsubs, maybe CrtSetDbgFlag + size_t(*GetSize) (IMemAlloc* memAlloc, void* pMem); + void* unknown5[9]; // they all do literally nothing + void (*DumpStats) (IMemAlloc* memAlloc); + void (*DumpStatsFileBase) (IMemAlloc* memAlloc, const char* pchFileBase); + void* unknown6[4]; + int (*heapchk) (IMemAlloc* memAlloc); }; VTable* m_vtable; diff --git a/NorthstarDedicatedTest/hooks.cpp b/NorthstarDedicatedTest/hooks.cpp index 3de8d483..ce4d75ad 100644 --- a/NorthstarDedicatedTest/hooks.cpp +++ b/NorthstarDedicatedTest/hooks.cpp @@ -1,19 +1,36 @@ #include "pch.h" #include "hooks.h" #include "hookutils.h" +#include "sigscanning.h" #include <wchar.h> #include <iostream> #include <vector> +#include <fstream> +#include <sstream> +#include <filesystem> +#include <Psapi.h> + +typedef LPSTR(*GetCommandLineAType)(); +LPSTR GetCommandLineAHook(); typedef HMODULE(*LoadLibraryExAType)(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +typedef HMODULE(*LoadLibraryAType)(LPCSTR lpLibFileName); +HMODULE LoadLibraryAHook(LPCSTR lpLibFileName); + typedef HMODULE(*LoadLibraryExWType)(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +typedef HMODULE(*LoadLibraryWType)(LPCWSTR lpLibFileName); +HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName); + +GetCommandLineAType GetCommandLineAOriginal; LoadLibraryExAType LoadLibraryExAOriginal; +LoadLibraryAType LoadLibraryAOriginal; LoadLibraryExWType LoadLibraryExWOriginal; +LoadLibraryWType LoadLibraryWOriginal; void InstallInitialHooks() { @@ -21,8 +38,57 @@ void InstallInitialHooks() spdlog::error("MH_Initialize failed"); HookEnabler hook; + ENABLER_CREATEHOOK(hook, &GetCommandLineA, &GetCommandLineAHook, reinterpret_cast<LPVOID*>(&GetCommandLineAOriginal)); ENABLER_CREATEHOOK(hook, &LoadLibraryExA, &LoadLibraryExAHook, reinterpret_cast<LPVOID*>(&LoadLibraryExAOriginal)); + ENABLER_CREATEHOOK(hook, &LoadLibraryA, &LoadLibraryAHook, reinterpret_cast<LPVOID*>(&LoadLibraryAOriginal)); ENABLER_CREATEHOOK(hook, &LoadLibraryExW, &LoadLibraryExWHook, reinterpret_cast<LPVOID*>(&LoadLibraryExWOriginal)); + ENABLER_CREATEHOOK(hook, &LoadLibraryW, &LoadLibraryWHook, reinterpret_cast<LPVOID*>(&LoadLibraryWOriginal)); +} + +LPSTR GetCommandLineAHook() +{ + static char* cmdlineModified; + static char* cmdlineOrg; + + if (cmdlineOrg == nullptr || cmdlineModified == nullptr) + { + cmdlineOrg = GetCommandLineAOriginal(); + bool isDedi = strstr(cmdlineOrg, "-dedicated"); // well, this one has to be a real argument + + std::string args; + std::ifstream cmdlineArgFile; + + // it looks like CommandLine() prioritizes parameters apprearing first, so we want the real commandline to take priority + // not to mention that cmdlineOrg starts with the EXE path + args.append(cmdlineOrg); + args.append(" "); + + // append those from the file + + cmdlineArgFile = std::ifstream(!isDedi ? "ns_startup_args.txt" : "ns_startup_args_dedi.txt"); + + if (cmdlineArgFile) + { + std::stringstream argBuffer; + argBuffer << cmdlineArgFile.rdbuf(); + cmdlineArgFile.close(); + + args.append(argBuffer.str()); + } + + auto len = args.length(); + cmdlineModified = new char[len + 1]; + if (!cmdlineModified) + { + spdlog::error("malloc failed for command line"); + return cmdlineOrg; + } + memcpy(cmdlineModified, args.c_str(), len + 1); + + spdlog::info("Command line: {}", cmdlineModified); + } + + return cmdlineModified; } // dll load callback stuff @@ -46,21 +112,75 @@ void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback) dllLoadCallbacks.push_back(callbackStruct); } -HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress) { - HMODULE moduleAddress = LoadLibraryExAOriginal(lpLibFileName, hFile, dwFlags); - - if (moduleAddress) + for (auto& callbackStruct : dllLoadCallbacks) + { + if (!callbackStruct->called && strstr(lpLibFileName + (strlen(lpLibFileName) - callbackStruct->dll.length()), callbackStruct->dll.c_str()) != nullptr) + { + callbackStruct->callback(moduleAddress); + callbackStruct->called = true; + } + } +} + +void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress) +{ + for (auto& callbackStruct : dllLoadCallbacks) + { + std::wstring wcharStrDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()); + const wchar_t* callbackDll = wcharStrDll.c_str(); + if (!callbackStruct->called && wcsstr(lpLibFileName + (wcslen(lpLibFileName) - wcharStrDll.length()), callbackDll) != nullptr) + { + callbackStruct->callback(moduleAddress); + callbackStruct->called = true; + } + } +} + +void CallAllPendingDLLLoadCallbacks() +{ + HMODULE hMods[1024]; + HANDLE hProcess = GetCurrentProcess(); + DWORD cbNeeded; + unsigned int i; + + // Get a list of all the modules in this process. + if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { - for (auto& callbackStruct : dllLoadCallbacks) + for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { - if (!callbackStruct->called && strstr(lpLibFileName + (strlen(lpLibFileName) - strlen(callbackStruct->dll.c_str())), callbackStruct->dll.c_str()) != nullptr) + wchar_t szModName[MAX_PATH]; + + // Get the full path to the module's file. + if (GetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { - callbackStruct->callback(moduleAddress); - callbackStruct->called = true; + CallLoadLibraryWCallbacks(szModName, hMods[i]); } } } +} + +HMODULE LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + HMODULE moduleAddress = LoadLibraryExAOriginal(lpLibFileName, hFile, dwFlags); + + if (moduleAddress) + { + CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); + } + + return moduleAddress; +} + +HMODULE LoadLibraryAHook(LPCSTR lpLibFileName) +{ + HMODULE moduleAddress = LoadLibraryAOriginal(lpLibFileName); + + if (moduleAddress) + { + CallLoadLibraryACallbacks(lpLibFileName, moduleAddress); + } return moduleAddress; } @@ -71,16 +191,19 @@ HMODULE LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) if (moduleAddress) { - for (auto& callbackStruct : dllLoadCallbacks) - { - std::wstring wcharStrDll = std::wstring(callbackStruct->dll.begin(), callbackStruct->dll.end()); - const wchar_t* callbackDll = wcharStrDll.c_str(); - if (!callbackStruct->called && wcsstr(lpLibFileName + (wcslen(lpLibFileName) - wcslen(callbackDll)), callbackDll) != nullptr) - { - callbackStruct->callback(moduleAddress); - callbackStruct->called = true; - } - } + CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); + } + + return moduleAddress; +} + +HMODULE LoadLibraryWHook(LPCWSTR lpLibFileName) +{ + HMODULE moduleAddress = LoadLibraryWOriginal(lpLibFileName); + + if (moduleAddress) + { + CallLoadLibraryWCallbacks(lpLibFileName, moduleAddress); } return moduleAddress; diff --git a/NorthstarDedicatedTest/hooks.h b/NorthstarDedicatedTest/hooks.h index 972b38a6..10e4d4ba 100644 --- a/NorthstarDedicatedTest/hooks.h +++ b/NorthstarDedicatedTest/hooks.h @@ -4,4 +4,6 @@ void InstallInitialHooks(); typedef void(*DllLoadCallbackFuncType)(HMODULE moduleAddress); -void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback);
\ No newline at end of file +void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback); + +void CallAllPendingDLLLoadCallbacks();
\ No newline at end of file diff --git a/NorthstarDedicatedTest/hookutils.cpp b/NorthstarDedicatedTest/hookutils.cpp index e86c671c..8ab24a3b 100644 --- a/NorthstarDedicatedTest/hookutils.cpp +++ b/NorthstarDedicatedTest/hookutils.cpp @@ -38,7 +38,7 @@ void HookEnabler::CreateHook(LPVOID ppTarget, LPVOID ppDetour, LPVOID* ppOrigina else { if (targetName != nullptr) - spdlog::error("MH_CreateHook failed for function %s", targetName); + spdlog::error("MH_CreateHook failed for function {}", targetName); else spdlog::error("MH_CreateHook failed for unknown function"); } @@ -51,7 +51,7 @@ HookEnabler::~HookEnabler() if (MH_EnableHook(hook->targetAddress) != MH_OK) { if (hook->targetName != nullptr) - spdlog::error("MH_EnableHook failed for function %s", hook->targetName); + spdlog::error("MH_EnableHook failed for function {}", hook->targetName); else spdlog::error("MH_EnableHook failed for unknown function"); } diff --git a/NorthstarDedicatedTest/include/MinHook.x64.dll b/NorthstarDedicatedTest/include/MinHook.x64.dll Binary files differdeleted file mode 100644 index b4f51c2e..00000000 --- a/NorthstarDedicatedTest/include/MinHook.x64.dll +++ /dev/null diff --git a/NorthstarDedicatedTest/include/MinHook.x64.lib b/NorthstarDedicatedTest/include/MinHook.x64.lib Binary files differindex 909fe682..213df08f 100644 --- a/NorthstarDedicatedTest/include/MinHook.x64.lib +++ b/NorthstarDedicatedTest/include/MinHook.x64.lib diff --git a/NorthstarDedicatedTest/keyvalues.cpp b/NorthstarDedicatedTest/keyvalues.cpp index 6de93822..b14ecf42 100644 --- a/NorthstarDedicatedTest/keyvalues.cpp +++ b/NorthstarDedicatedTest/keyvalues.cpp @@ -50,7 +50,7 @@ void ModManager::TryBuildKeyValues(const char* filename) // copy over patch kv files, and add #bases to new file, last mods' patches should be applied first // note: #include should be identical but it's actually just broken, thanks respawn - for (int i = m_loadedMods.size() - 1; i > -1; i--) + for (int64_t i = m_loadedMods.size() - 1; i > -1; i--) { if (!m_loadedMods[i].Enabled) continue; @@ -92,6 +92,7 @@ void ModManager::TryBuildKeyValues(const char* filename) char rootName[64]; memset(rootName, 0, sizeof(rootName)); + rootName[63] = '\0'; // iterate until we hit an ascii char that isn't in a # command or comment to get root obj name int i = 0; diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp new file mode 100644 index 00000000..08bacaf9 --- /dev/null +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -0,0 +1,109 @@ +#include "pch.h" +#include "languagehooks.h" +#include "gameutils.h" +#include <filesystem> +#include <regex> + +namespace fs = std::filesystem; + +typedef char* (*GetGameLanguageType)(); +char* GetGameLanguage(); + +typedef LANGID(*Tier0_DetectDefaultLanguageType)(); + +GetGameLanguageType GetGameLanguageOriginal; + +bool CheckLangAudioExists(char* lang) +{ + std::string path{ "r2\\sound\\general_" }; + path += lang; + path += ".mstr"; + return fs::exists(path); +} + +std::vector<std::string> file_list(fs::path dir, std::regex ext_pattern) +{ + std::vector<std::string> result; + + using iterator = fs::directory_iterator; + + const iterator end; + for (iterator iter{ dir }; iter != end; ++iter) + { + const std::string filename = iter->path().filename().string(); + std::smatch matches; + if (fs::is_regular_file(*iter) && std::regex_match(filename, matches, ext_pattern)) + { + result.push_back(std::move(matches.str(1))); + } + } + + return result; +} + +std::string GetAnyInstalledAudioLanguage() +{ + for (const auto& lang : file_list("r2\\sound\\", std::regex(".*?general_([a-z]+)_patch_1\\.mstr"))) + if (lang != "general" || lang != "") + return lang; + return "NO LANGUAGE DETECTED"; +} + +char* GetGameLanguageHook() +{ + auto tier0Handle = GetModuleHandleA("tier0.dll"); + auto Tier0_DetectDefaultLanguageType = GetProcAddress(tier0Handle, "Tier0_DetectDefaultLanguage"); + char* ingameLang1 = (char*)tier0Handle + 0xA9B60; // one of the globals we need to override if overriding lang (size: 256) + bool& canOriginDictateLang = *(bool*)((char*)tier0Handle + 0xA9A90); + + const char* forcedLanguage; + if (CommandLine()->CheckParm("-language", &forcedLanguage)) + { + if (!CheckLangAudioExists((char*)forcedLanguage)) + { + spdlog::info("User tried to force the language (-language) to \"{}\", but audio for this language doesn't exist and the game is bound to error, falling back to next option...", forcedLanguage); + } + else + { + spdlog::info("User forcing the language (-language) to: {}", forcedLanguage); + strncpy(ingameLang1, forcedLanguage, 256); + return ingameLang1; + } + } + + canOriginDictateLang = true; // let it try + { + auto lang = GetGameLanguageOriginal(); + if (!CheckLangAudioExists(lang)) + { + spdlog::info("Origin detected language \"{}\", but we do not have audio for it installed, falling back to the next option", lang); + + } + else + { + spdlog::info("Origin detected language: {}", lang); + return lang; + } + } + + Tier0_DetectDefaultLanguageType(); // force the global in tier0 to be populated with language inferred from user's system rather than defaulting to Russian + canOriginDictateLang = false; // Origin has no say anymore, we will fallback to user's system setup language + auto lang = GetGameLanguageOriginal(); + spdlog::info("Detected system language: {}", lang); + if (!CheckLangAudioExists(lang)) + { + spdlog::warn("Caution, audio for this language does NOT exist. You might want to override your game language with -language command line option."); + auto lang = GetAnyInstalledAudioLanguage(); + spdlog::warn("Falling back to first installed audio language: {}", lang.c_str()); + strncpy(ingameLang1, lang.c_str(), 256); + return ingameLang1; + } + + return lang; +} + +void InitialiseTier0LanguageHooks(HMODULE baseAddress) +{ + HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xF560, &GetGameLanguageHook, reinterpret_cast<LPVOID*>(&GetGameLanguageOriginal)); +}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/languagehooks.h b/NorthstarDedicatedTest/languagehooks.h new file mode 100644 index 00000000..55b591e0 --- /dev/null +++ b/NorthstarDedicatedTest/languagehooks.h @@ -0,0 +1,3 @@ +#pragma once + +void InitialiseTier0LanguageHooks(HMODULE baseAddress); diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index e13a9150..c5c5ea19 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -113,7 +113,7 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) GetModuleFileNameExA(GetCurrentProcess(), crashedModuleHandle, crashedModuleFullName, MAX_PATH); char* crashedModuleName = strrchr(crashedModuleFullName, '\\') + 1; - DWORD crashedModuleOffset = ((DWORD)exceptionAddress) - ((DWORD)crashedModuleInfo.lpBaseOfDll); + DWORD64 crashedModuleOffset = ((DWORD64)exceptionAddress) - ((DWORD64)crashedModuleInfo.lpBaseOfDll); CONTEXT* exceptionContext = exceptionInfo->ContextRecord; spdlog::error("Northstar has crashed! a minidump has been written and exception info is available below:"); @@ -186,17 +186,18 @@ void InitialiseLogging() AllocConsole(); freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v"); spdlog::flush_on(spdlog::level::info); // log file stuff - // generate log file, format should be nslog%d-%m-%Y %H-%M-%S.txt in gamedir/R2Northstar/logs + // generate log file, format should be nslog%Y-%m-%d %H-%M-%S.txt in gamedir/R2Northstar/logs // todo: might be good to delete logs that are too old time_t time = std::time(nullptr); tm currentTime = *std::localtime(&time); std::stringstream stream; - stream << std::put_time(¤tTime, "R2Northstar/logs/nslog%d-%m-%Y %H-%M-%S.txt"); + stream << std::put_time(¤tTime, "R2Northstar/logs/nslog%Y-%m-%d %H-%M-%S.txt"); // create logger spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); @@ -262,7 +263,7 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, } } - char formatted[2048]; + char formatted[2048] = { 0 }; bool shouldFormat = true; // because titanfall 2 is quite possibly the worst thing to yet exist, it sometimes gives invalid specifiers which will crash @@ -324,13 +325,59 @@ void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, spdlog::warn("Failed to format {} \"{}\"", typeStr, format); } + auto endpos = strlen(formatted); + if (formatted[endpos - 1] == '\n') + formatted[endpos - 1] = '\0'; // cut off repeated newline + spdlog::info("[SERVER {}] {}", typeStr, formatted); } + +typedef void(*Status_ConMsg_Type)(const char* text, ...); +Status_ConMsg_Type Status_ConMsg_Original; + +void Status_ConMsg_Hook(const char* text, ...) +{ + char formatted[2048]; + va_list list; + + va_start(list, text); + vsprintf_s(formatted, text, list); + va_end(list); + + auto endpos = strlen(formatted); + if (formatted[endpos - 1] == '\n') + formatted[endpos - 1] = '\0'; // cut off repeated newline + + spdlog::info(formatted); +} + +typedef bool(*CClientState_ProcessPrint_Type)(__int64 thisptr, __int64 msg); +CClientState_ProcessPrint_Type CClientState_ProcessPrint_Original; + +bool CClientState_ProcessPrint_Hook(__int64 thisptr, __int64 msg) +{ + char* text = *(char**)(msg + 0x20); + + auto endpos = strlen(text); + if (text[endpos - 1] == '\n') + text[endpos - 1] = '\0'; // cut off repeated newline + + spdlog::info(text); + return true; +} + void InitialiseEngineSpewFuncHooks(HMODULE baseAddress) { HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x11CA80, EngineSpewFuncHook, reinterpret_cast<LPVOID*>(&EngineSpewFunc)); + // Hook print function that status concmd uses to actually print data + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x15ABD0, Status_ConMsg_Hook, reinterpret_cast<LPVOID*>(&Status_ConMsg_Original)); + + // Hook CClientState::ProcessPrint + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1A1530, CClientState_ProcessPrint_Hook, reinterpret_cast<LPVOID*>(&CClientState_ProcessPrint_Original)); + Cvar_spewlog_enable = RegisterConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged"); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/main.h b/NorthstarDedicatedTest/main.h index ef5d86dc..90e88912 100644 --- a/NorthstarDedicatedTest/main.h +++ b/NorthstarDedicatedTest/main.h @@ -1,3 +1,3 @@ #pragma once -extern "C" __declspec(dllexport) void InitialiseNorthstar();
\ No newline at end of file +extern "C" __declspec(dllexport) bool InitialiseNorthstar();
\ No newline at end of file diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp index 0913be11..d34065c0 100644 --- a/NorthstarDedicatedTest/masterserver.cpp +++ b/NorthstarDedicatedTest/masterserver.cpp @@ -3,7 +3,6 @@ #include "concommand.h" #include "gameutils.h" #include "hookutils.h" -#include "libcurl/include/curl/curl.h" #include "serverauthentication.h" #include "gameutils.h" #include "rapidjson/document.h" @@ -24,6 +23,9 @@ ConVar* Cvar_ns_server_name; ConVar* Cvar_ns_server_desc; ConVar* Cvar_ns_server_password; +// Source ConVar +ConVar* Cvar_hostname; + MasterServerManager* g_MasterServerManager; typedef void(*CHostState__State_NewGameType)(CHostState* hostState); @@ -38,6 +40,39 @@ CHostState__State_ChangeLevelSPType CHostState__State_ChangeLevelSP; typedef void(*CHostState__State_GameShutdownType)(CHostState* hostState); CHostState__State_GameShutdownType CHostState__State_GameShutdown; +const char* HttplibErrorToString(httplib::Error error) +{ + switch (error) + { + case httplib::Error::Success: + return "httplib::Error::Success"; + case httplib::Error::Unknown: + return "httplib::Error::Unknown"; + case httplib::Error::Connection: + return "httplib::Error::Connection"; + case httplib::Error::BindIPAddress: + return "httplib::Error::BindIPAddress"; + case httplib::Error::Read: + return "httplib::Error::Read"; + case httplib::Error::Write: + return "httplib::Error::Write"; + case httplib::Error::ExceedRedirectCount: + return "httplib::Error::ExceedRedirectCount"; + case httplib::Error::Canceled: + return "httplib::Error::Canceled"; + case httplib::Error::SSLConnection: + return "httplib::Error::SSLConnection"; + case httplib::Error::SSLLoadingCerts: + return "httplib::Error::SSLLoadingCerts"; + case httplib::Error::SSLServerVerification: + return "httplib::Error::SSLServerVerification"; + case httplib::Error::UnsupportedMultipartBoundaryChars: + return "httplib::Error::UnsupportedMultipartBoundaryChars"; + } + + return ""; +} + RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, bool newRequiresPassword) { // passworded servers don't have public ips @@ -59,6 +94,18 @@ RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const maxPlayers = newMaxPlayers; } +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_STDERR, stdout); + if (CommandLine()->FindParm("-msinsecure")) // TODO: this check doesn't seem to work + { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + } +} + void MasterServerManager::ClearServerList() { // this doesn't really do anything lol, probably isn't threadsafe @@ -90,12 +137,11 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or spdlog::info("Trying to authenticate with northstar masterserver for user {}", uidStr); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/origin_auth?id={}&token={}", Cvar_ns_masterserver_hostname->m_pszString, uidStr, tokenStr).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -105,12 +151,12 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or { m_successfullyConnected = true; - rapidjson::Document originAuthInfo; + rapidjson_document originAuthInfo; originAuthInfo.Parse(readBuffer.c_str()); if (originAuthInfo.HasParseError()) { - spdlog::error("Failed reading origin auth info response: encountered parse error \{}\"", rapidjson::GetParseError_En(originAuthInfo.GetParseError())); + spdlog::error("Failed reading origin auth info response: encountered parse error \"{}\"", rapidjson::GetParseError_En(originAuthInfo.GetParseError())); goto REQUEST_END_CLEANUP; } @@ -163,12 +209,11 @@ void MasterServerManager::RequestServerList() spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->m_pszString); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/servers", Cvar_ns_masterserver_hostname->m_pszString).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -178,7 +223,7 @@ void MasterServerManager::RequestServerList() { m_successfullyConnected = true; - rapidjson::Document serverInfoJson; + rapidjson_document serverInfoJson; serverInfoJson.Parse(readBuffer.c_str()); if (serverInfoJson.HasParseError()) @@ -200,7 +245,7 @@ void MasterServerManager::RequestServerList() goto REQUEST_END_CLEANUP; } - rapidjson::GenericArray<false, rapidjson::Value> serverArray = serverInfoJson.GetArray(); + rapidjson::GenericArray<false, rapidjson_document::GenericValue> serverArray = serverInfoJson.GetArray(); spdlog::info("Got {} servers", serverArray.Size()); @@ -273,7 +318,7 @@ void MasterServerManager::RequestServerList() std::sort(m_remoteServers.begin(), m_remoteServers.end(), [](RemoteServerInfo& a, RemoteServerInfo& b) { return a.playerCount > b.playerCount; - }); + }); } else { @@ -301,12 +346,11 @@ void MasterServerManager::RequestMainMenuPromos() Sleep(500); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/mainmenupromos", Cvar_ns_masterserver_hostname->m_pszString).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -316,7 +360,7 @@ void MasterServerManager::RequestMainMenuPromos() { m_successfullyConnected = true; - rapidjson::Document mainMenuPromoJson; + rapidjson_document mainMenuPromoJson; mainMenuPromoJson.Parse(readBuffer.c_str()); if (mainMenuPromoJson.HasParseError()) @@ -412,12 +456,11 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken std::thread requestThread([this, uidStr, tokenStr]() { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->m_pszString, uidStr, tokenStr).c_str()); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -427,7 +470,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken { m_successfullyConnected = true; - rapidjson::Document authInfoJson; + rapidjson_document authInfoJson; authInfoJson.Parse(readBuffer.c_str()); if (authInfoJson.HasParseError()) @@ -480,7 +523,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken goto REQUEST_END_CLEANUP; } - newAuthData.pdata[i++] = (char)byte.GetUint(); + newAuthData.pdata[i++] = static_cast<char>(byte.GetUint()); } std::lock_guard<std::mutex> guard(g_ServerAuthenticationManager->m_authDataMutex); @@ -538,11 +581,10 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c spdlog::info("Attempting authentication with server of id \"{}\"", serverIdStr); CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -560,7 +602,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c { m_successfullyConnected = true; - rapidjson::Document connectionInfoJson; + rapidjson_document connectionInfoJson; connectionInfoJson.Parse(readBuffer.c_str()); if (connectionInfoJson.HasParseError()) @@ -595,7 +637,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c } m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString()); - m_pendingConnectionInfo.port = connectionInfoJson["port"].GetInt(); + m_pendingConnectionInfo.port = (unsigned short)connectionInfoJson["port"].GetUint(); strncpy(m_pendingConnectionInfo.authToken, connectionInfoJson["authToken"].GetString(), 31); m_pendingConnectionInfo.authToken[31] = 0; @@ -644,45 +686,18 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name m_ownServerId[0] = 0; m_ownServerAuthToken[0] = 0; - // build modinfo obj - rapidjson::Document modinfoDoc; - modinfoDoc.SetObject(); - modinfoDoc.AddMember("Mods", rapidjson::Value(rapidjson::kArrayType), modinfoDoc.GetAllocator()); - - int currentModIndex = 0; - for (Mod& mod : g_ModManager->m_loadedMods) - { - if (!mod.Enabled || (!mod.RequiredOnClient && !mod.Pdiff.size())) - continue; - - modinfoDoc["Mods"].PushBack(rapidjson::Value(rapidjson::kObjectType), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Pdiff", rapidjson::StringRef(&mod.Pdiff[0]), modinfoDoc.GetAllocator()); - - currentModIndex++; - } - - rapidjson::StringBuffer buffer; - buffer.Clear(); - rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); - modinfoDoc.Accept(writer); - const char* modInfoString = buffer.GetString(); - CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); curl_mime* mime = curl_mime_init(curl); curl_mimepart* part = curl_mime_addpart(mime); - curl_mime_data(part, modInfoString, buffer.GetSize()); + curl_mime_data(part, m_ownModInfoJson.c_str(), m_ownModInfoJson.size()); curl_mime_name(part, "modinfo"); curl_mime_filename(part, "modinfo.json"); curl_mime_type(part, "application/json"); @@ -712,7 +727,7 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name { m_successfullyConnected = true; - rapidjson::Document serverAddedJson; + rapidjson_document serverAddedJson; serverAddedJson.Parse(readBuffer.c_str()); if (serverAddedJson.HasParseError()) @@ -760,16 +775,69 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name do { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/heartbeat?id={}&playerCount={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, g_ServerAuthenticationManager->m_additionalPlayerData.size()).c_str()); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); + + // send all registration info so we have all necessary info to reregister our server if masterserver goes down, without a restart + // this isn't threadsafe :terror: + { + char* escapedNameNew = curl_easy_escape(curl, Cvar_ns_server_name->m_pszString, NULL); + char* escapedDescNew = curl_easy_escape(curl, Cvar_ns_server_desc->m_pszString, NULL); + char* escapedMapNew = curl_easy_escape(curl, g_pHostState->m_levelName, NULL); + char* escapedPlaylistNew = curl_easy_escape(curl, GetCurrentPlaylistName(), NULL); + char* escapedPasswordNew = curl_easy_escape(curl, Cvar_ns_server_password->m_pszString, NULL); + + int maxPlayers = 6; + char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false); + if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this + maxPlayers = std::stoi(maxPlayersVar); + + curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/update_values?id={}&port={}&authPort={}&name={}&description={}&map={}&playlist={}&playerCount={}&maxPlayers={}&password={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, Cvar_hostport->m_nValue, Cvar_ns_player_auth_port->m_nValue, escapedNameNew, escapedDescNew, escapedMapNew, escapedPlaylistNew, g_ServerAuthenticationManager->m_additionalPlayerData.size(), maxPlayers, escapedPasswordNew).c_str()); + + curl_free(escapedNameNew); + curl_free(escapedDescNew); + curl_free(escapedMapNew); + curl_free(escapedPlaylistNew); + curl_free(escapedPasswordNew); + } + + curl_mime* mime = curl_mime_init(curl); + curl_mimepart* part = curl_mime_addpart(mime); + + curl_mime_data(part, m_ownModInfoJson.c_str(), m_ownModInfoJson.size()); + curl_mime_name(part, "modinfo"); + curl_mime_filename(part, "modinfo.json"); + curl_mime_type(part, "application/json"); + + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); CURLcode result = curl_easy_perform(curl); - if (result != CURLcode::CURLE_OK) + if (result == CURLcode::CURLE_OK) + { + rapidjson_document serverAddedJson; + serverAddedJson.Parse(readBuffer.c_str()); + + if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject()) + { + if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString()) + { + strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); + m_ownServerId[sizeof(m_ownServerId) - 1] = 0; + } + + if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString()) + { + strncpy(m_ownServerAuthToken, serverAddedJson["serverAuthToken"].GetString(), sizeof(m_ownServerAuthToken)); + m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0; + } + } + } + else spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result)); curl_easy_cleanup(curl); @@ -805,11 +873,10 @@ void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist, std::thread requestThread([this, strMap, strPlaylist, maxPlayers] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -846,11 +913,10 @@ void MasterServerManager::UpdateServerPlayerCount(int playerCount) std::thread requestThread([this, playerCount] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, playerCount).c_str()); @@ -884,12 +950,11 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata, std::thread requestThread([this, strPlayerId, strPdata, pdataSize] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/accounts/write_persistence?id={}&serverId={}", Cvar_ns_masterserver_hostname->m_pszString, strPlayerId, m_ownServerId).c_str()); curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); @@ -926,11 +991,10 @@ void MasterServerManager::RemoveSelfFromServerList() std::thread requestThread([this] { CURL* curl = curl_easy_init(); + SetCommonHttpClientOptions(curl); std::string readBuffer; curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId).c_str()); @@ -969,6 +1033,10 @@ void CHostState__State_NewGameHook(CHostState* hostState) if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this maxPlayers = std::stoi(maxPlayersVar); + // Copy new server name cvar to source + Cvar_hostname->m_pszString = Cvar_ns_server_name->m_pszString; + Cvar_hostname->m_StringLength = Cvar_ns_server_name->m_StringLength; + g_MasterServerManager->AddSelfToServerList(Cvar_hostport->m_nValue, Cvar_ns_player_auth_port->m_nValue, Cvar_ns_server_name->m_pszString, Cvar_ns_server_desc->m_pszString, hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers, Cvar_ns_server_password->m_pszString); g_ServerAuthenticationManager->StartPlayerAuthServer(); g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = false; @@ -1004,9 +1072,9 @@ void CHostState__State_GameShutdownHook(CHostState* hostState) CHostState__State_GameShutdown(hostState); } -MasterServerManager::MasterServerManager() +MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownServerId{ "" }, m_ownClientAuthToken{ "" } { - curl_global_init(CURL_GLOBAL_DEFAULT); + } void InitialiseSharedMasterServer(HMODULE baseAddress) @@ -1020,6 +1088,9 @@ void InitialiseSharedMasterServer(HMODULE baseAddress) Cvar_ns_server_password = RegisterConVar("ns_server_password", "", FCVAR_GAMEDLL, ""); Cvar_ns_report_server_to_masterserver = RegisterConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, ""); Cvar_ns_report_sp_server_to_masterserver = RegisterConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, ""); + + Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8); + g_MasterServerManager = new MasterServerManager; RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "", FCVAR_CLIENTDLL); @@ -1029,4 +1100,4 @@ void InitialiseSharedMasterServer(HMODULE baseAddress) ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x16E5D0, CHostState__State_ChangeLevelMPHook, reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelMP)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x16E520, CHostState__State_ChangeLevelSPHook, reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelSP)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x16E640, CHostState__State_GameShutdownHook, reinterpret_cast<LPVOID*>(&CHostState__State_GameShutdown)); -}
\ No newline at end of file +} diff --git a/NorthstarDedicatedTest/masterserver.h b/NorthstarDedicatedTest/masterserver.h index b4d0d476..d7071b6c 100644 --- a/NorthstarDedicatedTest/masterserver.h +++ b/NorthstarDedicatedTest/masterserver.h @@ -37,7 +37,7 @@ public: char authToken[32]; in_addr ip; - int port; + unsigned short port; }; struct MainMenuPromoData @@ -72,6 +72,8 @@ public: char m_ownServerAuthToken[33]; char m_ownClientAuthToken[33]; + std::string m_ownModInfoJson; + bool m_bOriginAuthWithMasterServerDone = false; bool m_bOriginAuthWithMasterServerInProgress = false; @@ -93,6 +95,9 @@ public: bool m_bHasMainMenuPromoData = false; MainMenuPromoData m_MainMenuPromoData; +private: + void SetCommonHttpClientOptions(CURL* curl); + public: MasterServerManager(); void ClearServerList(); diff --git a/NorthstarDedicatedTest/maxplayers.cpp b/NorthstarDedicatedTest/maxplayers.cpp new file mode 100644 index 00000000..9d08e83f --- /dev/null +++ b/NorthstarDedicatedTest/maxplayers.cpp @@ -0,0 +1,675 @@ +#include "pch.h" +#include "maxplayers.h" +#include "gameutils.h" + +// 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; + +template<class T> +void ChangeOffset(void* addr, unsigned int offset) +{ + TempReadWrite rw(addr); + *((T*)addr) = offset; +} + +/* +typedef bool(*MatchRecvPropsToSendProps_R_Type)(__int64 lookup, __int64 tableNameBroken, __int64 sendTable, __int64 recvTable); +MatchRecvPropsToSendProps_R_Type MatchRecvPropsToSendProps_R_Original; + +bool MatchRecvPropsToSendProps_R_Hook(__int64 lookup, __int64 tableNameBroken, __int64 sendTable, __int64 recvTable) +{ + const char* tableName = *(const char**)(sendTable + 0x118); + + spdlog::info("MatchRecvPropsToSendProps_R table name {}", tableName); + + bool orig = MatchRecvPropsToSendProps_R_Original(lookup, tableNameBroken, sendTable, recvTable); + return orig; +} + +typedef bool(*DataTable_SetupReceiveTableFromSendTable_Type)(__int64 sendTable, bool needsDecoder); +DataTable_SetupReceiveTableFromSendTable_Type DataTable_SetupReceiveTableFromSendTable_Original; + +bool DataTable_SetupReceiveTableFromSendTable_Hook(__int64 sendTable, bool needsDecoder) +{ + const char* tableName = *(const char**)(sendTable + 0x118); + + spdlog::info("DataTable_SetupReceiveTableFromSendTable table name {}", tableName); + if (!strcmp(tableName, "m_bConnected")) { + char f[64]; + sprintf_s(f, "%p", sendTable); + MessageBoxA(0, f, "DataTable_SetupReceiveTableFromSendTable", 0); + } + + return DataTable_SetupReceiveTableFromSendTable_Original(sendTable, needsDecoder); +} +*/ + +typedef void* (*StringTables_CreateStringTable_Type)(__int64 thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags); +StringTables_CreateStringTable_Type StringTables_CreateStringTable_Original; + +void* StringTables_CreateStringTable_Hook(__int64 thisptr, const char* name, int maxentries, int userdatafixedsize, int userdatanetworkbits, int flags) +{ + // 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_Original(thisptr, name, maxentries, userdatafixedsize, userdatanetworkbits, flags); +} + +bool MaxPlayersIncreaseEnabled() +{ + return CommandLine() && CommandLine()->CheckParm("-experimentalmaxplayersincrease"); +} + +void InitialiseMaxPlayersOverride_Engine(HMODULE baseAddress) +{ + if (!MaxPlayersIncreaseEnabled()) + return; + + // patch GetPlayerLimits to ignore the boundary limit + ChangeOffset<unsigned char>((char*)baseAddress + 0x116458, 0xEB); // jle => jmp + + // patch ED_Alloc to change nFirstIndex + ChangeOffset<int>((char*)baseAddress + 0x18F46C + 1, NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1) + + // patch CGameServer::SpawnServer to change GetMaxClients inline + ChangeOffset<int>((char*)baseAddress + 0x119543 + 2, NEW_MAX_PLAYERS + 8 + 1); // original: 41 (sv.GetMaxClients() + 1) + + // patch CGameServer::SpawnServer to change for loop + ChangeOffset<unsigned char>((char*)baseAddress + 0x11957F + 2, NEW_MAX_PLAYERS); // original: 32 + + // patch CGameServer::SpawnServer to change for loop (there are two) + ChangeOffset<unsigned char>((char*)baseAddress + 0x119586 + 2, NEW_MAX_PLAYERS + 1); // original: 33 (32 + 1) + + // patch max players somewhere in CClientState + ChangeOffset<unsigned char>((char*)baseAddress + 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<unsigned char>((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<unsigned char>((char*)baseAddress + 0x114C48 + 2, NEW_MAX_PLAYERS); // original: 32 + + // do not load prebaked SendTable message list + ChangeOffset<unsigned char>((char*)baseAddress + 0x75859, 0xEB); // jnz -> jmp + + HookEnabler hook; + + // ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x209000, &MatchRecvPropsToSendProps_R_Hook, reinterpret_cast<LPVOID*>(&MatchRecvPropsToSendProps_R_Original)); + // ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1FACD0, &DataTable_SetupReceiveTableFromSendTable_Hook, reinterpret_cast<LPVOID*>(&DataTable_SetupReceiveTableFromSendTable_Original)); + + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x22E220, &StringTables_CreateStringTable_Hook, reinterpret_cast<LPVOID*>(&StringTables_CreateStringTable_Original)); +} + +typedef void(*RunUserCmds_Type)(bool a1, float a2); +RunUserCmds_Type RunUserCmds_Original; + +HMODULE serverBase = 0; +auto RandomIntZeroMax = (__int64(__fastcall*)())0; + +// lazy rebuild +void RunUserCmds_Hook(bool a1, float a2) +{ + 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 + 32i64))(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 = 0i64; + } + ++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)); + } +} + +typedef __int64(*SendPropArray2_Type)(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1); +SendPropArray2_Type SendPropArray2_Original; + +__int64 __fastcall SendPropArray2_Hook(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn, unsigned char unk1) +{ + // Change the amount of elements to account for a bigger player amount + if (!strcmp(name, "\"player_array\"")) + elements = NEW_MAX_PLAYERS; + + return SendPropArray2_Original(recvProp, elements, flags, name, proxyFn, unk1); +} + +void InitialiseMaxPlayersOverride_Server(HMODULE baseAddress) +{ + if (!MaxPlayersIncreaseEnabled()) + return; + + // get required data + serverBase = GetModuleHandleA("server.dll"); + RandomIntZeroMax = (decltype(RandomIntZeroMax))(GetProcAddress(GetModuleHandleA("vstdlib.dll"), "RandomIntZeroMax")); + + // patch max players amount + ChangeOffset<unsigned char>((char*)baseAddress + 0x9A44D + 3, NEW_MAX_PLAYERS); // 0x20 (32) => 0x80 (128) + + // patch SpawnGlobalNonRewinding to change forced edict index + ChangeOffset<unsigned char>((char*)baseAddress + 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<unsigned int>((char*)baseAddress + 0x5C560A + 1, CPlayerResource_ModifiedSize); + + // DT_PlayerResource::m_iPing SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5059 + 2, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C50A8 + 2, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C50E2 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_iPing DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB94598, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB9459C, NEW_MAX_PLAYERS + 1); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB945C0, PlayerResource_Ping_Size); + + // DT_PlayerResource::m_iTeam SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5110 + 2, CPlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C519C + 2, CPlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C517E + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_iTeam DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB94600, CPlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94604, NEW_MAX_PLAYERS + 1); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94628, PlayerResource_Team_Size); + + // DT_PlayerResource::m_iPRHealth SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C51C0 + 2, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5204 + 2, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C523E + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_iPRHealth DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB94668, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB9466C, NEW_MAX_PLAYERS + 1); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94690, PlayerResource_PRHealth_Size); + + // DT_PlayerResource::m_bConnected SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C526C + 2, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C52B4 + 2, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C52EE + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_bConnected DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB946D0, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB946D4, NEW_MAX_PLAYERS + 1); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB946F8, PlayerResource_Connected_Size); + + // DT_PlayerResource::m_bAlive SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5321 + 2, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5364 + 2, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C539E + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_bAlive DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB94738, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB9473C, NEW_MAX_PLAYERS + 1); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94760, PlayerResource_Alive_Size); + + // DT_PlayerResource::m_boolStats SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C53CC + 2, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5414 + 2, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C544E + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_boolStats DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB947A0, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB947A4, NEW_MAX_PLAYERS + 1); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB947C8, PlayerResource_BoolStats_Size); + + // DT_PlayerResource::m_killStats SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C547C + 2, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C54E2 + 2, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C54FE + 4, PlayerResource_KillStats_Length); + + // DT_PlayerResource::m_killStats DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB94808, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB9480C, PlayerResource_KillStats_Length); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94830, PlayerResource_KillStats_Size); + + // DT_PlayerResource::m_scoreStats SendProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5528 + 2, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5576 + 2, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C5584 + 4, PlayerResource_ScoreStats_Length); + + // DT_PlayerResource::m_scoreStats DataMap + ChangeOffset<unsigned int>((char*)baseAddress + 0xB94870, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94874, PlayerResource_ScoreStats_Length); + ChangeOffset<unsigned short>((char*)baseAddress + 0xB94898, PlayerResource_ScoreStats_Size); + + // CPlayerResource::UpdatePlayerData - m_bConnected + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C66EE + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C672E + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + + // CPlayerResource::UpdatePlayerData - m_iPing + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C6394 + 4, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C63DB + 4, CPlayerResource_OriginalSize + PlayerResource_Ping_Start); + + // CPlayerResource::UpdatePlayerData - m_iTeam + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C63FD + 4, CPlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C6442 + 4, CPlayerResource_OriginalSize + PlayerResource_Team_Start); + + // CPlayerResource::UpdatePlayerData - m_iPRHealth + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C645B + 4, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C64A0 + 4, CPlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + + // CPlayerResource::UpdatePlayerData - m_bConnected + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C64AA + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C64F0 + 4, CPlayerResource_OriginalSize + PlayerResource_Connected_Start); + + // CPlayerResource::UpdatePlayerData - m_bAlive + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C650A + 4, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C654F + 4, CPlayerResource_OriginalSize + PlayerResource_Alive_Start); + + // CPlayerResource::UpdatePlayerData - m_boolStats + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C6557 + 4, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C65A5 + 4, CPlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + + // CPlayerResource::UpdatePlayerData - m_scoreStats + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C65C2 + 3, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C65E3 + 4, CPlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + + // CPlayerResource::UpdatePlayerData - m_killStats + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C6654 + 3, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x5C665B + 3, CPlayerResource_OriginalSize + PlayerResource_KillStats_Start); + + // GameLoop::RunUserCmds - rebuild + HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x483D10, &RunUserCmds_Hook, reinterpret_cast<LPVOID*>(&RunUserCmds_Original)); + + *(DWORD*)((char*)baseAddress + 0x14E7390) = 0; + auto DT_PlayerResource_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x5C4FE0); + 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<unsigned int>((char*)baseAddress + 0x23924A + 1, CTeam_ModifiedSize); + + // CTeam::CTeam - increase memset length to clean newly allocated data + ChangeOffset<unsigned int>((char*)baseAddress + 0x2395AE + 2, 256 + CTeam_AddedSize); + + // hook required to change the size of DT_Team::"player_array" + HookEnabler hook2; + ENABLER_CREATEHOOK(hook2, (char*)baseAddress + 0x12B130, &SendPropArray2_Hook, reinterpret_cast<LPVOID*>(&SendPropArray2_Original)); + hook2.~HookEnabler(); // force hook before calling construct function + + *(DWORD*)((char*)baseAddress + 0xC945A0) = 0; + auto DT_Team_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x238F50); + DT_Team_Construct(); +} + +typedef __int64(*RecvPropArray2_Type)(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn); +RecvPropArray2_Type RecvPropArray2_Original; + +__int64 __fastcall RecvPropArray2_Hook(__int64 recvProp, int elements, int flags, const char* name, __int64 proxyFn) +{ + // Change the amount of elements to account for a bigger player amount + if (!strcmp(name, "\"player_array\"")) + elements = NEW_MAX_PLAYERS; + + return RecvPropArray2_Original(recvProp, elements, flags, name, proxyFn); +} + +void InitialiseMaxPlayersOverride_Client(HMODULE baseAddress) +{ + if (!MaxPlayersIncreaseEnabled()) + return; + + 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<unsigned int>((char*)baseAddress + 0x164C41 + 1, C_PlayerResource_ModifiedSize); + + // C_PlayerResource::C_PlayerResource - change loop end value + ChangeOffset<unsigned char>((char*)baseAddress + 0x1640C4 + 2, NEW_MAX_PLAYERS - 32); + + // C_PlayerResource::C_PlayerResource - change m_szName address + ChangeOffset<unsigned int>((char*)baseAddress + 0x1640D0 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); // appended to the end of the class + + // C_PlayerResource::C_PlayerResource - change m_szName address + ChangeOffset<unsigned int>((char*)baseAddress + 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<unsigned int>((char*)baseAddress + 0x1640D0 + 3, 2244 + C_PlayerResource_AddedSize); + + // C_PlayerResource::UpdatePlayerName - change m_szName address + ChangeOffset<unsigned int>((char*)baseAddress + 0x16431F + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName - change m_szName address 1 + ChangeOffset<unsigned int>((char*)baseAddress + 0x1645B1 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName - change m_szName address 2 + ChangeOffset<unsigned int>((char*)baseAddress + 0x1645C0 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName - change m_szName address 3 + ChangeOffset<unsigned int>((char*)baseAddress + 0x1645DD + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName internal func - change m_szName address 1 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164B71 + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName internal func - change m_szName address 2 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164B9B + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 1 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164641 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 2 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164650 + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName2 (?) - change m_szName address 3 + ChangeOffset<unsigned int>((char*)baseAddress + 0x16466D + 3, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 1 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164BA3 + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 2 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164BCE + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::GetPlayerName internal func - change m_szName2 (?) address 3 + ChangeOffset<unsigned int>((char*)baseAddress + 0x164BE7 + 4, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + + // C_PlayerResource::m_szName + ChangeOffset<unsigned int>((char*)baseAddress + 0xc350f8, C_PlayerResource_OriginalSize + PlayerResource_Name_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc350f8 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource size + ChangeOffset<unsigned int>((char*)baseAddress + 0x163415 + 6, C_PlayerResource_ModifiedSize); + + // DT_PlayerResource::m_iPing RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x163492 + 2, C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1634D6 + 2, C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163515 + 5, NEW_MAX_PLAYERS + 1); + + // C_PlayerResource::m_iPing + ChangeOffset<unsigned int>((char*)baseAddress + 0xc35170, C_PlayerResource_OriginalSize + PlayerResource_Ping_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc35170 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_iTeam RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x163549 + 2, C_PlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1635C8 + 2, C_PlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1635AD + 5, NEW_MAX_PLAYERS + 1); + + // C_PlayerResource::m_iTeam + ChangeOffset<unsigned int>((char*)baseAddress + 0xc351e8, C_PlayerResource_OriginalSize + PlayerResource_Team_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc351e8 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_iPRHealth RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x1635F9 + 2, C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163625 + 2, C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163675 + 5, NEW_MAX_PLAYERS + 1); + + // C_PlayerResource::m_iPRHealth + ChangeOffset<unsigned int>((char*)baseAddress + 0xc35260, C_PlayerResource_OriginalSize + PlayerResource_PRHealth_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc35260 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_bConnected RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x1636A9 + 2, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1636D5 + 2, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163725 + 5, NEW_MAX_PLAYERS + 1); + + // C_PlayerResource::m_bConnected + ChangeOffset<unsigned int>((char*)baseAddress + 0xc352d8, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc352d8 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_bAlive RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x163759 + 2, C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163785 + 2, C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1637D5 + 5, NEW_MAX_PLAYERS + 1); + + // C_PlayerResource::m_bAlive + ChangeOffset<unsigned int>((char*)baseAddress + 0xc35350, C_PlayerResource_OriginalSize + PlayerResource_Alive_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc35350 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_boolStats RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x163809 + 2, C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163835 + 2, C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163885 + 5, NEW_MAX_PLAYERS + 1); + + // C_PlayerResource::m_boolStats + ChangeOffset<unsigned int>((char*)baseAddress + 0xc353c8, C_PlayerResource_OriginalSize + PlayerResource_BoolStats_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc353c8 + 4, NEW_MAX_PLAYERS + 1); + + // DT_PlayerResource::m_killStats RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x1638B3 + 2, C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1638E5 + 2, C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163935 + 5, PlayerResource_KillStats_Length); + + // C_PlayerResource::m_killStats + ChangeOffset<unsigned int>((char*)baseAddress + 0xc35440, C_PlayerResource_OriginalSize + PlayerResource_KillStats_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc35440 + 4, PlayerResource_KillStats_Length); + + // DT_PlayerResource::m_scoreStats RecvProp + ChangeOffset<unsigned int>((char*)baseAddress + 0x163969 + 2, C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x163995 + 2, C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned int>((char*)baseAddress + 0x1639E5 + 5, PlayerResource_ScoreStats_Length); + + // C_PlayerResource::m_scoreStats + ChangeOffset<unsigned int>((char*)baseAddress + 0xc354b8, C_PlayerResource_OriginalSize + PlayerResource_ScoreStats_Start); + ChangeOffset<unsigned short>((char*)baseAddress + 0xc354b8 + 4, PlayerResource_ScoreStats_Length); + + // C_PlayerResource::GetPlayerName - change m_bConnected address + ChangeOffset<unsigned int>((char*)baseAddress + 0x164599 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + + // C_PlayerResource::GetPlayerName2 (?) - change m_bConnected address + ChangeOffset<unsigned int>((char*)baseAddress + 0x164629 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + + // C_PlayerResource::GetPlayerName internal func - change m_bConnected address + ChangeOffset<unsigned int>((char*)baseAddress + 0x164B13 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + + // Some other get name func (that seems to be unused) - change m_bConnected address + ChangeOffset<unsigned int>((char*)baseAddress + 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<unsigned int>((char*)baseAddress + 0x164834 + 3, C_PlayerResource_OriginalSize + PlayerResource_Connected_Start); + + *(DWORD*)((char*)baseAddress + 0xC35068) = 0; + auto DT_PlayerResource_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x163400); + 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<unsigned int>((char*)baseAddress + 0x182321 + 1, C_Team_ModifiedSize); + + // C_Team::C_Team - increase memset length to clean newly allocated data + ChangeOffset<unsigned int>((char*)baseAddress + 0x1804A2 + 2, 256 + C_Team_AddedSize); + + // DT_Team size + ChangeOffset<unsigned int>((char*)baseAddress + 0xC3AA0C, C_Team_ModifiedSize); + + // hook required to change the size of DT_Team::"player_array" + HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1CEDA0, &RecvPropArray2_Hook, reinterpret_cast<LPVOID*>(&RecvPropArray2_Original)); + hook.~HookEnabler(); // force hook before calling construct function + + *(DWORD*)((char*)baseAddress + 0xC3AFF8) = 0; + auto DT_Team_Construct = (__int64(__fastcall*)())((char*)baseAddress + 0x17F950); + DT_Team_Construct(); +}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/maxplayers.h b/NorthstarDedicatedTest/maxplayers.h new file mode 100644 index 00000000..cd191fc8 --- /dev/null +++ b/NorthstarDedicatedTest/maxplayers.h @@ -0,0 +1,4 @@ +#pragma once +void InitialiseMaxPlayersOverride_Engine(HMODULE baseAddress); +void InitialiseMaxPlayersOverride_Server(HMODULE baseAddress); +void InitialiseMaxPlayersOverride_Client(HMODULE baseAddress);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/memalloc.cpp b/NorthstarDedicatedTest/memalloc.cpp index 113f56b9..1b9eaae8 100644 --- a/NorthstarDedicatedTest/memalloc.cpp +++ b/NorthstarDedicatedTest/memalloc.cpp @@ -2,39 +2,76 @@ #include "memalloc.h" #include "gameutils.h" -// so for anyone reading this code, you may be curious why the fuck i'm overriding new to alloc into a static 100k buffer -// pretty much, the issue here is that we need to use the game's memory allocator (g_pMemAllocSingleton) or risk heap corruptions, but this allocator is defined in tier0 -// as such, it doesn't exist when we inject -// initially i wanted to just call malloc and free until g_pMemAllocSingleton was initialised, but the issue then becomes that we might try to -// call g_pMemAllocSingleton->Free on memory that was allocated with malloc, which will cause game to crash -// so, the best idea i had for this was to just alloc 100k of memory, have all pre-tier0 allocations use that -// (from what i can tell we hit about 12k before tier0 is loaded atm in debug builds, so it's more than enough) -// then just use the game's allocator after that -// yes, this means we leak 100k of memory, idk how else to do this without breaking stuff +// TODO: rename to malloc and free after removing statically compiled .libs -const int STATIC_ALLOC_SIZE = 100000; // alot more than we need, could reduce to 50k or even 25k later potentially - -size_t g_iStaticAllocated = 0; -char pStaticAllocBuf[STATIC_ALLOC_SIZE]; - -void* operator new(size_t n) +extern "C" void* _malloc_base(size_t n) { // allocate into static buffer if g_pMemAllocSingleton isn't initialised - if (g_pMemAllocSingleton) - return g_pMemAllocSingleton->m_vtable->Alloc(g_pMemAllocSingleton, n); - else + if (!g_pMemAllocSingleton) { - void* ret = pStaticAllocBuf + g_iStaticAllocated; - g_iStaticAllocated += n; - return ret; - } + InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); + } + return g_pMemAllocSingleton->m_vtable->Alloc(g_pMemAllocSingleton, n); } -void operator delete(void* p) +/*extern "C" void* malloc(size_t n) { - // 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; + return _malloc_base(n); +}*/ +extern "C" void _free_base(void* p) +{ + if (!g_pMemAllocSingleton) + { + spdlog::warn("Trying to free something before g_pMemAllocSingleton was ready, this should never happen"); + InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); + } g_pMemAllocSingleton->m_vtable->Free(g_pMemAllocSingleton, p); -}
\ No newline at end of file +} + + +extern "C" void* _realloc_base(void* oldPtr, size_t size) +{ + if (!g_pMemAllocSingleton) + { + InitialiseTier0GameUtilFunctions(GetModuleHandleA("tier0.dll")); + } + return g_pMemAllocSingleton->m_vtable->Realloc(g_pMemAllocSingleton, oldPtr, size); +} + +extern "C" void* _calloc_base(size_t n, size_t size) +{ + size_t bytes = n * size; + void* memory = _malloc_base(bytes); + if (memory) + { + memset(memory, 0, bytes); + } + return memory; +} + +extern "C" char* _strdup_base(const char* src) +{ + char* str; + char* p; + int len = 0; + + while (src[len]) + len++; + str = reinterpret_cast<char*>(_malloc_base(len + 1)); + p = str; + while (*src) + *p++ = *src++; + *p = '\0'; + return str; +} + +void* operator new(size_t n) +{ + return _malloc_base(n); +} + +void operator delete(void* p) +{ + _free_base(p); +}// /FORCE:MULTIPLE
\ No newline at end of file diff --git a/NorthstarDedicatedTest/memalloc.h b/NorthstarDedicatedTest/memalloc.h index fe3c5255..92ab9672 100644 --- a/NorthstarDedicatedTest/memalloc.h +++ b/NorthstarDedicatedTest/memalloc.h @@ -1,6 +1,42 @@ #pragma once -extern size_t g_iStaticAllocated; +#include "include/rapidjson/document.h" +//#include "include/rapidjson/allocators.h" + +extern "C" void* _malloc_base(size_t size); +extern "C" void* _calloc_base(size_t const count, size_t const size); +extern "C" void* _realloc_base(void* block, size_t size); +extern "C" void* _recalloc_base(void* const block, size_t const count, size_t const size); +extern "C" void _free_base(void* const block); +extern "C" char* _strdup_base(const char* src); void* operator new(size_t n); -void operator delete(void* p);
\ No newline at end of file +void operator delete(void* p); + +//void* malloc(size_t n); + +class SourceAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return _malloc_base(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + _free_base(originalPtr); + return NULL; + } + return _realloc_base(originalPtr, newSize); + } + static void Free(void* ptr) { _free_base(ptr); } +}; + +typedef rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<SourceAllocator>, SourceAllocator> rapidjson_document; +//typedef rapidjson::GenericDocument<rapidjson::UTF8<>, SourceAllocator, SourceAllocator> rapidjson_document; +//typedef rapidjson::Document rapidjson_document; +//using MyDocument = rapidjson::GenericDocument<rapidjson::UTF8<>, MemoryAllocator>; +//using rapidjson_document = rapidjson::GenericDocument<rapidjson::UTF8<>, SourceAllocator, SourceAllocator>; diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 4f4a2da0..c5f1bf49 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -2,7 +2,7 @@ #include "modmanager.h" #include "convar.h" #include "concommand.h" - +#include "masterserver.h" #include "rapidjson/error/en.h" #include "rapidjson/document.h" #include "rapidjson/ostreamwrapper.h" @@ -22,7 +22,7 @@ Mod::Mod(fs::path modDir, char* jsonBuf) ModDirectory = modDir; - rapidjson::Document modJson; + rapidjson_document modJson; modJson.Parse<rapidjson::ParseFlag::kParseCommentsFlag | rapidjson::ParseFlag::kParseTrailingCommasFlag>(jsonBuf); // fail if parse error @@ -309,7 +309,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.insert(std::make_pair(std::hash<std::string>{}(kvStr), kvStr)); + mod.KeyValues.emplace(std::hash<std::string>{}(kvStr), kvStr); } } } @@ -333,7 +333,7 @@ void ModManager::LoadMods() } // in a seperate loop because we register mod files in reverse order, since mods loaded later should have their files prioritised - for (int i = m_loadedMods.size() - 1; i > -1; i--) + for (int64_t i = m_loadedMods.size() - 1; i > -1; i--) { if (!m_loadedMods[i].Enabled) continue; @@ -355,6 +355,32 @@ void ModManager::LoadMods() } } + // build modinfo obj for masterserver + rapidjson_document modinfoDoc; + modinfoDoc.SetObject(); + modinfoDoc.AddMember("Mods", rapidjson_document::GenericValue(rapidjson::kArrayType), modinfoDoc.GetAllocator()); + + int currentModIndex = 0; + for (Mod& mod : m_loadedMods) + { + if (!mod.Enabled || (!mod.RequiredOnClient && !mod.Pdiff.size())) + continue; + + modinfoDoc["Mods"].PushBack(rapidjson_document::GenericValue(rapidjson::kObjectType), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Pdiff", rapidjson::StringRef(&mod.Pdiff[0]), modinfoDoc.GetAllocator()); + + currentModIndex++; + } + + rapidjson::StringBuffer buffer; + buffer.Clear(); + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + modinfoDoc.Accept(writer); + g_MasterServerManager->m_ownModInfoJson = std::string(buffer.GetString()); + m_hasLoadedMods = true; } @@ -379,7 +405,7 @@ void ModManager::UnloadMods() // should we be doing this here or should scripts be doing this manually? // main issue with doing this here is when we reload mods for connecting to a server, we write enabled mods, which isn't necessarily what we wanna do if (!m_enabledModsCfg.HasMember(mod.Name.c_str())) - m_enabledModsCfg.AddMember(rapidjson::StringRef(mod.Name.c_str()), rapidjson::Value(false), m_enabledModsCfg.GetAllocator()); + m_enabledModsCfg.AddMember(rapidjson_document::StringRefType(mod.Name.c_str()), rapidjson_document::GenericValue(false), m_enabledModsCfg.GetAllocator()); m_enabledModsCfg[mod.Name.c_str()].SetBool(mod.Enabled); } diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h index 5f2f6441..20cb0a42 100644 --- a/NorthstarDedicatedTest/modmanager.h +++ b/NorthstarDedicatedTest/modmanager.h @@ -4,6 +4,7 @@ #include <vector> #include <filesystem> #include "rapidjson/document.h" +#include "memalloc.h" namespace fs = std::filesystem; @@ -100,7 +101,7 @@ class ModManager private: bool m_hasLoadedMods = false; bool m_hasEnabledModsCfg; - rapidjson::Document m_enabledModsCfg; + rapidjson_document m_enabledModsCfg; // precalculated hashes size_t m_hScriptsRsonHash; diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h index 9ac5b8a9..a07d1401 100644 --- a/NorthstarDedicatedTest/pch.h +++ b/NorthstarDedicatedTest/pch.h @@ -11,10 +11,12 @@ // httplib ssl // add headers that you want to pre-compile here +#include "memalloc.h" #include <Windows.h> #include "logging.h" #include "include/MinHook.h" #include "spdlog/spdlog.h" +#include "libcurl/include/curl/curl.h" #include "hookutils.h" #endif
\ No newline at end of file diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp index 59a13a26..9cbd0c05 100644 --- a/NorthstarDedicatedTest/playlist.cpp +++ b/NorthstarDedicatedTest/playlist.cpp @@ -95,7 +95,7 @@ void InitialisePlaylistHooks(HMODULE baseAddress) { void* ptr = (char*)baseAddress + 0x18ED8D; TempReadWrite rw(ptr); - *((char*)ptr) = 0xC3; // jmp => ret + *((char*)ptr) = (char)0xC3; // jmp => ret } if (IsDedicated()) diff --git a/NorthstarDedicatedTest/rpakfilesystem.cpp b/NorthstarDedicatedTest/rpakfilesystem.cpp index 006a57c5..5dfb2386 100644 --- a/NorthstarDedicatedTest/rpakfilesystem.cpp +++ b/NorthstarDedicatedTest/rpakfilesystem.cpp @@ -6,8 +6,8 @@ typedef void*(*LoadCommonPaksForMapType)(char* map); LoadCommonPaksForMapType LoadCommonPaksForMap; -typedef void*(*LoadPakSyncType)(char* path, void* unknownSingleton, int flags); -typedef void*(*LoadPakAsyncType)(char* path, void* unknownSingleton, int flags, void* callback0, void* callback1); +typedef void*(*LoadPakSyncType)(const char* path, void* unknownSingleton, int flags); +typedef void*(*LoadPakAsyncType)(const char* path, void* unknownSingleton, int flags, void* callback0, void* callback1); // there are more i'm just too lazy to add struct PakLoadFuncs @@ -20,7 +20,7 @@ struct PakLoadFuncs PakLoadFuncs* g_pakLoadApi; void** pUnknownPakLoadSingleton; -void LoadPakAsync(char* path) +void LoadPakAsync(const char* path) { g_pakLoadApi->LoadPakAsync(path, *pUnknownPakLoadSingleton, 2, nullptr, nullptr); } diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp index 1093ff7f..da84280c 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -168,7 +168,7 @@ bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid, // get file length pdataStream.seekg(0, pdataStream.end); - int length = pdataStream.tellg(); + auto length = pdataStream.tellg(); pdataStream.seekg(0, pdataStream.beg); // copy pdata into buffer |