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 "PlayerManager.h"
33 #include "smn_usermsgs.h"
34 #include "logic_bridge.h"
35 #ifdef USE_PROTOBUF_USERMESSAGES
36 #include "UserMessagePBHelpers.h"
39 HandleType_t g_ProtobufType = NO_HANDLE_TYPE;
40 HandleType_t g_WrBitBufType = NO_HANDLE_TYPE;
41 HandleType_t g_RdBitBufType = NO_HANDLE_TYPE;
43 Handle_t g_CurMsgHandle;
45 #ifndef USE_PROTOBUF_USERMESSAGES
46 Handle_t g_ReadBufHandle;
50 int g_MsgPlayers[256];
51 bool g_IsMsgInExec = false;
53 typedef List<MsgListenerWrapper *> MsgWrapperList;
54 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
56 class UsrMessageNatives :
58 public IHandleTypeDispatch,
59 public IPluginsListener
63 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
64 void OnSourceModAllInitialized();
65 void OnSourceModShutdown();
66 void OnHandleDestroy(HandleType_t type, void *object);
67 bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
68 void OnPluginUnloaded(IPlugin *plugin);
70 MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
71 bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
72 bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
74 CStack<MsgListenerWrapper *> m_FreeListeners;
77 UsrMessageNatives::~UsrMessageNatives()
79 CStack<MsgListenerWrapper *>::iterator iter;
80 for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
84 m_FreeListeners.popall();
87 void UsrMessageNatives::OnSourceModAllInitialized()
91 handlesys->InitAccessDefaults(NULL, &sec);
92 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
94 #ifdef USE_PROTOBUF_USERMESSAGES
95 g_ProtobufType = handlesys->CreateType("ProtobufUM", this, 0, NULL, NULL, g_pCoreIdent, NULL);
97 g_WrBitBufType = handlesys->CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
98 g_RdBitBufType = handlesys->CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
100 g_ReadBufHandle = handlesys->CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
103 scripts->AddPluginsListener(this);
106 void UsrMessageNatives::OnSourceModShutdown()
109 sec.pIdentity = g_pCoreIdent;
111 #ifdef USE_PROTOBUF_USERMESSAGES
112 handlesys->RemoveType(g_ProtobufType, g_pCoreIdent);
116 handlesys->FreeHandle(g_ReadBufHandle, &sec);
118 handlesys->RemoveType(g_WrBitBufType, g_pCoreIdent);
119 handlesys->RemoveType(g_RdBitBufType, g_pCoreIdent);
126 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
128 #ifdef USE_PROTOBUF_USERMESSAGES
129 delete (SMProtobufMessage *)object;
133 bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
135 #ifdef USE_PROTOBUF_USERMESSAGES
136 // Different messages have different sizes, but this works as an approximate
137 *pSize = sizeof(protobuf::Message) + sizeof(SMProtobufMessage);
139 *pSize = sizeof(bf_read);
145 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
147 MsgWrapperList *pList;
149 if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
152 MsgListenerWrapper *pListener;
154 for (iter=pList->begin(); iter!=pList->end(); iter++)
157 if (g_UserMsgs.UnhookUserMessage2(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
159 m_FreeListeners.push(pListener);
167 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
169 MsgWrapperList *pList;
170 MsgListenerWrapper *pListener;
171 IPlugin *pl = scripts->FindPluginByContext(pCtx->GetContext());
173 if (m_FreeListeners.empty())
175 pListener = new MsgListenerWrapper;
177 pListener = m_FreeListeners.front();
178 m_FreeListeners.pop();
181 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
183 pList = new List<MsgListenerWrapper *>;
184 pl->SetProperty("MsgListeners", pList);
187 pList->push_back(pListener);
192 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
194 MsgWrapperList *pList;
195 MsgWrapperIter _iter;
196 MsgListenerWrapper *pListener;
197 IPlugin *pl = scripts->FindPluginByContext(pCtx->GetContext());
199 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
204 for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
206 pListener = (*_iter);
207 if ((msgid == pListener->GetMessageId())
208 && (intercept == pListener->IsInterceptHook())
209 && (pHook == pListener->GetHookedFunction()))
219 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
221 MsgWrapperList *pList;
222 MsgListenerWrapper *pListener;
223 IPlugin *pl = scripts->FindPluginByContext(pCtx->GetContext());
225 if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
232 m_FreeListeners.push(pListener);
237 /***************************************
239 * USER MESSAGE WRAPPER IMPLEMENTATION *
241 ***************************************/
243 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
262 m_IsInterceptHook = intercept;
265 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
267 size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
269 for (size_t i=0; i<size; i++)
272 #if SOURCE_ENGINE == SE_DOTA
273 index = pFilter->GetRecipientIndex(i).Get();
275 index = pFilter->GetRecipientIndex(i);
283 bool MsgListenerWrapper::IsInterceptHook() const
285 return m_IsInterceptHook;
288 int MsgListenerWrapper::GetMessageId() const
293 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
303 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
308 #ifdef USE_PROTOBUF_USERMESSAGES
309 void MsgListenerWrapper::OnUserMessage(int msg_id, protobuf::Message *msg, IRecipientFilter *pFilter)
311 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
316 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
318 #ifdef USE_PROTOBUF_USERMESSAGES
319 hndl = handlesys->CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), NULL, g_pCoreIdent, NULL);
321 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
322 hndl = g_ReadBufHandle;
325 m_Hook->PushCell(msg_id);
326 m_Hook->PushCell(hndl);
327 m_Hook->PushArray(g_MsgPlayers, size);
328 m_Hook->PushCell(size);
329 m_Hook->PushCell(pFilter->IsReliable());
330 m_Hook->PushCell(pFilter->IsInitMessage());
331 m_Hook->Execute(&res);
333 #ifdef USE_PROTOBUF_USERMESSAGES
335 sec.pIdentity = g_pCoreIdent;
336 handlesys->FreeHandle(hndl, &sec);
340 #ifdef USE_PROTOBUF_USERMESSAGES
341 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, protobuf::Message *msg, IRecipientFilter *pFilter)
343 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
347 cell_t res = static_cast<cell_t>(Pl_Continue);
348 size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
350 #ifdef USE_PROTOBUF_USERMESSAGES
351 hndl = handlesys->CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), NULL, g_pCoreIdent, NULL);
353 g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
354 hndl = g_ReadBufHandle;
357 m_Intercept->PushCell(msg_id);
358 m_Intercept->PushCell(hndl);
359 m_Intercept->PushArray(g_MsgPlayers, size);
360 m_Intercept->PushCell(size);
361 m_Intercept->PushCell(pFilter->IsReliable());
362 m_Intercept->PushCell(pFilter->IsInitMessage());
363 m_Intercept->Execute(&res);
365 #ifdef USE_PROTOBUF_USERMESSAGES
367 sec.pIdentity = g_pCoreIdent;
368 handlesys->FreeHandle(hndl, &sec);
371 return static_cast<ResultType>(res);
374 void MsgListenerWrapper::OnPostUserMessage(int msg_id, bool sent)
382 m_Notify->PushCell(msg_id);
383 m_Notify->PushCell(sent ? 1 : 0);
384 m_Notify->Execute(&res);
387 /***************************************
389 * USER MESSAGE NATIVE IMPLEMENTATIONS *
391 ***************************************/
393 static UsrMessageNatives s_UsrMessageNatives;
395 static cell_t smn_GetUserMessageType(IPluginContext *pCtx, const cell_t *params)
397 return g_UserMsgs.GetUserMessageType();
400 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
403 pCtx->LocalToString(params[1], &msgname);
405 return g_UserMsgs.GetMessageIndex(msgname);
408 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
412 pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
414 return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
417 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
421 unsigned int numClients;
424 CPlayer *pPlayer = NULL;
428 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
431 pCtx->LocalToString(params[1], &msgname);
433 if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
435 return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
438 pCtx->LocalToPhysAddr(params[2], &cl_array);
440 numClients = params[3];
442 /* Client validation */
443 for (unsigned int i = 0; i < numClients; i++)
445 client = cl_array[i];
446 pPlayer = g_Players.GetPlayerByIndex(client);
450 return pCtx->ThrowNativeError("Client index %d is invalid", client);
451 } else if (!pPlayer->IsConnected()) {
452 return pCtx->ThrowNativeError("Client %d is not connected", client);
456 #ifdef USE_PROTOBUF_USERMESSAGES
457 protobuf::Message *msg = g_UserMsgs.StartProtobufMessage(msgid, cl_array, numClients, params[4]);
460 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
463 g_CurMsgHandle = handlesys->CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), pCtx->GetIdentity(), g_pCoreIdent, NULL);
465 bf_write *pBitBuf = g_UserMsgs.StartBitBufMessage(msgid, cl_array, numClients, params[4]);
468 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
471 g_CurMsgHandle = handlesys->CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
474 g_IsMsgInExec = true;
476 return g_CurMsgHandle;
479 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
482 unsigned int numClients;
484 CPlayer *pPlayer = NULL;
485 int msgid = params[1];
489 return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
492 if (msgid < 0 || msgid >= 255)
494 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
497 pCtx->LocalToPhysAddr(params[2], &cl_array);
499 numClients = params[3];
501 /* Client validation */
502 for (unsigned int i = 0; i < numClients; i++)
504 client = cl_array[i];
505 pPlayer = g_Players.GetPlayerByIndex(client);
509 return pCtx->ThrowNativeError("Client index %d is invalid", client);
510 } else if (!pPlayer->IsConnected()) {
511 return pCtx->ThrowNativeError("Client %d is not connected", client);
515 #ifdef USE_PROTOBUF_USERMESSAGES
516 protobuf::Message *msg = g_UserMsgs.StartProtobufMessage(msgid, cl_array, numClients, params[4]);
519 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
522 g_CurMsgHandle = handlesys->CreateHandle(g_ProtobufType, new SMProtobufMessage(msg), pCtx->GetIdentity(), g_pCoreIdent, NULL);
524 bf_write *pBitBuf = g_UserMsgs.StartBitBufMessage(msgid, cl_array, numClients, params[4]);
527 return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
530 g_CurMsgHandle = handlesys->CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
532 g_IsMsgInExec = true;
534 return g_CurMsgHandle;
537 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
543 return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
546 g_UserMsgs.EndMessage();
548 sec.pOwner = pCtx->GetIdentity();
549 sec.pIdentity = g_pCoreIdent;
550 handlesys->FreeHandle(g_CurMsgHandle, &sec);
552 g_IsMsgInExec = false;
557 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
559 IPluginFunction *pHook, *pNotify;
560 MsgListenerWrapper *pListener;
562 int msgid = params[1];
564 if (msgid < 0 || msgid >= 255)
566 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
569 pHook = pCtx->GetFunctionById(params[2]);
572 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
574 pNotify = pCtx->GetFunctionById(params[4]);
575 intercept = (params[3]) ? true : false;
577 pListener = s_UsrMessageNatives.CreateListener(pCtx);
578 pListener->Initialize(msgid, pHook, pNotify, intercept);
580 g_UserMsgs.HookUserMessage2(msgid, pListener, intercept);
585 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
587 IPluginFunction *pFunc;
588 MsgListenerWrapper *pListener;
591 int msgid = params[1];
593 if (msgid < 0 || msgid >= 255)
595 return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
598 pFunc = pCtx->GetFunctionById(params[2]);
601 return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
603 intercept = (params[3]) ? true : false;
605 if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
607 return pCtx->ThrowNativeError("Unable to unhook the current user message");
611 if (!g_UserMsgs.UnhookUserMessage2(msgid, pListener, intercept))
613 return pCtx->ThrowNativeError("Unable to unhook the current user message");
616 s_UsrMessageNatives.DeleteListener(pCtx, iter);
621 REGISTER_NATIVES(usrmsgnatives)
623 {"GetUserMessageType", smn_GetUserMessageType},
624 {"GetUserMessageId", smn_GetUserMessageId},
625 {"GetUserMessageName", smn_GetUserMessageName},
626 {"StartMessage", smn_StartMessage},
627 {"StartMessageEx", smn_StartMessageEx},
628 {"EndMessage", smn_EndMessage},
629 {"HookUserMessage", smn_HookUserMessage},
630 {"UnhookUserMessage", smn_UnhookUserMessage},