sdktools_voice: implement ClientSpeaking forwards (#1247)

* Update basevotes

When a player leaves during a voteban, he will be banned anyway. Also added a cvar with a ban time setting.

* Update basevotes.sp

* Added VoiceHook functions

* Update extension.h

* Update extension.h

* Code optimization

* Added support for more engines

* Invalidate timers

* Fixes

* Update extension.cpp

* Update voice.cpp

* correct Forward event type to ET_Ignore.

* sdktools_voice: de-implement IsClientSpeaking

* sdktools_voice: Add notice/unbind IsClientSpeaking

Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
This commit is contained in:
Accelerator74 2020-08-18 16:02:34 +03:00 committed by GitHub
parent 031f80f6e1
commit 1b86c8564d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 230 additions and 2 deletions

View File

@ -57,6 +57,9 @@ SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *,
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_CSGO
SH_DECL_HOOK1_void_vafmt(IVEngineServer, ClientCommand, SH_NOATTRIB, 0, edict_t *);
#endif
#if defined CLIENTVOICE_HOOK_SUPPORT
SH_DECL_HOOK1_void(IServerGameClients, ClientVoice, SH_NOATTRIB, 0, edict_t *);
#endif
SDKTools g_SdkTools; /**< Global singleton for extension's main interface */
IServerGameEnts *gameents = NULL;
@ -76,6 +79,9 @@ IServer *iserver = NULL;
IBaseFileSystem *basefilesystem = NULL;
CGlobalVars *gpGlobals;
ISoundEmitterSystemBase *soundemitterbase = NULL;
ITimer *g_hTimerSpeaking[SM_MAXPLAYERS+1];
IForward *m_OnClientSpeaking;
IForward *m_OnClientSpeakingEnd;
#if SOURCE_ENGINE >= SE_ORANGEBOX
IServerTools *servertools = NULL;
@ -256,6 +262,9 @@ void SDKTools::SDK_OnUnload()
g_Hooks.Shutdown();
g_OutputManager.Shutdown();
forwards->ReleaseForward(m_OnClientSpeaking);
forwards->ReleaseForward(m_OnClientSpeakingEnd);
gameconfs->CloseGameConfigFile(g_pGameConf);
playerhelpers->RemoveClientListener(&g_SdkTools);
playerhelpers->UnregisterCommandTargetProcessor(this);
@ -314,7 +323,9 @@ bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_CSGO
SH_ADD_HOOK(IVEngineServer, ClientCommand, engine, SH_MEMBER(this, &SDKTools::OnSendClientCommand), false);
#endif
#if defined CLIENTVOICE_HOOK_SUPPORT
SH_ADD_HOOK(IServerGameClients, ClientVoice, serverClients, SH_MEMBER(this, &SDKTools::OnClientVoice), true);
#endif
gpGlobals = ismm->GetCGlobals();
enginePatch = SH_GET_CALLCLASS(engine);
enginesoundPatch = SH_GET_CALLCLASS(engsound);
@ -326,6 +337,9 @@ bool SDKTools::SDK_OnMetamodUnload(char *error, size_t maxlen)
{
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_CSGO
SH_REMOVE_HOOK(IVEngineServer, ClientCommand, engine, SH_MEMBER(this, &SDKTools::OnSendClientCommand), false);
#endif
#if defined CLIENTVOICE_HOOK_SUPPORT
SH_REMOVE_HOOK(IServerGameClients, ClientVoice, serverClients, SH_MEMBER(this, &SDKTools::OnClientVoice), true);
#endif
return true;
}
@ -344,6 +358,9 @@ void SDKTools::SDK_OnAllLoaded()
s_SoundHooks.Initialize();
g_Hooks.Initialize();
InitializeValveGlobals();
m_OnClientSpeaking = forwards->CreateForward("OnClientSpeaking", ET_Ignore, 1, NULL, Param_Cell);
m_OnClientSpeakingEnd = forwards->CreateForward("OnClientSpeakingEnd", ET_Ignore, 1, NULL, Param_Cell);
}
void SDKTools::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
@ -516,6 +533,13 @@ bool SDKTools::InterceptClientConnect(int client, char *error, size_t maxlength)
return true;
}
#if !defined CLIENTVOICE_HOOK_SUPPORT
void SDKTools::OnClientConnected(int client)
{
g_Hooks.OnClientConnected(client);
}
#endif
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_CSGO
void SDKTools::OnSendClientCommand(edict_t *pPlayer, const char *szFormat)
{
@ -532,6 +556,43 @@ void SDKTools::OnSendClientCommand(edict_t *pPlayer, const char *szFormat)
}
#endif
SourceMod::ResultType SDKTools::OnTimer(ITimer *pTimer, void *pData)
{
int client = (int)(intptr_t)pData;
m_OnClientSpeakingEnd->PushCell(client);
m_OnClientSpeakingEnd->Execute();
return Pl_Stop;
}
void SDKTools::OnTimerEnd(ITimer *pTimer, void *pData)
{
g_hTimerSpeaking[(int)(intptr_t)pData] = nullptr;
}
#if defined CLIENTVOICE_HOOK_SUPPORT
void SDKTools::OnClientVoice(edict_t *pPlayer)
{
if (!pPlayer)
{
return;
}
int client = IndexOfEdict(pPlayer);
if (g_hTimerSpeaking[client])
{
timersys->KillTimer(g_hTimerSpeaking[client]);
}
g_hTimerSpeaking[client] = timersys->CreateTimer(this, 0.3f, (void *)(intptr_t)client, 0);
m_OnClientSpeaking->PushCell(client);
m_OnClientSpeaking->Execute();
}
#endif
void SDKTools::OnClientPutInServer(int client)
{
g_Hooks.OnClientPutInServer(client);

View File

@ -61,6 +61,12 @@
#include <itoolentity.h>
#endif
#if SOURCE_ENGINE == SE_ALIENSWARM || SOURCE_ENGINE == SE_PORTAL2 || SOURCE_ENGINE == SE_INSURGENCY || SOURCE_ENGINE == SE_DOI || SOURCE_ENGINE == SE_BLADE || SOURCE_ENGINE == SE_CSGO
#define CLIENTVOICE_HOOK_SUPPORT
#else
#include <inetmsghandler.h>
#endif
/**
* @brief Implementation of the SDK Tools extension.
* Note: Uncomment one of the pre-defined virtual functions in order to use it.
@ -70,6 +76,7 @@ class SDKTools :
public IHandleTypeDispatch,
public IConCommandBaseAccessor,
public IClientListener,
public ITimedEvent,
public ICommandTargetProcessor
{
public: //public IHandleTypeDispatch
@ -95,8 +102,15 @@ public: //IConCommandBaseAccessor
bool RegisterConCommandBase(ConCommandBase *pVar);
public: //IClientListner
bool InterceptClientConnect(int client, char *error, size_t maxlength);
#if !defined CLIENTVOICE_HOOK_SUPPORT
void OnClientConnected(int client);
#endif
void OnClientPutInServer(int client);
void OnClientDisconnecting(int client);
public:
#if defined CLIENTVOICE_HOOK_SUPPORT
void OnClientVoice(edict_t *pPlayer);
#endif
public: // IVoiceServer
bool OnSetClientListening(int iReceiver, int iSender, bool bListen);
void VoiceInit();
@ -108,6 +122,9 @@ public: // IVoiceServer
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_CSGO
void OnSendClientCommand(edict_t *pPlayer, const char *szFormat);
#endif
public: //ITimedEvent
ResultType OnTimer(ITimer *pTimer, void *pData);
void OnTimerEnd(ITimer *pTimer, void *pData);
public: //ICommandTargetProcessor
bool ProcessCommandTarget(cmd_target_info_t *info);
@ -156,6 +173,11 @@ extern HandleType_t g_CallHandle;
extern HandleType_t g_TraceHandle;
/* Call Wrappers */
extern ICallWrapper *g_pAcceptInput;
/* Timers */
extern ITimer *g_hTimerSpeaking[SM_MAXPLAYERS+1];
/* Forwards */
extern IForward *m_OnClientSpeaking;
extern IForward *m_OnClientSpeakingEnd;
/* Call classes */
extern SourceHook::CallClass<IVEngineServer> *enginePatch;
extern SourceHook::CallClass<IEngineSound> *enginesoundPatch;

View File

@ -46,6 +46,9 @@ static bool PRCH_enabled = false;
static bool PRCH_used = false;
static bool PRCHPost_used = false;
static bool FILE_used = false;
#if !defined CLIENTVOICE_HOOK_SUPPORT
static bool PVD_used = false;
#endif
SH_DECL_MANUALHOOK2_void(PlayerRunCmdHook, 0, 0, 0, CUserCmd *, IMoveHelper *);
SH_DECL_HOOK2(IBaseFileSystem, FileExists, SH_NOATTRIB, 0, bool, const char*, const char *);
@ -54,6 +57,9 @@ SH_DECL_HOOK3(INetChannel, SendFile, SH_NOATTRIB, 0, bool, const char *, unsigne
#else
SH_DECL_HOOK2(INetChannel, SendFile, SH_NOATTRIB, 0, bool, const char *, unsigned int);
#endif
#if !defined CLIENTVOICE_HOOK_SUPPORT
SH_DECL_HOOK1(IClientMessageHandler, ProcessVoiceData, SH_NOATTRIB, 0, bool, CLC_VoiceData *);
#endif
SH_DECL_HOOK2_void(INetChannel, ProcessPacket, SH_NOATTRIB, 0, struct netpacket_s *, bool);
SourceHook::CallClass<IBaseFileSystem> *basefilesystemPatch = NULL;
@ -141,6 +147,36 @@ void CHookManager::OnClientConnect(int client)
NetChannelHook(client);
}
#if !defined CLIENTVOICE_HOOK_SUPPORT
void CHookManager::OnClientConnected(int client)
{
if (!PVD_used)
{
return;
}
IClient *pClient = iserver->GetClient(client-1);
if (!pClient)
{
return;
}
std::vector<CVTableHook *> &netProcessVoiceData = m_netProcessVoiceData;
CVTableHook hook(pClient);
for (size_t i = 0; i < netProcessVoiceData.size(); ++i)
{
if (hook == netProcessVoiceData[i])
{
return;
}
}
int hookid = SH_ADD_VPHOOK(IClientMessageHandler, ProcessVoiceData, (IClientMessageHandler *)((intptr_t)(pClient) + 4), SH_MEMBER(this, &CHookManager::ProcessVoiceData), true);
hook.SetHookID(hookid);
netProcessVoiceData.push_back(new CVTableHook(hook));
}
#endif
void CHookManager::OnClientPutInServer(int client)
{
if (PRCH_used)
@ -459,6 +495,31 @@ bool CHookManager::SendFile(const char *filename, unsigned int transferID)
RETURN_META_VALUE(MRES_IGNORED, false);
}
#if !defined CLIENTVOICE_HOOK_SUPPORT
bool CHookManager::ProcessVoiceData(CLC_VoiceData *msg)
{
IClient *pClient = (IClient *)((intptr_t)(META_IFACEPTR(IClient)) - 4);
if (pClient == NULL)
{
return true;
}
int client = pClient->GetPlayerSlot() + 1;
if (g_hTimerSpeaking[client])
{
timersys->KillTimer(g_hTimerSpeaking[client]);
}
g_hTimerSpeaking[client] = timersys->CreateTimer(&g_SdkTools, 0.3f, (void *)(intptr_t)client, 0);
m_OnClientSpeaking->PushCell(client);
m_OnClientSpeaking->Execute();
return true;
}
#endif
void CHookManager::OnPluginLoaded(IPlugin *plugin)
{
if (PRCH_enabled)
@ -502,6 +563,22 @@ void CHookManager::OnPluginLoaded(IPlugin *plugin)
}
}
}
#if !defined CLIENTVOICE_HOOK_SUPPORT
if (!PVD_used && (m_OnClientSpeaking->GetFunctionCount() || m_OnClientSpeakingEnd->GetFunctionCount()))
{
PVD_used = true;
int MaxClients = playerhelpers->GetMaxClients();
for (int i = 1; i <= MaxClients; i++)
{
if (playerhelpers->GetGamePlayer(i)->IsConnected())
{
OnClientConnected(i);
}
}
}
#endif
}
void CHookManager::OnPluginUnloaded(IPlugin *plugin)
@ -538,6 +615,19 @@ void CHookManager::OnPluginUnloaded(IPlugin *plugin)
m_netChannelHooks.clear();
FILE_used = false;
}
#if !defined CLIENTVOICE_HOOK_SUPPORT
if (PVD_used && !m_OnClientSpeaking->GetFunctionCount() && !m_OnClientSpeakingEnd->GetFunctionCount())
{
for (size_t i = 0; i < m_netProcessVoiceData.size(); ++i)
{
delete m_netProcessVoiceData[i];
}
m_netProcessVoiceData.clear();
PVD_used = false;
}
#endif
}
FeatureStatus CHookManager::GetFeatureStatus(FeatureType type, const char *name)

