From 9b848d1aa6c791b2ed9e594b62753caf5d3037de Mon Sep 17 00:00:00 2001 From: Sachin Date: Thu, 4 Jun 2026 21:48:15 +0530 Subject: [PATCH 1/2] feat: use filter message --- AMBuilder | 2 - gamedata/statusmodifier.jsonc | 15 +- playermanager.h | 2 +- serversideclient.h | 3 +- statusmodifier.cpp | 221 +++++++++-------------------- statusmodifier.h | 6 +- utils.h | 56 ++++---- utils/module.h | 156 -------------------- utils/plat.h | 63 -------- utils/plat_unix.cpp | 260 ---------------------------------- utils/plat_win.cpp | 107 -------------- utils/plat_win.h | 1 - 12 files changed, 107 insertions(+), 785 deletions(-) delete mode 100644 utils/module.h delete mode 100644 utils/plat.h delete mode 100644 utils/plat_unix.cpp delete mode 100644 utils/plat_win.cpp delete mode 100644 utils/plat_win.h diff --git a/AMBuilder b/AMBuilder index 1950c4c..8d50cb6 100644 --- a/AMBuilder +++ b/AMBuilder @@ -33,7 +33,6 @@ for sdk_target in MMSPlugin.sdk_targets: os.path.join('psapi.lib'), os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'funchook.lib'), os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'distorm.lib'), - os.path.join(sdk['path'], 'lib', 'public', 'win64', 'steam_api64.lib') ] binary.sources += [ @@ -41,7 +40,6 @@ for sdk_target in MMSPlugin.sdk_targets: 'gameconfig.cpp', os.path.join('..', 'SchemaEntity', 'schemasystem.cpp'), os.path.join('..', 'SchemaEntity', 'module.cpp'), - os.path.join('..', 'SchemaEntity', 'ctimer.cpp'), os.path.join(sdk['path'], 'entity2', 'entitysystem.cpp'), os.path.join(sdk['path'], 'entity2', 'entityidentity.cpp'), ] diff --git a/gamedata/statusmodifier.jsonc b/gamedata/statusmodifier.jsonc index 2f822e6..e4110e2 100644 --- a/gamedata/statusmodifier.jsonc +++ b/gamedata/statusmodifier.jsonc @@ -1,17 +1,22 @@ { "Signatures": { - // Search "NOTE: Printing secret stuff because non-public Steam universe.\n" - "StatusCommandFull": { + // Find string 'Client %d(%s) tried to send a RebroadcastSourceId msg.' which is in + // a logging call. On linux search for the one which is being used in CServerSideClient. + "FilterMessage": { "library": "engine", - "windows": "48 89 5C 24 ? 44 89 44 24 ? 89 54 24 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 33 DB", - "linux": "48 B8 ? ? ? ? ? ? ? ? 55 48 89 E5 41 57 41 56 49 89 FE 41 55 41 54 53" + "windows": "40 53 48 83 EC ? 48 8B 02 48 8B D9 48 8B CA", + "linux": "55 48 89 E5 53 48 89 FB 48 89 F7 48 83 EC ? 48 8B 06 FF 50 ? 48 89 C2 31 C0 48 39 15 ? ? ? ? 74 ? 48 8B 5D ? C9 C3 0F 1F 80 ? ? ? ? 8B 3D ? ? ? ? BE ? ? ? ? E8 ? ? ? ? 84 C0 74 ? 48 8B 43 ? 4C 8D 05 ? ? ? ? BE ? ? ? ? 0F B7 4B" } }, "Offsets": { "GameEntitySystem": { "windows": 88, "linux": 80 - } + }, + "GetPublicAdr": { + "windows": 32, + "linux": 32 + }, }, "Patches": {} } \ No newline at end of file diff --git a/playermanager.h b/playermanager.h index f46cea7..3e3c033 100644 --- a/playermanager.h +++ b/playermanager.h @@ -160,4 +160,4 @@ class CPlayerManager int m_iPlayerCount; }; -extern CPlayerManager *g_playerManager; \ No newline at end of file +extern CPlayerManager *g_PlayerManager; \ No newline at end of file diff --git a/serversideclient.h b/serversideclient.h index c473acb..f1e7951 100644 --- a/serversideclient.h +++ b/serversideclient.h @@ -14,6 +14,7 @@ #include #include +#include "protobuf/generated/netmessages.pb.h" class INetMessage; class CNetworkGameServerBase; @@ -89,7 +90,7 @@ class CServerSideClientBase : public CUtlSlot, public INetworkChannelNotify, pub virtual bool IsFakeClient() = 0; virtual bool IsHumanPlayer() = 0; - CPlayerSlot GetPlayerSlot() const { return m_nClientSlot; } + CPlayerSlot GetPlayerSlot(bool offset = false) const { return offset ? CPlayerSlot(*(int *)&m_Name) : m_nClientSlot; } CPlayerUserId GetUserID() const { return m_UserID; } CEntityIndex GetEntityIndex() const { return m_nEntityIndex; } CSteamID GetClientSteamID() const { return m_SteamID; } diff --git a/statusmodifier.cpp b/statusmodifier.cpp index 9f08395..5e74e5c 100644 --- a/statusmodifier.cpp +++ b/statusmodifier.cpp @@ -19,32 +19,24 @@ #include "gameconfig.h" #include "playermanager.h" #include "recipientfilter.h" -#include "serversideclient.h" #include "utils.h" StatusModifier g_StatusModifier; PLUGIN_EXPOSE(StatusModifier, g_StatusModifier); -IVEngineServer2 *engine = nullptr; +IVEngineServer2 *g_pEngineServer2 = nullptr; CGameEntitySystem *g_pEntitySystem = nullptr; -IGameEventSystem *g_gameEventSystem = nullptr; -ISteamGameServer *g_pGameServer = nullptr; -CSteamGameServerAPIContext steamctx; +IGameEventSystem *g_pGameEventSystem = nullptr; CGameConfig *g_GameConfig = nullptr; -CPlayerManager *g_playerManager = nullptr; - -double g_flUniversalTime; -float g_flLastTickedTime; -bool g_bHasTicked; +CPlayerManager *g_PlayerManager = nullptr; std::unordered_set g_mExcludeSlots; -void (*StatusFullPrintClient_t)(CNetworkGameServerBase *pBase, - int slot) = nullptr; +void (*FilterMessage_t)(CServerSideClient *pClient, CNetMessage *pMessage, INetChannel *pChannel) = nullptr; using namespace DynLibUtils; -funchook_t *m_StatusFullHook; +funchook_t *m_FilterMessageHook; std::vector g_StatusArray; std::string g_sServerIP; @@ -59,7 +51,7 @@ CGameEntitySystem *GameEntitySystem() } // Will return null between map end & new map startup, null check if necessary! -CGlobalVars *GetGlobals() { return engine->GetServerGlobals(); } +CGlobalVars *GetGlobals() { return g_pEngineServer2->GetServerGlobals(); } class GameSessionConfiguration_t { @@ -68,8 +60,6 @@ class GameSessionConfiguration_t SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t &, ISource2WorldSession *, const char *); -SH_DECL_HOOK3_void(IServerGameDLL, GameFrame, SH_NOATTRIB, 0, bool, bool, bool); -SH_DECL_HOOK0_void(IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0); SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot, ENetworkDisconnectionReason, const char *, uint64, const char *); @@ -80,48 +70,14 @@ SH_DECL_HOOK6(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, CPlayerSlot, const char *, uint64, const char *, bool, CBufferString *); -void FASTCALL Hook_StatusFullPrintClient(CNetworkGameServerBase *pBase, - int slot) -{ - // StatusFullPrintClient_t(pBase, slot); - - bool playerlist = false; - std::string buffer; - for (const std::string &message : g_StatusArray) - { - if (message.rfind("#HEADER#", 0) == 0) - { - ClientPrint(slot, HUD_PRINTCONSOLE, message.c_str() + 8); - continue; - } - - if (!playerlist && message.find("{PLAYER") != std::string::npos && message.find("{PLAYERC") == std::string::npos) - { - for (int i = 0; i < MAXPLAYERS; i++) - { - if (g_mExcludeSlots.find(i) != g_mExcludeSlots.end()) - continue; - - buffer = CheckMessageVariables(message, i, g_HeaderLayout); - if (buffer.length() > 0) - ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); - } - playerlist = true; - continue; - } - - buffer = CheckMessageVariables(message, -1, g_HeaderLayout); - if (buffer.length() > 0) - ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); - } -} +void Hook_FilterMessage(CServerSideClient *pClient, CNetMessage *pMessage, INetChannel *pChannel); bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) { PLUGIN_SAVEVARS(); - GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer2, + GET_V_IFACE_CURRENT(GetEngineFactory, g_pEngineServer2, IVEngineServer2, SOURCE2ENGINETOSERVER_INTERFACE_VERSION); GET_V_IFACE_CURRENT(GetFileSystemFactory, g_pFullFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION); @@ -130,27 +86,21 @@ bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, GAMERESOURCESERVICESERVER_INTERFACE_VERSION); GET_V_IFACE_ANY(GetEngineFactory, g_pSchemaSystem, ISchemaSystem, SCHEMASYSTEM_INTERFACE_VERSION); - GET_V_IFACE_ANY(GetServerFactory, g_pSource2Server, ISource2Server, - SOURCE2SERVER_INTERFACE_VERSION); GET_V_IFACE_ANY(GetEngineFactory, g_pNetworkServerService, INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION); GET_V_IFACE_ANY(GetEngineFactory, g_pNetworkMessages, INetworkMessages, NETWORKMESSAGES_INTERFACE_VERSION); - GET_V_IFACE_ANY(GetEngineFactory, g_gameEventSystem, IGameEventSystem, + GET_V_IFACE_ANY(GetEngineFactory, g_pGameEventSystem, IGameEventSystem, GAMEEVENTSYSTEM_INTERFACE_VERSION); GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); GET_V_IFACE_ANY(GetServerFactory, g_pSource2GameClients, IServerGameClients, SOURCE2GAMECLIENTS_INTERFACE_VERSION); + GET_V_IFACE_ANY(GetEngineFactory, g_pNetworkSystem, INetworkSystem, + NETWORKSYSTEM_INTERFACE_VERSION); SH_ADD_HOOK(INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER(this, &StatusModifier::Hook_StartupServer), true); - SH_ADD_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, - SH_MEMBER(this, &StatusModifier::Hook_GameFrame), true); - SH_ADD_HOOK( - IServerGameDLL, GameServerSteamAPIActivated, g_pSource2Server, - SH_MEMBER(this, &StatusModifier::Hook_GameServerSteamAPIActivated), - false); SH_ADD_HOOK(IServerGameClients, ClientDisconnect, g_pSource2GameClients, SH_MEMBER(this, &StatusModifier::Hook_ClientDisconnect), true); SH_ADD_HOOK(IServerGameClients, OnClientConnected, g_pSource2GameClients, @@ -168,26 +118,25 @@ bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, return false; } - CModule libengine(engine); + CModule libengine(g_pEngineServer2); - const char *szSignature = g_GameConfig->GetSignature("StatusCommandFull"); + const char *szSignature = g_GameConfig->GetSignature("FilterMessage"); - StatusFullPrintClient_t = libengine.FindPattern(szSignature) - .RCast(); - if (!StatusFullPrintClient_t) + FilterMessage_t = libengine.FindPattern(szSignature) + .RCast(); + if (!FilterMessage_t) { - ErrorLog("[%s] Failed to find function to get StatusPrintFull of client", + snprintf(error, maxlen, "Failed to find function to get FilterMessage"); + ErrorLog("[%s] Failed to find function to get FilterMessage", g_PLAPI->GetLogTag()); return false; } - else - { - m_StatusFullHook = funchook_create(); - funchook_prepare(m_StatusFullHook, (void **)&StatusFullPrintClient_t, - (void *)Hook_StatusFullPrintClient); - funchook_install(m_StatusFullHook, 0); - ConMsg("[StatusModifier] StatusPrintFull of client hooked successfully.\n"); - } + + m_FilterMessageHook = funchook_create(); + funchook_prepare(m_FilterMessageHook, (void **)&FilterMessage_t, + (void *)Hook_FilterMessage); + funchook_install(m_FilterMessageHook, 0); + ConMsg("[StatusModifier] FilterMessage hooked successfully.\n"); META_CONVAR_REGISTER(FCVAR_RELEASE | FCVAR_GAMEDLL); @@ -195,15 +144,14 @@ bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, g_SMAPI->AddListener(this, this); - g_playerManager = new CPlayerManager(); + g_PlayerManager = new CPlayerManager(); g_chServerStartTime = std::chrono::steady_clock::now(); if (late) { g_pEntitySystem = GameEntitySystem(); - g_playerManager->OnLateLoad(); - Hook_GameServerSteamAPIActivated(); + g_PlayerManager->OnLateLoad(); } return true; @@ -215,12 +163,6 @@ bool StatusModifier::Unload(char *error, size_t maxlen) SH_REMOVE_HOOK(INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER(this, &StatusModifier::Hook_StartupServer), true); - SH_REMOVE_HOOK(IServerGameDLL, GameFrame, g_pSource2Server, - SH_MEMBER(this, &StatusModifier::Hook_GameFrame), true); - SH_REMOVE_HOOK( - IServerGameDLL, GameServerSteamAPIActivated, g_pSource2Server, - SH_MEMBER(this, &StatusModifier::Hook_GameServerSteamAPIActivated), - false); SH_REMOVE_HOOK(IServerGameClients, ClientDisconnect, g_pSource2GameClients, SH_MEMBER(this, &StatusModifier::Hook_ClientDisconnect), true); SH_REMOVE_HOOK(IServerGameClients, OnClientConnected, g_pSource2GameClients, @@ -229,13 +171,11 @@ bool StatusModifier::Unload(char *error, size_t maxlen) SH_REMOVE_HOOK(IServerGameClients, ClientConnect, g_pSource2GameClients, SH_MEMBER(this, &StatusModifier::Hook_ClientConnect), false); - if (m_StatusFullHook) - funchook_destroy(m_StatusFullHook); - - RemoveTimers(); + if (m_FilterMessageHook) + funchook_destroy(m_FilterMessageHook); - if (g_playerManager) - delete g_playerManager; + if (g_PlayerManager) + delete g_PlayerManager; return true; } @@ -245,80 +185,55 @@ void StatusModifier::Hook_StartupServer( const char *pszMapName) { g_pEntitySystem = GameEntitySystem(); - g_bHasTicked = false; g_mExcludeSlots.clear(); } -void StatusModifier::Hook_GameFrame(bool simulating, bool bFirstTick, - bool bLastTick) +void FASTCALL Hook_FilterMessage(CServerSideClient *pClient, CNetMessage *pMessage, INetChannel *pChannel) { - /** - * simulating: - * *********** - * true | game is ticking - * false | game is not ticking - */ - - VPROF_BUDGET("StatusModifier::Hook_GameFramePost", "StatusModifierPerFrame"); - - if (!GetGlobals()) + if (!pClient || !pMessage) + { + FilterMessage_t(pClient, pMessage, pChannel); return; + } - if (simulating && g_bHasTicked) + int msgid = pMessage->GetNetMessage()->GetNetMessageInfo()->m_MessageId; + int slot = pClient->GetPlayerSlot(true).Get(); + if (msgid != clc_ServerStatus) { - g_flUniversalTime += GetGlobals()->curtime - g_flLastTickedTime; + FilterMessage_t(pClient, pMessage, pChannel); + return; } - g_flLastTickedTime = GetGlobals()->curtime; - g_bHasTicked = true; - - for (int i = g_timers.Tail(); i != g_timers.InvalidIndex();) + bool playerlist = false; + std::string buffer; + for (const std::string &message : g_StatusArray) { - auto timer = g_timers[i]; - - int prevIndex = i; - i = g_timers.Previous(i); - - if (timer->m_flLastExecute == -1) - timer->m_flLastExecute = g_flUniversalTime; - - // Timer execute - if (timer->m_flLastExecute + timer->m_flInterval <= g_flUniversalTime) + if (message.rfind("#HEADER#", 0) == 0) { - if (!timer->Execute()) - { - delete timer; - g_timers.Remove(prevIndex); - } - else - { - timer->m_flLastExecute = g_flUniversalTime; - } + ClientPrint(slot, HUD_PRINTCONSOLE, message.c_str() + 8); + continue; } - } -} -void StatusModifier::Hook_GameServerSteamAPIActivated() -{ - steamctx.Init(); - - if (!GetPublicIP()) - { - g_sServerIP = "Unknown"; - // clang-format off - new CTimer(5.0f, []() + if (!playerlist && message.find("{PLAYER") != std::string::npos && message.find("{PLAYERC") == std::string::npos) { - if(GetPublicIP()) + for (int i = 0; i < MAXPLAYERS; i++) { - return -1.0f; + if (g_mExcludeSlots.find(i) != g_mExcludeSlots.end()) + continue; + + buffer = CheckMessageVariables(message, i, g_HeaderLayout); + if (buffer.length() > 0) + ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); } - return 5.0f; - }); - // clang-format on - } + playerlist = true; + continue; + } - RETURN_META(MRES_IGNORED); + buffer = CheckMessageVariables(message, -1, g_HeaderLayout); + if (buffer.length() > 0) + ClientPrint(slot, HUD_PRINTCONSOLE, buffer.c_str()); + } } void StatusModifier::Hook_OnClientConnected(CPlayerSlot slot, @@ -332,7 +247,7 @@ void StatusModifier::Hook_OnClientConnected(CPlayerSlot slot, // Ideally we would use CServerSideClient::IsHLTV().. but it doesn't work :( if (bFakePlayer && V_strcmp(pszName, pszTvName)) - g_playerManager->OnBotConnected(slot); + g_PlayerManager->OnBotConnected(slot); } bool StatusModifier::Hook_ClientConnect(CPlayerSlot slot, const char *pszName, @@ -341,7 +256,7 @@ bool StatusModifier::Hook_ClientConnect(CPlayerSlot slot, const char *pszName, CBufferString *pRejectReason) { // Player is banned - if (!g_playerManager->OnClientConnected(slot, xuid, pszNetworkID)) + if (!g_PlayerManager->OnClientConnected(slot, xuid, pszNetworkID)) RETURN_META_VALUE(MRES_SUPERCEDE, false); RETURN_META_VALUE(MRES_IGNORED, true); @@ -352,7 +267,7 @@ void StatusModifier::Hook_ClientDisconnect(CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID) { - g_playerManager->OnClientDisconnect(slot); + g_PlayerManager->OnClientDisconnect(slot); } void LoadConfig() @@ -548,7 +463,7 @@ std::string CheckMessageVariables(const std::string &message, int slot, // {PLAYERCOUNT} if (sMessage.find("{PLAYERCOUNT}") != std::string::npos) ReplaceAll(sMessage, "{PLAYERCOUNT}", - std::to_string(g_playerManager->GetPlayerCount())); + std::to_string(g_PlayerManager->GetPlayerCount())); // {MAXPLAYERS} if (sMessage.find("{MAXPLAYERS}") != std::string::npos) @@ -567,7 +482,7 @@ std::string CheckMessageVariables(const std::string &message, int slot, if (slot > -1) { - Player *pPlayer = g_playerManager->GetPlayer(slot); + Player *pPlayer = g_PlayerManager->GetPlayer(slot); if (!pPlayer || !pPlayer->IsConnected()) return ""; @@ -598,7 +513,7 @@ std::string CheckMessageVariables(const std::string &message, int slot, int column = 0; if (sMessage.find("{PLAYERUSERID}") != std::string::npos) - set(column++, std::to_string(engine->GetPlayerUserId(slot).Get())); + set(column++, std::to_string(g_pEngineServer2->GetPlayerUserId(slot).Get())); if (sMessage.find("{PLAYERNAME}") != std::string::npos) { @@ -635,7 +550,7 @@ std::string CheckMessageVariables(const std::string &message, int slot, if (sMessage.find("{PLAYERTIME}") != std::string::npos && GetGlobals()) { - INetChannelInfo *pInfo = engine->GetPlayerNetInfo(slot); + INetChannelInfo *pInfo = g_pEngineServer2->GetPlayerNetInfo(slot); set(column++, pInfo ? FormatShortTime((int)pInfo->GetTimeConnected()) : "0s"); } diff --git a/statusmodifier.h b/statusmodifier.h index 3b7afd7..e750d23 100644 --- a/statusmodifier.h +++ b/statusmodifier.h @@ -19,7 +19,7 @@ #include "ctimer.h" #include "funchook.h" #include "bitvec.h" -#include +#include "serversideclient.h" #ifdef _WIN32 #define ROOTBIN "/bin/win64/" @@ -44,8 +44,6 @@ class StatusModifier final : public ISmmPlugin, public IMetamodListener bool Unload(char *error, size_t maxlen); void Hook_StartupServer(const GameSessionConfiguration_t &config, ISource2WorldSession *pSession, const char *pszMapName); - void Hook_GameFrame(bool simulating, bool bFirstTick, bool bLastTick); - void Hook_GameServerSteamAPIActivated(); void Hook_OnClientConnected(CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, const char *pszAddress, bool bFakePlayer); bool Hook_ClientConnect(CPlayerSlot slot, const char *pszName, uint64 xuid, const char *pszNetworkID, bool unk1, CBufferString *pRejectReason); void Hook_ClientDisconnect(CPlayerSlot slot, ENetworkDisconnectionReason reason, const char *pszName, uint64 xuid, const char *pszNetworkID); @@ -80,7 +78,7 @@ std::string CheckMessageVariables(const std::string &message, int slot, const PipeLayout &layout); PipeLayout ParseHeaderLayout(std::string_view header); std::string FormatShortTime(int seconds); -bool GetPublicIP(); +const char *GetPublicIP(); template std::string PadRight(const T &value, size_t width); diff --git a/utils.h b/utils.h index 2ac4a39..055d164 100644 --- a/utils.h +++ b/utils.h @@ -1,9 +1,9 @@ #pragma once -extern IGameEventSystem *g_gameEventSystem; +extern IGameEventSystem *g_pGameEventSystem; extern ISmmAPI *g_SMAPI; -extern ISteamGameServer *g_pGameServer; +extern CGameConfig *g_GameConfig; extern std::string g_sServerIP; #define HUD_PRINTCONSOLE 2 @@ -27,7 +27,7 @@ void ClientPrint(CPlayerSlot slot, int hud_dest, const char *msg, ...) CSingleRecipientFilter filter(slot); - g_gameEventSystem->PostEventAbstract(-1, false, &filter, pNetMsg, data, 0); + g_pGameEventSystem->PostEventAbstract(-1, false, &filter, pNetMsg, data, 0); delete data; } @@ -166,37 +166,29 @@ void ErrorLog(const char *msg, ...) } } -bool GetPublicIP() +bool IsIPInvalid(unsigned char ip[4]) { - g_pGameServer = SteamGameServer(); + return ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0; +} + +using GetPublicAdr_fn = netadr_t *(__thiscall *)(void *); +const char *GetPublicIP() +{ + if (!g_sServerIP.empty()) + return g_sServerIP.c_str(); - if (g_pGameServer) + netadr_t *pAddr = vmt::GetVMethod(g_GameConfig->GetOffset("GetPublicAdr"), g_pNetworkSystem)(g_pNetworkSystem); + + static ConVarRefAbstract port("hostport"); + int hostport = port.GetInt(); + + if (!pAddr || IsIPInvalid(pAddr->ip) || hostport == 0) { - SteamIPAddress_t sAddr = g_pGameServer->GetPublicIP(); - - int hostport = CommandLine()->ParmValue("-port", 0); - - if (!sAddr.IsSet() || hostport == 0) - { - ConMsg("Server IP / port not set. Retrying...\n"); - return false; - } - uint32_t ipaddr = sAddr.m_unIPv4; - uint32_t ip[4]; - - for (char iter = 3; iter > -1; --iter) - { - ip[(~iter) & 0x03] = (static_cast(ipaddr >> (iter * 8)) & - 0xFF); /* I hate you; SteamTools. */ - } - char buf[64]; - sprintf(buf, "%i.%i.%i.%i:%i", ip[0], ip[1], ip[2], ip[3], hostport); - - g_sServerIP = buf; - - // ConMsg("Server IP: %s\n", buf); - return true; + return "Unknown"; } - ConMsg("ISteamGameServer not valid. Retrying...\n"); - return false; + + char buf[64]; + snprintf(buf, sizeof(buf), "%d.%d.%d.%d:%d", pAddr->ip[0], pAddr->ip[1], pAddr->ip[2], pAddr->ip[3], hostport); + g_sServerIP = buf; + return g_sServerIP.c_str(); } \ No newline at end of file diff --git a/utils/module.h b/utils/module.h deleted file mode 100644 index c0d67ac..0000000 --- a/utils/module.h +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once -#include "dbg.h" -#include "interface.h" -#include "strtools.h" -#include "plat.h" - -#include -#include - - -#ifdef _WIN32 -#include -#endif - -enum SigError -{ - SIG_OK, - SIG_NOT_FOUND, - SIG_FOUND_MULTIPLE, -}; - -// equivalent to FindSignature, but allows for multiple signatures to be found and iterated over -class SignatureIterator -{ -public: - SignatureIterator(void *pBase, size_t iSize, const byte *pSignature, size_t iSigLength) : - m_pBase((byte *)pBase), m_iSize(iSize), m_pSignature(pSignature), m_iSigLength(iSigLength) - { - m_pCurrent = m_pBase; - } - - void *FindNext(bool allowWildcard) - { - for (size_t i = 0; i < m_iSize; i++) - { - size_t Matches = 0; - while (*(m_pCurrent + i + Matches) == m_pSignature[Matches] || (allowWildcard && m_pSignature[Matches] == '\x2A')) - { - Matches++; - if (Matches == m_iSigLength) - { - m_pCurrent += i + 1; - return m_pCurrent - 1; - } - } - } - - return nullptr; - } -private: - byte *m_pBase; - size_t m_iSize; - const byte *m_pSignature; - size_t m_iSigLength; - byte *m_pCurrent; -}; - -class CModule -{ -public: - CModule(const char *path, const char *module) : - m_pszModule(module), m_pszPath(path) - { - char szModule[MAX_PATH]; - - V_snprintf(szModule, MAX_PATH, "%s%s%s%s%s", Plat_GetGameDirectory(), path, MODULE_PREFIX, m_pszModule, MODULE_EXT); - - m_hModule = dlmount(szModule); - - if (!m_hModule) - Error("Could not find %s\n", szModule); - -#ifdef _WIN32 - MODULEINFO m_hModuleInfo; - GetModuleInformation(GetCurrentProcess(), m_hModule, &m_hModuleInfo, sizeof(m_hModuleInfo)); - - m_base = (void *)m_hModuleInfo.lpBaseOfDll; - m_size = m_hModuleInfo.SizeOfImage; - InitializeSections(); -#else - if (int e = GetModuleInformation(m_hModule, &m_base, &m_size, m_sections)) - Error("Failed to get module info for %s, error %d\n", szModule, e); -#endif - } - - void *FindSignature(const byte *pData, size_t iSigLength, int &error) - { - unsigned char *pMemory; - void *return_addr = nullptr; - error = 0; - - pMemory = (byte *)m_base; - - for (size_t i = 0; i < m_size; i++) - { - size_t Matches = 0; - while (*(pMemory + i + Matches) == pData[Matches] || pData[Matches] == '\x2A') - { - Matches++; - if (Matches == iSigLength) - { - if (return_addr) - { - error = SIG_FOUND_MULTIPLE; - return return_addr; - } - - return_addr = (void *)(pMemory + i); - break; - } - } - } - - if (!return_addr) - error = SIG_NOT_FOUND; - - return return_addr; - } - - void *FindInterface(const char *name) - { - CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(m_hModule, "CreateInterface"); - - if (!fn) - Error("Could not find CreateInterface in %s\n", m_pszModule); - - void *pInterface = fn(name, nullptr); - - if (!pInterface) - Error("Could not find %s in %s\n", name, m_pszModule); - - return pInterface; - } - - Section *GetSection(const std::string_view name) - { - for (auto §ion : m_sections) - { - if (section.m_szName == name) - return §ion; - } - - return nullptr; - } -#ifdef _WIN32 - void InitializeSections(); -#endif - void *FindVirtualTable(const std::string &name); -public: - const char *m_pszModule; - const char *m_pszPath; - HINSTANCE m_hModule; - void *m_base; - size_t m_size; - std::vector
m_sections; -}; diff --git a/utils/plat.h b/utils/plat.h deleted file mode 100644 index d102d14..0000000 --- a/utils/plat.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * ============================================================================= - * MultiAddonManager - * Copyright (C) 2023-2025 Source2ZE - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#pragma once -#include -#include -#include -#include "metamod_oslink.h" - -struct Section -{ - std::string m_szName; - void *m_pBase; - size_t m_iSize; -}; - -#if defined(_WIN32) -#define FASTCALL __fastcall -#define THISCALL __thiscall -#else -#define FASTCALL -#define THISCALL -#define strtok_s strtok_r -#endif - -struct Module -{ -#ifndef _WIN32 - void *pHandle; -#endif - uint8_t *pBase; - unsigned int nSize; -}; - -#ifndef _WIN32 -int GetModuleInformation(HINSTANCE module, void **base, size_t *length, std::vector
&m_sections); -#endif - -#ifdef _WIN32 -#define MODULE_PREFIX "" -#define MODULE_EXT ".dll" -#else -#define MODULE_PREFIX "lib" -#define MODULE_EXT ".so" -#endif - -void Plat_WriteMemory(void *pPatchAddress, uint8_t *pPatch, int iPatchSize); \ No newline at end of file diff --git a/utils/plat_unix.cpp b/utils/plat_unix.cpp deleted file mode 100644 index acd58f2..0000000 --- a/utils/plat_unix.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/** - * ============================================================================= - * MultiAddonManager - * Copyright (C) 2023-2025 Source2ZE - * - * DynLibUtils - * Copyright (C) 2023 komashchenko (Phoenix) - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#ifdef __linux__ -#include "module.h" -#include "plat.h" -#include -#include -#include -#include -#include "sys/mman.h" -#include -#include -#include -#include "dbg.h" -#include -#include -#include - -#include "tier0/memdbgon.h" - -#define PAGE_SIZE 4096 -#define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) - -struct ModuleInfo -{ - const char *path; // in - uint8_t *base; // out - uint size; // out -}; - -// https://github.com/alliedmodders/sourcemod/blob/master/core/logic/MemoryUtils.cpp#L502-L587 -// https://github.com/komashchenko/DynLibUtils/blob/5eb95475170becfcc64fd5d32d14ec2b76dcb6d4/module_linux.cpp#L95 -int GetModuleInformation(HINSTANCE hModule, void **base, size_t *length, std::vector
&m_sections) -{ - - link_map *lmap; - if (dlinfo(hModule, RTLD_DI_LINKMAP, &lmap) != 0) - { - dlclose(hModule); - return 1; - } - - int fd = open(lmap->l_name, O_RDONLY); - if (fd == -1) - { - dlclose(hModule); - return 2; - } - - struct stat st; - if (fstat(fd, &st) == 0) - { - void *map = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (map != MAP_FAILED) - { - ElfW(Ehdr) *ehdr = static_cast(map); - ElfW(Shdr) *shdrs = reinterpret_cast(reinterpret_cast(ehdr) + ehdr->e_shoff); - const char *strTab = reinterpret_cast(reinterpret_cast(ehdr) + shdrs[ehdr->e_shstrndx].sh_offset); - - for (auto i = 0; i < ehdr->e_phnum; ++i) - { - ElfW(Phdr) *phdr = reinterpret_cast(reinterpret_cast(ehdr) + ehdr->e_phoff + i * ehdr->e_phentsize); - if (phdr->p_type == PT_LOAD && phdr->p_flags & PF_X) - { - *base = reinterpret_cast(lmap->l_addr + phdr->p_vaddr); - *length = phdr->p_filesz; - break; - } - } - - for (auto i = 0; i < ehdr->e_shnum; ++i) - { - ElfW(Shdr) *shdr = reinterpret_cast(reinterpret_cast(shdrs) + i * ehdr->e_shentsize); - if (*(strTab + shdr->sh_name) == '\0') - continue; - - Section section; - section.m_szName = strTab + shdr->sh_name; - section.m_pBase = reinterpret_cast(lmap->l_addr + shdr->sh_addr); - section.m_iSize = shdr->sh_size; - m_sections.push_back(section); - } - - munmap(map, st.st_size); - } - } - - close(fd); - - return 0; -} - -static int parse_prot(const char *s) -{ - int prot = 0; - - for (; *s; s++) - { - switch (*s) - { - case '-': - break; - case 'r': - prot |= PROT_READ; - break; - case 'w': - prot |= PROT_WRITE; - break; - case 'x': - prot |= PROT_EXEC; - break; - case 's': - break; - case 'p': - break; - default: - break; - } - } - - return prot; -} - -static int get_prot(void *pAddr, size_t nSize) -{ - FILE *f = fopen("/proc/self/maps", "r"); - - uintptr_t nAddr = (uintptr_t)pAddr; - - char line[512]; - while (fgets(line, sizeof(line), f)) - { - char start[16]; - char end[16]; - char prot[16]; - - const char *src = line; - - char *dst = start; - while (*src != '-') - *dst++ = *src++; - *dst = 0; - - src++; // skip "-"" - - dst = end; - while (!isspace(*src)) - *dst++ = *src++; - *dst = 0; - - src++; // skip space - - dst = prot; - while (!isspace(*src)) - *dst++ = *src++; - *dst = 0; - - uintptr_t nStart = (uintptr_t)strtoul(start, nullptr, 16); - uintptr_t nEnd = (uintptr_t)strtoul(end, nullptr, 16); - - if (nStart < nAddr && nEnd >(nAddr + nSize)) - { - fclose(f); - return parse_prot(prot); - } - } - - fclose(f); - return 0; -} - -void Plat_WriteMemory(void *pPatchAddress, uint8_t *pPatch, int iPatchSize) -{ - int old_prot = get_prot(pPatchAddress, iPatchSize); - - uintptr_t page_size = sysconf(_SC_PAGESIZE); - uint8_t *align_addr = (uint8_t *)((uintptr_t)pPatchAddress & ~(page_size - 1)); - - uint8_t *end = (uint8_t *)pPatchAddress + iPatchSize; - uintptr_t align_size = end - align_addr; - - int result = mprotect(align_addr, align_size, PROT_READ | PROT_WRITE); - - memcpy(pPatchAddress, pPatch, iPatchSize); - - result = mprotect(align_addr, align_size, old_prot); -} - -void *CModule::FindVirtualTable(const std::string &name) -{ - auto readOnlyData = GetSection(".rodata"); - auto readOnlyRelocations = GetSection(".data.rel.ro"); - - if (!readOnlyData || !readOnlyRelocations) - { - Warning("Failed to find .rodata or .data.rel.ro section\n"); - return nullptr; - } - - std::string decoratedTableName = std::to_string(name.length()) + name; - - SignatureIterator sigIt(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte *)decoratedTableName.c_str(), decoratedTableName.size() + 1); - void *classNameString = sigIt.FindNext(false); - - if (!classNameString) - { - Warning("Failed to find type descriptor for %s\n", name.c_str()); - return nullptr; - } - - SignatureIterator sigIt2(readOnlyRelocations->m_pBase, readOnlyRelocations->m_iSize, (const byte *)&classNameString, sizeof(void *)); - void *typeName = sigIt2.FindNext(false); - - if (!typeName) - { - Warning("Failed to find type name for %s\n", name.c_str()); - return nullptr; - } - - void *typeInfo = (void *)((uintptr_t)typeName - 0x8); - - for (const auto §ionName : {std::string_view(".data.rel.ro"), std::string_view(".data.rel.ro.local")}) - { - auto section = GetSection(sectionName); - if (!section) - continue; - - SignatureIterator sigIt3(section->m_pBase, section->m_iSize, (const byte *)&typeInfo, sizeof(void *)); - - while (void *vtable = sigIt3.FindNext(false)) - { - if (*(int64_t *)((uintptr_t)vtable - 0x8) == 0) - return (void *)((uintptr_t)vtable + 0x8); - } - } - - Warning("Failed to find vtable for %s\n", name.c_str()); - return nullptr; -} -#endif diff --git a/utils/plat_win.cpp b/utils/plat_win.cpp deleted file mode 100644 index b544b16..0000000 --- a/utils/plat_win.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/** - * ============================================================================= - * MultiAddonManager - * Copyright (C) 2023-2025 Source2ZE - * - * DynLibUtils - * Copyright (C) 2023 komashchenko (Phoenix) - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include "module.h" -#include "plat.h" - -#include "tier0/memdbgon.h" - -void Plat_WriteMemory(void *pPatchAddress, uint8_t *pPatch, int iPatchSize) -{ - WriteProcessMemory(GetCurrentProcess(), pPatchAddress, (void *)pPatch, iPatchSize, nullptr); -} - - -void CModule::InitializeSections() -{ - IMAGE_DOS_HEADER *pDosHeader = reinterpret_cast(m_hModule); - IMAGE_NT_HEADERS *pNtHeader = reinterpret_cast(reinterpret_cast(m_hModule) + pDosHeader->e_lfanew); - - IMAGE_SECTION_HEADER *pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader); - - for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) - { - Section section; - section.m_szName = (char *)pSectionHeader[i].Name; - section.m_pBase = (void *)((uint8_t *)m_base + pSectionHeader[i].VirtualAddress); - section.m_iSize = pSectionHeader[i].SizeOfRawData; - - m_sections.push_back(std::move(section)); - } -} - -void *CModule::FindVirtualTable(const std::string &name) -{ - auto runTimeData = GetSection(".data"); - auto readOnlyData = GetSection(".rdata"); - - if (!runTimeData || !readOnlyData) - { - Warning("Failed to find .data or .rdata section\n"); - return nullptr; - } - - std::string decoratedTableName = ".?AV" + name + "@@"; - - SignatureIterator sigIt(runTimeData->m_pBase, runTimeData->m_iSize, (const byte *)decoratedTableName.c_str(), decoratedTableName.size() + 1); - void *typeDescriptor = sigIt.FindNext(false); - - if (!typeDescriptor) - { - Warning("Failed to find type descriptor for %s\n", name.c_str()); - return nullptr; - } - - typeDescriptor = (void *)((uintptr_t)typeDescriptor - 0x10); - - const uint32_t rttiTDRva = (uintptr_t)typeDescriptor - (uintptr_t)m_base; - - ConMsg("RTTI Type Descriptor RVA: 0x%p\n", rttiTDRva); - - SignatureIterator sigIt2(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte *)&rttiTDRva, sizeof(uint32_t)); - - while (void *completeObjectLocator = sigIt2.FindNext(false)) - { - auto completeObjectLocatorHeader = (uintptr_t)completeObjectLocator - 0xC; - // check RTTI Complete Object Locator header, always 0x1 - if (*(int32_t *)(completeObjectLocatorHeader) != 1) - continue; - - // check RTTI Complete Object Locator vtable offset - if (*(int32_t *)((uintptr_t)completeObjectLocator - 0x8) != 0) - continue; - - SignatureIterator sigIt3(readOnlyData->m_pBase, readOnlyData->m_iSize, (const byte *)&completeObjectLocatorHeader, sizeof(void *)); - void *vtable = sigIt3.FindNext(false); - - if (!vtable) - { - Warning("Failed to find vtable for %s\n", name.c_str()); - return nullptr; - } - - return (void *)((uintptr_t)vtable + 0x8); - } - - Warning("Failed to find RTTI Complete Object Locator for %s\n", name.c_str()); - return nullptr; -} diff --git a/utils/plat_win.h b/utils/plat_win.h deleted file mode 100644 index 6f70f09..0000000 --- a/utils/plat_win.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once From 0056d87ba071788cf28c6adbe07f495b2281e77f Mon Sep 17 00:00:00 2001 From: Sachin Date: Fri, 5 Jun 2026 16:13:16 +0530 Subject: [PATCH 2/2] fix: FilterMessage slot in linux --- gamedata/statusmodifier.jsonc | 4 ++++ serversideclient.h | 2 +- statusmodifier.cpp | 10 ++++++---- utils.h | 6 ++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/gamedata/statusmodifier.jsonc b/gamedata/statusmodifier.jsonc index be0f518..217158c 100644 --- a/gamedata/statusmodifier.jsonc +++ b/gamedata/statusmodifier.jsonc @@ -16,6 +16,10 @@ "GetPublicAdr": { "windows": 32, "linux": 32 + }, + "CServerSideClientBase::m_nClientSlot": { + "windows": 72, + "linux": 72 } }, "Patches": {} diff --git a/serversideclient.h b/serversideclient.h index f1e7951..fafcfcf 100644 --- a/serversideclient.h +++ b/serversideclient.h @@ -90,7 +90,7 @@ class CServerSideClientBase : public CUtlSlot, public INetworkChannelNotify, pub virtual bool IsFakeClient() = 0; virtual bool IsHumanPlayer() = 0; - CPlayerSlot GetPlayerSlot(bool offset = false) const { return offset ? CPlayerSlot(*(int *)&m_Name) : m_nClientSlot; } + CPlayerSlot GetPlayerSlot() const { return m_nClientSlot; } CPlayerUserId GetUserID() const { return m_UserID; } CEntityIndex GetEntityIndex() const { return m_nEntityIndex; } CSteamID GetClientSteamID() const { return m_SteamID; } diff --git a/statusmodifier.cpp b/statusmodifier.cpp index a49c17f..6e94eef 100644 --- a/statusmodifier.cpp +++ b/statusmodifier.cpp @@ -32,7 +32,7 @@ CPlayerManager *g_PlayerManager = nullptr; std::unordered_set g_mExcludeSlots; -void (*FilterMessage_t)(CServerSideClient *pClient, CNetMessage *pMessage, INetChannel *pChannel) = nullptr; +void (*FilterMessage_t)(void *pClient, CNetMessage *pMessage, INetChannel *pChannel) = nullptr; using namespace DynLibUtils; @@ -70,7 +70,7 @@ SH_DECL_HOOK6(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool, CPlayerSlot, const char *, uint64, const char *, bool, CBufferString *); -void Hook_FilterMessage(CServerSideClient *pClient, CNetMessage *pMessage, INetChannel *pChannel); +void Hook_FilterMessage(void *pClient, CNetMessage *pMessage, INetChannel *pChannel); bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) @@ -189,7 +189,7 @@ void StatusModifier::Hook_StartupServer( g_mExcludeSlots.clear(); } -void FASTCALL Hook_FilterMessage(CServerSideClient *pClient, CNetMessage *pMessage, INetChannel *pChannel) +void FASTCALL Hook_FilterMessage(void *pClient, CNetMessage *pMessage, INetChannel *pChannel) { if (!pClient || !pMessage) { @@ -198,7 +198,9 @@ void FASTCALL Hook_FilterMessage(CServerSideClient *pClient, CNetMessage *pMessa } int msgid = pMessage->GetNetMessage()->GetNetMessageInfo()->m_MessageId; - int slot = pClient->GetPlayerSlot(true).Get(); + static int playerIndex = g_GameConfig->GetOffset("CServerSideClientBase::m_nClientSlot") - WIN_LINUX(8, 48); + int slot = *(int *)((uintptr_t)pClient + playerIndex); + if (msgid != clc_ServerStatus) { FilterMessage_t(pClient, pMessage, pChannel); diff --git a/utils.h b/utils.h index 055d164..d20dd2e 100644 --- a/utils.h +++ b/utils.h @@ -8,6 +8,12 @@ extern std::string g_sServerIP; #define HUD_PRINTCONSOLE 2 +#ifdef _WIN32 +#define WIN_LINUX(win, linux) win +#else +#define WIN_LINUX(win, linux) linux +#endif + void ClientPrint(CPlayerSlot slot, int hud_dest, const char *msg, ...) { va_list args;