Refactor SourceTV instance hooking
Keep the instances in seperate wrappers to clear up the hooks. This allows for some OnServerStart and OnServerShutdown forwards. To prepare support for relay servers, CHLTVServer::Shutdown is hooked to detect shutdown instead of relying on the director unregistering the instance.
This commit is contained in:
		
							parent
							
								
									fb03d5f2d5
								
							
						
					
					
						commit
						fabdbe7d12
					
				
							
								
								
									
										248
									
								
								extension.cpp
									
									
									
									
									
								
							
							
						
						
									
										248
									
								
								extension.cpp
									
									
									
									
									
								
							| @ -30,15 +30,13 @@ | |||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include "extension.h" | #include "extension.h" | ||||||
|  | #include "hltvserverwrapper.h" | ||||||
| #include "forwards.h" | #include "forwards.h" | ||||||
| #include "natives.h" | #include "natives.h" | ||||||
| 
 | 
 | ||||||
| IHLTVDirector *hltvdirector = nullptr; | IHLTVDirector *hltvdirector = nullptr; | ||||||
| IHLTVServer *hltvserver = nullptr; |  | ||||||
| IDemoRecorder *demorecorder = nullptr; |  | ||||||
| void *host_client = nullptr; | void *host_client = nullptr; | ||||||
| void *old_host_client = nullptr; | HLTVServerWrapper *hltvserver = nullptr; | ||||||
| bool g_HostClientOverridden = false; |  | ||||||
| 
 | 
 | ||||||
| IGameEventManager2 *gameevents = nullptr; | IGameEventManager2 *gameevents = nullptr; | ||||||
| CGlobalVars *gpGlobals = nullptr; | CGlobalVars *gpGlobals = nullptr; | ||||||
| @ -58,19 +56,8 @@ SH_DECL_HOOK1_void(IHLTVDirector, AddHLTVServer, SH_NOATTRIB, 0, IHLTVServer *); | |||||||
| SH_DECL_HOOK1_void(IHLTVDirector, RemoveHLTVServer, SH_NOATTRIB, 0, IHLTVServer *); | SH_DECL_HOOK1_void(IHLTVDirector, RemoveHLTVServer, SH_NOATTRIB, 0, IHLTVServer *); | ||||||
| #else | #else | ||||||
| SH_DECL_HOOK1_void(IHLTVDirector, SetHLTVServer, SH_NOATTRIB, 0, IHLTVServer *); | SH_DECL_HOOK1_void(IHLTVDirector, SetHLTVServer, SH_NOATTRIB, 0, IHLTVServer *); | ||||||
| 
 |  | ||||||
| // Stuff to print to demo console
 |  | ||||||
| SH_DECL_HOOK0_void_vafmt(IClient, ClientPrintf, SH_NOATTRIB, 0); |  | ||||||
| // 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 | #endif | ||||||
| 
 | 
 | ||||||
| SH_DECL_HOOK1(IClient, ExecuteStringCommand, SH_NOATTRIB, 0, bool, const char *); |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * @file extension.cpp |  * @file extension.cpp | ||||||
|  * @brief Implement extension code here. |  * @brief Implement extension code here. | ||||||
| @ -106,33 +93,7 @@ bool SourceTVManager::SDK_OnLoad(char *error, size_t maxlength, bool late) | |||||||
| 		smutils->LogError(myself, "Failed to find host_client pointer. Server might crash when executing commands on SourceTV bot."); | 		smutils->LogError(myself, "Failed to find host_client pointer. Server might crash when executing commands on SourceTV bot."); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| #if SOURCE_ENGINE != SE_CSGO | 	g_HLTVServers.InitHooks(); | ||||||
| 	int offset; |  | ||||||
| 	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, &SourceTVManager::OnHLTVBotNetChanSendNetMsg), false); |  | ||||||
| 			g_SendNetMsgHooked = true; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		smutils->LogError(myself, "Failed to find CNetChan::SendNetMsg offset. Can't print to demo console."); |  | ||||||
| 	} |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| 	sharesys->AddNatives(myself, sourcetv_natives); | 	sharesys->AddNatives(myself, sourcetv_natives); | ||||||
| 	sharesys->RegisterLibrary(myself, "sourcetvmanager"); | 	sharesys->RegisterLibrary(myself, "sourcetvmanager"); | ||||||
| @ -160,19 +121,19 @@ void SourceTVManager::SDK_OnAllLoaded() | |||||||
| 		smutils->LogError(myself, "Failed to get IServer interface from SDKTools. Some functions won't work."); | 		smutils->LogError(myself, "Failed to get IServer interface from SDKTools. Some functions won't work."); | ||||||
| 
 | 
 | ||||||
| #if SOURCE_ENGINE == SE_CSGO | #if SOURCE_ENGINE == SE_CSGO | ||||||
| 	if (hltvdirector->GetHLTVServerCount() > 0) |  | ||||||
| 		SelectSourceTVServer(hltvdirector->GetHLTVServer(0)); |  | ||||||
| 
 |  | ||||||
| 	// Hook all the exisiting servers.
 | 	// Hook all the exisiting servers.
 | ||||||
