From a584e13e2479a22426478676cc21c7f8886df905 Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Sun, 8 May 2016 09:23:06 -0400 Subject: [PATCH 1/2] Add support for sending gameevent to specific client. --- core/EventManager.cpp | 9 +++++++ core/EventManager.h | 3 +++ core/PlayerManager.cpp | 27 +++++++++++++++++--- core/PlayerManager.h | 5 ++++ core/smn_events.cpp | 50 ++++++++++++++++++++++++++++++++++++++ plugins/include/events.inc | 7 ++++++ 6 files changed, 97 insertions(+), 4 deletions(-) diff --git a/core/EventManager.cpp b/core/EventManager.cpp index bba9caf014..5adaf0a8db 100644 --- a/core/EventManager.cpp +++ b/core/EventManager.cpp @@ -31,6 +31,8 @@ #include "EventManager.h" #include "sm_stringutil.h" +#include "PlayerManager.h" + #include "logic_bridge.h" #include @@ -360,6 +362,13 @@ void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast) m_FreeEvents.push(pInfo); } +void EventManager::FireEventToClient(EventInfo *pInfo, IClient *pClient) +{ + // The IClient vtable is +4 from the IGameEventListener2 (CBaseClient) vtable due to multiple inheritance. + IGameEventListener2 *pGameClient = (IGameEventListener2 *)((intptr_t)pClient - 4); + pGameClient->FireGameEvent(pInfo->pEvent); +} + void EventManager::CancelCreatedEvent(EventInfo *pInfo) { /* Free event from IGameEventManager2 */ diff --git a/core/EventManager.h b/core/EventManager.h index 005b938f0d..bf7bd37bfb 100644 --- a/core/EventManager.h +++ b/core/EventManager.h @@ -41,6 +41,8 @@ #include #include +class IClient; + using namespace SourceHook; struct EventInfo @@ -126,6 +128,7 @@ class EventManager : EventHookError UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post); EventInfo *CreateEvent(IPluginContext *pContext, const char *name, bool force=false); void FireEvent(EventInfo *pInfo, bool bDontBroadcast=false); + void FireEventToClient(EventInfo *pInfo, IClient *pClient); void CancelCreatedEvent(EventInfo *pInfo); private: // IGameEventManager2 hooks bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast); diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index 26f09b09b9..0a1ed6c763 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -42,6 +42,7 @@ #include "HalfLife2.h" #include #include +#include #include #include "ConsoleDetours.h" #include "logic_bridge.h" @@ -2311,10 +2312,9 @@ void CPlayer::DumpAdmin(bool deleting) void CPlayer::Kick(const char *str) { MarkAsBeingKicked(); - INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(m_iIndex)); - if (pNetChan == NULL) + IClient *pClient = GetIClient(); + if (pClient == nullptr) { - /* What does this even mean? Hell if I know. */ int userid = GetUserId(); if (userid > 0) { @@ -2325,7 +2325,6 @@ void CPlayer::Kick(const char *str) } else { - IClient *pClient = static_cast(pNetChan->GetMsgHandler()); #if SOURCE_ENGINE == SE_CSGO pClient->Disconnect(str); #else @@ -2540,6 +2539,26 @@ int CPlayer::GetLifeState() } } +IClient *CPlayer::GetIClient() const +{ +#if SOURCE_ENGINE == SE_TF2 \ + || SOURCE_ENGINE == SE_CSS \ + || SOURCE_ENGINE == SE_DODS \ + || SOURCE_ENGINE == SE_HL2DM \ + || SOURCE_ENGINE == SE_BMS \ + || SOURCE_ENGINE == SE_INSURGENCY + return engine->GetIServer()->GetClient(m_iIndex - 1); +#else + INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(m_iIndex)); + if (pNetChan) + { + return static_cast(pNetChan->GetMsgHandler()); + } + + return nullptr; +#endif +} + unsigned int CPlayer::GetSerial() { return m_Serial.value; diff --git a/core/PlayerManager.h b/core/PlayerManager.h index dc779cab76..76df8b1523 100644 --- a/core/PlayerManager.h +++ b/core/PlayerManager.h @@ -48,6 +48,8 @@ using namespace SourceHook; +class IClient; + #define PLAYER_LIFE_UNKNOWN 0 #define PLAYER_LIFE_ALIVE 1 #define PLAYER_LIFE_DEAD 2 @@ -104,6 +106,9 @@ class CPlayer : public IGamePlayer void DoBasicAdminChecks(); void MarkAsBeingKicked(); int GetLifeState(); + + // This can be NULL for fakeclients due to limitations in our impl + IClient *GetIClient() const; private: void Initialize(const char *name, const char *ip, edict_t *pEntity); void Connect(); diff --git a/core/smn_events.cpp b/core/smn_events.cpp index 6405001fac..a66d30ded9 100644 --- a/core/smn_events.cpp +++ b/core/smn_events.cpp @@ -32,6 +32,7 @@ #include "sm_globals.h" #include "sourcemm_api.h" #include "EventManager.h" +#include "PlayerManager.h" #include "logic_bridge.h" static cell_t sm_HookEvent(IPluginContext *pContext, const cell_t *params) @@ -146,6 +147,54 @@ static cell_t sm_FireEvent(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t sm_FireEventToClient(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + EventInfo *pInfo; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = handlesys->ReadHandle(hndl, g_EventManager.GetHandleType(), &sec, (void **)&pInfo)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid game event handle %x (error %d)", hndl, err); + } + + /* If identities do not match, don't fire event */ + if (pContext->GetIdentity() != pInfo->pOwner) + { + return pContext->ThrowNativeError("Game event \"%s\" could not be fired because it was not created by this plugin", pInfo->pEvent->GetName()); + } + + if (pInfo->bDontBroadcast) + { + return pContext->ThrowNativeError("Game event \"%s\" is set to not be broadcasted to clients", pInfo->pEvent->GetName()); + } + + int client = params[2]; + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", client); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", client); + } + + IClient *pClient = pPlayer->GetIClient(); + if (!pClient) + { + return pContext->ThrowNativeError("Sending events to fakeclints is not supported on this game (client %d)", client); + } + + g_EventManager.FireEventToClient(pInfo, pClient); + + return 1; +} + static cell_t sm_CancelCreatedEvent(IPluginContext *pContext, const cell_t *params) { Handle_t hndl = static_cast(params[1]); @@ -421,6 +470,7 @@ REGISTER_NATIVES(gameEventNatives) // Transitional syntax support. {"Event.Fire", sm_FireEvent}, + {"Event.FireToClient", sm_FireEventToClient}, {"Event.Cancel", sm_CancelCreatedEvent}, {"Event.GetName", sm_GetEventName}, {"Event.GetBool", sm_GetEventBool}, diff --git a/plugins/include/events.inc b/plugins/include/events.inc index 4ffa86c4d1..18c11e443e 100644 --- a/plugins/include/events.inc +++ b/plugins/include/events.inc @@ -79,6 +79,13 @@ methodmap Event < Handle // // @param dontBroadcast Optional boolean that determines if event should be broadcast to clients. public native void Fire(bool dontBroadcast=false); + + // Fires a game event to only the specified client. + // + // Unlike Fire, this function DOES NOT close the event Handle. + // + // @param client Index of client to receive the event.. + public native void FireToClient(int client); // Cancels a previously created game event that has not been fired. This // is necessary to avoid leaking memory when an event isn't fired. From 376a184b998f394c048957428f51a111b1a9dff5 Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Sun, 8 May 2016 11:16:05 -0400 Subject: [PATCH 2/2] Typo fix. --- core/smn_events.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/smn_events.cpp b/core/smn_events.cpp index a66d30ded9..2fc3a31c9e 100644 --- a/core/smn_events.cpp +++ b/core/smn_events.cpp @@ -187,7 +187,7 @@ static cell_t sm_FireEventToClient(IPluginContext *pContext, const cell_t *param IClient *pClient = pPlayer->GetIClient(); if (!pClient) { - return pContext->ThrowNativeError("Sending events to fakeclints is not supported on this game (client %d)", client); + return pContext->ThrowNativeError("Sending events to fakeclients is not supported on this game (client %d)", client); } g_EventManager.FireEventToClient(pInfo, pClient);