initial implementation of message hooks

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40558
This commit is contained in:
Borja Ferrer 2007-03-01 22:49:08 +00:00
parent f7df18f6fd
commit 7ebe3f33cd
9 changed files with 770 additions and 171 deletions

View File

@ -1,63 +0,0 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is not open source and may not be copied without explicit
* written permission of AlliedModders LLC. This file may not be redistributed
* in whole or significant part.
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_CMSGIDPOOL_H_
#define _INCLUDE_SOURCEMOD_CMSGIDPOOL_H_
#include "sm_trie.h"
#include "sourcemm_api.h"
#define INVALID_MESSAGE_ID -1
class CMessageIdPool
{
public:
CMessageIdPool()
{
m_Names = sm_trie_create();
}
~CMessageIdPool()
{
sm_trie_destroy(m_Names);
}
public:
int GetMessageId(const char *name)
{
int msgid;
if (!sm_trie_retrieve(m_Names, name, reinterpret_cast<void **>(&msgid)))
{
char buf[255];
int dummy;
msgid = 0;
while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy))
{
if (strcmp(name, buf) == 0)
{
sm_trie_insert(m_Names, name, reinterpret_cast<void *>(msgid));
return msgid;
}
msgid++;
}
return INVALID_MESSAGE_ID;
}
return msgid;
}
private:
Trie *m_Names;
};
#endif //_INCLUDE_SOURCEMOD_CMSGIDPOOL_H_

144
core/CMsgListenerWrapper.h Normal file
View File