| 	for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++) | 	for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++) | ||||||
| 	{ | 	{ | ||||||
| 		HookSourceTVServer(hltvdirector->GetHLTVServer(i)); | 		g_HLTVServers.AddServer(hltvdirector->GetHLTVServer(i)); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if (hltvdirector->GetHLTVServerCount() > 0) | ||||||
|  | 		SelectSourceTVServer(hltvdirector->GetHLTVServer(0)); | ||||||
| #else | #else | ||||||
| 	if (hltvdirector->GetHLTVServer()) | 	if (hltvdirector->GetHLTVServer()) | ||||||
| 	{ | 	{ | ||||||
|  | 		g_HLTVServers.AddServer(hltvdirector->GetHLTVServer()); | ||||||
| 		SelectSourceTVServer(hltvdirector->GetHLTVServer()); | 		SelectSourceTVServer(hltvdirector->GetHLTVServer()); | ||||||
| 		HookSourceTVServer(hltvdirector->GetHLTVServer()); |  | ||||||
| 	} | 	} | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| @ -204,27 +165,23 @@ void SourceTVManager::SDK_OnUnload() | |||||||
| 	SH_REMOVE_HOOK(IHLTVDirector, RemoveHLTVServer, hltvdirector, SH_MEMBER(this, &SourceTVManager::OnRemoveHLTVServer_Post), true); | 	SH_REMOVE_HOOK(IHLTVDirector, RemoveHLTVServer, hltvdirector, SH_MEMBER(this, &SourceTVManager::OnRemoveHLTVServer_Post), true); | ||||||
| #else | #else | ||||||
| 	SH_REMOVE_HOOK(IHLTVDirector, SetHLTVServer, hltvdirector, SH_MEMBER(this, &SourceTVManager::OnSetHLTVServer_Post), true); | 	SH_REMOVE_HOOK(IHLTVDirector, SetHLTVServer, hltvdirector, SH_MEMBER(this, &SourceTVManager::OnSetHLTVServer_Post), true); | ||||||
| 
 |  | ||||||
| 	if (g_SendNetMsgHooked) |  | ||||||
| 	{ |  | ||||||
| 		SH_REMOVE_MANUALHOOK(NetChan_SendNetMsg, &FakeNetChan, SH_MEMBER(this, &SourceTVManager::OnHLTVBotNetChanSendNetMsg), false); |  | ||||||
| 		g_SendNetMsgHooked = false; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | 	g_HLTVServers.ShutdownHooks(); | ||||||
|  | 
 | ||||||
| 	gameconfs->CloseGameConfigFile(g_pGameConf); | 	gameconfs->CloseGameConfigFile(g_pGameConf); | ||||||
| 
 | 
 | ||||||
| #if SOURCE_ENGINE == SE_CSGO | #if SOURCE_ENGINE == SE_CSGO | ||||||
| 	// Unhook all the existing servers.
 | 	// Unhook all the existing servers.
 | ||||||
| 	for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++) | 	for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++) | ||||||
| 	{ | 	{ | ||||||
| 		UnhookSourceTVServer(hltvdirector->GetHLTVServer(i)); | 		// We don't know if the extension is just being unloaded or the server is shutting down.
 | ||||||
|  | 		// So don't inform the plugins of this removal.
 | ||||||
|  | 		g_HLTVServers.RemoveServer(hltvdirector->GetHLTVServer(i), false); | ||||||
| 	} | 	} | ||||||
| #else | #else | ||||||
| 	// Unhook the server
 | 	// Unhook the server
 | ||||||
| 	if (hltvdirector->GetHLTVServer()) | 	g_HLTVServers.RemoveServer(hltvdirector->GetHLTVServer(), false); | ||||||
| 		UnhookSourceTVServer(hltvdirector->GetHLTVServer()); |  | ||||||
| #endif | #endif | ||||||
| 	g_pSTVForwards.Shutdown(); | 	g_pSTVForwards.Shutdown(); | ||||||
| } | } | ||||||
| @ -237,113 +194,43 @@ bool SourceTVManager::QueryRunning(char *error, size_t maxlength) | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SourceTVManager::HookSourceTVServer(IHLTVServer *hltv) |  | ||||||
| { |  | ||||||
| 	if (hltv != nullptr) |  | ||||||
| 	{ |  | ||||||
| 		g_pSTVForwards.HookServer(hltv->GetBaseServer()); |  | ||||||
| 		g_pSTVForwards.HookRecorder(GetDemoRecorderPtr(hltv)); |  | ||||||
| 
 |  | ||||||
| 		if (iserver) |  | ||||||
| 		{ |  | ||||||
| 			IClient *pClient = iserver->GetClient(hltv->GetHLTVSlot()); |  | ||||||
| 			if (pClient) |  | ||||||
| 			{ |  | ||||||
| 				SH_ADD_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &SourceTVManager::OnHLTVBotExecuteStringCommand), false); |  | ||||||
| 				SH_ADD_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &SourceTVManager::OnHLTVBotExecuteStringCommand_Post), true); |  | ||||||
| #if SOURCE_ENGINE != SE_CSGO |  | ||||||
| 				SH_ADD_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &SourceTVManager::OnHLTVBotClientPrintf_Post), false); |  | ||||||
| #endif |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SourceTVManager::UnhookSourceTVServer(IHLTVServer *hltv) |  | ||||||
| { |  | ||||||
| 	if (hltv != nullptr) |  | ||||||
| 	{ |  | ||||||
| 		g_pSTVForwards.UnhookServer(hltv->GetBaseServer()); |  | ||||||
| 		g_pSTVForwards.UnhookRecorder(GetDemoRecorderPtr(hltv)); |  | ||||||
| 
 |  | ||||||
| 		if (iserver) |  | ||||||
| 		{ |  | ||||||
| 			IClient *pClient = iserver->GetClient(hltv->GetHLTVSlot()); |  | ||||||
| 			if (pClient) |  | ||||||
| 			{ |  | ||||||
| 				SH_REMOVE_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &SourceTVManager::OnHLTVBotExecuteStringCommand), false); |  | ||||||
| 				SH_REMOVE_HOOK(IClient, ExecuteStringCommand, pClient, SH_MEMBER(this, &SourceTVManager::OnHLTVBotExecuteStringCommand_Post), true); |  | ||||||
| #if SOURCE_ENGINE != SE_CSGO |  | ||||||
| 				SH_REMOVE_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &SourceTVManager::OnHLTVBotClientPrintf_Post), false); |  | ||||||
| #endif |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SourceTVManager::SelectSourceTVServer(IHLTVServer *hltv) | void SourceTVManager::SelectSourceTVServer(IHLTVServer *hltv) | ||||||
| { | { | ||||||
| 	// Select the new server.
 | 	// Select the new server.
 | ||||||
| 	hltvserver = hltv; | 	hltvserver = g_HLTVServers.GetWrapper(hltv); | ||||||
| 	demorecorder = GetDemoRecorderPtr(hltvserver); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| IDemoRecorder *SourceTVManager::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; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (hltv) |  | ||||||
| 	{ |  | ||||||
| #if SOURCE_ENGINE == SE_CSGO |  | ||||||
| 		return (IDemoRecorder *)((intptr_t)hltv + offset); |  | ||||||
| #else |  | ||||||
| 		IServer *baseServer = hltv->GetBaseServer(); |  | ||||||
| 		return (IDemoRecorder *)((intptr_t)baseServer + offset); |  | ||||||
| #endif |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		return nullptr; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if SOURCE_ENGINE == SE_CSGO | #if SOURCE_ENGINE == SE_CSGO | ||||||
| void SourceTVManager::OnAddHLTVServer_Post(IHLTVServer *hltv) | void SourceTVManager::OnAddHLTVServer_Post(IHLTVServer *hltv) | ||||||
| { | { | ||||||
| 	HookSourceTVServer(hltv); | 	g_HLTVServers.AddServer(hltv); | ||||||
| 
 | 
 | ||||||
| 	// We already selected some SourceTV server. Keep it.
 | 	// We already selected some SourceTV server. Keep it.
 | ||||||
| 	if (hltvserver != nullptr) | 	if (hltvserver != nullptr) | ||||||
| 		RETURN_META(MRES_IGNORED); | 		RETURN_META(MRES_IGNORED); | ||||||
| 	 | 	 | ||||||
| 	// This is the first SourceTV server to be added. Hook it.
 | 	// This is the first SourceTV server to be added.
 | ||||||
| 	SelectSourceTVServer(hltv); | 	SelectSourceTVServer(hltv); | ||||||
| 	RETURN_META(MRES_IGNORED); | 	RETURN_META(MRES_IGNORED); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SourceTVManager::OnRemoveHLTVServer_Post(IHLTVServer *hltv) | void SourceTVManager::OnRemoveHLTVServer_Post(IHLTVServer *hltv) | ||||||
| { | { | ||||||
| 	UnhookSourceTVServer(hltv); | 	HLTVServerWrapper *wrapper = g_HLTVServers.GetWrapper(hltv); | ||||||
|  | 	if (!wrapper) | ||||||
|  | 		RETURN_META(MRES_IGNORED); | ||||||
|  | 
 | ||||||
|  | 	// With the CHLTVServer::Shutdown hook, this isn't needed?
 | ||||||
|  | 	// Doesn't hurt either..
 | ||||||
|  | 	g_HLTVServers.RemoveServer(hltv, true); | ||||||
| 
 | 
 | ||||||
| 	// We got this SourceTV server selected. Now it's gone :(
 | 	// We got this SourceTV server selected. Now it's gone :(
 | ||||||
| 	if (hltvserver == hltv) | 	if (hltvserver == wrapper) | ||||||
| 	{ | 	{ | ||||||
| 		// Is there another one available? Try to keep us operable.
 | 		// Is there another one available? Try to keep us operable.
 | ||||||
| 		if (hltvdirector->GetHLTVServerCount() > 0) | 		if (hltvdirector->GetHLTVServerCount() > 0) | ||||||
| 		{ | 		{ | ||||||
| 			SelectSourceTVServer(hltvdirector->GetHLTVServer(0)); | 			SelectSourceTVServer(hltvdirector->GetHLTVServer(0)); | ||||||
| 			HookSourceTVServer(hltvserver); |  | ||||||
| 		} | 		} | ||||||
| 		// No sourcetv active.
 | 		// No sourcetv active.
 | ||||||
| 		else | 		else | ||||||
| @ -354,45 +241,6 @@ void SourceTVManager::OnRemoveHLTVServer_Post(IHLTVServer *hltv) | |||||||
| 	RETURN_META(MRES_IGNORED); | 	RETURN_META(MRES_IGNORED); | ||||||
| } | } | ||||||
| #else | #else | ||||||
| void SourceTVManager::OnHLTVBotClientPrintf_Post(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); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	IClient *pClient = META_IFACEPTR(IClient); |  | ||||||
| 
 |  | ||||||
| 	void *pNetChannel = (void *)((char *)pClient + offset); |  | ||||||
| 	// 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; |  | ||||||
| 
 |  | ||||||
| 	RETURN_META(MRES_IGNORED); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool SourceTVManager::OnHLTVBotNetChanSendNetMsg(INetMessage &msg, bool bForceReliable, bool bVoice) |  | ||||||
| { |  | ||||||
| 	IClient *pClient = iserver->GetClient(hltvserver->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); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SourceTVManager::OnSetHLTVServer_Post(IHLTVServer *hltv) | void SourceTVManager::OnSetHLTVServer_Post(IHLTVServer *hltv) | ||||||
| { | { | ||||||
| 	// Server shut down?
 | 	// Server shut down?
 | ||||||
| @ -402,53 +250,15 @@ void SourceTVManager::OnSetHLTVServer_Post(IHLTVServer *hltv) | |||||||
| 		if (!hltvserver) | 		if (!hltvserver) | ||||||
| 			RETURN_META(MRES_IGNORED); | 			RETURN_META(MRES_IGNORED); | ||||||
| 
 | 
 | ||||||
| 		UnhookSourceTVServer(hltvserver); | 		// With the CHLTVServer::Shutdown hook, this isn't needed?
 | ||||||
|  | 		// Doesn't hurt either..
 | ||||||
|  | 		g_HLTVServers.RemoveServer(hltvserver->GetHLTVServer(), true); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		HookSourceTVServer(hltv); | 		g_HLTVServers.AddServer(hltv); | ||||||
| 	} | 	} | ||||||
| 	SelectSourceTVServer(hltv); | 	SelectSourceTVServer(hltv); | ||||||
| 	RETURN_META(MRES_IGNORED); | 	RETURN_META(MRES_IGNORED); | ||||||
| } | } | ||||||
| #endif | #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) |  | ||||||
| 		RETURN_META_VALUE(MRES_IGNORED, 0); |  | ||||||
| 
 |  | ||||||
| 	IClient *pClient = iserver->GetClient(hltvserver->GetHLTVSlot()); |  | ||||||
| 	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 SourceTVManager::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); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								extension.h
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								extension.h
									
									
									
									
									
								
							| @ -52,6 +52,7 @@ | |||||||
| #include "hltvclientwrapper.h" | #include "hltvclientwrapper.h" | ||||||
| 
 | 
 | ||||||
| class INetMessage; | class INetMessage; | ||||||
|  | class HLTVServerWrapper; | ||||||
| 
 | 
 | ||||||
| extern ConVar tv_force_steamauth; | extern ConVar tv_force_steamauth; | ||||||
| 
 | 
 | ||||||
| @ -135,7 +136,6 @@ public: // IConCommandBaseAccessor | |||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 	void SelectSourceTVServer(IHLTVServer *hltv); | 	void SelectSourceTVServer(IHLTVServer *hltv); | ||||||
| 	IDemoRecorder *GetDemoRecorderPtr(IHLTVServer *hltvserver); |  | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| #if SOURCE_ENGINE == SE_CSGO | #if SOURCE_ENGINE == SE_CSGO | ||||||
| @ -143,16 +143,7 @@ private: | |||||||
| 	void OnRemoveHLTVServer_Post(IHLTVServer *hltv); | 	void OnRemoveHLTVServer_Post(IHLTVServer *hltv); | ||||||
| #else | #else | ||||||
| 	void OnSetHLTVServer_Post(IHLTVServer *hltv); | 	void OnSetHLTVServer_Post(IHLTVServer *hltv); | ||||||
| 	 |  | ||||||
| 	bool OnHLTVBotNetChanSendNetMsg(INetMessage &msg, bool bForceReliable, bool bVoice); |  | ||||||
| 	void OnHLTVBotClientPrintf_Post(const char *buf); |  | ||||||
| #endif | #endif | ||||||
| 	bool OnHLTVBotExecuteStringCommand(const char *s); |  | ||||||
| 	bool OnHLTVBotExecuteStringCommand_Post(const char *s); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	void HookSourceTVServer(IHLTVServer *hltv); |  | ||||||
| 	void UnhookSourceTVServer(IHLTVServer *hltv); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Interfaces from SourceMod */ | /* Interfaces from SourceMod */ | ||||||
| @ -167,7 +158,7 @@ extern IGameEventManager2 *gameevents; | |||||||
| extern ICvar *icvar; | extern ICvar *icvar; | ||||||
| 
 | 
 | ||||||
| extern IHLTVDirector *hltvdirector; | extern IHLTVDirector *hltvdirector; | ||||||
| extern IHLTVServer *hltvserver; | extern HLTVServerWrapper *hltvserver; | ||||||
| extern IDemoRecorder *demorecorder; | extern void *host_client; | ||||||
| 
 | 
 | ||||||
| #endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
 | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								forwards.cpp
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								forwards.cpp
									
									
									
									
									
								
							| @ -104,6 +104,9 @@ void CForwardManager::Init() | |||||||
| 	m_SpectatorDisconnectFwd = forwards->CreateForward("SourceTV_OnSpectatorDisconnect", ET_Ignore, 2, NULL, Param_Cell, Param_String); | 	m_SpectatorDisconnectFwd = forwards->CreateForward("SourceTV_OnSpectatorDisconnect", ET_Ignore, 2, NULL, Param_Cell, Param_String); | ||||||
| 	m_SpectatorDisconnectedFwd = forwards->CreateForward("SourceTV_OnSpectatorDisconnected", ET_Ignore, 2, NULL, Param_Cell, Param_String); | 	m_SpectatorDisconnectedFwd = forwards->CreateForward("SourceTV_OnSpectatorDisconnected", ET_Ignore, 2, NULL, Param_Cell, Param_String); | ||||||
| 	m_SpectatorPutInServerFwd = forwards->CreateForward("SourceTV_OnSpectatorPutInServer", ET_Ignore, 1, NULL, Param_Cell); | 	m_SpectatorPutInServerFwd = forwards->CreateForward("SourceTV_OnSpectatorPutInServer", ET_Ignore, 1, NULL, Param_Cell); | ||||||
|  | 
 | ||||||
|  | 	m_ServerStartFwd = forwards->CreateForward("SourceTV_OnServerStart", ET_Ignore, 1, NULL, Param_Cell); | ||||||
|  | 	m_ServerShutdownFwd = forwards->CreateForward("SourceTV_OnServerShutdown", ET_Ignore, 1, NULL, Param_Cell); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CForwardManager::Shutdown() | void CForwardManager::Shutdown() | ||||||
| @ -115,6 +118,9 @@ void CForwardManager::Shutdown() | |||||||
| 	forwards->ReleaseForward(m_SpectatorDisconnectFwd); | 	forwards->ReleaseForward(m_SpectatorDisconnectFwd); | ||||||
| 	forwards->ReleaseForward(m_SpectatorDisconnectedFwd); | 	forwards->ReleaseForward(m_SpectatorDisconnectedFwd); | ||||||
| 	forwards->ReleaseForward(m_SpectatorPutInServerFwd); | 	forwards->ReleaseForward(m_SpectatorPutInServerFwd); | ||||||
|  | 
 | ||||||
|  | 	forwards->ReleaseForward(m_ServerStartFwd); | ||||||
|  | 	forwards->ReleaseForward(m_ServerShutdownFwd); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CForwardManager::HookRecorder(IDemoRecorder *recorder) | void CForwardManager::HookRecorder(IDemoRecorder *recorder) | ||||||
| @ -188,6 +194,18 @@ void CForwardManager::UnhookClient(IClient *client) | |||||||
| 	SH_REMOVE_HOOK(IClient, Disconnect, client, SH_MEMBER(this, &CForwardManager::OnSpectatorDisconnect), false); | 	SH_REMOVE_HOOK(IClient, Disconnect, client, SH_MEMBER(this, &CForwardManager::OnSpectatorDisconnect), false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void CForwardManager::CallOnServerStart(IHLTVServer *server) | ||||||
|  | { | ||||||
|  | 	m_ServerStartFwd->PushCell(0); // TODO: Get right hltvinstance
 | ||||||
|  | 	m_ServerStartFwd->Execute(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CForwardManager::CallOnServerShutdown(IHLTVServer *server) | ||||||
|  | { | ||||||
|  | 	m_ServerShutdownFwd->PushCell(0); // TODO: Get right hltvinstance
 | ||||||
|  | 	m_ServerShutdownFwd->Execute(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #if SOURCE_ENGINE == SE_CSGO | #if SOURCE_ENGINE == SE_CSGO | ||||||
| static bool ExtractPlayerName(CUtlVector<NetMsg_SplitPlayerConnect *> &pSplitPlayerConnectVector, char *name, int maxlen) | static bool ExtractPlayerName(CUtlVector<NetMsg_SplitPlayerConnect *> &pSplitPlayerConnectVector, char *name, int maxlen) | ||||||
| { | { | ||||||
|  | |||||||
| @ -69,6 +69,9 @@ public: | |||||||
| 	void HookServer(IServer *server); | 	void HookServer(IServer *server); | ||||||
| 	void UnhookServer(IServer *server); | 	void UnhookServer(IServer *server); | ||||||
| 
 | 
 | ||||||
|  | 	void CallOnServerStart(IHLTVServer *server); | ||||||
|  | 	void CallOnServerShutdown(IHLTVServer *server); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
| 	void HookClient(IClient *client); | 	void HookClient(IClient *client); | ||||||
| 	void UnhookClient(IClient *client); | 	void UnhookClient(IClient *client); | ||||||
| @ -96,6 +99,9 @@ private: | |||||||
| 	IForward *m_SpectatorDisconnectedFwd; | 	IForward *m_SpectatorDisconnectedFwd; | ||||||
| 	IForward *m_SpectatorPutInServerFwd; | 	IForward *m_SpectatorPutInServerFwd; | ||||||
| 
 | 
 | ||||||
|  | 	IForward *m_ServerStartFwd; | ||||||
|  | 	IForward *m_ServerShutdownFwd; | ||||||
|  | 
 | ||||||
| 	bool m_bHasClientConnectOffset = false; | 	bool m_bHasClientConnectOffset = false; | ||||||
| 	bool m_bHasRejectConnectionOffset = false; | 	bool m_bHasRejectConnectionOffset = false; | ||||||
| 	bool m_bHasGetChallengeTypeOffset = false; | 	bool m_bHasGetChallengeTypeOffset = false; | ||||||
|  | |||||||
							
								
								
									
										350
									
								
								hltvserverwrapper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								hltvserverwrapper.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,350 @@ | |||||||
|  | #include "hltvserverwrapper.h" | ||||||
|  | #include "forwards.h" | ||||||
|  | 
 | ||||||
|  | void *old_host_client = nullptr; | ||||||
|  | bool g_HostClientOverridden = false; | ||||||
|  | 
 | ||||||
|  | SH_DECL_HOOK1(IClient, ExecuteStringCommand, SH_NOATTRIB, 0, bool, const char *); | ||||||
|  | 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); | ||||||
|  | // 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; | ||||||
|  | 
 | ||||||
|  | 	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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int HLTVServerWrapper::GetInstanceNumber() | ||||||
|  | { | ||||||
|  | 	return g_HLTVServers.GetInstanceNumber(m_HLTVServer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HLTVServerWrapper::Hook() | ||||||
|  | { | ||||||
|  | 	if (!m_Connected) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	g_pSTVForwards.HookServer(m_HLTVServer->GetBaseServer()); | ||||||
|  | 	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) | ||||||
|  | 		{ | ||||||
|  | 			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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HLTVServerWrapper::Unhook() | ||||||
|  | { | ||||||
|  | 	if (!m_Connected) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	g_pSTVForwards.UnhookServer(m_HLTVServer->GetBaseServer()); | ||||||
|  | 	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) | ||||||
|  | 		{ | ||||||
|  | 			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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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) | ||||||
|  | 		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::OnHLTVBotClientPrintf_Post(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); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	IClient *pClient = META_IFACEPTR(IClient); | ||||||
|  | 
 | ||||||
|  | 	void *pNetChannel = (void *)((char *)pClient + offset); | ||||||
|  | 	// 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; | ||||||
|  | 
 | ||||||
|  | 	RETURN_META(MRES_IGNORED); | ||||||
|  | } | ||||||
|  | #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 | ||||||
|  | 	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() | ||||||
|  | { | ||||||
|  | #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) | ||||||
|  | { | ||||||
|  | 	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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (hltv) | ||||||
|  | 	{ | ||||||
|  | #if SOURCE_ENGINE == SE_CSGO | ||||||
|  | 		return (IDemoRecorder *)((intptr_t)hltv + offset); | ||||||
|  | #else | ||||||
|  | 		IServer *baseServer = hltv->GetBaseServer(); | ||||||
|  | 		return (IDemoRecorder *)((intptr_t)baseServer + offset); | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		return nullptr; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HLTVServerWrapperManager::HasShutdownOffset() | ||||||
|  | { | ||||||
|  | 	return m_bHasShutdownOffset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #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; | ||||||
							
								
								
									
										95
									
								
								hltvserverwrapper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								hltvserverwrapper.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | |||||||
|  | /**
 | ||||||
|  | * 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 <http://www.gnu.org/licenses/>.
 | ||||||
|  | * | ||||||
|  | * 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 <http://www.sourcemod.net/license.php>.
 | ||||||
|  | * | ||||||
|  | * Version: $Id$ | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_HLTVSERVER_H_ | ||||||
|  | #define _INCLUDE_SOURCEMOD_EXTENSION_HLTVSERVER_H_ | ||||||
|  | 
 | ||||||
|  | #include "extension.h" | ||||||
|  | #include "amtl/am-vector.h" | ||||||
|  | #include "amtl/am-utility.h" | ||||||
|  | 
 | ||||||
|  | class HLTVServerWrapper { | ||||||
|  | public: | ||||||
|  | 	HLTVServerWrapper(IHLTVServer *hltvserver); | ||||||
|  | 	void Shutdown(bool bInformPlugins); | ||||||
|  | 
 | ||||||
|  | 	IHLTVServer *GetHLTVServer(); | ||||||
|  | 	IServer *GetBaseServer(); | ||||||
|  | 	IDemoRecorder *GetDemoRecorder(); | ||||||
|  | 	int GetInstanceNumber(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	void Hook(); | ||||||
|  | 	void Unhook(); | ||||||
|  | 
 | ||||||
|  | 	// Hooks
 | ||||||
|  | 	bool OnHLTVBotExecuteStringCommand(const char *s); | ||||||
|  | 	bool OnHLTVBotExecuteStringCommand_Post(const char *s); | ||||||
|  | 	void OnHLTVServerShutdown(); | ||||||
|  | 
 | ||||||
|  | #if SOURCE_ENGINE != SE_CSGO | ||||||
|  | 	void OnHLTVBotClientPrintf_Post(const char *buf); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	bool m_Connected = false; | ||||||
|  | 	IHLTVServer *m_HLTVServer = nullptr; | ||||||
|  | 	IDemoRecorder *m_DemoRecorder = nullptr; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class HLTVServerWrapperManager | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	void InitHooks(); | ||||||
|  | 	void ShutdownHooks(); | ||||||
|  | 	void AddServer(IHLTVServer *hltvserver); | ||||||
|  | 	void RemoveServer(IHLTVServer *hltvserver, bool bInformPlugins); | ||||||
|  | 	HLTVServerWrapper *GetWrapper(IHLTVServer *hltvserver); | ||||||
|  | 	int GetInstanceNumber(IHLTVServer *hltvserver); | ||||||
|  | 
 | ||||||
|  | 	IDemoRecorder *GetDemoRecorderPtr(IHLTVServer *hltv); | ||||||
|  | 	bool HasShutdownOffset(); | ||||||
|  | 
 | ||||||
|  | #if SOURCE_ENGINE != SE_CSGO | ||||||
|  | 	bool OnHLTVBotNetChanSendNetMsg(INetMessage &msg, bool bForceReliable, bool bVoice); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | #if SOURCE_ENGINE != SE_CSGO | ||||||
|  | 	bool m_bSendNetMsgHooked = false; | ||||||
|  | #endif | ||||||
|  | 	bool m_bHasShutdownOffset = false; | ||||||
|  | 	ke::Vector<ke::AutoPtr<HLTVServerWrapper>> m_HLTVServers; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern HLTVServerWrapperManager g_HLTVServers; | ||||||
|  | 
 | ||||||
|  | #endif // _INCLUDE_SOURCEMOD_EXTENSION_HLTVSERVER_H_
 | ||||||
							
								
								
									
										82
									
								
								natives.cpp
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								natives.cpp
									
									
									
									
									
								
							| @ -31,6 +31,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "extension.h" | #include "extension.h" | ||||||
| #include "natives.h" | #include "natives.h" | ||||||
|  | #include "hltvserverwrapper.h" | ||||||
| 
 | 
 | ||||||
| #define TICK_INTERVAL			(gpGlobals->interval_per_tick) | #define TICK_INTERVAL			(gpGlobals->interval_per_tick) | ||||||
| #define TIME_TO_TICKS( dt )		( (int)( 0.5f + (float)(dt) / TICK_INTERVAL ) ) | #define TIME_TO_TICKS( dt )		( (int)( 0.5f + (float)(dt) / TICK_INTERVAL ) ) | ||||||
| @ -94,20 +95,16 @@ static cell_t Native_GetSelectedServerInstance(IPluginContext *pContext, const c | |||||||
| 	if (hltvserver == nullptr) | 	if (hltvserver == nullptr) | ||||||
| 		return -1; | 		return -1; | ||||||
| 
 | 
 | ||||||
| #if SOURCE_ENGINE == SE_CSGO | 	return hltvserver->GetInstanceNumber(); | ||||||
| 	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
 | // native SourceTV_IsActive();
 | ||||||
| 	hltvserver = nullptr; | static cell_t Native_IsActive(IPluginContext *pContext, const cell_t *params) | ||||||
| 	return -1; | { | ||||||
| #else | 	if (hltvserver == nullptr) | ||||||
| 	// There only is one hltv server.
 |  | ||||||
| 		return 0; | 		return 0; | ||||||
| #endif | 
 | ||||||
|  | 	return hltvserver->GetBaseServer()->IsActive(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // native SourceTV_IsMasterProxy();
 | // native SourceTV_IsMasterProxy();
 | ||||||
| @ -116,7 +113,7 @@ static cell_t Native_IsMasterProxy(IPluginContext *pContext, const cell_t *param | |||||||
| 	if (hltvserver == nullptr) | 	if (hltvserver == nullptr) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	return hltvserver->IsMasterProxy(); | 	return hltvserver->GetHLTVServer()->IsMasterProxy(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // native bool:SourceTV_GetServerIP(String:ip[], maxlen);
 | // native bool:SourceTV_GetServerIP(String:ip[], maxlen);
 | ||||||
| @ -125,7 +122,7 @@ static cell_t Native_GetServerIP(IPluginContext *pContext, const cell_t *params) | |||||||
| 	if (hltvserver == nullptr) | 	if (hltvserver == nullptr) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	const netadr_t *adr = hltvserver->GetRelayAddress(); | 	const netadr_t *adr = hltvserver->GetHLTVServer()->GetRelayAddress(); | ||||||
| 
 | 
 | ||||||
| 	char buf[16]; | 	char buf[16]; | ||||||
| 	V_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", adr->ip[0], adr->ip[1], adr->ip[2], adr->ip[3]); | 	V_snprintf(buf, sizeof(buf), "%d.%d.%d.%d", adr->ip[0], adr->ip[1], adr->ip[2], adr->ip[3]); | ||||||
| @ -149,7 +146,7 @@ static cell_t Native_GetBotIndex(IPluginContext *pContext, const cell_t *params) | |||||||
| 	if (hltvserver == nullptr) | 	if (hltvserver == nullptr) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	return hltvserver->GetHLTVSlot() + 1; | 	return hltvserver->GetHLTVServer()->GetHLTVSlot() + 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // native bool:SourceTV_GetLocalStats(&proxies, &slots, &specs);
 | // native bool:SourceTV_GetLocalStats(&proxies, &slots, &specs);
 | ||||||
| @ -159,7 +156,7 @@ static cell_t Native_GetLocalStats(IPluginContext *pContext, const cell_t *param | |||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	int proxies, slots, specs; | 	int proxies, slots, specs; | ||||||
| 	hltvserver->GetLocalStats(proxies, slots, specs); | 	hltvserver->GetHLTVServer()->GetLocalStats(proxies, slots, specs); | ||||||
| 
 | 
 | ||||||
| 	cell_t *plProxies, *plSlots, *plSpecs; | 	cell_t *plProxies, *plSlots, *plSpecs; | ||||||
| 	pContext->LocalToPhysAddr(params[1], &plProxies); | 	pContext->LocalToPhysAddr(params[1], &plProxies); | ||||||
| @ -179,7 +176,7 @@ static cell_t Native_GetGlobalStats(IPluginContext *pContext, const cell_t *para | |||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	int proxies, slots, specs; | 	int proxies, slots, specs; | ||||||
| 	hltvserver->GetGlobalStats(proxies, slots, specs); | 	hltvserver->GetHLTVServer()->GetGlobalStats(proxies, slots, specs); | ||||||
| 
 | 
 | ||||||
| 	cell_t *plProxies, *plSlots, *plSpecs; | 	cell_t *plProxies, *plSlots, *plSpecs; | ||||||
| 	pContext->LocalToPhysAddr(params[1], &plProxies); | 	pContext->LocalToPhysAddr(params[1], &plProxies); | ||||||
| @ -275,9 +272,9 @@ static cell_t Native_BroadcastScreenMessage(IPluginContext *pContext, const cell | |||||||
| 	int ret = 1; | 	int ret = 1; | ||||||
| 	bool bLocalOnly = params[1] != 0; | 	bool bLocalOnly = params[1] != 0; | ||||||
| 	if (bLocalOnly) | 	if (bLocalOnly) | ||||||
| 		hltvserver->BroadcastEvent(msg); | 		hltvserver->GetHLTVServer()->BroadcastEvent(msg); | ||||||
| 	else | 	else | ||||||
| 		ret = BroadcastEventLocal(hltvserver, msg, false); | 		ret = BroadcastEventLocal(hltvserver->GetHLTVServer(), msg, false); | ||||||
| 
 | 
 | ||||||
| 	gameevents->FreeEvent(msg); | 	gameevents->FreeEvent(msg); | ||||||
| 
 | 
 | ||||||
| @ -331,9 +328,9 @@ static cell_t Native_BroadcastChatMessage(IPluginContext *pContext, const cell_t | |||||||
| 	int ret = 1; | 	int ret = 1; | ||||||
| 	bool bLocalOnly = params[1] != 0; | 	bool bLocalOnly = params[1] != 0; | ||||||
| 	if (bLocalOnly) | 	if (bLocalOnly) | ||||||
| 		hltvserver->BroadcastEvent(msg); | 		hltvserver->GetHLTVServer()->BroadcastEvent(msg); | ||||||
| 	else | 	else | ||||||
| 		ret = BroadcastEventLocal(hltvserver, msg, false); | 		ret = BroadcastEventLocal(hltvserver->GetHLTVServer(), msg, false); | ||||||
| 
 | 
 | ||||||
| 	gameevents->FreeEvent(msg); | 	gameevents->FreeEvent(msg); | ||||||
| 
 | 
 | ||||||
| @ -395,7 +392,7 @@ static cell_t Native_ForceFixedCameraShot(IPluginContext *pContext, const cell_t | |||||||
| 	shot->SetInt("target", params[3] ? gamehelpers->ReferenceToIndex(params[3]) : 0); | 	shot->SetInt("target", params[3] ? gamehelpers->ReferenceToIndex(params[3]) : 0); | ||||||
| 	shot->SetFloat("fov", sp_ctof(params[4])); | 	shot->SetFloat("fov", sp_ctof(params[4])); | ||||||
| 
 | 
 | ||||||
| 	hltvserver->BroadcastEvent(shot); | 	hltvserver->GetHLTVServer()->BroadcastEvent(shot); | ||||||
| 	gameevents->FreeEvent(shot); | 	gameevents->FreeEvent(shot); | ||||||
| 
 | 
 | ||||||
| 	// Prevent auto director from changing shots until we allow it to again.
 | 	// Prevent auto director from changing shots until we allow it to again.
 | ||||||
| @ -437,7 +434,7 @@ static cell_t Native_ForceChaseCameraShot(IPluginContext *pContext, const cell_t | |||||||
| 	// Update director state
 | 	// Update director state
 | ||||||
| 	g_HLTVDirectorWrapper.SetPVSEntity(gamehelpers->ReferenceToIndex(params[1])); | 	g_HLTVDirectorWrapper.SetPVSEntity(gamehelpers->ReferenceToIndex(params[1])); | ||||||
| 
 | 
 | ||||||
| 	hltvserver->BroadcastEvent(shot); | 	hltvserver->GetHLTVServer()->BroadcastEvent(shot); | ||||||
| 	gameevents->FreeEvent(shot); | 	gameevents->FreeEvent(shot); | ||||||
| 
 | 
 | ||||||
| 	// Prevent auto director from changing shots until we allow it to again.
 | 	// Prevent auto director from changing shots until we allow it to again.
 | ||||||
| @ -449,10 +446,7 @@ static cell_t Native_ForceChaseCameraShot(IPluginContext *pContext, const cell_t | |||||||
| // native bool:SourceTV_IsRecording();
 | // native bool:SourceTV_IsRecording();
 | ||||||
| static cell_t Native_IsRecording(IPluginContext *pContext, const cell_t *params) | static cell_t Native_IsRecording(IPluginContext *pContext, const cell_t *params) | ||||||
| { | { | ||||||
| 	if (demorecorder == nullptr) | 	return hltvserver->GetDemoRecorder()->IsRecording(); | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	return demorecorder->IsRecording(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Checks in COM_IsValidPath in the engine
 | // Checks in COM_IsValidPath in the engine
 | ||||||
| @ -464,7 +458,7 @@ static bool IsValidPath(const char *path) | |||||||
| // native bool:SourceTV_StartRecording(const String:sFilename[]);
 | // native bool:SourceTV_StartRecording(const String:sFilename[]);
 | ||||||
| static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *params) | static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *params) | ||||||
| { | { | ||||||
| 	if (hltvserver == nullptr || demorecorder == nullptr) | 	if (hltvserver == nullptr) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	// SourceTV is not active.
 | 	// SourceTV is not active.
 | ||||||
| @ -472,11 +466,11 @@ static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *para | |||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	// Only SourceTV Master can record demos instantly
 | 	// Only SourceTV Master can record demos instantly
 | ||||||
| 	if (!hltvserver->IsMasterProxy()) | 	if (!hltvserver->GetHLTVServer()->IsMasterProxy()) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	// already recording
 | 	// already recording
 | ||||||
| 	if (demorecorder->IsRecording()) | 	if (hltvserver->GetDemoRecorder()->IsRecording()) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	char *pFile; | 	char *pFile; | ||||||
| @ -512,7 +506,7 @@ static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *para | |||||||
| 	} | 	} | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	demorecorder->StartRecording(pPath, false); | 	hltvserver->GetDemoRecorder()->StartRecording(pPath, false); | ||||||
| 
 | 
 | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
| @ -520,17 +514,14 @@ static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *para | |||||||
| // native bool:SourceTV_StopRecording();
 | // native bool:SourceTV_StopRecording();
 | ||||||
| static cell_t Native_StopRecording(IPluginContext *pContext, const cell_t *params) | static cell_t Native_StopRecording(IPluginContext *pContext, const cell_t *params) | ||||||
| { | { | ||||||
| 	if (demorecorder == nullptr) | 	if (!hltvserver->GetDemoRecorder()->IsRecording()) | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	if (!demorecorder->IsRecording()) |  | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| #if SOURCE_ENGINE == SE_CSGO | #if SOURCE_ENGINE == SE_CSGO | ||||||
| 	hltvserver->StopRecording(NULL); | 	hltvserver->GetDemoRecorder()->StopRecording(NULL); | ||||||
| 	// TODO: Stop recording on all other active hltvservers (tv_stoprecord in csgo does this)
 | 	// TODO: Stop recording on all other active hltvservers (tv_stoprecord in csgo does this)
 | ||||||
| #else | #else | ||||||
| 	demorecorder->StopRecording(); | 	hltvserver->GetDemoRecorder()->StopRecording(); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| 	return 1; | 	return 1; | ||||||
| @ -539,13 +530,10 @@ static cell_t Native_StopRecording(IPluginContext *pContext, const cell_t *param | |||||||
| // native bool:SourceTV_GetDemoFileName(String:sFilename[], maxlen);
 | // native bool:SourceTV_GetDemoFileName(String:sFilename[], maxlen);
 | ||||||
| static cell_t Native_GetDemoFileName(IPluginContext *pContext, const cell_t *params) | static cell_t Native_GetDemoFileName(IPluginContext *pContext, const cell_t *params) | ||||||
| { | { | ||||||
| 	if (demorecorder == nullptr) | 	if (!hltvserver->GetDemoRecorder()->IsRecording()) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	if (!demorecorder->IsRecording()) | 	char *pDemoFile = (char *)hltvserver->GetDemoRecorder()->GetDemoFile(); | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	char *pDemoFile = (char *)demorecorder->GetDemoFile(); |  | ||||||
| 	if (!pDemoFile) | 	if (!pDemoFile) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| @ -557,13 +545,10 @@ static cell_t Native_GetDemoFileName(IPluginContext *pContext, const cell_t *par | |||||||
| // native SourceTV_GetRecordingTick();
 | // native SourceTV_GetRecordingTick();
 | ||||||
| static cell_t Native_GetRecordingTick(IPluginContext *pContext, const cell_t *params) | static cell_t Native_GetRecordingTick(IPluginContext *pContext, const cell_t *params) | ||||||
| { | { | ||||||
| 	if (demorecorder == nullptr) | 	if (!hltvserver->GetDemoRecorder()->IsRecording()) | ||||||
| 		return -1; | 		return -1; | ||||||
| 
 | 
 | ||||||
| 	if (!demorecorder->IsRecording()) | 	return hltvserver->GetDemoRecorder()->GetRecordingTick(); | ||||||
| 		return -1; |  | ||||||
| 
 |  | ||||||
| 	return demorecorder->GetRecordingTick(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // native bool:SourceTV_PrintToDemoConsole(const String:format[], any:...);
 | // native bool:SourceTV_PrintToDemoConsole(const String:format[], any:...);
 | ||||||
| @ -574,7 +559,7 @@ static cell_t Native_PrintToDemoConsole(IPluginContext *pContext, const cell_t * | |||||||
| 
 | 
 | ||||||
| 	if (!iserver) | 	if (!iserver) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	IClient *pClient = iserver->GetClient(hltvserver->GetHLTVSlot()); | 	IClient *pClient = iserver->GetClient(hltvserver->GetHLTVServer()->GetHLTVSlot()); | ||||||
| 	if (!pClient) | 	if (!pClient) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| @ -742,6 +727,7 @@ const sp_nativeinfo_t sourcetv_natives[] = | |||||||
| 	{ "SourceTV_GetServerInstanceCount", Native_GetServerInstanceCount }, | 	{ "SourceTV_GetServerInstanceCount", Native_GetServerInstanceCount }, | ||||||
| 	{ "SourceTV_SelectServerInstance", Native_SelectServerInstance }, | 	{ "SourceTV_SelectServerInstance", Native_SelectServerInstance }, | ||||||
| 	{ "SourceTV_GetSelectedServerInstance", Native_GetSelectedServerInstance }, | 	{ "SourceTV_GetSelectedServerInstance", Native_GetSelectedServerInstance }, | ||||||
|  | 	{ "SourceTV_IsActive", Native_IsActive }, | ||||||
| 	{ "SourceTV_IsMasterProxy", Native_IsMasterProxy }, | 	{ "SourceTV_IsMasterProxy", Native_IsMasterProxy }, | ||||||
| 	{ "SourceTV_GetServerIP", Native_GetServerIP }, | 	{ "SourceTV_GetServerIP", Native_GetServerIP }, | ||||||
| 	{ "SourceTV_GetServerPort", Native_GetServerPort }, | 	{ "SourceTV_GetServerPort", Native_GetServerPort }, | ||||||
|  | |||||||
| @ -57,6 +57,16 @@ public bool:SourceTV_OnSpectatorPreConnect(const String:name[], String:password[ | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | public SourceTV_OnServerStart(instance) | ||||||
|  | { | ||||||
|  | 	PrintToServer("SourceTV instance %d started.", instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | public SourceTV_OnServerShutdown(instance) | ||||||
|  | { | ||||||
|  | 	PrintToServer("SourceTV instance %d shutdown.", instance); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| public SourceTV_OnSpectatorConnected(client) | public SourceTV_OnSpectatorConnected(client) | ||||||
| { | { | ||||||
| 	PrintToServer("SourceTV client %d connected. (isconnected %d)", client, SourceTV_IsClientConnected(client)); | 	PrintToServer("SourceTV client %d connected. (isconnected %d)", client, SourceTV_IsClientConnected(client)); | ||||||
|  | |||||||
| @ -63,6 +63,12 @@ | |||||||
| 				"linux"	"65" | 				"linux"	"65" | ||||||
| 			} | 			} | ||||||
| 			 | 			 | ||||||
|  | 			"CHLTVServer::Shutdown" | ||||||
|  | 			{ | ||||||
|  | 				"windows"	"45" | ||||||
|  | 				"linux"	"46" | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
| 			"CHLTVDirector::m_iPVSEntity" | 			"CHLTVDirector::m_iPVSEntity" | ||||||
| 			{ | 			{ | ||||||
| 				"windows"	"32" | 				"windows"	"32" | ||||||
| @ -182,6 +188,12 @@ | |||||||
| 				"linux"	"56" | 				"linux"	"56" | ||||||
| 			} | 			} | ||||||
| 			 | 			 | ||||||
|  | 			"CHLTVServer::Shutdown" | ||||||
|  | 			{ | ||||||
|  | 				"windows"	"41" | ||||||
|  | 				"linux"	"42" | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
| 			"CHLTVDirector::m_iPVSEntity" | 			"CHLTVDirector::m_iPVSEntity" | ||||||
| 			{ | 			{ | ||||||
| 				"windows"	"16" | 				"windows"	"16" | ||||||
|  | |||||||
| @ -35,12 +35,35 @@ native SourceTV_SelectServerInstance(instance); | |||||||
|  */ |  */ | ||||||
| native SourceTV_GetSelectedServerInstance(); | native SourceTV_GetSelectedServerInstance(); | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Called when a SourceTV is initialized. | ||||||
|  |  * | ||||||
|  |  * @param instance	The SourceTV instance number. | ||||||
|  |  * @noreturn | ||||||
|  |  */ | ||||||
|  | forward SourceTV_OnServerStart(instance); | ||||||
|  | 
 | ||||||
|  |  /** | ||||||
|  |   * Called when a SourceTV server instance is shutdown. | ||||||
|  |   * | ||||||
|  |   * @param instance	The SourceTV instance number. | ||||||
|  |   * @noreturn | ||||||
|  |   */ | ||||||
|  | forward SourceTV_OnServerShutdown(instance); | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Returns whether this SourceTV instance is currently broadcasting. | ||||||
|  |  * | ||||||
|  |  * @return	True if SourceTV instance is broadcasting, false otherwise. | ||||||
|  |  */ | ||||||
|  | native bool:SourceTV_IsActive(); | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Returns whether this SourceTV instance is a master proxy or relay. |  * Returns whether this SourceTV instance is a master proxy or relay. | ||||||
|  * |  * | ||||||
|  * @return	True if SourceTV instance is master proxy, false otherwise. |  * @return	True if SourceTV instance is master proxy, false otherwise. | ||||||
|  */ |  */ | ||||||
| native SourceTV_IsMasterProxy(); | native bool:SourceTV_IsMasterProxy(); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Get the local ip of the SourceTV server. |  * Get the local ip of the SourceTV server. | ||||||
| @ -427,7 +450,10 @@ public __ext_stvmngr_SetNTVOptional() | |||||||
| 	MarkNativeAsOptional("SourceTV_GetServerInstanceCount"); | 	MarkNativeAsOptional("SourceTV_GetServerInstanceCount"); | ||||||
| 	MarkNativeAsOptional("SourceTV_SelectServerInstance"); | 	MarkNativeAsOptional("SourceTV_SelectServerInstance"); | ||||||
| 	MarkNativeAsOptional("SourceTV_GetSelectedServerInstance"); | 	MarkNativeAsOptional("SourceTV_GetSelectedServerInstance"); | ||||||
|  | 	MarkNativeAsOptional("SourceTV_IsActive"); | ||||||
| 	MarkNativeAsOptional("SourceTV_IsMasterProxy"); | 	MarkNativeAsOptional("SourceTV_IsMasterProxy"); | ||||||
|  | 	MarkNativeAsOptional("SourceTV_GetServerIP"); | ||||||
|  | 	MarkNativeAsOptional("SourceTV_GetServerPort"); | ||||||
| 	MarkNativeAsOptional("SourceTV_GetBotIndex"); | 	MarkNativeAsOptional("SourceTV_GetBotIndex"); | ||||||
| 	MarkNativeAsOptional("SourceTV_GetLocalStats"); | 	MarkNativeAsOptional("SourceTV_GetLocalStats"); | ||||||
| 	MarkNativeAsOptional("SourceTV_GetGlobalStats"); | 	MarkNativeAsOptional("SourceTV_GetGlobalStats"); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user