core/smn_usermsgs.cpp
author David Anderson <dvander@alliedmods.net>
Mon Dec 10 22:20:53 2007 +0000 (2007-12-10)
changeset 1783 e6e2f440376d
parent 1766 d30c252089be
child 1871 a152bfe8634b
permissions -rw-r--r--
- fixed amb1237 part 1 - maplists.cfg missing could cause ReadMapList() to fail
- fixed amb1237 part 2 - nextmap could fail in certain cases with mapchooser present
     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 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 "smn_usermsgs.h"
    36 
    37 HandleType_t g_WrBitBufType;
    38 HandleType_t g_RdBitBufType;
    39 Handle_t g_CurMsgHandle;
    40 Handle_t g_ReadBufHandle;
    41 bf_read g_ReadBitBuf;
    42 
    43 int g_MsgPlayers[256];
    44 bool g_IsMsgInExec = false;
    45 
    46 typedef List<MsgListenerWrapper *> MsgWrapperList;
    47 typedef List<MsgListenerWrapper *>::iterator MsgWrapperIter;
    48 
    49 class UsrMessageNatives :
    50 	public SMGlobalClass,
    51 	public IHandleTypeDispatch,
    52 	public IPluginsListener
    53 {
    54 public:
    55 	~UsrMessageNatives();
    56 public: //SMGlobalClass, IHandleTypeDispatch, IPluginListener
    57 	void OnSourceModAllInitialized();
    58 	void OnSourceModShutdown();
    59 	void OnHandleDestroy(HandleType_t type, void *object);
    60 	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
    61 	void OnPluginUnloaded(IPlugin *plugin);
    62 public:
    63 	MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
    64 	bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
    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 
    84 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    85 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
    86 
    87 	g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    88 	g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    89 
    90 	g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
    91 
    92 	g_PluginSys.AddPluginsListener(this);
    93 }
    94 
    95 void UsrMessageNatives::OnSourceModShutdown()
    96 {
    97 	HandleSecurity sec;
    98 	sec.pIdentity = g_pCoreIdent;
    99 
   100 	g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
   101 
   102 	g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
   103 	g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
   104 
   105 	g_WrBitBufType = 0;
   106 	g_RdBitBufType = 0;
   107 }
   108 
   109 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
   110 {
   111 }
   112 
   113 bool UsrMessageNatives::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   114 {
   115 	bf_read *pRead = (bf_read *)object;
   116 
   117 	*pSize = sizeof(bf_read) + pRead->GetNumBytesRead() + pRead->GetNumBytesLeft();
   118 
   119 	return true;
   120 }
   121 
   122 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
   123 {
   124 	MsgWrapperList *pList;
   125 
   126 	if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
   127 	{
   128 		MsgWrapperIter iter;
   129 		MsgListenerWrapper *pListener;
   130 
   131 		for (iter=pList->begin(); iter!=pList->end(); iter++)
   132 		{
   133 			pListener = (*iter);
   134 			if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
   135 			{
   136 				m_FreeListeners.push(pListener);
   137 			}
   138 		}
   139 
   140 		delete pList;
   141 	}
   142 }
   143 
   144 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
   145 {
   146 	MsgWrapperList *pList;
   147 	MsgListenerWrapper *pListener;
   148 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   149 
   150 	if (m_FreeListeners.empty())
   151 	{
   152 		pListener = new MsgListenerWrapper;
   153 	} else {
   154 		pListener = m_FreeListeners.front();
   155 		m_FreeListeners.pop();
   156 	}
   157 
   158 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   159 	{
   160 		pList = new List<MsgListenerWrapper *>;
   161 		pl->SetProperty("MsgListeners", pList);
   162 	}
   163 
   164 	pList->push_back(pListener);
   165 
   166 	return pListener;
   167 }
   168 
   169 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
   170 {
   171 	MsgWrapperList *pList;
   172 	MsgWrapperIter _iter;
   173 	MsgListenerWrapper *pListener;
   174 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   175 
   176 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   177 	{
   178 		return false;
   179 	}
   180 
   181 	for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
   182 	{
   183 		pListener = (*_iter);
   184 		if ((msgid == pListener->GetMessageId()) 
   185 			&& (intercept == pListener->IsInterceptHook()) 
   186 			&& (pHook == pListener->GetHookedFunction()))
   187 		{
   188 			*iter = _iter;
   189 			return true;
   190 		}
   191 	}
   192 
   193 	return false;
   194 }
   195 
   196 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
   197 {
   198 	MsgWrapperList *pList;
   199 	MsgListenerWrapper *pListener;
   200 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   201 
   202 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   203 	{
   204 		return false;
   205 	}
   206 
   207 	pListener = (*iter);
   208 	pList->erase(iter);
   209 	m_FreeListeners.push(pListener);
   210 
   211 	return true;
   212 }
   213 
   214 /***************************************
   215  *                                     *
   216  * USER MESSAGE WRAPPER IMPLEMENTATION *
   217  *                                     *
   218  ***************************************/
   219 
   220 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
   221 {
   222 	if (intercept)
   223 	{
   224 		m_Intercept = hook;
   225 		m_Hook = NULL;
   226 	} else {
   227 		m_Hook = hook;
   228 		m_Intercept = NULL;
   229 	}
   230 
   231 	if (notify)
   232 	{
   233 		m_Notify = notify;
   234 	} else {
   235 		m_Notify = NULL;
   236 	}
   237 
   238 	m_MsgId = msgid;
   239 	m_IsInterceptHook = intercept;
   240 }
   241 
   242 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
   243 {
   244 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
   245 
   246 	for (size_t i=0; i<size; i++)
   247 	{
   248 		pl_array[i] = pFilter->GetRecipientIndex(i);
   249 	}
   250 
   251 	return size;
   252 }
   253 
   254 bool MsgListenerWrapper::IsInterceptHook() const
   255 {
   256 	return m_IsInterceptHook;
   257 }
   258 
   259 int MsgListenerWrapper::GetMessageId() const
   260 {
   261 	return m_MsgId;
   262 }
   263 
   264 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
   265 {
   266 	if (m_Hook)
   267 	{
   268 		return m_Hook;
   269 	} else {
   270 		return m_Intercept;
   271 	}
   272 }
   273 
   274 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
   275 {
   276 	return m_Notify;
   277 }
   278 
   279 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   280 {
   281 	cell_t res;
   282 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   283 
   284 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   285 
   286 	m_Hook->PushCell(msg_id);
   287 	m_Hook->PushCell(g_ReadBufHandle);
   288 	m_Hook->PushArray(g_MsgPlayers, size);
   289 	m_Hook->PushCell(size);
   290 	m_Hook->PushCell(pFilter->IsReliable());
   291 	m_Hook->PushCell(pFilter->IsInitMessage());
   292 	m_Hook->Execute(&res);
   293 }
   294 
   295 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   296 {
   297 	cell_t res = static_cast<cell_t>(Pl_Continue);
   298 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   299 
   300 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   301 
   302 	m_Intercept->PushCell(msg_id);
   303 	m_Intercept->PushCell(g_ReadBufHandle);
   304 	m_Intercept->PushArray(g_MsgPlayers, size);
   305 	m_Intercept->PushCell(size);
   306 	m_Intercept->PushCell(pFilter->IsReliable());
   307 	m_Intercept->PushCell(pFilter->IsInitMessage());
   308 	m_Intercept->Execute(&res);
   309 
   310 	return static_cast<ResultType>(res);
   311 }
   312 
   313 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
   314 {
   315 	if (!m_Notify)
   316 	{
   317 		return;
   318 	}
   319 
   320 	cell_t res;
   321 	m_Notify->PushCell(msg_id);
   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 	int msgid;
   355 	bf_write *pBitBuf;
   356 
   357 	if (g_IsMsgInExec)
   358 	{
   359 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   360 	}
   361 
   362 	pCtx->LocalToString(params[1], &msgname);
   363 
   364 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   365 	{
   366 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   367 	}
   368 
   369 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   370 
   371 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   372 	if (!pBitBuf)
   373 	{
   374 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   375 	}
   376 
   377 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   378 	g_IsMsgInExec = true;
   379 
   380 	return g_CurMsgHandle;
   381 }
   382 
   383 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   384 {
   385 	cell_t *cl_array;
   386 	bf_write *pBitBuf;
   387 	int msgid = params[1];
   388 
   389 	if (g_IsMsgInExec)
   390 	{
   391 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   392 	}
   393 
   394 	if (msgid < 0 || msgid >= 255)
   395 	{
   396 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   397 	}
   398 
   399 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   400 
   401 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   402 	if (!pBitBuf)
   403 	{
   404 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   405 	}
   406 
   407 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   408 	g_IsMsgInExec = true;
   409 
   410 	return g_CurMsgHandle;
   411 }
   412 
   413 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
   414 {
   415 	HandleSecurity sec;
   416 
   417 	if (!g_IsMsgInExec)
   418 	{
   419 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   420 	}
   421 
   422 	g_UserMsgs.EndMessage();
   423 
   424 	sec.pOwner = pCtx->GetIdentity();
   425 	sec.pIdentity = g_pCoreIdent;
   426 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   427 
   428 	g_IsMsgInExec = false;
   429 
   430 	return 1;
   431 }
   432 
   433 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   434 {
   435 	IPluginFunction *pHook, *pNotify;
   436 	MsgListenerWrapper *pListener;
   437 	bool intercept;
   438 	int msgid = params[1];
   439 
   440 	if (msgid < 0 || msgid >= 255)
   441 	{
   442 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   443 	}
   444 
   445 	pHook = pCtx->GetFunctionById(params[2]);
   446 	if (!pHook)
   447 	{
   448 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   449 	}
   450 	pNotify = pCtx->GetFunctionById(params[4]);
   451 	intercept = (params[3]) ? true : false;
   452 
   453 	pListener = s_UsrMessageNatives.CreateListener(pCtx);
   454 	pListener->Initialize(msgid, pHook, pNotify, intercept);
   455 
   456 	g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
   457 
   458 	return 1;
   459 }
   460 
   461 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
   462 {
   463 	IPluginFunction *pFunc;
   464 	MsgListenerWrapper *pListener;
   465 	MsgWrapperIter iter;
   466 	bool intercept;
   467 	int msgid = params[1];
   468 
   469 	if (msgid < 0 || msgid >= 255)
   470 	{
   471 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   472 	}
   473 
   474 	pFunc = pCtx->GetFunctionById(params[2]);
   475 	if (!pFunc)
   476 	{
   477 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   478 	}
   479 	intercept = (params[3]) ? true : false;
   480 
   481 	if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
   482 	{
   483 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   484 	}
   485 
   486 	pListener = (*iter);
   487 	if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
   488 	{
   489 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   490 	}
   491 
   492 	s_UsrMessageNatives.DeleteListener(pCtx, iter);
   493 
   494 	return 1;
   495 }
   496 
   497 REGISTER_NATIVES(usrmsgnatives)
   498 {
   499 	{"GetUserMessageId",			smn_GetUserMessageId},
   500 	{"GetUserMessageName",			smn_GetUserMessageName},
   501 	{"StartMessage",				smn_StartMessage},
   502 	{"StartMessageEx",				smn_StartMessageEx},
   503 	{"EndMessage",					smn_EndMessage},
   504 	{"HookUserMessage",				smn_HookUserMessage},
   505 	{"UnhookUserMessage",			smn_UnhookUserMessage},
   506 	{NULL,							NULL}
   507 };