/** * vim: set ts=4 : * ============================================================================= * SourceMod * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 3.0, as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * As a special exception, AlliedModders LLC gives you permission to link the * code of this program (as well as its derivative works) to "Half-Life 2," the * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software * by the Valve Corporation. You must obey the GNU General Public License in * all respects for all other code used. Additionally, AlliedModders LLC grants * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . * * Version: $Id$ */ #include "HandleSys.h" #include "PluginSys.h" #include "UserMessages.h" #include "PlayerManager.h" #include "smn_usermsgs.h" HandleType_t g_WrBitBufType; HandleType_t g_RdBitBufType; Handle_t g_CurMsgHandle; Handle_t g_ReadBufHandle; bf_read g_ReadBitBuf; int g_MsgPlayers[256]; bool g_IsMsgInExec = false; typedef List MsgWrapperList; typedef List::iterator MsgWrapperIter; class UsrMessageNatives : public SMGlobalClass, public IHandleTypeDispatch, public IPluginsListener { public: ~UsrMessageNatives(); public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener void OnSourceModAllInitialized(); void OnSourceModShutdown(); void OnHandleDestroy(HandleType_t type, void *object); bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); void OnPluginUnloaded(IPlugin *plugin); public: MsgListenerWrapper *CreateListener(IPluginContext *pCtx); bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter); bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter); private: CStack m_FreeListeners; }; UsrMessageNatives::~UsrMessageNatives() { CStack::iterator iter; for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++) { delete (*iter); } m_FreeListeners.popall(); } void UsrMessageNatives::OnSourceModAllInitialized() { HandleAccess sec; g_HandleSys.InitAccessDefaults(NULL, &sec); sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY; g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL); g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL); g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL); g_PluginSys.AddPluginsListener(this); } void UsrMessageNatives::OnSourceModShutdown() { HandleSecurity sec; sec.pIdentity = g_pCoreIdent; g_HandleSys.FreeHandle(g_ReadBufHandle, &sec); g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent); g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent); g_WrBitBufType = 0; g_RdBitBufType = 0; } void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object) { } bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize) { *pSize = sizeof(bf_read); return true; } void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin) { MsgWrapperList *pList; if (plugin->GetProperty("MsgListeners", reinterpret_cast(&pList), true)) { MsgWrapperIter iter; MsgListenerWrapper *pListener; for (iter=pList->begin(); iter!=pList->end(); iter++) { pListener = (*iter); if (g_UserMsgs.UnhookUserMessage2(pListener->GetMessageId(), pListener, pListener->IsInterceptHook())) { m_FreeListeners.push(pListener); } } delete pList; } } MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx) { MsgWrapperList *pList; MsgListenerWrapper *pListener; IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext()); if (m_FreeListeners.empty()) { pListener = new MsgListenerWrapper; } else { pListener = m_FreeListeners.front(); m_FreeListeners.pop(); } if (!pl->GetProperty("MsgListeners", reinterpret_cast(&pList))) { pList = new List; pl->SetProperty("MsgListeners", pList); } pList->push_back(pListener); return pListener; } bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter) { MsgWrapperList *pList; MsgWrapperIter _iter; MsgListenerWrapper *pListener; IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext()); if (!pl->GetProperty("MsgListeners", reinterpret_cast(&pList))) { return false; } for (_iter=pList->begin(); _iter!=pList->end(); _iter++) { pListener = (*_iter); if ((msgid == pListener->GetMessageId()) && (intercept == pListener->IsInterceptHook()) && (pHook == pListener->GetHookedFunction())) { *iter = _iter; return true; } } return false; } bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter) { MsgWrapperList *pList; MsgListenerWrapper *pListener; IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext()); if (!pl->GetProperty("MsgListeners", reinterpret_cast(&pList))) { return false; } pListener = (*iter); pList->erase(iter); m_FreeListeners.push(pListener); return true; } /*************************************** * * * USER MESSAGE WRAPPER IMPLEMENTATION * * * ***************************************/ void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept) { if (intercept) { m_Intercept = hook; m_Hook = NULL; } else { m_Hook = hook; m_Intercept = NULL; } if (notify) { m_Notify = notify; } else { m_Notify = NULL; } m_MsgId = msgid; m_IsInterceptHook = intercept; } size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter) { size_t size = static_cast(pFilter->GetRecipientCount()); for (size_t i=0; iGetRecipientIndex(i); } return size; } bool MsgListenerWrapper::IsInterceptHook() const { return m_IsInterceptHook; } int MsgListenerWrapper::GetMessageId() const { return m_MsgId; } IPluginFunction *MsgListenerWrapper::GetHookedFunction() const { if (m_Hook) { return m_Hook; } else { return m_Intercept; } } IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const { return m_Notify; } void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) { cell_t res; size_t size = _FillInPlayers(g_MsgPlayers, pFilter); g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten()); m_Hook->PushCell(msg_id); m_Hook->PushCell(g_ReadBufHandle); m_Hook->PushArray(g_MsgPlayers, size); m_Hook->PushCell(size); m_Hook->PushCell(pFilter->IsReliable()); m_Hook->PushCell(pFilter->IsInitMessage()); m_Hook->Execute(&res); } ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter) { cell_t res = static_cast(Pl_Continue); size_t size = _FillInPlayers(g_MsgPlayers, pFilter); g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten()); m_Intercept->PushCell(msg_id); m_Intercept->PushCell(g_ReadBufHandle); m_Intercept->PushArray(g_MsgPlayers, size); m_Intercept->PushCell(size); m_Intercept->PushCell(pFilter->IsReliable()); m_Intercept->PushCell(pFilter->IsInitMessage()); m_Intercept->Execute(&res); return static_cast(res); } void MsgListenerWrapper::OnPostUserMessage(int msg_id, bool sent) { if (!m_Notify) { return; } cell_t res; m_Notify->PushCell(msg_id); m_Notify->PushCell(sent ? 1 : 0); m_Notify->Execute(&res); } /*************************************** * * * USER MESSAGE NATIVE IMPLEMENTATIONS * * * ***************************************/ static UsrMessageNatives s_UsrMessageNatives; static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params) { char *msgname; pCtx->LocalToString(params[1], &msgname); return g_UserMsgs.GetMessageIndex(msgname); } static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params) { char *msgname; pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname); return g_UserMsgs.GetMessageName(params[1], msgname, params[3]); } static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params) { char *msgname; cell_t *cl_array; unsigned int numClients; int msgid; bf_write *pBitBuf; int client; CPlayer *pPlayer = NULL; if (g_IsMsgInExec) { return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress"); } pCtx->LocalToString(params[1], &msgname); if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID) { return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname); } pCtx->LocalToPhysAddr(params[2], &cl_array); numClients = params[3]; /* Client validation */ for (unsigned int i = 0; i < numClients; i++) { client = cl_array[i]; pPlayer = g_Players.GetPlayerByIndex(client); if (!pPlayer) { return pCtx->ThrowNativeError("Client index %d is invalid", client); } else if (!pPlayer->IsConnected()) { return pCtx->ThrowNativeError("Client %d is not connected", client); } } pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, numClients, params[4]); if (!pBitBuf) { return pCtx->ThrowNativeError("Unable to execute a new message while in hook"); } g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL); g_IsMsgInExec = true; return g_CurMsgHandle; } static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params) { cell_t *cl_array; unsigned int numClients; bf_write *pBitBuf; int client; CPlayer *pPlayer = NULL; int msgid = params[1]; if (g_IsMsgInExec) { return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress"); } if (msgid < 0 || msgid >= 255) { return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid); } pCtx->LocalToPhysAddr(params[2], &cl_array); numClients = params[3]; /* Client validation */ for (unsigned int i = 0; i < numClients; i++) { client = cl_array[i]; pPlayer = g_Players.GetPlayerByIndex(client); if (!pPlayer) { return pCtx->ThrowNativeError("Client index %d is invalid", client); } else if (!pPlayer->IsConnected()) { return pCtx->ThrowNativeError("Client %d is not connected", client); } } pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, numClients, params[4]); if (!pBitBuf) { return pCtx->ThrowNativeError("Unable to execute a new message while in hook"); } g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL); g_IsMsgInExec = true; return g_CurMsgHandle; } static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params) { HandleSecurity sec; if (!g_IsMsgInExec) { return pCtx->ThrowNativeError("Unable to end message, no message is in progress"); } g_UserMsgs.EndMessage(); sec.pOwner = pCtx->GetIdentity(); sec.pIdentity = g_pCoreIdent; g_HandleSys.FreeHandle(g_CurMsgHandle, &sec); g_IsMsgInExec = false; return 1; } static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params) { IPluginFunction *pHook, *pNotify; MsgListenerWrapper *pListener; bool intercept; int msgid = params[1]; if (msgid < 0 || msgid >= 255) { return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid); } pHook = pCtx->GetFunctionById(params[2]); if (!pHook) { return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]); } pNotify = pCtx->GetFunctionById(params[4]); intercept = (params[3]) ? true : false; pListener = s_UsrMessageNatives.CreateListener(pCtx); pListener->Initialize(msgid, pHook, pNotify, intercept); g_UserMsgs.HookUserMessage2(msgid, pListener, intercept); return 1; } static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params) { IPluginFunction *pFunc; MsgListenerWrapper *pListener; MsgWrapperIter iter; bool intercept; int msgid = params[1]; if (msgid < 0 || msgid >= 255) { return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid); } pFunc = pCtx->GetFunctionById(params[2]); if (!pFunc) { return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]); } intercept = (params[3]) ? true : false; if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter)) { return pCtx->ThrowNativeError("Unable to unhook the current user message"); } pListener = (*iter); if (!g_UserMsgs.UnhookUserMessage2(msgid, pListener, intercept)) { return pCtx->ThrowNativeError("Unable to unhook the current user message"); } s_UsrMessageNatives.DeleteListener(pCtx, iter); return 1; } REGISTER_NATIVES(usrmsgnatives) { {"GetUserMessageId", smn_GetUserMessageId}, {"GetUserMessageName", smn_GetUserMessageName}, {"StartMessage", smn_StartMessage}, {"StartMessageEx", smn_StartMessageEx}, {"EndMessage", smn_EndMessage}, {"HookUserMessage", smn_HookUserMessage}, {"UnhookUserMessage", smn_UnhookUserMessage}, {NULL, NULL} };