@ -0,0 +1,144 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is not open source and may not be copied without explicit
* written permission of AlliedModders LLC. This file may not be redistributed
* in whole or significant part.
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_CMSGLISTENERWRAPPER_H_
#define _INCLUDE_SOURCEMOD_CMSGLISTENERWRAPPER_H_
extern int g_MsgPlayers[256];
class MsgListenerWrapper : public IUserMessageListener
{
public:
void InitListener(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept);
bool IsInterceptHook() const;
int GetMessageId() const;
IPluginFunction *GetHookedFunction() const;
IPluginFunction *GetNotifyFunction() const;
public: //IUserMessageListener
void OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter);
ResultType InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter);
void OnUserMessageSent(int msg_id);
private:
size_t PreparePlArray(int *pl_array, IRecipientFilter *pFilter);
private:
IPluginFunction *m_Hook;
IPluginFunction *m_Intercept;
IPluginFunction *m_Notify;
bool m_IsInterceptHook;
int m_MsgId;
};
inline size_t MsgListenerWrapper::PreparePlArray(int *pl_array, IRecipientFilter *pFilter)
{
size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
for (size_t i=0; i<size; i++)
{
pl_array[i] = pFilter->GetRecipientIndex(i);
}
return size;
}
inline bool MsgListenerWrapper::IsInterceptHook() const
{
return m_IsInterceptHook;
}
inline int MsgListenerWrapper::GetMessageId() const
{
return m_MsgId;
}
inline IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
{
if (m_Hook)
{
return m_Hook;
} else {
return m_Intercept;
}
}
inline IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
{
return m_Notify;
}
inline void MsgListenerWrapper::InitListener(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
{
if (intercept)
{
m_Intercept = hook;
m_Hook = NULL;
} else {
m_Hook = hook;
m_Intercept = NULL;
}
if (notify)
{
m_Notify = notify;
} else {
m_Notify = NULL;
}
m_MsgId = msgid;
m_IsInterceptHook = intercept;
}
inline void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
{
cell_t res;
size_t size = PreparePlArray(g_MsgPlayers, pFilter);
m_Hook->PushCell(msg_id);
m_Hook->PushCell(0); //:TODO: push handle!
m_Hook->PushArray(g_MsgPlayers, size, NULL);
m_Hook->PushCell(size);
m_Hook->PushCell(pFilter->IsReliable());
m_Hook->PushCell(pFilter->IsInitMessage());
m_Hook->Execute(&res);
}
inline ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
{
cell_t res = static_cast<cell_t>(Pl_Continue);
size_t size = PreparePlArray(g_MsgPlayers, pFilter);
m_Intercept->PushCell(msg_id);
m_Intercept->PushCell(0); //:TODO: push handle!
m_Intercept->PushArray(g_MsgPlayers, size, NULL);
m_Intercept->PushCell(size);
m_Intercept->PushCell(pFilter->IsReliable());
m_Intercept->PushCell(pFilter->IsInitMessage());
m_Intercept->Execute(&res);
return static_cast<ResultType>(res);
}
inline void MsgListenerWrapper::OnUserMessageSent(int msg_id)
{
cell_t res;
if (!m_Notify)
{
return;
}
m_Notify->PushCell(msg_id);
m_Notify->Execute(&res);
}
#endif //_INCLUDE_SOURCEMOD_CMSGLISTENERWRAPPER_H_

314
core/CUserMessages.cpp Normal file
View File

@ -0,0 +1,314 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is not open source and may not be copied without explicit
* written permission of AlliedModders LLC. This file may not be redistributed
* in whole or significant part.
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#include "CUserMessages.h"
CUserMessages g_UserMsgs;
SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
CUserMessages::CUserMessages() : m_InterceptBuffer(m_pBase, 2500)
{
m_Names = sm_trie_create();
m_HookCount = 0;
m_InExec = false;
m_InHook = false;
m_CurFlags = 0;
m_CurId = INVALID_MESSAGE_ID;
}
CUserMessages::~CUserMessages()
{
sm_trie_destroy(m_Names);
}
void CUserMessages::OnSourceModAllShutdown()
{
if (m_HookCount)
{
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
}
m_HookCount = 0;
}
int CUserMessages::GetMessageIndex(const char *msg)
{
int msgid;
if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
{
char buf[255];
int dummy;
msgid = 0;
while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy))
{
if (strcmp(msg, buf) == 0)
{
sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
return msgid;
}
msgid++;
}
return INVALID_MESSAGE_ID;
}
return msgid;
}
bf_write *CUserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
{
bf_write *buffer;
if (m_InExec)
{
return NULL;
}
if (msg_id < 0 || msg_id >= 255)
{
return NULL;
}
m_CellRecFilter.SetRecipientPtr(players, playersNum);
m_CurFlags = flags;
if (m_CurFlags & USERMSG_INITMSG)
{
m_CellRecFilter.SetInitMessage(true);
}
if (m_CurFlags & USERMSG_RELIABLE)
{
m_CellRecFilter.SetReliable(true);
}
m_InExec = true;
if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
{
buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
} else {
buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
}
return buffer;
}
bool CUserMessages::EndMessage()
{
if (!m_InExec)
{
return false;
}
if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
{
engine->MessageEnd();
} else {
ENGINE_CALL(MessageEnd)();
}
m_InExec = false;
m_CurFlags = 0;
m_CellRecFilter.ResetFilter();
return true;
}
bool CUserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
{
if (msg_id < 0 || msg_id >= 255)
{
return false;
}
if (!m_HookCount++)
{
SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
}
if (intercept)
{
m_msgIntercepts[msg_id].push_back(pListener);
} else {
m_msgHooks[msg_id].push_back(pListener);
}
return true;
}
bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
{
//:TODO: restrict user from unhooking stuff during a message hook to avoid iterator mess
List<IUserMessageListener *> *lst;
IUserMessageListener *listener;
MsgIter iter;
bool deleted = false;
if (msg_id < 0 || msg_id >= 255)
{
return false;
}
lst = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
for (iter=lst->begin(); iter!=lst->end(); iter++)
{
listener = (*iter);
if (listener == pListener)
{
lst->erase(iter);
deleted = true;
break;
}
}
if (deleted && !(--m_HookCount))
{
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
}
return (deleted) ? true : false;
}
bf_write *CUserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
{
bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
bool is_hook_empty = m_msgHooks[msg_type].empty();
if (is_intercept_empty && is_hook_empty)
{
m_InHook = false;
RETURN_META_VALUE(MRES_IGNORED, NULL);
}
if ((m_InExec) && !(m_CurFlags & USERMSG_PASSTHRU_ALL))
{
m_InHook = false;
RETURN_META_VALUE(MRES_IGNORED, NULL);
}
m_CurId = msg_type;
m_CurRecFilter = filter;
m_InterceptBuffer.Reset();
m_InHook = true;
if (!is_intercept_empty)
{
RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
}
RETURN_META_VALUE(MRES_IGNORED, NULL);
}
bf_write *CUserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
{
if (!m_InHook)
{
RETURN_META_VALUE(MRES_IGNORED, NULL);
}
m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
RETURN_META_VALUE(MRES_IGNORED, NULL);
}
void CUserMessages::OnMessageEnd_Post()
{
if (!m_InHook || (META_RESULT_STATUS == MRES_SUPERCEDE))
{
RETURN_META(MRES_IGNORED);
}
List<IUserMessageListener *> *lst;
IUserMessageListener *listener;
MsgIter iter;
lst = &m_msgIntercepts[m_CurId];
for (iter=lst->begin(); iter!=lst->end(); iter++)
{
listener = (*iter);
listener->OnUserMessageSent(m_CurId);
}
lst = &m_msgHooks[m_CurId];
for (iter=lst->begin(); iter!=lst->end(); iter++)
{
listener = (*iter);
listener->OnUserMessageSent(m_CurId);
}
}
void CUserMessages::OnMessageEnd_Pre()
{
if (!m_InHook)
{
RETURN_META(MRES_IGNORED);
}
List<IUserMessageListener *> *lst;
IUserMessageListener *listener;
bf_write *engine_bfw;
MsgIter iter;
ResultType res;
bool intercepted = false;
bool handled = false;
lst = &m_msgIntercepts[m_CurId];
for (iter=lst->begin(); iter!=lst->end(); iter++)
{
listener = (*iter);
res = listener->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
if (res == Pl_Stop)
{
goto supercede;
} else if (res == Pl_Handled) {
handled = true;
}
intercepted = true;
}
if (handled)
{
goto supercede;
}
if (intercepted)
{
engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
ENGINE_CALL(MessageEnd)();
goto supercede;
}
lst = &m_msgHooks[m_CurId];
for (iter=lst->begin(); iter!=lst->end(); iter++)
{
listener = (*iter);
listener->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
}
RETURN_META(MRES_IGNORED);
supercede:
RETURN_META(MRES_SUPERCEDE);
}

