3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #include "HandleSys.h"
33 #include "PluginSys.h"
34 #include "UserMessages.h"
35 #include "smn_usermsgs.h"
37 HandleType_t g_WrBitBufType;
38 HandleType_t g_RdBitBufType;
39 Handle_t g_CurMsgHandle;
40 Handle_t g_ReadBufHandle;
43 int g_MsgPlayers[256];
44 bool g_IsMsgInExec = false;
46 typedef List<MsgListenerWrapper *> MsgWrapperList;
47 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
49 class UsrMessageNatives :
51 public IHandleTypeDispatch,
52 public IPluginsListener
56 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
57 void OnSourceModAllInitialized();
58 void OnSourceModShutdown();
59 void OnHandleDestroy(HandleType_t type, void *object);
60 bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
61 void OnPluginUnloaded(IPlugin *plugin);
63 MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
64 bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
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()
84 g_HandleSys.InitAccessDefaults(NULL, &sec);
85 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
87 g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
88 g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
90 g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
92 g_PluginSys.AddPluginsListener(this);
95 void UsrMessageNatives::OnSourceModShutdown()
98 sec.pIdentity = g_pCoreIdent;
100 g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
102 g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
103 g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
109 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
113 bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
115 bf_read *pRead = (bf_read *)object;
117 *pSize = sizeof(bf_read) + pRead->GetNumBytesRead() + pRead->GetNumBytesLeft();
122 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
124 MsgWrapperList *pList;
126 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
129 MsgListenerWrapper *pListener;
131 for (iter=pList->begin(); iter!=pList->end(); iter++)
134 if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
136 m_FreeListeners.push(pListener);
144 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
146 MsgWrapperList *pList;
147 MsgListenerWrapper *pListener;
148 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
150 if (m_FreeListeners.empty())
152 pListener = new MsgListenerWrapper;
154 pListener = m_FreeListeners.front();
155 m_FreeListeners.pop();
158 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
160 pList = new List<MsgListenerWrapper *>;
161 pl->SetProperty("MsgListeners", pList);
164 pList->push_back(pListener);
169 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
171 MsgWrapperList *pList;
172 MsgWrapperIter _iter;
173 MsgListenerWrapper *pListener;
174 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
176 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
181 for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
183 pListener = (*_iter);
184 if ((msgid == pListener->GetMessageId())
185 && (intercept == pListener->IsInterceptHook())
186 && (pHook == pListener->GetHookedFunction()))
196 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
198 MsgWrapperList *pList;
199 MsgListenerWrapper *pListener;
200 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
202 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
209 m_FreeListeners.push(pListener);
214 /***************************************
216 * USER MESSAGE WRAPPER IMPLEMENTATION *
218 ***************************************/
220 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
239 m_IsInterceptHook = intercept;
242 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
244 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
246 for (size_t i=0; i<size; i++)
248 pl_array[i] = pFilter->GetRecipientIndex(i);
254 bool MsgListenerWrapper::IsInterceptHook() const
256 return m_IsInterceptHook;
259 int MsgListenerWrapper::GetMessageId() const
264 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
274 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
279 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
282 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
284 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
286 m_Hook->PushCell(msg_id);
287 m_Hook->PushCell(g_ReadBufHandle);
288 m_Hook->PushArray(g_MsgPlayers, size);
289 m_Hook->PushCell(size);
290 m_Hook->PushCell(pFilter->IsReliable());
291 m_Hook->PushCell(pFilter->IsInitMessage());
292 m_Hook->Execute(&res);
295 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
297 cell_t res = static_cast<cell_t>(Pl_Continue);
298 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
300 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
302 m_Intercept->PushCell(msg_id);
303 m_Intercept->PushCell(g_ReadBufHandle);
304 m_Intercept->PushArray(g_MsgPlayers, size);
305 m_Intercept->PushCell(size);
306 m_Intercept->PushCell(pFilter->IsReliable());
307 m_Intercept->PushCell(pFilter->IsInitMessage());
308 m_Intercept->Execute(&res);
310 return static_cast<ResultType>(res);
313 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
321 m_Notify->PushCell(msg_id);
322 m_Notify->Execute(&res);
325 /***************************************
327 * USER MESSAGE NATIVE IMPLEMENTATIONS *
329 ***************************************/
331 static UsrMessageNatives s_UsrMessageNatives;
333 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
336 pCtx->LocalToString(params[1], &msgname);
338 return g_UserMsgs.GetMessageIndex(msgname);
341 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
345 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
347 return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
350 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
359 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
362 pCtx->LocalToString(params[1], &msgname);
364 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
366 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
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_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
387 int msgid = params[1];
391 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
394 if (msgid < 0 || msgid >= 255)
396 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
399 pCtx->LocalToPhysAddr(params[2], &cl_array);
401 pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
404 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
407 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
408 g_IsMsgInExec = true;
410 return g_CurMsgHandle;
413 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
419 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
422 g_UserMsgs.EndMessage();
424 sec.pOwner = pCtx->GetIdentity();
425 sec.pIdentity = g_pCoreIdent;
426 g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
428 g_IsMsgInExec = false;
433 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
435 IPluginFunction *pHook, *pNotify;
436 MsgListenerWrapper *pListener;
438 int msgid = params[1];
440 if (msgid < 0 || msgid >= 255)
442 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
445 pHook = pCtx->GetFunctionById(params[2]);
448 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
450 pNotify = pCtx->GetFunctionById(params[4]);
451 intercept = (params[3]) ? true : false;
453 pListener = s_UsrMessageNatives.CreateListener(pCtx);
454 pListener->Initialize(msgid, pHook, pNotify, intercept);
456 g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
461 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
463 IPluginFunction *pFunc;
464 MsgListenerWrapper *pListener;
467 int msgid = params[1];
469 if (msgid < 0 || msgid >= 255)
471 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
474 pFunc = pCtx->GetFunctionById(params[2]);
477 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
479 intercept = (params[3]) ? true : false;
481 if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
483 return pCtx->ThrowNativeError("Unable to unhook the current user message");
487 if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
489 return pCtx->ThrowNativeError("Unable to unhook the current user message");
492 s_UsrMessageNatives.DeleteListener(pCtx, iter);
497 REGISTER_NATIVES(usrmsgnatives)
499 {"GetUserMessageId", smn_GetUserMessageId},
500 {"GetUserMessageName", smn_GetUserMessageName},
501 {"StartMessage", smn_StartMessage},
502 {"StartMessageEx", smn_StartMessageEx},
503 {"EndMessage", smn_EndMessage},
504 {"HookUserMessage", smn_HookUserMessage},
505 {"UnhookUserMessage", smn_UnhookUserMessage},