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