72
core/CUserMessages.h Normal file
View File

@ -0,0 +1,72 @@
/**
* vim: set ts=4 :
* ===============================================================
* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
* ===============================================================
*
* This file is not open source and may not be copied without explicit
* written permission of AlliedModders LLC. This file may not be redistributed
* in whole or significant part.
* For information, see LICENSE.txt or http://www.sourcemod.net/license.php
*
* Version: $Id$
*/
#ifndef _INCLUDE_SOURCEMOD_CUSERMESSAGES_H_
#define _INCLUDE_SOURCEMOD_CUSERMESSAGES_H_
#include "sm_globals.h"
#include <IUserMessages.h>
#include <sh_list.h>
#include "sourcemm_api.h"
#include "sm_trie.h"
#include "CellRecipientFilter.h"
using namespace SourceHook;
using namespace SourceMod;
#define INVALID_MESSAGE_ID -1
typedef List<IUserMessageListener *>::iterator MsgIter;
class CUserMessages :
public IUserMessages,
public SMGlobalClass
{
public:
CUserMessages();
~CUserMessages();
public: //SMGlobalClass
void OnSourceModAllShutdown();
public: //IUserMessages
int GetMessageIndex(const char *msg);
bool HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept=false);
bool UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept=false);
bf_write *StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags);
bool EndMessage();
public:
bf_write *OnStartMessage_Pre(IRecipientFilter *filter, int msg_type);
bf_write *OnStartMessage_Post(IRecipientFilter *filter, int msg_type);
void OnMessageEnd_Pre();
void OnMessageEnd_Post();
private:
List<IUserMessageListener *> m_msgHooks[255];
List<IUserMessageListener *> m_msgIntercepts[255];
unsigned char m_pBase[2500];
IRecipientFilter *m_CurRecFilter;
bf_write m_InterceptBuffer;
bf_write *m_OrigBuffer;
bf_read m_ReadBuffer;
size_t m_HookCount;
bool m_InHook;
Trie *m_Names;
CellRecipientFilter m_CellRecFilter;
bool m_InExec;
int m_CurFlags;
int m_CurId;
};
extern CUserMessages g_UserMsgs;
#endif //_INCLUDE_SOURCEMOD_CUSERMESSAGES_H_

