1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/smn_usermsgs.cpp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,505 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "HandleSys.h"
1.36 +#include "PluginSys.h"
1.37 +#include "UserMessages.h"
1.38 +#include "smn_usermsgs.h"
1.39 +
1.40 +HandleType_t g_WrBitBufType;
1.41 +HandleType_t g_RdBitBufType;
1.42 +Handle_t g_CurMsgHandle;
1.43 +Handle_t g_ReadBufHandle;
1.44 +bf_read g_ReadBitBuf;
1.45 +
1.46 +int g_MsgPlayers[256];
1.47 +bool g_IsMsgInExec = false;
1.48 +
1.49 +typedef List<MsgListenerWrapper *> MsgWrapperList;
1.50 +typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
1.51 +
1.52 +class UsrMessageNatives :
1.53 + public SMGlobalClass,
1.54 + public IHandleTypeDispatch,
1.55 + public IPluginsListener
1.56 +{
1.57 +public:
1.58 + ~UsrMessageNatives();
1.59 +public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
1.60 + void OnSourceModAllInitialized();
1.61 + void OnSourceModShutdown();
1.62 + void OnHandleDestroy(HandleType_t type, void *object);
1.63 + bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
1.64 + void OnPluginUnloaded(IPlugin *plugin);
1.65 +public:
1.66 + MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
1.67 + bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
1.68 + bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
1.69 +private:
1.70 + CStack<MsgListenerWrapper *> m_FreeListeners;
1.71 +};
1.72 +
1.73 +UsrMessageNatives::~UsrMessageNatives()
1.74 +{
1.75 + CStack<MsgListenerWrapper *>::iterator iter;
1.76 + for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
1.77 + {
1.78 + delete (*iter);
1.79 + }
1.80 + m_FreeListeners.popall();
1.81 +}
1.82 +
1.83 +void UsrMessageNatives::OnSourceModAllInitialized()
1.84 +{
1.85 + HandleAccess sec;
1.86 +
1.87 + g_HandleSys.InitAccessDefaults(NULL, &sec);
1.88 + sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1.89 +
1.90 + g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
1.91 + g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
1.92 +
1.93 + g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
1.94 +
1.95 + g_PluginSys.AddPluginsListener(this);
1.96 +}
1.97 +
1.98 +void UsrMessageNatives::OnSourceModShutdown()
1.99 +{
1.100 + HandleSecurity sec;
1.101 + sec.pIdentity = g_pCoreIdent;
1.102 +
1.103 + g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
1.104 +
1.105 + g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
1.106 + g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
1.107 +
1.108 + g_WrBitBufType = 0;
1.109 + g_RdBitBufType = 0;
1.110 +}
1.111 +
1.112 +void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
1.113 +{
1.114 +}
1.115 +
1.116 +bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1.117 +{
1.118 + *pSize = sizeof(bf_read);
1.119 +
1.120 + return true;
1.121 +}
1.122 +
1.123 +void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
1.124 +{
1.125 + MsgWrapperList *pList;
1.126 +
1.127 + if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
1.128 + {
1.129 + MsgWrapperIter iter;
1.130 + MsgListenerWrapper *pListener;
1.131 +
1.132 + for (iter=pList->begin(); iter!=pList->end(); iter++)
1.133 + {
1.134 + pListener = (*iter);
1.135 + if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
1.136 + {
1.137 + m_FreeListeners.push(pListener);
1.138 + }
1.139 + }
1.140 +
1.141 + delete pList;
1.142 + }
1.143 +}
1.144 +
1.145 +MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
1.146 +{
1.147 + MsgWrapperList *pList;
1.148 + MsgListenerWrapper *pListener;
1.149 + IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
1.150 +
1.151 + if (m_FreeListeners.empty())
1.152 + {
1.153 + pListener = new MsgListenerWrapper;
1.154 + } else {
1.155 + pListener = m_FreeListeners.front();
1.156 + m_FreeListeners.pop();
1.157 + }
1.158 +
1.159 + if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
1.160 + {
1.161 + pList = new List<MsgListenerWrapper *>;
1.162 + pl->SetProperty("MsgListeners", pList);
1.163 + }
1.164 +
1.165 + pList->push_back(pListener);
1.166 +
1.167 + return pListener;
1.168 +}
1.169 +
1.170 +bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
1.171 +{
1.172 + MsgWrapperList *pList;
1.173 + MsgWrapperIter _iter;
1.174 + MsgListenerWrapper *pListener;
1.175 + IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
1.176 +
1.177 + if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
1.178 + {
1.179 + return false;
1.180 + }
1.181 +
1.182 + for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
1.183 + {
1.184 + pListener = (*_iter);
1.185 + if ((msgid == pListener->GetMessageId())
1.186 + && (intercept == pListener->IsInterceptHook())
1.187 + && (pHook == pListener->GetHookedFunction()))
1.188 + {
1.189 + *iter = _iter;
1.190 + return true;
1.191 + }
1.192 + }
1.193 +
1.194 + return false;
1.195 +}
1.196 +
1.197 +bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
1.198 +{
1.199 + MsgWrapperList *pList;
1.200 + MsgListenerWrapper *pListener;
1.201 + IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
1.202 +
1.203 + if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
1.204 + {
1.205 + return false;
1.206 + }
1.207 +
1.208 + pListener = (*iter);
1.209 + pList->erase(iter);
1.210 + m_FreeListeners.push(pListener);
1.211 +
1.212 + return true;
1.213 +}
1.214 +
1.215 +/***************************************
1.216 + * *
1.217 + * USER MESSAGE WRAPPER IMPLEMENTATION *
1.218 + * *
1.219 + ***************************************/
1.220 +
1.221 +void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
1.222 +{
1.223 + if (intercept)
1.224 + {
1.225 + m_Intercept = hook;
1.226 + m_Hook = NULL;
1.227 + } else {
1.228 + m_Hook = hook;
1.229 + m_Intercept = NULL;
1.230 + }
1.231 +
1.232 + if (notify)
1.233 + {
1.234 + m_Notify = notify;
1.235 + } else {
1.236 + m_Notify = NULL;
1.237 + }
1.238 +
1.239 + m_MsgId = msgid;
1.240 + m_IsInterceptHook = intercept;
1.241 +}
1.242 +
1.243 +size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
1.244 +{
1.245 + size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
1.246 +
1.247 + for (size_t i=0; i<size; i++)
1.248 + {
1.249 + pl_array[i] = pFilter->GetRecipientIndex(i);
1.250 + }
1.251 +
1.252 + return size;
1.253 +}
1.254 +
1.255 +bool MsgListenerWrapper::IsInterceptHook() const
1.256 +{
1.257 + return m_IsInterceptHook;
1.258 +}
1.259 +
1.260 +int MsgListenerWrapper::GetMessageId() const
1.261 +{
1.262 + return m_MsgId;
1.263 +}
1.264 +
1.265 +IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
1.266 +{
1.267 + if (m_Hook)
1.268 + {
1.269 + return m_Hook;
1.270 + } else {
1.271 + return m_Intercept;
1.272 + }
1.273 +}
1.274 +
1.275 +IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
1.276 +{
1.277 + return m_Notify;
1.278 +}
1.279 +
1.280 +void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
1.281 +{
1.282 + cell_t res;
1.283 + size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
1.284 +
1.285 + g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
1.286 +
1.287 + m_Hook->PushCell(msg_id);
1.288 + m_Hook->PushCell(g_ReadBufHandle);
1.289 + m_Hook->PushArray(g_MsgPlayers, size);
1.290 + m_Hook->PushCell(size);
1.291 + m_Hook->PushCell(pFilter->IsReliable());
1.292 + m_Hook->PushCell(pFilter->IsInitMessage());
1.293 + m_Hook->Execute(&res);
1.294 +}
1.295 +
1.296 +ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
1.297 +{
1.298 + cell_t res = static_cast<cell_t>(Pl_Continue);
1.299 + size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
1.300 +
1.301 + g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
1.302 +
1.303 + m_Intercept->PushCell(msg_id);
1.304 + m_Intercept->PushCell(g_ReadBufHandle);
1.305 + m_Intercept->PushArray(g_MsgPlayers, size);
1.306 + m_Intercept->PushCell(size);
1.307 + m_Intercept->PushCell(pFilter->IsReliable());
1.308 + m_Intercept->PushCell(pFilter->IsInitMessage());
1.309 + m_Intercept->Execute(&res);
1.310 +
1.311 + return static_cast<ResultType>(res);
1.312 +}
1.313 +
1.314 +void MsgListenerWrapper::OnUserMessageSent(int msg_id)
1.315 +{
1.316 + if (!m_Notify)
1.317 + {
1.318 + return;
1.319 + }
1.320 +
1.321 + cell_t res;
1.322 + m_Notify->PushCell(msg_id);
1.323 + m_Notify->Execute(&res);
1.324 +}
1.325 +
1.326 +/***************************************
1.327 + * *
1.328 + * USER MESSAGE NATIVE IMPLEMENTATIONS *
1.329 + * *
1.330 + ***************************************/
1.331 +
1.332 +static UsrMessageNatives s_UsrMessageNatives;
1.333 +
1.334 +static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
1.335 +{
1.336 + char *msgname;
1.337 + pCtx->LocalToString(params[1], &msgname);
1.338 +
1.339 + return g_UserMsgs.GetMessageIndex(msgname);
1.340 +}
1.341 +
1.342 +static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
1.343 +{
1.344 + char *msgname;
1.345 +
1.346 + pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
1.347 +
1.348 + return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
1.349 +}
1.350 +
1.351 +static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
1.352 +{
1.353 + char *msgname;
1.354 + cell_t *cl_array;
1.355 + int msgid;
1.356 + bf_write *pBitBuf;
1.357 +
1.358 + if (g_IsMsgInExec)
1.359 + {
1.360 + return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
1.361 + }
1.362 +
1.363 + pCtx->LocalToString(params[1], &msgname);
1.364 +
1.365 + if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
1.366 + {
1.367 + return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
1.368 + }
1.369 +
1.370 + pCtx->LocalToPhysAddr(params[2], &cl_array);
1.371 +
1.372 + pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
1.373 + if (!pBitBuf)
1.374 + {
1.375 + return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
1.376 + }
1.377 +
1.378 + g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
1.379 + g_IsMsgInExec = true;
1.380 +
1.381 + return g_CurMsgHandle;
1.382 +}
1.383 +
1.384 +static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
1.385 +{
1.386 + cell_t *cl_array;
1.387 + bf_write *pBitBuf;
1.388 + int msgid = params[1];
1.389 +
1.390 + if (g_IsMsgInExec)
1.391 + {
1.392 + return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
1.393 + }
1.394 +
1.395 + if (msgid < 0 || msgid >= 255)
1.396 + {
1.397 + return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
1.398 + }
1.399 +
1.400 + pCtx->LocalToPhysAddr(params[2], &cl_array);
1.401 +
1.402 + pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
1.403 + if (!pBitBuf)
1.404 + {
1.405 + return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
1.406 + }
1.407 +
1.408 + g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
1.409 + g_IsMsgInExec = true;
1.410 +
1.411 + return g_CurMsgHandle;
1.412 +}
1.413 +
1.414 +static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
1.415 +{
1.416 + HandleSecurity sec;
1.417 +
1.418 + if (!g_IsMsgInExec)
1.419 + {
1.420 + return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
1.421 + }
1.422 +
1.423 + g_UserMsgs.EndMessage();
1.424 +
1.425 + sec.pOwner = pCtx->GetIdentity();
1.426 + sec.pIdentity = g_pCoreIdent;
1.427 + g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
1.428 +
1.429 + g_IsMsgInExec = false;
1.430 +
1.431 + return 1;
1.432 +}
1.433 +
1.434 +static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
1.435 +{
1.436 + IPluginFunction *pHook, *pNotify;
1.437 + MsgListenerWrapper *pListener;
1.438 + bool intercept;
1.439 + int msgid = params[1];
1.440 +
1.441 + if (msgid < 0 || msgid >= 255)
1.442 + {
1.443 + return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
1.444 + }
1.445 +
1.446 + pHook = pCtx->GetFunctionById(params[2]);
1.447 + if (!pHook)
1.448 + {
1.449 + return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
1.450 + }
1.451 + pNotify = pCtx->GetFunctionById(params[4]);
1.452 + intercept = (params[3]) ? true : false;
1.453 +
1.454 + pListener = s_UsrMessageNatives.CreateListener(pCtx);
1.455 + pListener->Initialize(msgid, pHook, pNotify, intercept);
1.456 +
1.457 + g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
1.458 +
1.459 + return 1;
1.460 +}
1.461 +
1.462 +static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
1.463 +{
1.464 + IPluginFunction *pFunc;
1.465 + MsgListenerWrapper *pListener;
1.466 + MsgWrapperIter iter;
1.467 + bool intercept;
1.468 + int msgid = params[1];
1.469 +
1.470 + if (msgid < 0 || msgid >= 255)
1.471 + {
1.472 + return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
1.473 + }
1.474 +
1.475 + pFunc = pCtx->GetFunctionById(params[2]);
1.476 + if (!pFunc)
1.477 + {
1.478 + return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
1.479 + }
1.480 + intercept = (params[3]) ? true : false;
1.481 +
1.482 + if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
1.483 + {
1.484 + return pCtx->ThrowNativeError("Unable to unhook the current user message");
1.485 + }
1.486 +
1.487 + pListener = (*iter);
1.488 + if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
1.489 + {
1.490 + return pCtx->ThrowNativeError("Unable to unhook the current user message");
1.491 + }
1.492 +
1.493 + s_UsrMessageNatives.DeleteListener(pCtx, iter);
1.494 +
1.495 + return 1;
1.496 +}
1.497 +
1.498 +REGISTER_NATIVES(usrmsgnatives)
1.499 +{
1.500 + {"GetUserMessageId", smn_GetUserMessageId},
1.501 + {"GetUserMessageName", smn_GetUserMessageName},
1.502 + {"StartMessage", smn_StartMessage},
1.503 + {"StartMessageEx", smn_StartMessageEx},
1.504 + {"EndMessage", smn_EndMessage},
1.505 + {"HookUserMessage", smn_HookUserMessage},
1.506 + {"UnhookUserMessage", smn_UnhookUserMessage},
1.507 + {NULL, NULL}
1.508 +};