diff --git a/AMBuilder b/AMBuilder
index 9e5725d..37120f6 100644
--- a/AMBuilder
+++ b/AMBuilder
@@ -6,11 +6,14 @@ projectName = 'sourcetvmanager'
# smsdk_ext.cpp will be automatically added later
sourceFiles = [
'extension.cpp',
+ 'commonhooks.cpp',
'natives.cpp',
'forwards.cpp',
'hltvserverwrapper.cpp',
'hltvdirectorwrapper.cpp',
- 'hltvclientwrapper.cpp'
+ 'hltvclientwrapper.cpp',
+ os.path.join(Extension.sm_root, 'public', 'CDetour', 'detours.cpp'),
+ os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c')
]
###############
@@ -36,12 +39,6 @@ for sdk_name in ['css', 'tf2', 'dods', 'hl2dm', 'csgo']:
binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk)
compiler = binary.compiler
- if builder.target_platform == 'linux':
- binary.sources += [
- os.path.join(Extension.sm_root, 'public', 'CDetour', 'detours.cpp'),
- os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c')
- ]
-
if sdk.name == 'csgo':
compiler.cxxincludes += [
os.path.join(sdk.path, 'common', 'protobuf-2.5.0', 'src'),
diff --git a/commonhooks.cpp b/commonhooks.cpp
new file mode 100644
index 0000000..b4c68f0
--- /dev/null
+++ b/commonhooks.cpp
@@ -0,0 +1,63 @@
+/**
+* vim: set ts=4 :
+* =============================================================================
+* SourceMod SourceTV Manager Extension
+* Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved.
+* =============================================================================
+*
+* This program is free software; you can redistribute it and/or modify it under
+* the terms of the GNU General Public License, version 3.0, as published by the
+* Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+* details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program. If not, see .
+*
+* As a special exception, AlliedModders LLC gives you permission to link the
+* code of this program (as well as its derivative works) to "Half-Life 2," the
+* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+* by the Valve Corporation. You must obey the GNU General Public License in
+* all respects for all other code used. Additionally, AlliedModders LLC grants
+* this exception to all derivative works. AlliedModders LLC defines further
+* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+* or .
+*
+* Version: $Id$
+*/
+
+#include "extension.h"
+#include "commonhooks.h"
+#include "forwards.h"
+#include "hltvserverwrapper.h"
+
+CCommonHooks g_pSTVCommonHooks;
+
+// Declare the hook here so we can use it in hltvserverwrapper to fix crashes with the "status" command and host_client.
+// And use it in forwards to hook spectator chat messages.
+SH_DECL_HOOK1(IClient, ExecuteStringCommand, SH_NOATTRIB, 0, bool, const char *);
+
+void CCommonHooks::AddSpectatorHook(CForwardManager *fwdmgr, IClient *client)
+{
+ SH_ADD_HOOK(IClient, ExecuteStringCommand, client, SH_MEMBER(fwdmgr, &CForwardManager::OnSpectatorExecuteStringCommand), false);
+}
+
+void CCommonHooks::RemoveSpectatorHook(CForwardManager *fwdmgr, IClient *client)
+{
+ SH_REMOVE_HOOK(IClient, ExecuteStringCommand, client, SH_MEMBER(fwdmgr, &CForwardManager::OnSpectatorExecuteStringCommand), false);
+}
+
+void CCommonHooks::AddHLTVClientHook(HLTVServerWrapper *wrapper, IClient *client)
+{
+ SH_ADD_HOOK(IClient, ExecuteStringCommand, client, SH_MEMBER(wrapper, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand), false);
+ SH_ADD_HOOK(IClient, ExecuteStringCommand, client, SH_MEMBER(wrapper, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand_Post), true);
+}
+
+void CCommonHooks::RemoveHLTVClientHook(HLTVServerWrapper *wrapper, IClient *client)
+{
+ SH_REMOVE_HOOK(IClient, ExecuteStringCommand, client, SH_MEMBER(wrapper, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand), false);
+ SH_REMOVE_HOOK(IClient, ExecuteStringCommand, client, SH_MEMBER(wrapper, &HLTVServerWrapper::OnHLTVBotExecuteStringCommand_Post), true);
+}
\ No newline at end of file
diff --git a/commonhooks.h b/commonhooks.h
new file mode 100644
index 0000000..1a65527
--- /dev/null
+++ b/commonhooks.h
@@ -0,0 +1,52 @@
+/**
+* vim: set ts=4 :
+* =============================================================================
+* SourceMod SourceTV Manager Extension
+* Copyright (C) 2004-2016 AlliedModders LLC. All rights reserved.
+* =============================================================================
+*
+* This program is free software; you can redistribute it and/or modify it under
+* the terms of the GNU General Public License, version 3.0, as published by the
+* Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+* details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program. If not, see .
+*
+* As a special exception, AlliedModders LLC gives you permission to link the
+* code of this program (as well as its derivative works) to "Half-Life 2," the
+* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+* by the Valve Corporation. You must obey the GNU General Public License in
+* all respects for all other code used. Additionally, AlliedModders LLC grants
+* this exception to all derivative works. AlliedModders LLC defines further
+* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+* or .
+*
+* Version: $Id$
+*/
+
+#ifndef _INCLUDE_SOURCEMOD_EXTENSION_COMMON_HOOKS_H_
+#define _INCLUDE_SOURCEMOD_EXTENSION_COMMON_HOOKS_H_
+
+class IClient;
+class CForwardManager;
+class HLTVServerWrapper;
+
+class CCommonHooks {
+public:
+ // For forwards
+ void AddSpectatorHook(CForwardManager *fwdmgr, IClient *client);
+ void RemoveSpectatorHook(CForwardManager *fwdmgr, IClient *client);
+
+ // For host_client and status fix
+ void AddHLTVClientHook(HLTVServerWrapper *wrapper, IClient *client);
+ void RemoveHLTVClientHook(HLTVServerWrapper *wrapper, IClient *client);
+};
+
+extern CCommonHooks g_pSTVCommonHooks;
+
+#endif // _INCLUDE_SOURCEMOD_EXTENSION_COMMON_HOOKS_H_
diff --git a/extension.cpp b/extension.cpp
index 94a8ffc..a13dc87 100644
--- a/extension.cpp
+++ b/extension.cpp
@@ -95,9 +95,7 @@ bool SourceTVManager::SDK_OnLoad(char *error, size_t maxlength, bool late)
g_HLTVServers.InitHooks();
-#ifndef WIN32
CDetourManager::Init(smutils->GetScriptingEngine(), g_pGameConf);
-#endif
sharesys->AddNatives(myself, sourcetv_natives);
sharesys->RegisterLibrary(myself, "sourcetvmanager");
diff --git a/extension.h b/extension.h
index e0cbd25..e3b199c 100644
--- a/extension.h
+++ b/extension.h
@@ -40,9 +40,7 @@
#include "smsdk_ext.h"
#include
#include
-#ifndef WIN32
#include "CDetour/detours.h"
-#endif
#include "ihltvdirector.h"
#include "ihltv.h"
#include "iserver.h"
diff --git a/forwards.cpp b/forwards.cpp
index 9011852..430b907 100644
--- a/forwards.cpp
+++ b/forwards.cpp
@@ -32,6 +32,7 @@
#include "extension.h"
#include "forwards.h"
#include "hltvserverwrapper.h"
+#include "commonhooks.h"
CForwardManager g_pSTVForwards;
@@ -128,6 +129,8 @@ 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_SpectatorChatMessageFwd = forwards->CreateForward("SourceTV_OnSpectatorChatMessage", ET_Hook, 3, NULL, Param_Cell, Param_String, Param_String);
+ m_SpectatorChatMessagePostFwd = forwards->CreateForward("SourceTV_OnSpectatorChatMessage_Post", ET_Ignore, 3, NULL, Param_Cell, Param_String, Param_String);
m_ServerStartFwd = forwards->CreateForward("SourceTV_OnServerStart", ET_Ignore, 1, NULL, Param_Cell);
m_ServerShutdownFwd = forwards->CreateForward("SourceTV_OnServerShutdown", ET_Ignore, 1, NULL, Param_Cell);
@@ -142,6 +145,8 @@ void CForwardManager::Shutdown()
forwards->ReleaseForward(m_SpectatorDisconnectFwd);
forwards->ReleaseForward(m_SpectatorDisconnectedFwd);
forwards->ReleaseForward(m_SpectatorPutInServerFwd);
+ forwards->ReleaseForward(m_SpectatorChatMessageFwd);
+ forwards->ReleaseForward(m_SpectatorChatMessagePostFwd);
forwards->ReleaseForward(m_ServerStartFwd);
forwards->ReleaseForward(m_ServerShutdownFwd);
@@ -206,6 +211,9 @@ void CForwardManager::UnhookServer(HLTVServerWrapper *wrapper)
void CForwardManager::HookClient(IClient *client)
{
+ // Hook ExecuteStringCommand for chat messages
+ g_pSTVCommonHooks.AddSpectatorHook(this, client);
+
void *pGameClient = (void *)((intptr_t)client - 4);
if (m_bHasActivatePlayerOffset)
SH_ADD_MANUALHOOK(CBaseClient_ActivatePlayer, pGameClient, SH_MEMBER(this, &CForwardManager::OnSpectatorPutInServer), true);
@@ -221,6 +229,9 @@ void CForwardManager::HookClient(IClient *client)
void CForwardManager::UnhookClient(IClient *client)
{
+ // Remove ExecuteStringCommand hook
+ g_pSTVCommonHooks.RemoveSpectatorHook(this, client);
+
void *pGameClient = (void *)((intptr_t)client - 4);
if (m_bHasActivatePlayerOffset)
SH_REMOVE_MANUALHOOK(CBaseClient_ActivatePlayer, pGameClient, SH_MEMBER(this, &CForwardManager::OnSpectatorPutInServer), true);
@@ -421,6 +432,119 @@ void CForwardManager::OnSpectatorPutInServer()
RETURN_META(MRES_IGNORED);
}
+bool CForwardManager::OnSpectatorExecuteStringCommand(const char *s)
+{
+ IClient *client = META_IFACEPTR(IClient);
+ if (!s || !s[0])
+ RETURN_META_VALUE(MRES_IGNORED, true);
+
+ CCommand args;
+ if (!args.Tokenize(s))
+ RETURN_META_VALUE(MRES_IGNORED, true);
+
+ // See if the client wants to chat.
+ if (!Q_stricmp(args[0], "say") && args.ArgC() > 1)
+ {
+ // TODO find correct hltvserver this client is connected to!
+
+ // Save the client index and message.
+ hltvserver->SetLastChatClient(client);
+ hltvserver->SetLastChatMessage(args[1]);
+
+ /*bool ret = SH_CALL(client, &IClient::ExecuteStringCommand)(s);
+
+ hltvserver->SetLastChatClient(0);
+ hltvserver->SetLastChatMessage(nullptr);
+ RETURN_META_VALUE(MRES_SUPERCEDE, ret);*/
+ }
+
+ RETURN_META_VALUE(MRES_IGNORED, true);
+}
+
+DETOUR_DECL_MEMBER2(DetourHLTVServer_BroadcastLocalChat, void, const char *, chat, const char *, chatgroup)
+{
+ // IServer is +8 from CHLTVServer due to multiple inheritance
+ IServer *server = (IServer *)((intptr_t)this + 8);
+ HLTVServerWrapper *wrapper = g_HLTVServers.GetWrapper(server);
+
+ char chatBuffer[256], groupBuffer[256];
+ // TODO: Use saved wrapper->GetLastChatMessage() and add "name : " manually again after plugins are done.
+ ke::SafeStrcpy(chatBuffer, sizeof(chatBuffer), chat);
+ ke::SafeStrcpy(groupBuffer, sizeof(groupBuffer), chatgroup);
+
+ if (wrapper)
+ {
+ // Call the forward for this message.
+ bool supercede = g_pSTVForwards.CallOnSpectatorChatMessage(wrapper, chatBuffer, sizeof(chatBuffer), groupBuffer, sizeof(groupBuffer));
+ if (supercede)
+ return;
+ }
+
+ // Call the engine function with our modified parameters.
+ DETOUR_MEMBER_CALL(DetourHLTVServer_BroadcastLocalChat)(chatBuffer, groupBuffer);
+
+ if (wrapper)
+ {
+ g_pSTVForwards.CallOnSpectatorChatMessage_Post(wrapper, chatBuffer, groupBuffer);
+ }
+}
+
+void CForwardManager::CreateBroadcastLocalChatDetour()
+{
+ if (m_bBroadcastLocalChatDetoured)
+ return;
+
+ m_DBroadcastLocalChat = DETOUR_CREATE_MEMBER(DetourHLTVServer_BroadcastLocalChat, "CHLTVServer::BroadcastLocalChat");
+
+ if (m_DBroadcastLocalChat != nullptr)
+ {
+ m_DBroadcastLocalChat->EnableDetour();
+ m_bBroadcastLocalChatDetoured = true;
+ return;
+ }
+ smutils->LogError(myself, "CHLTVServer::BroadcastLocalChat detour could not be initialized.");
+}
+
+void CForwardManager::RemoveBroadcastLocalChatDetour()
+{
+ if (m_DBroadcastLocalChat != nullptr)
+ {
+ m_DBroadcastLocalChat->Destroy();
+ m_DBroadcastLocalChat = nullptr;
+ }
+ m_bBroadcastLocalChatDetoured = false;
+}
+
+bool CForwardManager::CallOnSpectatorChatMessage(HLTVServerWrapper *server, char *msg, int msglen, char *chatgroup, int grouplen)
+{
+ int clientIndex = 0;
+ IClient *client = server->GetLastChatClient();
+ if (client)
+ clientIndex = client->GetPlayerSlot() + 1;
+
+ m_SpectatorChatMessageFwd->PushCell(clientIndex);
+ m_SpectatorChatMessageFwd->PushStringEx(msg, msglen, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
+ m_SpectatorChatMessageFwd->PushStringEx(chatgroup, grouplen, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
+
+ cell_t res = Pl_Continue;
+ m_SpectatorChatMessageFwd->Execute(&res);
+ if (res >= Pl_Handled)
+ return true;
+ return false;
+}
+
+void CForwardManager::CallOnSpectatorChatMessage_Post(HLTVServerWrapper *server, const char *msg, const char *chatgroup)
+{
+ int clientIndex = 0;
+ IClient *client = server->GetLastChatClient();
+ if (client)
+ clientIndex = client->GetPlayerSlot() + 1;
+
+ m_SpectatorChatMessagePostFwd->PushCell(clientIndex);
+ m_SpectatorChatMessagePostFwd->PushString(msg);
+ m_SpectatorChatMessagePostFwd->PushString(chatgroup);
+ m_SpectatorChatMessagePostFwd->Execute();
+}
// These two hooks are actually only hooked on windows.
void CForwardManager::OnStartRecording_Post(const char *filename, bool bContinuously)
@@ -506,10 +630,10 @@ DETOUR_DECL_MEMBER0(DetourHLTVStopRecording, void)
#endif
}
-bool CForwardManager::CreateStartRecordingDetour()
+void CForwardManager::CreateStartRecordingDetour()
{
if (m_bStartRecordingDetoured)
- return true;
+ return;
m_DStartRecording = DETOUR_CREATE_MEMBER(DetourHLTVStartRecording, "CHLTVDemoRecorder::StartRecording");
@@ -517,10 +641,10 @@ bool CForwardManager::CreateStartRecordingDetour()
{
m_DStartRecording->EnableDetour();
m_bStartRecordingDetoured = true;
- return true;
+ return;
}
smutils->LogError(myself, "CHLTVDemoRecorder::StartRecording detour could not be initialized.");
- return false;
+ return;
}
void CForwardManager::RemoveStartRecordingDetour()
@@ -533,10 +657,10 @@ void CForwardManager::RemoveStartRecordingDetour()
m_bStartRecordingDetoured = false;
}
-bool CForwardManager::CreateStopRecordingDetour()
+void CForwardManager::CreateStopRecordingDetour()
{
if (m_bStopRecordingDetoured)
- return true;
+ return;
m_DStopRecording = DETOUR_CREATE_MEMBER(DetourHLTVStopRecording, "CHLTVDemoRecorder::StopRecording");
@@ -544,10 +668,10 @@ bool CForwardManager::CreateStopRecordingDetour()
{
m_DStopRecording->EnableDetour();
m_bStopRecordingDetoured = true;
- return true;
+ return;
}
smutils->LogError(myself, "CHLTVDemoRecorder::StopRecording detour could not be initialized.");
- return false;
+ return;
}
void CForwardManager::RemoveStopRecordingDetour()
diff --git a/forwards.h b/forwards.h
index 5e11c4e..4271f9b 100644
--- a/forwards.h
+++ b/forwards.h
@@ -70,9 +70,9 @@ public:
void UnhookServer(HLTVServerWrapper *server);
#ifndef WIN32
- bool CreateStartRecordingDetour();
+ void CreateStartRecordingDetour();
void RemoveStartRecordingDetour();
- bool CreateStopRecordingDetour();
+ void CreateStopRecordingDetour();
void RemoveStopRecordingDetour();
#endif
@@ -82,6 +82,13 @@ public:
void CallOnStartRecording(IDemoRecorder *recorder, const char *filename, bool bContinuously);
void CallOnStopRecording(IDemoRecorder *recorder);
+ bool CallOnSpectatorChatMessage(HLTVServerWrapper *server, char *msg, int msglen, char *chatgroup, int grouplen);
+ void CallOnSpectatorChatMessage_Post(HLTVServerWrapper *server, const char *msg, const char *chatgroup);
+
+ bool OnSpectatorExecuteStringCommand(const char *s);
+ void CreateBroadcastLocalChatDetour();
+ void RemoveBroadcastLocalChatDetour();
+
private:
void HookClient(IClient *client);
void UnhookClient(IClient *client);
@@ -112,6 +119,8 @@ private:
IForward *m_SpectatorDisconnectFwd;
IForward *m_SpectatorDisconnectedFwd;
IForward *m_SpectatorPutInServerFwd;
+ IForward *m_SpectatorChatMessageFwd;
+ IForward *m_SpectatorChatMessagePostFwd;
IForward *m_ServerStartFwd;
IForward *m_ServerShutdownFwd;
@@ -122,6 +131,9 @@ private:
bool m_bHasActivatePlayerOffset = false;
bool m_bHasDisconnectOffset = false;
+ bool m_bBroadcastLocalChatDetoured = false;
+ CDetour *m_DBroadcastLocalChat = nullptr;
+
// Only need the detours on linux. Windows always uses its vtables..
#ifndef WIN32
bool m_bStartRecordingDetoured = false;
diff --git a/hltvserverwrapper.cpp b/hltvserverwrapper.cpp
index 462dd2d..5f0b792 100644
--- a/hltvserverwrapper.cpp
+++ b/hltvserverwrapper.cpp
@@ -1,10 +1,10 @@
#include "hltvserverwrapper.h"
#include "forwards.h"
+#include "commonhooks.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
@@ -30,6 +30,7 @@ HLTVServerWrapper::HLTVServerWrapper(IHLTVServer *hltvserver)
m_HLTVServer = hltvserver;
m_DemoRecorder = g_HLTVServers.GetDemoRecorderPtr(hltvserver);
m_Connected = true;
+ m_LastChatClient = nullptr;
Hook();
@@ -84,6 +85,26 @@ int HLTVServerWrapper::GetInstanceNumber()
return g_HLTVServers.GetInstanceNumber(m_HLTVServer);
}
+IClient *HLTVServerWrapper::GetLastChatClient()
+{
+ return m_LastChatClient;
+}
+
+void HLTVServerWrapper::SetLastChatClient(IClient *client)
+{
+ m_LastChatClient = client;
+}
+
+const char *HLTVServerWrapper::GetLastChatMessage()
+{
+ return m_LastChatMessage;
+}
+
+void HLTVServerWrapper::SetLastChatMessage(const char *msg)
+{
+ m_LastChatMessage = msg;
+}
+
HLTVClientWrapper *HLTVServerWrapper::GetClient(int index)
{
// Grow the vector with null pointers
@@ -123,8 +144,8 @@ void HLTVServerWrapper::Hook()
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);
+ // Hook ExecuteStringCommand
+ g_pSTVCommonHooks.AddHLTVClientHook(this, pClient);
#if SOURCE_ENGINE != SE_CSGO
SH_ADD_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnIClient_ClientPrintf_Post), false);
#ifndef WIN32
@@ -155,8 +176,8 @@ void HLTVServerWrapper::Unhook()
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);
+ // Remove ExecuteStringCommand hook
+ g_pSTVCommonHooks.RemoveHLTVClientHook(this, pClient);
#if SOURCE_ENGINE != SE_CSGO
SH_REMOVE_HOOK(IClient, ClientPrintf, pClient, SH_MEMBER(this, &HLTVServerWrapper::OnIClient_ClientPrintf_Post), false);
#ifndef WIN32
@@ -327,6 +348,7 @@ void HLTVServerWrapperManager::InitHooks()
void HLTVServerWrapperManager::ShutdownHooks()
{
+ g_pSTVForwards.RemoveBroadcastLocalChatDetour();
#ifndef WIN32
g_pSTVForwards.RemoveStartRecordingDetour();
g_pSTVForwards.RemoveStopRecordingDetour();
@@ -343,8 +365,9 @@ void HLTVServerWrapperManager::ShutdownHooks()
void HLTVServerWrapperManager::AddServer(IHLTVServer *hltvserver)
{
-#ifndef WIN32
// Create the detours once the first sourcetv server is created.
+ g_pSTVForwards.CreateBroadcastLocalChatDetour();
+#ifndef WIN32
g_pSTVForwards.CreateStartRecordingDetour();
g_pSTVForwards.CreateStopRecordingDetour();
#endif
diff --git a/hltvserverwrapper.h b/hltvserverwrapper.h
index 5482fa0..a211fa7 100644
--- a/hltvserverwrapper.h
+++ b/hltvserverwrapper.h
@@ -48,13 +48,19 @@ public:
HLTVClientWrapper *GetClient(int index);
int GetInstanceNumber();
+ IClient *GetLastChatClient();
+ void SetLastChatClient(IClient *client);
+ const char *GetLastChatMessage();
+ void SetLastChatMessage(const char *msg);
+
+ bool OnHLTVBotExecuteStringCommand(const char *s);
+ bool OnHLTVBotExecuteStringCommand_Post(const char *s);
+
private:
void Hook();
void Unhook();
// Hooks
- bool OnHLTVBotExecuteStringCommand(const char *s);
- bool OnHLTVBotExecuteStringCommand_Post(const char *s);
void OnHLTVServerShutdown();
#if SOURCE_ENGINE != SE_CSGO
@@ -68,6 +74,9 @@ private:
IHLTVServer *m_HLTVServer = nullptr;
IDemoRecorder *m_DemoRecorder = nullptr;
ke::Vector> m_Clients;
+
+ IClient *m_LastChatClient = nullptr;
+ const char *m_LastChatMessage = nullptr;
};
class HLTVServerWrapperManager
diff --git a/sourcetv_test.sp b/sourcetv_test.sp
index aaa625c..4abc1c6 100644
--- a/sourcetv_test.sp
+++ b/sourcetv_test.sp
@@ -87,6 +87,17 @@ public SourceTV_OnSpectatorDisconnected(client, const String:reason[255])
PrintToServer("SourceTV client %d disconnected (isconnected %d) with reason -> %s.", client, SourceTV_IsClientConnected(client), reason);
}
+public Action:SourceTV_OnSpectatorChatMessage(client, String:message[255], String:chatgroup[255])
+{
+ PrintToServer("SourceTV client %d (chatgroup \"%s\") writes: %s", client, chatgroup, message);
+ return Plugin_Continue;
+}
+
+public SourceTV_OnSpectatorChatMessage_Post(client, const String:message[], const String:chatgroup[])
+{
+ PrintToServer("SourceTV client %d (chatgroup \"%s\") wrote: %s", client, chatgroup, message);
+}
+
public Action:Cmd_GetServerCount(client, args)
{
ReplyToCommand(client, "SourceTV server count: %d", SourceTV_GetServerInstanceCount());
diff --git a/sourcetvmanager.games.txt b/sourcetvmanager.games.txt
index f1483fc..cdc8987 100644
--- a/sourcetvmanager.games.txt
+++ b/sourcetvmanager.games.txt
@@ -115,6 +115,14 @@
"windows" "\x55\x8B\xEC\x83\xEC\x4C\x53\x8B\xD9\xC7\x45\xB4\x2A\x2A\x2A\x2A\x56\x8D"
}
+ "CHLTVServer::BroadcastLocalChat"
+ {
+ "library" "engine"
+ "linux" "@_ZN11CHLTVServer18BroadcastLocalChatEPKcS1_"
+ // "hltv_chat"
+ "windows" "\x55\x8B\xEC\x83\xEC\x54\xA1\x2A\x2A\x2A\x2A\x53"
+ }
+
"CHLTVServer::GetRecordingDemoFilename"
{
"library" "engine"
@@ -274,6 +282,14 @@
"windows" "\x55\x8B\xEC\x81\xEC\x44\x04\x00\x00\x53"
}
+ "CHLTVServer::BroadcastLocalChat"
+ {
+ "library" "engine"
+ "linux" "@_ZN11CHLTVServer18BroadcastLocalChatEPKcS1_"
+ // "hltv_chat"
+ "windows" "\x55\x8B\xEC\x81\xEC\x4C\x04\x00\x00\x53\x8B\xD9"
+ }
+
"CHLTVServer::Shutdown"
{
"library" "engine"
diff --git a/sourcetvmanager.inc b/sourcetvmanager.inc
index 86a1995..97bff67 100644
--- a/sourcetvmanager.inc
+++ b/sourcetvmanager.inc
@@ -434,6 +434,29 @@ forward SourceTV_OnSpectatorDisconnected(client, const String:reason[255]);
*/
forward SourceTV_OnSpectatorPutInServer(client);
+/**
+ * Called before a spectator's chat message is sent.
+ * The message and chat group can be changed.
+ * Only called for directly connected clients - no proxies.
+ *
+ * @param client The spectator client index.
+ * @param message The message the client typed.
+ * @param chatgroup The chatgroup this message is sent to (tv_chatgroup).
+ * @return >= Plugin_Handled to block the message, Plugin_Continue to let it through.
+ */
+forward Action:SourceTV_OnSpectatorChatMessage(client, String:message[255], String:chatgroup[255]);
+
+/**
+ * Called after a spectator wrote a chat message.
+ * Only called for directly connected clients - no proxies.
+ *
+ * @param client The spectator client index.
+ * @param message The message the client typed.
+ * @param chatgroup The chatgroup this message is sent to (tv_chatgroup).
+ * @noreturn
+ */
+forward SourceTV_OnSpectatorChatMessage_Post(client, const String:message[], const String:chatgroup[]);
+
/**
* Do not edit below this line!
*/