core/smn_usermsgs.cpp
author Michael McKoy <ferret99gt@gmail.com>
Wed Aug 08 23:45:24 2007 +0000 (2007-08-08)
changeset 1284 ac293d8351a3
parent 1231 17badaf66ce4
child 1330 ab94316bb52b
permissions -rw-r--r--
Import of baseplugins.sp
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * ================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or 
     9  * modify it under the terms of the GNU General Public License, 
    10  * version 3.0, as published by the Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include "HandleSys.h"
    34 #include "PluginSys.h"
    35 #include "UserMessages.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 	void OnPluginUnloaded(IPlugin *plugin);
    62 public:
    63 	MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
    64 	MsgWrapperIter FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept);
    65 	bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
    66 private:
    67 	CStack<MsgListenerWrapper *> m_FreeListeners;
    68 };
    69 
    70 UsrMessageNatives::~UsrMessageNatives()
    71 {
    72 	CStack<MsgListenerWrapper *>::iterator iter;
    73 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    74 	{
    75 		delete (*iter);
    76 	}
    77 	m_FreeListeners.popall();
    78 }
    79 
    80 void UsrMessageNatives::OnSourceModAllInitialized()
    81 {
    82 	HandleAccess sec;
    83 	sec.access[HandleAccess_Delete] |= HANDLE_RESTRICT_IDENTITY;
    84 
    85 	g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    86 	g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    87 
    88 	g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
    89 
    90 	g_PluginSys.AddPluginsListener(this);
    91 }
    92 
    93 void UsrMessageNatives::OnSourceModShutdown()
    94 {
    95 	HandleSecurity sec;
    96 	sec.pIdentity = g_pCoreIdent;
    97 
    98 	g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
    99 
   100 	g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
   101 	g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
   102 
   103 	g_WrBitBufType = 0;
   104 	g_RdBitBufType = 0;
   105 }
   106 
   107 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
   108 {
   109 }
   110 
   111 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
   112 {
   113 	MsgWrapperList *pList;
   114 
   115 	if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
   116 	{
   117 		MsgWrapperIter iter;
   118 		MsgListenerWrapper *pListener;
   119 
   120 		for (iter=pList->begin(); iter!=pList->end(); iter++)
   121 		{
   122 			pListener = (*iter);
   123 			if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
   124 			{
   125 				m_FreeListeners.push(pListener);
   126 			}
   127 		}
   128 
   129 		delete pList;
   130 	}
   131 }
   132 
   133 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
   134 {
   135 	MsgWrapperList *pList;
   136 	MsgListenerWrapper *pListener;
   137 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   138 
   139 	if (m_FreeListeners.empty())
   140 	{
   141 		pListener = new MsgListenerWrapper;
   142 	} else {
   143 		pListener = m_FreeListeners.front();
   144 		m_FreeListeners.pop();
   145 	}
   146 
   147 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   148 	{
   149 		pList = new List<MsgListenerWrapper *>;
   150 		pl->SetProperty("MsgListeners", pList);
   151 	}
   152 
   153 	pList->push_back(pListener);
   154 
   155 	return pListener;
   156 }
   157 
   158 MsgWrapperIter UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept)
   159 {
   160 	MsgWrapperList *pList;
   161 	MsgWrapperIter iter;
   162 	MsgListenerWrapper *pListener;
   163 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   164 
   165 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   166 	{
   167 		return NULL;
   168 	}
   169 
   170 	for (iter=pList->begin(); iter!=pList->end(); iter++)
   171 	{
   172 		pListener = (*iter);
   173 		if ((msgid == pListener->GetMessageId()) 
   174 			&& (intercept == pListener->IsInterceptHook()) 
   175 			&& (pHook == pListener->GetHookedFunction()))
   176 		{
   177 			return iter;
   178 		}
   179 	}
   180 
   181 	return NULL;
   182 }
   183 
   184 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
   185 {
   186 	MsgWrapperList *pList;
   187 	MsgListenerWrapper *pListener;
   188 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   189 
   190 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   191 	{
   192 		return false;
   193 	}
   194 
   195 	pListener = (*iter);
   196 	pList->erase(iter);
   197 	m_FreeListeners.push(pListener);
   198 
   199 	return true;
   200 }
   201 
   202 /***************************************
   203  *                                     *
   204  * USER MESSAGE WRAPPER IMPLEMENTATION *
   205  *                                     *
   206  ***************************************/
   207 
   208 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
   209 {
   210 	if (intercept)
   211 	{
   212 		m_Intercept = hook;
   213 		m_Hook = NULL;
   214 	} else {
   215 		m_Hook = hook;
   216 		m_Intercept = NULL;
   217 	}
   218 
   219 	if (notify)
   220 	{
   221 		m_Notify = notify;
   222 	} else {
   223 		m_Notify = NULL;
   224 	}
   225 
   226 	m_MsgId = msgid;
   227 	m_IsInterceptHook = intercept;
   228 }
   229 
   230 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
   231 {
   232 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
   233 
   234 	for (size_t i=0; i<size; i++)
   235 	{
   236 		pl_array[i] = pFilter->GetRecipientIndex(i);
   237 	}
   238 
   239 	return size;
   240 }
   241 
   242 bool MsgListenerWrapper::IsInterceptHook() const
   243 {
   244 	return m_IsInterceptHook;
   245 }
   246 
   247 int MsgListenerWrapper::GetMessageId() const
   248 {
   249 	return m_MsgId;
   250 }
   251 
   252 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
   253 {
   254 	if (m_Hook)
   255 	{
   256 		return m_Hook;
   257 	} else {
   258 		return m_Intercept;
   259 	}
   260 }
   261 
   262 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
   263 {
   264 	return m_Notify;
   265 }
   266 
   267 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   268 {
   269 	cell_t res;
   270 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   271 
   272 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   273 
   274 	m_Hook->PushCell(msg_id);
   275 	m_Hook->PushCell(g_ReadBufHandle);
   276 	m_Hook->PushArray(g_MsgPlayers, size);
   277 	m_Hook->PushCell(size);
   278 	m_Hook->PushCell(pFilter->IsReliable());
   279 	m_Hook->PushCell(pFilter->IsInitMessage());
   280 	m_Hook->Execute(&res);
   281 }
   282 
   283 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   284 {
   285 	cell_t res = static_cast<cell_t>(Pl_Continue);
   286 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   287 
   288 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   289 
   290 	m_Intercept->PushCell(msg_id);
   291 	m_Intercept->PushCell(g_ReadBufHandle);
   292 	m_Intercept->PushArray(g_MsgPlayers, size);
   293 	m_Intercept->PushCell(size);
   294 	m_Intercept->PushCell(pFilter->IsReliable());
   295 	m_Intercept->PushCell(pFilter->IsInitMessage());
   296 	m_Intercept->Execute(&res);
   297 
   298 	return static_cast<ResultType>(res);
   299 }
   300 
   301 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
   302 {
   303 	if (!m_Notify)
   304 	{
   305 		return;
   306 	}
   307 
   308 	cell_t res;
   309 	m_Notify->PushCell(msg_id);
   310 	m_Notify->Execute(&res);
   311 }
   312 
   313 /***************************************
   314  *                                     *
   315  * USER MESSAGE NATIVE IMPLEMENTATIONS *
   316  *                                     *
   317  ***************************************/
   318 
   319 static UsrMessageNatives s_UsrMessageNatives;
   320 
   321 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
   322 {
   323 	char *msgname;
   324 	pCtx->LocalToString(params[1], &msgname);
   325 
   326 	return g_UserMsgs.GetMessageIndex(msgname);
   327 }
   328 
   329 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
   330 {
   331 	char *msgname;
   332 
   333 	pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
   334 
   335 	return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
   336 }
   337 
   338 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
   339 {
   340 	char *msgname;
   341 	cell_t *cl_array;
   342 	int msgid;
   343 	bf_write *pBitBuf;
   344 
   345 	if (g_IsMsgInExec)
   346 	{
   347 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   348 	}
   349 
   350 	pCtx->LocalToString(params[1], &msgname);
   351 
   352 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   353 	{
   354 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   355 	}
   356 
   357 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   358 
   359 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   360 	if (!pBitBuf)
   361 	{
   362 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   363 	}
   364 
   365 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   366 	g_IsMsgInExec = true;
   367 
   368 	return g_CurMsgHandle;
   369 }
   370 
   371 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   372 {
   373 	cell_t *cl_array;
   374 	bf_write *pBitBuf;
   375 	int msgid = params[1];
   376 
   377 	if (g_IsMsgInExec)
   378 	{
   379 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   380 	}
   381 
   382 	if (msgid < 0 || msgid >= 255)
   383 	{
   384 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   385 	}
   386 
   387 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   388 
   389 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], 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_EndMessage(IPluginContext *pCtx, const cell_t *params)
   402 {
   403 	HandleSecurity sec;
   404 
   405 	if (!g_IsMsgInExec)
   406 	{
   407 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   408 	}
   409 
   410 	g_UserMsgs.EndMessage();
   411 
   412 	sec.pOwner = pCtx->GetIdentity();
   413 	sec.pIdentity = g_pCoreIdent;
   414 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   415 
   416 	g_IsMsgInExec = false;
   417 
   418 	return 1;
   419 }
   420 
   421 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   422 {
   423 	IPluginFunction *pHook, *pNotify;
   424 	MsgListenerWrapper *pListener;
   425 	bool intercept;
   426 	int msgid = params[1];
   427 
   428 	if (msgid < 0 || msgid >= 255)
   429 	{
   430 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   431 	}
   432 
   433 	pHook = pCtx->GetFunctionById(params[2]);
   434 	if (!pHook)
   435 	{
   436 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   437 	}
   438 	pNotify = pCtx->GetFunctionById(params[4]);
   439 	intercept = (params[3]) ? true : false;
   440 
   441 	pListener = s_UsrMessageNatives.CreateListener(pCtx);
   442 	pListener->Initialize(msgid, pHook, pNotify, intercept);
   443 
   444 	g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
   445 
   446 	return 1;
   447 }
   448 
   449 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
   450 {
   451 	IPluginFunction *pFunc;
   452 	MsgListenerWrapper *pListener;
   453 	MsgWrapperIter iter;
   454 	bool intercept;
   455 	int msgid = params[1];
   456 
   457 	if (msgid < 0 || msgid >= 255)
   458 	{
   459 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   460 	}
   461 
   462 	pFunc = pCtx->GetFunctionById(params[2]);
   463 	if (!pFunc)
   464 	{
   465 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   466 	}
   467 	intercept = (params[3]) ? true : false;
   468 
   469 	iter = s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept);
   470 	if (iter == NULL)
   471 	{
   472 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   473 	}
   474 
   475 	pListener = (*iter);
   476 	if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
   477 	{
   478 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   479 	}
   480 
   481 	s_UsrMessageNatives.DeleteListener(pCtx, iter);
   482 
   483 	return 1;
   484 }
   485 
   486 REGISTER_NATIVES(usrmsgnatives)
   487 {
   488 	{"GetUserMessageId",			smn_GetUserMessageId},
   489 	{"GetUserMessageName",			smn_GetUserMessageName},
   490 	{"StartMessage",				smn_StartMessage},
   491 	{"StartMessageEx",				smn_StartMessageEx},
   492 	{"EndMessage",					smn_EndMessage},
   493 	{"HookUserMessage",				smn_HookUserMessage},
   494 	{"UnhookUserMessage",			smn_UnhookUserMessage},
   495 	{NULL,							NULL}
   496 };