3 * ================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ================================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License,
10 * version 3.0, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to
21 * link the code of this program (as well as its derivative works) to
22 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
23 * Game MODs that run on software by the Valve Corporation. You must
24 * obey the GNU General Public License in all respects for all other
25 * code used. Additionally, AlliedModders LLC grants this exception
26 * to all derivative works. AlliedModders LLC defines further
27 * exceptions, found in LICENSE.txt (as of this writing, version
28 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
33 #include "HandleSys.h"
34 #include "PluginSys.h"
35 #include "UserMessages.h"
36 #include "smn_usermsgs.h"
38 HandleType_t g_WrBitBufType;
39 HandleType_t g_RdBitBufType;
40 Handle_t g_CurMsgHandle;
41 Handle_t g_ReadBufHandle;
44 int g_MsgPlayers[256];
45 bool g_IsMsgInExec = false;
47 typedef List<MsgListenerWrapper *> MsgWrapperList;
48 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
50 class UsrMessageNatives :
52 public IHandleTypeDispatch,
53 public IPluginsListener
57 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
58 void OnSourceModAllInitialized();
59 void OnSourceModShutdown();
60 void OnHandleDestroy(HandleType_t type, void *object);
61 void OnPluginUnloaded(IPlugin *plugin);
63 MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
64 MsgWrapperIter FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
65 bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
67 CStack<MsgListenerWrapper *> m_FreeListeners;
70 UsrMessageNatives::~UsrMessageNatives()
72 CStack<MsgListenerWrapper *>::iterator iter;
73 for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
77 m_FreeListeners.popall();
80 void UsrMessageNatives::OnSourceModAllInitialized()
83 sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
85 g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
86 g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
88 g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
90 g_PluginSys.AddPluginsListener(this);
93 void UsrMessageNatives::OnSourceModShutdown()
96 sec.pIdentity = g_pCoreIdent;
98 g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
100 g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
101 g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
107 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
111 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
113 MsgWrapperList *pList;
115 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
118 MsgListenerWrapper *pListener;
120 for (iter=pList->begin(); iter!=pList->end(); iter++)
123 if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
125 m_FreeListeners.push(pListener);
133 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
135 MsgWrapperList *pList;
136 MsgListenerWrapper *pListener;
137 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
139 if (m_FreeListeners.empty())
141 pListener = new MsgListenerWrapper;
143 pListener = m_FreeListeners.front();
144 m_FreeListeners.pop();
147 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
149 pList = new List<MsgListenerWrapper *>;
150 pl->SetProperty("MsgListeners", pList);
153 pList->push_back(pListener);
158 MsgWrapperIter UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
160 MsgWrapperList *pList;
162 MsgListenerWrapper *pListener;
163 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
165 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
170 for (iter=pList->begin(); iter!=pList->end(); iter++)
173 if ((msgid == pListener->GetMessageId())
174 && (intercept == pListener->IsInterceptHook())
175 && (pHook == pListener->GetHookedFunction()))
184 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
186 MsgWrapperList *pList;
187 MsgListenerWrapper *pListener;
188 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
190 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
197 m_FreeListeners.push(pListener);
202 /***************************************
204 * USER MESSAGE WRAPPER IMPLEMENTATION *
206 ***************************************/
208 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
227 m_IsInterceptHook = intercept;
230 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
232 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
234 for (size_t i=0; i<size; i++)
236 pl_array[i] = pFilter->GetRecipientIndex(i);
242 bool MsgListenerWrapper::IsInterceptHook() const
244 return m_IsInterceptHook;
247 int MsgListenerWrapper::GetMessageId() const
252 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
262 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
267 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
270 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
272 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
274 m_Hook->PushCell(msg_id);
275 m_Hook->PushCell(g_ReadBufHandle);
276 m_Hook->PushArray(g_MsgPlayers, size);
277 m_Hook->PushCell(size);
278 m_Hook->PushCell(pFilter->IsReliable());
279 m_Hook->PushCell(pFilter->IsInitMessage());
280 m_Hook->Execute(&res);
283 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
285 cell_t res = static_cast<cell_t>(Pl_Continue);
286 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
288 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
290 m_Intercept->PushCell(msg_id);
291 m_Intercept->PushCell(g_ReadBufHandle);
292 m_Intercept->PushArray(g_MsgPlayers, size);
293 m_Intercept->PushCell(size);
294 m_Intercept->PushCell(pFilter->IsReliable());
295 m_Intercept->PushCell(pFilter->IsInitMessage());
296 m_Intercept->Execute(&res);
298 return static_cast<ResultType>(res);
301 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
309 m_Notify->PushCell(msg_id);
310 m_Notify->Execute(&res);
313 /***************************************
315 * USER MESSAGE NATIVE IMPLEMENTATIONS *
317 ***************************************/
319 static UsrMessageNatives s_UsrMessageNatives;
321 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
324 pCtx->LocalToString(params[1], &msgname);
326 return g_UserMsgs.GetMessageIndex(msgname);
329 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
333 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
335 return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
338 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
347 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
350 pCtx->LocalToString(params[1], &msgname);
352 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
354 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
357 pCtx->LocalToPhysAddr(params[2], &cl_array);
359 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
362 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
365 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
366 g_IsMsgInExec = true;
368 return g_CurMsgHandle;
371 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
375 int msgid = params[1];
379 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
382 if (msgid < 0 || msgid >= 255)
384 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
387 pCtx->LocalToPhysAddr(params[2], &cl_array);
389 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
392 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
395 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
396 g_IsMsgInExec = true;
398 return g_CurMsgHandle;
401 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
407 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
410 g_UserMsgs.EndMessage();
412 sec.pOwner = pCtx->GetIdentity();
413 sec.pIdentity = g_pCoreIdent;
414 g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
416 g_IsMsgInExec = false;
421 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
423 IPluginFunction *pHook, *pNotify;
424 MsgListenerWrapper *pListener;
426 int msgid = params[1];
428 if (msgid < 0 || msgid >= 255)
430 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
433 pHook = pCtx->GetFunctionById(params[2]);
436 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
438 pNotify = pCtx->GetFunctionById(params[4]);
439 intercept = (params[3]) ? true : false;
441 pListener = s_UsrMessageNatives.CreateListener(pCtx);
442 pListener->Initialize(msgid, pHook, pNotify, intercept);
444 g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
449 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
451 IPluginFunction *pFunc;
452 MsgListenerWrapper *pListener;
455 int msgid = params[1];
457 if (msgid < 0 || msgid >= 255)
459 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
462 pFunc = pCtx->GetFunctionById(params[2]);
465 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
467 intercept = (params[3]) ? true : false;
469 iter = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
472 return pCtx->ThrowNativeError("Unable to unhook the current user message");
476 if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
478 return pCtx->ThrowNativeError("Unable to unhook the current user message");
481 s_UsrMessageNatives.DeleteListener(pCtx, iter);
486 REGISTER_NATIVES(usrmsgnatives)
488 {"GetUserMessageId", smn_GetUserMessageId},
489 {"GetUserMessageName", smn_GetUserMessageName},
490 {"StartMessage", smn_StartMessage},
491 {"StartMessageEx", smn_StartMessageEx},
492 {"EndMessage", smn_EndMessage},
493 {"HookUserMessage", smn_HookUserMessage},
494 {"UnhookUserMessage", smn_UnhookUserMessage},