View File

@ -47,6 +47,9 @@ public:
void Initialize();
void Shutdown();
void OnClientConnect(int client);
#if !defined CLIENTVOICE_HOOK_SUPPORT
void OnClientConnected(int client);
#endif
void OnClientPutInServer(int client);
void PlayerRunCmd(CUserCmd *ucmd, IMoveHelper *moveHelper);
void PlayerRunCmdPost(CUserCmd *ucmd, IMoveHelper *moveHelper);
@ -57,6 +60,9 @@ public: /* NetChannel/Related Hooks */
bool SendFile(const char *filename, unsigned int transferID, bool isReplayDemo);
#else
bool SendFile(const char *filename, unsigned int transferID);
#endif
#if !defined CLIENTVOICE_HOOK_SUPPORT
bool ProcessVoiceData(CLC_VoiceData *msg);
#endif
void ProcessPacket(struct netpacket_s *packet, bool bHasHeader);
void ProcessPacket_Post(struct netpacket_s *packet, bool bHasHeader);
@ -78,6 +84,9 @@ private:
std::vector<CVTableHook *> m_runUserCmdHooks;
std::vector<CVTableHook *> m_runUserCmdPostHooks;
std::vector<CVTableHook *> m_netChannelHooks;
#if !defined CLIENTVOICE_HOOK_SUPPORT
std::vector<CVTableHook *> m_netProcessVoiceData;
#endif
INetChannel *m_pActiveNetChannel;
bool m_bFSTranHookWarned = false;
bool m_bReplayEnabled = false;

