Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions AMBuilder
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,24 @@ for sdk_target in MMSPlugin.sdk_targets:
])]

binary.compiler.cxxincludes += [
os.path.join(builder.currentSourcePath, 'vendor', 'funchook', 'include'),
os.path.join(builder.sourcePath, 'public'),
os.path.join(builder.sourcePath, 'include'),
os.path.join(builder.sourcePath, '..', 'SchemaEntity'),
]

if binary.compiler.target.platform == 'linux':
binary.compiler.postlink += [
os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'libfunchook.a'),
os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'libdistorm.a'),
]
elif binary.compiler.target.platform == 'windows':
binary.compiler.postlink += [
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'),
]

binary.sources += [
'statusmodifier.cpp',
'gameconfig.cpp',
Expand Down
16 changes: 12 additions & 4 deletions gamedata/statusmodifier.jsonc
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{
"Signatures": {
// 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": "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,
Expand All @@ -8,11 +17,10 @@
"windows": 32,
"linux": 32
},
"SendNetMessage": {
"windows": 15,
"linux": 16
"CServerSideClientBase::m_nClientSlot": {
"windows": 72,
"linux": 72
}
},
"Signatures": {},
"Patches": {}
}
131 changes: 70 additions & 61 deletions statusmodifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
#include <inetchannel.h>
#include <networksystem/inetworkmessages.h>
#include <networksystem/inetworkserializer.h>
#include <networksystem/inetworksystem.h>

#include "protobuf/generated/cstrike15_usermessages.pb.h"
#include "protobuf/generated/usermessages.pb.h"
#include "protobuf/generated/netmessages.pb.h"

#include <fstream>
#include <iomanip>
Expand All @@ -21,22 +19,25 @@
#include "gameconfig.h"
#include "playermanager.h"
#include "recipientfilter.h"
#include "serversideclient.h"
#include "utils.h"

StatusModifier g_StatusModifier;
PLUGIN_EXPOSE(StatusModifier, g_StatusModifier);
IVEngineServer2 *g_pEngineServer2 = nullptr;
CGameEntitySystem *g_pEntitySystem = nullptr;
IGameEventSystem *g_pGameEventSystem = nullptr;
int g_iSendNetMessageId = -1;

CGameConfig *g_GameConfig = nullptr;
CPlayerManager *g_PlayerManager = nullptr;

std::unordered_set<int> g_mExcludeSlots;

void (*FilterMessage_t)(void *pClient, CNetMessage *pMessage, INetChannel *pChannel) = nullptr;

using namespace DynLibUtils;

funchook_t *m_FilterMessageHook;

std::vector<std::string> g_StatusArray;
std::string g_sServerIP;
std::chrono::time_point<std::chrono::steady_clock> g_chServerStartTime;
Expand All @@ -56,8 +57,6 @@ class GameSessionConfiguration_t
{
};

SH_DECL_MANUALHOOK2_void(SendNetMessage_t, 0, 0, 0, CNetMessage *, NetChannelBufType_t);

SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0,
const GameSessionConfiguration_t &, ISource2WorldSession *,
const char *);
Expand All @@ -71,38 +70,7 @@ SH_DECL_HOOK6(IServerGameClients, ClientConnect, SH_NOATTRIB, 0, bool,
CPlayerSlot, const char *, uint64, const char *, bool,
CBufferString *);

void ShowCustomStatusMessage(int 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(void *pClient, CNetMessage *pMessage, INetChannel *pChannel);

bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error,
size_t maxlen, bool late)
Expand Down Expand Up @@ -152,16 +120,23 @@ bool StatusModifier::Load(PluginId id, ISmmAPI *ismm, char *error,

CModule libengine(g_pEngineServer2);

void *pServerSideClientVTable = libengine.GetVirtualTableByName("CServerSideClient");
if (!pServerSideClientVTable)
const char *szSignature = g_GameConfig->GetSignature("FilterMessage");

FilterMessage_t = libengine.FindPattern(szSignature)
.RCast<decltype(FilterMessage_t)>();
if (!FilterMessage_t)
{
snprintf(error, maxlen, "Failed to find ServerSideClient vtable");
ErrorLog("[StatusModifier] Failed to find ServerSideClient vtable");
snprintf(error, maxlen, "Failed to find function to get FilterMessage");
ErrorLog("[%s] Failed to find function to get FilterMessage",
g_PLAPI->GetLogTag());
return false;
}

SH_MANUALHOOK_RECONFIGURE(SendNetMessage_t, g_GameConfig->GetOffset("SendNetMessage"), 0, 0);
g_iSendNetMessageId = SH_ADD_MANUALDVPHOOK(SendNetMessage_t, pServerSideClientVTable, SH_MEMBER(this, &StatusModifier::Hook_SendNetMessage), false);
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);

Expand Down Expand Up @@ -196,7 +171,8 @@ bool StatusModifier::Unload(char *error, size_t maxlen)
SH_REMOVE_HOOK(IServerGameClients, ClientConnect, g_pSource2GameClients,
SH_MEMBER(this, &StatusModifier::Hook_ClientConnect), false);

SH_REMOVE_HOOK_ID(g_iSendNetMessageId);
if (m_FilterMessageHook)
funchook_destroy(m_FilterMessageHook);

if (g_PlayerManager)
delete g_PlayerManager;
Expand All @@ -213,6 +189,55 @@ void StatusModifier::Hook_StartupServer(
g_mExcludeSlots.clear();
}

void FASTCALL Hook_FilterMessage(void *pClient, CNetMessage *pMessage, INetChannel *pChannel)
{
if (!pClient || !pMessage)
{
FilterMessage_t(pClient, pMessage, pChannel);
return;
}

int msgid = pMessage->GetNetMessage()->GetNetMessageInfo()->m_MessageId;
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);
return;
}

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 StatusModifier::Hook_OnClientConnected(CPlayerSlot slot,
const char *pszName, uint64 xuid,
const char *pszNetworkID,
Expand Down Expand Up @@ -247,22 +272,6 @@ void StatusModifier::Hook_ClientDisconnect(CPlayerSlot slot,
g_PlayerManager->OnClientDisconnect(slot);
}

void StatusModifier::Hook_SendNetMessage(CNetMessage *pData, NetChannelBufType_t bufType)
{
CServerSideClient *pClient = META_IFACEPTR(CServerSideClient);
if (!pClient || !pData)
RETURN_META(MRES_IGNORED);

int msgid = pData->GetNetMessage()->GetNetMessageInfo()->m_MessageId;
// ConMsg("%d called messageId: %d\n", pClient->GetPlayerSlot().Get(), msgid);
if (msgid == svc_Print)
{
ShowCustomStatusMessage(pClient->GetPlayerSlot().Get());
RETURN_META(MRES_SUPERCEDE);
}
RETURN_META(MRES_IGNORED);
}

void LoadConfig()
{
const char *pszConfigPath =
Expand Down
3 changes: 2 additions & 1 deletion statusmodifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

#include "convar.h"
#include "module.h"
#include "funchook.h"
#include "bitvec.h"
#include "serversideclient.h"

#ifdef _WIN32
#define ROOTBIN "/bin/win64/"
Expand All @@ -41,7 +43,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_SendNetMessage(CNetMessage *pData, NetChannelBufType_t bufType);
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);
Expand Down
8 changes: 7 additions & 1 deletion utils.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
#pragma once

extern IGameEventSystem *g_pGameEventSystem;
extern CGameConfig *g_GameConfig;
extern ISmmAPI *g_SMAPI;

extern CGameConfig *g_GameConfig;
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;
Expand Down
Loading
Loading