core/smn_usermsgs.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Nov 19 02:16:45 2008 -0600 (2008-11-19)
changeset 2415 9ac720a36ce4
parent 2289 44045ead61cd
child 2636 dfa3c6dd31a5
permissions -rw-r--r--
Merge.
     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::OnUserMessageSent(int msg_id)
   313 {
   314 	if (!m_Notify)
   315 	{
   316 		return;
   317 	}
   318 
   319 	cell_t res;
   320 	m_Notify->PushCell(msg_id);
   321 	m_Notify->Execute(&res);
   322 }
   323 
   324 /***************************************
   325  *                                     *
   326  * USER MESSAGE NATIVE IMPLEMENTATIONS *
   327  *                                     *
   328  ***************************************/
   329 
   330 static UsrMessageNatives s_UsrMessageNatives;
   331 
   332 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
   333 {
   334 	char *msgname;
   335 	pCtx->LocalToString(params[1], &msgname);
   336 
   337 	return g_UserMsgs.GetMessageIndex(msgname);
   338 }
   339 
   340 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
   341 {
   342 	char *msgname;
   343 
   344 	pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
   345 
   346 	return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
   347 }
   348 
   349 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
   350 {
   351 	char *msgname;
   352 	cell_t *cl_array;
   353 	unsigned int numClients;
   354 	int msgid;
   355 	bf_write *pBitBuf;
   356 	int client;
   357 	CPlayer *pPlayer = NULL;
   358 
   359 	if (g_IsMsgInExec)
   360 	{
   361 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   362 	}
   363 
   364 	pCtx->LocalToString(params[1], &msgname);
   365 
   366 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   367 	{
   368 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   369 	}
   370 
   371 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   372 
   373 	numClients = params[3];
   374 
   375 	/* Client validation */
   376 	for (unsigned int i = 0; i < numClients; i++)
   377 	{
   378 		client = cl_array[i];
   379 		pPlayer = g_Players.GetPlayerByIndex(client);
   380 
   381 		if (!pPlayer)
   382 		{
   383 			return pCtx->ThrowNativeError("Client index %d is invalid", client);
   384 		} else if (!pPlayer->IsConnected()) {
   385 			return pCtx->ThrowNativeError("Client %d is not connected", client);
   386 		}
   387 	}
   388 
   389 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, numClients, params[4]);
   390 	if (!pBitBuf)
   391 	{
   392 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   393 	}
   394 
   395 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   396 	g_IsMsgInExec = true;
   397 
   398 	return g_CurMsgHandle;
   399 }
   400 
   401 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   402 {
   403 	cell_t *cl_array;
   404 	unsigned int numClients;
   405 	bf_write *pBitBuf;
   406 	int client;
   407 	CPlayer *pPlayer = NULL;
   408 	int msgid = params[1];
   409 
   410 	if (g_IsMsgInExec)
   411 	{
   412 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   413 	}
   414 
   415 	if (msgid < 0 || msgid >= 255)
   416 	{
   417 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   418 	}
   419 
   420 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   421 
   422 	numClients = params[3];
   423 
   424 	/* Client validation */
   425 	for (unsigned int i = 0; i < numClients; i++)
   426 	{
   427 		client = cl_array[i];
   428 		pPlayer = g_Players.GetPlayerByIndex(client);
   429 
   430 		if (!pPlayer)
   431 		{
   432 			return pCtx->ThrowNativeError("Client index %d is invalid", client);
   433 		} else if (!pPlayer->IsConnected()) {
   434 			return pCtx->ThrowNativeError("Client %d is not connected", client);
   435 		}
   436 	}
   437 
   438 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, numClients, params[4]);
   439 	if (!pBitBuf)
   440 	{
   441 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   442 	}
   443 
   444 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   445 	g_IsMsgInExec = true;
   446 
   447 	return g_CurMsgHandle;
   448 }
   449 
   450 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
   451 {
   452 	HandleSecurity sec;
   453 
   454 	if (!g_IsMsgInExec)
   455 	{
   456 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   457 	}
   458 
   459 	g_UserMsgs.EndMessage();
   460 
   461 	sec.pOwner = pCtx->GetIdentity();
   462 	sec.pIdentity = g_pCoreIdent;
   463 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   464 
   465 	g_IsMsgInExec = false;
   466 
   467 	return 1;
   468 }
   469 
   470 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   471 {
   472 	IPluginFunction *pHook, *pNotify;
   473 	MsgListenerWrapper *pListener;
   474 	bool intercept;
   475 	int msgid = params[1];
   476 
   477 	if (msgid < 0 || msgid >= 255)
   478 	{
   479 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   480 	}
   481 
   482 	pHook = pCtx->GetFunctionById(params[2]);
   483 	if (!pHook)
   484 	{
   485 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   486 	}
   487 	pNotify = pCtx->GetFunctionById(params[4]);
   488 	intercept = (params[3]) ? true : false;
   489 
   490 	pListener = s_UsrMessageNatives.CreateListener(pCtx);
   491 	pListener->Initialize(msgid, pHook, pNotify, intercept);
   492 
   493 	g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
   494 
   495 	return 1;
   496 }
   497 
   498 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
   499 {
   500 	IPluginFunction *pFunc;
   501 	MsgListenerWrapper *pListener;
   502 	MsgWrapperIter iter;
   503 	bool intercept;
   504 	int msgid = params[1];
   505 
   506 	if (msgid < 0 || msgid >= 255)
   507 	{
   508 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   509 	}
   510 
   511 	pFunc = pCtx->GetFunctionById(params[2]);
   512 	if (!pFunc)
   513 	{
   514 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   515 	}
   516 	intercept = (params[3]) ? true : false;
   517 
   518 	if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
   519 	{
   520 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   521 	}
   522 
   523 	pListener = (*iter);
   524 	if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
   525 	{
   526 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   527 	}
   528 
   529 	s_UsrMessageNatives.DeleteListener(pCtx, iter);
   530 
   531 	return 1;
   532 }
   533 
   534 REGISTER_NATIVES(usrmsgnatives)
   535 {
   536 	{"GetUserMessageId",			smn_GetUserMessageId},
   537 	{"GetUserMessageName",			smn_GetUserMessageName},
   538 	{"StartMessage",				smn_StartMessage},
   539 	{"StartMessageEx",				smn_StartMessageEx},
   540 	{"EndMessage",					smn_EndMessage},
   541 	{"HookUserMessage",				smn_HookUserMessage},
   542 	{"UnhookUserMessage",			smn_UnhookUserMessage},
   543 	{NULL,							NULL}
   544 };