Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sending gameevent to specific client. #505

Merged
merged 2 commits into from
May 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/EventManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

#include "EventManager.h"
#include "sm_stringutil.h"
#include "PlayerManager.h"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this blank line needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

#include "logic_bridge.h"
#include <bridge/include/IScriptManager.h>

Expand Down Expand Up @@ -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 */
Expand Down
3 changes: 3 additions & 0 deletions core/EventManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
#include <IForwardSys.h>
#include <IPluginSys.h>

class IClient;

using namespace SourceHook;

struct EventInfo
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 23 additions & 4 deletions core/PlayerManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "HalfLife2.h"
#include <inetchannel.h>
#include <iclient.h>
#include <iserver.h>
#include <IGameConfigs.h>
#include "ConsoleDetours.h"
#include "logic_bridge.h"
Expand Down Expand Up @@ -2311,10 +2312,9 @@ void CPlayer::DumpAdmin(bool deleting)
void CPlayer::Kick(const char *str)
{
MarkAsBeingKicked();
INetChannel *pNetChan = static_cast<INetChannel *>(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)
{
Expand All @@ -2325,7 +2325,6 @@ void CPlayer::Kick(const char *str)
}
else
{
IClient *pClient = static_cast<IClient *>(pNetChan->GetMsgHandler());
#if SOURCE_ENGINE == SE_CSGO
pClient->Disconnect(str);
#else
Expand Down Expand Up @@ -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()
{
return m_Serial.value;
Expand Down
5 changes: 5 additions & 0 deletions core/PlayerManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@

using namespace SourceHook;

class IClient;

#define PLAYER_LIFE_UNKNOWN 0
#define PLAYER_LIFE_ALIVE 1
#define PLAYER_LIFE_DEAD 2
Expand Down Expand Up @@ -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();
Expand Down
50 changes: 50 additions & 0 deletions core/smn_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are super rare cases where a client can be connected but still have an invalid net channel (KAC/SMAC used to cause crashes in convar queries). Despite this, everything looks fine.

:shipit:

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pics or it didn't happen.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ironically it was part of the code you refactored 😄

https://hg.alliedmods.net/releases/sourcemod-1.3/rev/82cfdef347cc

}

g_EventManager.FireEventToClient(pInfo, pClient);

return 1;
}

static cell_t sm_CancelCreatedEvent(IPluginContext *pContext, const cell_t *params)
{
Handle_t hndl = static_cast<Handle_t>(params[1]);
Expand Down Expand Up @@ -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},
Expand Down
7 changes: 7 additions & 0 deletions plugins/include/events.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down