diff options
Diffstat (limited to 'NorthstarDLL/server')
-rw-r--r-- | NorthstarDLL/server/alltalk.cpp | 28 | ||||
-rw-r--r-- | NorthstarDLL/server/auth/bansystem.cpp | 224 | ||||
-rw-r--r-- | NorthstarDLL/server/auth/bansystem.h | 19 | ||||
-rw-r--r-- | NorthstarDLL/server/auth/serverauthentication.cpp | 380 | ||||
-rw-r--r-- | NorthstarDLL/server/auth/serverauthentication.h | 58 | ||||
-rw-r--r-- | NorthstarDLL/server/buildainfile.cpp | 395 | ||||
-rw-r--r-- | NorthstarDLL/server/r2server.cpp | 16 | ||||
-rw-r--r-- | NorthstarDLL/server/r2server.h | 110 | ||||
-rw-r--r-- | NorthstarDLL/server/serverchathooks.cpp | 174 | ||||
-rw-r--r-- | NorthstarDLL/server/serverchathooks.h | 24 | ||||
-rw-r--r-- | NorthstarDLL/server/servernethooks.cpp | 218 | ||||
-rw-r--r-- | NorthstarDLL/server/serverpresence.cpp | 228 | ||||
-rw-r--r-- | NorthstarDLL/server/serverpresence.h | 94 |
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; |