View File

@ -21,7 +21,7 @@
class CellRecipientFilter : public IRecipientFilter
{
public:
CellRecipientFilter() : m_Reliable(false), m_InitMessage(false), m_Size(0), m_CellRecipients(NULL) {}
CellRecipientFilter() : m_Reliable(false), m_InitMessage(false), m_Size(0) {}
~CellRecipientFilter() {}
public: //IRecipientFilter
bool IsReliable() const;
@ -34,10 +34,10 @@ public:
void SetInitMessage(bool isinitmsg);
void ResetFilter();
private:
cell_t m_CellRecipients[255];
bool m_Reliable;
bool m_InitMessage;
size_t m_Size;
cell_t *m_CellRecipients;
};
inline void CellRecipientFilter::ResetFilter()
@ -45,7 +45,6 @@ inline void CellRecipientFilter::ResetFilter()
m_Reliable = false;
m_InitMessage = false;
m_Size = 0;
m_CellRecipients = NULL;
}
inline bool CellRecipientFilter::IsReliable() const
@ -84,7 +83,7 @@ inline void CellRecipientFilter::SetReliable(bool isreliable)
inline void CellRecipientFilter::SetRecipientPtr(cell_t *ptr, size_t count)
{
m_CellRecipients = ptr;
memcpy(m_CellRecipients, ptr, count * sizeof(cell_t));
m_Size = count;
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="8,00"
Name="sourcemod_mm"
ProjectGUID="{E39527CD-7CAB-4420-97CC-DA1B93B260BC}"
RootNamespace="sourcemod_mm"
@ -223,6 +223,10 @@
RelativePath="..\CTranslator.cpp"
>
</File>
<File
RelativePath="..\CUserMessages.cpp"
>
</File>
<File
RelativePath="..\sm_autonatives.cpp"
>
@ -294,7 +298,7 @@
>
</File>
<File
RelativePath="..\CMsgIdPool.h"
RelativePath="..\CMsgListenerWrapper.h"
>
</File>
<File
@ -309,6 +313,10 @@
RelativePath="..\CTranslator.h"
>
</File>
<File
RelativePath="..\CUserMessages.h"
>
</File>
<File
RelativePath="..\sm_autonatives.h"
>
@ -409,6 +417,10 @@
RelativePath="..\..\public\ITextParsers.h"
>
</File>
<File
RelativePath="..\..\public\IUserMessages.h"
>
</File>
</Filter>
<Filter
Name="Systems"

View File

@ -13,40 +13,148 @@
*/
#include "HandleSys.h"
#include "CellRecipientFilter.h"
#include "CMsgIdPool.h"
#include "PluginSys.h"
#include "CUserMessages.h"
#include "CMsgListenerWrapper.h"
#define USERMSG_PASSTHRU (1<<0)
#define USERMSG_PASSTHRU_ALL (1<<1)
#define USERMSG_RELIABLE (1<<2)
#define USERMSG_INITMSG (1<<3)
CMessageIdPool g_MessageIds;
CellRecipientFilter g_MsgRecFilter;
HandleType_t g_WrBitBufType;
Handle_t g_CurMsgHandle;
int g_MsgPlayers[256];
bool g_IsMsgInExec = false;
int g_CurMsgFlags = 0;
class UsrMessageNatives :
public SMGlobalClass,
public IHandleTypeDispatch
public IHandleTypeDispatch,
public IPluginsListener
{
public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
void OnSourceModAllInitialized();
void OnSourceModShutdown();
void OnHandleDestroy(HandleType_t type, void *object);
void OnPluginUnloaded(IPlugin *plugin);
public:
void OnSourceModAllInitialized()
{
g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
}
void OnSourceModShutdown()
{
g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
g_WrBitBufType = 0;
}
void OnHandleDestroy(HandleType_t type, void *object)
{
}
MsgListenerWrapper *GetNewListener(IPluginContext *pCtx);
MsgListenerWrapper *FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
bool RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept);
private:
CStack<MsgListenerWrapper *> m_FreeListeners;
};
void UsrMessageNatives::OnSourceModAllInitialized()
{
g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
g_PluginSys.AddPluginsListener(this);
}
void UsrMessageNatives::OnSourceModShutdown()
{
g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
g_PluginSys.RemovePluginsListener(this);
g_WrBitBufType = 0;
}
void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
{
}
void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
{
List<MsgListenerWrapper *> *wrapper_list;
if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list), true))
{
List<MsgListenerWrapper *>::iterator iter;
MsgListenerWrapper *listener;
for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++)
{
listener = (*iter);
if (g_UserMsgs.UnhookUserMessage(listener->GetMessageId(), listener, listener->IsInterceptHook()))
{
m_FreeListeners.push(listener);
}
}
delete wrapper_list;
}
}
MsgListenerWrapper *UsrMessageNatives::GetNewListener(IPluginContext *pCtx)
{
MsgListenerWrapper *listener_wrapper;
if (m_FreeListeners.empty())
{
listener_wrapper = new MsgListenerWrapper;
} else {
listener_wrapper = m_FreeListeners.front();
m_FreeListeners.pop();
}
List<MsgListenerWrapper *> *wrapper_list;
IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
{
wrapper_list = new List<MsgListenerWrapper *>;
pl->SetProperty("MsgListeners", wrapper_list);
}
wrapper_list->push_back(listener_wrapper);
return listener_wrapper;
}
MsgListenerWrapper *UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
{
MsgListenerWrapper *listener;
List<MsgListenerWrapper *> *wrapper_list;
List<MsgListenerWrapper *>::iterator iter;
IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
bool found = false;
if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
{
return NULL;
}
for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++)
{
listener = (*iter);
if ((msgid == listener->GetMessageId())
&& (intercept == listener->IsInterceptHook())
&& (pHook == listener->GetHookedFunction()))
{
found = true;
break;
}
}
return (found) ? listener : NULL;
}
bool UsrMessageNatives::RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept)
{
List<MsgListenerWrapper *> *wrapper_list;
IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
{
return false;
}
wrapper_list->remove(listener);
m_FreeListeners.push(listener);
return true;
}
/**************************************
* *
* USER MESSAGE NATIVE IMPLEMENTATIONS *
* *
***************************************/
static UsrMessageNatives s_UsrMessageNatives;
static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
{
char *msgname;
@ -57,7 +165,7 @@ static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
return 0;
}
return g_MessageIds.GetMessageId(msgname);
return g_UserMsgs.GetMessageIndex(msgname);
}
static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
@ -78,30 +186,14 @@ static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
return 0;
}
if ((msgid=g_MessageIds.GetMessageId(msgname)) == INVALID_MESSAGE_ID)
if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
{
return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
}
pCtx->LocalToPhysAddr(params[2], &cl_array);
g_CurMsgFlags = params[4];
g_MsgRecFilter.SetRecipientPtr(cl_array, params[3]);
if (g_CurMsgFlags & USERMSG_INITMSG)
{
g_MsgRecFilter.SetInitMessage(true);
}
if (g_CurMsgFlags & USERMSG_RELIABLE)
{
g_MsgRecFilter.SetReliable(true);
}
if (g_CurMsgFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) //:TODO: change this when we can hook messages
{
pBitBuf = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&g_MsgRecFilter), msgid);
} else {
pBitBuf = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&g_MsgRecFilter), msgid);
}
pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
g_IsMsgInExec = true;
@ -120,30 +212,14 @@ static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
}
if (msgid < 0 || msgid > 255)
if (msgid < 0 || msgid >= 255)
{
return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
}
pCtx->LocalToPhysAddr(params[2], &cl_array);
g_CurMsgFlags = params[4];
g_MsgRecFilter.SetRecipientPtr(cl_array, params[3]);
if (g_CurMsgFlags & USERMSG_INITMSG)
{
g_MsgRecFilter.SetInitMessage(true);
}
if (g_CurMsgFlags & USERMSG_RELIABLE)
{
g_MsgRecFilter.SetReliable(true);
}
if (g_CurMsgFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU)) //:TODO: change this when we can hook messages
{
pBitBuf = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&g_MsgRecFilter), msgid);
} else {
pBitBuf = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&g_MsgRecFilter), msgid);
}
pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
g_IsMsgInExec = true;
@ -160,25 +236,78 @@ static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
}
if (g_CurMsgFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
{
engine->MessageEnd();
} else {
ENGINE_CALL(MessageEnd)();
}
g_UserMsgs.EndMessage();
sec.pOwner = pCtx->GetIdentity();
sec.pIdentity = g_pCoreIdent;
g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
g_IsMsgInExec = false;
g_CurMsgFlags = 0;
g_MsgRecFilter.ResetFilter();
return 1;
}
static UsrMessageNatives s_UsrMessageNatives;
static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
{
IPluginFunction *pHook, *pNotify;
MsgListenerWrapper *listener;
bool intercept;
int msgid = params[1];
if (msgid < 0 || msgid >= 255)
{
return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
}
pHook = pCtx->GetFunctionById(params[2]);
if (!pHook)
{
return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
}
pNotify = pCtx->GetFunctionById(params[4]);
intercept = (params[3]) ? true : false;
listener = s_UsrMessageNatives.GetNewListener(pCtx);
listener->InitListener(msgid, pHook, pNotify, intercept);
g_UserMsgs.HookUserMessage(msgid, listener, intercept);
return 1;
}
static cell_t smn_UnHookUserMessage(IPluginContext *pCtx, const cell_t *params)
{
IPluginFunction *pFunc;
MsgListenerWrapper *listener;
bool intercept;
int msgid = params[1];
if (msgid < 0 || msgid >= 255)
{
return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
}
pFunc = pCtx->GetFunctionById(params[2]);
if (!pFunc)
{
return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
}
intercept = (params[3]) ? true : false;
listener = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
if (!listener)
{
return pCtx->ThrowNativeError("Unable to unhook the current user message");
}
if (!g_UserMsgs.UnhookUserMessage(msgid, listener, intercept))
{
return pCtx->ThrowNativeError("Unable to unhook the current user message");
}
s_UsrMessageNatives.RemoveListener(pCtx, listener, intercept);
return 1;
}
REGISTER_NATIVES(usrmsgnatives)
{
@ -186,5 +315,7 @@ REGISTER_NATIVES(usrmsgnatives)
{"StartMessage", smn_StartMessage},
{"StartMessageEx", smn_StartMessageEx},
{"EndMessage", smn_EndMessage},
{"HookUserMessage", smn_HookUserMessage},
{"UnHookUserMessage", smn_UnHookUserMessage},
{NULL, NULL}
};

