3 * =============================================================================
5 * Copyright (C) 2004-2008 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 "PlayerManager.h"
35 #include "smn_usermsgs.h"
36 #ifdef USE_PROTOBUF_USERMESSAGES
37 #include "UserMessagePBHelpers.h"
40 HandleType_t g_ProtobufType = NO_HANDLE_TYPE;
41 HandleType_t g_WrBitBufType = NO_HANDLE_TYPE;
42 HandleType_t g_RdBitBufType = NO_HANDLE_TYPE;
44 Handle_t g_CurMsgHandle;
46 #ifndef USE_PROTOBUF_USERMESSAGES
47 Handle_t g_ReadBufHandle;
51 int g_MsgPlayers[256];
52 bool g_IsMsgInExec = false;
54 typedef List<MsgListenerWrapper *> MsgWrapperList;
55 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
57 class UsrMessageNatives :
59 public IHandleTypeDispatch,
60 public IPluginsListener
64 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
65 void OnSourceModAllInitialized();
66 void OnSourceModShutdown();
67 void OnHandleDestroy(HandleType_t type, void *object);
68 bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
69 void OnPluginUnloaded(IPlugin *plugin);
71 MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
72 bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
73 bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
75 CStack<MsgListenerWrapper *> m_FreeListeners;
78 UsrMessageNatives::~UsrMessageNatives()
80 CStack<MsgListenerWrapper *>::iterator iter;
81 for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
85 m_FreeListeners.popall();
88 void UsrMessageNatives::OnSourceModAllInitialized()
92 g_HandleSys.InitAccessDefaults(NULL, &sec);
93 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
95 #ifdef USE_PROTOBUF_USERMESSAGES
96 g_ProtobufType = g_HandleSys.CreateType("ProtobufUM", this, 0, NULL, NULL, g_pCoreIdent, NULL);
98 g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
99 g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
101 g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
104 g_PluginSys.AddPluginsListener(this);
107 void UsrMessageNatives::OnSourceModShutdown()
110 sec.pIdentity = g_pCoreIdent;
112 #ifdef USE_PROTOBUF_USERMESSAGES
113 g_HandleSys.RemoveType(g_ProtobufType, g_pCoreIdent);
117 g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
119 g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
120 g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
127 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
129 #ifdef USE_PROTOBUF_USERMESSAGES
130 delete (SMProtobufMessage *)object;
134 bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
136 #ifdef USE_PROTOBUF_USERMESSAGES
137 // Different messages have different sizes, but this works as an approximate
138 *pSize = sizeof(protobuf::Message) + sizeof(SMProtobufMessage);
140 *pSize = sizeof(bf_read);
146 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
148 MsgWrapperList *pList;
150 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
153 MsgListenerWrapper *pListener;
155 for (iter=pList->begin(); iter!=pList->end(); iter++)
158 if (g_UserMsgs.UnhookUserMessage2(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
160 m_FreeListeners.push(pListener);
168 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
170 MsgWrapperList *pList;
171 MsgListenerWrapper *pListener;
172 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
174 if (m_FreeListeners.empty())
176 pListener = new MsgListenerWrapper;
178 pListener = m_FreeListeners.front();
179 m_FreeListeners.pop();
182 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
184 pList = new List<MsgListenerWrapper *>;
185 pl->SetProperty("MsgListeners", pList);
188 pList->push_back(pListener);
193 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
195 MsgWrapperList *pList;
196 MsgWrapperIter _iter;
197 MsgListenerWrapper *pListener;
198 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
200 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
205 for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
207 pListener = (*_iter);
208 if ((msgid == pListener->GetMessageId())
209 && (intercept == pListener->IsInterceptHook())
210 && (pHook == pListener->GetHookedFunction()))
220 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
222 MsgWrapperList *pList;
223 MsgListenerWrapper *pListener;
224 IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
226 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
233 m_FreeListeners.push(pListener);
238 /***************************************
240 * USER MESSAGE WRAPPER IMPLEMENTATION *
242 ***************************************/
244 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
263 m_IsInterceptHook = intercept;
266 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
268 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
270 for (size_t i=0; i<size; i++)
272 pl_array[i] = pFilter->GetRecipientIndex(i);
278 bool MsgListenerWrapper::IsInterceptHook() const
280 return m_IsInterceptHook;
283 int MsgListenerWrapper::GetMessageId() const
288 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
298 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
303 #ifdef USE_PROTOBUF_USERMESSAGES
304 void MsgListenerWrapper::OnUserMessage(int msg_id, protobuf::Message *msg, IRecipientFilter *pFilter)
306 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
311 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
313 #ifdef USE_PROTOBUF_USERMESSAGES
314 hndl = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), NULL, g_pCoreIdent, NULL);
316 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
317 hndl = g_ReadBufHandle;
320 m_Hook->PushCell(msg_id);
321 m_Hook->PushCell(hndl);
322 m_Hook->PushArray(g_MsgPlayers, size);
323 m_Hook->PushCell(size);
324 m_Hook->PushCell(pFilter->IsReliable());
325 m_Hook->PushCell(pFilter->IsInitMessage());
326 m_Hook->Execute(&res);
328 #ifdef USE_PROTOBUF_USERMESSAGES
330 sec.pIdentity = g_pCoreIdent;
331 g_HandleSys.FreeHandle(hndl, &sec);
335 #ifdef USE_PROTOBUF_USERMESSAGES
336 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, protobuf::Message *msg, IRecipientFilter *pFilter)
338 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
342 cell_t res = static_cast<cell_t>(Pl_Continue);
343 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
345 #ifdef USE_PROTOBUF_USERMESSAGES
346 hndl = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), NULL, g_pCoreIdent, NULL);
348 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
349 hndl = g_ReadBufHandle;
352 m_Intercept->PushCell(msg_id);
353 m_Intercept->PushCell(hndl);
354 m_Intercept->PushArray(g_MsgPlayers, size);
355 m_Intercept->PushCell(size);
356 m_Intercept->PushCell(pFilter->IsReliable());
357 m_Intercept->PushCell(pFilter->IsInitMessage());
358 m_Intercept->Execute(&res);
360 #ifdef USE_PROTOBUF_USERMESSAGES
362 sec.pIdentity = g_pCoreIdent;
363 g_HandleSys.FreeHandle(hndl, &sec);
366 return static_cast<ResultType>(res);
369 void MsgListenerWrapper::OnPostUserMessage(int msg_id, bool sent)
377 m_Notify->PushCell(msg_id);
378 m_Notify->PushCell(sent ? 1 : 0);
379 m_Notify->Execute(&res);
382 /***************************************
384 * USER MESSAGE NATIVE IMPLEMENTATIONS *
386 ***************************************/
388 static UsrMessageNatives s_UsrMessageNatives;
390 static cell_t smn_GetUserMessageType(IPluginContext *pCtx, const cell_t *params)
392 return g_UserMsgs.GetUserMessageType();
395 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
398 pCtx->LocalToString(params[1], &msgname);
400 return g_UserMsgs.GetMessageIndex(msgname);
403 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
407 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
409 return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
412 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
416 unsigned int numClients;
419 CPlayer *pPlayer = NULL;
423 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
426 pCtx->LocalToString(params[1], &msgname);
428 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
430 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
433 pCtx->LocalToPhysAddr(params[2], &cl_array);
435 numClients = params[3];
437 /* Client validation */
438 for (unsigned int i = 0; i < numClients; i++)
440 client = cl_array[i];
441 pPlayer = g_Players.GetPlayerByIndex(client);
445 return pCtx->ThrowNativeError("Client index %d is invalid", client);
446 } else if (!pPlayer->IsConnected()) {
447 return pCtx->ThrowNativeError("Client %d is not connected", client);
451 #ifdef USE_PROTOBUF_USERMESSAGES
452 protobuf::Message *msg = g_UserMsgs.StartProtobufMessage(msgid, cl_array, numClients, params[4]);
455 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
458 g_CurMsgHandle = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), pCtx->GetIdentity(), g_pCoreIdent, NULL);
460 bf_write *pBitBuf = g_UserMsgs.StartBitBufMessage(msgid, cl_array, numClients, params[4]);
463 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
466 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
469 g_IsMsgInExec = true;
471 return g_CurMsgHandle;
474 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
477 unsigned int numClients;
479 CPlayer *pPlayer = NULL;
480 int msgid = params[1];
484 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
487 if (msgid < 0 || msgid >= 255)
489 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
492 pCtx->LocalToPhysAddr(params[2], &cl_array);
494 numClients = params[3];
496 /* Client validation */
497 for (unsigned int i = 0; i < numClients; i++)
499 client = cl_array[i];
500 pPlayer = g_Players.GetPlayerByIndex(client);
504 return pCtx->ThrowNativeError("Client index %d is invalid", client);
505 } else if (!pPlayer->IsConnected()) {
506 return pCtx->ThrowNativeError("Client %d is not connected", client);
510 #ifdef USE_PROTOBUF_USERMESSAGES
511 protobuf::Message *msg = g_UserMsgs.StartProtobufMessage(msgid, cl_array, numClients, params[4]);
514 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
517 g_CurMsgHandle = g_HandleSys.CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), pCtx->GetIdentity(), g_pCoreIdent, NULL);
519 bf_write *pBitBuf = g_UserMsgs.StartBitBufMessage(msgid, cl_array, numClients, params[4]);
522 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
525 g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
527 g_IsMsgInExec = true;
529 return g_CurMsgHandle;
532 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
538 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
541 g_UserMsgs.EndMessage();
543 sec.pOwner = pCtx->GetIdentity();
544 sec.pIdentity = g_pCoreIdent;
545 g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
547 g_IsMsgInExec = false;
552 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
554 IPluginFunction *pHook, *pNotify;
555 MsgListenerWrapper *pListener;
557 int msgid = params[1];
559 if (msgid < 0 || msgid >= 255)
561 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
564 pHook = pCtx->GetFunctionById(params[2]);
567 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
569 pNotify = pCtx->GetFunctionById(params[4]);
570 intercept = (params[3]) ? true : false;
572 pListener = s_UsrMessageNatives.CreateListener(pCtx);
573 pListener->Initialize(msgid, pHook, pNotify, intercept);
575 g_UserMsgs.HookUserMessage2(msgid, pListener, intercept);
580 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
582 IPluginFunction *pFunc;
583 MsgListenerWrapper *pListener;
586 int msgid = params[1];
588 if (msgid < 0 || msgid >= 255)
590 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
593 pFunc = pCtx->GetFunctionById(params[2]);
596 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
598 intercept = (params[3]) ? true : false;
600 if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
602 return pCtx->ThrowNativeError("Unable to unhook the current user message");
606 if (!g_UserMsgs.UnhookUserMessage2(msgid, pListener, intercept))
608 return pCtx->ThrowNativeError("Unable to unhook the current user message");
611 s_UsrMessageNatives.DeleteListener(pCtx, iter);
616 REGISTER_NATIVES(usrmsgnatives)
618 {"GetUserMessageType", smn_GetUserMessageType},
619 {"GetUserMessageId", smn_GetUserMessageId},
620 {"GetUserMessageName", smn_GetUserMessageName},
621 {"StartMessage", smn_StartMessage},
622 {"StartMessageEx", smn_StartMessageEx},
623 {"EndMessage", smn_EndMessage},
624 {"HookUserMessage", smn_HookUserMessage},
625 {"UnhookUserMessage", smn_UnhookUserMessage},