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