3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
15 #include "HandleSys.h"
16 #include "PluginSys.h"
17 #include "UserMessages.h"
18 #include "smn_usermsgs.h"
20 HandleType_t g_WrBitBufType;
21 HandleType_t g_RdBitBufType;
22 Handle_t g_CurMsgHandle;
23 Handle_t g_ReadBufHandle;
26 int g_MsgPlayers[256];
27 bool g_IsMsgInExec = false;
29 typedef List<MsgListenerWrapper *> MsgWrapperList;
30 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
32 class UsrMessageNatives :
34 public IHandleTypeDispatch,
35 public IPluginsListener
37 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
38 void OnSourceModAllInitialized();
39 void OnSourceModShutdown();
40 void OnHandleDestroy(HandleType_t type, void *object);
41 void OnPluginUnloaded(IPlugin *plugin);
43 MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
44 MsgWrapperIter FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
45 bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
47 CStack<MsgListenerWrapper *> m_FreeListeners;
50 void UsrMessageNatives::OnSourceModAllInitialized()
53 sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
55 g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
56 g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
58 g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
60 g_PluginSys.AddPluginsListener(this);
63 void UsrMessageNatives::OnSourceModShutdown()
66 sec.pIdentity = g_pCoreIdent;
68 g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
70 g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
71 g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
73 g_PluginSys.RemovePluginsListener(this);
78 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
82 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
84 MsgWrapperList *pList;
86 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
89 MsgListenerWrapper *pListener;
91 for (iter=pList->begin(); iter!=pList->end(); iter++)
94 if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
96 m_FreeListeners.push(pListener);
104 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
106 MsgWrapperList *pList;
107 MsgListenerWrapper *pListener;
108 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
110 if (m_FreeListeners.empty())
112 pListener = new MsgListenerWrapper;
114 pListener = m_FreeListeners.front();
115 m_FreeListeners.pop();
118 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
120 pList = new List<MsgListenerWrapper *>;
121 pl->SetProperty("MsgListeners", pList);
124 pList->push_back(pListener);
129 MsgWrapperIter UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
131 MsgWrapperList *pList;
133 MsgListenerWrapper *pListener;
134 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
136 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
141 for (iter=pList->begin(); iter!=pList->end(); iter++)
144 if ((msgid == pListener->GetMessageId())
145 && (intercept == pListener->IsInterceptHook())
146 && (pHook == pListener->GetHookedFunction()))
155 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
157 MsgWrapperList *pList;
158 MsgListenerWrapper *pListener;
159 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
161 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
168 m_FreeListeners.push(pListener);
173 /***************************************
175 * USER MESSAGE WRAPPER IMPLEMENTATION *
177 ***************************************/
179 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
198 m_IsInterceptHook = intercept;
201 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
203 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
205 for (size_t i=0; i<size; i++)
207 pl_array[i] = pFilter->GetRecipientIndex(i);
213 bool MsgListenerWrapper::IsInterceptHook() const
215 return m_IsInterceptHook;
218 int MsgListenerWrapper::GetMessageId() const
223 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
233 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
238 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
241 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
243 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
245 m_Hook->PushCell(msg_id);
246 m_Hook->PushCell(g_ReadBufHandle);
247 m_Hook->PushArray(g_MsgPlayers, size);
248 m_Hook->PushCell(size);
249 m_Hook->PushCell(pFilter->IsReliable());
250 m_Hook->PushCell(pFilter->IsInitMessage());
251 m_Hook->Execute(&res);
254 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
256 cell_t res = static_cast<cell_t>(Pl_Continue);
257 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
259 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
261 m_Intercept->PushCell(msg_id);
262 m_Intercept->PushCell(g_ReadBufHandle);
263 m_Intercept->PushArray(g_MsgPlayers, size);
264 m_Intercept->PushCell(size);
265 m_Intercept->PushCell(pFilter->IsReliable());
266 m_Intercept->PushCell(pFilter->IsInitMessage());
267 m_Intercept->Execute(&res);
269 return static_cast<ResultType>(res);
272 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
280 m_Notify->PushCell(msg_id);
281 m_Notify->Execute(&res);
284 /***************************************
286 * USER MESSAGE NATIVE IMPLEMENTATIONS *
288 ***************************************/
290 static UsrMessageNatives s_UsrMessageNatives;
292 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
296 if ((err=pCtx->LocalToString(params[1], &msgname)) != SP_ERROR_NONE)
298 pCtx->ThrowNativeErrorEx(err, NULL);
302 return g_UserMsgs.GetMessageIndex(msgname);
305 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
309 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
311 if (!g_UserMsgs.GetMessageName(params[1], msgname, params[3]))
320 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
329 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
332 if ((err=pCtx->LocalToString(params[1], &msgname)) != SP_ERROR_NONE)
334 pCtx->ThrowNativeErrorEx(err, NULL);
338 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
340 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
343 pCtx->LocalToPhysAddr(params[2], &cl_array);
345 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
348 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
351 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
352 g_IsMsgInExec = true;
354 return g_CurMsgHandle;
357 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
361 int msgid = params[1];
365 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
368 if (msgid < 0 || msgid >= 255)
370 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
373 pCtx->LocalToPhysAddr(params[2], &cl_array);
375 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
378 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
381 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
382 g_IsMsgInExec = true;
384 return g_CurMsgHandle;
387 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
393 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
396 g_UserMsgs.EndMessage();
398 sec.pOwner = pCtx->GetIdentity();
399 sec.pIdentity = g_pCoreIdent;
400 g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
402 g_IsMsgInExec = false;
407 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
409 IPluginFunction *pHook, *pNotify;
410 MsgListenerWrapper *pListener;
412 int msgid = params[1];
414 if (msgid < 0 || msgid >= 255)
416 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
419 pHook = pCtx->GetFunctionById(params[2]);
422 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
424 pNotify = pCtx->GetFunctionById(params[4]);
425 intercept = (params[3]) ? true : false;
427 pListener = s_UsrMessageNatives.CreateListener(pCtx);
428 pListener->Initialize(msgid, pHook, pNotify, intercept);
430 g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
435 static cell_t smn_UnHookUserMessage(IPluginContext *pCtx, const cell_t *params)
437 IPluginFunction *pFunc;
438 MsgListenerWrapper *pListener;
441 int msgid = params[1];
443 if (msgid < 0 || msgid >= 255)
445 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
448 pFunc = pCtx->GetFunctionById(params[2]);
451 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
453 intercept = (params[3]) ? true : false;
455 iter = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
458 return pCtx->ThrowNativeError("Unable to unhook the current user message");
462 if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
464 return pCtx->ThrowNativeError("Unable to unhook the current user message");
467 s_UsrMessageNatives.DeleteListener(pCtx, iter);
472 REGISTER_NATIVES(usrmsgnatives)
474 {"GetUserMessageId", smn_GetUserMessageId},
475 {"GetUserMessageName", smn_GetUserMessageName},
476 {"StartMessage", smn_StartMessage},
477 {"StartMessageEx", smn_StartMessageEx},
478 {"EndMessage", smn_EndMessage},
479 {"HookUserMessage", smn_HookUserMessage},
480 {"UnHookUserMessage", smn_UnHookUserMessage},