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 "hltvserverwrapper.h"
|
||||
#include "forwards.h"
|
||||
#include "natives.h"
|
||||
|
||||
IHLTVDirector *hltvdirector = nullptr;
|
||||
IHLTVServer *hltvserver = nullptr;
|
||||
IDemoRecorder *demorecorder = nullptr;
|
||||
void *host_client = nullptr;
|
||||
void *old_host_client = nullptr;
|
||||
bool g_HostClientOverridden = false;
|
||||
HLTVServerWrapper *hltvserver = nullptr;
|
||||
|
||||
IGameEventManager2 *gameevents = 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 *);
|
||||
#else
|
||||
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
|
||||
|
||||
SH_DECL_HOOK1(IClient, ExecuteStringCommand, SH_NOATTRIB, 0, bool, const char *);
|
||||
|
||||
/**
|
||||
* @file extension.cpp
|
||||
* @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.");
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE != SE_CSGO
|
||||
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
|
||||
g_HLTVServers.InitHooks();
|
||||
|
||||
sharesys->AddNatives(myself, sourcetv_natives);
|
||||
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.");
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
if (hltvdirector->GetHLTVServerCount() > 0)
|
||||
SelectSourceTVServer(hltvdirector->GetHLTVServer(0));
|
||||
|
||||
// Hook all the exisiting servers.
|
||||
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
|
||||
if (hltvdirector->GetHLTVServer())
|
||||
{
|
||||
g_HLTVServers.AddServer(hltvdirector->GetHLTVServer());
|
||||
SelectSourceTVServer(hltvdirector->GetHLTVServer());
|
||||
HookSourceTVServer(hltvdirector->GetHLTVServer());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -204,27 +165,23 @@ void SourceTVManager::SDK_OnUnload()
|
||||
SH_REMOVE_HOOK(IHLTVDirector, RemoveHLTVServer, hltvdirector, SH_MEMBER(this, &SourceTVManager::OnRemoveHLTVServer_Post), true);
|
||||
#else
|
||||
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
|
||||
|
||||
g_HLTVServers.ShutdownHooks();
|
||||
|
||||
gameconfs->CloseGameConfigFile(g_pGameConf);
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
// Unhook all the existing servers.
|
||||
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
|
||||
// Unhook the server
|
||||
if (hltvdirector->GetHLTVServer())
|
||||
UnhookSourceTVServer(hltvdirector->GetHLTVServer());
|
||||
g_HLTVServers.RemoveServer(hltvdirector->GetHLTVServer(), false);
|
||||
#endif
|
||||
g_pSTVForwards.Shutdown();
|
||||
}
|
||||
@ -237,113 +194,43 @@ bool SourceTVManager::QueryRunning(char *error, size_t maxlength)
|
||||
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)
|
||||
{
|
||||
// Select the new server.
|
||||
hltvserver = 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;
|
||||
}
|
||||
hltvserver = g_HLTVServers.GetWrapper(hltv);
|
||||
}
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
void SourceTVManager::OnAddHLTVServer_Post(IHLTVServer *hltv)
|
||||
{
|
||||
HookSourceTVServer(hltv);
|
||||
g_HLTVServers.AddServer(hltv);
|
||||
|
||||
// We already selected some SourceTV server. Keep it.
|
||||
if (hltvserver != nullptr)
|
||||
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);
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
|
||||
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 :(
|
||||
if (hltvserver == hltv)
|
||||
if (hltvserver == wrapper)
|
||||
{
|
||||
// Is there another one available? Try to keep us operable.
|
||||
if (hltvdirector->GetHLTVServerCount() > 0)
|
||||
{
|
||||
SelectSourceTVServer(hltvdirector->GetHLTVServer(0));
|
||||
HookSourceTVServer(hltvserver);
|
||||
}
|
||||
// No sourcetv active.
|
||||
else
|
||||
@ -354,45 +241,6 @@ void SourceTVManager::OnRemoveHLTVServer_Post(IHLTVServer *hltv)
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
#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)
|
||||
{
|
||||
// Server shut down?
|
||||
@ -402,53 +250,15 @@ void SourceTVManager::OnSetHLTVServer_Post(IHLTVServer *hltv)
|
||||
if (!hltvserver)
|
||||
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
|
||||
{
|
||||
HookSourceTVServer(hltv);
|
||||
g_HLTVServers.AddServer(hltv);
|
||||
}
|
||||
SelectSourceTVServer(hltv);
|
||||
RETURN_META(MRES_IGNORED);
|
||||
}
|
||||
#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"
|
||||
|
||||
class INetMessage;
|
||||
class HLTVServerWrapper;
|
||||
|
||||
extern ConVar tv_force_steamauth;
|
||||
|
||||
@ -135,7 +136,6 @@ public: // IConCommandBaseAccessor
|
||||
|
||||
public:
|
||||
void SelectSourceTVServer(IHLTVServer *hltv);
|
||||
IDemoRecorder *GetDemoRecorderPtr(IHLTVServer *hltvserver);
|
||||
|
||||
private:
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
@ -143,16 +143,7 @@ private:
|
||||
void OnRemoveHLTVServer_Post(IHLTVServer *hltv);
|
||||
#else
|
||||
void OnSetHLTVServer_Post(IHLTVServer *hltv);
|
||||
|
||||
bool OnHLTVBotNetChanSendNetMsg(INetMessage &msg, bool bForceReliable, bool bVoice);
|
||||
void OnHLTVBotClientPrintf_Post(const char *buf);
|
||||
#endif
|
||||
bool OnHLTVBotExecuteStringCommand(const char *s);
|
||||
bool OnHLTVBotExecuteStringCommand_Post(const char *s);
|
||||
|
||||
private:
|
||||
void HookSourceTVServer(IHLTVServer *hltv);
|
||||
void UnhookSourceTVServer(IHLTVServer *hltv);
|
||||
};
|
||||
|
||||
/* Interfaces from SourceMod */
|
||||
@ -167,7 +158,7 @@ extern IGameEventManager2 *gameevents;
|
||||
extern ICvar *icvar;
|
||||
|
||||
extern IHLTVDirector *hltvdirector;
|
||||
extern IHLTVServer *hltvserver;
|
||||
extern IDemoRecorder *demorecorder;
|
||||
extern HLTVServerWrapper *hltvserver;
|
||||
extern void *host_client;
|
||||
|
||||
#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_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_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()
|
||||
@ -115,6 +118,9 @@ void CForwardManager::Shutdown()
|
||||
forwards->ReleaseForward(m_SpectatorDisconnectFwd);
|
||||
forwards->ReleaseForward(m_SpectatorDisconnectedFwd);
|
||||
forwards->ReleaseForward(m_SpectatorPutInServerFwd);
|
||||
|
||||
forwards->ReleaseForward(m_ServerStartFwd);
|
||||
forwards->ReleaseForward(m_ServerShutdownFwd);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
static bool ExtractPlayerName(CUtlVector<NetMsg_SplitPlayerConnect *> &pSplitPlayerConnectVector, char *name, int maxlen)
|
||||
{
|
||||
|
@ -69,6 +69,9 @@ public:
|
||||
void HookServer(IServer *server);
|
||||
void UnhookServer(IServer *server);
|
||||
|
||||
void CallOnServerStart(IHLTVServer *server);
|
||||
void CallOnServerShutdown(IHLTVServer *server);
|
||||
|
||||
private:
|
||||
void HookClient(IClient *client);
|
||||
void UnhookClient(IClient *client);
|
||||
@ -96,6 +99,9 @@ private:
|
||||
IForward *m_SpectatorDisconnectedFwd;
|
||||
IForward *m_SpectatorPutInServerFwd;
|
||||
|
||||
IForward *m_ServerStartFwd;
|
||||
IForward *m_ServerShutdownFwd;
|
||||
|
||||
bool m_bHasClientConnectOffset = false;
|
||||
bool m_bHasRejectConnectionOffset = 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_
|
84
natives.cpp
84
natives.cpp
@ -31,6 +31,7 @@
|
||||
|
||||
#include "extension.h"
|
||||
#include "natives.h"
|
||||
#include "hltvserverwrapper.h"
|
||||
|
||||
#define TICK_INTERVAL (gpGlobals->interval_per_tick)
|
||||
#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)
|
||||
return -1;
|
||||
|
||||
#if SOURCE_ENGINE == SE_CSGO
|
||||
for (int i = 0; i < hltvdirector->GetHLTVServerCount(); i++)
|
||||
{
|
||||
if (hltvserver == hltvdirector->GetHLTVServer(i))
|
||||
return i;
|
||||
}
|
||||
return hltvserver->GetInstanceNumber();
|
||||
}
|
||||
|
||||
// We should have found it in the above loop :S
|
||||
hltvserver = nullptr;
|
||||
return -1;
|
||||
#else
|
||||
// There only is one hltv server.
|
||||
return 0;
|
||||
#endif
|
||||
// native SourceTV_IsActive();
|
||||
static cell_t Native_IsActive(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (hltvserver == nullptr)
|
||||
return 0;
|
||||
|
||||
return hltvserver->GetBaseServer()->IsActive();
|
||||
}
|
||||
|
||||
// native SourceTV_IsMasterProxy();
|
||||
@ -116,7 +113,7 @@ static cell_t Native_IsMasterProxy(IPluginContext *pContext, const cell_t *param
|
||||
if (hltvserver == nullptr)
|
||||
return 0;
|
||||
|
||||
return hltvserver->IsMasterProxy();
|
||||
return hltvserver->GetHLTVServer()->IsMasterProxy();
|
||||
}
|
||||
|
||||
// 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)
|
||||
return 0;
|
||||
|
||||
const netadr_t *adr = hltvserver->GetRelayAddress();
|
||||
const netadr_t *adr = hltvserver->GetHLTVServer()->GetRelayAddress();
|
||||
|
||||
char buf[16];
|
||||
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)
|
||||
return 0;
|
||||
|
||||
return hltvserver->GetHLTVSlot() + 1;
|
||||
return hltvserver->GetHLTVServer()->GetHLTVSlot() + 1;
|
||||
}
|
||||
|
||||
// native bool:SourceTV_GetLocalStats(&proxies, &slots, &specs);
|
||||
@ -159,7 +156,7 @@ static cell_t Native_GetLocalStats(IPluginContext *pContext, const cell_t *param
|
||||
return 0;
|
||||
|
||||
int proxies, slots, specs;
|
||||
hltvserver->GetLocalStats(proxies, slots, specs);
|
||||
hltvserver->GetHLTVServer()->GetLocalStats(proxies, slots, specs);
|
||||
|
||||
cell_t *plProxies, *plSlots, *plSpecs;
|
||||
pContext->LocalToPhysAddr(params[1], &plProxies);
|
||||
@ -179,7 +176,7 @@ static cell_t Native_GetGlobalStats(IPluginContext *pContext, const cell_t *para
|
||||
return 0;
|
||||
|
||||
int proxies, slots, specs;
|
||||
hltvserver->GetGlobalStats(proxies, slots, specs);
|
||||
hltvserver->GetHLTVServer()->GetGlobalStats(proxies, slots, specs);
|
||||
|
||||
cell_t *plProxies, *plSlots, *plSpecs;
|
||||
pContext->LocalToPhysAddr(params[1], &plProxies);
|
||||
@ -275,9 +272,9 @@ static cell_t Native_BroadcastScreenMessage(IPluginContext *pContext, const cell
|
||||
int ret = 1;
|
||||
bool bLocalOnly = params[1] != 0;
|
||||
if (bLocalOnly)
|
||||
hltvserver->BroadcastEvent(msg);
|
||||
hltvserver->GetHLTVServer()->BroadcastEvent(msg);
|
||||
else
|
||||
ret = BroadcastEventLocal(hltvserver, msg, false);
|
||||
ret = BroadcastEventLocal(hltvserver->GetHLTVServer(), msg, false);
|
||||
|
||||
gameevents->FreeEvent(msg);
|
||||
|
||||
@ -331,9 +328,9 @@ static cell_t Native_BroadcastChatMessage(IPluginContext *pContext, const cell_t
|
||||
int ret = 1;
|
||||
bool bLocalOnly = params[1] != 0;
|
||||
if (bLocalOnly)
|
||||
hltvserver->BroadcastEvent(msg);
|
||||
hltvserver->GetHLTVServer()->BroadcastEvent(msg);
|
||||
else
|
||||
ret = BroadcastEventLocal(hltvserver, msg, false);
|
||||
ret = BroadcastEventLocal(hltvserver->GetHLTVServer(), msg, false);
|
||||
|
||||
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->SetFloat("fov", sp_ctof(params[4]));
|
||||
|
||||
hltvserver->BroadcastEvent(shot);
|
||||
hltvserver->GetHLTVServer()->BroadcastEvent(shot);
|
||||
gameevents->FreeEvent(shot);
|
||||
|
||||
// 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
|
||||
g_HLTVDirectorWrapper.SetPVSEntity(gamehelpers->ReferenceToIndex(params[1]));
|
||||
|
||||
hltvserver->BroadcastEvent(shot);
|
||||
hltvserver->GetHLTVServer()->BroadcastEvent(shot);
|
||||
gameevents->FreeEvent(shot);
|
||||
|
||||
// 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();
|
||||
static cell_t Native_IsRecording(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (demorecorder == nullptr)
|
||||
return 0;
|
||||
|
||||
return demorecorder->IsRecording();
|
||||
return hltvserver->GetDemoRecorder()->IsRecording();
|
||||
}
|
||||
|
||||
// Checks in COM_IsValidPath in the engine
|
||||
@ -464,7 +458,7 @@ static bool IsValidPath(const char *path)
|
||||
// native bool:SourceTV_StartRecording(const String:sFilename[]);
|
||||
static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (hltvserver == nullptr || demorecorder == nullptr)
|
||||
if (hltvserver == nullptr)
|
||||
return 0;
|
||||
|
||||
// SourceTV is not active.
|
||||
@ -472,11 +466,11 @@ static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *para
|
||||
return 0;
|
||||
|
||||
// Only SourceTV Master can record demos instantly
|
||||
if (!hltvserver->IsMasterProxy())
|
||||
if (!hltvserver->GetHLTVServer()->IsMasterProxy())
|
||||
return 0;
|
||||
|
||||
// already recording
|
||||
if (demorecorder->IsRecording())
|
||||
if (hltvserver->GetDemoRecorder()->IsRecording())
|
||||
return 0;
|
||||
|
||||
char *pFile;
|
||||
@ -512,7 +506,7 @@ static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *para
|
||||
}
|
||||
#endif
|
||||
|
||||
demorecorder->StartRecording(pPath, false);
|
||||
hltvserver->GetDemoRecorder()->StartRecording(pPath, false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -520,17 +514,14 @@ static cell_t Native_StartRecording(IPluginContext *pContext, const cell_t *para
|
||||
// native bool:SourceTV_StopRecording();
|
||||
static cell_t Native_StopRecording(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (demorecorder == nullptr)
|
||||
return 0;
|
||||
|
||||
if (!demorecorder->IsRecording())
|
||||
if (!hltvserver->GetDemoRecorder()->IsRecording())
|
||||
return 0;
|
||||
|
||||
#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)
|
||||
#else
|
||||
demorecorder->StopRecording();
|
||||
hltvserver->GetDemoRecorder()->StopRecording();
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
@ -539,13 +530,10 @@ static cell_t Native_StopRecording(IPluginContext *pContext, const cell_t *param
|
||||
// native bool:SourceTV_GetDemoFileName(String:sFilename[], maxlen);
|
||||
static cell_t Native_GetDemoFileName(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (demorecorder == nullptr)
|
||||
if (!hltvserver->GetDemoRecorder()->IsRecording())
|
||||
return 0;
|
||||
|
||||
if (!demorecorder->IsRecording())
|
||||
return 0;
|
||||
|
||||
char *pDemoFile = (char *)demorecorder->GetDemoFile();
|
||||
char *pDemoFile = (char *)hltvserver->GetDemoRecorder()->GetDemoFile();
|
||||
if (!pDemoFile)
|
||||
return 0;
|
||||
|
||||
@ -557,13 +545,10 @@ static cell_t Native_GetDemoFileName(IPluginContext *pContext, const cell_t *par
|
||||
// native SourceTV_GetRecordingTick();
|
||||
static cell_t Native_GetRecordingTick(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
if (demorecorder == nullptr)
|
||||
if (!hltvserver->GetDemoRecorder()->IsRecording())
|
||||
return -1;
|
||||
|
||||
if (!demorecorder->IsRecording())
|
||||
return -1;
|
||||
|
||||
return demorecorder->GetRecordingTick();
|
||||
return hltvserver->GetDemoRecorder()->GetRecordingTick();
|
||||
}
|
||||
|
||||
// native bool:SourceTV_PrintToDemoConsole(const String:format[], any:...);
|
||||
@ -574,7 +559,7 @@ static cell_t Native_PrintToDemoConsole(IPluginContext *pContext, const cell_t *
|
||||
|
||||
if (!iserver)
|
||||
return 0;
|
||||
IClient *pClient = iserver->GetClient(hltvserver->GetHLTVSlot());
|
||||
IClient *pClient = iserver->GetClient(hltvserver->GetHLTVServer()->GetHLTVSlot());
|
||||
if (!pClient)
|
||||
return 0;
|
||||
|
||||
@ -742,6 +727,7 @@ const sp_nativeinfo_t sourcetv_natives[] =
|
||||
{ "SourceTV_GetServerInstanceCount", Native_GetServerInstanceCount },
|
||||
{ "SourceTV_SelectServerInstance", Native_SelectServerInstance },
|
||||
{ "SourceTV_GetSelectedServerInstance", Native_GetSelectedServerInstance },
|
||||
{ "SourceTV_IsActive", Native_IsActive },
|
||||
{ "SourceTV_IsMasterProxy", Native_IsMasterProxy },
|
||||
{ "SourceTV_GetServerIP", Native_GetServerIP },
|
||||
{ "SourceTV_GetServerPort", Native_GetServerPort },
|
||||
|
@ -57,6 +57,16 @@ public bool:SourceTV_OnSpectatorPreConnect(const String:name[], String:password[
|
||||
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)
|
||||
{
|
||||
PrintToServer("SourceTV client %d connected. (isconnected %d)", client, SourceTV_IsClientConnected(client));
|
||||
|
@ -63,6 +63,12 @@
|
||||
"linux" "65"
|
||||
}
|
||||
|
||||
"CHLTVServer::Shutdown"
|
||||
{
|
||||
"windows" "45"
|
||||
"linux" "46"
|
||||
}
|
||||
|
||||
"CHLTVDirector::m_iPVSEntity"
|
||||
{
|
||||
"windows" "32"
|
||||
@ -182,6 +188,12 @@
|
||||
"linux" "56"
|
||||
}
|
||||
|
||||
"CHLTVServer::Shutdown"
|
||||
{
|
||||
"windows" "41"
|
||||
"linux" "42"
|
||||
}
|
||||
|
||||
"CHLTVDirector::m_iPVSEntity"
|
||||
{
|
||||
"windows" "16"
|
||||
|
@ -35,12 +35,35 @@ native SourceTV_SelectServerInstance(instance);
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
@ -427,7 +450,10 @@ public __ext_stvmngr_SetNTVOptional()
|
||||
MarkNativeAsOptional("SourceTV_GetServerInstanceCount");
|
||||
MarkNativeAsOptional("SourceTV_SelectServerInstance");
|
||||
MarkNativeAsOptional("SourceTV_GetSelectedServerInstance");
|
||||
MarkNativeAsOptional("SourceTV_IsActive");
|
||||
MarkNativeAsOptional("SourceTV_IsMasterProxy");
|
||||
MarkNativeAsOptional("SourceTV_GetServerIP");
|
||||
MarkNativeAsOptional("SourceTV_GetServerPort");
|
||||
MarkNativeAsOptional("SourceTV_GetBotIndex");
|
||||
MarkNativeAsOptional("SourceTV_GetLocalStats");
|
||||
MarkNativeAsOptional("SourceTV_GetGlobalStats");
|
||||
|
Loading…
Reference in New Issue
Block a user