aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL/server
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL/server')
-rw-r--r--NorthstarDLL/server/alltalk.cpp28
-rw-r--r--NorthstarDLL/server/auth/bansystem.cpp224
-rw-r--r--NorthstarDLL/server/auth/bansystem.h19
-rw-r--r--NorthstarDLL/server/auth/serverauthentication.cpp380
-rw-r--r--NorthstarDLL/server/auth/serverauthentication.h58
-rw-r--r--NorthstarDLL/server/buildainfile.cpp395
-rw-r--r--NorthstarDLL/server/r2server.cpp16
-rw-r--r--NorthstarDLL/server/r2server.h110
-rw-r--r--NorthstarDLL/server/serverchathooks.cpp174
-rw-r--r--NorthstarDLL/server/serverchathooks.h24
-rw-r--r--NorthstarDLL/server/servernethooks.cpp218
-rw-r--r--NorthstarDLL/server/serverpresence.cpp228
-rw-r--r--NorthstarDLL/server/serverpresence.h94
13 files changed, 0 insertions, 1968 deletions
diff --git a/NorthstarDLL/server/alltalk.cpp b/NorthstarDLL/server/alltalk.cpp
deleted file mode 100644
index d71b0bae..00000000
--- a/NorthstarDLL/server/alltalk.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "core/convar/convar.h"
-#include "engine/r2engine.h"
-
-size_t __fastcall ShouldAllowAlltalk()
-{
- // this needs to return a 64 bit integer where 0 = true and 1 = false
- static ConVar* Cvar_sv_alltalk = R2::g_pCVar->FindVar("sv_alltalk");
- if (Cvar_sv_alltalk->GetBool())
- return 0;
-
- // lobby should default to alltalk, otherwise don't allow it
- return strcmp(R2::g_pGlobals->m_pMapName, "mp_lobby");
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerAllTalk, ConVar, (CModule module))
-{
- // replace strcmp function called in CClient::ProcessVoiceData with our own code that calls ShouldAllowAllTalk
- CMemoryAddress base = module.Offset(0x1085FA);
-
- base.Patch("48 B8"); // mov rax, 64 bit int
- // (uint8_t*)&ShouldAllowAlltalk doesn't work for some reason? need to make it a uint64 first
- uint64_t pShouldAllowAllTalk = reinterpret_cast<uint64_t>(ShouldAllowAlltalk);
- base.Offset(0x2).Patch((uint8_t*)&pShouldAllowAllTalk, 8);
- base.Offset(0xA).Patch("FF D0"); // call rax
-
- // nop until compare (test eax, eax)
- base.Offset(0xC).NOP(0x7);
-}
diff --git a/NorthstarDLL/server/auth/bansystem.cpp b/NorthstarDLL/server/auth/bansystem.cpp
deleted file mode 100644
index 9b9d24c4..00000000
--- a/NorthstarDLL/server/auth/bansystem.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-#include "bansystem.h"
-#include "serverauthentication.h"
-#include "core/convar/concommand.h"
-#include "server/r2server.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-#include "config/profile.h"
-
-#include <filesystem>
-
-const char* BANLIST_PATH_SUFFIX = "/banlist.txt";
-const char BANLIST_COMMENT_CHAR = '#';
-
-ServerBanSystem* g_pBanSystem;
-
-void ServerBanSystem::OpenBanlist()
-{
- std::ifstream banlistStream(GetNorthstarPrefix() + "/banlist.txt");
-
- if (!banlistStream.fail())
- {
- std::string line;
- while (std::getline(banlistStream, line))
- {
- // ignore line if first char is # or line is empty
- if (line == "" || line.front() == BANLIST_COMMENT_CHAR)
- continue;
-
- // remove tabs which shouldnt be there but maybe someone did the funny
- line.erase(std::remove(line.begin(), line.end(), '\t'), line.end());
- // remove spaces to allow for spaces before uids
- line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
-
- // check if line is empty to allow for newlines in the file
- if (line == "")
- continue;
-
- // for inline comments like: 123123123 #banned for unfunny
- std::string uid = line.substr(0, line.find(BANLIST_COMMENT_CHAR));
-
- m_vBannedUids.push_back(strtoull(uid.c_str(), nullptr, 10));
- }
-
- banlistStream.close();
- }
-
- // open write stream for banlist // dont do this to allow for all time access
- // m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary | std::ofstream::app);
-}
-
-void ServerBanSystem::ReloadBanlist()
-{
- std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt");
-
- if (!fsBanlist.fail())
- {
- std::string line;
- // since we wanna use this as the reload func we need to clear the list
- m_vBannedUids.clear();
- while (std::getline(fsBanlist, line))
- m_vBannedUids.push_back(strtoull(line.c_str(), nullptr, 10));
-
- fsBanlist.close();
- }
-}
-
-void ServerBanSystem::ClearBanlist()
-{
- m_vBannedUids.clear();
-
- // reopen the file, don't provide std::ofstream::app so it clears on open
- m_sBanlistStream.close();
- m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary);
- m_sBanlistStream.close();
-}
-
-void ServerBanSystem::BanUID(uint64_t uid)
-{
- // checking if last char is \n to make sure uids arent getting fucked
- std::ifstream fsBanlist(GetNorthstarPrefix() + "/banlist.txt");
- std::string content((std::istreambuf_iterator<char>(fsBanlist)), (std::istreambuf_iterator<char>()));
- fsBanlist.close();
-
- m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary | std::ofstream::app);
- if (content.back() != '\n')
- m_sBanlistStream << std::endl;
-
- m_vBannedUids.push_back(uid);
- m_sBanlistStream << std::to_string(uid) << std::endl;
- m_sBanlistStream.close();
- spdlog::info("{} was banned", uid);
-}
-
-void ServerBanSystem::UnbanUID(uint64_t uid)
-{
- auto findResult = std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid);
- if (findResult == m_vBannedUids.end())
- return;
-
- m_vBannedUids.erase(findResult);
-
- std::vector<std::string> banlistText;
- std::ifstream fs_readBanlist(GetNorthstarPrefix() + "/banlist.txt");
-
- if (!fs_readBanlist.fail())
- {
- std::string line;
- while (std::getline(fs_readBanlist, line))
- {
- // support for comments and newlines added in https://github.com/R2Northstar/NorthstarLauncher/pull/227
-
- std::string modLine = line; // copy the line into a free var that we can fuck with, line will be the original
-
- // remove tabs which shouldnt be there but maybe someone did the funny
- modLine.erase(std::remove(modLine.begin(), modLine.end(), '\t'), modLine.end());
- // remove spaces to allow for spaces before uids
- modLine.erase(std::remove(modLine.begin(), modLine.end(), ' '), modLine.end());
-
- // ignore line if first char is # or empty line, just add it
- if (line.front() == BANLIST_COMMENT_CHAR || modLine == "")
- {
- banlistText.push_back(line);
- continue;
- }
-
- // for inline comments like: 123123123 #banned for unfunny
- std::string lineUid = line.substr(0, line.find(BANLIST_COMMENT_CHAR));
- // have to erase spaces or else inline comments will fuck up the uid finding
- lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), '\t'), lineUid.end());
- lineUid.erase(std::remove(lineUid.begin(), lineUid.end(), ' '), lineUid.end());
-
- // if the uid in the line is the uid we wanna unban
- if (std::to_string(uid) == lineUid)
- {
- // comment the uid out
- line.insert(0, "# ");
-
- // add a comment with unban date
- // not necessary but i feel like this makes it better
- std::time_t t = std::time(0);
- std::tm* now = std::localtime(&t);
-
- std::ostringstream unbanComment;
-
- //{y}/{m}/{d} {h}:{m}
- unbanComment << " # unban date: ";
- unbanComment << now->tm_year + 1900 << "-"; // this lib is so fucking awful
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_mon + 1 << "-";
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_mday << " ";
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_hour << ":";
- unbanComment << std::setw(2) << std::setfill('0') << now->tm_min;
-
- line.append(unbanComment.str());
- }
-
- banlistText.push_back(line);
- }
-
- fs_readBanlist.close();
- }
-
- // open write stream for banlist // without append so we clear the file
- if (m_sBanlistStream.is_open())
- m_sBanlistStream.close();
- m_sBanlistStream.open(GetNorthstarPrefix() + "/banlist.txt", std::ofstream::out | std::ofstream::binary);
-
- for (std::string updatedLine : banlistText)
- m_sBanlistStream << updatedLine << std::endl;
-
- m_sBanlistStream.close();
- spdlog::info("{} was unbanned", uid);
-}
-
-bool ServerBanSystem::IsUIDAllowed(uint64_t uid)
-{
- uint64_t localPlayerUserID = strtoull(R2::g_pLocalPlayerUserID, nullptr, 10);
- if (localPlayerUserID == uid)
- return true;
-
- ReloadBanlist(); // Reload to have up to date list on join
- return std::find(m_vBannedUids.begin(), m_vBannedUids.end(), uid) == m_vBannedUids.end();
-}
-
-void ConCommand_ban(const CCommand& args)
-{
- if (args.ArgC() < 2)
- return;
-
- for (int i = 0; i < R2::g_pGlobals->m_nMaxClients; i++)
- {
- R2::CBaseClient* player = &R2::g_pClientArray[i];
-
- if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1)))
- {
- g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10));
- R2::CBaseClient__Disconnect(player, 1, "Banned from server");
- break;
- }
- }
-}
-
-void ConCommand_unban(const CCommand& args)
-{
- if (args.ArgC() < 2)
- return;
-
- // assumedly the player being unbanned here wasn't already connected, so don't need to iterate over players or anything
- g_pBanSystem->UnbanUID(strtoull(args.Arg(1), nullptr, 10));
-}
-
-void ConCommand_clearbanlist(const CCommand& args)
-{
- g_pBanSystem->ClearBanlist();
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", BanSystem, ConCommand, (CModule module))
-{
- 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_GAMEDLL);
- RegisterConCommand("clearbanlist", ConCommand_clearbanlist, "clears all uids on the banlist", FCVAR_GAMEDLL);
-}
diff --git a/NorthstarDLL/server/auth/bansystem.h b/NorthstarDLL/server/auth/bansystem.h
deleted file mode 100644
index 6f180126..00000000
--- a/NorthstarDLL/server/auth/bansystem.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-#include <fstream>
-
-class ServerBanSystem
-{
- private:
- std::ofstream m_sBanlistStream;
- std::vector<uint64_t> m_vBannedUids;
-
- public:
- void OpenBanlist();
- void ReloadBanlist();
- void ClearBanlist();
- void BanUID(uint64_t uid);
- void UnbanUID(uint64_t uid);
- bool IsUIDAllowed(uint64_t uid);
-};
-
-extern ServerBanSystem* g_pBanSystem;
diff --git a/NorthstarDLL/server/auth/serverauthentication.cpp b/NorthstarDLL/server/auth/serverauthentication.cpp
deleted file mode 100644
index d5653dcc..00000000
--- a/NorthstarDLL/server/auth/serverauthentication.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-#include "serverauthentication.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "core/convar/cvar.h"
-#include "core/convar/convar.h"
-#include "masterserver/masterserver.h"
-#include "server/serverpresence.h"
-#include "engine/hoststate.h"
-#include "bansystem.h"
-#include "core/convar/concommand.h"
-#include "dedicated/dedicated.h"
-#include "config/profile.h"
-#include "core/tier0.h"
-#include "engine/r2engine.h"
-#include "client/r2client.h"
-#include "server/r2server.h"
-
-#include <fstream>
-#include <filesystem>
-#include <string>
-#include <thread>
-
-AUTOHOOK_INIT()
-
-// global vars
-ServerAuthenticationManager* g_pServerAuthentication;
-CBaseServer__RejectConnectionType CBaseServer__RejectConnection;
-
-void ServerAuthenticationManager::AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata)
-{
- std::string uidS = std::to_string(uid);
-
- RemoteAuthData newAuthData {};
- strncpy_s(newAuthData.uid, sizeof(newAuthData.uid), uidS.c_str(), uidS.length());
- strncpy_s(newAuthData.username, sizeof(newAuthData.username), username.c_str(), username.length());
- newAuthData.pdata = new char[pdata.length()];
- newAuthData.pdataSize = pdata.length();
- memcpy(newAuthData.pdata, pdata.c_str(), newAuthData.pdataSize);
-
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
- m_RemoteAuthenticationData[token] = newAuthData;
-}
-
-void ServerAuthenticationManager::AddPlayer(R2::CBaseClient* pPlayer, const char* pToken)
-{
- PlayerAuthenticationData additionalData;
-
- auto remoteAuthData = m_RemoteAuthenticationData.find(pToken);
- if (remoteAuthData != m_RemoteAuthenticationData.end())
- additionalData.pdataSize = remoteAuthData->second.pdataSize;
- else
- additionalData.pdataSize = R2::PERSISTENCE_MAX_SIZE;
-
- additionalData.usingLocalPdata = pPlayer->m_iPersistenceReady == R2::ePersistenceReady::READY_INSECURE;
-
- m_PlayerAuthenticationData.insert(std::make_pair(pPlayer, additionalData));
-}
-
-void ServerAuthenticationManager::RemovePlayer(R2::CBaseClient* pPlayer)
-{
- if (m_PlayerAuthenticationData.count(pPlayer))
- m_PlayerAuthenticationData.erase(pPlayer);
-}
-
-bool ServerAuthenticationManager::VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64])
-{
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
-
- // always use name from masterserver if available
- // use of strncpy_s here should verify that this is always nullterminated within valid buffer size
- auto authData = m_RemoteAuthenticationData.find(pAuthToken);
- if (authData != m_RemoteAuthenticationData.end() && *authData->second.username)
- strncpy_s(pOutVerifiedName, 64, authData->second.username, 63);
- else
- strncpy_s(pOutVerifiedName, 64, pName, 63);
-
- // now, check that whatever name we have is actually valid
- // first, make sure it's >1 char
- if (!*pOutVerifiedName)
- return false;
-
- // next, make sure it's within a valid range of ascii characters
- for (int i = 0; pOutVerifiedName[i]; i++)
- {
- if (pOutVerifiedName[i] < 32 || pOutVerifiedName[i] > 126)
- return false;
- }
-
- return true;
-}
-
-bool ServerAuthenticationManager::IsDuplicateAccount(R2::CBaseClient* pPlayer, const char* pPlayerUid)
-{
- if (m_bAllowDuplicateAccounts)
- return false;
-
- bool bHasUidPlayer = false;
- for (int i = 0; i < R2::g_pGlobals->m_nMaxClients; i++)
- if (&R2::g_pClientArray[i] != pPlayer && !strcmp(pPlayerUid, R2::g_pClientArray[i].m_UID))
- return true;
-
- return false;
-}
-
-bool ServerAuthenticationManager::CheckAuthentication(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken)
-{
- std::string sUid = std::to_string(iUid);
-
- // check whether this player's authentication is valid but don't actually write anything to the player, we'll do that later
- // if we don't need auth this is valid
- if (Cvar_ns_auth_allow_insecure->GetBool())
- return true;
-
- // local server that doesn't need auth (probably sp) and local player
- if (m_bStartingLocalSPGame && !strcmp(sUid.c_str(), R2::g_pLocalPlayerUserID))
- return true;
-
- // don't allow duplicate accounts
- if (IsDuplicateAccount(pPlayer, sUid.c_str()))
- return false;
-
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
- auto authData = m_RemoteAuthenticationData.find(pAuthToken);
- if (authData != m_RemoteAuthenticationData.end() && !strcmp(sUid.c_str(), authData->second.uid))
- return true;
-
- return false;
-}
-
-void ServerAuthenticationManager::AuthenticatePlayer(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken)
-{
- // for bot players, generate a new uid
- if (pPlayer->m_bFakePlayer)
- iUid = 0; // is this a good way of doing things :clueless:
-
- std::string sUid = std::to_string(iUid);
-
- // copy uuid
- strcpy(pPlayer->m_UID, sUid.c_str());
-
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
- auto authData = m_RemoteAuthenticationData.find(pAuthToken);
- if (authData != m_RemoteAuthenticationData.end())
- {
- // if we're resetting let script handle the reset with InitPersistentData() on connect
- if (!m_bForceResetLocalPlayerPersistence || strcmp(sUid.c_str(), R2::g_pLocalPlayerUserID))
- {
- // copy pdata into buffer
- memcpy(pPlayer->m_PersistenceBuffer, authData->second.pdata, authData->second.pdataSize);
- }
-
- // set persistent data as ready
- pPlayer->m_iPersistenceReady = R2::ePersistenceReady::READY_REMOTE;
- }
- // we probably allow insecure at this point, but make sure not to write anyway if not insecure
- else if (Cvar_ns_auth_allow_insecure->GetBool() || pPlayer->m_bFakePlayer)
- {
- // set persistent data as ready
- // note: actual placeholder persistent data is populated in script with InitPersistentData()
- pPlayer->m_iPersistenceReady = R2::ePersistenceReady::READY_INSECURE;
- }
-}
-
-bool ServerAuthenticationManager::RemovePlayerAuthData(R2::CBaseClient* pPlayer)
-{
- if (!Cvar_ns_erase_auth_info->GetBool()) // keep auth data forever
- return false;
-
- // hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect
- if (m_bNeedLocalAuthForNewgame && !strcmp(pPlayer->m_UID, R2::g_pLocalPlayerUserID))
- return false;
-
- // we don't have our auth token at this point, so lookup authdata by uid
- for (auto& auth : m_RemoteAuthenticationData)
- {
- if (!strcmp(pPlayer->m_UID, auth.second.uid))
- {
- // pretty sure this is fine, since we don't iterate after the erase
- // i think if we iterated after it'd be undefined behaviour tho
- std::lock_guard<std::mutex> guard(m_AuthDataMutex);
-
- delete[] auth.second.pdata;
- m_RemoteAuthenticationData.erase(auth.first);
- return true;
- }
- }
-
- return false;
-}
-
-void ServerAuthenticationManager::WritePersistentData(R2::CBaseClient* pPlayer)
-{
- if (pPlayer->m_iPersistenceReady == R2::ePersistenceReady::READY_REMOTE)
- {
- g_pMasterServerManager->WritePlayerPersistentData(
- pPlayer->m_UID, (const char*)pPlayer->m_PersistenceBuffer, m_PlayerAuthenticationData[pPlayer].pdataSize);
- }
- else if (Cvar_ns_auth_allow_insecure_write->GetBool())
- {
- // todo: write pdata to disk here
- }
-}
-
-// auth hooks
-
-// 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 calls from cbaseclient::connectclient
-char* pNextPlayerToken;
-uint64_t iNextPlayerUid;
-
-// clang-format off
-AUTOHOOK(CBaseServer__ConnectClient, engine.dll + 0x114430,
-void*,, (
- void* self,
- void* addr,
- void* a3,
- uint32_t a4,
- uint32_t a5,
- int32_t a6,
- void* a7,
- char* playerName,
- char* serverFilter,
- void* a10,
- char a11,
- void* a12,
- char a13,
- char a14,
- int64_t uid,
- uint32_t a16,
- uint32_t a17))
-// clang-format on
-{
- // auth tokens are sent with serverfilter, can't be accessed from player struct to my knowledge, so have to do this here
- pNextPlayerToken = serverFilter;
- iNextPlayerUid = uid;
-
- return CBaseServer__ConnectClient(self, addr, a3, a4, a5, a6, a7, playerName, serverFilter, a10, a11, a12, a13, a14, uid, a16, a17);
-}
-
-ConVar* Cvar_ns_allowuserclantags;
-
-// clang-format off
-AUTOHOOK(CBaseClient__Connect, engine.dll + 0x101740,
-bool,, (R2::CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer, void* a5, char pDisconnectReason[256], void* a7))
-// clang-format on
-{
- const char* pAuthenticationFailure = nullptr;
- char pVerifiedName[64];
-
- if (!bFakePlayer)
- {
- if (!g_pServerAuthentication->VerifyPlayerName(pNextPlayerToken, pName, pVerifiedName))
- pAuthenticationFailure = "Invalid Name.";
- else if (!g_pBanSystem->IsUIDAllowed(iNextPlayerUid))
- pAuthenticationFailure = "Banned From server.";
- else if (!g_pServerAuthentication->CheckAuthentication(self, iNextPlayerUid, pNextPlayerToken))
- pAuthenticationFailure = "Authentication Failed.";
- }
- else // need to copy name for bots still
- strncpy_s(pVerifiedName, pName, 63);
-
- if (pAuthenticationFailure)
- {
- spdlog::info("{}'s (uid {}) connection was rejected: \"{}\"", pName, iNextPlayerUid, pAuthenticationFailure);
-
- strncpy_s(pDisconnectReason, 256, pAuthenticationFailure, 255);
- return false;
- }
-
- // try to actually connect the player
- if (!CBaseClient__Connect(self, pVerifiedName, pNetChannel, bFakePlayer, a5, pDisconnectReason, a7))
- return false;
-
- // we already know this player's authentication data is legit, actually write it to them now
- g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken);
-
- g_pServerAuthentication->AddPlayer(self, pNextPlayerToken);
- g_pServerLimits->AddPlayer(self);
-
- return true;
-}
-
-// clang-format off
-AUTOHOOK(CBaseClient__ActivatePlayer, engine.dll + 0x100F80,
-void,, (R2::CBaseClient* self))
-// clang-format on
-{
- // 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_bForceResetLocalPlayerPersistence = false;
- g_pServerAuthentication->WritePersistentData(self);
- g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
- }
-
- CBaseClient__ActivatePlayer(self);
-}
-
-// clang-format off
-AUTOHOOK(_CBaseClient__Disconnect, engine.dll + 0x1012C0,
-void,, (R2::CBaseClient* self, uint32_t unknownButAlways1, const char* pReason, ...))
-// clang-format on
-{
- // have to manually format message because can't pass varargs to original func
- char buf[1024];
-
- va_list va;
- va_start(va, pReason);
- vsprintf(buf, pReason, va);
- va_end(va);
-
- // this reason is used while connecting to a local server, hacky, but just ignore it
- if (strcmp(pReason, "Connection closing"))
- {
- spdlog::info("Player {} disconnected: \"{}\"", self->m_Name, buf);
-
- // dcing, write persistent data
- if (g_pServerAuthentication->m_PlayerAuthenticationData[self].needPersistenceWriteOnLeave)
- g_pServerAuthentication->WritePersistentData(self);
-
- memset(self->m_PersistenceBuffer, 0, g_pServerAuthentication->m_PlayerAuthenticationData[self].pdataSize);
- g_pServerAuthentication->RemovePlayerAuthData(self); // won't do anything 99% of the time, but just in case
-
- g_pServerAuthentication->RemovePlayer(self);
- g_pServerLimits->RemovePlayer(self);
- }
-
- g_pServerPresence->SetPlayerCount(g_pServerAuthentication->m_PlayerAuthenticationData.size());
-
- _CBaseClient__Disconnect(self, unknownButAlways1, buf);
-}
-
-void ConCommand_ns_resetpersistence(const CCommand& args)
-{
- if (*R2::g_pServerState == R2::server_state_t::ss_active)
- {
- spdlog::error("ns_resetpersistence must be entered from the main menu");
- return;
- }
-
- spdlog::info("resetting persistence on next lobby load...");
- g_pServerAuthentication->m_bForceResetLocalPlayerPersistence = true;
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerAuthentication, (ConCommand, ConVar), (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- g_pServerAuthentication = new ServerAuthenticationManager;
-
- g_pServerAuthentication->Cvar_ns_erase_auth_info =
- new ConVar("ns_erase_auth_info", "1", FCVAR_GAMEDLL, "Whether auth info should be erased from this server on disconnect or crash");
- g_pServerAuthentication->Cvar_ns_auth_allow_insecure =
- new ConVar("ns_auth_allow_insecure", "0", FCVAR_GAMEDLL, "Whether this server will allow unauthenicated players to connect");
- g_pServerAuthentication->Cvar_ns_auth_allow_insecure_write = new ConVar(
- "ns_auth_allow_insecure_write",
- "0",
- FCVAR_GAMEDLL,
- "Whether the pdata of unauthenticated clients will be written to disk when changed");
-
- RegisterConCommand(
- "ns_resetpersistence", ConCommand_ns_resetpersistence, "resets your pdata when you next enter the lobby", FCVAR_NONE);
-
- // patch to disable kicking based on incorrect serverfilter in connectclient, since we repurpose it for use as an auth token
- module.Offset(0x114655).Patch("EB");
-
- // patch to disable fairfight marking players as cheaters and kicking them
- module.Offset(0x101012).Patch("E9 90 00");
-
- CBaseServer__RejectConnection = module.Offset(0x1182E0).RCast<CBaseServer__RejectConnectionType>();
-
- if (Tier0::CommandLine()->CheckParm("-allowdupeaccounts"))
- {
- // patch to allow same of multiple account
- module.Offset(0x114510).Patch("EB");
-
- g_pServerAuthentication->m_bAllowDuplicateAccounts = true;
- }
-}
diff --git a/NorthstarDLL/server/auth/serverauthentication.h b/NorthstarDLL/server/auth/serverauthentication.h
deleted file mode 100644
index dd0e13af..00000000
--- a/NorthstarDLL/server/auth/serverauthentication.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-#include "core/convar/convar.h"
-#include "engine/r2engine.h"
-#include <unordered_map>
-#include <string>
-
-struct RemoteAuthData
-{
- char uid[33];
- char username[64];
-
- // pdata
- char* pdata;
- size_t pdataSize;
-};
-
-struct PlayerAuthenticationData
-{
- bool usingLocalPdata;
- size_t pdataSize;
- bool needPersistenceWriteOnLeave = true;
-};
-
-typedef int64_t (*CBaseServer__RejectConnectionType)(void* a1, unsigned int a2, void* a3, const char* a4, ...);
-extern CBaseServer__RejectConnectionType CBaseServer__RejectConnection;
-
-class ServerAuthenticationManager
-{
- public:
- ConVar* Cvar_ns_erase_auth_info;
- ConVar* Cvar_ns_auth_allow_insecure;
- ConVar* Cvar_ns_auth_allow_insecure_write;
-
- std::mutex m_AuthDataMutex;
- std::unordered_map<std::string, RemoteAuthData> m_RemoteAuthenticationData;
- std::unordered_map<R2::CBaseClient*, PlayerAuthenticationData> m_PlayerAuthenticationData;
-
- bool m_bAllowDuplicateAccounts = false;
- bool m_bNeedLocalAuthForNewgame = false;
- bool m_bForceResetLocalPlayerPersistence = false;
- bool m_bStartingLocalSPGame = false;
-
- public:
- void AddRemotePlayer(std::string token, uint64_t uid, std::string username, std::string pdata);
-
- void AddPlayer(R2::CBaseClient* pPlayer, const char* pAuthToken);
- void RemovePlayer(R2::CBaseClient* pPlayer);
-
- bool VerifyPlayerName(const char* pAuthToken, const char* pName, char pOutVerifiedName[64]);
- bool IsDuplicateAccount(R2::CBaseClient* pPlayer, const char* pUid);
- bool CheckAuthentication(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken);
-
- void AuthenticatePlayer(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken);
- bool RemovePlayerAuthData(R2::CBaseClient* pPlayer);
- void WritePersistentData(R2::CBaseClient* pPlayer);
-};
-
-extern ServerAuthenticationManager* g_pServerAuthentication;
diff --git a/NorthstarDLL/server/buildainfile.cpp b/NorthstarDLL/server/buildainfile.cpp
deleted file mode 100644
index d0143295..00000000
--- a/NorthstarDLL/server/buildainfile.cpp
+++ /dev/null
@@ -1,395 +0,0 @@
-#include "core/convar/convar.h"
-#include "engine/hoststate.h"
-#include "engine/r2engine.h"
-
-#include <fstream>
-#include <filesystem>
-
-AUTOHOOK_INIT()
-
-const int AINET_VERSION_NUMBER = 57;
-const int AINET_SCRIPT_VERSION_NUMBER = 21;
-const int PLACEHOLDER_CRC = 0;
-const int MAX_HULLS = 5;
-
-#pragma pack(push, 1)
-struct CAI_NodeLink
-{
- short srcId;
- short destId;
- bool hulls[MAX_HULLS];
- char unk0;
- char unk1; // maps => unk0 on disk
- char unk2[5];
- int64_t flags;
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct CAI_NodeLinkDisk
-{
- short srcId;
- short destId;
- char unk0;
- bool hulls[MAX_HULLS];
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct CAI_Node
-{
- int index; // not present on disk
- float x;
- float y;
- float z;
- float hulls[MAX_HULLS];
- float yaw;
-
- int unk0; // always 2 in buildainfile, maps directly to unk0 in disk struct
- int unk1; // maps directly to unk1 in disk struct
- int unk2[MAX_HULLS]; // maps directly to unk2 in disk struct, despite being ints rather than shorts
-
- // view server.dll+393672 for context and death wish
- char unk3[MAX_HULLS]; // hell on earth, should map to unk3 on disk
- char pad[3]; // aligns next bytes
- float unk4[MAX_HULLS]; // i have no fucking clue, calculated using some kind of demon hell function float magic
-
- CAI_NodeLink** links;
- char unk5[16];
- int linkcount;
- int unk11; // bad name lmao
- short unk6; // should match up to unk4 on disk
- char unk7[16]; // padding until next bit
- short unk8; // should match up to unk5 on disk
- char unk9[8]; // padding until next bit
- char unk10[8]; // should match up to unk6 on disk
-};
-#pragma pack(pop)
-
-// the way CAI_Nodes are represented in on-disk ain files
-#pragma pack(push, 1)
-struct CAI_NodeDisk
-{
- float x;
- float y;
- float z;
- float yaw;
- float hulls[MAX_HULLS];
-
- char unk0;
- int unk1;
- short unk2[MAX_HULLS];
- char unk3[MAX_HULLS];
- short unk4;
- short unk5;
- char unk6[8];
-}; // total size of 68 bytes
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct UnkNodeStruct0
-{
- int index;
- char unk0;
- char unk1; // maps to unk1 on disk
- char pad0[2]; // padding to +8
-
- float x;
- float y;
- float z;
-
- char pad5[4];
- int* unk2; // maps to unk5 on disk;
- char pad1[16]; // pad to +48
- int unkcount0; // maps to unkcount0 on disk
-
- char pad2[4]; // pad to +56
- int* unk3;
- char pad3[16]; // pad to +80
- int unkcount1;
-
- char pad4[132];
- char unk5;
-};
-#pragma pack(pop)
-
-int* pUnkStruct0Count;
-UnkNodeStruct0*** pppUnkNodeStruct0s;
-
-#pragma pack(push, 1)
-struct UnkLinkStruct1
-{
- short unk0;
- short unk1;
- int unk2;
- char unk3;
- char unk4;
- char unk5;
-};
-#pragma pack(pop)
-
-int* pUnkLinkStruct1Count;
-UnkLinkStruct1*** pppUnkStruct1s;
-
-#pragma pack(push, 1)
-struct CAI_ScriptNode
-{
- float x;
- float y;
- float z;
- uint64_t scriptdata;
-};
-#pragma pack(pop)
-
-#pragma pack(push, 1)
-struct CAI_Network
-{
- // +0
- char unk0[8];
- // +8
- int linkcount; // this is uninitialised and never set on ain build, fun!
- // +12
- char unk1[124];
- // +136
- int zonecount;
- // +140
- char unk2[16];
- // +156
- int unk5; // unk8 on disk
- // +160
- char unk6[4];
- // +164
- int hintcount;
- // +168
- short hints[2000]; // these probably aren't actually hints, but there's 1 of them per hint so idk
- // +4168
- int scriptnodecount;
- // +4172
- CAI_ScriptNode scriptnodes[4000];
- // +84172
- int nodecount;
- // +84176
- CAI_Node** nodes;
-};
-#pragma pack(pop)
-
-ConVar* Cvar_ns_ai_dumpAINfileFromLoad;
-
-void DumpAINInfo(CAI_Network* aiNetwork)
-{
- fs::path writePath(fmt::format("{}/maps/graphs", R2::g_pModName));
- writePath /= R2::g_pGlobals->m_pMapName;
- writePath += ".ain";
-
- // dump from memory
- spdlog::info("writing ain file {}", writePath.string());
- spdlog::info("");
- spdlog::info("");
- spdlog::info("");
- spdlog::info("");
- spdlog::info("");
-
- std::ofstream writeStream(writePath, std::ofstream::binary);
- spdlog::info("writing ainet version: {}", AINET_VERSION_NUMBER);
- writeStream.write((char*)&AINET_VERSION_NUMBER, sizeof(int));
-
- int mapVersion = R2::g_pGlobals->m_nMapVersion;
- spdlog::info("writing map version: {}", mapVersion);
- writeStream.write((char*)&mapVersion, sizeof(int));
- spdlog::info("writing placeholder crc: {}", PLACEHOLDER_CRC);
- writeStream.write((char*)&PLACEHOLDER_CRC, sizeof(int));
-
- int calculatedLinkcount = 0;
-
- // path nodes
- spdlog::info("writing nodecount: {}", aiNetwork->nodecount);
- writeStream.write((char*)&aiNetwork->nodecount, sizeof(int));
-
- for (int i = 0; i < aiNetwork->nodecount; i++)
- {
- // construct on-disk node struct
- CAI_NodeDisk diskNode;
- diskNode.x = aiNetwork->nodes[i]->x;
- diskNode.y = aiNetwork->nodes[i]->y;
- diskNode.z = aiNetwork->nodes[i]->z;
- diskNode.yaw = aiNetwork->nodes[i]->yaw;
- memcpy(diskNode.hulls, aiNetwork->nodes[i]->hulls, sizeof(diskNode.hulls));
- diskNode.unk0 = (char)aiNetwork->nodes[i]->unk0;
- diskNode.unk1 = aiNetwork->nodes[i]->unk1;
-
- for (int j = 0; j < MAX_HULLS; j++)
- {
- diskNode.unk2[j] = (short)aiNetwork->nodes[i]->unk2[j];
- spdlog::info((short)aiNetwork->nodes[i]->unk2[j]);
- }
-
- memcpy(diskNode.unk3, aiNetwork->nodes[i]->unk3, sizeof(diskNode.unk3));
- diskNode.unk4 = aiNetwork->nodes[i]->unk6;
- diskNode.unk5 =
- -1; // aiNetwork->nodes[i]->unk8; // this field is wrong, however, it's always -1 in vanilla navmeshes anyway, so no biggie
- memcpy(diskNode.unk6, aiNetwork->nodes[i]->unk10, sizeof(diskNode.unk6));
-
- spdlog::info("writing node {} from {} to {:x}", aiNetwork->nodes[i]->index, (void*)aiNetwork->nodes[i], writeStream.tellp());
- writeStream.write((char*)&diskNode, sizeof(CAI_NodeDisk));
-
- calculatedLinkcount += aiNetwork->nodes[i]->linkcount;
- }
-
- // links
- spdlog::info("linkcount: {}", aiNetwork->linkcount);
- spdlog::info("calculated total linkcount: {}", calculatedLinkcount);
-
- calculatedLinkcount /= 2;
- if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
- {
- if (aiNetwork->linkcount == calculatedLinkcount)
- spdlog::info("caculated linkcount is normal!");
- else
- spdlog::warn("calculated linkcount has weird value! this is expected on build!");
- }
-
- spdlog::info("writing linkcount: {}", calculatedLinkcount);
- writeStream.write((char*)&calculatedLinkcount, sizeof(int));
-
- for (int i = 0; i < aiNetwork->nodecount; i++)
- {
- for (int j = 0; j < aiNetwork->nodes[i]->linkcount; j++)
- {
- // skip links that don't originate from current node
- if (aiNetwork->nodes[i]->links[j]->srcId != aiNetwork->nodes[i]->index)
- continue;
-
- CAI_NodeLinkDisk diskLink;
- diskLink.srcId = aiNetwork->nodes[i]->links[j]->srcId;
- diskLink.destId = aiNetwork->nodes[i]->links[j]->destId;
- diskLink.unk0 = aiNetwork->nodes[i]->links[j]->unk1;
- memcpy(diskLink.hulls, aiNetwork->nodes[i]->links[j]->hulls, sizeof(diskLink.hulls));
-
- spdlog::info("writing link {} => {} to {:x}", diskLink.srcId, diskLink.destId, writeStream.tellp());
- writeStream.write((char*)&diskLink, sizeof(CAI_NodeLinkDisk));
- }
- }
-
- // don't know what this is, it's likely a block from tf1 that got deprecated? should just be 1 int per node
- spdlog::info("writing {:x} bytes for unknown block at {:x}", aiNetwork->nodecount * sizeof(uint32_t), writeStream.tellp());
- uint32_t* unkNodeBlock = new uint32_t[aiNetwork->nodecount];
- memset(unkNodeBlock, 0, aiNetwork->nodecount * sizeof(uint32_t));
- writeStream.write((char*)unkNodeBlock, aiNetwork->nodecount * sizeof(uint32_t));
- delete[] unkNodeBlock;
-
- // TODO: this is traverse nodes i think? these aren't used in tf2 ains so we can get away with just writing count=0 and skipping
- // but ideally should actually dump these
- spdlog::info("writing {} traversal nodes at {:x}...", 0, writeStream.tellp());
- short traverseNodeCount = 0;
- writeStream.write((char*)&traverseNodeCount, sizeof(short));
- // only write count since count=0 means we don't have to actually do anything here
-
- // TODO: ideally these should be actually dumped, but they're always 0 in tf2 from what i can tell
- spdlog::info("writing {} bytes for unknown hull block at {:x}", MAX_HULLS * 8, writeStream.tellp());
- char* unkHullBlock = new char[MAX_HULLS * 8];
- memset(unkHullBlock, 0, MAX_HULLS * 8);
- writeStream.write(unkHullBlock, MAX_HULLS * 8);
- delete[] unkHullBlock;
-
- // unknown struct that's seemingly node-related
- spdlog::info("writing {} unknown node structs at {:x}", *pUnkStruct0Count, writeStream.tellp());
- writeStream.write((char*)pUnkStruct0Count, sizeof(*pUnkStruct0Count));
- for (int i = 0; i < *pUnkStruct0Count; i++)
- {
- spdlog::info("writing unknown node struct {} at {:x}", i, writeStream.tellp());
- UnkNodeStruct0* nodeStruct = (*pppUnkNodeStruct0s)[i];
-
- writeStream.write((char*)&nodeStruct->index, sizeof(nodeStruct->index));
- writeStream.write((char*)&nodeStruct->unk1, sizeof(nodeStruct->unk1));
-
- writeStream.write((char*)&nodeStruct->x, sizeof(nodeStruct->x));
- writeStream.write((char*)&nodeStruct->y, sizeof(nodeStruct->y));
- writeStream.write((char*)&nodeStruct->z, sizeof(nodeStruct->z));
-
- writeStream.write((char*)&nodeStruct->unkcount0, sizeof(nodeStruct->unkcount0));
- for (int j = 0; j < nodeStruct->unkcount0; j++)
- {
- short unk2Short = (short)nodeStruct->unk2[j];
- writeStream.write((char*)&unk2Short, sizeof(unk2Short));
- }
-
- writeStream.write((char*)&nodeStruct->unkcount1, sizeof(nodeStruct->unkcount1));
- for (int j = 0; j < nodeStruct->unkcount1; j++)
- {
- short unk3Short = (short)nodeStruct->unk3[j];
- writeStream.write((char*)&unk3Short, sizeof(unk3Short));
- }
-
- writeStream.write((char*)&nodeStruct->unk5, sizeof(nodeStruct->unk5));
- }
-
- // unknown struct that's seemingly link-related
- spdlog::info("writing {} unknown link structs at {:x}", *pUnkLinkStruct1Count, writeStream.tellp());
- writeStream.write((char*)pUnkLinkStruct1Count, sizeof(*pUnkLinkStruct1Count));
- for (int i = 0; i < *pUnkLinkStruct1Count; i++)
- {
- // disk and memory structs are literally identical here so just directly write
- spdlog::info("writing unknown link struct {} at {:x}", i, writeStream.tellp());
- writeStream.write((char*)(*pppUnkStruct1s)[i], sizeof(*(*pppUnkStruct1s)[i]));
- }
-
- // some weird int idk what this is used for
- writeStream.write((char*)&aiNetwork->unk5, sizeof(aiNetwork->unk5));
-
- // tf2-exclusive stuff past this point, i.e. ain v57 only
- spdlog::info("writing {} script nodes at {:x}", aiNetwork->scriptnodecount, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->scriptnodecount, sizeof(aiNetwork->scriptnodecount));
- for (int i = 0; i < aiNetwork->scriptnodecount; i++)
- {
- // disk and memory structs are literally identical here so just directly write
- spdlog::info("writing script node {} at {:x}", i, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->scriptnodes[i], sizeof(aiNetwork->scriptnodes[i]));
- }
-
- spdlog::info("writing {} hints at {:x}", aiNetwork->hintcount, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->hintcount, sizeof(aiNetwork->hintcount));
- for (int i = 0; i < aiNetwork->hintcount; i++)
- {
- spdlog::info("writing hint data {} at {:x}", i, writeStream.tellp());
- writeStream.write((char*)&aiNetwork->hints[i], sizeof(aiNetwork->hints[i]));
- }
-
- writeStream.close();
-}
-
-// clang-format off
-AUTOHOOK(CAI_NetworkBuilder__Build, server.dll + 0x385E20,
-void, __fastcall, (void* builder, CAI_Network* aiNetwork, void* unknown))
-// clang-format on
-{
- CAI_NetworkBuilder__Build(builder, aiNetwork, unknown);
-
- DumpAINInfo(aiNetwork);
-}
-
-// clang-format off
-AUTOHOOK(LoadAINFile, server.dll + 0x3933A0,
-void, __fastcall, (void* aimanager, void* buf, const char* filename))
-// clang-format on
-{
- LoadAINFile(aimanager, buf, filename);
-
- if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
- {
- spdlog::info("running DumpAINInfo for loaded file {}", filename);
- DumpAINInfo(*(CAI_Network**)((char*)aimanager + 2536));
- }
-}
-
-ON_DLL_LOAD("server.dll", BuildAINFile, (CModule module))
-{
- AUTOHOOK_DISPATCH()
-
- Cvar_ns_ai_dumpAINfileFromLoad = new ConVar(
- "ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk");
-
- pUnkStruct0Count = module.Offset(0x1063BF8).RCast<int*>();
- pppUnkNodeStruct0s = module.Offset(0x1063BE0).RCast<UnkNodeStruct0***>();
- pUnkLinkStruct1Count = module.Offset(0x1063AA8).RCast<int*>();
- pppUnkStruct1s = module.Offset(0x1063A90).RCast<UnkLinkStruct1***>();
-}
diff --git a/NorthstarDLL/server/r2server.cpp b/NorthstarDLL/server/r2server.cpp
deleted file mode 100644
index cf7add0d..00000000
--- a/NorthstarDLL/server/r2server.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "r2server.h"
-
-using namespace R2;
-
-// use the R2 namespace for game funcs
-namespace R2
-{
- CBaseEntity* (*Server_GetEntityByIndex)(int index);
- CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex);
-} // namespace R2
-
-ON_DLL_LOAD("server.dll", R2GameServer, (CModule module))
-{
- Server_GetEntityByIndex = module.Offset(0xFB820).RCast<CBaseEntity* (*)(int)>();
- UTIL_PlayerByIndex = module.Offset(0x26AA10).RCast<CBasePlayer*(__fastcall*)(int)>();
-}
diff --git a/NorthstarDLL/server/r2server.h b/NorthstarDLL/server/r2server.h
deleted file mode 100644
index 8fde7b9d..00000000
--- a/NorthstarDLL/server/r2server.h
+++ /dev/null
@@ -1,110 +0,0 @@
-#pragma once
-
-#include "core/math/vector.h"
-
-// use the R2 namespace for game funcs
-namespace R2
-{
- // server entity stuff
- class CBaseEntity;
- extern CBaseEntity* (*Server_GetEntityByIndex)(int index);
-
- // clang-format off
- OFFSET_STRUCT(CBasePlayer)
- {
- FIELD(0x58, uint32_t m_nPlayerIndex)
-
- FIELD(0x23E8, bool m_grappleActive)
- FIELD(0x1D08, uint32_t m_platformUserId)
- FIELD(0x1D10, int32_t m_classModsActive)
- FIELD(0x1D8C, int32_t m_posClassModsActive)
- FIELD(0x1DCC, bool m_passives)
- FIELD(0x4948, int32_t m_selectedOffhand)
- FIELD(0x1358, int32_t m_selectedOffhandPendingHybridAction)
- FIELD(0x1E88, int32_t m_playerFlags)
- FIELD(0x26A8, int32_t m_lastUCmdSimulationTicks)
- FIELD(0x26AC, float m_lastUCmdSimulationRemainderTime)
- FIELD(0x1F04, int32_t m_remoteTurret)
- FIELD(0x414, int32_t m_hGroundEntity)
- FIELD(0x13B8, int32_t m_titanSoul)
- FIELD(0x2054, int32_t m_petTitan)
- FIELD(0x4D4, int32_t m_iHealth)
- FIELD(0x4D0, int32_t m_iMaxHealth)
- FIELD(0x4F1, int32_t m_lifeState)
- FIELD(0x50C, float m_flMaxspeed)
- FIELD(0x298, int32_t m_fFlags)
- FIELD(0x1F64, int32_t m_iObserverMode)
- FIELD(0x1F6C, int32_t m_hObserverTarget)
- FIELD(0x2098, int32_t m_hViewModel)
- FIELD(0x27E4, int32_t m_ubEFNointerpParity)
- FIELD(0x1FA4, int32_t m_activeBurnCardIndex)
- FIELD(0x1B68, int32_t m_hColorCorrectionCtrl)
- FIELD(0x19E0, int32_t m_PlayerFog__m_hCtrl)
- FIELD(0x26BC, bool m_bShouldDrawPlayerWhileUsingViewEntity)
- FIELD(0x2848, char m_title[32])
- FIELD(0x2964, bool m_useCredit)
- FIELD(0x1F40, float m_damageImpulseNoDecelEndTime)
- FIELD(0x1E8C, bool m_hasMic)
- FIELD(0x1E8D, bool m_inPartyChat)
- FIELD(0x1E90, float m_playerMoveSpeedScale)
- FIELD(0x1F58, float m_flDeathTime)
- FIELD(0x25A8, bool m_iSpawnParity)
- FIELD(0x102284, Vector3 m_upDir)
- FIELD(0x259C, float m_lastDodgeTime)
- FIELD(0x22E0, bool m_wallHanging)
- FIELD(0x22EC, int32_t m_traversalType)
- FIELD(0x22F0, int32_t m_traversalState)
- FIELD(0x2328, Vector3 m_traversalRefPos)
- FIELD(0x231C, Vector3 m_traversalForwardDir)
- FIELD(0x2354, float m_traversalYawDelta)
- FIELD(0x2358, int32_t m_traversalYawPoseParameter)
- FIELD(0x2050, int32_t m_grappleHook)
- FIELD(0x27C0, int32_t m_autoSprintForced)
- FIELD(0x27C4, bool m_fIsSprinting)
- FIELD(0x27CC, float m_sprintStartedTime)
- FIELD(0x27D0, float m_sprintStartedFrac)
- FIELD(0x27D4, float m_sprintEndedTime)
- FIELD(0x27D8, float m_sprintEndedFrac)
- FIELD(0x27DC, float m_stickySprintStartTime)
- FIELD(0x2998, float m_smartAmmoPreviousHighestLockOnMeFractionValue)
- FIELD(0x23FC, int32_t m_activeZipline)
- FIELD(0x2400, bool m_ziplineReverse)
- FIELD(0x2410, int32_t m_ziplineState)
- FIELD(0x2250, int32_t m_duckState)
- FIELD(0x2254, Vector3 m_StandHullMin)
- FIELD(0x2260, Vector3 m_StandHullMax)
- FIELD(0x226C, Vector3 m_DuckHullMin)
- FIELD(0x2278, Vector3 m_DuckHullMax)
- FIELD(0x205C, int32_t m_xp)
- FIELD(0x2060, int32_t m_generation)
- FIELD(0x2064, int32_t m_rank)
- FIELD(0x2068, int32_t m_serverForceIncreasePlayerListGenerationParity)
- FIELD(0x206C, bool m_isPlayingRanked)
- FIELD(0x2070, float m_skill_mu)
- FIELD(0x1E80, int32_t m_titanSoulBeingRodeoed)
- FIELD(0x1E84, int32_t m_entitySyncingWithMe)
- FIELD(0x2078, float m_nextTitanRespawnAvailable)
- FIELD(0x1C90, bool m_hasBadReputation)
- FIELD(0x1C91, char m_communityName[64])
- FIELD(0x1CD1, char m_communityClanTag[16])
- FIELD(0x1CE1, char m_factionName[16])
- FIELD(0x1CF1, char m_hardwareIcon[16])
- FIELD(0x1D01, bool m_happyHourActive)
- FIELD(0x1EF4, int32_t m_gestureAutoKillBitfield)
- FIELD(0x2EA8, int32_t m_pilotClassIndex)
- FIELD(0x100490, Vector3 m_vecAbsOrigin)
- FIELD(0x25BE, bool m_isPerformingBoostAction)
- FIELD(0x240C, bool m_ziplineValid3pWeaponLayerAnim)
- FIELD(0x345C, int32_t m_playerScriptNetDataGlobal)
- FIELD(0x1598, int32_t m_bZooming)
- FIELD(0x1599, bool m_zoomToggleOn)
- FIELD(0x159C, float m_zoomBaseFrac)
- FIELD(0x15A0, float m_zoomBaseTime)
- FIELD(0x15A4, float m_zoomFullStartTime)
- FIELD(0xA04, int32_t m_camoIndex)
- FIELD(0xA08, int32_t m_decalIndex)
- };
- // clang-format on
-
- extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex);
-} // namespace R2
diff --git a/NorthstarDLL/server/serverchathooks.cpp b/NorthstarDLL/server/serverchathooks.cpp
deleted file mode 100644
index cb3af244..00000000
--- a/NorthstarDLL/server/serverchathooks.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-#include "serverchathooks.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "squirrel/squirrel.h"
-#include "server/r2server.h"
-#include "util/utils.h"
-
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
-
-AUTOHOOK_INIT()
-
-class CServerGameDLL;
-
-class CRecipientFilter
-{
- char unknown[58];
-};
-
-CServerGameDLL* g_pServerGameDLL;
-
-void(__fastcall* CServerGameDLL__OnReceivedSayTextMessage)(
- CServerGameDLL* self, unsigned int senderPlayerId, const char* text, int channelId);
-
-void(__fastcall* CRecipientFilter__Construct)(CRecipientFilter* self);
-void(__fastcall* CRecipientFilter__Destruct)(CRecipientFilter* self);
-void(__fastcall* CRecipientFilter__AddAllPlayers)(CRecipientFilter* self);
-void(__fastcall* CRecipientFilter__AddRecipient)(CRecipientFilter* self, const R2::CBasePlayer* player);
-void(__fastcall* CRecipientFilter__MakeReliable)(CRecipientFilter* self);
-
-void(__fastcall* UserMessageBegin)(CRecipientFilter* filter, const char* messagename);
-void(__fastcall* MessageEnd)();
-void(__fastcall* MessageWriteByte)(int iValue);
-void(__fastcall* MessageWriteString)(const char* sz);
-void(__fastcall* MessageWriteBool)(bool bValue);
-
-bool bShouldCallSayTextHook = false;
-// clang-format off
-AUTOHOOK(_CServerGameDLL__OnReceivedSayTextMessage, server.dll + 0x1595C0,
-void, __fastcall, (CServerGameDLL* self, unsigned int senderPlayerId, const char* text, bool isTeam))
-// clang-format on
-{
- NS::Utils::RemoveAsciiControlSequences(const_cast<char*>(text), true);
-
- // MiniHook doesn't allow calling the base function outside of anywhere but the hook function.
- // To allow bypassing the hook, isSkippingHook can be set.
- if (bShouldCallSayTextHook)
- {
- bShouldCallSayTextHook = false;
- _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam);
- return;
- }
-
- // check chat ratelimits
- if (!g_pServerLimits->CheckChatLimits(&R2::g_pClientArray[senderPlayerId - 1]))
- return;
-
- SQRESULT result = g_pSquirrel<ScriptContext::SERVER>->Call(
- "CServerGameDLL_ProcessMessageStartThread", static_cast<int>(senderPlayerId) - 1, text, isTeam);
-
- if (result == SQRESULT_ERROR)
- _CServerGameDLL__OnReceivedSayTextMessage(self, senderPlayerId, text, isTeam);
-}
-
-void ChatSendMessage(unsigned int playerIndex, const char* text, bool isTeam)
-{
- bShouldCallSayTextHook = true;
- CServerGameDLL__OnReceivedSayTextMessage(
- g_pServerGameDLL,
- // Ensure the first bit isn't set, since this indicates a custom message
- (playerIndex + 1) & CUSTOM_MESSAGE_INDEX_MASK,
- text,
- isTeam);
-}
-
-void ChatBroadcastMessage(int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType)
-{
- R2::CBasePlayer* toPlayer = NULL;
- if (toPlayerIndex >= 0)
- {
- toPlayer = R2::UTIL_PlayerByIndex(toPlayerIndex + 1);
- if (toPlayer == NULL)
- return;
- }
-
- // Build a new string where the first byte is the message type
- char sendText[256];
- sendText[0] = (char)messageType;
- 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);
-
- CRecipientFilter filter;
- CRecipientFilter__Construct(&filter);
- if (toPlayer == NULL)
- {
- CRecipientFilter__AddAllPlayers(&filter);
- }
- else
- {
- CRecipientFilter__AddRecipient(&filter, toPlayer);
- }
- CRecipientFilter__MakeReliable(&filter);
-
- UserMessageBegin(&filter, "SayText");
- MessageWriteByte(fromPlayerId);
- MessageWriteString(sendText);
- MessageWriteBool(isTeam);
- MessageWriteBool(isDead);
- MessageEnd();
-
- CRecipientFilter__Destruct(&filter);
-}
-
-ADD_SQFUNC("void", NSSendMessage, "int playerIndex, string text, bool isTeam", "", ScriptContext::SERVER)
-{
- int playerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 1);
- const char* text = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 2);
- bool isTeam = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 3);
-
- ChatSendMessage(playerIndex, text, isTeam);
-
- return SQRESULT_NULL;
-}
-
-ADD_SQFUNC(
- "void",
- NSBroadcastMessage,
- "int fromPlayerIndex, int toPlayerIndex, string text, bool isTeam, bool isDead, int messageType",
- "",
- ScriptContext::SERVER)
-{
- int fromPlayerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 1);
- int toPlayerIndex = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 2);
- const char* text = g_pSquirrel<ScriptContext::SERVER>->getstring(sqvm, 3);
- bool isTeam = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 4);
- bool isDead = g_pSquirrel<ScriptContext::SERVER>->getbool(sqvm, 5);
- int messageType = g_pSquirrel<ScriptContext::SERVER>->getinteger(sqvm, 6);
-
- if (messageType < 1)
- {
- g_pSquirrel<ScriptContext::SERVER>->raiseerror(sqvm, fmt::format("Invalid message type {}", messageType).c_str());
- return SQRESULT_ERROR;
- }
-
- ChatBroadcastMessage(fromPlayerIndex, toPlayerIndex, text, isTeam, isDead, (CustomMessageType)messageType);
-
- return SQRESULT_NULL;
-}
-
-ON_DLL_LOAD("engine.dll", EngineServerChatHooks, (CModule module))
-{
- g_pServerGameDLL = module.Offset(0x13F0AA98).RCast<CServerGameDLL*>();
-}
-
-ON_DLL_LOAD_RELIESON("server.dll", ServerChatHooks, ServerSquirrel, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(server.dll)
-
- CServerGameDLL__OnReceivedSayTextMessage =
- module.Offset(0x1595C0).RCast<void(__fastcall*)(CServerGameDLL*, unsigned int, const char*, int)>();
- CRecipientFilter__Construct = module.Offset(0x1E9440).RCast<void(__fastcall*)(CRecipientFilter*)>();
- CRecipientFilter__Destruct = module.Offset(0x1E9700).RCast<void(__fastcall*)(CRecipientFilter*)>();
- CRecipientFilter__AddAllPlayers = module.Offset(0x1E9940).RCast<void(__fastcall*)(CRecipientFilter*)>();
- CRecipientFilter__AddRecipient = module.Offset(0x1E9B30).RCast<void(__fastcall*)(CRecipientFilter*, const R2::CBasePlayer*)>();
- CRecipientFilter__MakeReliable = module.Offset(0x1EA4E0).RCast<void(__fastcall*)(CRecipientFilter*)>();
-
- UserMessageBegin = module.Offset(0x15C520).RCast<void(__fastcall*)(CRecipientFilter*, const char*)>();
- MessageEnd = module.Offset(0x158880).RCast<void(__fastcall*)()>();
- MessageWriteByte = module.Offset(0x158A90).RCast<void(__fastcall*)(int)>();
- MessageWriteString = module.Offset(0x158D00).RCast<void(__fastcall*)(const char*)>();
- MessageWriteBool = module.Offset(0x158A00).RCast<void(__fastcall*)(bool)>();
-}
diff --git a/NorthstarDLL/server/serverchathooks.h b/NorthstarDLL/server/serverchathooks.h
deleted file mode 100644
index d033e769..00000000
--- a/NorthstarDLL/server/serverchathooks.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-
-enum class CustomMessageType : char
-{
- Chat = 1,
- Whisper = 2
-};
-
-constexpr unsigned char CUSTOM_MESSAGE_INDEX_BIT = 0b10000000;
-constexpr unsigned char CUSTOM_MESSAGE_INDEX_MASK = (unsigned char)~CUSTOM_MESSAGE_INDEX_BIT;
-
-// Send a vanilla chat message as if it was from the player.
-void ChatSendMessage(unsigned int playerIndex, const char* text, bool isteam);
-
-// Send a custom message.
-// fromPlayerIndex: set to -1 for a [SERVER] message, or another value to send from a specific player
-// toPlayerIndex: set to -1 to send to all players, or another value to send to a single player
-// isTeam: display a [TEAM] badge
-// isDead: display a [DEAD] badge
-// messageType: send a specific message type
-void ChatBroadcastMessage(
- int fromPlayerIndex, int toPlayerIndex, const char* text, bool isTeam, bool isDead, CustomMessageType messageType);
diff --git a/NorthstarDLL/server/servernethooks.cpp b/NorthstarDLL/server/servernethooks.cpp
deleted file mode 100644
index f74f2d38..00000000
--- a/NorthstarDLL/server/servernethooks.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-#include "core/convar/convar.h"
-#include "engine/r2engine.h"
-#include "shared/exploit_fixes/ns_limits.h"
-#include "masterserver/masterserver.h"
-
-#include <string>
-#include <thread>
-#include <bcrypt.h>
-
-AUTOHOOK_INIT()
-
-static ConVar* Cvar_net_debug_atlas_packet;
-static ConVar* Cvar_net_debug_atlas_packet_insecure;
-
-static BCRYPT_ALG_HANDLE HMACSHA256;
-constexpr size_t HMACSHA256_LEN = 256 / 8;
-
-static bool InitHMACSHA256()
-{
- NTSTATUS status;
- DWORD hashLength = 0;
- ULONG hashLengthSz = 0;
-
- if ((status = BCryptOpenAlgorithmProvider(&HMACSHA256, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG)))
- {
- spdlog::error("failed to initialize HMAC-SHA256: BCryptOpenAlgorithmProvider: error 0x{:08X}", (ULONG)status);
- return false;
- }
-
- if ((status = BCryptGetProperty(HMACSHA256, BCRYPT_HASH_LENGTH, (PUCHAR)&hashLength, sizeof(hashLength), &hashLengthSz, 0)))
- {
- spdlog::error("failed to initialize HMAC-SHA256: BCryptGetProperty(BCRYPT_HASH_LENGTH): error 0x{:08X}", (ULONG)status);
- return false;
- }
-
- if (hashLength != HMACSHA256_LEN)
- {
- spdlog::error("failed to initialize HMAC-SHA256: BCryptGetProperty(BCRYPT_HASH_LENGTH): unexpected value {}", hashLength);
- return false;
- }
-
- return true;
-}
-
-// compare the HMAC-SHA256(data, key) against sig (note: all strings are treated as raw binary data)
-static bool VerifyHMACSHA256(std::string key, std::string sig, std::string data)
-{
- uint8_t invalid = 1;
- char hash[HMACSHA256_LEN];
-
- NTSTATUS status;
- BCRYPT_HASH_HANDLE h = NULL;
-
- if ((status = BCryptCreateHash(HMACSHA256, &h, NULL, 0, (PUCHAR)key.c_str(), (ULONG)key.length(), 0)))
- {
- spdlog::error("failed to verify HMAC-SHA256: BCryptCreateHash: error 0x{:08X}", (ULONG)status);
- goto cleanup;
- }
-
- if ((status = BCryptHashData(h, (PUCHAR)data.c_str(), (ULONG)data.length(), 0)))
- {
- spdlog::error("failed to verify HMAC-SHA256: BCryptHashData: error 0x{:08X}", (ULONG)status);
- goto cleanup;
- }
-
- if ((status = BCryptFinishHash(h, (PUCHAR)&hash, (ULONG)sizeof(hash), 0)))
- {
- spdlog::error("failed to verify HMAC-SHA256: BCryptFinishHash: error 0x{:08X}", (ULONG)status);
- goto cleanup;
- }
-
- // constant-time compare
- if (sig.length() == sizeof(hash))
- {
- invalid = 0;
- for (size_t i = 0; i < sizeof(hash); i++)
- invalid |= (uint8_t)(sig[i]) ^ (uint8_t)(hash[i]);
- }
-
-cleanup:
- if (h)
- BCryptDestroyHash(h);
- return !invalid;
-}
-
-// v1 HMACSHA256-signed masterserver request (HMAC-SHA256(JSONData, MasterServerToken) + JSONData)
-static void ProcessAtlasConnectionlessPacketSigreq1(R2::netpacket_t* packet, bool dbg, std::string pType, std::string pData)
-{
- if (pData.length() < HMACSHA256_LEN)
- {
- if (dbg)
- spdlog::warn("ignoring Atlas connectionless packet (size={} type={}): invalid: too short for signature", packet->size, pType);
- return;
- }
-
- std::string pSig; // is binary data, not actually an ASCII string
- pSig = pData.substr(0, HMACSHA256_LEN);
- pData = pData.substr(HMACSHA256_LEN);
-
- if (!g_pMasterServerManager || !g_pMasterServerManager->m_sOwnServerAuthToken[0])
- {
- if (dbg)
- spdlog::warn(
- "ignoring Atlas connectionless packet (size={} type={}): invalid (data={}): no masterserver token yet",
- packet->size,
- pType,
- pData);
- return;
- }
-
- if (!VerifyHMACSHA256(std::string(g_pMasterServerManager->m_sOwnServerAuthToken), pSig, pData))
- {
- if (!Cvar_net_debug_atlas_packet_insecure->GetBool())
- {
- if (dbg)
- spdlog::warn(
- "ignoring Atlas connectionless packet (size={} type={}): invalid: invalid signature (key={})",
- packet->size,
- pType,
- std::string(g_pMasterServerManager->m_sOwnServerAuthToken));
- return;
- }
- spdlog::warn(
- "processing Atlas connectionless packet (size={} type={}) with invalid signature due to net_debug_atlas_packet_insecure",
- packet->size,
- pType);
- }
-
- if (dbg)
- spdlog::info("got Atlas connectionless packet (size={} type={} data={})", packet->size, pType, pData);
-
- std::thread t(&MasterServerManager::ProcessConnectionlessPacketSigreq1, g_pMasterServerManager, pData);
- t.detach();
-
- return;
-}
-
-static void ProcessAtlasConnectionlessPacket(R2::netpacket_t* packet)
-{
- bool dbg = Cvar_net_debug_atlas_packet->GetBool();
-
- // extract kind, null-terminated type, data
- std::string pType, pData;
- for (int i = 5; i < packet->size; i++)
- {
- if (packet->data[i] == '\x00')
- {
- pType.assign((char*)(&packet->data[5]), (size_t)(i - 5));
- if (i + 1 < packet->size)
- pData.assign((char*)(&packet->data[i + 1]), (size_t)(packet->size - i - 1));
- break;
- }
- }
-
- // note: all Atlas connectionless packets should be idempotent so multiple attempts can be made to mitigate packet loss
- // note: all long-running Atlas connectionless packet handlers should be started in a new thread (with copies of the data) to avoid
- // blocking networking
-
- // v1 HMACSHA256-signed masterserver request
- if (pType == "sigreq1")
- {
- ProcessAtlasConnectionlessPacketSigreq1(packet, dbg, pType, pData);
- return;
- }
-
- if (dbg)
- spdlog::warn("ignoring Atlas connectionless packet (size={} type={}): unknown type", packet->size, pType);
- return;
-}
-
-AUTOHOOK(ProcessConnectionlessPacket, engine.dll + 0x117800, bool, , (void* a1, R2::netpacket_t* packet))
-{
- // packet->data consists of 0xFFFFFFFF (int32 -1) to indicate packets aren't split, followed by a header consisting of a single
- // character, which is used to uniquely identify the packet kind. Most kinds follow this with a null-terminated string payload
- // then an arbitrary amoount of data.
-
- // T (no rate limits since we authenticate packets before doing anything expensive)
- if (4 < packet->size && packet->data[4] == 'T')
- {
- ProcessAtlasConnectionlessPacket(packet);
- return false;
- }
-
- // check rate limits for the original unconnected packets
- if (!g_pServerLimits->CheckConnectionlessPacketLimits(packet))
- return false;
-
- // A, H, I, N
- return ProcessConnectionlessPacket(a1, packet);
-}
-
-ON_DLL_LOAD_RELIESON("engine.dll", ServerNetHooks, ConVar, (CModule module))
-{
- AUTOHOOK_DISPATCH_MODULE(engine.dll)
-
- if (!InitHMACSHA256())
- throw std::runtime_error("failed to initialize bcrypt");
-
- if (!VerifyHMACSHA256(
- "test",
- "\x88\xcd\x21\x08\xb5\x34\x7d\x97\x3c\xf3\x9c\xdf\x90\x53\xd7\xdd\x42\x70\x48\x76\xd8\xc9\xa9\xbd\x8e\x2d\x16\x82\x59\xd3\xdd"
- "\xf7",
- "test"))
- throw std::runtime_error("bcrypt HMAC-SHA256 is broken");
-
- Cvar_net_debug_atlas_packet = new ConVar(
- "net_debug_atlas_packet",
- "0",
- FCVAR_NONE,
- "Whether to log detailed debugging information for Atlas connectionless packets (warning: this allows unlimited amounts of "
- "arbitrary data to be logged)");
-
- Cvar_net_debug_atlas_packet_insecure = new ConVar(
- "net_debug_atlas_packet_insecure",
- "0",
- FCVAR_NONE,
- "Whether to disable signature verification for Atlas connectionless packets (DANGEROUS: this allows anyone to impersonate Atlas)");
-}
diff --git a/NorthstarDLL/server/serverpresence.cpp b/NorthstarDLL/server/serverpresence.cpp
deleted file mode 100644
index 159b9f30..00000000
--- a/NorthstarDLL/server/serverpresence.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "serverpresence.h"
-#include "shared/playlist.h"
-#include "core/tier0.h"
-#include "core/convar/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;
-}
-
-void ServerPresenceManager::CreateConVars()
-{
- // clang-format off
- // 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 name", 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 description", 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");
- // clang-format on
-}
-
-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, map/playlist name, and playercount/maxplayers
- m_ServerPresence.m_iPort = 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_ServerPresence.m_bIsSingleplayerServer = false;
-
- m_bHasPresence = true;
- m_bFirstPresenceUpdate = 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 || !Cvar_ns_report_server_to_masterserver->GetBool()) // don't run until we actually have server presence
- return;
-
- // don't run if we're sp and don't want to report sp
- if (m_ServerPresence.m_bIsSingleplayerServer && !Cvar_ns_report_sp_server_to_masterserver->GetBool())
- return;
-
- // Call RunFrame() so that reporters can, for example, handle std::future results as soon as they arrive.
- for (ServerPresenceReporter* reporter : m_vPresenceReporters)
- reporter->RunFrame(flCurrentTime, &m_ServerPresence);
-
- // run on a specified delay
- if ((flCurrentTime - m_flLastPresenceUpdate) * 1000 < Cvar_ns_server_presence_update_rate->GetFloat())
- return;
-
- // is this the first frame we're updating this presence?
- if (m_bFirstPresenceUpdate)
- {
- // let reporters setup/clear any state
- for (ServerPresenceReporter* reporter : m_vPresenceReporters)
- reporter->CreatePresence(&m_ServerPresence);
-
- m_bFirstPresenceUpdate = false;
- }
-
- 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::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, bool isInitialising)
-{
- // if the server is initialising (i.e. this is first map) on sp, set the server to sp
- if (isInitialising)
- m_ServerPresence.m_bIsSingleplayerServer = !strncmp(pMapName, "sp_", 3);
-
- // 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, (CModule module))
-{
- g_pServerPresence->CreateConVars();
- Cvar_hostname = module.Offset(0x1315BAE8).Deref().RCast<ConVar*>();
-}
diff --git a/NorthstarDLL/server/serverpresence.h b/NorthstarDLL/server/serverpresence.h
deleted file mode 100644
index 3aabecde..00000000
--- a/NorthstarDLL/server/serverpresence.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#pragma once
-#include "core/convar/convar.h"
-
-struct ServerPresence
-{
- public:
- int m_iPort;
-
- std::string m_sServerId;
-
- 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];
- bool m_bIsSingleplayerServer; // whether the server started in sp
-
- int m_iPlayerCount;
- int m_iMaxPlayers;
-
- ServerPresence()
- {
- memset(this, 0, sizeof(this));
- }
-
- ServerPresence(const ServerPresence* obj)
- {
- m_iPort = obj->m_iPort;
-
- m_sServerId = obj->m_sServerId;
-
- 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 CreatePresence(const ServerPresence* pServerPresence) {}
- virtual void ReportPresence(const ServerPresence* pServerPresence) {}
- virtual void DestroyPresence(const ServerPresence* pServerPresence) {}
- virtual void RunFrame(double flCurrentTime, const ServerPresence* pServerPresence) {}
-};
-
-class ServerPresenceManager
-{
- private:
- ServerPresence m_ServerPresence;
-
- bool m_bHasPresence = false;
- bool m_bFirstPresenceUpdate = 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:
- void AddPresenceReporter(ServerPresenceReporter* reporter);
-
- void CreateConVars();
-
- void CreatePresence();
- void DestroyPresence();
- void RunFrame(double flCurrentTime);
-
- void SetPort(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, bool isInitialising = false);
- void SetPlaylist(const char* pPlaylistName);
- void SetPlayerCount(const int iPlayerCount);
-};
-
-extern ServerPresenceManager* g_pServerPresence;