aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDedicatedTest
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDedicatedTest')
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj2
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters6
-rw-r--r--NorthstarDedicatedTest/chatcommand.cpp7
-rw-r--r--NorthstarDedicatedTest/dllmain.cpp3
-rw-r--r--NorthstarDedicatedTest/filesystem.cpp1
-rw-r--r--NorthstarDedicatedTest/gameutils.cpp19
-rw-r--r--NorthstarDedicatedTest/gameutils.h30
-rw-r--r--NorthstarDedicatedTest/masterserver.cpp304
-rw-r--r--NorthstarDedicatedTest/masterserver.h15
-rw-r--r--NorthstarDedicatedTest/modlocalisation.cpp38
-rw-r--r--NorthstarDedicatedTest/modlocalisation.h3
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp12
-rw-r--r--NorthstarDedicatedTest/modmanager.h2
-rw-r--r--NorthstarDedicatedTest/pch.h1
-rw-r--r--NorthstarDedicatedTest/scriptserverbrowser.cpp50
-rw-r--r--NorthstarDedicatedTest/serverauthentication.cpp27
-rw-r--r--NorthstarDedicatedTest/serverauthentication.h5
-rw-r--r--NorthstarDedicatedTest/squirrel.cpp4
18 files changed, 443 insertions, 86 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
index 234894fa..b6ceebf1 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
@@ -310,6 +310,7 @@
<ClInclude Include="logging.h" />
<ClInclude Include="main.h" />
<ClInclude Include="masterserver.h" />
+ <ClInclude Include="modlocalisation.h" />
<ClInclude Include="modmanager.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="scriptmodmenu.h" />
@@ -334,6 +335,7 @@
<ClCompile Include="hooks.cpp" />
<ClCompile Include="hookutils.cpp" />
<ClCompile Include="keyvalues.cpp" />
+ <ClCompile Include="modlocalisation.cpp" />
<ClCompile Include="logging.cpp" />
<ClCompile Include="masterserver.cpp" />
<ClCompile Include="modmanager.cpp" />
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
index 0da3d860..041c8e84 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
@@ -561,6 +561,9 @@
<ClInclude Include="chatcommand.h">
<Filter>Header Files\Client</Filter>
</ClInclude>
+ <ClInclude Include="modlocalisation.h">
+ <Filter>Header Files\Client</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
@@ -635,6 +638,9 @@
<ClCompile Include="chatcommand.cpp">
<Filter>Source Files\Client</Filter>
</ClCompile>
+ <ClCompile Include="modlocalisation.cpp">
+ <Filter>Source Files\Client</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="include\spdlog\fmt\bundled\LICENSE.rst">
diff --git a/NorthstarDedicatedTest/chatcommand.cpp b/NorthstarDedicatedTest/chatcommand.cpp
index 866a1d79..4742b5fd 100644
--- a/NorthstarDedicatedTest/chatcommand.cpp
+++ b/NorthstarDedicatedTest/chatcommand.cpp
@@ -2,19 +2,20 @@
#include "chatcommand.h"
#include "concommand.h"
-typedef void(__fastcall *ClientSayTextType)(void* a1, const char* message, char unknownAlways1, bool isTeamChat);
+// note: isIngameChat is an int64 because the whole register the arg is stored in needs to be 0'd out to work
+typedef void(__fastcall *ClientSayTextType)(void* a1, const char* message, __int64 isIngameChat, bool isTeamChat);
ClientSayTextType SayText;
void ConCommand_say(const CCommand& args)
{
if (args.ArgC() >= 2)
- SayText(nullptr, args.Arg(1), 0, false);
+ SayText(nullptr, args.ArgS(), true, false);
}
void ConCommand_say_team(const CCommand& args)
{
if (args.ArgC() >= 2)
- SayText(nullptr, args.Arg(1), 0, true);
+ SayText(nullptr, args.ArgS(), true, true);
}
void InitialiseChatCommands(HMODULE baseAddress)
diff --git a/NorthstarDedicatedTest/dllmain.cpp b/NorthstarDedicatedTest/dllmain.cpp
index f5118900..8cea40a0 100644
--- a/NorthstarDedicatedTest/dllmain.cpp
+++ b/NorthstarDedicatedTest/dllmain.cpp
@@ -15,6 +15,7 @@
#include "masterserver.h"
#include "gameutils.h"
#include "chatcommand.h"
+#include "modlocalisation.h"
bool initialised = false;
@@ -60,11 +61,11 @@ void InitialiseNorthstar()
if (!IsDedicated())
{
AddDllLoadCallback("client.dll", InitialiseClientSquirrel);
-
AddDllLoadCallback("client.dll", InitialiseSourceConsole);
AddDllLoadCallback("engine.dll", InitialiseChatCommands);
AddDllLoadCallback("client.dll", InitialiseScriptModMenu);
AddDllLoadCallback("client.dll", InitialiseScriptServerBrowser);
+ AddDllLoadCallback("localize.dll", InitialiseModLocalisation);
}
AddDllLoadCallback("server.dll", InitialiseServerSquirrel);
diff --git a/NorthstarDedicatedTest/filesystem.cpp b/NorthstarDedicatedTest/filesystem.cpp
index 15b8b4dd..458cfd5c 100644
--- a/NorthstarDedicatedTest/filesystem.cpp
+++ b/NorthstarDedicatedTest/filesystem.cpp
@@ -28,6 +28,7 @@ FileHandle_t ReadFileFromFilesystemHook(IFileSystem* filesystem, const char* pPa
bool readingOriginalFile;
std::string currentModPath;
SourceInterface<IFileSystem>* g_Filesystem;
+void* g_pVGuiLocalize;
void InitialiseFilesystem(HMODULE baseAddress)
{
diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp
index 9d61ab0a..1cf919fb 100644
--- a/NorthstarDedicatedTest/gameutils.cpp
+++ b/NorthstarDedicatedTest/gameutils.cpp
@@ -1,13 +1,28 @@
#include "pch.h"
#include "gameutils.h"
+#include "convar.h"
// cmd.h
-
Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer;
Cbuf_AddTextType Cbuf_AddText;
+// hoststate stuff
+CHostState* g_GameCHostStateSingleton;
+
+// network stuff
+ConVar* Cvar_hostport;
+
+// playlist stuff
+GetCurrentPlaylistType GetCurrentPlaylistName;
+
void InitialiseEngineGameUtilFunctions(HMODULE baseAddress)
{
Cbuf_GetCurrentPlayer = (Cbuf_GetCurrentPlayerType)((char*)baseAddress + 0x120630);
Cbuf_AddText = (Cbuf_AddTextType)((char*)baseAddress + 0x1203B0);
-} \ No newline at end of file
+
+ g_GameCHostStateSingleton = (CHostState*)((char*)baseAddress + 0x7CF180);
+
+ Cvar_hostport = (ConVar*)((char*)baseAddress + 0x1FA6070);
+
+ GetCurrentPlaylistName = (GetCurrentPlaylistType)((char*)baseAddress + 0x18C640);
+}
diff --git a/NorthstarDedicatedTest/gameutils.h b/NorthstarDedicatedTest/gameutils.h
index 4c2254e7..042e9bd5 100644
--- a/NorthstarDedicatedTest/gameutils.h
+++ b/NorthstarDedicatedTest/gameutils.h
@@ -1,4 +1,5 @@
#pragma once
+#include "convar.h"
// cmd.h
@@ -54,4 +55,33 @@ extern Cbuf_GetCurrentPlayerType Cbuf_GetCurrentPlayer;
typedef void(*Cbuf_AddTextType)(ECommandTarget_t eTarget, const char* text, cmd_source_t source);
extern Cbuf_AddTextType Cbuf_AddText;
+
+// hoststate stuff
+
+struct CHostState
+{
+public:
+ int32_t m_iCurrentState;
+ int32_t m_iNextState;
+
+ float m_vecLocation[3];
+ float m_angLocation[3];
+
+ char m_levelName[32];
+
+ // not reversed past this point, struct seems weird
+};
+
+extern CHostState* g_GameCHostStateSingleton;
+
+// network stuff
+
+extern ConVar* Cvar_hostport;
+
+
+// playlist stuff
+
+typedef const char*(*GetCurrentPlaylistType)();
+extern GetCurrentPlaylistType GetCurrentPlaylistName;
+
void InitialiseEngineGameUtilFunctions(HMODULE baseAddress); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp
index d8b765ec..b8d1cd73 100644
--- a/NorthstarDedicatedTest/masterserver.cpp
+++ b/NorthstarDedicatedTest/masterserver.cpp
@@ -1,62 +1,54 @@
#include "pch.h"
#include "masterserver.h"
+#include "concommand.h"
+#include "gameutils.h"
+#include "hookutils.h"
#include "httplib.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
-#include "concommand.h"
-
ConVar* Cvar_ns_masterserver_hostname;
ConVar* Cvar_ns_masterserver_port;
+ConVar* Cvar_ns_report_server_to_masterserver;
+ConVar* Cvar_ns_report_sp_server_to_masterserver;
-MasterServerManager* g_MasterServerManager;
+ConVar* Cvar_ns_server_name;
+ConVar* Cvar_ns_server_desc;
+ConVar* Cvar_ns_server_password;
-// requires password constructor
-RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers)
-{
- // passworded servers don't have public ips
- requiresPassword = true;
- memset(&ip, 0, sizeof(ip));
- port = 0;
+MasterServerManager* g_MasterServerManager;
- strncpy((char*)id, newId, 31);
- id[31] = 0;
- strncpy((char*)name, newName, 63);
- name[63] = 0;
+typedef void(*CHostState__State_NewGameType)(CHostState* hostState);
+CHostState__State_NewGameType CHostState__State_NewGame;
- description = std::string(newDescription);
+typedef void(*CHostState__State_ChangeLevelMPType)(CHostState* hostState);
+CHostState__State_ChangeLevelMPType CHostState__State_ChangeLevelMP;
- strncpy((char*)map, newMap, 31);
- map[31] = 0;
- strncpy((char*)playlist, newPlaylist, 15);
- playlist[15] = 0;
+typedef void(*CHostState__State_ChangeLevelSPType)(CHostState* hostState);
+CHostState__State_ChangeLevelSPType CHostState__State_ChangeLevelSP;
- playerCount = newPlayerCount;
- maxPlayers = newMaxPlayers;
-}
+typedef void(*CHostState__State_GameShutdownType)(CHostState* hostState);
+CHostState__State_GameShutdownType CHostState__State_GameShutdown;
-// doesnt require password constructor
-RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, in_addr newIp, int newPort)
+RemoteServerInfo::RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, bool newRequiresPassword)
{
- requiresPassword = false;
+ // passworded servers don't have public ips
+ requiresPassword = newRequiresPassword;
- strncpy((char*)id, newId, 31);
- id[31] = 0;
- strncpy((char*)name, newName, 63);
- name[63] = 0;
+ strncpy((char*)id, newId, sizeof(id));
+ id[sizeof(id) - 1] = 0;
+ strncpy((char*)name, newName, sizeof(name));
+ name[sizeof(name) - 1] = 0;
description = std::string(newDescription);
- strncpy((char*)map, newMap, 31);
- map[31] = 0;
- strncpy((char*)playlist, newPlaylist, 15);
- playlist[15] = 0;
+ strncpy((char*)map, newMap, sizeof(map));
+ map[sizeof(map) - 1] = 0;
+ strncpy((char*)playlist, newPlaylist, sizeof(playlist));
+ playlist[sizeof(playlist) - 1] = 0;
playerCount = newPlayerCount;
maxPlayers = newMaxPlayers;
-
- ip = newIp;
- port = newPort;
}
void MasterServerManager::ClearServerList()
@@ -142,25 +134,15 @@ void MasterServerManager::RequestServerList()
goto REQUEST_END_CLEANUP;
}
- bool hasPassword = serverObj["hasPassword"].GetBool();
const char* id = serverObj["id"].GetString();
bool createNewServerInfo = true;
for (RemoteServerInfo& server : m_remoteServers)
{
// if server already exists, update info rather than adding to it
- if (!strncmp((const char*)server.id, id, 31))
+ if (!strncmp((const char*)server.id, id, 32))
{
- if (hasPassword)
- server = RemoteServerInfo(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt());
- else
- {
- in_addr addr;
- addr.S_un.S_addr = serverObj["ip"].GetUint64();
-
- server = RemoteServerInfo(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt(), addr, serverObj["port"].GetInt());
- }
-
+ server = RemoteServerInfo(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt(), serverObj["hasPassword"].IsTrue());
createNewServerInfo = false;
break;
}
@@ -168,24 +150,7 @@ void MasterServerManager::RequestServerList()
// server didn't exist
if (createNewServerInfo)
- {
- // passworded servers shouldn't send ip/port
- if (hasPassword)
- m_remoteServers.emplace_back(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt());
- else
- {
- if (!serverObj.HasMember("ip") || !serverObj["ip"].IsUint64() || !serverObj.HasMember("port") || !serverObj["port"].IsNumber())
- {
- spdlog::error("Failed reading masterserver response: malformed server object");
- goto REQUEST_END_CLEANUP;
- }
-
- in_addr addr;
- addr.S_un.S_addr = serverObj["ip"].GetUint64();
-
- m_remoteServers.emplace_back(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt(), addr, serverObj["port"].GetInt());
- }
- }
+ m_remoteServers.emplace_back(id, serverObj["name"].GetString(), serverObj["description"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt(), serverObj["hasPassword"].IsTrue());
spdlog::info("Server {} on map {} with playlist {} has {}/{} players", serverObj["name"].GetString(), serverObj["map"].GetString(), serverObj["playlist"].GetString(), serverObj["playerCount"].GetInt(), serverObj["maxPlayers"].GetInt());
}
@@ -205,7 +170,7 @@ void MasterServerManager::RequestServerList()
requestThread.detach();
}
-void MasterServerManager::TryAuthenticateWithServer(char* serverId, char* password)
+void MasterServerManager::AuthenticateWithServer(char* serverId, char* password)
{
// dont wait, just stop if we're trying to do 2 auth requests at once
if (m_authenticatingWithGameServer)
@@ -222,8 +187,6 @@ void MasterServerManager::TryAuthenticateWithServer(char* serverId, char* passwo
spdlog::info("Attempting authentication with server of id \"{}\"", serverId);
- spdlog::info(fmt::format("/client/auth_with_server?server={}&password={}", serverId, password));
-
if (auto result = http.Post(fmt::format("/client/auth_with_server?server={}&password={}", serverId, password).c_str()))
{
m_successfullyConnected = true;
@@ -256,13 +219,13 @@ void MasterServerManager::TryAuthenticateWithServer(char* serverId, char* passwo
goto REQUEST_END_CLEANUP;
}
- if (!connectionInfoJson.HasMember("success") || !connectionInfoJson.HasMember("ip") || !connectionInfoJson["ip"].IsUint64() || !connectionInfoJson.HasMember("port") || !connectionInfoJson["port"].IsNumber() || !connectionInfoJson.HasMember("authToken") || !connectionInfoJson["authToken"].IsString())
+ if (!connectionInfoJson.HasMember("success") || !connectionInfoJson.HasMember("ip") || !connectionInfoJson["ip"].IsString() || !connectionInfoJson.HasMember("port") || !connectionInfoJson["port"].IsNumber() || !connectionInfoJson.HasMember("authToken") || !connectionInfoJson["authToken"].IsString())
{
spdlog::error("Failed reading masterserver authentication response: malformed json object");
goto REQUEST_END_CLEANUP;
}
- m_pendingConnectionInfo.ip.S_un.S_addr = connectionInfoJson["ip"].GetUint64();
+ m_pendingConnectionInfo.ip.S_un.S_addr = inet_addr(connectionInfoJson["ip"].GetString());
m_pendingConnectionInfo.port = connectionInfoJson["port"].GetInt();
strncpy(m_pendingConnectionInfo.authToken, connectionInfoJson["authToken"].GetString(), 31);
@@ -286,16 +249,215 @@ void MasterServerManager::TryAuthenticateWithServer(char* serverId, char* passwo
requestThread.detach();
}
+void MasterServerManager::AddSelfToServerList(int port, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password)
+{
+ if (!Cvar_ns_report_server_to_masterserver->m_nValue)
+ return;
+
+ if (!Cvar_ns_report_sp_server_to_masterserver->m_nValue && !strncmp(map, "sp_", 3))
+ return;
+
+ std::thread requestThread([this, port, name, description, map, playlist, maxPlayers, password] {
+ httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue);
+ http.set_connection_timeout(20);
+
+ m_ownServerId[0] = 0;
+
+ std::string request;
+ if (*password)
+ request = fmt::format("/server/add_server?port={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}", port, name, description, map, playlist, maxPlayers, password);
+ else
+ request = fmt::format("/server/add_server?port={}&name={}&description={}&map={}&playlist={}&maxPlayers={}", port, name, description, map, playlist, maxPlayers);
+
+ if (auto result = http.Post(request.c_str()))
+ {
+ m_successfullyConnected = true;
+
+ rapidjson::Document serverAddedJson;
+ serverAddedJson.Parse(result->body.c_str());
+
+ if (serverAddedJson.HasParseError())
+ {
+ spdlog::error("Failed reading masterserver authentication response: encountered parse error \"{}\"", rapidjson::GetParseError_En(serverAddedJson.GetParseError()));
+ return;
+ }
+
+ if (!serverAddedJson.IsObject())
+ {
+ spdlog::error("Failed reading masterserver authentication response: root object is not an object");
+ return;
+ }
+
+ if (serverAddedJson.HasMember("error"))
+ {
+ spdlog::error("Failed reading masterserver response: got fastify error response");
+ spdlog::error(result->body);
+ return;
+ }
+
+ if (!serverAddedJson["success"].IsTrue())
+ {
+ spdlog::error("Adding server to masterserver failed: \"success\" is not true");
+ return;
+ }
+
+ if (!serverAddedJson.HasMember("id") || !serverAddedJson["id"].IsString())
+ {
+ spdlog::error("Failed reading masterserver response: malformed json object");
+ return;
+ }
+
+ strncpy(m_ownServerId, serverAddedJson["id"].GetString(), sizeof(m_ownServerId));
+ m_ownServerId[sizeof(m_ownServerId) - 1] = 0;
+
+
+ // heartbeat thread
+ std::thread heartbeatThread([this] {
+ httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue);
+ http.set_connection_timeout(10);
+
+ while (*m_ownServerId)
+ {
+ Sleep(15000);
+ http.Post(fmt::format("/server/heartbeat?id={}", m_ownServerId).c_str());
+ }
+ });
+
+ heartbeatThread.detach();
+ }
+ else
+ {
+ spdlog::error("Failed authenticating with server: error {}", result.error());
+ m_successfullyConnected = false;
+ }
+ });
+
+ requestThread.detach();
+}
+
+void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist)
+{
+ // dont call this if we don't have a server id
+ if (!*m_ownServerId)
+ return;
+
+ std::thread requestThread([this, map, playlist] {
+ httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue);
+ http.set_connection_timeout(10);
+
+ // we dont process this at all atm, maybe do later, but atm not necessary
+ if (auto result = http.Post(fmt::format("/server/update_values?id={}&map={}&playlist={}", m_ownServerId, map, playlist).c_str()))
+ {
+ m_successfullyConnected = true;
+ }
+ else
+ {
+ m_successfullyConnected = false;
+ }
+ });
+
+ requestThread.detach();
+}
+
+void MasterServerManager::UpdateServerPlayerCount(int playerCount)
+{
+ // dont call this if we don't have a server id
+ if (!*m_ownServerId)
+ return;
+
+ std::thread requestThread([this, playerCount] {
+ httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue);
+ http.set_connection_timeout(10);
+
+ // we dont process this at all atm, maybe do later, but atm not necessary
+ if (auto result = http.Post(fmt::format("/server/update_values?id={}&playerCount={}", m_ownServerId, playerCount).c_str()))
+ {
+ m_successfullyConnected = true;
+ }
+ else
+ {
+ m_successfullyConnected = false;
+ }
+ });
+
+ requestThread.detach();
+}
+
+void MasterServerManager::RemoveSelfFromServerList()
+{
+ // dont call this if we don't have a server id
+ if (!*m_ownServerId || !Cvar_ns_report_server_to_masterserver->m_nValue)
+ return;
+
+ std::thread requestThread([this] {
+ httplib::Client http(Cvar_ns_masterserver_hostname->m_pszString, Cvar_ns_masterserver_port->m_nValue);
+ http.set_connection_timeout(10);
+
+ // we dont process this at all atm, maybe do later, but atm not necessary
+ if (auto result = http.Delete(fmt::format("/server/remove_server?id={}", m_ownServerId).c_str()))
+ {
+ m_successfullyConnected = true;
+ }
+ else
+ {
+ m_successfullyConnected = false;
+ }
+
+ m_ownServerId[0] = 0;
+ });
+
+ requestThread.detach();
+}
+
void ConCommand_ns_fetchservers(const CCommand& args)
{
g_MasterServerManager->RequestServerList();
}
+void CHostState__State_NewGameHook(CHostState* hostState)
+{
+ // not 100% we should do this here, but whatever
+ Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode);
+
+ g_MasterServerManager->AddSelfToServerList(Cvar_hostport->m_nValue, Cvar_ns_server_name->m_pszString, Cvar_ns_server_desc->m_pszString, hostState->m_levelName, (char*)GetCurrentPlaylistName(), 16, Cvar_ns_server_password->m_pszString);
+ CHostState__State_NewGame(hostState);
+}
+
+void CHostState__State_ChangeLevelMPHook(CHostState* hostState)
+{
+ g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName());
+ CHostState__State_ChangeLevelMP(hostState);
+}
+
+void CHostState__State_ChangeLevelSPHook(CHostState* hostState)
+{
+ g_MasterServerManager->UpdateServerMapAndPlaylist(hostState->m_levelName, (char*)GetCurrentPlaylistName());
+ CHostState__State_ChangeLevelSP(hostState);
+}
+
+void CHostState__State_GameShutdownHook(CHostState* hostState)
+{
+ g_MasterServerManager->RemoveSelfFromServerList();
+ CHostState__State_GameShutdown(hostState);
+}
+
void InitialiseSharedMasterServer(HMODULE baseAddress)
{
Cvar_ns_masterserver_hostname = RegisterConVar("ns_masterserver_hostname", "localhost", FCVAR_NONE, "");
Cvar_ns_masterserver_port = RegisterConVar("ns_masterserver_port", "8080", FCVAR_NONE, "");
+
+ Cvar_ns_server_name = RegisterConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "");
+ Cvar_ns_server_desc = RegisterConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "");
+ Cvar_ns_server_password = RegisterConVar("ns_server_password", "", FCVAR_GAMEDLL, "");
+ Cvar_ns_report_server_to_masterserver = RegisterConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "");
+ Cvar_ns_report_sp_server_to_masterserver = RegisterConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "");
g_MasterServerManager = new MasterServerManager;
RegisterConCommand("ns_fetchservers", ConCommand_ns_fetchservers, "", FCVAR_CLIENTDLL);
+
+ 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 + 0x16E640, CHostState__State_GameShutdownHook, reinterpret_cast<LPVOID*>(&CHostState__State_GameShutdown));
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/masterserver.h b/NorthstarDedicatedTest/masterserver.h
index 76e19b48..9b8e8a89 100644
--- a/NorthstarDedicatedTest/masterserver.h
+++ b/NorthstarDedicatedTest/masterserver.h
@@ -5,7 +5,7 @@
class RemoteServerInfo
{
public:
- char id[32];
+ char id[33]; // 32 bytes + nullterminator
// server info
char name[64];
@@ -18,12 +18,9 @@ public:
// connection stuff
bool requiresPassword;
- in_addr ip;
- int port;
public:
- RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers);
- RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, in_addr newIp, int newPort);
+ RemoteServerInfo(const char* newId, const char* newName, const char* newDescription, const char* newMap, const char* newPlaylist, int newPlayerCount, int newMaxPlayers, bool newRequiresPassword);
};
struct RemoteServerConnectionInfo
@@ -42,6 +39,8 @@ private:
bool m_authenticatingWithGameServer = false;
public:
+ char m_ownServerId[33];
+
bool m_scriptRequestingServerList = false;
bool m_successfullyConnected = true;
@@ -56,7 +55,11 @@ public:
public:
void ClearServerList();
void RequestServerList();
- void TryAuthenticateWithServer(char* serverId, char* password);
+ void AuthenticateWithServer(char* serverId, char* password);
+ void AddSelfToServerList(int port, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password);
+ void UpdateServerMapAndPlaylist(char* map, char* playlist);
+ void UpdateServerPlayerCount(int playerCount);
+ void RemoveSelfFromServerList();
};
void InitialiseSharedMasterServer(HMODULE baseAddress);
diff --git a/NorthstarDedicatedTest/modlocalisation.cpp b/NorthstarDedicatedTest/modlocalisation.cpp
new file mode 100644
index 00000000..ccc06722
--- /dev/null
+++ b/NorthstarDedicatedTest/modlocalisation.cpp
@@ -0,0 +1,38 @@
+#include "pch.h"
+#include "modlocalisation.h"
+#include "hookutils.h"
+#include "modmanager.h"
+
+typedef char(*AddLocalisationFileType)(void* g_pVguiLocalize, const char* path, const char* pathId);
+AddLocalisationFileType AddLocalisationFile;
+
+bool loadModLocalisationFiles = true;
+
+char AddLocalisationFileHook(void* g_pVguiLocalize, const char* path, char* pathId)
+{
+ char ret = AddLocalisationFile(g_pVguiLocalize, path, pathId);
+
+ if (!loadModLocalisationFiles)
+ return ret;
+
+ loadModLocalisationFiles = false;
+
+ for (Mod* mod : g_ModManager->m_loadedMods)
+ {
+ for (std::string& localisationFile : mod->LocalisationFiles)
+ {
+ spdlog::info("Adding mod localisation file {}", localisationFile);
+ AddLocalisationFile(g_pVguiLocalize, localisationFile.c_str(), pathId);
+ }
+ }
+
+ loadModLocalisationFiles = true;
+
+ return ret;
+}
+
+void InitialiseModLocalisation(HMODULE baseAddress)
+{
+ HookEnabler hook;
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x6D80, AddLocalisationFileHook, reinterpret_cast<LPVOID*>(&AddLocalisationFile));
+} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/modlocalisation.h b/NorthstarDedicatedTest/modlocalisation.h
new file mode 100644
index 00000000..b7d9cd50
--- /dev/null
+++ b/NorthstarDedicatedTest/modlocalisation.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void InitialiseModLocalisation(HMODULE baseAddress); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp
index 342736e6..dedbbbc8 100644
--- a/NorthstarDedicatedTest/modmanager.cpp
+++ b/NorthstarDedicatedTest/modmanager.cpp
@@ -163,6 +163,17 @@ Mod::Mod(fs::path modDir, char* jsonBuf)
}
}
+ if (modJson.HasMember("Localisation") && modJson["Localisation"].IsArray())
+ {
+ for (auto& localisationStr : modJson["Localisation"].GetArray())
+ {
+ if (!localisationStr.IsString())
+ continue;
+
+ LocalisationFiles.push_back(localisationStr.GetString());
+ }
+ }
+
wasReadSuccessfully = true;
}
@@ -237,6 +248,7 @@ void ModManager::LoadMods()
if (fs::is_regular_file(file) && file.path().extension() == "vpk")
mod->Vpks.push_back(file.path().string());
+ // read keyvalues paths
if (fs::exists(mod->ModDirectory / "keyvalues"))
{
for (fs::directory_entry file : fs::recursive_directory_iterator(mod->ModDirectory / "keyvalues"))
diff --git a/NorthstarDedicatedTest/modmanager.h b/NorthstarDedicatedTest/modmanager.h
index ad5e74ff..079e1613 100644
--- a/NorthstarDedicatedTest/modmanager.h
+++ b/NorthstarDedicatedTest/modmanager.h
@@ -70,6 +70,8 @@ public:
std::vector<ModScript*> Scripts;
// convars created by the mod
std::vector<ModConVar*> ConVars;
+ // custom localisation files created by the mod
+ std::vector<std::string> LocalisationFiles;
// other files:
diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h
index 04e5847d..19192f04 100644
--- a/NorthstarDedicatedTest/pch.h
+++ b/NorthstarDedicatedTest/pch.h
@@ -5,6 +5,7 @@
#define _CRT_SECURE_NO_WARNINGS
#define RAPIDJSON_NOMEMBERITERATORCLASS // need this for rapidjson
#define NOMINMAX // this too
+#define _WINSOCK_DEPRECATED_NO_WARNINGS // temp because i'm very lazy and want to use inet_addr, remove later
// add headers that you want to pre-compile here
#include <Windows.h>
diff --git a/NorthstarDedicatedTest/scriptserverbrowser.cpp b/NorthstarDedicatedTest/scriptserverbrowser.cpp
index 419ed274..c5446031 100644
--- a/NorthstarDedicatedTest/scriptserverbrowser.cpp
+++ b/NorthstarDedicatedTest/scriptserverbrowser.cpp
@@ -49,6 +49,21 @@ SQInteger SQ_GetServerName(void* sqvm)
return 1;
}
+// string function NSGetServerDescription( int serverIndex )
+SQInteger SQ_GetServerDescription(void* sqvm)
+{
+ SQInteger serverIndex = ClientSq_getinteger(sqvm, 1);
+
+ if (serverIndex >= g_MasterServerManager->m_remoteServers.size())
+ {
+ spdlog::warn("Tried to get description of server index {} when only {} servers are available", serverIndex, g_MasterServerManager->m_remoteServers.size());
+ return 0;
+ }
+
+ ClientSq_pushstring(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].description.c_str(), -1);
+ return 1;
+}
+
// string function NSGetServerMap( int serverIndex )
SQInteger SQ_GetServerMap(void* sqvm)
{
@@ -79,6 +94,36 @@ SQInteger SQ_GetServerPlaylist(void* sqvm)
return 1;
}
+// int function NSGetServerPlayerCount( int serverIndex )
+SQInteger SQ_GetServerPlayerCount(void* sqvm)
+{
+ SQInteger serverIndex = ClientSq_getinteger(sqvm, 1);
+
+ if (serverIndex >= g_MasterServerManager->m_remoteServers.size())
+ {
+ spdlog::warn("Tried to get playercount of server index {} when only {} servers are available", serverIndex, g_MasterServerManager->m_remoteServers.size());
+ return 0;
+ }
+
+ ClientSq_pushinteger(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].playerCount);
+ return 1;
+}
+
+// int function NSGetServerMaxPlayerCount( int serverIndex )
+SQInteger SQ_GetServerMaxPlayerCount(void* sqvm)
+{
+ SQInteger serverIndex = ClientSq_getinteger(sqvm, 1);
+
+ if (serverIndex >= g_MasterServerManager->m_remoteServers.size())
+ {
+ spdlog::warn("Tried to get max playercount of server index {} when only {} servers are available", serverIndex, g_MasterServerManager->m_remoteServers.size());
+ return 0;
+ }
+
+ ClientSq_pushinteger(sqvm, g_MasterServerManager->m_remoteServers[serverIndex].maxPlayers);
+ return 1;
+}
+
// string function NSGetServerID( int serverIndex )
SQInteger SQ_GetServerID(void* sqvm)
{
@@ -131,7 +176,7 @@ SQInteger SQ_TryAuthWithServer(void* sqvm)
}
// do auth
- g_MasterServerManager->TryAuthenticateWithServer(g_MasterServerManager->m_remoteServers[serverIndex].id, (char*)password);
+ g_MasterServerManager->AuthenticateWithServer(g_MasterServerManager->m_remoteServers[serverIndex].id, (char*)password);
return 0;
}
@@ -181,8 +226,11 @@ void InitialiseScriptServerBrowser(HMODULE baseAddress)
g_UISquirrelManager->AddFuncRegistration("void", "NSClearRecievedServerList", "", "", SQ_ClearRecievedServerList);
g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerName", "int serverIndex", "", SQ_GetServerName);
+ g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerDescription", "int serverIndex", "", SQ_GetServerDescription);
g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerMap", "int serverIndex", "", SQ_GetServerMap);
g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerPlaylist", "int serverIndex", "", SQ_GetServerPlaylist);
+ g_UISquirrelManager->AddFuncRegistration("int", "NSGetServerPlayerCount", "int serverIndex", "", SQ_GetServerPlayerCount);
+ g_UISquirrelManager->AddFuncRegistration("int", "NSGetServerMaxPlayerCount", "int serverIndex", "", SQ_GetServerMaxPlayerCount);
g_UISquirrelManager->AddFuncRegistration("string", "NSGetServerID", "int serverIndex", "", SQ_GetServerID);
g_UISquirrelManager->AddFuncRegistration("bool", "NSServerRequiresPassword", "int serverIndex", "", SQ_ServerRequiresPassword);
diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp
index 820cb69d..964c6b16 100644
--- a/NorthstarDedicatedTest/serverauthentication.cpp
+++ b/NorthstarDedicatedTest/serverauthentication.cpp
@@ -2,8 +2,11 @@
#include "serverauthentication.h"
#include "convar.h"
#include "hookutils.h"
+#include "masterserver.h"
+#include "httplib.h"
#include <fstream>
#include <filesystem>
+#include <thread>
// hook types
@@ -25,10 +28,25 @@ CGameClient__ExecuteStringCommandType CGameClient__ExecuteStringCommand;
// global vars
ServerAuthenticationManager* g_ServerAuthenticationManager;
+ConVar* Cvar_ns_player_auth_port;
ConVar* CVar_ns_auth_allow_insecure;
ConVar* CVar_ns_auth_allow_insecure_write;
ConVar* CVar_sv_quota_stringcmdspersecond;
+void ServerAuthenticationManager::StartPlayerAuthServer()
+{
+ m_runningPlayerAuthThread = true;
+
+ std::thread serverThread([this] {
+ while (m_runningPlayerAuthThread)
+ {
+
+ }
+ });
+
+ serverThread.detach();
+}
+
void ServerAuthenticationManager::AddPlayerAuthData(char* authToken, char* uid, char* pdata, size_t pdataSize)
{
@@ -122,6 +140,8 @@ void ServerAuthenticationManager::WritePersistentData(void* player)
// auth hooks
+int playerCount = 0; // temp
+
// store these in vars so we can use them in CBaseClient::Connect
// this is fine because ptrs won't decay by the time we use this, just don't use it outside of cbaseclient::connect
char* nextPlayerToken;
@@ -146,6 +166,8 @@ char CBaseClient__ConnectHook(void* self, char* name, __int64 netchan_ptr_arg, c
else if (!g_ServerAuthenticationManager->AuthenticatePlayer(self, nextPlayerUid, nextPlayerToken))
CBaseClient__Disconnect(self, 1, "Authentication Failed");
+ playerCount++;
+
return ret;
}
@@ -154,7 +176,10 @@ void CBaseClient__ActivatePlayerHook(void* 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 (*((char*)self + 0x4A0) >= (char)0x3 && !g_ServerAuthenticationManager->RemovePlayerAuthData(self))
+ {
g_ServerAuthenticationManager->WritePersistentData(self);
+ g_MasterServerManager->UpdateServerPlayerCount(playerCount);
+ }
CBaseClient__ActivatePlayer(self);
}
@@ -172,6 +197,8 @@ void CBaseClient__DisconnectHook(void* self, uint32_t unknownButAlways1, const c
// dcing, write persistent data
g_ServerAuthenticationManager->WritePersistentData(self);
+ g_MasterServerManager->UpdateServerPlayerCount(playerCount = std::max(playerCount - 1, 0));
+
CBaseClient__Disconnect(self, unknownButAlways1, buf);
}
diff --git a/NorthstarDedicatedTest/serverauthentication.h b/NorthstarDedicatedTest/serverauthentication.h
index 66802f31..e9aae4f0 100644
--- a/NorthstarDedicatedTest/serverauthentication.h
+++ b/NorthstarDedicatedTest/serverauthentication.h
@@ -15,8 +15,10 @@ class ServerAuthenticationManager
{
public:
std::unordered_map<std::string, AuthData*> m_authData;
+ bool m_runningPlayerAuthThread = false;
public:
+ void StartPlayerAuthServer();
void AddPlayerAuthData(char* authToken, char* uid, char* pdata, size_t pdataSize);
bool AuthenticatePlayer(void* player, int64_t uid, char* authToken);
bool RemovePlayerAuthData(void* player);
@@ -25,4 +27,5 @@ public:
void InitialiseServerAuthentication(HMODULE baseAddress);
-extern ServerAuthenticationManager* g_ServerAuthenticationManager; \ No newline at end of file
+extern ServerAuthenticationManager* g_ServerAuthenticationManager;
+extern ConVar* Cvar_ns_player_auth_port; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/squirrel.cpp b/NorthstarDedicatedTest/squirrel.cpp
index 90988eca..50e24075 100644
--- a/NorthstarDedicatedTest/squirrel.cpp
+++ b/NorthstarDedicatedTest/squirrel.cpp
@@ -163,7 +163,9 @@ void InitialiseServerSquirrel(HMODULE baseAddress)
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x799E0, &ScriptCompileErrorHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerSQCompileError)); // server compileerror function
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1D5C0, &CallScriptInitCallbackHook<SERVER>, reinterpret_cast<LPVOID*>(&ServerCallScriptInitCallback)); // server callscriptinitcallback function
- RegisterConCommand("script", ExecuteCodeCommand<SERVER>, "Executes script code on the server vm", FCVAR_GAMEDLL);
+ // cheat and clientcmd_can_execute allows clients to execute this, but since it's unsafe we only allow it when cheats are enabled
+ // for script_client and script_ui, we don't use cheats, so clients can execute them on themselves all they want
+ RegisterConCommand("script", ExecuteCodeCommand<SERVER>, "Executes script code on the server vm", FCVAR_GAMEDLL | FCVAR_CLIENTCMD_CAN_EXECUTE | FCVAR_CHEAT);
}
// hooks