517 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			517 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "hltvserverwrapper.h"
 | 
						|
#include "forwards.h"
 | 
						|
#include "commonhooks.h"
 | 
						|
 | 
						|
void *old_host_client = nullptr;
 | 
						|
bool g_HostClientOverridden = false;
 | 
						|
 | 
						|
SH_DECL_MANUALHOOK0_void(CHLTVServer_Shutdown, 0, 0, 0);
 | 
						|
 | 
						|
#if SOURCE_ENGINE != SE_CSGO
 | 
						|
 | 
						|
// 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];
 | 
						|
static void *FakeNetChan = &FakeNetChanVtbl;
 | 
						|
SH_DECL_MANUALHOOK3(NetChan_SendNetMsg, 0, 0, 0, bool, INetMessage &, bool, bool);
 | 
						|
#endif // SOURCE_ENGINE != SE_CSGO
 | 
						|
 | 
						|
HLTVServerWrapper::HLTVServerWrapper(IHLTVServer *hltvserver)
 | 
						|
{
 | 
						|
	m_HLTVServer = hltvserver;
 | 
						|
	m_DemoRecorder = g_HLTVServers.GetDemoRecorderPtr(hltvserver);
 | 
						|
	m_Connected = true;
 | 
						|
	m_LastChatClient = nullptr;
 | 
						|
 | 
						|
	Hook();
 | 
						|
 | 
						|
	// Inform the plugins
 | 
						|
	g_pSTVForwards.CallOnServerStart(hltvserver);
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapper::Shutdown(bool bInformPlugins)
 | 
						|
{
 | 
						|
	if (!m_Connected)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (bInformPlugins)
 | 
						|
		g_pSTVForwards.CallOnServerShutdown(m_HLTVServer);
 | 
						|
 | 
						|
	Unhook();
 | 
						|
 | 
						|
	m_HLTVServer = nullptr;
 | 
						|
	m_DemoRecorder = nullptr;
 | 
						|
	m_Connected = false;
 | 
						|
}
 | 
						|
 | 
						|
IServer *HLTVServerWrapper::GetBaseServer()
 | 
						|
{
 | 
						|
	return m_HLTVServer->GetBaseServer();
 | 
						|
}
 | 
						|
 | 
						|
IHLTVServer *HLTVServerWrapper::GetHLTVServer()
 | 
						|
{
 | 
						|
	return m_HLTVServer;
 | 
						|
}
 | 
						|
 | 
						|
IDemoRecorder *HLTVServerWrapper::GetDemoRecorder()
 | 
						|
{
 | 
						|
	return m_DemoRecorder;
 | 
						|
}
 | 
						|
 | 
						|
char *HLTVServerWrapper::GetDemoFileName()
 | 
						|
{
 | 
						|
	if (!m_DemoRecorder)
 | 
						|
		return nullptr;
 | 
						|
 | 
						|
#if SOURCE_ENGINE == SE_CSGO
 | 
						|
	return (char *)m_DemoRecorder + 8;
 | 
						|
#else
 | 
						|
	return (char *)m_DemoRecorder->GetDemoFile();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
int HLTVServerWrapper::GetInstanceNumber()
 | 
						|
{
 | 
						|
	return g_HLTVServers.GetInstanceNumber(m_HLTVServer);
 | 
						|
}
 | 
						|
 | 
						|
IClient *HLTVServerWrapper::GetLastChatClient()
 | 
						|
{
 | 
						|
	return m_LastChatClient;
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapper::SetLastChatClient(IClient *client)
 | 
						|
{
 | 
						|
	m_LastChatClient = client;
 | 
						|
}
 | 
						|
 | 
						|
const char *HLTVServerWrapper::GetLastChatMessage()
 | 
						|
{
 | 
						|
	return m_LastChatMessage;
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapper::SetLastChatMessage(const char *msg)
 | 
						|
{
 | 
						|
	m_LastChatMessage = msg;
 | 
						|
}
 | 
						|
 | 
						|
HLTVClientWrapper *HLTVServerWrapper::GetClient(int index)
 | 
						|
{
 | 
						|
	// Grow the vector with null pointers
 | 
						|
	// There might have been clients with lower indexes before we were loaded.
 | 
						|
	if (m_Clients.length() < (size_t)index)
 | 
						|
	{
 | 
						|
		int start = m_Clients.length();
 | 
						|
		m_Clients.resize(index);
 | 
						|
		for (int i = start; i < index; i++)
 | 
						|
		{
 | 
						|
			m_Clients[i] = nullptr;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (!m_Clients[index - 1])
 | 
						|
	{
 | 
						|
		m_Clients[index - 1] = new HLTVClientWrapper();
 | 
						|
	}
 | 
						|
 | 
						|
	return m_Clients[index - 1];
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapper::Hook()
 | 
						|
{
 | 
						|
	if (!m_Connected)
 | 
						|
		return;
 | 
						|
 | 
						|
	g_pSTVForwards.HookServer(this);
 | 
						|
	if (m_DemoRecorder)
 | 
						|
		g_pSTVForwards.HookRecorder(m_DemoRecorder);
 | 
						|
 | 
						|
	if (g_HLTVServers.HasShutdownOffset())
 | 
						|
		SH_ADD_MANUALHOOK(CHLTVServer_Shutdown, m_HLTVServer->GetBaseServer(), SH_MEMBER(this, &HLTVServerWrapper::OnHLTVServerShutdown), false);
 | 
						|
 | 
						|
	if (iserver)
 | 
						|
	{
 | 
						|
		IClient *pClient = iserver->GetClient(m_HLTVServer->GetHLTVSlot());
 | 
						|
		if (pClient)
 | 
						|
		{
 | 
						|
			// Hook ExecuteStringCommand
 | 
						|
			g_pSTVCommonHooks.AddHLTVClientHook(this, pClient);
 | 
						|
#if SOURCE_ENGINE != SE_CSGO
 | 
						|
			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
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapper::Unhook()
 | 
						|
{
 | 
						|
	if (!m_Connected)
 | 
						|
		return;
 | 
						|
 | 
						|
	g_pSTVForwards.UnhookServer(this);
 | 
						|
	if (m_DemoRecorder)
 | 
						|
		g_pSTVForwards.UnhookRecorder(m_DemoRecorder);
 | 
						|
 | 
						|
	if (g_HLTVServers.HasShutdownOffset())
 | 
						|
		SH_REMOVE_MANUALHOOK(CHLTVServer_Shutdown, m_HLTVServer->GetBaseServer(), SH_MEMBER(this, &HLTVServerWrapper::OnHLTVServerShutdown), false);
 | 
						|
 | 
						|
	if (iserver)
 | 
						|
	{
 | 
						|
		IClient *pClient = iserver->GetClient(m_HLTVServer->GetHLTVSlot());
 | 
						|
		if (pClient)
 | 
						|
		{
 | 
						|
			// Remove ExecuteStringCommand hook
 | 
						|
			g_pSTVCommonHooks.RemoveHLTVClientHook(this, pClient);
 | 
						|
#if SOURCE_ENGINE != SE_CSGO
 | 
						|
			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
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// CHLTVServer::Shutdown deregisters the hltvserver from the hltvdirector, 
 | 
						|
// so RemoveHLTVServer/SetHLTVServer(NULL) is called too on the master proxy.
 | 
						|
void HLTVServerWrapper::OnHLTVServerShutdown()
 | 
						|
{
 | 
						|
	if (!m_Connected)
 | 
						|
		RETURN_META(MRES_IGNORED);
 | 
						|
 | 
						|
	Shutdown(true);
 | 
						|
 | 
						|
	RETURN_META(MRES_IGNORED);
 | 
						|
}
 | 
						|
 | 
						|
// 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 HLTVServerWrapper::OnHLTVBotExecuteStringCommand(const char *s)
 | 
						|
{
 | 
						|
	if (!host_client)
 | 
						|
	{
 | 
						|
		// Block crash in status command.
 | 
						|
		if (!Q_stricmp(s, "status"))
 | 
						|
			RETURN_META_VALUE(MRES_SUPERCEDE, 0);
 | 
						|
		else
 | 
						|
			RETURN_META_VALUE(MRES_IGNORED, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	IClient *pClient = META_IFACEPTR(IClient);
 | 
						|
	if (!pClient)
 | 
						|
		RETURN_META_VALUE(MRES_IGNORED, 0);
 | 
						|
 | 
						|
	// The IClient vtable is +4 from the CBaseClient vtable due to multiple inheritance.
 | 
						|
	void *pGameClient = (void *)((intptr_t)pClient - 4);
 | 
						|
 | 
						|
	old_host_client = *(void **)host_client;
 | 
						|
	*(void **)host_client = pGameClient;
 | 
						|
	g_HostClientOverridden = true;
 | 
						|
 | 
						|
	RETURN_META_VALUE(MRES_IGNORED, 0);
 | 
						|
}
 | 
						|
 | 
						|
bool HLTVServerWrapper::OnHLTVBotExecuteStringCommand_Post(const char *s)
 | 
						|
{
 | 
						|
	if (!host_client || !g_HostClientOverridden)
 | 
						|
		RETURN_META_VALUE(MRES_IGNORED, 0);
 | 
						|
 | 
						|
	*(void **)host_client = old_host_client;
 | 
						|
	g_HostClientOverridden = false;
 | 
						|
	RETURN_META_VALUE(MRES_IGNORED, 0);
 | 
						|
}
 | 
						|
 | 
						|
#if SOURCE_ENGINE != SE_CSGO
 | 
						|
void HLTVServerWrapper::OnCGameClient_ClientPrintf_Post(const char* buf)
 | 
						|
{
 | 
						|
	void *pGameClient = META_IFACEPTR(void);
 | 
						|
	IClient *pClient = (IClient *)((intptr_t)pGameClient + 4);
 | 
						|
	HandleClientPrintf(pClient, buf);
 | 
						|
 | 
						|
	// We already called the function in HandleClientPrintf.
 | 
						|
	// Would crash or not do anything anyways.
 | 
						|
	RETURN_META(MRES_SUPERCEDE);
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapper::OnIClient_ClientPrintf_Post(const char* buf)
 | 
						|
{
 | 
						|
	IClient *pClient = META_IFACEPTR(IClient);
 | 
						|
	HandleClientPrintf(pClient, buf);
 | 
						|
 | 
						|
	// We already called the function in HandleClientPrintf.
 | 
						|
	// Would crash or not do anything anyways.
 | 
						|
	RETURN_META(MRES_SUPERCEDE);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
	}
 | 
						|
 | 
						|
#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.
 | 
						|
	// This will call our own OnHLTVBotNetChanSendNetMsg function
 | 
						|
	SH_CALL(pClient, &IClient::ClientPrintf)("%s", buf);
 | 
						|
	// Set the fake netchannel back to 0.
 | 
						|
	*(void **)pNetChannel = nullptr;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/**
 | 
						|
 * Manage the wrappers!
 | 
						|
 */
 | 
						|
void HLTVServerWrapperManager::InitHooks()
 | 
						|
{
 | 
						|
	int offset;
 | 
						|
	if (g_pGameConf->GetOffset("CHLTVServer::Shutdown", &offset))
 | 
						|
	{
 | 
						|
		SH_MANUALHOOK_RECONFIGURE(CHLTVServer_Shutdown, offset, 0, 0);
 | 
						|
		m_bHasShutdownOffset = true;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		smutils->LogError(myself, "Failed to find CHLTVServer::Shutdown offset.");
 | 
						|
	}
 | 
						|
 | 
						|
#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)
 | 
						|
		{
 | 
						|
			smutils->LogError(myself, "CNetChan::SendNetMsg offset too big. Need to raise define and recompile. Contact the author.");
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			// This is a hack. Bots don't have a net channel, but ClientPrintf tries to call m_NetChannel->SendNetMsg directly.
 | 
						|
			// CGameClient::SendNetMsg would have redirected it to the hltvserver correctly, but isn't used there..
 | 
						|
			// We craft a fake object with a large enough "vtable" and hook it using sourcehook.
 | 
						|
			// Before a call to ClientPrintf, this fake object is set as CBaseClient::m_NetChannel, so ClientPrintf creates 
 | 
						|
			// the SVC_Print INetMessage and calls our "hooked" m_NetChannel->SendNetMsg function.
 | 
						|
			// In that function we just call CGameClient::SendNetMsg with the given INetMessage to flow it through the same
 | 
						|
			// path as other net messages.
 | 
						|
			SH_MANUALHOOK_RECONFIGURE(NetChan_SendNetMsg, offset, 0, 0);
 | 
						|
			SH_ADD_MANUALHOOK(NetChan_SendNetMsg, &FakeNetChan, SH_MEMBER(this, &HLTVServerWrapperManager::OnHLTVBotNetChanSendNetMsg), false);
 | 
						|
			m_bSendNetMsgHooked = true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		smutils->LogError(myself, "Failed to find CNetChan::SendNetMsg offset. Can't print to demo console.");
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapperManager::ShutdownHooks()
 | 
						|
{
 | 
						|
	g_pSTVForwards.RemoveBroadcastLocalChatDetour();
 | 
						|
#ifndef WIN32
 | 
						|
	g_pSTVForwards.RemoveStartRecordingDetour();
 | 
						|
	g_pSTVForwards.RemoveStopRecordingDetour();
 | 
						|
#endif
 | 
						|
 | 
						|
#if SOURCE_ENGINE != SE_CSGO
 | 
						|
	if (m_bSendNetMsgHooked)
 | 
						|
	{
 | 
						|
		SH_REMOVE_MANUALHOOK(NetChan_SendNetMsg, &FakeNetChan, SH_MEMBER(this, &HLTVServerWrapperManager::OnHLTVBotNetChanSendNetMsg), false);
 | 
						|
		m_bSendNetMsgHooked = false;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapperManager::AddServer(IHLTVServer *hltvserver)
 | 
						|
{
 | 
						|
	// Create the detours once the first sourcetv server is created.
 | 
						|
	g_pSTVForwards.CreateBroadcastLocalChatDetour();
 | 
						|
#ifndef WIN32
 | 
						|
	g_pSTVForwards.CreateStartRecordingDetour();
 | 
						|
	g_pSTVForwards.CreateStopRecordingDetour();
 | 
						|
#endif
 | 
						|
 | 
						|
	HLTVServerWrapper *wrapper = new HLTVServerWrapper(hltvserver);
 | 
						|
	m_HLTVServers.append(wrapper);
 | 
						|
}
 | 
						|
 | 
						|
void HLTVServerWrapperManager::RemoveServer(IHLTVServer *hltvserver, bool bInformPlugins)
 | 
						|
{
 | 
						|
	for (unsigned int i = 0; i < m_HLTVServers.length(); i++)
 | 
						|
	{
 | 
						|
		HLTVServerWrapper *wrapper = m_HLTVServers[i];
 | 
						|
		if (wrapper->GetHLTVServer() != hltvserver)
 | 
						|
			continue;
 | 
						|
 | 
						|
		wrapper->Shutdown(bInformPlugins);
 | 
						|
		m_HLTVServers.remove(i);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
HLTVServerWrapper *HLTVServerWrapperManager::GetWrapper(IHLTVServer *hltvserver)
 | 
						|
{
 | 
						|
	for (unsigned int i = 0; i < m_HLTVServers.length(); i++)
 | 
						|
	{
 | 
						|
		HLTVServerWrapper *wrapper = m_HLTVServers[i];
 | 
						|
		if (wrapper->GetHLTVServer() == hltvserver)
 | 
						|
			return wrapper;
 | 
						|
	}
 | 
						|
	return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
HLTVServerWrapper *HLTVServerWrapperManager::GetWrapper(IServer *server)
 | 
						|
{
 | 
						|
	for (unsigned int i = 0; i < m_HLTVServers.length(); i++)
 | 
						|
	{
 | 
						|
		HLTVServerWrapper *wrapper = m_HLTVServers[i];
 | 
						|
		if (wrapper->GetBaseServer() == server)
 | 
						|
			return wrapper;
 | 
						|
	}
 | 
						|
	return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
HLTVServerWrapper *HLTVServerWrapperManager::GetWrapper(IDemoRecorder *demorecorder)
 | 
						|
{
 | 
						|
	for (unsigned int i = 0; i < m_HLTVServers.length(); i++)
 | 
						|
	{
 | 
						|
		HLTVServerWrapper *wrapper = m_HLTVServers[i];
 | 
						|
		if (wrapper->GetDemoRecorder() != nullptr && wrapper->GetDemoRecorder() == demorecorder)
 | 
						|
			return wrapper;
 | 
						|
	}
 | 
						|
	return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
int HLTVServerWrapperManager::GetInstanceNumber(IHLTVServer *hltvserver)
 | 
						|
{
 | 
						|
#if SOURCE_ENGINE == SE_CSGO
 | 
						|
	for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++)
 | 
						|
	{
 | 
						|
		if (hltvserver == hltvdirector->GetHLTVServer(i))
 | 
						|
			return i;
 | 
						|
	}
 | 
						|
 | 
						|
	// We should have found it in the above loop :S
 | 
						|
	smutils->LogError(myself, "Failed to find IHLTVServer instance in director.");
 | 
						|
	return -1;
 | 
						|
#else
 | 
						|
	return 0;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
IDemoRecorder *HLTVServerWrapperManager::GetDemoRecorderPtr(IHLTVServer *hltv)
 | 
						|
{
 | 
						|
	static int offset = -1;
 | 
						|
	if (offset == -1)
 | 
						|
	{
 | 
						|
		void *addr;
 | 
						|
		if (!g_pGameConf->GetAddress("CHLTVServer::m_DemoRecorder", &addr))
 | 
						|
		{
 | 
						|
			smutils->LogError(myself, "Failed to get CHLTVServer::m_DemoRecorder offset.");
 | 
						|
			return nullptr;
 | 
						|
		}
 | 
						|
 | 
						|
		*(int **)&offset = (int *)addr;
 | 
						|
 | 
						|
		// See if we have to subtract something from the offset.
 | 
						|
		int baseOffset = 0;
 | 
						|
		if (g_pGameConf->GetOffset("CHLTVDemoRecorder_BaseOffset", &baseOffset))
 | 
						|
		{
 | 
						|
			offset -= baseOffset;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (hltv)
 | 
						|
	{
 | 
						|
		IServer *baseServer = hltv->GetBaseServer();
 | 
						|
#ifndef WIN32
 | 
						|
	return (IDemoRecorder *)((intptr_t)baseServer + offset);
 | 
						|
#else
 | 
						|
#if SOURCE_ENGINE == SE_CSGO
 | 
						|
		return (IDemoRecorder *)((intptr_t)hltv + offset);
 | 
						|
#else
 | 
						|
		return (IDemoRecorder *)((intptr_t)baseServer + offset);
 | 
						|
#endif // SOURCE_ENGINE == SE_CSGO
 | 
						|
#endif // !WIN32
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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)
 | 
						|
{
 | 
						|
	// No need to worry about the right selected hltvserver, because there can only be one.
 | 
						|
	IClient *pClient = iserver->GetClient(hltvserver->GetHLTVServer()->GetHLTVSlot());
 | 
						|
	if (!pClient)
 | 
						|
		RETURN_META_VALUE(MRES_SUPERCEDE, false);
 | 
						|
 | 
						|
	// Let the message flow through the intended path like CGameClient::SendNetMsg wants to.
 | 
						|
	bool bRetSent = pClient->SendNetMsg(msg, bForceReliable);
 | 
						|
 | 
						|
	// It's important to supercede, because there is no original function to call.
 | 
						|
	// (the "vtable" was empty before hooking it)
 | 
						|
	// See FakeNetChan variable at the top.
 | 
						|
	RETURN_META_VALUE(MRES_SUPERCEDE, bRetSent);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
HLTVServerWrapperManager g_HLTVServers;
 |