View File

@ -85,6 +85,7 @@ native EndMessage();
* @brief Called when a message is hooked
*
* @param msg_id Message index.
* @param bf Handle to the input bit buffer of the message.
* @param players Array containing player indexes.
* @param playersNum Number of players in the array.
* @param reliable True if message is reliable, false otherwise.
@ -92,7 +93,14 @@ native EndMessage();
* @return Ignored for normal hooks. For intercept hooks, false blocks
* the message from being sent, and true continues.
*/
functag MsgHook bool:public(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init);
functag MsgHook Action:public(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init);
/**
* @brief Called when a message is finished sending.
*
* @param msg_id Message index.
*/
functag MsgSentNotify public(UserMsg:msg_id);
/**
* @brief Hooks a user message.
@ -102,10 +110,11 @@ functag MsgHook bool:public(UserMsg:msg_id, Handle:bf, const players[], playersN
* @param intercept If intercept is true, message will be fully intercepted,
* allowing the user to block the message. Otherwise,
* the hook is normal and ignores the return value.
* @param notify Notification function.
* @noreturn
* @error Invalid message index.
*/
native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false);
native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false, MsgSentNotify:notify=MsgSentNotify:-1);
/**
* @brief Removes one usermessage hook.
@ -118,33 +127,6 @@ native HookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false);
*/
native UnHookUserMessage(UserMsg:msg_id, MsgHook:hook, bool:intercept=false);
/**
* @brief Called when a message is finished sending.
*
* @param msg_id Message index.
*/
functag MsgSentNotify public(UserMsg:msg_id);
/**
* @brief Notifies when a message has finished sending.
*
* @param msg_id Message index.
* @param notify Notification function.
* @noreturn
* @error Invalid message index.
*/
native NotifyUserMessage(UserMsg:msg_id, MsgSentNotify:notify);
/**
* @brief Removes a user message notification.
*
* @param msg_id Message index.
* @param notify Notification function.
* @noreturn
* @error Invalid message index.
*/
native RemoveNotifyUserMessage(UserMsg:msg_id, MsgSentNotify:notify);
/**
* Starts a usermessage (network message) that broadcasts to all clients.
* @note See StartMessage or StartMessageEx().

View File

@ -19,7 +19,9 @@
#ifndef _INCLUDE_SOURCEMOD_INTERFACE_USERMESSAGES_H_
#define _INCLUDE_SOURCEMOD_INTERFACE_USERMESSAGES_H_
#include <IShareSys.h>
#include <sp_vm_api.h>
#include <IForwardSys.h>
#include <bitbuf.h>
#include <irecipientfilter.h>
@ -59,9 +61,9 @@ namespace SourceMod
* @param pFtiler Recipient filter.
* @return True to allow message, false to scrap it.
*/
virtual bool InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
virtual ResultType InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
{
return true;
return Pl_Continue;
}
/**
@ -73,6 +75,11 @@ namespace SourceMod
}
};
#define USERMSG_PASSTHRU (1<<0) /**< Message will pass through other SourceMM plugins */
#define USERMSG_PASSTHRU_ALL (1<<1) /**< Message will pass through other SourceMM plugins AND SourceMod */
#define USERMSG_RELIABLE (1<<2) /**< Message will be set to reliable */
#define USERMSG_INITMSG (1<<3) /**< Message will be considered to be an initmsg */
/**
* @brief Contains functions for hooking user messages.
*/
@ -127,14 +134,15 @@ namespace SourceMod
* @param players Array containing player indexes.
* @param playersNum Number of players in the array.
* @param flags Flags to use for sending the message.
* @return bf_write structure to write message with.
* @return bf_write structure to write message with, or NULL on failure.
*/
virtual bf_write *StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags) =0;
/**
* @brief Wrapper around UserMessageEnd for use with StartMessage().
* @return True on success, false otherwise.
*/
virtual void EndMessage() =0;
virtual bool EndMessage() =0;
};
}