From 9ae243137589b34c80ad85d3dc8789c265bfbf46 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Mon, 14 Mar 2016 18:47:20 +0100 Subject: [PATCH] Fix printing to demo console on CS:S linux This is getting rediculous. Our own native calls IClient::ClientPrintf to print stuff to the demo console. The engine's Host_Client_Printf uses the CGameClient vtable's ClientPrintf. To catch the output of the "status" command, we have to hook both vtables on linux... Windows casts to IClient in Host_Client_Printf, so no need to do that there. --- hltvserverwrapper.cpp | 73 +++++++++++++++++++++++++++++++++------ hltvserverwrapper.h | 6 +++- sourcetvmanager.games.txt | 7 +++- 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/hltvserverwrapper.cpp b/hltvserverwrapper.cpp index d4a60e4..4f3e7fc 100644 --- a/hltvserverwrapper.cpp +++ b/hltvserverwrapper.cpp @@ -11,6 +11,13 @@ SH_DECL_MANUALHOOK0_void(CHLTVServer_Shutdown, 0, 0, 0); // Stuff to print to demo console SH_DECL_HOOK0_void_vafmt(IClient, ClientPrintf, SH_NOATTRIB, 0); + +// Linux has the ClientPrintf method in both CGameClient and IClient's vtables +// and uses both.. Need to hook both....... i guess? +#ifndef WIN32 +SH_DECL_MANUALHOOK0_void_vafmt(CGameClient_ClientPrintf, 0, 0, 0); +#endif + // This should be large enough. #define FAKE_VTBL_LENGTH 70 static void *FakeNetChanVtbl[FAKE_VTBL_LENGTH]; @@ -107,8 +114,14 @@ void HLTVServerWrapper::Hook() SH_ADD_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand), false); SH_ADD_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand_Post), true); #if SOURCE_ENGINE != SE_CSGO - SH_ADD_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnHLTVBotClientPrintf_Post), false); -#endif + SH_ADD_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnIClient_ClientPrintf_Post), false); +#ifndef WIN32 + // The IClient vtable is +4 from the CBaseClient vtable due to multiple inheritance. + void *pGameClient = (void *)((intptr_t)pClient - 4); + if (g_HLTVServers.HasClientPrintfOffset()) + SH_ADD_MANUALHOOK(CGameClient_ClientPrintf, pGameClient, SH_MEMBER(this, &HLTVServerWrapper::OnCGameClient_ClientPrintf_Post), false); +#endif // !WIN32 +#endif // SOURCE_ENGINE != SE_CSGO } } } @@ -133,8 +146,14 @@ void HLTVServerWrapper::Unhook() SH_REMOVE_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand), false); SH_REMOVE_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand_Post), true); #if SOURCE_ENGINE != SE_CSGO - SH_REMOVE_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnHLTVBotClientPrintf_Post), false); -#endif + SH_REMOVE_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnIClient_ClientPrintf_Post), false); +#ifndef WIN32 + // The IClient vtable is +4 from the CBaseClient vtable due to multiple inheritance. + void *pGameClient = (void *)((intptr_t)pClient - 4); + if (g_HLTVServers.HasClientPrintfOffset()) + SH_REMOVE_MANUALHOOK(CGameClient_ClientPrintf, pGameClient, SH_MEMBER(this, &HLTVServerWrapper::OnCGameClient_ClientPrintf_Post), false); +#endif // !WIN32 +#endif // SOURCE_ENGINE != SE_CSGO } } } @@ -190,19 +209,38 @@ bool HLTVServerWrapper::OnHLTVBotExecuteStringCommand_Post(const char *s) } #if SOURCE_ENGINE != SE_CSGO -void HLTVServerWrapper::OnHLTVBotClientPrintf_Post(const char* buf) +void HLTVServerWrapper::OnCGameClient_ClientPrintf_Post(const char* buf) +{ + void *pGameClient = META_IFACEPTR(void); + IClient *pClient = (IClient *)((intptr_t)pGameClient + 4); + HandleClientPrintf(pClient, buf); + + RETURN_META(MRES_IGNORED); +} + +void HLTVServerWrapper::OnIClient_ClientPrintf_Post(const char* buf) +{ + IClient *pClient = META_IFACEPTR(IClient); + HandleClientPrintf(pClient, buf); + + RETURN_META(MRES_IGNORED); +} + +void HLTVServerWrapper::HandleClientPrintf(IClient *pClient, const char* buf) { // Craft our own "NetChan" pointer static int offset = -1; if (!g_pGameConf->GetOffset("CBaseClient::m_NetChannel", &offset) || offset == -1) { smutils->LogError(myself, "Failed to find CBaseClient::m_NetChannel offset. Can't print to demo console."); - RETURN_META(MRES_IGNORED); + return; } - IClient *pClient = META_IFACEPTR(IClient); - +#ifdef WIN32 void *pNetChannel = (void *)((char *)pClient + offset); +#else + void *pNetChannel = (void *)((char *)pClient + offset - 4); +#endif // Set our fake netchannel *(void **)pNetChannel = &FakeNetChan; // Call ClientPrintf again, this time with a "Netchannel" set on the bot. @@ -210,8 +248,6 @@ void HLTVServerWrapper::OnHLTVBotClientPrintf_Post(const char* buf) SH_CALL(pClient, &IClient::ClientPrintf)("%s", buf); // Set the fake netchannel back to 0. *(void **)pNetChannel = nullptr; - - RETURN_META(MRES_IGNORED); } #endif @@ -232,6 +268,18 @@ void HLTVServerWrapperManager::InitHooks() } #if SOURCE_ENGINE != SE_CSGO +#ifndef WIN32 + if (g_pGameConf->GetOffset("CGameClient::ClientPrintf", &offset)) + { + SH_MANUALHOOK_RECONFIGURE(CGameClient_ClientPrintf, offset, 0, 0); + m_bHasClientPrintfOffset = true; + } + else + { + smutils->LogError(myself, "Failed to find CGameClient::ClientPrintf offset. Won't catch \"status\" console output."); + } +#endif // !WIN32 + if (g_pGameConf->GetOffset("CNetChan::SendNetMsg", &offset)) { if (offset >= FAKE_VTBL_LENGTH) @@ -390,6 +438,11 @@ bool HLTVServerWrapperManager::HasShutdownOffset() return m_bHasShutdownOffset; } +bool HLTVServerWrapperManager::HasClientPrintfOffset() +{ + return m_bHasClientPrintfOffset; +} + #if SOURCE_ENGINE != SE_CSGO bool HLTVServerWrapperManager::OnHLTVBotNetChanSendNetMsg(INetMessage &msg, bool bForceReliable, bool bVoice) { diff --git a/hltvserverwrapper.h b/hltvserverwrapper.h index 1780c80..0b73d6d 100644 --- a/hltvserverwrapper.h +++ b/hltvserverwrapper.h @@ -57,7 +57,9 @@ private: void OnHLTVServerShutdown(); #if SOURCE_ENGINE != SE_CSGO - void OnHLTVBotClientPrintf_Post(const char *buf); + void OnIClient_ClientPrintf_Post(const char *buf); + void OnCGameClient_ClientPrintf_Post(const char *buf); + void HandleClientPrintf(IClient *pClient, const char* buf); #endif private: @@ -80,6 +82,7 @@ public: int GetInstanceNumber(IHLTVServer *hltvserver); IDemoRecorder *GetDemoRecorderPtr(IHLTVServer *hltv); + bool HasClientPrintfOffset(); bool HasShutdownOffset(); #if SOURCE_ENGINE != SE_CSGO @@ -90,6 +93,7 @@ private: #if SOURCE_ENGINE != SE_CSGO bool m_bSendNetMsgHooked = false; #endif + bool m_bHasClientPrintfOffset = false; bool m_bHasShutdownOffset = false; ke::Vector> m_HLTVServers; }; diff --git a/sourcetvmanager.games.txt b/sourcetvmanager.games.txt index c079c29..67b9195 100644 --- a/sourcetvmanager.games.txt +++ b/sourcetvmanager.games.txt @@ -174,7 +174,12 @@ "CBaseClient::m_NetChannel" { "windows" "192" - "linux" "164" + "linux" "196" + } + + "CGameClient::ClientPrintf" + { + "linux" "24" } "CBaseServer::BroadcastPrintf"