diff --git a/extension.cpp b/extension.cpp index 9a7dc4f..7426fae 100644 --- a/extension.cpp +++ b/extension.cpp @@ -31,6 +31,7 @@ #include "extension.h" #include "forwards.h" +#include "natives.h" IHLTVDirector *hltvdirector = nullptr; IHLTVServer *hltvserver = nullptr; @@ -152,6 +153,7 @@ void SourceTVManager::SDK_OnAllLoaded() SM_GET_LATE_IFACE(SDKTOOLS, sdktools); g_pSTVForwards.Init(); + SetupNativeCalls(); iserver = sdktools->GetIServer(); if (!iserver) @@ -396,6 +398,16 @@ void SourceTVManager::OnSetHLTVServer_Post(IHLTVServer *hltv) } #endif + +// When bots issue a command that would print stuff to their console, +// the server might crash, because ExecuteStringCommand doesn't set the +// global host_client pointer to the client on whom the command is run. +// Host_Client_Printf blatantly tries to call host_client->ClientPrintf +// while the pointer might point to some other player or garbage. +// This leads to e.g. the output of the "status" command not being +// recorded in the SourceTV demo. +// The approach here is to set host_client correctly for the SourceTV +// bot and reset it to the old value after command execution. bool SourceTVManager::OnHLTVBotExecuteStringCommand(const char *s) { if (!hltvserver || !iserver || !host_client) diff --git a/forwards.cpp b/forwards.cpp index cbee6b3..250e2d1 100644 --- a/forwards.cpp +++ b/forwards.cpp @@ -43,9 +43,11 @@ SH_DECL_HOOK0_void(IDemoRecorder, StopRecording, SH_NOATTRIB, 0) #if SOURCE_ENGINE == SE_CSGO SH_DECL_MANUALHOOK13(CHLTVServer_ConnectClient, 0, 0, 0, IClient *, const netadr_t &, int, int, int, const char *, const char *, const char *, int, CUtlVector &, bool, CrossPlayPlatform_t, const unsigned char *, int); +SH_DECL_MANUALHOOK1_void_vafmt(CHLTVServer_RejectConnection, 0, 0, 0, const netadr_t &); SH_DECL_HOOK1_void(IClient, Disconnect, SH_NOATTRIB, 0, const char *); #else SH_DECL_MANUALHOOK9(CHLTVServer_ConnectClient, 0, 0, 0, IClient *, netadr_t &, int, int, int, int, const char *, const char *, const char *, int); +SH_DECL_MANUALHOOK3_void(CHLTVServer_RejectConnection, 0, 0, 0, const netadr_t &, int, const char *); SH_DECL_HOOK0_void_vafmt(IClient, Disconnect, SH_NOATTRIB, 0); #endif SH_DECL_MANUALHOOK0_void(CBaseClient_ActivatePlayer, 0, 0, 0); @@ -65,6 +67,16 @@ void CForwardManager::Init() m_bHasClientConnectOffset = true; } + if (!g_pGameConf->GetOffset("CHLTVServer::RejectConnection", &offset) || offset == -1) + { + smutils->LogError(myself, "Failed to get CHLTVServer::RejectConnection offset."); + } + else + { + SH_MANUALHOOK_RECONFIGURE(CHLTVServer_RejectConnection, offset, 0, 0); + m_bHasRejectConnectionOffset = true; + } + if (!g_pGameConf->GetOffset("CHLTVServer::GetChallengeType", &offset) || offset == -1) { smutils->LogError(myself, "Failed to get CHLTVServer::GetChallengeType offset."); @@ -177,59 +189,6 @@ void CForwardManager::UnhookClient(IClient *client) } #if SOURCE_ENGINE == SE_CSGO -// CBaseServer::RejectConnection(ns_address const&, char const*, ...) -static void RejectConnection(IServer *server, const netadr_t &address, const char *pchReason) -{ - static ICallWrapper *pRejectConnection = nullptr; - - if (!pRejectConnection) - { - int offset = -1; - if (!g_pGameConf->GetOffset("CHLTVServer::RejectConnection", &offset) || offset == -1) - { - smutils->LogError(myself, "Failed to get CHLTVServer::RejectConnection offset."); - return; - } - - PassInfo pass[4]; - pass[0].flags = PASSFLAG_BYVAL; - pass[0].type = PassType_Basic; - pass[0].size = sizeof(void *); - pass[1].flags = PASSFLAG_BYVAL; - pass[1].type = PassType_Basic; - pass[1].size = sizeof(void *); - pass[2].flags = PASSFLAG_BYVAL; - pass[2].type = PassType_Basic; - pass[2].size = sizeof(char *); - pass[3].flags = PASSFLAG_BYVAL; - pass[3].type = PassType_Basic; - pass[3].size = sizeof(char *); - - void **vtable = *(void ***)server; - void *func = vtable[offset]; - - pRejectConnection = bintools->CreateCall(func, CallConv_Cdecl, NULL, pass, 4); - } - - static char fmt[] = "%s"; - - if (pRejectConnection) - { - unsigned char vstk[sizeof(void *) * 2 + sizeof(char *) * 2]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)server; - vptr += sizeof(void *); - *(void **)vptr = (void *)&address; - vptr += sizeof(void *); - *(char **)vptr = fmt; - vptr += sizeof(char *); - *(const char **)vptr = pchReason; - - pRejectConnection->Execute(vstk, NULL); - } -} - static bool ExtractPlayerName(CUtlVector &pSplitPlayerConnectVector, char *name, int maxlen) { for (int i = 0; i < pSplitPlayerConnectVector.Count(); i++) @@ -254,50 +213,6 @@ static bool ExtractPlayerName(CUtlVector &pSplitPla } return false; } -#else -static void RejectConnection(IServer *server, netadr_t &address, int iClientChallenge, char *pchReason) -{ - static ICallWrapper *pRejectConnection = nullptr; - - if (!pRejectConnection) - { - int offset = -1; - if (!g_pGameConf->GetOffset("CHLTVServer::RejectConnection", &offset) || offset == -1) - { - smutils->LogError(myself, "Failed to get CHLTVServer::RejectConnection offset."); - return; - } - - PassInfo pass[3]; - pass[0].flags = PASSFLAG_BYVAL; - pass[0].type = PassType_Basic; - pass[0].size = sizeof(netadr_t *); - pass[1].flags = PASSFLAG_BYVAL; - pass[1].type = PassType_Basic; - pass[1].size = sizeof(int); - pass[2].flags = PASSFLAG_BYVAL; - pass[2].type = PassType_Basic; - pass[2].size = sizeof(char *); - - pRejectConnection = bintools->CreateVCall(offset, 0, 0, NULL, pass, 3); - } - - if (pRejectConnection) - { - unsigned char vstk[sizeof(void *) + sizeof(netadr_t *) + sizeof(int) + sizeof(char *)]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)server; - vptr += sizeof(void *); - *(netadr_t **)vptr = &address; - vptr += sizeof(netadr_t *); - *(int *)vptr = iClientChallenge; - vptr += sizeof(int); - *(char **)vptr = pchReason; - - pRejectConnection->Execute(vstk, NULL); - } -} #endif // Mimic Connect extension https://forums.alliedmods.net/showthread.php?t=162489 @@ -340,11 +255,14 @@ IClient *CForwardManager::OnSpectatorConnect(netadr_t & address, int nProtocol, IServer *server = META_IFACEPTR(IServer); if (retVal == 0) { + if (m_bHasRejectConnectionOffset) + { #if SOURCE_ENGINE == SE_CSGO - RejectConnection(server, address, rejectReason); + SH_MCALL(server, CHLTVServer_RejectConnection)(address, rejectReason); #else - RejectConnection(server, address, iClientChallenge, rejectReason); + SH_MCALL(server, CHLTVServer_RejectConnection)(address, iClientChallenge, rejectReason); #endif + } RETURN_META_VALUE(MRES_SUPERCEDE, nullptr); } diff --git a/forwards.h b/forwards.h index d442bca..e6a1933 100644 --- a/forwards.h +++ b/forwards.h @@ -98,6 +98,7 @@ private: IForward *m_SpectatorPutInServerFwd; bool m_bHasClientConnectOffset = false; + bool m_bHasRejectConnectionOffset = false; bool m_bHasGetChallengeTypeOffset = false; bool m_bHasActivatePlayerOffset = false; }; diff --git a/natives.cpp b/natives.cpp index 997f329..91131ba 100644 --- a/natives.cpp +++ b/natives.cpp @@ -30,12 +30,31 @@ */ #include "extension.h" +#include "natives.h" #define TICK_INTERVAL (gpGlobals->interval_per_tick) #define TIME_TO_TICKS( dt ) ( (int)( 0.5f + (float)(dt) / TICK_INTERVAL ) ) extern const sp_nativeinfo_t sourcetv_natives[]; +SH_DECL_MANUALHOOK0_void_vafmt(CBaseServer_BroadcastPrintf, 0, 0, 0); + +bool g_bHasClientPrintfOffset = false; + +void SetupNativeCalls() +{ + int offset = -1; + if (!g_pGameConf->GetOffset("CBaseServer::BroadcastPrintf", &offset) || offset == -1) + { + smutils->LogError(myself, "Failed to get CBaseServer::BroadcastPrintf offset."); + } + else + { + SH_MANUALHOOK_RECONFIGURE(CBaseServer_BroadcastPrintf, offset, 0, 0); + g_bHasClientPrintfOffset = true; + } +} + // native SourceTV_GetServerInstanceCount(); static cell_t Native_GetServerInstanceCount(IPluginContext *pContext, const cell_t *params) { @@ -238,34 +257,8 @@ static cell_t Native_BroadcastConsoleMessage(IPluginContext *pContext, const cel if (hltvserver == nullptr) return 0; - static ICallWrapper *pBroadcastPrintf = nullptr; - - if (!pBroadcastPrintf) - { - int offset = -1; - if (!g_pGameConf->GetOffset("CBaseServer::BroadcastPrintf", &offset) || offset == -1) - { - pContext->ReportError("Failed to get CBaseServer::BroadcastPrintf offset."); - return 0; - } - - PassInfo pass[3]; - pass[0].flags = PASSFLAG_BYVAL; - pass[0].type = PassType_Basic; - pass[0].size = sizeof(void *); - pass[1].flags = PASSFLAG_BYVAL; - pass[1].type = PassType_Basic; - pass[1].size = sizeof(char *); - pass[2].flags = PASSFLAG_BYVAL; - pass[2].type = PassType_Basic; - pass[2].size = sizeof(char *); - - void *iserver = (void *)hltvserver->GetBaseServer(); - void **vtable = *(void ***)iserver; - void *func = vtable[offset]; - - pBroadcastPrintf = bintools->CreateCall(func, CallConv_Cdecl, NULL, pass, 3); - } + if (!g_bHasClientPrintfOffset) + return 0; char buffer[1024]; size_t len; @@ -276,21 +269,7 @@ static cell_t Native_BroadcastConsoleMessage(IPluginContext *pContext, const cel return 0; } - static char fmt[] = "%s\n"; - - if (pBroadcastPrintf) - { - unsigned char vstk[sizeof(void *) + sizeof(char *) * 2]; - unsigned char *vptr = vstk; - - *(void **)vptr = (void *)hltvserver->GetBaseServer(); - vptr += sizeof(void *); - *(char **)vptr = fmt; - vptr += sizeof(char *); - *(char **)vptr = buffer; - - pBroadcastPrintf->Execute(vstk, NULL); - } + SH_MCALL(hltvserver->GetBaseServer(), CBaseServer_BroadcastPrintf)("%s\n", buffer); return 1; } diff --git a/natives.h b/natives.h new file mode 100644 index 0000000..6e4421c --- /dev/null +++ b/natives.h @@ -0,0 +1,37 @@ +/** +* vim: set ts=4 : +* ============================================================================= +* SourceMod SourceTV Manager Extension +* Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved. +* ============================================================================= +* +* 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 . +* +* As a special exception, AlliedModders LLC gives you permission to link the +* code of this program (as well as its derivative works) to "Half-Life 2," the +* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software +* by the Valve Corporation. You must obey the GNU General Public License in +* all respects for all other code used. Additionally, AlliedModders LLC grants +* this exception to all derivative works. AlliedModders LLC defines further +* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), +* or . +* +* Version: $Id$ +*/ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_NATIVES_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_NATIVES_H_ + +void SetupNativeCalls(); + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_NATIVES_H_ \ No newline at end of file