core/smn_usermsgs.cpp
author David Anderson <dvander@alliedmods.net>
Sun Dec 02 02:10:37 2007 +0000 (2007-12-02)
changeset 1738 cb3be0b31d99
parent 1541 e46ad208b9b4
child 1766 d30c252089be
permissions -rw-r--r--
- added beefy new ReadMapList() native
- admin menu now uses ReadMapList()
- added UTIL_TrimWhitespace() to stringutils
- moved GetFileTime() implementation to ILibrarySys
- cleaned up sorting include a bit
- removed adminmenu_maplist.ini, since it's specified by maplists.cfg
- added maplists.cfg
     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 	void OnPluginUnloaded(IPlugin *plugin);
    61 public:
    62 	MsgListenerWrapper *CreateListener(IPluginContext *pCtx);
    63 	bool FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter);
    64 	bool DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter);
    65 private:
    66 	CStack<MsgListenerWrapper *> m_FreeListeners;
    67 };
    68 
    69 UsrMessageNatives::~UsrMessageNatives()
    70 {
    71 	CStack<MsgListenerWrapper *>::iterator iter;
    72 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    73 	{
    74 		delete (*iter);
    75 	}
    76 	m_FreeListeners.popall();
    77 }
    78 
    79 void UsrMessageNatives::OnSourceModAllInitialized()
    80 {
    81 	HandleAccess sec;
    82 
    83 	g_HandleSys.InitAccessDefaults(NULL, &sec);
    84 	sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
    85 
    86 	g_WrBitBufType = g_HandleSys.CreateType("BitBufWriter", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    87 	g_RdBitBufType = g_HandleSys.CreateType("BitBufReader", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    88 
    89 	g_ReadBufHandle = g_HandleSys.CreateHandle(g_RdBitBufType, &g_ReadBitBuf, NULL, g_pCoreIdent, NULL);
    90 
    91 	g_PluginSys.AddPluginsListener(this);
    92 }
    93 
    94 void UsrMessageNatives::OnSourceModShutdown()
    95 {
    96 	HandleSecurity sec;
    97 	sec.pIdentity = g_pCoreIdent;
    98 
    99 	g_HandleSys.FreeHandle(g_ReadBufHandle, &sec);
   100 
   101 	g_HandleSys.RemoveType(g_WrBitBufType, g_pCoreIdent);
   102 	g_HandleSys.RemoveType(g_RdBitBufType, g_pCoreIdent);
   103 
   104 	g_WrBitBufType = 0;
   105 	g_RdBitBufType = 0;
   106 }
   107 
   108 void UsrMessageNatives::OnHandleDestroy(HandleType_t type, void *object)
   109 {
   110 }
   111 
   112 void UsrMessageNatives::OnPluginUnloaded(IPlugin *plugin)
   113 {
   114 	MsgWrapperList *pList;
   115 
   116 	if (plugin->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList), true))
   117 	{
   118 		MsgWrapperIter iter;
   119 		MsgListenerWrapper *pListener;
   120 
   121 		for (iter=pList->begin(); iter!=pList->end(); iter++)
   122 		{
   123 			pListener = (*iter);
   124 			if (g_UserMsgs.UnhookUserMessage(pListener->GetMessageId(), pListener, pListener->IsInterceptHook()))
   125 			{
   126 				m_FreeListeners.push(pListener);
   127 			}
   128 		}
   129 
   130 		delete pList;
   131 	}
   132 }
   133 
   134 MsgListenerWrapper *UsrMessageNatives::CreateListener(IPluginContext *pCtx)
   135 {
   136 	MsgWrapperList *pList;
   137 	MsgListenerWrapper *pListener;
   138 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   139 
   140 	if (m_FreeListeners.empty())
   141 	{
   142 		pListener = new MsgListenerWrapper;
   143 	} else {
   144 		pListener = m_FreeListeners.front();
   145 		m_FreeListeners.pop();
   146 	}
   147 
   148 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   149 	{
   150 		pList = new List<MsgListenerWrapper *>;
   151 		pl->SetProperty("MsgListeners", pList);
   152 	}
   153 
   154 	pList->push_back(pListener);
   155 
   156 	return pListener;
   157 }
   158 
   159 bool UsrMessageNatives::FindListener(int msgid, IPluginContext *pCtx, IPluginFunction *pHook, bool intercept, MsgWrapperIter *iter)
   160 {
   161 	MsgWrapperList *pList;
   162 	MsgWrapperIter _iter;
   163 	MsgListenerWrapper *pListener;
   164 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   165 
   166 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   167 	{
   168 		return false;
   169 	}
   170 
   171 	for (_iter=pList->begin(); _iter!=pList->end(); _iter++)
   172 	{
   173 		pListener = (*_iter);
   174 		if ((msgid == pListener->GetMessageId()) 
   175 			&& (intercept == pListener->IsInterceptHook()) 
   176 			&& (pHook == pListener->GetHookedFunction()))
   177 		{
   178 			*iter = _iter;
   179 			return true;
   180 		}
   181 	}
   182 
   183 	return false;
   184 }
   185 
   186 bool UsrMessageNatives::DeleteListener(IPluginContext *pCtx, MsgWrapperIter iter)
   187 {
   188 	MsgWrapperList *pList;
   189 	MsgListenerWrapper *pListener;
   190 	IPlugin *pl = g_PluginSys.FindPluginByContext(pCtx->GetContext());
   191 
   192 	if (!pl->GetProperty("MsgListeners", reinterpret_cast<void **>(&pList)))
   193 	{
   194 		return false;
   195 	}
   196 
   197 	pListener = (*iter);
   198 	pList->erase(iter);
   199 	m_FreeListeners.push(pListener);
   200 
   201 	return true;
   202 }
   203 
   204 /***************************************
   205  *                                     *
   206  * USER MESSAGE WRAPPER IMPLEMENTATION *
   207  *                                     *
   208  ***************************************/
   209 
   210 void MsgListenerWrapper::Initialize(int msgid, IPluginFunction *hook, IPluginFunction *notify, bool intercept)
   211 {
   212 	if (intercept)
   213 	{
   214 		m_Intercept = hook;
   215 		m_Hook = NULL;
   216 	} else {
   217 		m_Hook = hook;
   218 		m_Intercept = NULL;
   219 	}
   220 
   221 	if (notify)
   222 	{
   223 		m_Notify = notify;
   224 	} else {
   225 		m_Notify = NULL;
   226 	}
   227 
   228 	m_MsgId = msgid;
   229 	m_IsInterceptHook = intercept;
   230 }
   231 
   232 size_t MsgListenerWrapper::_FillInPlayers(int *pl_array, IRecipientFilter *pFilter)
   233 {
   234 	size_t size = static_cast<size_t>(pFilter->GetRecipientCount());
   235 
   236 	for (size_t i=0; i<size; i++)
   237 	{
   238 		pl_array[i] = pFilter->GetRecipientIndex(i);
   239 	}
   240 
   241 	return size;
   242 }
   243 
   244 bool MsgListenerWrapper::IsInterceptHook() const
   245 {
   246 	return m_IsInterceptHook;
   247 }
   248 
   249 int MsgListenerWrapper::GetMessageId() const
   250 {
   251 	return m_MsgId;
   252 }
   253 
   254 IPluginFunction *MsgListenerWrapper::GetHookedFunction() const
   255 {
   256 	if (m_Hook)
   257 	{
   258 		return m_Hook;
   259 	} else {
   260 		return m_Intercept;
   261 	}
   262 }
   263 
   264 IPluginFunction *MsgListenerWrapper::GetNotifyFunction() const
   265 {
   266 	return m_Notify;
   267 }
   268 
   269 void MsgListenerWrapper::OnUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   270 {
   271 	cell_t res;
   272 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   273 
   274 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   275 
   276 	m_Hook->PushCell(msg_id);
   277 	m_Hook->PushCell(g_ReadBufHandle);
   278 	m_Hook->PushArray(g_MsgPlayers, size);
   279 	m_Hook->PushCell(size);
   280 	m_Hook->PushCell(pFilter->IsReliable());
   281 	m_Hook->PushCell(pFilter->IsInitMessage());
   282 	m_Hook->Execute(&res);
   283 }
   284 
   285 ResultType MsgListenerWrapper::InterceptUserMessage(int msg_id, bf_write *bf, IRecipientFilter *pFilter)
   286 {
   287 	cell_t res = static_cast<cell_t>(Pl_Continue);
   288 	size_t size = _FillInPlayers(g_MsgPlayers, pFilter);
   289 
   290 	g_ReadBitBuf.StartReading(bf->GetBasePointer(), bf->GetNumBytesWritten());
   291 
   292 	m_Intercept->PushCell(msg_id);
   293 	m_Intercept->PushCell(g_ReadBufHandle);
   294 	m_Intercept->PushArray(g_MsgPlayers, size);
   295 	m_Intercept->PushCell(size);
   296 	m_Intercept->PushCell(pFilter->IsReliable());
   297 	m_Intercept->PushCell(pFilter->IsInitMessage());
   298 	m_Intercept->Execute(&res);
   299 
   300 	return static_cast<ResultType>(res);
   301 }
   302 
   303 void MsgListenerWrapper::OnUserMessageSent(int msg_id)
   304 {
   305 	if (!m_Notify)
   306 	{
   307 		return;
   308 	}
   309 
   310 	cell_t res;
   311 	m_Notify->PushCell(msg_id);
   312 	m_Notify->Execute(&res);
   313 }
   314 
   315 /***************************************
   316  *                                     *
   317  * USER MESSAGE NATIVE IMPLEMENTATIONS *
   318  *                                     *
   319  ***************************************/
   320 
   321 static UsrMessageNatives s_UsrMessageNatives;
   322 
   323 static cell_t smn_GetUserMessageId(IPluginContext *pCtx, const cell_t *params)
   324 {
   325 	char *msgname;
   326 	pCtx->LocalToString(params[1], &msgname);
   327 
   328 	return g_UserMsgs.GetMessageIndex(msgname);
   329 }
   330 
   331 static cell_t smn_GetUserMessageName(IPluginContext *pCtx, const cell_t *params)
   332 {
   333 	char *msgname;
   334 
   335 	pCtx->LocalToPhysAddr(params[2], (cell_t **)&msgname);
   336 
   337 	return g_UserMsgs.GetMessageName(params[1], msgname, params[3]);
   338 }
   339 
   340 static cell_t smn_StartMessage(IPluginContext *pCtx, const cell_t *params)
   341 {
   342 	char *msgname;
   343 	cell_t *cl_array;
   344 	int msgid;
   345 	bf_write *pBitBuf;
   346 
   347 	if (g_IsMsgInExec)
   348 	{
   349 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   350 	}
   351 
   352 	pCtx->LocalToString(params[1], &msgname);
   353 
   354 	if ((msgid=g_UserMsgs.GetMessageIndex(msgname)) == INVALID_MESSAGE_ID)
   355 	{
   356 		return pCtx->ThrowNativeError("Invalid message name: \"%s\"", msgname);
   357 	}
   358 
   359 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   360 
   361 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   362 	if (!pBitBuf)
   363 	{
   364 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   365 	}
   366 
   367 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   368 	g_IsMsgInExec = true;
   369 
   370 	return g_CurMsgHandle;
   371 }
   372 
   373 static cell_t smn_StartMessageEx(IPluginContext *pCtx, const cell_t *params)
   374 {
   375 	cell_t *cl_array;
   376 	bf_write *pBitBuf;
   377 	int msgid = params[1];
   378 
   379 	if (g_IsMsgInExec)
   380 	{
   381 		return pCtx->ThrowNativeError("Unable to execute a new message, there is already one in progress");
   382 	}
   383 
   384 	if (msgid < 0 || msgid >= 255)
   385 	{
   386 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   387 	}
   388 
   389 	pCtx->LocalToPhysAddr(params[2], &cl_array);
   390 
   391 	pBitBuf = g_UserMsgs.StartMessage(msgid, cl_array, params[3], params[4]);
   392 	if (!pBitBuf)
   393 	{
   394 		return pCtx->ThrowNativeError("Unable to execute a new message while in hook");
   395 	}
   396 
   397 	g_CurMsgHandle = g_HandleSys.CreateHandle(g_WrBitBufType, pBitBuf, pCtx->GetIdentity(), g_pCoreIdent, NULL);
   398 	g_IsMsgInExec = true;
   399 
   400 	return g_CurMsgHandle;
   401 }
   402 
   403 static cell_t smn_EndMessage(IPluginContext *pCtx, const cell_t *params)
   404 {
   405 	HandleSecurity sec;
   406 
   407 	if (!g_IsMsgInExec)
   408 	{
   409 		return pCtx->ThrowNativeError("Unable to end message, no message is in progress");
   410 	}
   411 
   412 	g_UserMsgs.EndMessage();
   413 
   414 	sec.pOwner = pCtx->GetIdentity();
   415 	sec.pIdentity = g_pCoreIdent;
   416 	g_HandleSys.FreeHandle(g_CurMsgHandle, &sec);
   417 
   418 	g_IsMsgInExec = false;
   419 
   420 	return 1;
   421 }
   422 
   423 static cell_t smn_HookUserMessage(IPluginContext *pCtx, const cell_t *params)
   424 {
   425 	IPluginFunction *pHook, *pNotify;
   426 	MsgListenerWrapper *pListener;
   427 	bool intercept;
   428 	int msgid = params[1];
   429 
   430 	if (msgid < 0 || msgid >= 255)
   431 	{
   432 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   433 	}
   434 
   435 	pHook = pCtx->GetFunctionById(params[2]);
   436 	if (!pHook)
   437 	{
   438 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   439 	}
   440 	pNotify = pCtx->GetFunctionById(params[4]);
   441 	intercept = (params[3]) ? true : false;
   442 
   443 	pListener = s_UsrMessageNatives.CreateListener(pCtx);
   444 	pListener->Initialize(msgid, pHook, pNotify, intercept);
   445 
   446 	g_UserMsgs.HookUserMessage(msgid, pListener, intercept);
   447 
   448 	return 1;
   449 }
   450 
   451 static cell_t smn_UnhookUserMessage(IPluginContext *pCtx, const cell_t *params)
   452 {
   453 	IPluginFunction *pFunc;
   454 	MsgListenerWrapper *pListener;
   455 	MsgWrapperIter iter;
   456 	bool intercept;
   457 	int msgid = params[1];
   458 
   459 	if (msgid < 0 || msgid >= 255)
   460 	{
   461 		return pCtx->ThrowNativeError("Invalid message id supplied (%d)", msgid);
   462 	}
   463 
   464 	pFunc = pCtx->GetFunctionById(params[2]);
   465 	if (!pFunc)
   466 	{
   467 		return pCtx->ThrowNativeError("Invalid function id (%X)", params[2]);
   468 	}
   469 	intercept = (params[3]) ? true : false;
   470 
   471 	if (!s_UsrMessageNatives.FindListener(msgid, pCtx, pFunc, intercept, &iter))
   472 	{
   473 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   474 	}
   475 
   476 	pListener = (*iter);
   477 	if (!g_UserMsgs.UnhookUserMessage(msgid, pListener, intercept))
   478 	{
   479 		return pCtx->ThrowNativeError("Unable to unhook the current user message");
   480 	}
   481 
   482 	s_UsrMessageNatives.DeleteListener(pCtx, iter);
   483 
   484 	return 1;
   485 }
   486 
   487 REGISTER_NATIVES(usrmsgnatives)
   488 {
   489 	{"GetUserMessageId",			smn_GetUserMessageId},
   490 	{"GetUserMessageName",			smn_GetUserMessageName},
   491 	{"StartMessage",				smn_StartMessage},
   492 	{"StartMessageEx",				smn_StartMessageEx},
   493 	{"EndMessage",					smn_EndMessage},
   494 	{"HookUserMessage",				smn_HookUserMessage},
   495 	{"UnhookUserMessage",			smn_UnhookUserMessage},
   496 	{NULL,							NULL}
   497 };