aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobTheBob9 <for.oliver.kirkham@gmail.com>2022-07-18 00:00:39 +0100
committerBobTheBob9 <for.oliver.kirkham@gmail.com>2022-07-18 00:00:39 +0100
commitb0bef05111a95a4cce6250d2b79e2aa5baa6dd98 (patch)
tree5bdc39717bebfc437adf8683405d58d97e1f43b2
parent6ae30c9b15fcc200c7b642016e7adbfdf9b979f4 (diff)
downloadNorthstarLauncher-b0bef05111a95a4cce6250d2b79e2aa5baa6dd98.tar.gz
NorthstarLauncher-b0bef05111a95a4cce6250d2b79e2aa5baa6dd98.zip
use modular ServerPresence system for registering servers
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj3
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj.filters9
-rw-r--r--NorthstarDLL/bansystem.cpp18
-rw-r--r--NorthstarDLL/bansystem.h2
-rw-r--r--NorthstarDLL/dedicated.cpp42
-rw-r--r--NorthstarDLL/exploitfixes.cpp2
-rw-r--r--NorthstarDLL/hooks.cpp79
-rw-r--r--NorthstarDLL/hooks.h7
-rw-r--r--NorthstarDLL/hoststate.cpp81
-rw-r--r--NorthstarDLL/limits.cpp14
-rw-r--r--NorthstarDLL/limits.h10
-rw-r--r--NorthstarDLL/localchatwriter.cpp5
-rw-r--r--NorthstarDLL/logging.cpp4
-rw-r--r--NorthstarDLL/masterserver.cpp752
-rw-r--r--NorthstarDLL/masterserver.h30
-rw-r--r--NorthstarDLL/miscserverscript.cpp4
-rw-r--r--NorthstarDLL/modmanager.cpp2
-rw-r--r--NorthstarDLL/playlist.cpp17
-rw-r--r--NorthstarDLL/plugins.cpp3
-rw-r--r--NorthstarDLL/printcommands.cpp4
-rw-r--r--NorthstarDLL/r2engine.cpp8
-rw-r--r--NorthstarDLL/r2engine.h54
-rw-r--r--NorthstarDLL/r2server.cpp10
-rw-r--r--NorthstarDLL/r2server.h51
-rw-r--r--NorthstarDLL/rpakfilesystem.cpp3
-rw-r--r--NorthstarDLL/runframe.cpp18
-rw-r--r--NorthstarDLL/serverauthentication.cpp111
-rw-r--r--NorthstarDLL/serverauthentication.h16
-rw-r--r--NorthstarDLL/serverchathooks.cpp7
-rw-r--r--NorthstarDLL/serverpresence.cpp207
-rw-r--r--NorthstarDLL/serverpresence.h88
-rw-r--r--NorthstarDLL/squirrel.cpp23
32 files changed, 912 insertions, 772 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj
index 43e1062a..fc3cddf4 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj
+++ b/NorthstarDLL/NorthstarDLL.vcxproj
@@ -549,6 +549,7 @@
<ClInclude Include="rpakfilesystem.h" />
<ClInclude Include="scriptsrson.h" />
<ClInclude Include="serverauthentication.h" />
+ <ClInclude Include="serverpresence.h" />
<ClInclude Include="sigscanning.h" />
<ClInclude Include="sourceconsole.h" />
<ClInclude Include="sourceinterface.h" />
@@ -609,6 +610,7 @@
<ClCompile Include="r2engine.cpp" />
<ClCompile Include="r2server.cpp" />
<ClCompile Include="rpakfilesystem.cpp" />
+ <ClCompile Include="runframe.cpp" />
<ClCompile Include="scriptbrowserhooks.cpp" />
<ClCompile Include="scriptmainmenupromos.cpp" />
<ClCompile Include="scriptmodmenu.cpp" />
@@ -618,6 +620,7 @@
<ClCompile Include="miscserverscript.cpp" />
<ClCompile Include="serverchathooks.cpp" />
<ClCompile Include="scriptservertoclientstringcommand.cpp" />
+ <ClCompile Include="serverpresence.cpp" />
<ClCompile Include="sigscanning.cpp" />
<ClCompile Include="sourceconsole.cpp" />
<ClCompile Include="sourceinterface.cpp" />
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters
index 514800c1..c0ae1b24 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj.filters
+++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters
@@ -1476,6 +1476,9 @@
<ClInclude Include="NSMem.h">
<Filter>Header Files\Hooks</Filter>
</ClInclude>
+ <ClInclude Include="serverpresence.h">
+ <Filter>Header Files\Server</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -1667,6 +1670,12 @@
<ClCompile Include="logging.cpp">
<Filter>Source Files\Console</Filter>
</ClCompile>
+ <ClCompile Include="serverpresence.cpp">
+ <Filter>Source Files\Server</Filter>
+ </ClCompile>
+ <ClCompile Include="runframe.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDLL/bansystem.cpp b/NorthstarDLL/bansystem.cpp
index d1b58102..29e07376 100644
--- a/NorthstarDLL/bansystem.cpp
+++ b/NorthstarDLL/bansystem.cpp
@@ -11,7 +11,7 @@
const char* BANLIST_PATH_SUFFIX = "/banlist.txt";
-ServerBanSystem* g_pServerBanSystem;
+ServerBanSystem* g_pBanSystem;
void ServerBanSystem::OpenBanlist()
{
@@ -72,11 +72,11 @@ void ConCommand_ban(const CCommand& args)
// assuming maxplayers 32
for (int i = 0; i < 32; i++)
{
- R2::CBasePlayer* player = R2::UTIL_PlayerByIndex(i);
+ R2::CBaseClient* player = &R2::g_pClientArray[i];
if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1)))
{
- g_pServerBanSystem->BanUID(strtoll((char*)player + 0xF500, nullptr, 10));
+ g_pBanSystem->BanUID(strtoll((char*)player + 0xF500, nullptr, 10));
R2::CBaseClient__Disconnect(player, 1, "Banned from server");
break;
}
@@ -89,20 +89,20 @@ void ConCommand_unban(const CCommand& args)
return;
// assumedly the player being unbanned here wasn't already connected, so don't need to iterate over players or anything
- g_pServerBanSystem->UnbanUID(strtoll(args.Arg(1), nullptr, 10));
+ g_pBanSystem->UnbanUID(strtoll(args.Arg(1), nullptr, 10));
}
void ConCommand_clearbanlist(const CCommand& args)
{
- g_pServerBanSystem->ClearBanlist();
+ g_pBanSystem->ClearBanlist();
}
ON_DLL_LOAD_RELIESON("engine.dll", BanSystem, ConCommand, (HMODULE baseAddress))
{
- g_pServerBanSystem = new ServerBanSystem;
- g_pServerBanSystem->OpenBanlist();
+ g_pBanSystem = new ServerBanSystem;
+ g_pBanSystem->OpenBanlist();
RegisterConCommand("ban", ConCommand_ban, "bans a given player by uid or name", FCVAR_GAMEDLL);
- RegisterConCommand("unban", ConCommand_unban, "unbans a given player by uid", FCVAR_NONE);
- RegisterConCommand("clearbanlist", ConCommand_clearbanlist, "clears all uids on the banlist", FCVAR_NONE);
+ RegisterConCommand("unban", ConCommand_unban, "unbans a given player by uid", FCVAR_GAMEDLL);
+ RegisterConCommand("clearbanlist", ConCommand_clearbanlist, "clears all uids on the banlist", FCVAR_GAMEDLL);
} \ No newline at end of file
diff --git a/NorthstarDLL/bansystem.h b/NorthstarDLL/bansystem.h
index 0d21aaa5..ccae7de2 100644
--- a/NorthstarDLL/bansystem.h
+++ b/NorthstarDLL/bansystem.h
@@ -15,4 +15,4 @@ class ServerBanSystem
bool IsUIDAllowed(uint64_t uid);
};
-extern ServerBanSystem* g_pServerBanSystem;
+extern ServerBanSystem* g_pBanSystem;
diff --git a/NorthstarDLL/dedicated.cpp b/NorthstarDLL/dedicated.cpp
index cbaed442..20e6121f 100644
--- a/NorthstarDLL/dedicated.cpp
+++ b/NorthstarDLL/dedicated.cpp
@@ -7,6 +7,7 @@
#include "serverauthentication.h"
#include "masterserver.h"
#include "printcommand.h"
+#include "NSMem.h"
AUTOHOOK_INIT()
@@ -69,26 +70,6 @@ void RunServer(CDedicatedExports* dedicated)
double frameStart = Tier0::Plat_FloatTime();
g_pEngine->Frame();
- // only update the title after at least 500ms since the last update
- if ((frameStart - frameTitle) > 0.5)
- {
- frameTitle = frameStart;
-
- // this way of getting playercount/maxplayers honestly really sucks, but not got any other methods of doing it rn
- const char* maxPlayers = GetCurrentPlaylistVar("max_players", true);
- if (!maxPlayers)
- maxPlayers = "6";
-
- SetConsoleTitleA(fmt::format(
- "{} - {} {}/{} players ({})",
- g_pMasterServerManager->m_sUnicodeServerName,
- g_pHostState->m_levelName,
- g_pServerAuthentication->m_PlayerAuthenticationData.size(),
- maxPlayers,
- GetCurrentPlaylistName())
- .c_str());
- }
-
std::this_thread::sleep_for(std::chrono::duration<double, std::ratio<1>>(
Cvar_base_tickinterval_mp->GetFloat() - fmin(Tier0::Plat_FloatTime() - frameStart, 0.25)));
}
@@ -127,8 +108,21 @@ DWORD WINAPI ConsoleInputThread(PVOID pThreadParameter)
return 0;
}
-#include "NSMem.h"
-ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, (HMODULE engineAddress))
+// use server presence to update window title
+class DedicatedConsoleServerPresence : public ServerPresenceReporter
+{
+ void ReportPresence(const ServerPresence* pServerPresence) override
+ {
+ SetConsoleTitleA(fmt::format("{} - {} {}/{} players ({})",
+ pServerPresence->m_sServerName,
+ pServerPresence->m_MapName,
+ pServerPresence->m_iPlayerCount,
+ pServerPresence->m_iMaxPlayers,
+ pServerPresence->m_PlaylistName).c_str());
+ }
+};
+
+ON_DLL_LOAD_DEDI_RELIESON("engine.dll", DedicatedServer, ServerPresence, (HMODULE engineAddress))
{
spdlog::info("InitialiseDedicated");
@@ -240,6 +234,10 @@ ON_DLL_LOAD_DEDI("engine.dll", DedicatedServer, (HMODULE engineAddress))
Tier0::CommandLine()->AppendParm("-nomessagebox", 0);
Tier0::CommandLine()->AppendParm("+host_preload_shaders", "0");
Tier0::CommandLine()->AppendParm("+net_usesocketsforloopback", "1");
+
+ // use presence reporter for console title
+ DedicatedConsoleServerPresence* presenceReporter = new DedicatedConsoleServerPresence;
+ g_pServerPresence->AddPresenceReporter(presenceReporter);
// Disable Quick Edit mode to reduce chance of user unintentionally hanging their server by selecting something.
if (!Tier0::CommandLine()->CheckParm("-bringbackquickedit"))
diff --git a/NorthstarDLL/exploitfixes.cpp b/NorthstarDLL/exploitfixes.cpp
index fbe91e3b..7c00726b 100644
--- a/NorthstarDLL/exploitfixes.cpp
+++ b/NorthstarDLL/exploitfixes.cpp
@@ -331,7 +331,7 @@ bool, __fastcall, (const char* pModName)) // 48 83 EC 28 48 8B 0D ? ? ? ? 48 8D
// ratelimit stringcmds, and prevent remote clients from calling non-FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS commands
bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource);
AUTOHOOK(CGameClient__ExecuteStringCommand,
-engine.dll + 0x1022E0, bool, , (R2::CBasePlayer* self, uint32_t unknown, const char* pCommandString))
+engine.dll + 0x1022E0, bool, , (R2::CBaseClient* self, uint32_t unknown, const char* pCommandString))
{
if (!g_pServerLimits->CheckStringCommandLimits(self))
{
diff --git a/NorthstarDLL/hooks.cpp b/NorthstarDLL/hooks.cpp
index 3c5438cd..bb696cdb 100644
--- a/NorthstarDLL/hooks.cpp
+++ b/NorthstarDLL/hooks.cpp
@@ -18,23 +18,49 @@ AUTOHOOK_INIT()
__dllLoadCallback::__dllLoadCallback(
eDllLoadCallbackSide side, const std::string dllName, DllLoadCallbackFuncType callback, std::string uniqueStr, std::string reliesOn)
{
+ // parse reliesOn array from string
+ std::vector<std::string> reliesOnArray;
+
+ if (reliesOn.length() && reliesOn[0] != '(')
+ {
+ reliesOnArray.push_back(reliesOn);
+ }
+ else
+ {
+ // follows the format (tag, tag, tag)
+ std::string sCurrentTag;
+ for (int i = 1; i < reliesOn.length(); i++)
+ {
+ if (!isspace(reliesOn[i]))
+ {
+ if (reliesOn[i] == ',' || reliesOn[i] == ')')
+ {
+ reliesOnArray.push_back(sCurrentTag);
+ sCurrentTag = "";
+ }
+ else
+ sCurrentTag += reliesOn[i];
+ }
+ }
+ }
+
switch (side)
{
case eDllLoadCallbackSide::UNSIDED:
{
- AddDllLoadCallback(dllName, callback, uniqueStr, reliesOn);
+ AddDllLoadCallback(dllName, callback, uniqueStr, reliesOnArray);
break;
}
case eDllLoadCallbackSide::CLIENT:
{
- AddDllLoadCallbackForClient(dllName, callback, uniqueStr, reliesOn);
+ AddDllLoadCallbackForClient(dllName, callback, uniqueStr, reliesOnArray);
break;
}
case eDllLoadCallbackSide::DEDICATED_SERVER:
{
- AddDllLoadCallbackForDedicatedServer(dllName, callback, uniqueStr, reliesOn);
+ AddDllLoadCallbackForDedicatedServer(dllName, callback, uniqueStr, reliesOnArray);
break;
}
}
@@ -100,7 +126,7 @@ struct DllLoadCallback
std::string dll;
DllLoadCallbackFuncType callback;
std::string tag;
- std::string reliesOn;
+ std::vector<std::string> reliesOn;
bool called;
};
@@ -112,7 +138,7 @@ std::vector<DllLoadCallback>& GetDllLoadCallbacks()
return vec;
}
-void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn)
+void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::vector<std::string> reliesOn)
{
DllLoadCallback& callbackStruct = GetDllLoadCallbacks().emplace_back();
@@ -124,7 +150,7 @@ void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::
}
void AddDllLoadCallbackForDedicatedServer(
- std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn)
+ std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::vector<std::string> reliesOn)
{
if (!IsDedicatedServer())
return;
@@ -132,7 +158,7 @@ void AddDllLoadCallbackForDedicatedServer(
AddDllLoadCallback(dll, callback, tag, reliesOn);
}
-void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::string reliesOn)
+void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback, std::string tag, std::vector<std::string> reliesOn)
{
if (IsDedicatedServer())
return;
@@ -233,13 +259,25 @@ void CallLoadLibraryACallbacks(LPCSTR lpLibFileName, HMODULE moduleAddress)
{
if (!callbackStruct.called && fs::path(lpLibFileName).filename() == fs::path(callbackStruct.dll).filename())
{
- if (callbackStruct.reliesOn != "" &&
- std::find(calledTags.begin(), calledTags.end(), callbackStruct.reliesOn) == calledTags.end())
+ bool bShouldContinue = false;
+
+ if (!callbackStruct.reliesOn.empty())
{
- bDoneCalling = false;
- continue;
+ for (std::string tag : callbackStruct.reliesOn)
+ {
+ if (std::find(calledTags.begin(), calledTags.end(), tag) == calledTags.end())
+ {
+ bDoneCalling = false;
+ bShouldContinue = true;
+ break;
+ }
+ }
}
+
+ if (bShouldContinue)
+ continue;
+
callbackStruct.callback(moduleAddress);
calledTags.push_back(callbackStruct.tag);
callbackStruct.called = true;
@@ -261,13 +299,24 @@ void CallLoadLibraryWCallbacks(LPCWSTR lpLibFileName, HMODULE moduleAddress)
{
if (!callbackStruct.called && fs::path(lpLibFileName).filename() == fs::path(callbackStruct.dll).filename())
{
- if (callbackStruct.reliesOn != "" &&
- std::find(calledTags.begin(), calledTags.end(), callbackStruct.reliesOn) == calledTags.end())
+ bool bShouldContinue = false;
+
+ if (!callbackStruct.reliesOn.empty())
{
- bDoneCalling = false;
- continue;
+ for (std::string tag : callbackStruct.reliesOn)
+ {
+ if (std::find(calledTags.begin(), calledTags.end(), tag) == calledTags.end())
+ {
+ bDoneCalling = false;
+ bShouldContinue = true;
+ break;
+ }
+ }
}
+ if (bShouldContinue)
+ continue;
+
callbackStruct.callback(moduleAddress);
calledTags.push_back(callbackStruct.tag);
callbackStruct.called = true;
diff --git a/NorthstarDLL/hooks.h b/NorthstarDLL/hooks.h
index 7b5c7a67..9e33cbbe 100644
--- a/NorthstarDLL/hooks.h
+++ b/NorthstarDLL/hooks.h
@@ -5,10 +5,11 @@
void InstallInitialHooks();
typedef void (*DllLoadCallbackFuncType)(HMODULE moduleAddress);
-void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::string reliesOn = "");
+void AddDllLoadCallback(std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::vector<std::string> reliesOn = {});
void AddDllLoadCallbackForDedicatedServer(
- std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::string reliesOn = "");
-void AddDllLoadCallbackForClient(std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::string reliesOn = "");
+ std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::vector<std::string> reliesOn = {});
+void AddDllLoadCallbackForClient(
+ std::string dll, DllLoadCallbackFuncType callback, std::string tag = "", std::vector<std::string> reliesOn = {});
void CallAllPendingDLLLoadCallbacks();
diff --git a/NorthstarDLL/hoststate.cpp b/NorthstarDLL/hoststate.cpp
index aabfbe7b..39d05ced 100644
--- a/NorthstarDLL/hoststate.cpp
+++ b/NorthstarDLL/hoststate.cpp
@@ -2,6 +2,7 @@
#include "hoststate.h"
#include "masterserver.h"
#include "serverauthentication.h"
+#include "serverpresence.h"
#include "playlist.h"
#include "tier0.h"
#include "r2engine.h"
@@ -16,8 +17,18 @@ namespace R2
CHostState* g_pHostState;
} // namespace R2
+ConVar* Cvar_hostport;
+
+void ServerStartingOrChangingMap()
+{
+ // 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))
+ g_pCVar->FindVar("net_data_block_enabled")->SetValue(true);
+}
+
AUTOHOOK(CHostState__State_NewGame, engine.dll + 0x16E7D0,
-void,, (CHostState* hostState))
+void,, (CHostState* self))
{
spdlog::info("HostState: NewGame");
@@ -28,67 +39,58 @@ void,, (CHostState* hostState))
if (g_pServerAuthentication->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))
- g_pCVar->FindVar("net_data_block_enabled")->SetValue(true);
+ ServerStartingOrChangingMap();
double dStartTime = Tier0::Plat_FloatTime();
- CHostState__State_NewGame(hostState);
+ CHostState__State_NewGame(self);
spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime);
- int maxPlayers = 6;
- const char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
- if (maxPlayersVar) // GetCurrentPlaylistVar can return null so protect against this
- maxPlayers = std::stoi(maxPlayersVar);
-
- // Copy new server name cvar to source
- Cvar_hostname->SetValue(Cvar_ns_server_name->GetString());
-
- g_pMasterServerManager->AddSelfToServerList(
- Cvar_hostport->GetInt(),
- g_pServerAuthentication->Cvar_ns_player_auth_port->GetInt(),
- Cvar_ns_server_name->GetString(),
- Cvar_ns_server_desc->GetString(),
- hostState->m_levelName,
- GetCurrentPlaylistName(),
- maxPlayers,
- Cvar_ns_server_password->GetString());
+ // setup server presence
+ g_pServerPresence->CreatePresence();
+ g_pServerPresence->SetMap(g_pHostState->m_levelName);
+ g_pServerPresence->SetPlaylist(GetCurrentPlaylistName());
+ g_pServerPresence->SetPort(Cvar_hostport->GetInt());
+
g_pServerAuthentication->StartPlayerAuthServer();
g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false;
}
AUTOHOOK(CHostState__State_ChangeLevelMP, engine.dll + 0x16E520,
-void,, (CHostState* hostState))
+void,, (CHostState* self))
{
spdlog::info("HostState: ChangeLevelMP");
- int maxPlayers = 6;
- const char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
- 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))
- g_pCVar->FindVar("net_data_block_enabled")->SetValue(true);
-
- g_pMasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, GetCurrentPlaylistName(), maxPlayers);
+ ServerStartingOrChangingMap();
double dStartTime = Tier0::Plat_FloatTime();
- CHostState__State_ChangeLevelMP(hostState);
+ CHostState__State_ChangeLevelMP(self);
spdlog::info("loading took {}s", Tier0::Plat_FloatTime() - dStartTime);
+
+ g_pServerPresence->SetMap(g_pHostState->m_levelName);
}
AUTOHOOK(CHostState__State_GameShutdown, engine.dll + 0x16E520,
-void,, (CHostState* hostState))
+void,, (CHostState* self))
{
spdlog::info("HostState: GameShutdown");
- g_pMasterServerManager->RemoveSelfFromServerList();
+ g_pServerPresence->DestroyPresence();
g_pServerAuthentication->StopPlayerAuthServer();
- CHostState__State_GameShutdown(hostState);
+ CHostState__State_GameShutdown(self);
+}
+
+AUTOHOOK(CHostState__FrameUpdate, engine.dll + 0x16DB00,
+void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime))
+{
+ CHostState__FrameUpdate(self, flCurrentTime, flFrameTime);
+
+ if (*R2::g_pServerState == R2::server_state_t::ss_active)
+ {
+ // update server presence
+ g_pServerPresence->RunFrame(flCurrentTime);
+ }
+
}
ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (HMODULE baseAddress))
@@ -96,4 +98,5 @@ ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (HMODULE baseAddress))
AUTOHOOK_DISPATCH()
g_pHostState = (CHostState*)((char*)baseAddress + 0x7CF180);
+ Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070);
} \ No newline at end of file
diff --git a/NorthstarDLL/limits.cpp b/NorthstarDLL/limits.cpp
index 051ed8f8..60c04e16 100644
--- a/NorthstarDLL/limits.cpp
+++ b/NorthstarDLL/limits.cpp
@@ -12,13 +12,13 @@ ServerLimitsManager* g_pServerLimits;
ConVar* Cvar_net_datablock_enabled;
-void ServerLimitsManager::AddPlayer(R2::CBasePlayer* player)
+void ServerLimitsManager::AddPlayer(R2::CBaseClient* player)
{
PlayerLimitData limitData;
m_PlayerLimitData.insert(std::make_pair(player, limitData));
}
-bool ServerLimitsManager::CheckStringCommandLimits(R2::CBasePlayer* player)
+bool ServerLimitsManager::CheckStringCommandLimits(R2::CBaseClient* player)
{
if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1)
{
@@ -42,7 +42,7 @@ bool ServerLimitsManager::CheckStringCommandLimits(R2::CBasePlayer* player)
return true;
}
-bool ServerLimitsManager::CheckChatLimits(R2::CBasePlayer* player)
+bool ServerLimitsManager::CheckChatLimits(R2::CBaseClient* player)
{
if (Tier0::Plat_FloatTime() - m_PlayerLimitData[player].lastSayTextLimitStart >= 1.0)
{
@@ -57,7 +57,8 @@ bool ServerLimitsManager::CheckChatLimits(R2::CBasePlayer* player)
return true;
}
-AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0, char, __fastcall, (void* self, void* buf))
+AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0,
+char, __fastcall, (void* self, void* buf))
{
enum eNetChanLimitMode
{
@@ -72,7 +73,7 @@ AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0, char, __fastcall, (vo
if (R2::g_pHostState->m_iCurrentState == R2::HostState_t::HS_RUN && Tier0::ThreadInServerFrameThread())
{
// player that sent the message
- R2::CBasePlayer* sender = *(R2::CBasePlayer**)((char*)self + 368);
+ R2::CBaseClient* sender = *(R2::CBaseClient**)((char*)self + 368);
// if no sender, return
// relatively certain this is fine?
@@ -111,7 +112,8 @@ AUTOHOOK(CNetChan__ProcessMessages, engine.dll + 0x2140A0, char, __fastcall, (vo
return ret;
}
-AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, R2::netpacket_t* packet))
+AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800,
+bool, , (void* a1, R2::netpacket_t* packet))
{
if (packet->adr.type == R2::NA_IP &&
(!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->GetBool()) || !Cvar_net_datablock_enabled->GetBool()))
diff --git a/NorthstarDLL/limits.h b/NorthstarDLL/limits.h
index 2303ba5e..cf87b745 100644
--- a/NorthstarDLL/limits.h
+++ b/NorthstarDLL/limits.h
@@ -1,5 +1,5 @@
#pragma once
-#include "r2server.h"
+#include "r2engine.h"
#include "convar.h"
#include <unordered_map>
@@ -32,13 +32,13 @@ class ServerLimitsManager
ConVar* Cvar_sv_querylimit_per_sec;
ConVar* Cvar_sv_max_chat_messages_per_sec;
- std::unordered_map<R2::CBasePlayer*, PlayerLimitData> m_PlayerLimitData;
+ std::unordered_map<R2::CBaseClient*, PlayerLimitData> m_PlayerLimitData;
std::vector<UnconnectedPlayerLimitData> m_UnconnectedPlayerLimitData;
public:
- void AddPlayer(R2::CBasePlayer* player);
- bool CheckStringCommandLimits(R2::CBasePlayer* player);
- bool CheckChatLimits(R2::CBasePlayer* player);
+ void AddPlayer(R2::CBaseClient* player);
+ bool CheckStringCommandLimits(R2::CBaseClient* player);
+ bool CheckChatLimits(R2::CBaseClient* player);
};
extern ServerLimitsManager* g_pServerLimits;
diff --git a/NorthstarDLL/localchatwriter.cpp b/NorthstarDLL/localchatwriter.cpp
index 7f5e2a0a..62543b95 100644
--- a/NorthstarDLL/localchatwriter.cpp
+++ b/NorthstarDLL/localchatwriter.cpp
@@ -280,12 +280,11 @@ void LocalChatWriter::Write(const char* str)
if (startOfEscape != str)
{
// There is some text before the escape sequence, just print that
-
size_t copyChars = startOfEscape - str;
if (copyChars > 255)
copyChars = 255;
- strncpy(writeBuffer, str, copyChars);
- writeBuffer[copyChars] = 0;
+
+ strncpy_s(writeBuffer, copyChars + 1, str, copyChars);
InsertText(writeBuffer);
}
diff --git a/NorthstarDLL/logging.cpp b/NorthstarDLL/logging.cpp
index d1a4f5fd..702955ab 100644
--- a/NorthstarDLL/logging.cpp
+++ b/NorthstarDLL/logging.cpp
@@ -228,12 +228,12 @@ void InitialiseLogging()
// Bind stdout to receive console output.
FILE* fp;
freopen_s(&fp, "CONOUT$", "w", stdout);
- _dup2(_fileno(stdout), _fileno(stderr));
+ //_dup2(_fileno(stdout), _fileno(stderr));
spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v");
}
-ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", EngineSpewFuncHooks, ConCommand, (HMODULE baseAddress))
+ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, (HMODULE baseAddress))
{
AUTOHOOK_DISPATCH_MODULE(engine.dll)
diff --git a/NorthstarDLL/masterserver.cpp b/NorthstarDLL/masterserver.cpp
index 4d5f371b..d19779d7 100644
--- a/NorthstarDLL/masterserver.cpp
+++ b/NorthstarDLL/masterserver.cpp
@@ -3,7 +3,6 @@
#include "concommand.h"
#include "playlist.h"
#include "serverauthentication.h"
-#include "hoststate.h"
#include "tier0.h"
#include "r2engine.h"
#include "modmanager.h"
@@ -18,83 +17,10 @@
#include <cstring>
#include <regex>
-ConVar* Cvar_ns_masterserver_hostname;
-ConVar* Cvar_ns_report_server_to_masterserver;
-ConVar* Cvar_ns_report_sp_server_to_masterserver;
-
-ConVar* Cvar_ns_server_name;
-ConVar* Cvar_ns_server_desc;
-ConVar* Cvar_ns_server_password;
-
-ConVar* Cvar_ns_curl_log_enable;
-
-// Source ConVar
-ConVar* Cvar_hostname;
-ConVar* Cvar_hostport;
-
MasterServerManager* g_pMasterServerManager;
-// Convert a hex digit char to integer.
-inline int hctod(char c)
-{
- if (c >= 'A' && c <= 'F')
- {
- return c - 'A' + 10;
- }
- else if (c >= 'a' && c <= 'f')
- {
- return c - 'a' + 10;
- }
- else
- {
- return c - '0';
- }
-}
-
-// This function interprets all 4-hexadecimal-digit unicode codepoint characters like \u4E2D to UTF-8 encoding.
-std::string unescape_unicode(const std::string& str)
-{
- std::string result;
- std::regex r("\\\\u([a-f\\d]{4})", std::regex::icase);
- auto matches_begin = std::sregex_iterator(str.begin(), str.end(), r);
- auto matches_end = std::sregex_iterator();
- std::smatch last_match;
- for (std::sregex_iterator i = matches_begin; i != matches_end; ++i)
- {
- last_match = *i;
- result.append(last_match.prefix());
- unsigned int cp = 0;
- for (int i = 2; i <= 5; ++i)
- {
- cp *= 16;
- cp += hctod(last_match.str()[i]);
- }
- if (cp <= 0x7F)
- {
- result.push_back(cp);
- }
- else if (cp <= 0x7FF)
- {
- result.push_back((cp >> 6) | 0b11000000 & (~(1 << 5)));
- result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- }
- else if (cp <= 0xFFFF)
- {
- result.push_back((cp >> 12) | 0b11100000 & (~(1 << 4)));
- result.push_back((cp >> 6) & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
- }
- }
- if (!last_match.ready())
- {
- return str;
- }
- else
- {
- result.append(last_match.suffix());
- }
- return result;
-}
+ConVar* Cvar_ns_masterserver_hostname;
+ConVar* Cvar_ns_curl_log_enable;
RemoteServerInfo::RemoteServerInfo(
const char* newId,
@@ -109,23 +35,19 @@ RemoteServerInfo::RemoteServerInfo(
// passworded servers don't have public ips
requiresPassword = newRequiresPassword;
- strncpy((char*)id, newId, sizeof(id));
- id[sizeof(id) - 1] = 0;
- strncpy((char*)name, newName, sizeof(name));
- name[sizeof(name) - 1] = 0;
+ strncpy_s((char*)id, sizeof(id), newId, sizeof(id) - 1);
+ strncpy_s((char*)name, sizeof(name), newName, sizeof(name) - 1);
description = std::string(newDescription);
- strncpy((char*)map, newMap, sizeof(map));
- map[sizeof(map) - 1] = 0;
- strncpy((char*)playlist, newPlaylist, sizeof(playlist));
- playlist[sizeof(playlist) - 1] = 0;
+ strncpy_s((char*)map, sizeof(map) , newMap, sizeof(map) - 1);
+ strncpy_s((char*)playlist, sizeof(playlist) , newPlaylist, sizeof(playlist) - 1);
playerCount = newPlayerCount;
maxPlayers = newMaxPlayers;
}
-void MasterServerManager::SetCommonHttpClientOptions(CURL* curl)
+void SetCommonHttpClientOptions(CURL* curl)
{
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_easy_setopt(curl, CURLOPT_VERBOSE, Cvar_ns_curl_log_enable->GetBool());
@@ -205,8 +127,10 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(const char* uid, co
if (originAuthInfo["success"].IsTrue() && originAuthInfo.HasMember("token") && originAuthInfo["token"].IsString())
{
- strncpy(m_sOwnClientAuthToken, originAuthInfo["token"].GetString(), sizeof(m_sOwnClientAuthToken));
- m_sOwnClientAuthToken[sizeof(m_sOwnClientAuthToken) - 1] = 0;
+ strncpy_s(
+ m_sOwnClientAuthToken,
+ sizeof(m_sOwnClientAuthToken), originAuthInfo["token"].GetString(),
+ sizeof(m_sOwnClientAuthToken) - 1);
spdlog::info("Northstar origin authentication completed successfully!");
}
else
@@ -579,8 +503,7 @@ void MasterServerManager::AuthenticateWithOwnServer(const char* uid, const char*
}
RemoteAuthData newAuthData {};
- strncpy(newAuthData.uid, authInfoJson["id"].GetString(), sizeof(newAuthData.uid));
- newAuthData.uid[sizeof(newAuthData.uid) - 1] = 0;
+ strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), authInfoJson["id"].GetString(), sizeof(newAuthData.uid) - 1);
newAuthData.pdataSize = authInfoJson["persistentData"].GetArray().Size();
newAuthData.pdata = new char[newAuthData.pdataSize];
@@ -729,8 +652,11 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl
m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString());
m_pendingConnectionInfo.port = (unsigned short)connectionInfoJson["port"].GetUint();
- strncpy(m_pendingConnectionInfo.authToken, connectionInfoJson["authToken"].GetString(), 31);
- m_pendingConnectionInfo.authToken[31] = 0;
+ strncpy_s(
+ m_pendingConnectionInfo.authToken,
+ sizeof(m_pendingConnectionInfo.authToken),
+ connectionInfoJson["authToken"].GetString(),
+ sizeof(m_pendingConnectionInfo.authToken) - 1);
m_bHasPendingConnectionInfo = true;
m_bSuccessfullyAuthenticatedWithGameServer = true;
@@ -752,43 +678,35 @@ void MasterServerManager::AuthenticateWithServer(const char* uid, const char* pl
requestThread.detach();
}
-void MasterServerManager::AddSelfToServerList(
- int port,
- int authPort,
- const char* name,
- const char* description,
- const char* map,
- const char* playlist,
- int maxPlayers,
- const char* password)
+void MasterServerManager::WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize)
{
- if (!Cvar_ns_report_server_to_masterserver->GetBool())
- return;
-
- if (!Cvar_ns_report_sp_server_to_masterserver->GetBool() && !strncmp(map, "sp_", 3))
+ // still call this if we don't have a server id, since lobbies that aren't port forwarded need to be able to call it
+ m_bSavingPersistentData = true;
+ if (!pdataSize)
{
- m_bRequireClientAuth = false;
+ spdlog::warn("attempted to write pdata of size 0!");
return;
}
- m_bRequireClientAuth = true;
-
- std::string strName(name);
- std::string strDescription(description);
- std::string strMap(map);
- std::string strPlaylist(playlist);
- std::string strPassword(password);
+ std::string strPlayerId(playerId);
+ std::string strPdata(pdata, pdataSize);
std::thread requestThread(
- [this, port, authPort, strName, strDescription, strMap, strPlaylist, maxPlayers, strPassword]
+ [this, strPlayerId, strPdata, pdataSize]
{
- m_sOwnServerId[0] = 0;
- m_sOwnServerAuthToken[0] = 0;
-
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->GetString(),
+ strPlayerId,
+ m_sOwnServerId)
+ .c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
@@ -796,401 +714,299 @@ void MasterServerManager::AddSelfToServerList(
curl_mime* mime = curl_mime_init(curl);
curl_mimepart* part = curl_mime_addpart(mime);
- curl_mime_data(part, m_sOwnModInfoJson.c_str(), m_sOwnModInfoJson.size());
- curl_mime_name(part, "modinfo");
- curl_mime_filename(part, "modinfo.json");
- curl_mime_type(part, "application/json");
+ curl_mime_data(part, strPdata.c_str(), pdataSize);
+ curl_mime_name(part, "pdata");
+ curl_mime_filename(part, "file.pdata");
+ curl_mime_type(part, "application/octet-stream");
curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- // format every paramter because computers hate me
- {
- char* nameEscaped = curl_easy_escape(curl, strName.c_str(), strName.length());
- char* descEscaped = curl_easy_escape(curl, strDescription.c_str(), strDescription.length());
- char* mapEscaped = curl_easy_escape(curl, strMap.c_str(), strMap.length());
- char* playlistEscaped = curl_easy_escape(curl, strPlaylist.c_str(), strPlaylist.length());
- char* passwordEscaped = curl_easy_escape(curl, strPassword.c_str(), strPassword.length());
-
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}",
- Cvar_ns_masterserver_hostname->GetString(),
- port,
- authPort,
- nameEscaped,
- descEscaped,
- mapEscaped,
- playlistEscaped,
- maxPlayers,
- passwordEscaped)
- .c_str());
-
- curl_free(nameEscaped);
- curl_free(descEscaped);
- curl_free(mapEscaped);
- curl_free(playlistEscaped);
- curl_free(passwordEscaped);
- }
-
CURLcode result = curl_easy_perform(curl);
if (result == CURLcode::CURLE_OK)
- {
m_bSuccessfullyConnected = true;
-
- rapidjson_document serverAddedJson;
- serverAddedJson.Parse(readBuffer.c_str());
-
- if (serverAddedJson.HasParseError())
- {
- spdlog::error(
- "Failed reading masterserver authentication response: encountered parse error \"{}\"",
- rapidjson::GetParseError_En(serverAddedJson.GetParseError()));
- goto REQUEST_END_CLEANUP;
- }
-
- if (!serverAddedJson.IsObject())
- {
- spdlog::error("Failed reading masterserver authentication response: root object is not an object");
- goto REQUEST_END_CLEANUP;
- }
-
- if (serverAddedJson.HasMember("error"))
- {
- spdlog::error("Failed reading masterserver response: got fastify error response");
- spdlog::error(readBuffer);
- goto REQUEST_END_CLEANUP;
- }
-
- if (!serverAddedJson["success"].IsTrue())
- {
- spdlog::error("Adding server to masterserver failed: \"success\" is not true");
- goto REQUEST_END_CLEANUP;
- }
-
- if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() ||
- !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString())
- {
- spdlog::error("Failed reading masterserver response: malformed json object");
- goto REQUEST_END_CLEANUP;
- }
-
- strncpy(m_sOwnServerId, serverAddedJson["id"].GetString(), sizeof(m_sOwnServerId));
- m_sOwnServerId[sizeof(m_sOwnServerId) - 1] = 0;
-
- strncpy(m_sOwnServerAuthToken, serverAddedJson["serverAuthToken"].GetString(), sizeof(m_sOwnServerAuthToken));
- m_sOwnServerAuthToken[sizeof(m_sOwnServerAuthToken) - 1] = 0;
-
- // heartbeat thread
- // ideally this should actually be done in main thread, rather than on it's own thread, so it'd stop if server freezes
- std::thread heartbeatThread(
- [this]
- {
- Sleep(5000);
-
- // defensive check, as m_ownServer could be set to null during the Sleep(5000) above
- if (!*m_sOwnServerId)
- return;
-
- do
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
-
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- 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, g_pMasterServerManager->m_sUnicodeServerName.c_str(), NULL);
- char* escapedDescNew = curl_easy_escape(curl, g_pMasterServerManager->m_sUnicodeServerDesc.c_str(), NULL);
- char* escapedMapNew = curl_easy_escape(curl, R2::g_pHostState->m_levelName, NULL);
- char* escapedPlaylistNew = curl_easy_escape(curl, R2::GetCurrentPlaylistName(), NULL);
- char* escapedPasswordNew = curl_easy_escape(curl, Cvar_ns_server_password->GetString(), NULL);
-
- int maxPlayers = 6;
- const char* maxPlayersVar = R2::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->GetString(),
- m_sOwnServerId,
- Cvar_hostport->GetInt(),
- g_pServerAuthentication->Cvar_ns_player_auth_port->GetInt(),
- escapedNameNew,
- escapedDescNew,
- escapedMapNew,
- escapedPlaylistNew,
- g_pServerAuthentication->m_PlayerAuthenticationData.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_sOwnModInfoJson.c_str(), m_sOwnModInfoJson.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);
-
- // defensive check, as m_ownServerId could be set to null before this request gets processed
- if (!*m_sOwnServerId)
- return;
-
- 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_sOwnServerId, serverAddedJson["id"].GetString(), sizeof(m_sOwnServerId));
- m_sOwnServerId[sizeof(m_sOwnServerId) - 1] = 0;
- }
-
- if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString())
- {
- strncpy(
- m_sOwnServerAuthToken,
- serverAddedJson["serverAuthToken"].GetString(),
- sizeof(m_sOwnServerAuthToken));
- m_sOwnServerAuthToken[sizeof(m_sOwnServerAuthToken) - 1] = 0;
- }
- }
- }
- else
- spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result));
-
- curl_easy_cleanup(curl);
- Sleep(10000);
- } while (*m_sOwnServerId);
- });
-
- heartbeatThread.detach();
- }
else
- {
- spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result));
m_bSuccessfullyConnected = false;
- }
- REQUEST_END_CLEANUP:
curl_easy_cleanup(curl);
- curl_mime_free(mime);
+
+ m_bSavingPersistentData = false;
});
requestThread.detach();
}
-void MasterServerManager::UpdateServerMapAndPlaylist(const char* map, const char* playlist, int maxPlayers)
+class MasterServerPresenceReporter : public ServerPresenceReporter
{
- // dont call this if we don't have a server id
- if (!*m_sOwnServerId)
- return;
-
- std::string strMap(map);
- std::string strPlaylist(playlist);
+ void ReportPresence(const ServerPresence* pServerPresence) override
+ {
+ // make a copy of presence for multithreading purposes
+ ServerPresence threadedPresence(pServerPresence);
- std::thread requestThread(
- [this, strMap, strPlaylist, maxPlayers]
+ if (!*g_pMasterServerManager->m_sOwnServerId)
{
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
+ // add server
+ std::thread addServerThread(
+ [threadedPresence]
+ {
+ g_pMasterServerManager->m_sOwnServerId[0] = 0;
+ g_pMasterServerManager->m_sOwnServerAuthToken[0] = 0;
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
+ CURL* curl = curl_easy_init();
+ SetCommonHttpClientOptions(curl);
- // escape params
- {
- char* mapEscaped = curl_easy_escape(curl, strMap.c_str(), strMap.length());
- char* playlistEscaped = curl_easy_escape(curl, strPlaylist.c_str(), strPlaylist.length());
+ std::string readBuffer;
+ curl_easy_setopt(curl, CURLOPT_POST, 1L);
+ 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={}&map={}&playlist={}&maxPlayers={}",
- Cvar_ns_masterserver_hostname->GetString(),
- m_sOwnServerId,
- mapEscaped,
- playlistEscaped,
- maxPlayers)
- .c_str());
+ curl_mime* mime = curl_mime_init(curl);
+ curl_mimepart* part = curl_mime_addpart(mime);
- curl_free(mapEscaped);
- curl_free(playlistEscaped);
- }
+ curl_mime_data(
+ part, g_pMasterServerManager->m_sOwnModInfoJson.c_str(), g_pMasterServerManager->m_sOwnModInfoJson.size());
+ curl_mime_name(part, "modinfo");
+ curl_mime_filename(part, "modinfo.json");
+ curl_mime_type(part, "application/json");
- CURLcode result = curl_easy_perform(curl);
+ curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- if (result == CURLcode::CURLE_OK)
- m_bSuccessfullyConnected = true;
- else
- m_bSuccessfullyConnected = false;
+ // format every paramter because computers hate me
+ {
+ char* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), NULL);
+ char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), NULL);
+ char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, NULL);
+ char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, NULL);
+ char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, NULL);
+
+ curl_easy_setopt(
+ curl,
+ CURLOPT_URL,
+ fmt::format(
+ "{}/server/"
+ "add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}",
+ Cvar_ns_masterserver_hostname->GetString(),
+ threadedPresence.m_iPort,
+ threadedPresence.m_iAuthPort,
+ nameEscaped,
+ descEscaped,
+ mapEscaped,
+ playlistEscaped,
+ threadedPresence.m_iMaxPlayers,
+ passwordEscaped)
+ .c_str());
+
+ curl_free(nameEscaped);
+ curl_free(descEscaped);
+ curl_free(mapEscaped);
+ curl_free(playlistEscaped);
+ curl_free(passwordEscaped);
+ }
- curl_easy_cleanup(curl);
- });
+ CURLcode result = curl_easy_perform(curl);
- requestThread.detach();
-}
+ if (result == CURLcode::CURLE_OK)
+ {
+ g_pMasterServerManager->m_bSuccessfullyConnected = true;
-void MasterServerManager::UpdateServerPlayerCount(int playerCount)
-{
- // dont call this if we don't have a server id
- if (!*m_sOwnServerId)
- return;
+ rapidjson_document serverAddedJson;
+ serverAddedJson.Parse(readBuffer.c_str());
- std::thread requestThread(
- [this, playerCount]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
+ if (serverAddedJson.HasParseError())
+ {
+ spdlog::error(
+ "Failed reading masterserver authentication response: encountered parse error \"{}\"",
+ rapidjson::GetParseError_En(serverAddedJson.GetParseError()));
+ goto REQUEST_END_CLEANUP;
+ }
- std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
- 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->GetString(), m_sOwnServerId, playerCount)
- .c_str());
+ if (!serverAddedJson.IsObject())
+ {
+ spdlog::error("Failed reading masterserver authentication response: root object is not an object");
+ goto REQUEST_END_CLEANUP;
+ }
- CURLcode result = curl_easy_perform(curl);
+ if (serverAddedJson.HasMember("error"))
+ {
+ spdlog::error("Failed reading masterserver response: got fastify error response");
+ spdlog::error(readBuffer);
+ goto REQUEST_END_CLEANUP;
+ }
- if (result == CURLcode::CURLE_OK)
- m_bSuccessfullyConnected = true;
- else
- m_bSuccessfullyConnected = false;
+ if (!serverAddedJson["success"].IsTrue())
+ {
+ spdlog::error("Adding server to masterserver failed: \"success\" is not true");
+ goto REQUEST_END_CLEANUP;
+ }
- curl_easy_cleanup(curl);
- });
+ if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString() ||
+ !serverAddedJson.HasMember("serverAuthToken") || !serverAddedJson["serverAuthToken"].IsString())
+ {
+ spdlog::error("Failed reading masterserver response: malformed json object");
+ goto REQUEST_END_CLEANUP;
+ }
- requestThread.detach();
-}
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerId,
+ sizeof(g_pMasterServerManager->m_sOwnServerId),
+ serverAddedJson["id"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerId) - 1);
+
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerAuthToken,
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken),
+ serverAddedJson["serverAuthToken"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1);
+ }
+ else
+ {
+ spdlog::error("Failed adding self to server list: error {}", curl_easy_strerror(result));
+ g_pMasterServerManager->m_bSuccessfullyConnected = false;
+ }
-void MasterServerManager::WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize)
-{
- // still call this if we don't have a server id, since lobbies that aren't port forwarded need to be able to call it
- m_bSavingPersistentData = true;
- if (!pdataSize)
- {
- spdlog::warn("attempted to write pdata of size 0!");
- return;
- }
+ REQUEST_END_CLEANUP:
+ curl_easy_cleanup(curl);
+ curl_mime_free(mime);
+ });
+ addServerThread.detach();
+ }
+ else
+ {
+ // update server
+ std::thread updateServerThread(
+ [threadedPresence]
+ {
+ CURL* curl = curl_easy_init();
+ SetCommonHttpClientOptions(curl);
- std::string strPlayerId(playerId);
- std::string strPdata(pdata, pdataSize);
+ std::string readBuffer;
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
- std::thread requestThread(
- [this, strPlayerId, strPdata, pdataSize]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
+ // 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* nameEscaped = curl_easy_escape(curl, threadedPresence.m_sServerName.c_str(), NULL);
+ char* descEscaped = curl_easy_escape(curl, threadedPresence.m_sServerDesc.c_str(), NULL);
+ char* mapEscaped = curl_easy_escape(curl, threadedPresence.m_MapName, NULL);
+ char* playlistEscaped = curl_easy_escape(curl, threadedPresence.m_PlaylistName, NULL);
+ char* passwordEscaped = curl_easy_escape(curl, threadedPresence.m_Password, NULL);
+
+ 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->GetString(),
+ g_pMasterServerManager->m_sOwnServerId,
+ threadedPresence.m_iPort,
+ threadedPresence.m_iAuthPort,
+ nameEscaped,
+ descEscaped,
+ mapEscaped,
+ playlistEscaped,
+ threadedPresence.m_iPlayerCount,
+ threadedPresence.m_iMaxPlayers,
+ passwordEscaped)
+ .c_str());
+
+ curl_free(nameEscaped);
+ curl_free(descEscaped);
+ curl_free(mapEscaped);
+ curl_free(playlistEscaped);
+ curl_free(passwordEscaped);
+ }
- std::string readBuffer;
- curl_easy_setopt(
- curl,
- CURLOPT_URL,
- fmt::format(
- "{}/accounts/write_persistence?id={}&serverId={}",
- Cvar_ns_masterserver_hostname->GetString(),
- strPlayerId,
- m_sOwnServerId)
- .c_str());
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- 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* mime = curl_mime_init(curl);
- curl_mimepart* part = curl_mime_addpart(mime);
+ curl_mime_data(
+ part, g_pMasterServerManager->m_sOwnModInfoJson.c_str(), g_pMasterServerManager->m_sOwnModInfoJson.size());
+ curl_mime_name(part, "modinfo");
+ curl_mime_filename(part, "modinfo.json");
+ curl_mime_type(part, "application/json");
- curl_mime_data(part, strPdata.c_str(), pdataSize);
- curl_mime_name(part, "pdata");
- curl_mime_filename(part, "file.pdata");
- curl_mime_type(part, "application/octet-stream");
+ curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
+ CURLcode result = curl_easy_perform(curl);
- CURLcode result = curl_easy_perform(curl);
+ if (result == CURLcode::CURLE_OK)
+ {
+ rapidjson_document serverAddedJson;
+ serverAddedJson.Parse(readBuffer.c_str());
- if (result == CURLcode::CURLE_OK)
- m_bSuccessfullyConnected = true;
- else
- m_bSuccessfullyConnected = false;
+ if (!serverAddedJson.HasParseError() && serverAddedJson.IsObject())
+ {
+ if (serverAddedJson.HasMember("id") && serverAddedJson["id"].IsString())
+ {
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerId,
+ sizeof(g_pMasterServerManager->m_sOwnServerId),
+ serverAddedJson["id"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerId) - 1);
+ }
- curl_easy_cleanup(curl);
+ if (serverAddedJson.HasMember("serverAuthToken") && serverAddedJson["serverAuthToken"].IsString())
+ {
- m_bSavingPersistentData = false;
- });
+ strncpy_s(
+ g_pMasterServerManager->m_sOwnServerAuthToken,
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken),
+ serverAddedJson["serverAuthToken"].GetString(),
+ sizeof(g_pMasterServerManager->m_sOwnServerAuthToken) - 1);
+ }
+ }
+ }
+ else
+ spdlog::warn("Heartbeat failed with error {}", curl_easy_strerror(result));
- requestThread.detach();
-}
+ curl_easy_cleanup(curl);
+ });
+ updateServerThread.detach();
+ }
+ }
-void MasterServerManager::RemoveSelfFromServerList()
-{
- // dont call this if we don't have a server id
- if (!*m_sOwnServerId || !Cvar_ns_report_server_to_masterserver->GetBool())
- return;
+ void DestroyPresence(const ServerPresence* pServerPresence) override
+ {
+ // dont call this if we don't have a server id
+ if (!*g_pMasterServerManager->m_sOwnServerId)
+ return;
- std::thread requestThread(
- [this]
- {
- CURL* curl = curl_easy_init();
- SetCommonHttpClientOptions(curl);
+ 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_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->GetString(), m_sOwnServerId).c_str());
+ std::string readBuffer;
+ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ 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->GetString(), g_pMasterServerManager->m_sOwnServerId)
+ .c_str());
- *m_sOwnServerId = 0;
- CURLcode result = curl_easy_perform(curl);
+ *g_pMasterServerManager->m_sOwnServerId = 0;
+ CURLcode result = curl_easy_perform(curl);
- if (result == CURLcode::CURLE_OK)
- m_bSuccessfullyConnected = true;
- else
- m_bSuccessfullyConnected = false;
+ if (result == CURLcode::CURLE_OK)
+ g_pMasterServerManager->m_bSuccessfullyConnected = true;
+ else
+ g_pMasterServerManager->m_bSuccessfullyConnected = false;
- curl_easy_cleanup(curl);
- });
+ curl_easy_cleanup(curl);
+ });
- requestThread.detach();
-}
+ requestThread.detach();
+ }
+};
void ConCommand_ns_fetchservers(const CCommand& args)
{
@@ -1199,27 +1015,15 @@ void ConCommand_ns_fetchservers(const CCommand& args)
MasterServerManager::MasterServerManager() : m_pendingConnectionInfo {}, m_sOwnServerId {""}, m_sOwnClientAuthToken {""} {}
-ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, ConCommand, (HMODULE baseAddress))
+ON_DLL_LOAD_RELIESON("engine.dll", MasterServer, (ConCommand, ServerPresence), (HMODULE baseAddress))
{
g_pMasterServerManager = new MasterServerManager;
Cvar_ns_masterserver_hostname = new ConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, "");
-
- Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "This server's description", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
- g_pMasterServerManager->m_sUnicodeServerName = unescape_unicode(Cvar_ns_server_name->GetString());
- });
- Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
- g_pMasterServerManager->m_sUnicodeServerDesc = unescape_unicode(Cvar_ns_server_desc->GetString());
- });
-
- Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "This server's password");
- Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "Whether we should report this server to the masterserver");
- Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "Whether we should report this server to the masterserver, when started in singleplayer");
-
Cvar_ns_curl_log_enable = new ConVar("ns_curl_log_enable", "0", FCVAR_NONE, "Whether curl should log to the console");
- Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8);
- Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070);
-
RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "Fetch all servers from the masterserver", FCVAR_CLIENTDLL);
+
+ MasterServerPresenceReporter* presenceReporter = new MasterServerPresenceReporter;
+ g_pServerPresence->AddPresenceReporter(presenceReporter);
} \ No newline at end of file
diff --git a/NorthstarDLL/masterserver.h b/NorthstarDLL/masterserver.h
index ae5167df..aa31a4a5 100644
--- a/NorthstarDLL/masterserver.h
+++ b/NorthstarDLL/masterserver.h
@@ -1,22 +1,13 @@
#pragma once
#include "convar.h"
+#include "serverpresence.h"
#include <WinSock2.h>
#include <string>
#include <cstring>
extern ConVar* Cvar_ns_masterserver_hostname;
-extern ConVar* Cvar_ns_report_server_to_masterserver;
-extern ConVar* Cvar_ns_report_sp_server_to_masterserver;
-
-extern ConVar* Cvar_ns_server_name;
-extern ConVar* Cvar_ns_server_desc;
-extern ConVar* Cvar_ns_server_password;
-
extern ConVar* Cvar_ns_curl_log_enable;
-extern ConVar* Cvar_hostname;
-extern ConVar* Cvar_hostport;
-
struct RemoteModInfo
{
public:
@@ -96,8 +87,6 @@ class MasterServerManager
char m_sOwnClientAuthToken[33];
std::string m_sOwnModInfoJson;
- std::string m_sUnicodeServerName; // Unicode unescaped version of Cvar_ns_auth_servername for support in cjk characters
- std::string m_sUnicodeServerDesc; // Unicode unescaped version of Cvar_ns_auth_serverdesc for support in cjk characters
bool m_bOriginAuthWithMasterServerDone = false;
bool m_bOriginAuthWithMasterServerInProgress = false;
@@ -120,9 +109,6 @@ class MasterServerManager
bool m_bHasMainMenuPromoData = false;
MainMenuPromoData m_sMainMenuPromoData;
- private:
- void SetCommonHttpClientOptions(CURL* curl);
-
public:
MasterServerManager();
@@ -132,22 +118,8 @@ class MasterServerManager
void AuthenticateOriginWithMasterServer(const char* uid, const char* originToken);
void AuthenticateWithOwnServer(const char* uid, const char* playerToken);
void AuthenticateWithServer(const char* uid, const char* playerToken, const char* serverId, const char* password);
- void AddSelfToServerList(
- int port,
- int authPort,
- const char* name,
- const char* description,
- const char* map,
- const char* playlist,
- int maxPlayers,
- const char* password);
- void UpdateServerMapAndPlaylist(const char* map, const char* playlist, int playerCount);
- void UpdateServerPlayerCount(int playerCount);
void WritePlayerPersistentData(const char* playerId, const char* pdata, size_t pdataSize);
- void RemoveSelfFromServerList();
};
-std::string unescape_unicode(const std::string& str);
extern MasterServerManager* g_pMasterServerManager;
extern ConVar* Cvar_ns_masterserver_hostname;
-extern ConVar* Cvar_ns_server_password;
diff --git a/NorthstarDLL/miscserverscript.cpp b/NorthstarDLL/miscserverscript.cpp
index 12ab5708..1b075079 100644
--- a/NorthstarDLL/miscserverscript.cpp
+++ b/NorthstarDLL/miscserverscript.cpp
@@ -11,7 +11,7 @@
SQRESULT SQ_EarlyWritePlayerIndexPersistenceForLeave(void* sqvm)
{
int playerIndex = g_pServerSquirrel->getinteger(sqvm, 1);
- R2::CBasePlayer* player = R2::UTIL_PlayerByIndex(playerIndex);
+ R2::CBaseClient* player = &R2::g_pClientArray[playerIndex];
if (!g_pServerAuthentication->m_PlayerAuthenticationData.count(player))
{
@@ -35,7 +35,7 @@ SQRESULT SQ_IsWritingPlayerPersistence(void* sqvm)
SQRESULT SQ_IsPlayerIndexLocalPlayer(void* sqvm)
{
int playerIndex = g_pServerSquirrel->getinteger(sqvm, 1);
- R2::CBasePlayer* player = R2::UTIL_PlayerByIndex(playerIndex);
+ R2::CBaseClient* player = &R2::g_pClientArray[playerIndex];
if (!g_pServerAuthentication->m_PlayerAuthenticationData.count(player))
{
g_pServerSquirrel->raiseerror(sqvm, fmt::format("Invalid playerindex {}", playerIndex).c_str());
diff --git a/NorthstarDLL/modmanager.cpp b/NorthstarDLL/modmanager.cpp
index 9ed3ea6f..b0ff609e 100644
--- a/NorthstarDLL/modmanager.cpp
+++ b/NorthstarDLL/modmanager.cpp
@@ -636,7 +636,7 @@ fs::path GetCompiledAssetsPath()
return fs::path(GetNorthstarPrefix() + COMPILED_ASSETS_SUFFIX);
}
-ON_DLL_LOAD_RELIESON("engine.dll", ModManager, ConCommand, (HMODULE baseAddress))
+ON_DLL_LOAD_RELIESON("engine.dll", ModManager, (ConCommand, MasterServer), (HMODULE baseAddress))
{
g_pModManager = new ModManager;
diff --git a/NorthstarDLL/playlist.cpp b/NorthstarDLL/playlist.cpp
index 0eb0e1d2..b8fadc98 100644
--- a/NorthstarDLL/playlist.cpp
+++ b/NorthstarDLL/playlist.cpp
@@ -5,6 +5,7 @@
#include "convar.h"
#include "squirrel.h"
#include "hoststate.h"
+#include "serverpresence.h"
AUTOHOOK_INIT()
@@ -30,10 +31,17 @@ char,, (void* a1, void* a2))
}
AUTOHOOK(SetCurrentPlaylist, engine.dll + 0x18EB20,
-void,, (const char* pPlaylistName))
+bool, __fastcall, (const char* pPlaylistName))
{
- SetCurrentPlaylist(pPlaylistName);
- spdlog::info("Set playlist to {}", pPlaylistName);
+ bool bSuccess = SetCurrentPlaylist(pPlaylistName);
+
+ if (bSuccess)
+ {
+ spdlog::info("Set playlist to {}", R2::GetCurrentPlaylistName());
+ g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName());
+ }
+
+ return bSuccess;
}
AUTOHOOK(SetPlaylistVarOverride, engine.dll + 0x18ED00,
@@ -54,7 +62,6 @@ const char*,, (const char* pVarName, bool bUseOverrides))
return GetCurrentPlaylistVar(pVarName, bUseOverrides);
}
-
AUTOHOOK(GetCurrentGamemodeMaxPlayers, engine.dll + 0x18C430,
int,, ())
{
@@ -84,7 +91,7 @@ void ConCommand_setplaylistvaroverride(const CCommand& args)
R2::SetPlaylistVarOverride(args.Arg(i), args.Arg(i + 1));
}
-ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, ConCommand, (HMODULE baseAddress))
+ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, (ConCommand, ConVar), (HMODULE baseAddress))
{
AUTOHOOK_DISPATCH()
diff --git a/NorthstarDLL/plugins.cpp b/NorthstarDLL/plugins.cpp
index a6087efe..d34e0392 100644
--- a/NorthstarDLL/plugins.cpp
+++ b/NorthstarDLL/plugins.cpp
@@ -3,6 +3,7 @@
#include "plugins.h"
#include "masterserver.h"
#include "convar.h"
+#include "serverpresence.h"
#include <chrono>
#include <windows.h>
@@ -194,7 +195,7 @@ SQRESULT SQ_UpdateListenServer(void* sqvm)
{
AcquireSRWLockExclusive(&serverInfoLock);
serverInfo.id = g_pMasterServerManager->m_sOwnServerId;
- serverInfo.password = Cvar_ns_server_password->GetString();
+ serverInfo.password = "";// g_pServerPresence->Cvar_ns_server_password->GetString(); todo this fr
ReleaseSRWLockExclusive(&serverInfoLock);
return SQRESULT_NOTNULL;
}
diff --git a/NorthstarDLL/printcommands.cpp b/NorthstarDLL/printcommands.cpp
index 12b927d0..cb0ce542 100644
--- a/NorthstarDLL/printcommands.cpp
+++ b/NorthstarDLL/printcommands.cpp
@@ -100,8 +100,8 @@ void ConCommand_find(const CCommand& arg)
for (int i = 0; i < arg.ArgC() - 1; i++)
{
// make lowercase to avoid case sensitivity
- strncpy(pTempName, map.second->m_pszName, sizeof(pTempName));
- strncpy(pTempSearchTerm, arg.Arg(i + 1), sizeof(pTempSearchTerm));
+ strncpy_s(pTempName, sizeof(pTempName), map.second->m_pszName, sizeof(pTempName) - 1);
+ strncpy_s(pTempSearchTerm, sizeof(pTempSearchTerm), arg.Arg(i + 1), sizeof(pTempSearchTerm) - 1);
for (int i = 0; pTempName[i]; i++)
pTempName[i] = tolower(pTempName[i]);
diff --git a/NorthstarDLL/r2engine.cpp b/NorthstarDLL/r2engine.cpp
index 5d063141..883178ee 100644
--- a/NorthstarDLL/r2engine.cpp
+++ b/NorthstarDLL/r2engine.cpp
@@ -13,6 +13,10 @@ namespace R2
CEngine* g_pEngine;
void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...);
+
+ CBaseClient* g_pClientArray;
+
+ server_state_t* g_pServerState;
} // namespace R2
ON_DLL_LOAD("engine.dll", R2Engine, (HMODULE baseAddress))
@@ -24,4 +28,8 @@ ON_DLL_LOAD("engine.dll", R2Engine, (HMODULE baseAddress))
g_pEngine = *(CEngine**)((char*)baseAddress + 0x7D70C8);
CBaseClient__Disconnect = (void (*)(void*, uint32_t, const char*, ...))((char*)baseAddress + 0x1012C0);
+
+ g_pServerState = (server_state_t*)((char*)baseAddress + 0x12A53D48);
+
+ g_pClientArray = (CBaseClient*)((char*)baseAddress + 0x12A53F90);
} \ No newline at end of file
diff --git a/NorthstarDLL/r2engine.h b/NorthstarDLL/r2engine.h
index b3d118de..848adc75 100644
--- a/NorthstarDLL/r2engine.h
+++ b/NorthstarDLL/r2engine.h
@@ -140,4 +140,58 @@ namespace R2
// struct netpacket_s* pNext; // for internal use, should be NULL in public
} netpacket_t;
#pragma pack(pop)
+
+ const int PERSISTENCE_MAX_SIZE = 0xD000;
+
+ // note: NOT_READY and READY are the only entries we have here that are defined by the vanilla game
+ // entries after this are custom and used to determine the source of persistence, e.g. whether it is local or remote
+ enum class ePersistenceReady : char
+ {
+ NOT_READY,
+ READY = 3,
+ READY_INSECURE = 3,
+ READY_REMOTE
+ };
+
+ #pragma pack(push, 1)
+ struct CBaseClient // 0x2D728 bytes
+ {
+ char pad0[0x16];
+
+ // +0x16
+ char m_Name[64];
+ // +0x56
+
+ char pad1[0x44A];
+
+ // +0x4A0
+ ePersistenceReady m_iPersistenceReady;
+ // +0x4A1
+
+ char pad2[0x59];
+
+ // +0x4FA
+ char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE];
+
+ char pad3[0x2006];
+
+ // +0xF500
+ char m_UID[32];
+ // +0xF520
+
+ char pad4[0x1E208];
+ };
+ #pragma pack(pop)
+
+ extern CBaseClient* g_pClientArray;
+
+ enum server_state_t
+ {
+ ss_dead = 0, // Dead
+ ss_loading, // Spawning
+ ss_active, // Running
+ ss_paused, // Running, but paused
+ };
+
+ extern server_state_t* g_pServerState;
} // namespace R2
diff --git a/NorthstarDLL/r2server.cpp b/NorthstarDLL/r2server.cpp
index 848104d4..8d04beff 100644
--- a/NorthstarDLL/r2server.cpp
+++ b/NorthstarDLL/r2server.cpp
@@ -6,18 +6,12 @@ using namespace R2;
// use the R2 namespace for game funcs
namespace R2
{
- server_state_t* g_pServerState;
- void* (*Server_GetEntityByIndex)(int index);
+ CBaseEntity* (*Server_GetEntityByIndex)(int index);
CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex);
} // namespace R2
-ON_DLL_LOAD("engine.dll", R2EngineServer, (HMODULE baseAddress))
-{
- g_pServerState = (server_state_t*)((char*)baseAddress + 0x12A53D48);
-}
-
ON_DLL_LOAD("server.dll", R2GameServer, (HMODULE baseAddress))
{
- Server_GetEntityByIndex = (void*(*)(int))((char*)baseAddress + 0xFB820);
+ Server_GetEntityByIndex = (CBaseEntity*(*)(int))((char*)baseAddress + 0xFB820);
UTIL_PlayerByIndex = (CBasePlayer*(__fastcall*)(int))((char*)baseAddress + 0x26AA10);
} \ No newline at end of file
diff --git a/NorthstarDLL/r2server.h b/NorthstarDLL/r2server.h
index c032d722..9d8cbdde 100644
--- a/NorthstarDLL/r2server.h
+++ b/NorthstarDLL/r2server.h
@@ -3,55 +3,10 @@
// use the R2 namespace for game funcs
namespace R2
{
- enum server_state_t
- {
- ss_dead = 0, // Dead
- ss_loading, // Spawning
- ss_active, // Running
- ss_paused, // Running, but paused
- };
-
- extern server_state_t* g_pServerState;
-
// server entity stuff
- extern void* (*Server_GetEntityByIndex)(int index);
-
- const int PERSISTENCE_MAX_SIZE = 0xD000;
-
- enum class ePersistenceReady : char
- {
- NOT_READY,
- READY = 3,
- READY_LOCAL = 3,
- READY_REMOTE
- };
-
- #pragma pack(push, 1)
- struct CBasePlayer
- {
- char pad0[0x16];
-
- // +0x16
- char m_Name[64];
- // +0x56
-
- char pad1[0x44A];
-
- // +0x4A0
- ePersistenceReady m_iPersistenceReady;
- // +0x4A1
-
- char pad2[0x59];
-
- // +0x4FA
- char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE];
-
- char pad3[0x2006];
-
- // +0xF500
- char m_UID[32];
- };
- #pragma pack(pop)
+ class CBaseEntity;
+ extern CBaseEntity* (*Server_GetEntityByIndex)(int index);
+ class CBasePlayer;
extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex);
} // namespace R2 \ No newline at end of file
diff --git a/NorthstarDLL/rpakfilesystem.cpp b/NorthstarDLL/rpakfilesystem.cpp
index 4d35aa8e..28d8b101 100644
--- a/NorthstarDLL/rpakfilesystem.cpp
+++ b/NorthstarDLL/rpakfilesystem.cpp
@@ -189,8 +189,7 @@ void*, , (const char* pPath, void* a2))
// need to allocate a new string for this
std::string newPath = (modFile->second.m_pOwningMod->m_ModDirectory / "mod" / modFile->second.m_Path).string();
allocatedNewPath = new char[newPath.size() + 1];
- strncpy(allocatedNewPath, newPath.c_str(), newPath.size());
- allocatedNewPath[newPath.size()] = '\0';
+ strncpy_s(allocatedNewPath, newPath.size() + 1, newPath.c_str(), newPath.size());
pPath = allocatedNewPath;
}
}
diff --git a/NorthstarDLL/runframe.cpp b/NorthstarDLL/runframe.cpp
new file mode 100644
index 00000000..ac0f3055
--- /dev/null
+++ b/NorthstarDLL/runframe.cpp
@@ -0,0 +1,18 @@
+#include "pch.h"
+#include "r2engine.h"
+#include "r2server.h"
+#include "hoststate.h"
+#include "serverpresence.h"
+
+AUTOHOOK_INIT()
+
+AUTOHOOK(CEngine__Frame, engine.dll + 0x1C8650,
+void, , (R2::CEngine* self))
+{
+ CEngine__Frame(self);
+}
+
+ON_DLL_LOAD("engine.dll", RunFrame, (HMODULE baseAddress))
+{
+ AUTOHOOK_DISPATCH()
+} \ No newline at end of file
diff --git a/NorthstarDLL/serverauthentication.cpp b/NorthstarDLL/serverauthentication.cpp
index 350999fb..3fe6b10b 100644
--- a/NorthstarDLL/serverauthentication.cpp
+++ b/NorthstarDLL/serverauthentication.cpp
@@ -4,6 +4,7 @@
#include "cvar.h"
#include "convar.h"
#include "masterserver.h"
+#include "serverpresence.h"
#include "hoststate.h"
#include "bansystem.h"
#include "concommand.h"
@@ -36,6 +37,7 @@ void ServerAuthenticationManager::StartPlayerAuthServer()
return;
}
+ g_pServerPresence->SetAuthPort(Cvar_ns_player_auth_port->GetInt()); // set auth port for presence
m_bRunningPlayerAuthThread = true;
// listen is a blocking call so thread this
@@ -65,11 +67,11 @@ void ServerAuthenticationManager::StartPlayerAuthServer()
}
RemoteAuthData newAuthData {};
- strncpy(newAuthData.uid, request.get_param_value("id").c_str(), sizeof(newAuthData.uid));
- newAuthData.uid[sizeof(newAuthData.uid) - 1] = 0;
-
- strncpy(newAuthData.username, request.get_param_value("username").c_str(), sizeof(newAuthData.username));
- newAuthData.username[sizeof(newAuthData.username) - 1] = 0;
+ strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), request.get_param_value("id").c_str(), sizeof(newAuthData.uid) - 1);
+ strncpy_s(
+ newAuthData.username,
+ sizeof(newAuthData.username), request.get_param_value("username").c_str(),
+ sizeof(newAuthData.username) - 1);
newAuthData.pdataSize = request.body.size();
newAuthData.pdata = new char[newAuthData.pdataSize];
@@ -99,16 +101,16 @@ void ServerAuthenticationManager::StopPlayerAuthServer()
m_PlayerAuthServer.stop();
}
-void ServerAuthenticationManager::AddPlayerData(R2::CBasePlayer* player, const char* pToken)
+void ServerAuthenticationManager::AddPlayerData(R2::CBaseClient* player, const char* pToken)
{
PlayerAuthenticationData additionalData;
additionalData.pdataSize = m_RemoteAuthenticationData[pToken].pdataSize;
- additionalData.usingLocalPdata = player->m_iPersistenceReady == R2::ePersistenceReady::READY_LOCAL;
+ additionalData.usingLocalPdata = player->m_iPersistenceReady == R2::ePersistenceReady::READY_INSECURE;
m_PlayerAuthenticationData.insert(std::make_pair(player, additionalData));
}
-void ServerAuthenticationManager::VerifyPlayerName(R2::CBasePlayer* player, char* authToken, char* name)
+void ServerAuthenticationManager::VerifyPlayerName(R2::CBaseClient* player, char* authToken, char* name)
{
std::lock_guard<std::mutex> guard(m_AuthDataMutex);
@@ -122,13 +124,12 @@ void ServerAuthenticationManager::VerifyPlayerName(R2::CBasePlayer* player, char
{
// limit name length to 64 characters just in case something changes, this technically shouldn't be needed given the master
// server gets usernames from origin but we have it just in case
- strncpy(name, authData.username, 64);
- name[63] = 0;
+ strncpy_s(name, 64, authData.username, 63);
}
}
}
-bool ServerAuthenticationManager::AuthenticatePlayer(R2::CBasePlayer* player, uint64_t uid, char* authToken)
+bool ServerAuthenticationManager::AuthenticatePlayer(R2::CBaseClient* player, uint64_t uid, char* authToken)
{
std::string strUid = std::to_string(uid);
std::lock_guard<std::mutex> guard(m_AuthDataMutex);
@@ -141,29 +142,11 @@ bool ServerAuthenticationManager::AuthenticatePlayer(R2::CBasePlayer* player, ui
if (!strcmp(strUid.c_str(), authData.uid)) // connecting client's uid is the same as auth's uid
{
- authFail = false;
- // uuid
+ // copy uuid
strcpy(player->m_UID, strUid.c_str());
- // reset from disk if we're doing that
- if (m_bForceReadLocalPlayerPersistenceFromDisk && !strcmp(authData.uid, R2::g_pLocalPlayerUserID))
- {
- std::fstream pdataStream(GetNorthstarPrefix() + "/placeholder_playerdata.pdata", std::ios_base::in);
-
- if (!pdataStream.fail())
- {
- // get file length
- pdataStream.seekg(0, pdataStream.end);
- auto length = pdataStream.tellg();
- pdataStream.seekg(0, pdataStream.beg);
-
- // copy pdata into buffer
- pdataStream.read(player->m_PersistenceBuffer, length);
- }
- else // fallback to remote pdata if no local default
- memcpy(player->m_PersistenceBuffer, authData.pdata, authData.pdataSize);
- }
- else
+ // if we're resetting let script handle the reset
+ if (!m_bForceResetLocalPlayerPersistence || strcmp(authData.uid, R2::g_pLocalPlayerUserID))
{
// copy pdata into buffer
memcpy(player->m_PersistenceBuffer, authData.pdata, authData.pdataSize);
@@ -171,47 +154,30 @@ bool ServerAuthenticationManager::AuthenticatePlayer(R2::CBasePlayer* player, ui
// set persistent data as ready
player->m_iPersistenceReady = R2::ePersistenceReady::READY_REMOTE;
+ authFail = false;
}
}
if (authFail)
{
- // set persistent data as ready
- player->m_iPersistenceReady = R2::ePersistenceReady::READY_LOCAL;
- return CVar_ns_auth_allow_insecure->GetBool();
-
-
- // logic needs an overhaul as it is bad and shit
- // todo: if need default pdata, we should populate in script using InitPersistentData() and just null it out here
-
- // insecure connections are allowed, try reading from disk
- // uuid
- /* strcpy((char*)player + 0xF500, strUid.c_str());
-
- // try reading pdata file for player
- std::string pdataPath = GetNorthstarPrefix() + "/playerdata_";
- pdataPath += strUid;
- pdataPath += ".pdata";
-
- std::fstream pdataStream(pdataPath, std::ios_base::in);
- if (pdataStream.fail()) // file doesn't exist, use placeholder
- pdataStream = std::fstream(GetNorthstarPrefix() + "/placeholder_playerdata.pdata");
-
- // get file length
- pdataStream.seekg(0, pdataStream.end);
- auto length = pdataStream.tellg();
- pdataStream.seekg(0, pdataStream.beg);
-
- // copy pdata into buffer
- pdataStream.read((char*)player + 0x4FA, length);
+ if (CVar_ns_auth_allow_insecure->GetBool())
+ {
+ // copy uuid
+ strcpy((char*)player + 0xF500, strUid.c_str());
- pdataStream.close();*/
+ // set persistent data as ready
+ // note: actual persistent data is populated in script with InitPersistentData()
+ player->m_iPersistenceReady = R2::ePersistenceReady::READY_INSECURE;
+ return true;
+ }
+ else
+ return false;
}
return true; // auth successful, client stays on
}
-bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBasePlayer* player)
+bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBaseClient* player)
{
if (!Cvar_ns_erase_auth_info->GetBool()) // keep auth data forever
return false;
@@ -238,7 +204,7 @@ bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBasePlayer* player)
return false;
}
-void ServerAuthenticationManager::WritePersistentData(R2::CBasePlayer* player)
+void ServerAuthenticationManager::WritePersistentData(R2::CBaseClient* player)
{
if (player->m_iPersistenceReady == R2::ePersistenceReady::READY_REMOTE)
{
@@ -286,7 +252,7 @@ void*,, (
}
AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740,
-bool,, (R2::CBasePlayer* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7))
+bool,, (R2::CBaseClient* self, char* name, __int64 netchan_ptr_arg, char b_fake_player_arg, __int64 a5, char* Buffer, void* a7))
{
// try changing name before all else
g_pServerAuthentication->VerifyPlayerName(self, pNextPlayerToken, name);
@@ -294,11 +260,10 @@ bool,, (R2::CBasePlayer* self, char* name, __int64 netchan_ptr_arg, char b_fake_
// try to auth player, dc if it fails
// we connect regardless of auth, because returning bad from this function can fuck client state p bad
bool ret = CBaseClient__Connect(self, name, netchan_ptr_arg, b_fake_player_arg, a5, Buffer, a7);
-
if (!ret)
return ret;
- if (!g_pServerBanSystem->IsUIDAllowed(iNextPlayerUid))
+ if (!g_pBanSystem->IsUIDAllowed(iNextPlayerUid))
{
R2::CBaseClient__Disconnect(self, 1, "Banned from server");
return ret;
@@ -318,23 +283,23 @@ bool,, (R2::CBasePlayer* self, char* name, __int64 netchan_ptr_arg, char b_fake_
}
AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80,
-void,, (R2::CBasePlayer* self))
+void,, (R2::CBaseClient* self))
{
// if we're authed, write our persistent data
// RemovePlayerAuthData returns true if it removed successfully, i.e. on first call only, and we only want to write on >= second call
// (since this func is called on map loads)
if (self->m_iPersistenceReady >= R2::ePersistenceReady::READY && !g_pServerAuthentication->RemovePlayerAuthData(self))
{
- g_pServerAuthentication->m_bForceReadLocalPlayerPersistenceFromDisk = false;
+ g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = false;
g_pServerAuthentication->WritePersistentData(self);
- g_pMasterServerManager->UpdateServerPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
+ g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
}
CBaseClient__ActivatePlayer(self);
}
AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0,
-void,, (R2::CBasePlayer* self, uint32_t unknownButAlways1, const char* pReason, ...))
+void,, (R2::CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...))
{
// have to manually format message because can't pass varargs to original func
char buf[1024];
@@ -358,7 +323,7 @@ void,, (R2::CBasePlayer* self, uint32_t unknownButAlways1, const char* pReason,
if (g_pServerAuthentication->m_PlayerAuthenticationData.count(self))
{
g_pServerAuthentication->m_PlayerAuthenticationData.erase(self);
- g_pMasterServerManager->UpdateServerPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
+ g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
}
_CBaseClient__Disconnect(self, unknownButAlways1, buf);
@@ -373,10 +338,10 @@ void ConCommand_ns_resetpersistence(const CCommand& args)
}
spdlog::info("resetting persistence on next lobby load...");
- g_pServerAuthentication->m_bForceReadLocalPlayerPersistenceFromDisk = true;
+ g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = true;
}
-ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, ConCommand, (HMODULE baseAddress))
+ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), (HMODULE baseAddress))
{
AUTOHOOK_DISPATCH()
diff --git a/NorthstarDLL/serverauthentication.h b/NorthstarDLL/serverauthentication.h
index 86b4594b..31710ae1 100644
--- a/NorthstarDLL/serverauthentication.h
+++ b/NorthstarDLL/serverauthentication.h
@@ -1,7 +1,7 @@
#pragma once
#include "convar.h"
#include "httplib.h"
-#include "r2server.h"
+#include "r2engine.h"
#include <unordered_map>
#include <string>
@@ -35,19 +35,19 @@ class ServerAuthenticationManager
std::mutex m_AuthDataMutex;
std::unordered_map<std::string, RemoteAuthData> m_RemoteAuthenticationData;
- std::unordered_map<R2::CBasePlayer*, PlayerAuthenticationData> m_PlayerAuthenticationData;
+ std::unordered_map<R2::CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData;
bool m_bRunningPlayerAuthThread = false;
bool m_bNeedLocalAuthForNewgame = false;
- bool m_bForceReadLocalPlayerPersistenceFromDisk = false;
+ bool m_bForceResetLocalPlayerPersistence = false;
public:
void StartPlayerAuthServer();
void StopPlayerAuthServer();
- void AddPlayerData(R2::CBasePlayer* player, const char* pToken);
- bool AuthenticatePlayer(R2::CBasePlayer* player, uint64_t uid, char* authToken);
- void VerifyPlayerName(R2::CBasePlayer* player, char* authToken, char* name);
- bool RemovePlayerAuthData(R2::CBasePlayer* player);
- void WritePersistentData(R2::CBasePlayer* player);
+ void AddPlayerData(R2::CBaseClient* player, const char* pToken);
+ bool AuthenticatePlayer(R2::CBaseClient* player, uint64_t uid, char* authToken);
+ void VerifyPlayerName(R2::CBaseClient* player, char* authToken, char* name);
+ bool RemovePlayerAuthData(R2::CBaseClient* player);
+ void WritePersistentData(R2::CBaseClient* player);
};
extern ServerAuthenticationManager* g_pServerAuthentication;
diff --git a/NorthstarDLL/serverchathooks.cpp b/NorthstarDLL/serverchathooks.cpp
index 000c6eea..da7d34dd 100644
--- a/NorthstarDLL/serverchathooks.cpp
+++ b/NorthstarDLL/serverchathooks.cpp
@@ -48,10 +48,8 @@ void,, (CServerGameDLL* self, unsigned int senderPlayerId, const char* text, boo
return;
}
- R2::CBasePlayer* sender = R2::UTIL_PlayerByIndex(senderPlayerId - 1);
-
// check chat ratelimits
- if (!g_pServerLimits->CheckChatLimits(sender))
+ if (!g_pServerLimits->CheckChatLimits(&R2::g_pClientArray[senderPlayerId - 1]))
return;
if (g_pServerSquirrel->setupfunc("CServerGameDLL_ProcessMessageStartThread") != SQRESULT_ERROR)
@@ -89,8 +87,7 @@ void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* te
// Build a new string where the first byte is the message type
char sendText[256];
sendText[0] = (char)messageType;
- strncpy(sendText + 1, text, 255);
- sendText[255] = 0;
+ strncpy_s(sendText + 1, 255, text, 254);
// Anonymous custom messages use playerId=0, non-anonymous ones use a player ID with the first bit set
unsigned int fromPlayerId = fromPlayerIndex < 0 ? 0 : ((fromPlayerIndex + 1) | CUSTOM_MESSAGE_INDEX_BIT);
diff --git a/NorthstarDLL/serverpresence.cpp b/NorthstarDLL/serverpresence.cpp
new file mode 100644
index 00000000..3db39c82
--- /dev/null
+++ b/NorthstarDLL/serverpresence.cpp
@@ -0,0 +1,207 @@
+#include "pch.h"
+#include "serverpresence.h"
+#include "playlist.h"
+#include "tier0.h"
+#include "convar.h"
+
+#include <regex>
+
+ServerPresenceManager* g_pServerPresence;
+
+ConVar* Cvar_hostname;
+
+// Convert a hex digit char to integer.
+inline int hctod(char c)
+{
+ if (c >= 'A' && c <= 'F')
+ {
+ return c - 'A' + 10;
+ }
+ else if (c >= 'a' && c <= 'f')
+ {
+ return c - 'a' + 10;
+ }
+ else
+ {
+ return c - '0';
+ }
+}
+
+// This function interprets all 4-hexadecimal-digit unicode codepoint characters like \u4E2D to UTF-8 encoding.
+std::string UnescapeUnicode(const std::string& str)
+{
+ std::string result;
+
+ std::regex r("\\\\u([a-f\\d]{4})", std::regex::icase);
+ auto matches_begin = std::sregex_iterator(str.begin(), str.end(), r);
+ auto matches_end = std::sregex_iterator();
+ std::smatch last_match;
+
+ for (std::sregex_iterator i = matches_begin; i != matches_end; ++i)
+ {
+ last_match = *i;
+ result.append(last_match.prefix());
+ unsigned int cp = 0;
+ for (int i = 2; i <= 5; ++i)
+ {
+ cp *= 16;
+ cp += hctod(last_match.str()[i]);
+ }
+ if (cp <= 0x7F)
+ {
+ result.push_back(cp);
+ }
+ else if (cp <= 0x7FF)
+ {
+ result.push_back((cp >> 6) | 0b11000000 & (~(1 << 5)));
+ result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
+ }
+ else if (cp <= 0xFFFF)
+ {
+ result.push_back((cp >> 12) | 0b11100000 & (~(1 << 4)));
+ result.push_back((cp >> 6) & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
+ result.push_back(cp & ((1 << 6) - 1) | 0b10000000 & (~(1 << 6)));
+ }
+ }
+
+ if (!last_match.ready())
+ return str;
+ else
+ result.append(last_match.suffix());
+
+ return result;
+}
+
+ServerPresenceManager::ServerPresenceManager()
+{
+ // register convars
+ Cvar_ns_server_presence_update_rate = new ConVar(
+ "ns_server_presence_update_rate", "5000", FCVAR_GAMEDLL, "How often we update our server's presence on server lists in ms");
+
+ Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "This server's description", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
+ g_pServerPresence->SetName(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_name->GetString()));
+
+ // update engine hostname cvar
+ Cvar_hostname->SetValue(g_pServerPresence->Cvar_ns_server_name->GetString());
+ });
+
+ Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "This server's name", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
+ g_pServerPresence->SetDescription(UnescapeUnicode(g_pServerPresence->Cvar_ns_server_desc->GetString()));
+ });
+
+ Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "This server's password", false, 0, false, 0, [](ConVar* cvar, const char* pOldValue, float flOldValue) {
+ g_pServerPresence->SetPassword(g_pServerPresence->Cvar_ns_server_password->GetString());
+ });
+
+ Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "Whether we should report this server to the masterserver");
+ Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "Whether we should report this server to the masterserver, when started in singleplayer");
+}
+
+void ServerPresenceManager::AddPresenceReporter(ServerPresenceReporter* reporter)
+{
+ m_vPresenceReporters.push_back(reporter);
+}
+
+void ServerPresenceManager::CreatePresence()
+{
+ // reset presence fields that rely on runtime server state
+ // these being: port/auth port, map/playlist name, and playercount/maxplayers
+ m_ServerPresence.m_iPort = 0;
+ m_ServerPresence.m_iAuthPort = 0;
+
+ m_ServerPresence.m_iPlayerCount = 0; // this should actually be 0 at this point, so shouldn't need updating later
+ m_ServerPresence.m_iMaxPlayers = 0;
+
+ memset(m_ServerPresence.m_MapName, 0, sizeof(m_ServerPresence.m_MapName));
+ memset(m_ServerPresence.m_PlaylistName, 0, sizeof(m_ServerPresence.m_PlaylistName));
+
+ m_bHasPresence = true;
+
+ // code that's calling this should set up the reset fields at this point
+}
+
+void ServerPresenceManager::DestroyPresence()
+{
+ m_bHasPresence = false;
+
+ for (ServerPresenceReporter* reporter : m_vPresenceReporters)
+ reporter->DestroyPresence(&m_ServerPresence);
+}
+
+void ServerPresenceManager::RunFrame(double flCurrentTime)
+{
+ if (!m_bHasPresence) // don't run until we actually have server presence
+ return;
+
+ // run on a specified delay
+ if ((flCurrentTime - m_flLastPresenceUpdate) * 1000 < Cvar_ns_server_presence_update_rate->GetFloat())
+ return;
+
+ m_flLastPresenceUpdate = flCurrentTime;
+
+ for (ServerPresenceReporter* reporter : m_vPresenceReporters)
+ reporter->ReportPresence(&m_ServerPresence);
+}
+
+void ServerPresenceManager::SetPort(const int iPort)
+{
+ // update port
+ m_ServerPresence.m_iPort = iPort;
+}
+
+void ServerPresenceManager::SetAuthPort(const int iAuthPort)
+{
+ // update authport
+ m_ServerPresence.m_iAuthPort = iAuthPort;
+}
+
+void ServerPresenceManager::SetName(const std::string sServerNameUnicode)
+{
+ // update name
+ m_ServerPresence.m_sServerName = sServerNameUnicode;
+}
+
+void ServerPresenceManager::SetDescription(const std::string sServerDescUnicode)
+{
+ // update desc
+ m_ServerPresence.m_sServerDesc = sServerDescUnicode;
+}
+
+void ServerPresenceManager::SetPassword(const char* pPassword)
+{
+ // update password
+ strncpy_s(m_ServerPresence.m_Password, sizeof(m_ServerPresence.m_Password), pPassword, sizeof(m_ServerPresence.m_Password) - 1);
+}
+
+void ServerPresenceManager::SetMap(const char* pMapName)
+{
+ // update map
+ strncpy_s(m_ServerPresence.m_MapName, sizeof(m_ServerPresence.m_MapName), pMapName, sizeof(m_ServerPresence.m_MapName) - 1);
+}
+
+void ServerPresenceManager::SetPlaylist(const char* pPlaylistName)
+{
+ // update playlist
+ strncpy_s(m_ServerPresence.m_PlaylistName, sizeof(m_ServerPresence.m_PlaylistName), pPlaylistName, sizeof(m_ServerPresence.m_PlaylistName) - 1);
+
+ // update maxplayers
+ const char* pMaxPlayers = R2::GetCurrentPlaylistVar("max_players", true);
+
+ // can be null in some situations, so default 6
+ if (pMaxPlayers)
+ m_ServerPresence.m_iMaxPlayers = std::stoi(pMaxPlayers);
+ else
+ m_ServerPresence.m_iMaxPlayers = 6;
+}
+
+void ServerPresenceManager::SetPlayerCount(const int iPlayerCount)
+{
+ m_ServerPresence.m_iPlayerCount = iPlayerCount;
+}
+
+ON_DLL_LOAD_RELIESON("engine.dll", ServerPresence, ConVar, (HMODULE baseAddress))
+{
+ g_pServerPresence = new ServerPresenceManager;
+
+ Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8);
+} \ No newline at end of file
diff --git a/NorthstarDLL/serverpresence.h b/NorthstarDLL/serverpresence.h
new file mode 100644
index 00000000..c6f18846
--- /dev/null
+++ b/NorthstarDLL/serverpresence.h
@@ -0,0 +1,88 @@
+#pragma once
+#include "convar.h"
+
+struct ServerPresence
+{
+ int m_iPort;
+ int m_iAuthPort;
+
+ std::string m_sServerName;
+ std::string m_sServerDesc;
+ char m_Password[256]; // probably bigger than will ever be used in practice, lol
+
+ char m_MapName[32];
+ char m_PlaylistName[64];
+
+ int m_iPlayerCount;
+ int m_iMaxPlayers;
+
+ ServerPresence()
+ {
+ memset(this, 0, sizeof(this));
+ }
+
+ ServerPresence(const ServerPresence* obj)
+ {
+ m_iPort = obj->m_iPort;
+ m_iAuthPort = obj->m_iAuthPort;
+
+ m_sServerName = obj->m_sServerName;
+ m_sServerDesc = obj->m_sServerDesc;
+ memcpy(m_Password, obj->m_Password, sizeof(m_Password));
+
+ memcpy(m_MapName, obj->m_MapName, sizeof(m_MapName));
+ memcpy(m_PlaylistName, obj->m_PlaylistName, sizeof(m_PlaylistName));
+
+ m_iPlayerCount = obj->m_iPlayerCount;
+ m_iMaxPlayers = obj->m_iMaxPlayers;
+ }
+};
+
+class ServerPresenceReporter
+{
+ public:
+ virtual void ReportPresence(const ServerPresence* pServerPresence) {}
+ virtual void DestroyPresence(const ServerPresence* pServerPresence) {}
+};
+
+class ServerPresenceManager
+{
+ private:
+ ServerPresence m_ServerPresence;
+
+ bool m_bHasPresence = false;
+
+ std::vector<ServerPresenceReporter*> m_vPresenceReporters;
+
+ double m_flLastPresenceUpdate = 0;
+ ConVar* Cvar_ns_server_presence_update_rate;
+
+ ConVar* Cvar_ns_server_name;
+ ConVar* Cvar_ns_server_desc;
+ ConVar* Cvar_ns_server_password;
+
+ ConVar* Cvar_ns_report_server_to_masterserver;
+ ConVar* Cvar_ns_report_sp_server_to_masterserver;
+
+ public:
+ ServerPresenceManager();
+
+ void AddPresenceReporter(ServerPresenceReporter* reporter);
+
+ void CreatePresence();
+ void DestroyPresence();
+ void RunFrame(double flCurrentTime);
+
+ void SetPort(const int iPort);
+ void SetAuthPort(const int iPort);
+
+ void SetName(const std::string sServerNameUnicode);
+ void SetDescription(const std::string sServerDescUnicode);
+ void SetPassword(const char* pPassword);
+
+ void SetMap(const char* pMapName);
+ void SetPlaylist(const char* pPlaylistName);
+ void SetPlayerCount(const int iPlayerCount);
+};
+
+extern ServerPresenceManager* g_pServerPresence;
diff --git a/NorthstarDLL/squirrel.cpp b/NorthstarDLL/squirrel.cpp
index 2db944e8..e2333dad 100644
--- a/NorthstarDLL/squirrel.cpp
+++ b/NorthstarDLL/squirrel.cpp
@@ -2,6 +2,7 @@
#include "squirrel.h"
#include "concommand.h"
#include "modmanager.h"
+#include "dedicated.h"
#include "r2engine.h"
#include "NSMem.h"
@@ -130,14 +131,20 @@ template <ScriptContext context> void ScriptCompileErrorHook(void* sqvm, const c
// ideally we'd just check if the sqvm was fully initialised here, somehow
if (strcmp(file, "console") && strcmp(file, "unnamedbuffer"))
{
- R2::Cbuf_AddText(
- R2::Cbuf_GetCurrentPlayer(),
- fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext))
- .c_str(),
- R2::cmd_source_t::kCommandSrcCode);
-
- if (realContext == ScriptContext::UI) // likely temp: show console so user can see any errors, as error message wont display if ui is dead
- R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "showconsole", R2::cmd_source_t::kCommandSrcCode);
+ // kill dedicated server if we hit this
+ if (IsDedicatedServer())
+ abort();
+ else
+ {
+ R2::Cbuf_AddText(
+ R2::Cbuf_GetCurrentPlayer(),
+ fmt::format("disconnect \"Encountered {} script compilation error, see console for details.\"", GetContextName(realContext))
+ .c_str(),
+ R2::cmd_source_t::kCommandSrcCode);
+
+ if (realContext == ScriptContext::UI) // likely temp: show console so user can see any errors, as error message wont display if ui is dead
+ R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "showconsole", R2::cmd_source_t::kCommandSrcCode);
+ }
}
// dont call the original function since it kills game lol