core/UserMessages.cpp
author David Anderson <dvander@alliedmods.net>
Fri Sep 07 02:20:08 2007 +0000 (2007-09-07)
changeset 1399 0375333cbc07
parent 1330 ab94316bb52b
child 1690 7cc4f79a0416
permissions -rw-r--r--
added amb923 - CheckCommandAccess() changes
     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 "UserMessages.h"
    33 #include "sm_stringutil.h"
    34 
    35 UserMessages g_UserMsgs;
    36 
    37 SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
    38 SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
    39 
    40 UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
    41 {
    42 	m_Names = sm_trie_create();
    43 	m_HookCount = 0;
    44 	m_InExec = false;
    45 	m_InHook = false;
    46 	m_CurFlags = 0;
    47 	m_CurId = INVALID_MESSAGE_ID;
    48 }
    49 
    50 UserMessages::~UserMessages()
    51 {
    52 	sm_trie_destroy(m_Names);
    53 
    54 	CStack<ListenerInfo *>::iterator iter;
    55 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    56 	{
    57 		delete (*iter);
    58 	}
    59 	m_FreeListeners.popall();
    60 }
    61 
    62 void UserMessages::OnSourceModStartup(bool late)
    63 {
    64 	/* -1 means SourceMM was unable to get the user message list */
    65 	m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
    66 }
    67 
    68 void UserMessages::OnSourceModAllInitialized()
    69 {
    70 	g_ShareSys.AddInterface(NULL, this);
    71 }
    72 
    73 void UserMessages::OnSourceModAllShutdown()
    74 {
    75 	if (m_HookCount)
    76 	{
    77 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
    78 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
    79 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
    80 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
    81 	}
    82 	m_HookCount = 0;
    83 }
    84 
    85 int UserMessages::GetMessageIndex(const char *msg)
    86 {
    87 	int msgid;
    88 
    89 	if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
    90 	{
    91 		if (m_FallbackSearch)
    92 		{
    93 			char msgbuf[64];
    94 			int size;
    95 			msgid = 0;
    96 
    97 			while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
    98 			{
    99 				if (strcmp(msgbuf, msg) == 0)
   100 				{
   101 					sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
   102 					return msgid;
   103 				}
   104 				msgid++;
   105 			}
   106 		}
   107 
   108 		msgid = g_SMAPI->FindUserMessage(msg);
   109 
   110 		if (msgid != INVALID_MESSAGE_ID)
   111 		{
   112 			sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
   113 		}
   114 	}
   115 
   116 	return msgid;
   117 }
   118 
   119 bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
   120 {
   121 	if (m_FallbackSearch)
   122 	{
   123 		int size;
   124 		return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
   125 	}
   126 
   127 	const char *msg = g_SMAPI->GetUserMessage(msgid);
   128 
   129 	if (msg)
   130 	{
   131 		strncopy(buffer, msg, maxlength);
   132 		return true;
   133 	}
   134 
   135 	return false;
   136 }
   137 
   138 bf_write *UserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
   139 {
   140 	bf_write *buffer;
   141 
   142 	if (m_InExec || m_InHook)
   143 	{
   144 		return NULL;
   145 	}
   146 	if (msg_id < 0 || msg_id >= 255)
   147 	{
   148 		return NULL;
   149 	}
   150 
   151 	m_CellRecFilter.Initialize(players, playersNum);
   152 
   153 	m_CurFlags = flags;
   154 	if (m_CurFlags & USERMSG_INITMSG)
   155 	{
   156 		m_CellRecFilter.SetToInit(true);
   157 	}
   158 	if (m_CurFlags & USERMSG_RELIABLE)
   159 	{
   160 		m_CellRecFilter.SetToReliable(true);
   161 	}
   162 
   163 	m_InExec = true;
   164 
   165 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   166 	{
   167 		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   168 	} else {
   169 		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   170 	}
   171 
   172 	return buffer;
   173 }
   174 
   175 bool UserMessages::EndMessage()
   176 {
   177 	if (!m_InExec)
   178 	{
   179 		return false;
   180 	}
   181 
   182 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   183 	{
   184 		ENGINE_CALL(MessageEnd)();
   185 	} else {
   186 		engine->MessageEnd();
   187 	}
   188 
   189 	m_InExec = false;
   190 	m_CurFlags = 0;
   191 	m_CellRecFilter.Reset();
   192 
   193 	return true;
   194 }
   195 
   196 bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   197 {
   198 	if (msg_id < 0 || msg_id >= 255)
   199 	{
   200 		return false;
   201 	}
   202 
   203 	ListenerInfo *pInfo;
   204 	if (m_FreeListeners.empty())
   205 	{
   206 		pInfo = new ListenerInfo;
   207 	} else {
   208 		pInfo = m_FreeListeners.front();
   209 		m_FreeListeners.pop();
   210 	}
   211 
   212 	pInfo->Callback = pListener;
   213 	pInfo->IsHooked = false;
   214 	pInfo->KillMe = false;
   215 
   216 	if (!m_HookCount++)
   217 	{
   218 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
   219 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
   220 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
   221 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
   222 	}
   223 
   224 	if (intercept)
   225 	{
   226 		m_msgIntercepts[msg_id].push_back(pInfo);
   227 	} else {
   228 		m_msgHooks[msg_id].push_back(pInfo);
   229 	}
   230 
   231 	return true;
   232 }
   233 
   234 bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   235 {	
   236 	MsgList *pList;
   237 	MsgIter iter;
   238 	ListenerInfo *pInfo;
   239 	bool deleted = false;
   240 
   241 	if (msg_id < 0 || msg_id >= 255)
   242 	{
   243 		return false;
   244 	}
   245 
   246 	pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   247 	for (iter=pList->begin(); iter!=pList->end(); iter++)
   248 	{
   249 		pInfo = (*iter);
   250 		if (pInfo->Callback == pListener)
   251 		{
   252 			if (pInfo->IsHooked)
   253 			{
   254 				pInfo->KillMe = true;
   255 				return true;
   256 			}
   257 			pList->erase(iter);
   258 			deleted = true;
   259 			break;
   260 		}
   261 	}
   262 
   263 	if (deleted)
   264 	{
   265 		_DecRefCounter();
   266 	}
   267 
   268 	return deleted;
   269 }
   270 
   271 void UserMessages::_DecRefCounter()
   272 {
   273 	if (--m_HookCount == 0)
   274 	{
   275 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
   276 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
   277 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
   278 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
   279 	}
   280 }
   281 
   282 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   283 {
   284 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   285 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   286 
   287 	if ((is_intercept_empty && is_hook_empty)
   288 		|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
   289 	{
   290 		m_InHook = false;
   291 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   292 	}
   293 
   294 	m_CurId = msg_type;
   295 	m_CurRecFilter = filter;
   296 	m_InHook = true;
   297 	m_BlockEndPost = false;
   298 
   299 	if (!is_intercept_empty)
   300 	{
   301 		m_InterceptBuffer.Reset();
   302 		RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   303 	}
   304 
   305 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   306 }
   307 
   308 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   309 {
   310 	if (!m_InHook)
   311 	{
   312 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   313 	}
   314 
   315 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   316 
   317 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   318 }
   319 
   320 void UserMessages::OnMessageEnd_Post()
   321 {
   322 	if (!m_InHook || m_BlockEndPost)
   323 	{
   324 		RETURN_META(MRES_IGNORED);
   325 	}
   326 
   327 	MsgList *pList;
   328 	MsgIter iter;
   329 	ListenerInfo *pInfo;
   330 
   331 	pList = &m_msgIntercepts[m_CurId];
   332 	for (iter=pList->begin(); iter!=pList->end(); )
   333 	{
   334 		pInfo = (*iter);
   335 		pInfo->IsHooked = true;
   336 		pInfo->Callback->OnUserMessageSent(m_CurId);
   337 
   338 		if (pInfo->KillMe)
   339 		{
   340 			iter = pList->erase(iter);
   341 			m_FreeListeners.push(pInfo);
   342 			_DecRefCounter();
   343 			continue;
   344 		}
   345 
   346 		pInfo->IsHooked = false;
   347 		iter++;
   348 	}
   349 
   350 	m_InHook = false;
   351 
   352 	pList = &m_msgHooks[m_CurId];
   353 	for (iter=pList->begin(); iter!=pList->end(); )
   354 	{
   355 		pInfo = (*iter);
   356 		pInfo->IsHooked = true;
   357 		pInfo->Callback->OnUserMessageSent(m_CurId);
   358 
   359 		if (pInfo->KillMe)
   360 		{
   361 			iter = pList->erase(iter);
   362 			m_FreeListeners.push(pInfo);
   363 			_DecRefCounter();
   364 			continue;
   365 		}
   366 
   367 		pInfo->IsHooked = false;
   368 		iter++;
   369 	}
   370 }
   371 
   372 void UserMessages::OnMessageEnd_Pre()
   373 {
   374 	if (!m_InHook)
   375 	{
   376 		RETURN_META(MRES_IGNORED);
   377 	}
   378 
   379 	MsgList *pList;
   380 	MsgIter iter;
   381 	ListenerInfo *pInfo;
   382 
   383 	ResultType res;
   384 	bool intercepted = false;
   385 	bool handled = false;
   386 
   387 	pList = &m_msgIntercepts[m_CurId];
   388 	for (iter=pList->begin(); iter!=pList->end(); )
   389 	{
   390 		pInfo = (*iter);
   391 		pInfo->IsHooked = true;
   392 		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   393 
   394 		intercepted = true;
   395 
   396 		switch (res)
   397 		{
   398 		case Pl_Stop:
   399 			{
   400 				if (pInfo->KillMe)
   401 				{
   402 					pList->erase(iter);
   403 					m_FreeListeners.push(pInfo);
   404 					_DecRefCounter();
   405 					goto supercede;
   406 				}
   407 				pInfo->IsHooked = false;
   408 				goto supercede;
   409 			}
   410 		case Pl_Handled:
   411 			{
   412 				handled = true;
   413 				if (pInfo->KillMe)
   414 				{
   415 					iter = pList->erase(iter);
   416 					m_FreeListeners.push(pInfo);
   417 					_DecRefCounter();
   418 					continue;
   419 				}
   420 				break;
   421 			}
   422 		default:
   423 			{
   424 				if (pInfo->KillMe)
   425 				{
   426 					iter = pList->erase(iter);
   427 					m_FreeListeners.push(pInfo);
   428 					_DecRefCounter();
   429 					continue;
   430 				}
   431 				break;
   432 			}
   433 		}
   434 		pInfo->IsHooked = false;
   435 		iter++;
   436 	}
   437 
   438 	if (handled)
   439 	{
   440 		goto supercede;
   441 	}
   442 
   443 	if (intercepted)
   444 	{
   445 		bf_write *engine_bfw;
   446 
   447 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   448 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   449 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   450 		ENGINE_CALL(MessageEnd)();
   451 	}
   452 
   453 	pList = &m_msgHooks[m_CurId];
   454 	for (iter=pList->begin(); iter!=pList->end(); )
   455 	{
   456 		pInfo = (*iter);
   457 		pInfo->IsHooked = true;
   458 		pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   459 
   460 		if (pInfo->KillMe)
   461 		{
   462 			iter = pList->erase(iter);
   463 			m_FreeListeners.push(pInfo);
   464 			_DecRefCounter();
   465 			continue;
   466 		}
   467 
   468 		pInfo->IsHooked = false;
   469 		iter++;
   470 	}
   471 
   472 	RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
   473 supercede:
   474 	m_InHook = false;
   475 	m_BlockEndPost = true;
   476 	RETURN_META(MRES_SUPERCEDE);
   477 }