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 "CUserMessages.h"
18 #include "smn_usermsgs.h"
20 HandleType_t g_WrBitBufType;
21 Handle_t g_CurMsgHandle;
22 int g_MsgPlayers[256];
23 bool g_IsMsgInExec = false;
25 class UsrMessageNatives :
27 public IHandleTypeDispatch,
28 public IPluginsListener
30 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
31 void OnSourceModAllInitialized();
32 void OnSourceModShutdown();
33 void OnHandleDestroy(HandleType_t type, void *object);
34 void OnPluginUnloaded(IPlugin *plugin);
36 MsgListenerWrapper *GetNewListener(IPluginContext *pCtx);
37 MsgListenerWrapper *FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
38 bool RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept);
40 CStack<MsgListenerWrapper *> m_FreeListeners;
43 void UsrMessageNatives::OnSourceModAllInitialized()
45 g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
46 g_PluginSys.AddPluginsListener(this);
49 void UsrMessageNatives::OnSourceModShutdown()
51 g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
52 g_PluginSys.RemovePluginsListener(this);
56 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
60 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
62 List<MsgListenerWrapper *> *wrapper_list;
64 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list), true))
66 List<MsgListenerWrapper *>::iterator iter;
67 MsgListenerWrapper *listener;
69 for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++)
72 if (g_UserMsgs.UnhookUserMessage(listener->GetMessageId(), listener, listener->IsInterceptHook()))
74 m_FreeListeners.push(listener);
82 MsgListenerWrapper *UsrMessageNatives::GetNewListener(IPluginContext *pCtx)
84 MsgListenerWrapper *listener_wrapper;
86 if (m_FreeListeners.empty())
88 listener_wrapper = new MsgListenerWrapper;
90 listener_wrapper = m_FreeListeners.front();
91 m_FreeListeners.pop();
94 List<MsgListenerWrapper *> *wrapper_list;
95 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
97 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
99 wrapper_list = new List<MsgListenerWrapper *>;
100 pl->SetProperty("MsgListeners", wrapper_list);
103 wrapper_list->push_back(listener_wrapper);
105 return listener_wrapper;
108 MsgListenerWrapper *UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
110 MsgListenerWrapper *listener;
111 List<MsgListenerWrapper *> *wrapper_list;
112 List<MsgListenerWrapper *>::iterator iter;
113 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
116 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
121 for (iter=wrapper_list->begin(); iter!=wrapper_list->end(); iter++)
124 if ((msgid == listener->GetMessageId())
125 && (intercept == listener->IsInterceptHook())
126 && (pHook == listener->GetHookedFunction()))
133 return (found) ? listener : NULL;
136 bool UsrMessageNatives::RemoveListener(IPluginContext *pCtx, MsgListenerWrapper *listener, bool intercept)
138 List<MsgListenerWrapper *> *wrapper_list;
139 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
141 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&wrapper_list)))
146 wrapper_list->remove(listener);
147 m_FreeListeners.push(listener);
152 /***************************************
154 * USER MESSAGE WRAPPER IMPLEMENTATION *
156 ***************************************/
158 size_t MsgListenerWrapper::PreparePlArray(int *pl_array, IRecipientFilter *pFilter)
160 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
162 for (size_t i=0; i<size; i++)
164 pl_array[i] = pFilter->GetRecipientIndex(i);
170 bool MsgListenerWrapper::IsInterceptHook() const
172 return m_IsInterceptHook;
175 int MsgListenerWrapper::GetMessageId() const
180 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
190 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
195 void MsgListenerWrapper::InitListener(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
214 m_IsInterceptHook = intercept;
217 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
220 size_t size = PreparePlArray(g_MsgPlayers, pFilter);
222 m_Hook->PushCell(msg_id);
223 m_Hook->PushCell(0); //:TODO: push handle!
224 m_Hook->PushArray(g_MsgPlayers, size);
225 m_Hook->PushCell(size);
226 m_Hook->PushCell(pFilter->IsReliable());
227 m_Hook->PushCell(pFilter->IsInitMessage());
228 m_Hook->Execute(&res);
231 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
233 cell_t res = static_cast<cell_t>(Pl_Continue);
234 size_t size = PreparePlArray(g_MsgPlayers, pFilter);
236 m_Intercept->PushCell(msg_id);
237 m_Intercept->PushCell(0); //:TODO: push handle!
238 m_Intercept->PushArray(g_MsgPlayers, size);
239 m_Intercept->PushCell(size);
240 m_Intercept->PushCell(pFilter->IsReliable());
241 m_Intercept->PushCell(pFilter->IsInitMessage());
242 m_Intercept->Execute(&res);
244 return static_cast<ResultType>(res);
247 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
256 m_Notify->PushCell(msg_id);
257 m_Notify->Execute(&res);
260 /***************************************
262 * USER MESSAGE NATIVE IMPLEMENTATIONS *
264 ***************************************/
266 static UsrMessageNatives s_UsrMessageNatives;
268 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
272 if ((err=pCtx->LocalToString(params[1], &msgname)) != SP_ERROR_NONE)
274 pCtx->ThrowNativeErrorEx(err, NULL);
278 return g_UserMsgs.GetMessageIndex(msgname);
281 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
285 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
287 if (!g_UserMsgs.GetMessageName(params[1], msgname, params[3]))
296 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
305 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
308 if ((err=pCtx->LocalToString(params[1], &msgname)) != SP_ERROR_NONE)
310 pCtx->ThrowNativeErrorEx(err, NULL);
314 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
316 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
319 pCtx->LocalToPhysAddr(params[2], &cl_array);
321 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
323 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
324 g_IsMsgInExec = true;
326 return g_CurMsgHandle;
329 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
333 int msgid = params[1];
337 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
340 if (msgid < 0 || msgid >= 255)
342 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
345 pCtx->LocalToPhysAddr(params[2], &cl_array);
347 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
349 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
350 g_IsMsgInExec = true;
352 return g_CurMsgHandle;
355 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
361 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
364 g_UserMsgs.EndMessage();
366 sec.pOwner = pCtx->GetIdentity();
367 sec.pIdentity = g_pCoreIdent;
368 g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
370 g_IsMsgInExec = false;
375 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
377 IPluginFunction *pHook, *pNotify;
378 MsgListenerWrapper *listener;
380 int msgid = params[1];
382 if (msgid < 0 || msgid >= 255)
384 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
387 pHook = pCtx->GetFunctionById(params[2]);
390 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
392 pNotify = pCtx->GetFunctionById(params[4]);
394 intercept = (params[3]) ? true : false;
395 listener = s_UsrMessageNatives.GetNewListener(pCtx);
396 listener->InitListener(msgid, pHook, pNotify, intercept);
398 g_UserMsgs.HookUserMessage(msgid, listener, intercept);
403 static cell_t smn_UnHookUserMessage(IPluginContext *pCtx, const cell_t *params)
405 IPluginFunction *pFunc;
406 MsgListenerWrapper *listener;
408 int msgid = params[1];
410 if (msgid < 0 || msgid >= 255)
412 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
415 pFunc = pCtx->GetFunctionById(params[2]);
418 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
421 intercept = (params[3]) ? true : false;
422 listener = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
425 return pCtx->ThrowNativeError("Unable to unhook the current user message");
428 if (!g_UserMsgs.UnhookUserMessage(msgid, listener, intercept))
430 return pCtx->ThrowNativeError("Unable to unhook the current user message");
433 s_UsrMessageNatives.RemoveListener(pCtx, listener, intercept);
438 REGISTER_NATIVES(usrmsgnatives)
440 {"GetUserMessageId", smn_GetUserMessageId},
441 {"GetUserMessageName", smn_GetUserMessageName},
442 {"StartMessage", smn_StartMessage},
443 {"StartMessageEx", smn_StartMessageEx},
444 {"EndMessage", smn_EndMessage},
445 {"HookUserMessage", smn_HookUserMessage},
446 {"UnHookUserMessage", smn_UnHookUserMessage},