core/smn_usermsgs.cpp
author David Anderson <dvander@alliedmods.net>
Sun Mar 01 16:39:25 2009 -0500 (2009-03-01)
changeset 2636 dfa3c6dd31a5
parent 2289 44045ead61cd
child 2736 3bab645fb4c7
permissions -rw-r--r--
Improved user message interception API (bug 3631, r=pred).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     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.
    11  * 
    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
    15  * details.
    16  *
    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/>.
    19  *
    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>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "HandleSys.h"
    33 #include "PluginSys.h"
    34 #include "UserMessages.h"
    35 #include "PlayerManager.h"
    36 #include "smn_usermsgs.h"
    37 
    38 HandleType_t g_WrBitBufType;
    39 HandleType_t g_RdBitBufType;
    40 Handle_t g_CurMsgHandle;
    41 Handle_t g_ReadBufHandle;
    42 bf_read g_ReadBitBuf;
    43 
    44 int g_MsgPlayers[256];
    45 bool g_IsMsgInExec = false;
    46 
    47 typedef List<MsgListenerWrapper *> MsgWrapperList;
    48 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
    49 
    50 class UsrMessageNatives :
    51 	public SMGlobalClass,
    52 	public IHandleTypeDispatch,
    53 	public IPluginsListener
    54 {
    55 public:
    56 	~UsrMessageNatives();
    57 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
    58 	void OnSourceModAllInitialized();
    59 	void OnSourceModShutdown();
    60 	void OnHandleDestroy(HandleType_t type, void *object);
    61 	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
    62 	void OnPluginUnloaded(IPlugin *plugin);
    63 public:
    64 	MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
    65 	bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
    66 	bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
    67 private:
    68 	CStack<MsgListenerWrapper *> m_FreeListeners;
    69 };
    70 
    71 UsrMessageNatives::~UsrMessageNatives()
    72 {
    73 	CStack<MsgListenerWrapper *>::iterator iter;
    74 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    75 	{
    76 		delete (*iter);
    77 	}
    78 	m_FreeListeners.popall();
    79 }
    80 
    81 void UsrMessageNatives::OnSourceModAllInitialized()
    82 {
    83 	HandleAccess sec;
    84 
    85 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    86 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
    87 
    88 	g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    89 	g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    90 
    91 	g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
    92 
    93 	g_PluginSys.AddPluginsListener(this);
    94 }
    95 
    96 void UsrMessageNatives::OnSourceModShutdown()
    97 {
    98 	HandleSecurity sec;
    99 	sec.pIdentity = g_pCoreIdent;
   100 
   101 	g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
   102 
   103 	g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
   104 	g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
   105 
   106 	g_WrBitBufType = 0;
   107 	g_RdBitBufType = 0;
   108 }
   109 
   110 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
   111 {
   112 }
   113 
   114 bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   115 {
   116 	*pSize = sizeof(bf_read);
   117 
   118 	return true;
   119 }
   120 
   121 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
   122 {
   123 	MsgWrapperList *pList;
   124 
   125 	if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
   126 	{
   127 		MsgWrapperIter iter;
   128 		MsgListenerWrapper *pListener;
   129 
   130 		for (iter=pList->begin(); iter!=pList->end(); iter++)
   131 		{
   132 			pListener = (*iter);
   133 			if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
   134 			{
   135 				m_FreeListeners.push(pListener);
   136 			}
   137 		}
   138 
   139 		delete pList;
   140 	}
   141 }
   142 
   143 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
   144 {
   145 	MsgWrapperList *pList;
   146 	MsgListenerWrapper *pListener;
   147 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   148 
   149 	if (m_FreeListeners.empty())
   150 	{
   151 		pListener = new MsgListenerWrapper;
   152 	} else {
   153 		pListener = m_FreeListeners.front();
   154 		m_FreeListeners.pop();
   155 	}
   156 
   157 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   158 	{
   159 		pList = new List<MsgListenerWrapper *>;
   160 		pl->SetProperty("MsgListeners", pList);
   161 	}
   162 
   163 	pList->push_back(pListener);
   164 
   165 	return pListener;
   166 }
   167 
   168 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
   169 {
   170 	MsgWrapperList *pList;
   171 	MsgWrapperIter _iter;
   172 	MsgListenerWrapper *pListener;
   173 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   174 
   175 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   176 	{
   177 		return false;
   178 	}
   179 
   180 	for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
   181 	{
   182 		pListener = (*_iter);
   183 		if ((msgid == pListener->GetMessageId()) 
   184 			&& (intercept == pListener->IsInterceptHook()) 
   185 			&& (pHook == pListener->GetHookedFunction()))
   186 		{
   187 			*iter = _iter;
   188 			return true;
   189 		}
   190 	}
   191 
   192 	return false;
   193 }
   194 
   195 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
   196 {
   197 	MsgWrapperList *pList;
   198 	MsgListenerWrapper *pListener;
   199 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   200 
   201 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   202 	{
   203 		return false;
   204 	}
   205 
   206 	pListener = (*iter);
   207 	pList->erase(iter);
   208 	m_FreeListeners.push(pListener);
   209 
   210 	return true;
   211 }
   212 
   213 /***************************************
   214  *                                     *
   215  * USER MESSAGE WRAPPER IMPLEMENTATION *
   216  *                                     *
   217  ***************************************/
   218 
   219 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
   220 {
   221 	if (intercept)
   222 	{
   223 		m_Intercept = hook;
   224 		m_Hook = NULL;
   225 	} else {
   226 		m_Hook = hook;
   227 		m_Intercept = NULL;
   228 	}
   229 
   230 	if (notify)
   231 	{
   232 		m_Notify = notify;
   233 	} else {
   234 		m_Notify = NULL;
   235 	}
   236 
   237 	m_MsgId = msgid;
   238 	m_IsInterceptHook = intercept;
   239 }
   240 
   241 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
   242 {
   243 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
   244 
   245 	for (size_t i=0; i<size; i++)
   246 	{
   247 		pl_array[i] = pFilter->GetRecipientIndex(i);
   248 	}
   249 
   250 	return size;
   251 }
   252 
   253 bool MsgListenerWrapper::IsInterceptHook() const
   254 {
   255 	return m_IsInterceptHook;
   256 }
   257 
   258 int MsgListenerWrapper::GetMessageId() const
   259 {
   260 	return m_MsgId;
   261 }
   262 
   263 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
   264 {
   265 	if (m_Hook)
   266 	{
   267 		return m_Hook;
   268 	} else {
   269 		return m_Intercept;
   270 	}
   271 }
   272 
   273 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
   274 {
   275 	return m_Notify;
   276 }
   277 
   278 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   279 {
   280 	cell_t res;
   281 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   282 
   283 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   284 
   285 	m_Hook->PushCell(msg_id);
   286 	m_Hook->PushCell(g_ReadBufHandle);
   287 	m_Hook->PushArray(g_MsgPlayers, size);
   288 	m_Hook->PushCell(size);
   289 	m_Hook->PushCell(pFilter->IsReliable());
   290 	m_Hook->PushCell(pFilter->IsInitMessage());
   291 	m_Hook->Execute(&res);
   292 }
   293 
   294 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   295 {
   296 	cell_t res = static_cast<cell_t>(Pl_Continue);
   297 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   298 
   299 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   300 
   301 	m_Intercept->PushCell(msg_id);
   302 	m_Intercept->PushCell(g_ReadBufHandle);
   303 	m_Intercept->PushArray(g_MsgPlayers, size);
   304 	m_Intercept->PushCell(size);
   305 	m_Intercept->PushCell(pFilter->IsReliable());
   306 	m_Intercept->PushCell(pFilter->IsInitMessage());
   307 	m_Intercept->Execute(&res);
   308 
   309 	return static_cast<ResultType>(res);
   310 }
   311 
   312 void MsgListenerWrapper::OnPostUserMessage(int msg_id, bool sent)
   313 {
   314 	if (!m_Notify)
   315 	{
   316 		return;
   317 	}
   318 
   319 	cell_t res;
   320 	m_Notify->PushCell(msg_id);
   321 	m_Notify->PushCell(sent ? 1 : 0);
   322 	m_Notify->Execute(&res);
   323 }
   324 
   325 /***************************************
   326  *                                     *
   327  * USER MESSAGE NATIVE IMPLEMENTATIONS *
   328  *                                     *
   329  ***************************************/
   330 
   331 static UsrMessageNatives s_UsrMessageNatives;
   332 
   333 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
   334 {
   335 	char *msgname;
   336 	pCtx->LocalToString(params[1], &msgname);
   337 
   338 	return g_UserMsgs.GetMessageIndex(msgname);
   339 }
   340 
   341 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
   342 {
   343 	char *msgname;
   344 
   345 	pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
   346 
   347 	return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
   348 }
   349 
   350 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
   351 {
   352 	char *msgname;
   353 	cell_t *cl_array;
   354 	unsigned int numClients;
   355 	int msgid;
   356 	bf_write *pBitBuf;
   357 	int client;
   358 	CPlayer *pPlayer = NULL;
   359 
   360 	if (g_IsMsgInExec)
   361 	{
   362 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   363 	}
   364 
   365 	pCtx->LocalToString(params[1], &msgname);
   366 
   367 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   368 	{
   369 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   370 	}
   371 
   372 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   373 
   374 	numClients = params[3];
   375 
   376 	/* Client validation */
   377 	for (unsigned int i = 0; i < numClients; i++)
   378 	{
   379 		client = cl_array[i];
   380 		pPlayer = g_Players.GetPlayerByIndex(client);
   381 
   382 		if (!pPlayer)
   383 		{
   384 			return pCtx->ThrowNativeError("Client index %d is invalid", client);
   385 		} else if (!pPlayer->IsConnected()) {
   386 			return pCtx->ThrowNativeError("Client %d is not connected", client);
   387 		}
   388 	}
   389 
   390 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, numClients, params[4]);
   391 	if (!pBitBuf)
   392 	{
   393 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   394 	}
   395 
   396 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   397 	g_IsMsgInExec = true;
   398 
   399 	return g_CurMsgHandle;
   400 }
   401 
   402 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   403 {
   404 	cell_t *cl_array;
   405 	unsigned int numClients;
   406 	bf_write *pBitBuf;
   407 	int client;
   408 	CPlayer *pPlayer = NULL;
   409 	int msgid = params[1];
   410 
   411 	if (g_IsMsgInExec)
   412 	{
   413 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   414 	}
   415 
   416 	if (msgid < 0 || msgid >= 255)
   417 	{
   418 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   419 	}
   420 
   421 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   422 
   423 	numClients = params[3];
   424 
   425 	/* Client validation */
   426 	for (unsigned int i = 0; i < numClients; i++)
   427 	{
   428 		client = cl_array[i];
   429 		pPlayer = g_Players.GetPlayerByIndex(client);
   430 
   431 		if (!pPlayer)
   432 		{
   433 			return pCtx->ThrowNativeError("Client index %d is invalid", client);
   434 		} else if (!pPlayer->IsConnected()) {
   435 			return pCtx->ThrowNativeError("Client %d is not connected", client);
   436 		}
   437 	}
   438 
   439 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, numClients, params[4]);
   440 	if (!pBitBuf)
   441 	{
   442 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   443 	}
   444 
   445 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   446 	g_IsMsgInExec = true;
   447 
   448 	return g_CurMsgHandle;
   449 }
   450 
   451 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
   452 {
   453 	HandleSecurity sec;
   454 
   455 	if (!g_IsMsgInExec)
   456 	{
   457 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   458 	}
   459 
   460 	g_UserMsgs.EndMessage();
   461 
   462 	sec.pOwner = pCtx->GetIdentity();
   463 	sec.pIdentity = g_pCoreIdent;
   464 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   465 
   466 	g_IsMsgInExec = false;
   467 
   468 	return 1;
   469 }
   470 
   471 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   472 {
   473 	IPluginFunction *pHook, *pNotify;
   474 	MsgListenerWrapper *pListener;
   475 	bool intercept;
   476 	int msgid = params[1];
   477 
   478 	if (msgid < 0 || msgid >= 255)
   479 	{
   480 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   481 	}
   482 
   483 	pHook = pCtx->GetFunctionById(params[2]);
   484 	if (!pHook)
   485 	{
   486 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   487 	}
   488 	pNotify = pCtx->GetFunctionById(params[4]);
   489 	intercept = (params[3]) ? true : false;
   490 
   491 	pListener = s_UsrMessageNatives.CreateListener(pCtx);
   492 	pListener->Initialize(msgid, pHook, pNotify, intercept);
   493 
   494 	g_UserMsgs.HookUserMessage2(msgid, pListener, intercept);
   495 
   496 	return 1;
   497 }
   498 
   499 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
   500 {
   501 	IPluginFunction *pFunc;
   502 	MsgListenerWrapper *pListener;
   503 	MsgWrapperIter iter;
   504 	bool intercept;
   505 	int msgid = params[1];
   506 
   507 	if (msgid < 0 || msgid >= 255)
   508 	{
   509 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   510 	}
   511 
   512 	pFunc = pCtx->GetFunctionById(params[2]);
   513 	if (!pFunc)
   514 	{
   515 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   516 	}
   517 	intercept = (params[3]) ? true : false;
   518 
   519 	if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
   520 	{
   521 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   522 	}
   523 
   524 	pListener = (*iter);
   525 	if (!g_UserMsgs.UnhookUserMessage2(msgid, pListener, intercept))
   526 	{
   527 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   528 	}
   529 
   530 	s_UsrMessageNatives.DeleteListener(pCtx, iter);
   531 
   532 	return 1;
   533 }
   534 
   535 REGISTER_NATIVES(usrmsgnatives)
   536 {
   537 	{"GetUserMessageId",			smn_GetUserMessageId},
   538 	{"GetUserMessageName",			smn_GetUserMessageName},
   539 	{"StartMessage",				smn_StartMessage},
   540 	{"StartMessageEx",				smn_StartMessageEx},
   541 	{"EndMessage",					smn_EndMessage},
   542 	{"HookUserMessage",				smn_HookUserMessage},
   543 	{"UnhookUserMessage",			smn_UnhookUserMessage},
   544 	{NULL,							NULL}
   545 };