User message functions now fallback on IServerGameDLL::GetUserMessageInfo() if SourceMM was unable to get the message list for some reason.
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
39 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
40 void OnSourceModAllInitialized();
41 void OnSourceModShutdown();
42 void OnHandleDestroy(HandleType_t type, void *object);
43 void OnPluginUnloaded(IPlugin *plugin);
45 MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
46 MsgWrapperIter FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
47 bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
49 CStack<MsgListenerWrapper *> m_FreeListeners;
52 UsrMessageNatives::~UsrMessageNatives()
54 CStack<MsgListenerWrapper *>::iterator iter;
55 for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
59 m_FreeListeners.popall();
62 void UsrMessageNatives::OnSourceModAllInitialized()
65 sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
67 g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
68 g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
70 g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
72 g_PluginSys.AddPluginsListener(this);
75 void UsrMessageNatives::OnSourceModShutdown()
78 sec.pIdentity = g_pCoreIdent;
80 g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
82 g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
83 g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
89 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
93 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
95 MsgWrapperList *pList;
97 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
100 MsgListenerWrapper *pListener;
102 for (iter=pList->begin(); iter!=pList->end(); iter++)
105 if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
107 m_FreeListeners.push(pListener);
115 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
117 MsgWrapperList *pList;
118 MsgListenerWrapper *pListener;
119 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
121 if (m_FreeListeners.empty())
123 pListener = new MsgListenerWrapper;
125 pListener = m_FreeListeners.front();
126 m_FreeListeners.pop();
129 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
131 pList = new List<MsgListenerWrapper *>;
132 pl->SetProperty("MsgListeners", pList);
135 pList->push_back(pListener);
140 MsgWrapperIter UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
142 MsgWrapperList *pList;
144 MsgListenerWrapper *pListener;
145 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
147 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
152 for (iter=pList->begin(); iter!=pList->end(); iter++)
155 if ((msgid == pListener->GetMessageId())
156 && (intercept == pListener->IsInterceptHook())
157 && (pHook == pListener->GetHookedFunction()))
166 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
168 MsgWrapperList *pList;
169 MsgListenerWrapper *pListener;
170 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
172 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
179 m_FreeListeners.push(pListener);
184 /***************************************
186 * USER MESSAGE WRAPPER IMPLEMENTATION *
188 ***************************************/
190 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
209 m_IsInterceptHook = intercept;
212 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
214 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
216 for (size_t i=0; i<size; i++)
218 pl_array[i] = pFilter->GetRecipientIndex(i);
224 bool MsgListenerWrapper::IsInterceptHook() const
226 return m_IsInterceptHook;
229 int MsgListenerWrapper::GetMessageId() const
234 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
244 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
249 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
252 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
254 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
256 m_Hook->PushCell(msg_id);
257 m_Hook->PushCell(g_ReadBufHandle);
258 m_Hook->PushArray(g_MsgPlayers, size);
259 m_Hook->PushCell(size);
260 m_Hook->PushCell(pFilter->IsReliable());
261 m_Hook->PushCell(pFilter->IsInitMessage());
262 m_Hook->Execute(&res);
265 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
267 cell_t res = static_cast<cell_t>(Pl_Continue);
268 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
270 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
272 m_Intercept->PushCell(msg_id);
273 m_Intercept->PushCell(g_ReadBufHandle);
274 m_Intercept->PushArray(g_MsgPlayers, size);
275 m_Intercept->PushCell(size);
276 m_Intercept->PushCell(pFilter->IsReliable());
277 m_Intercept->PushCell(pFilter->IsInitMessage());
278 m_Intercept->Execute(&res);
280 return static_cast<ResultType>(res);
283 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
291 m_Notify->PushCell(msg_id);
292 m_Notify->Execute(&res);
295 /***************************************
297 * USER MESSAGE NATIVE IMPLEMENTATIONS *
299 ***************************************/
301 static UsrMessageNatives s_UsrMessageNatives;
303 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
306 pCtx->LocalToString(params[1], &msgname);
308 return g_UserMsgs.GetMessageIndex(msgname);
311 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
315 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
317 return 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 pCtx->LocalToString(params[1], &msgname);
334 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
336 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
339 pCtx->LocalToPhysAddr(params[2], &cl_array);
341 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
344 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
347 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
348 g_IsMsgInExec = true;
350 return g_CurMsgHandle;
353 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
357 int msgid = params[1];
361 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
364 if (msgid < 0 || msgid >= 255)
366 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
369 pCtx->LocalToPhysAddr(params[2], &cl_array);
371 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
374 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
377 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
378 g_IsMsgInExec = true;
380 return g_CurMsgHandle;
383 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
389 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
392 g_UserMsgs.EndMessage();
394 sec.pOwner = pCtx->GetIdentity();
395 sec.pIdentity = g_pCoreIdent;
396 g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
398 g_IsMsgInExec = false;
403 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
405 IPluginFunction *pHook, *pNotify;
406 MsgListenerWrapper *pListener;
408 int msgid = params[1];
410 if (msgid < 0 || msgid >= 255)
412 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
415 pHook = pCtx->GetFunctionById(params[2]);
418 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
420 pNotify = pCtx->GetFunctionById(params[4]);
421 intercept = (params[3]) ? true : false;
423 pListener = s_UsrMessageNatives.CreateListener(pCtx);
424 pListener->Initialize(msgid, pHook, pNotify, intercept);
426 g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
431 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
433 IPluginFunction *pFunc;
434 MsgListenerWrapper *pListener;
437 int msgid = params[1];
439 if (msgid < 0 || msgid >= 255)
441 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
444 pFunc = pCtx->GetFunctionById(params[2]);
447 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
449 intercept = (params[3]) ? true : false;
451 iter = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
454 return pCtx->ThrowNativeError("Unable to unhook the current user message");
458 if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
460 return pCtx->ThrowNativeError("Unable to unhook the current user message");
463 s_UsrMessageNatives.DeleteListener(pCtx, iter);
468 REGISTER_NATIVES(usrmsgnatives)
470 {"GetUserMessageId", smn_GetUserMessageId},
471 {"GetUserMessageName", smn_GetUserMessageName},
472 {"StartMessage", smn_StartMessage},
473 {"StartMessageEx", smn_StartMessageEx},
474 {"EndMessage", smn_EndMessage},
475 {"HookUserMessage", smn_HookUserMessage},
476 {"UnhookUserMessage", smn_UnhookUserMessage},