diff options
Diffstat (limited to 'NorthstarDedicatedTest')
-rw-r--r-- | NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj | 17 | ||||
-rw-r--r-- | NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters | 19 | ||||
-rw-r--r-- | NorthstarDedicatedTest/bansystem.cpp | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/dedicated.cpp | 7 | ||||
-rw-r--r-- | NorthstarDedicatedTest/dllmain.cpp | 6 | ||||
-rw-r--r-- | NorthstarDedicatedTest/gameutils.cpp | 9 | ||||
-rw-r--r-- | NorthstarDedicatedTest/gameutils.h | 17 | ||||
-rw-r--r-- | NorthstarDedicatedTest/languagehooks.cpp | 4 | ||||
-rw-r--r-- | NorthstarDedicatedTest/latencyflex.cpp | 47 | ||||
-rw-r--r-- | NorthstarDedicatedTest/latencyflex.h | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/logging.cpp | 39 | ||||
-rw-r--r-- | NorthstarDedicatedTest/logging.h | 1 | ||||
-rw-r--r-- | NorthstarDedicatedTest/masterserver.cpp | 112 | ||||
-rw-r--r-- | NorthstarDedicatedTest/masterserver.h | 2 | ||||
-rw-r--r-- | NorthstarDedicatedTest/miscserverfixes.cpp | 15 | ||||
-rw-r--r-- | NorthstarDedicatedTest/modmanager.cpp | 28 | ||||
-rw-r--r-- | NorthstarDedicatedTest/rpakfilesystem.cpp | 6 | ||||
-rw-r--r-- | NorthstarDedicatedTest/serverauthentication.cpp | 40 |
18 files changed, 293 insertions, 80 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj index c6f0c8c0..93b35e5c 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|x64"> @@ -61,10 +61,6 @@ <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> - <BufferSecurityCheck> - </BufferSecurityCheck> - <ExceptionHandling> - </ExceptionHandling> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -93,10 +89,6 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> - <BufferSecurityCheck> - </BufferSecurityCheck> - <ExceptionHandling> - </ExceptionHandling> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -527,6 +519,7 @@ <ClInclude Include="include\spdlog\version.h" /> <ClInclude Include="keyvalues.h" /> <ClInclude Include="languagehooks.h" /> + <ClInclude Include="latencyflex.h" /> <ClInclude Include="logging.h" /> <ClInclude Include="main.h" /> <ClInclude Include="masterserver.h" /> @@ -570,6 +563,7 @@ <ClCompile Include="hooks.cpp" /> <ClCompile Include="hookutils.cpp" /> <ClCompile Include="keyvalues.cpp" /> + <ClCompile Include="latencyflex.cpp" /> <ClCompile Include="maxplayers.cpp" /> <ClCompile Include="languagehooks.cpp" /> <ClCompile Include="memalloc.cpp" /> @@ -631,7 +625,10 @@ <None Include="include\spdlog\fmt\bundled\LICENSE.rst" /> </ItemGroup> <ItemGroup> - <MASM Include="audio_asm.asm" /> + <MASM Include="audio_asm.asm" /> + </ItemGroup> + <ItemGroup> + <None Include="..\Northstar-Legal.txt" CopyToOutputDirectory="PreserveNewest" /> </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters index 41072c60..777f2396 100644 --- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters +++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> <Filter Include="Source Files"> @@ -1434,6 +1434,9 @@ <ClInclude Include="languagehooks.h"> <Filter>Header Files\Client</Filter> </ClInclude> + <ClInclude Include="latencyflex.h"> + <Filter>Header Files\Client</Filter> + </ClInclude> <ClInclude Include="audio.h"> <Filter>Header Files\Client</Filter> </ClInclude> @@ -1559,11 +1562,19 @@ <ClCompile Include="languagehooks.cpp"> <Filter>Source Files\Client</Filter> </ClCompile> + <ClCompile Include="latencyflex.cpp"> + <Filter>Source Files\Client</Filter> + </ClCompile> <ClCompile Include="audio.cpp"> <Filter>Source Files\Client</Filter> </ClCompile> </ItemGroup> <ItemGroup> + <MASM Include="audio_asm.asm"> + <Filter>Source Files\Client</Filter> + </MASM> + </ItemGroup> + <ItemGroup> <None Include="include\spdlog\fmt\bundled\LICENSE.rst"> <Filter>Header Files\include\spdlog\fmt\bundled</Filter> </None> @@ -1648,10 +1659,6 @@ <None Include="include\crypto\dso_conf.h.in"> <Filter>Header Files\include\openssl\crypto</Filter> </None> - </ItemGroup> - <ItemGroup> - <MASM Include="audio_asm.asm"> - <Filter>Source Files\Client</Filter> - </MASM> + <None Include="..\Northstar-Legal.txt" /> </ItemGroup> </Project>
\ No newline at end of file diff --git a/NorthstarDedicatedTest/bansystem.cpp b/NorthstarDedicatedTest/bansystem.cpp index 610c20e4..a4d994d0 100644 --- a/NorthstarDedicatedTest/bansystem.cpp +++ b/NorthstarDedicatedTest/bansystem.cpp @@ -71,7 +71,7 @@ void BanPlayerCommand(const CCommand& args) { void* player = GetPlayerByIndex(i); - if (!strcmp((char*)player + 0x16, args.Arg(1)) || strcmp((char*)player + 0xF500, args.Arg(1))) + if (!strcmp((char*)player + 0x16, args.Arg(1)) || !strcmp((char*)player + 0xF500, args.Arg(1))) { g_ServerBanSystem->BanUID(strtoll((char*)player + 0xF500, nullptr, 10)); CBaseClient__Disconnect(player, 1, "Banned from server"); diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp index 1359a33e..641fb42e 100644 --- a/NorthstarDedicatedTest/dedicated.cpp +++ b/NorthstarDedicatedTest/dedicated.cpp @@ -50,6 +50,11 @@ void RunServer(CDedicatedExports* dedicated) // run initial 2 ticks, 1 to initialise engine and 1 to load initial map g_pEngine->Frame(); + + // run server autoexec + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); + g_pEngine->Frame(); // to fix a bug: set current playlist again, otherwise max_players will be set wrong @@ -406,7 +411,7 @@ void InitialiseDedicated(HMODULE engineAddress) CommandLine()->AppendParm("-windowed", 0); CommandLine()->AppendParm("+host_preload_shaders", "0"); CommandLine()->AppendParm("+net_usesocketsforloopback", "1"); - CommandLine()->AppendParm("+exec", "autoexec_ns_server"); + //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")) diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp index fd82e382..db422467 100644 --- a/NorthstarDedicatedTest/dllmain.cpp +++ b/NorthstarDedicatedTest/dllmain.cpp @@ -21,6 +21,7 @@ #include "securitypatches.h" #include "miscserverscript.h" #include "clientauthhooks.h" +#include "latencyflex.h" #include "scriptbrowserhooks.h" #include "scriptmainmenupromos.h" #include "miscclientfixes.h" @@ -73,12 +74,12 @@ bool InitialiseNorthstar() initialised = true; + SetEnvironmentVariableA("OPENSSL_ia32cap", "~0x200000200000000"); curl_global_init_mem(CURL_GLOBAL_DEFAULT, _malloc_base, _free_base, _realloc_base, _strdup_base, _calloc_base); InitialiseLogging(); - - // apply initial hooks InstallInitialHooks(); + CreateLogFiles(); InitialiseInterfaceCreationHooks(); AddDllLoadCallback("tier0.dll", InitialiseTier0GameUtilFunctions); @@ -110,6 +111,7 @@ bool InitialiseNorthstar() AddDllLoadCallback("client.dll", InitialiseScriptServerBrowser); AddDllLoadCallback("localize.dll", InitialiseModLocalisation); AddDllLoadCallback("engine.dll", InitialiseClientAuthHooks); + AddDllLoadCallback("client.dll", InitialiseLatencyFleX); AddDllLoadCallback("engine.dll", InitialiseScriptExternalBrowserHooks); AddDllLoadCallback("client.dll", InitialiseScriptMainMenuPromos); AddDllLoadCallback("client.dll", InitialiseMiscClientFixes); diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp index 1cbd8648..f9fd4d95 100644 --- a/NorthstarDedicatedTest/gameutils.cpp +++ b/NorthstarDedicatedTest/gameutils.cpp @@ -18,9 +18,11 @@ CHostState* g_pHostState; // cengine stuff CEngine* g_pEngine; +server_state_t* sv_m_State; // network stuff ConVar* Cvar_hostport; +ConVar* Cvar_net_datablock_enabled; // playlist stuff GetCurrentPlaylistType GetCurrentPlaylistName; @@ -45,6 +47,8 @@ ConVar* Cvar_communities_hostname; ErrorType Error; CommandLineType CommandLine; Plat_FloatTimeType Plat_FloatTime; +ThreadInServerFrameThreadType ThreadInServerFrameThread; +GetBaseLocalClientType GetBaseLocalClient; void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) { @@ -54,8 +58,10 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) g_pHostState = (CHostState*)((char*)baseAddress + 0x7CF180); g_pEngine = *(CEngine**)((char*)baseAddress + 0x7D70C8); + sv_m_State = (server_state_t*)((char*)baseAddress + 0x12A53D48); Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070); + Cvar_net_datablock_enabled = (ConVar*)((char*)baseAddress + 0x12A4F6D0); GetCurrentPlaylistName = (GetCurrentPlaylistType)((char*)baseAddress + 0x18C640); SetCurrentPlaylist = (SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20); @@ -67,6 +73,8 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress) Cvar_match_defaultMap = (ConVar*)((char*)baseAddress + 0x8AB530); Cvar_communities_hostname = (ConVar*)((char*)baseAddress + 0x13157E50); + + GetBaseLocalClient = (GetBaseLocalClientType)((char*)baseAddress + 0x78200); } void InitialiseServerGameUtilFunctions(HMODULE baseAddress) @@ -106,4 +114,5 @@ void InitialiseTier0GameUtilFunctions(HMODULE baseAddress) Error = reinterpret_cast<ErrorType>(GetProcAddress(baseAddress, "Error")); CommandLine = reinterpret_cast<CommandLineType>(GetProcAddress(baseAddress, "CommandLine")); Plat_FloatTime = reinterpret_cast<Plat_FloatTimeType>(GetProcAddress(baseAddress, "Plat_FloatTime")); + ThreadInServerFrameThread = reinterpret_cast<ThreadInServerFrameThreadType>(GetProcAddress(baseAddress, "ThreadInServerFrameThread")); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h index 43f387d1..48ad75e4 100644 --- a/NorthstarDedicatedTest/gameutils.h +++ b/NorthstarDedicatedTest/gameutils.h @@ -193,8 +193,19 @@ public: extern CEngine* g_pEngine; +enum server_state_t +{ + ss_dead = 0, // Dead + ss_loading, // Spawning + ss_active, // Running + ss_paused, // Running, but paused +}; + +extern server_state_t* sv_m_State; + // network stuff extern ConVar* Cvar_hostport; +extern ConVar* Cvar_net_datablock_enabled; // playlist stuff typedef const char*(*GetCurrentPlaylistType)(); @@ -234,6 +245,12 @@ extern CommandLineType CommandLine; typedef double(*Plat_FloatTimeType)(); extern Plat_FloatTimeType Plat_FloatTime; +typedef bool(*ThreadInServerFrameThreadType)(); +extern ThreadInServerFrameThreadType ThreadInServerFrameThread; + +typedef void*(*GetBaseLocalClientType)(); +extern GetBaseLocalClientType GetBaseLocalClient; + void InitialiseEngineGameUtilFunctions(HMODULE baseAddress); void InitialiseServerGameUtilFunctions(HMODULE baseAddress); void InitialiseTier0GameUtilFunctions(HMODULE baseAddress);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/languagehooks.cpp b/NorthstarDedicatedTest/languagehooks.cpp index 08bacaf9..0929b73d 100644 --- a/NorthstarDedicatedTest/languagehooks.cpp +++ b/NorthstarDedicatedTest/languagehooks.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "languagehooks.h" #include "gameutils.h" +#include "dedicated.h" #include <filesystem> #include <regex> @@ -104,6 +105,9 @@ char* GetGameLanguageHook() void InitialiseTier0LanguageHooks(HMODULE baseAddress) { + if (IsDedicated()) + return; + HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0xF560, &GetGameLanguageHook, reinterpret_cast<LPVOID*>(&GetGameLanguageOriginal)); }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp new file mode 100644 index 00000000..84b79d33 --- /dev/null +++ b/NorthstarDedicatedTest/latencyflex.cpp @@ -0,0 +1,47 @@ +#include "pch.h" +#include "latencyflex.h" +#include "hookutils.h" +#include "dedicated.h" +#include "convar.h" + +typedef void(*OnRenderStartType)(); +OnRenderStartType OnRenderStart; + +ConVar* Cvar_r_latencyflex; + +HMODULE m_lfxModule{}; +typedef void (*PFN_winelfx_WaitAndBeginFrame)(); +PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{}; + +void OnRenderStartHook() +{ + if (Cvar_r_latencyflex->m_nValue) + m_winelfx_WaitAndBeginFrame(); + + OnRenderStart(); +} + +void InitialiseLatencyFleX(HMODULE baseAddress) +{ + if (IsDedicated()) + return; + + // Connect to the LatencyFleX service + // LatencyFleX is an open source vendor agnostic replacement for Nvidia Reflex input latency reduction technology. + // https://ishitatsuyuki.github.io/post/latencyflex/ + m_lfxModule = LoadLibraryA("latencyflex_wine.dll"); + + if (m_lfxModule == nullptr) + { + spdlog::info("Unable to load LatencyFleX library, LatencyFleX disabled."); + return; + } + + m_winelfx_WaitAndBeginFrame = reinterpret_cast<PFN_winelfx_WaitAndBeginFrame>(reinterpret_cast<void*>(GetProcAddress(m_lfxModule,"winelfx_WaitAndBeginFrame"))); + spdlog::info("LatencyFleX initialized."); + + Cvar_r_latencyflex = RegisterConVar("r_latencyflex", "1", FCVAR_ARCHIVE, "Whether or not to use LatencyFleX input latency reduction."); + + HookEnabler hook; + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1952C0, &OnRenderStartHook, reinterpret_cast<LPVOID*>(&OnRenderStart)); +}
\ No newline at end of file diff --git a/NorthstarDedicatedTest/latencyflex.h b/NorthstarDedicatedTest/latencyflex.h new file mode 100644 index 00000000..db801261 --- /dev/null +++ b/NorthstarDedicatedTest/latencyflex.h @@ -0,0 +1,2 @@ +#pragma once +void InitialiseLatencyFleX(HMODULE baseAddress);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp index a7e522dc..235bfe2e 100644 --- a/NorthstarDedicatedTest/logging.cpp +++ b/NorthstarDedicatedTest/logging.cpp @@ -10,6 +10,27 @@ #include <Psapi.h> #include <minidumpapiset.h> + +// This needs to be called after hooks are loaded so we can access the command line args +void CreateLogFiles() +{ + if (strstr(GetCommandLineA(), "-disablelogs")) + { + spdlog::default_logger()->set_level(spdlog::level::off); + } + else + { + // 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%Y-%m-%d %H-%M-%S.txt"); + spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); + spdlog::flush_on(spdlog::level::info); + } +} + long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) { static bool logged = false; @@ -154,7 +175,7 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) time_t time = std::time(nullptr); tm currentTime = *std::localtime(&time); std::stringstream stream; - stream << std::put_time(¤tTime, "R2Northstar/logs/nsdump%d-%m-%Y %H-%M-%S.dmp"); + stream << std::put_time(¤tTime, "R2Northstar/logs/nsdump%Y-%m-%d %H-%M-%S.dmp"); auto hMinidumpFile = CreateFileA(stream.str().c_str(), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hMinidumpFile) @@ -171,8 +192,7 @@ long __stdcall ExceptionFilter(EXCEPTION_POINTERS* exceptionInfo) spdlog::error("Failed to write minidump file {}!", stream.str()); if (!IsDedicated()) - MessageBoxA(0, "Northstar has crashed! A crash log and dump can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); - + MessageBoxA(0, "Northstar has crashed! Crash info can be found in R2Northstar/logs", "Northstar has crashed!", MB_ICONERROR | MB_OK); } logged = true; @@ -187,20 +207,7 @@ 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 - // 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"); - - // create logger - spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false)); } ConVar* Cvar_spewlog_enable; diff --git a/NorthstarDedicatedTest/logging.h b/NorthstarDedicatedTest/logging.h index 5a4522b4..da13e46f 100644 --- a/NorthstarDedicatedTest/logging.h +++ b/NorthstarDedicatedTest/logging.h @@ -1,5 +1,6 @@ #pragma once #include "context.h" +void CreateLogFiles(); void InitialiseLogging(); void InitialiseEngineSpewFuncHooks(HMODULE baseAddress);
\ No newline at end of file diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp index 74bd9af5..c040a239 100644 --- a/NorthstarDedicatedTest/masterserver.cpp +++ b/NorthstarDedicatedTest/masterserver.cpp @@ -686,32 +686,6 @@ 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_document::GenericValue(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_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); - const char* modInfoString = buffer.GetString(); - CURL* curl = curl_easy_init(); SetCommonHttpClientOptions(curl); @@ -723,7 +697,7 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name 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"); @@ -787,8 +761,8 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name goto REQUEST_END_CLEANUP; } - strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); - m_ownServerId[sizeof(m_ownServerId) - 1] = 0; + strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId)); + m_ownServerId[sizeof(m_ownServerId) - 1] = 0; strncpy(m_ownServerAuthToken, serverAddedJson["serverAuthToken"].GetString(), sizeof(m_ownServerAuthToken)); m_ownServerAuthToken[sizeof(m_ownServerAuthToken) - 1] = 0; @@ -805,13 +779,65 @@ void MasterServerManager::AddSelfToServerList(int port, int authPort, char* name 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_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); @@ -1000,6 +1026,14 @@ void CHostState__State_NewGameHook(CHostState* hostState) if (g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame) SetCurrentPlaylist("tdm"); + // net_data_block_enabled is required for sp, force it if we're on an sp map + // sucks for security but just how it be + if (!strncmp(g_pHostState->m_levelName, "sp_", 3)) + { + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "net_data_block_enabled 1", cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); + } + CHostState__State_NewGame(hostState); int maxPlayers = 6; @@ -1023,12 +1057,24 @@ void CHostState__State_ChangeLevelMPHook(CHostState* hostState) if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this maxPlayers = std::stoi(maxPlayersVar); + // net_data_block_enabled is required for sp, force it if we're on an sp map + // sucks for security but just how it be + if (!strncmp(g_pHostState->m_levelName, "sp_", 3)) + { + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "net_data_block_enabled 1", cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); + } + g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers); CHostState__State_ChangeLevelMP(hostState); } void CHostState__State_ChangeLevelSPHook(CHostState* hostState) { + // is this even called? genuinely i don't think so + // from what i can tell, it's not called on mp=>sp change or sp=>sp change + // so idk it's fucked + int maxPlayers = 6; char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false); if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this @@ -1071,7 +1117,7 @@ void InitialiseSharedMasterServer(HMODULE baseAddress) HookEnabler hook; ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x16E7D0, CHostState__State_NewGameHook, reinterpret_cast<LPVOID*>(&CHostState__State_NewGame)); - 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 + 0x16E520, CHostState__State_ChangeLevelMPHook, reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelMP)); + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x16E5D0, CHostState__State_ChangeLevelSPHook, reinterpret_cast<LPVOID*>(&CHostState__State_ChangeLevelSP)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x16E640, CHostState__State_GameShutdownHook, reinterpret_cast<LPVOID*>(&CHostState__State_GameShutdown)); } diff --git a/NorthstarDedicatedTest/masterserver.h b/NorthstarDedicatedTest/masterserver.h index d3c83052..d7071b6c 100644 --- a/NorthstarDedicatedTest/masterserver.h +++ b/NorthstarDedicatedTest/masterserver.h @@ -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; diff --git a/NorthstarDedicatedTest/miscserverfixes.cpp b/NorthstarDedicatedTest/miscserverfixes.cpp index 2007a30c..5db9ebbd 100644 --- a/NorthstarDedicatedTest/miscserverfixes.cpp +++ b/NorthstarDedicatedTest/miscserverfixes.cpp @@ -6,8 +6,19 @@ void InitialiseMiscServerFixes(HMODULE baseAddress) { // ret at the start of the concommand GenerateObjFile as it can crash servers { - void* ptr = (char*)baseAddress + 0x38D920; + char* ptr = reinterpret_cast<char*>(baseAddress) + 0x38D920; TempReadWrite rw(ptr); - *((char*)ptr) = (char)0xC3; + *ptr = 0xC3; + } + + // nop out call to VGUI shutdown since it crashes the game when quitting from the console + { + char* ptr = reinterpret_cast<char*>(baseAddress) + 0x154A96; + TempReadWrite rw(ptr); + *(ptr++) = 0x90; // nop + *(ptr++) = 0x90; // nop + *(ptr++) = 0x90; // nop + *(ptr++) = 0x90; // nop + *ptr = 0x90; // nop } }
\ No newline at end of file diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp index 34bba6af..2e287dcd 100644 --- a/NorthstarDedicatedTest/modmanager.cpp +++ b/NorthstarDedicatedTest/modmanager.cpp @@ -3,7 +3,7 @@ #include "convar.h" #include "concommand.h" #include "audio.h" - +#include "masterserver.h" #include "rapidjson/error/en.h" #include "rapidjson/document.h" #include "rapidjson/ostreamwrapper.h" @@ -372,6 +372,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; } 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 da84280c..8cbf666e 100644 --- a/NorthstarDedicatedTest/serverauthentication.cpp +++ b/NorthstarDedicatedTest/serverauthentication.cpp @@ -7,6 +7,8 @@ #include "gameutils.h" #include "bansystem.h" #include "miscserverscript.h" +#include "concommand.h" +#include "dedicated.h" #include <fstream> #include <filesystem> #include <thread> @@ -18,7 +20,7 @@ const char* AUTHSERVER_VERIFY_STRING = "I am a northstar server!"; typedef void*(*CBaseServer__ConnectClientType)(void* server, void* a2, void* a3, uint32_t a4, uint32_t a5, int32_t a6, void* a7, void* a8, char* serverFilter, void* a10, char a11, void* a12, char a13, char a14, int64_t uid, uint32_t a16, uint32_t a17); CBaseServer__ConnectClientType CBaseServer__ConnectClient; -typedef char(*CBaseClient__ConnectType)(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, int a7); +typedef bool(*CBaseClient__ConnectType)(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7); CBaseClient__ConnectType CBaseClient__Connect; typedef void(*CBaseClient__ActivatePlayerType)(void* self); @@ -41,6 +43,8 @@ ProcessConnectionlessPacketType ProcessConnectionlessPacket; typedef void(*CServerGameDLL__OnRecievedSayTextMessageType)(void* self, unsigned int senderClientIndex, const char* message, char unknown); CServerGameDLL__OnRecievedSayTextMessageType CServerGameDLL__OnRecievedSayTextMessage; +typedef void(*ConCommand__DispatchType)(ConCommand* command, const CCommand& args, void* a3); +ConCommand__DispatchType ConCommand__Dispatch; // global vars ServerAuthenticationManager* g_ServerAuthenticationManager; @@ -237,11 +241,14 @@ void* CBaseServer__ConnectClientHook(void* server, void* a2, void* a3, uint32_t return CBaseServer__ConnectClient(server, a2, a3, a4, a5, a6, a7, a8, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17); } -char CBaseClient__ConnectHook(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, int a7) +bool CBaseClient__ConnectHook(void* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7) { // try to auth player, dc if it fails // we connect irregardless of auth, because returning bad from this function can fuck client state p bad - char ret = CBaseClient__Connect(self, name, netchan_ptr_arg, b_fake_player_arg, a5, Buffer, a7); + bool ret = CBaseClient__Connect(self, name, netchan_ptr_arg, b_fake_player_arg, a5, Buffer, a7); + + if (!ret) + return ret; if (!g_ServerBanSystem->IsUIDAllowed(nextPlayerUid)) { @@ -310,8 +317,13 @@ void CBaseClient__DisconnectHook(void* self, uint32_t unknownButAlways1, const c } // maybe this should be done outside of auth code, but effort to refactor rn and it sorta fits +// hack: store the client that's executing the current stringcmd for pCommand->Dispatch() hook later +void* pExecutingGameClient = 0; + char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const char* pCommandString) { + pExecutingGameClient = self; + if (CVar_sv_quota_stringcmdspersecond->m_nValue != -1) { // note: this isn't super perfect, legit clients can trigger it in lobby, mostly good enough tho imo @@ -336,13 +348,30 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c return CGameClient__ExecuteStringCommand(self, unknown, pCommandString); } +void ConCommand__DispatchHook(ConCommand* command, const CCommand& args, void* a3) +{ + // patch to ensure FCVAR_GAMEDLL concommands without FCVAR_CLIENTCMD_CAN_EXECUTE can't be executed by remote clients + if (*sv_m_State == server_state_t::ss_active && !command->IsFlagSet(FCVAR_CLIENTCMD_CAN_EXECUTE) && !!pExecutingGameClient) + { + if (IsDedicated()) + return; + + // hack because don't currently have a way to check GetBaseLocalClient().m_nPlayerSlot + if (strcmp((char*)pExecutingGameClient + 0xF500, g_LocalPlayerUserID)) + return; + } + + ConCommand__Dispatch(command, args, a3); + pExecutingGameClient = 0; +} + char __fastcall CNetChan___ProcessMessagesHook(void* self, void* buf) { double startTime = Plat_FloatTime(); char ret = CNetChan___ProcessMessages(self, buf); // check processing limits, unless we're in a level transition - if (g_pHostState->m_iCurrentState == HostState_t::HS_RUN) + if (g_pHostState->m_iCurrentState == HostState_t::HS_RUN && ThreadInServerFrameThread()) { // player that sent the message void* sender = *(void**)((char*)self + 368); @@ -388,7 +417,7 @@ void CBaseClient__SendServerInfoHook(void* self) bool ProcessConnectionlessPacketHook(void* a1, netpacket_t* packet) { - if (packet->adr.type == NA_IP) + if (packet->adr.type == NA_IP && (!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->m_nValue) || !Cvar_net_datablock_enabled->m_nValue)) { // bad lookup: optimise later tm UnconnectedPlayerSendData* sendData = nullptr; @@ -478,6 +507,7 @@ void InitialiseServerAuthentication(HMODULE baseAddress) ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x2140A0, &CNetChan___ProcessMessagesHook, reinterpret_cast<LPVOID*>(&CNetChan___ProcessMessages)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x104FB0, &CBaseClient__SendServerInfoHook, reinterpret_cast<LPVOID*>(&CBaseClient__SendServerInfo)); ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x117800, &ProcessConnectionlessPacketHook, reinterpret_cast<LPVOID*>(&ProcessConnectionlessPacket)); + ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417440, &ConCommand__DispatchHook, reinterpret_cast<LPVOID*>(&ConCommand__Dispatch)); // patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token { |