View File

@ -66,7 +66,7 @@
#define SMEXT_ENABLE_GAMECONF
#define SMEXT_ENABLE_MEMUTILS
#define SMEXT_ENABLE_GAMEHELPERS
//#define SMEXT_ENABLE_TIMERSYS
#define SMEXT_ENABLE_TIMERSYS
#define SMEXT_ENABLE_ADTFACTORY
#define SMEXT_ENABLE_PLUGINSYS

View File

@ -167,6 +167,11 @@ bool SDKTools::OnSetClientListening(int iReceiver, int iSender, bool bListen)
void SDKTools::OnClientDisconnecting(int client)
{
if (g_hTimerSpeaking[client])
{
timersys->KillTimer(g_hTimerSpeaking[client]);
}
int max_clients = playerhelpers->GetMaxClients();
if (g_VoiceHookCount == 0)
@ -355,6 +360,33 @@ static cell_t IsClientMuted(IPluginContext *pContext, const cell_t *params)
return g_ClientMutes[params[1]][params[2]];
}
/* FIXME: Presently if there's no hook present these natives will result in an invalid state.
* One suggestion could be to look at if the native is bound, and then invoke the hooks in the background.
* However, at that point really we should be enforcing the forward usage to catch new-consumers immediately.
* If you're looking to work on this, you're welcome to ping asherkin or KyleS, or even submit a patch to add this.
* Additional comments can be found here (if GitHub still exists): https://github.com/alliedmodders/sourcemod/pull/1247
*/
static cell_t IsClientSpeaking(IPluginContext *pContext, const cell_t *params)
{
IGamePlayer *player;
player = playerhelpers->GetGamePlayer(params[1]);
if (player == NULL)
{
return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
}
else if (!player->IsConnected())
{
return pContext->ThrowNativeError("Client %d is not connected", params[1]);
}
else if (!player->IsInGame())
{
return false;
}
return g_hTimerSpeaking[params[1]] != nullptr;
}
sp_nativeinfo_t g_VoiceNatives[] =
{
{"SetClientListeningFlags", SetClientListeningFlags},

View File

@ -56,6 +56,20 @@ enum ListenOverride
Listen_Yes /**< Can hear */
};
/**
* Called when a client is speaking.
*
* @param client The client index
*/
forward void OnClientSpeaking(int client);
/**
* Called once a client speaking end.
*
* @param client The client index
*/
forward void OnClientSpeakingEnd(int client);
/**
* Set the client listening flags.
*