Add support for sending gameevent to specific client. (#505)

This commit is contained in:
Nicholas Hastings 2016-05-12 22:15:23 -04:00
parent 98e8f70424
commit 20c9815619
6 changed files with 97 additions and 4 deletions

View File

@ -31,6 +31,8 @@
#include "EventManager.h" #include "EventManager.h"
#include "sm_stringutil.h" #include "sm_stringutil.h"
#include "PlayerManager.h"
#include "logic_bridge.h" #include "logic_bridge.h"
#include <bridge/include/IScriptManager.h> #include <bridge/include/IScriptManager.h>
@ -360,6 +362,13 @@ void EventManager::FireEvent(EventInfo *pInfo, bool bDontBroadcast)
m_FreeEvents.push(pInfo); 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) void EventManager::CancelCreatedEvent(EventInfo *pInfo)
{ {
/* Free event from IGameEventManager2 */ /* Free event from IGameEventManager2 */

View File

@ -41,6 +41,8 @@
#include <IForwardSys.h> #include <IForwardSys.h>
#include <IPluginSys.h> #include <IPluginSys.h>
class IClient;
using namespace SourceHook; using namespace SourceHook;
struct EventInfo struct EventInfo
@ -126,6 +128,7 @@ public:
EventHookError UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post); EventHookError UnhookEvent(const char *name, IPluginFunction *pFunction, EventHookMode mode=EventHookMode_Post);
EventInfo *CreateEvent(IPluginContext *pContext, const char *name, bool force=false); EventInfo *CreateEvent(IPluginContext *pContext, const char *name, bool force=false);
void FireEvent(EventInfo *pInfo, bool bDontBroadcast=false); void FireEvent(EventInfo *pInfo, bool bDontBroadcast=false);
void FireEventToClient(EventInfo *pInfo, IClient *pClient);
void CancelCreatedEvent(EventInfo *pInfo); void CancelCreatedEvent(EventInfo *pInfo);
private: // IGameEventManager2 hooks private: // IGameEventManager2 hooks
bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast); bool OnFireEvent(IGameEvent *pEvent, bool bDontBroadcast);

View File

@ -42,6 +42,7 @@
#include "HalfLife2.h" #include "HalfLife2.h"
#include <inetchannel.h> #include <inetchannel.h>
#include <iclient.h> #include <iclient.h>
#include <iserver.h>
#include <IGameConfigs.h> #include <IGameConfigs.h>
#include "ConsoleDetours.h" #include "ConsoleDetours.h"
#include "logic_bridge.h" #include "logic_bridge.h"
@ -2311,10 +2312,9 @@ void CPlayer::DumpAdmin(bool deleting)
void CPlayer::Kick(const char *str) void CPlayer::Kick(const char *str)
{ {
MarkAsBeingKicked(); MarkAsBeingKicked();
INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(m_iIndex)); IClient *pClient = GetIClient();
if (pNetChan == NULL) if (pClient == nullptr)
{ {
/* What does this even mean? Hell if I know. */
int userid = GetUserId(); int userid = GetUserId();
if (userid > 0) if (userid > 0)
{ {
@ -2325,7 +2325,6 @@ void CPlayer::Kick(const char *str)
} }
else else
{ {
IClient *pClient = static_cast<IClient *>(pNetChan->GetMsgHandler());
#if SOURCE_ENGINE == SE_CSGO #if SOURCE_ENGINE == SE_CSGO
pClient->Disconnect(str); pClient->Disconnect(str);
#else #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<INetChannel *>(engine->GetPlayerNetInfo(m_iIndex));
if (pNetChan)
{
return static_cast<IClient *>(pNetChan->GetMsgHandler());
}
return nullptr;
#endif
}
unsigned int CPlayer::GetSerial() unsigned int CPlayer::GetSerial()
{ {
return m_Serial.value; return m_Serial.value;

View File

@ -48,6 +48,8 @@
using namespace SourceHook; using namespace SourceHook;
class IClient;
#define PLAYER_LIFE_UNKNOWN 0 #define PLAYER_LIFE_UNKNOWN 0
#define PLAYER_LIFE_ALIVE 1 #define PLAYER_LIFE_ALIVE 1
#define PLAYER_LIFE_DEAD 2 #define PLAYER_LIFE_DEAD 2
@ -104,6 +106,9 @@ public:
void DoBasicAdminChecks(); void DoBasicAdminChecks();
void MarkAsBeingKicked(); void MarkAsBeingKicked();
int GetLifeState(); int GetLifeState();
// This can be NULL for fakeclients due to limitations in our impl
IClient *GetIClient() const;
private: private:
void Initialize(const char *name, const char *ip, edict_t *pEntity); void Initialize(const char *name, const char *ip, edict_t *pEntity);
void Connect(); void Connect();

View File

@ -32,6 +32,7 @@
#include "sm_globals.h" #include "sm_globals.h"
#include "sourcemm_api.h" #include "sourcemm_api.h"
#include "EventManager.h" #include "EventManager.h"
#include "PlayerManager.h"
#include "logic_bridge.h" #include "logic_bridge.h"
static cell_t sm_HookEvent(IPluginContext *pContext, const cell_t *params) 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; return 1;
} }
static cell_t sm_FireEventToClient(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast<Handle_t>(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 fakeclients 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) static cell_t sm_CancelCreatedEvent(IPluginContext *pContext, const cell_t *params)
{ {
Handle_t hndl = static_cast<Handle_t>(params[1]); Handle_t hndl = static_cast<Handle_t>(params[1]);
@ -421,6 +470,7 @@ REGISTER_NATIVES(gameEventNatives)
// Transitional syntax support. // Transitional syntax support.
{"Event.Fire", sm_FireEvent}, {"Event.Fire", sm_FireEvent},
{"Event.FireToClient", sm_FireEventToClient},
{"Event.Cancel", sm_CancelCreatedEvent}, {"Event.Cancel", sm_CancelCreatedEvent},
{"Event.GetName", sm_GetEventName}, {"Event.GetName", sm_GetEventName},
{"Event.GetBool", sm_GetEventBool}, {"Event.GetBool", sm_GetEventBool},

View File

@ -79,6 +79,13 @@ methodmap Event < Handle
// //
// @param dontBroadcast Optional boolean that determines if event should be broadcast to clients. // @param dontBroadcast Optional boolean that determines if event should be broadcast to clients.
public native void Fire(bool dontBroadcast=false); 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 // Cancels a previously created game event that has not been fired. This
// is necessary to avoid leaking memory when an event isn't fired. // is necessary to avoid leaking memory when an event isn't fired.