aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDedicatedTest
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDedicatedTest')
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj17
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters19
-rw-r--r--NorthstarDedicatedTest/bansystem.cpp2
-rw-r--r--NorthstarDedicatedTest/dedicated.cpp7
-rw-r--r--NorthstarDedicatedTest/dllmain.cpp6
-rw-r--r--NorthstarDedicatedTest/gameutils.cpp9
-rw-r--r--NorthstarDedicatedTest/gameutils.h17
-rw-r--r--NorthstarDedicatedTest/languagehooks.cpp4
-rw-r--r--NorthstarDedicatedTest/latencyflex.cpp47
-rw-r--r--NorthstarDedicatedTest/latencyflex.h2
-rw-r--r--NorthstarDedicatedTest/logging.cpp39
-rw-r--r--NorthstarDedicatedTest/logging.h1
-rw-r--r--NorthstarDedicatedTest/masterserver.cpp112
-rw-r--r--NorthstarDedicatedTest/masterserver.h2
-rw-r--r--NorthstarDedicatedTest/miscserverfixes.cpp15
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp28
-rw-r--r--NorthstarDedicatedTest/rpakfilesystem.cpp6
-rw-r--r--NorthstarDedicatedTest/serverauthentication.cpp40
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(&currentTime, "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(&currentTime, "R2Northstar/logs/nsdump%d-%m-%Y %H-%M-%S.dmp");
+ stream << std::put_time(&currentTime, "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(&currentTime, "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
{