core/UserMessages.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sat Aug 03 19:03:12 2013 -0400 (2013-08-03)
changeset 3969 fe179ff9a57a
parent 3872 4c93f74cae51
child 4073 56283c919b7b
permissions -rw-r--r--
Triggering build.
     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 #include "logic_bridge.h"
    35 
    36 #if SOURCE_ENGINE == SE_DOTA
    37 #include <dota_usermessage_helpers.h>
    38 #elif SOURCE_ENGINE == SE_CSGO
    39 #include <cstrike15_usermessage_helpers.h>
    40 #endif
    41 
    42 UserMessages g_UserMsgs;
    43 
    44 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
    45 SH_DECL_HOOK3_void(IVEngineServer, SendUserMessage, SH_NOATTRIB, 0, IRecipientFilter &, int, const protobuf::Message &);
    46 #else
    47 #if SOURCE_ENGINE >= SE_LEFT4DEAD
    48 SH_DECL_HOOK3(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int, const char *);
    49 #else
    50 SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
    51 #endif
    52 SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
    53 #endif // ==SE_CSGO
    54 
    55 UserMessages::UserMessages()
    56 #ifndef USE_PROTOBUF_USERMESSAGES
    57 	: m_InterceptBuffer(m_pBase, 2500)
    58 {
    59 	m_Names = sm_trie_create();
    60 #else
    61 	: m_InterceptBuffer(NULL)
    62 {
    63 #endif
    64 	m_HookCount = 0;
    65 	m_InExec = false;
    66 	m_InHook = false;
    67 	m_CurFlags = 0;
    68 	m_CurId = INVALID_MESSAGE_ID;
    69 }
    70 
    71 UserMessages::~UserMessages()
    72 {
    73 #ifndef USE_PROTOBUF_USERMESSAGES
    74 	sm_trie_destroy(m_Names);
    75 #endif
    76 
    77 	CStack<ListenerInfo *>::iterator iter;
    78 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    79 	{
    80 		delete (*iter);
    81 	}
    82 	m_FreeListeners.popall();
    83 }
    84 
    85 void UserMessages::OnSourceModStartup(bool late)
    86 {
    87 #ifndef USE_PROTOBUF_USERMESSAGES
    88 	/* -1 means SourceMM was unable to get the user message list */
    89 	m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
    90 #endif
    91 }
    92 
    93 void UserMessages::OnSourceModAllInitialized()
    94 {
    95 	sharesys->AddInterface(NULL, this);
    96 }
    97 
    98 void UserMessages::OnSourceModAllShutdown()
    99 {
   100 	if (m_HookCount)
   101 	{
   102 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   103 		SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Pre), false);
   104 		SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Post), true);
   105 #else
   106 		SH_REMOVE_HOOK(IVEngineServer, UserMessageBegin, engine, SH_MEMBER(this, &UserMessages::OnStartMessage_Pre), false);
   107 		SH_REMOVE_HOOK(IVEngineServer, UserMessageBegin, engine, SH_MEMBER(this, &UserMessages::OnStartMessage_Post), true);
   108 		SH_REMOVE_HOOK(IVEngineServer, MessageEnd, engine, SH_MEMBER(this, &UserMessages::OnMessageEnd_Pre), false);
   109 		SH_REMOVE_HOOK(IVEngineServer, MessageEnd, engine, SH_MEMBER(this, &UserMessages::OnMessageEnd_Post), true);
   110 #endif
   111 	}
   112 	m_HookCount = 0;
   113 }
   114 
   115 int UserMessages::GetMessageIndex(const char *msg)
   116 {
   117 #if SOURCE_ENGINE == SE_DOTA
   118 	// Can split this per engine and/or game later
   119 	return g_DotaUsermessageHelpers.GetIndex(msg);
   120 #elif SOURCE_ENGINE == SE_CSGO
   121 	// Can split this per engine and/or game later
   122 	return g_Cstrike15UsermessageHelpers.GetIndex(msg);
   123 #else
   124 	int msgid;
   125 
   126 	if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
   127 	{
   128 		if (m_FallbackSearch)
   129 		{
   130 			char msgbuf[64];
   131 			int size;
   132 			msgid = 0;
   133 
   134 			while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
   135 			{
   136 				if (strcmp(msgbuf, msg) == 0)
   137 				{
   138 					sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
   139 					return msgid;
   140 				}
   141 				msgid++;
   142 			}
   143 		}
   144 
   145 		msgid = g_SMAPI->FindUserMessage(msg);
   146 
   147 		if (msgid != INVALID_MESSAGE_ID)
   148 		{
   149 			sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
   150 		}
   151 	}
   152 
   153 	return msgid;
   154 #endif
   155 }
   156 
   157 bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
   158 {
   159 #ifdef USE_PROTOBUF_USERMESSAGES
   160 #if SOURCE_ENGINE == SE_DOTA
   161 	const char *pszName = g_DotaUsermessageHelpers.GetName(msgid);
   162 #elif SOURCE_ENGINE == SE_CSGO
   163 	const char *pszName = g_Cstrike15UsermessageHelpers.GetName(msgid);
   164 #endif
   165 	if (!pszName)
   166 		return false;
   167 
   168 	strncopy(buffer, pszName, maxlength);
   169 	return true;
   170 #else
   171 	if (m_FallbackSearch)
   172 	{
   173 		int size;
   174 		return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
   175 	}
   176 
   177 	const char *msg = g_SMAPI->GetUserMessage(msgid);
   178 
   179 	if (msg)
   180 	{
   181 		strncopy(buffer, msg, maxlength);
   182 		return true;
   183 	}
   184 
   185 	return false;
   186 #endif
   187 }
   188 
   189 bf_write *UserMessages::StartBitBufMessage(int msg_id, const cell_t players[], unsigned int playersNum, int flags)
   190 {
   191 #ifdef USE_PROTOBUF_USERMESSAGES
   192 	return NULL;
   193 #else
   194 	bf_write *buffer;
   195 
   196 	if (m_InExec || m_InHook)
   197 	{
   198 		return NULL;
   199 	}
   200 	if (msg_id < 0 || msg_id >= 255)
   201 	{
   202 		return NULL;
   203 	}
   204 
   205 	m_CellRecFilter.Initialize(players, playersNum);
   206 
   207 	m_CurFlags = flags;
   208 	if (m_CurFlags & USERMSG_INITMSG)
   209 	{
   210 		m_CellRecFilter.SetToInit(true);
   211 	}
   212 	if (m_CurFlags & USERMSG_RELIABLE)
   213 	{
   214 		m_CellRecFilter.SetToReliable(true);
   215 	}
   216 
   217 	m_InExec = true;
   218 
   219 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   220 	{
   221 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   222 		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, g_SMAPI->GetUserMessage(msg_id));
   223 #else
   224 		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   225 #endif
   226 	} else {
   227 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   228 		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, g_SMAPI->GetUserMessage(msg_id));
   229 #else
   230 		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   231 #endif
   232 	}
   233 
   234 	return buffer;
   235 #endif // USE_PROTOBUF_USERMESSAGES
   236 }
   237 
   238 google::protobuf::Message *UserMessages::StartProtobufMessage(int msg_id, const cell_t players[], unsigned int playersNum, int flags)
   239 {
   240 #ifndef USE_PROTOBUF_USERMESSAGES
   241 	return NULL;
   242 #else
   243 	protobuf::Message *buffer;
   244 
   245 	if (m_InExec || m_InHook)
   246 	{
   247 		return NULL;
   248 	}
   249 	if (msg_id < 0 || msg_id >= 255)
   250 	{
   251 		return NULL;
   252 	}
   253 
   254 	m_CurId = msg_id;
   255 	m_CellRecFilter.Initialize(players, playersNum);
   256 
   257 	m_CurFlags = flags;
   258 	if (m_CurFlags & USERMSG_INITMSG)
   259 	{
   260 		m_CellRecFilter.SetToInit(true);
   261 	}
   262 	if (m_CurFlags & USERMSG_RELIABLE)
   263 	{
   264 		m_CellRecFilter.SetToReliable(true);
   265 	}
   266 
   267 	m_InExec = true;
   268 
   269 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   270 	{
   271 		// direct message creation, return buffer "from engine". keep track
   272 		m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
   273 		buffer = m_FakeEngineBuffer;
   274 	} else {
   275 		char messageName[32];
   276 		if (!GetMessageName(msg_id, messageName, sizeof(messageName)))
   277 		{
   278 			m_InExec = false;
   279 			return NULL;
   280 		}
   281 
   282 		protobuf::Message *msg = OnStartMessage_Pre(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
   283 		switch (m_FakeMetaRes)
   284 		{
   285 		case MRES_IGNORED:
   286 		case MRES_HANDLED:
   287 			m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
   288 			buffer = m_FakeEngineBuffer;
   289 			break;		
   290 
   291 		case MRES_OVERRIDE:
   292 			m_FakeEngineBuffer = GetMessagePrototype(msg_id)->New();
   293 		// fallthrough
   294 		case MRES_SUPERCEDE:
   295 			buffer = msg;
   296 			break;
   297 		}
   298 		
   299 		OnStartMessage_Post(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, messageName);
   300 	}
   301 
   302 	return buffer;
   303 #endif // USE_PROTOBUF_USERMESSAGES
   304 }
   305 
   306 bool UserMessages::EndMessage()
   307 {
   308 	if (!m_InExec)
   309 	{
   310 		return false;
   311 	}
   312 
   313 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   314 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   315 	{
   316 		ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
   317 		delete m_FakeEngineBuffer;
   318 		m_FakeEngineBuffer = NULL;
   319 	} else {
   320 		OnMessageEnd_Pre();
   321 
   322 		switch (m_FakeMetaRes)
   323 		{
   324 		case MRES_IGNORED:
   325 		case MRES_HANDLED:
   326 		case MRES_OVERRIDE:
   327 			engine->SendUserMessage(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
   328 			delete m_FakeEngineBuffer;
   329 			m_FakeEngineBuffer = NULL;
   330 			break;
   331 		//case MRES_SUPERCEDE:
   332 		}
   333 
   334 		OnMessageEnd_Post();
   335 	}
   336 #else
   337 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   338 	{
   339 		ENGINE_CALL(MessageEnd)();
   340 	} else {
   341 		engine->MessageEnd();
   342 	}
   343 #endif // SE_CSGO || SE_DOTA
   344 
   345 	m_InExec = false;
   346 	m_CurFlags = 0;
   347 	m_CellRecFilter.Reset();
   348 
   349 	return true;
   350 }
   351 
   352 UserMessageType UserMessages::GetUserMessageType() const
   353 {
   354 #ifdef USE_PROTOBUF_USERMESSAGES
   355 	return UM_Protobuf;
   356 #else
   357 	return UM_BitBuf;
   358 #endif
   359 }
   360 
   361 bool UserMessages::HookUserMessage2(int msg_id,
   362 									IUserMessageListener *pListener,
   363 									bool intercept)
   364 {
   365 #ifdef USE_PROTOBUF_USERMESSAGES
   366 	return InternalHook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, true);
   367 #else
   368 	return InternalHook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, true);
   369 #endif
   370 }
   371 
   372 bool UserMessages::UnhookUserMessage2(int msg_id,
   373 									  IUserMessageListener *pListener,
   374 									  bool intercept)
   375 {
   376 #ifdef USE_PROTOBUF_USERMESSAGES
   377 	return InternalUnhook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, true);
   378 #else
   379 	return InternalUnhook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, true);
   380 #endif
   381 }
   382 
   383 bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   384 {
   385 #ifdef USE_PROTOBUF_USERMESSAGES
   386 	return InternalHook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, false);
   387 #else
   388 	return InternalHook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, false);
   389 #endif
   390 }
   391 
   392 bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   393 {	
   394 #ifdef USE_PROTOBUF_USERMESSAGES
   395 	return InternalUnhook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, false);
   396 #else
   397 	return InternalUnhook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, false);
   398 #endif
   399 }
   400 
   401 #ifdef USE_PROTOBUF_USERMESSAGES
   402 bool UserMessages::InternalHook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew)
   403 #else
   404 bool UserMessages::InternalHook(int msg_id, IBitBufUserMessageListener *pListener, bool intercept, bool isNew)
   405 #endif
   406 {
   407 	if (msg_id < 0 || msg_id >= 255)
   408 	{
   409 		return false;
   410 	}
   411 
   412 	ListenerInfo *pInfo;
   413 	if (m_FreeListeners.empty())
   414 	{
   415 		pInfo = new ListenerInfo;
   416 	} else {
   417 		pInfo = m_FreeListeners.front();
   418 		m_FreeListeners.pop();
   419 	}
   420 
   421 	pInfo->Callback = pListener;
   422 	pInfo->IsHooked = false;
   423 	pInfo->KillMe = false;
   424 	pInfo->IsNew = isNew;
   425 
   426 	if (!m_HookCount++)
   427 	{
   428 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   429 		SH_ADD_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Pre), false);
   430 		SH_ADD_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Post), true);
   431 #else
   432 		SH_ADD_HOOK(IVEngineServer, UserMessageBegin, engine, SH_MEMBER(this, &UserMessages::OnStartMessage_Pre), false);
   433 		SH_ADD_HOOK(IVEngineServer, UserMessageBegin, engine, SH_MEMBER(this, &UserMessages::OnStartMessage_Post), true);
   434 		SH_ADD_HOOK(IVEngineServer, MessageEnd, engine, SH_MEMBER(this, &UserMessages::OnMessageEnd_Pre), false);
   435 		SH_ADD_HOOK(IVEngineServer, MessageEnd, engine, SH_MEMBER(this, &UserMessages::OnMessageEnd_Post), true);
   436 #endif
   437 	}
   438 
   439 	if (intercept)
   440 	{
   441 		m_msgIntercepts[msg_id].push_back(pInfo);
   442 	} else {
   443 		m_msgHooks[msg_id].push_back(pInfo);
   444 	}
   445 
   446 	return true;
   447 }
   448 
   449 #ifdef USE_PROTOBUF_USERMESSAGES
   450 const protobuf::Message *UserMessages::GetMessagePrototype(int msg_type)
   451 {
   452 #if SOURCE_ENGINE == SE_DOTA
   453 	return g_DotaUsermessageHelpers.GetPrototype(msg_type);
   454 #elif SOURCE_ENGINE == SE_CSGO
   455 	return g_Cstrike15UsermessageHelpers.GetPrototype(msg_type);
   456 #endif
   457 }
   458 #endif
   459 
   460 #ifdef USE_PROTOBUF_USERMESSAGES
   461 bool UserMessages::InternalUnhook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew)
   462 #else
   463 bool UserMessages::InternalUnhook(int msg_id, IBitBufUserMessageListener *pListener, bool intercept, bool isNew)
   464 #endif
   465 {
   466 	MsgList *pList;
   467 	MsgIter iter;
   468 	ListenerInfo *pInfo;
   469 	bool deleted = false;
   470 
   471 	if (msg_id < 0 || msg_id >= 255)
   472 	{
   473 		return false;
   474 	}
   475 
   476 	pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   477 	for (iter=pList->begin(); iter!=pList->end(); iter++)
   478 	{
   479 		pInfo = (*iter);
   480 		if (pInfo->Callback == pListener && pInfo->IsNew == isNew)
   481 		{
   482 			if (pInfo->IsHooked)
   483 			{
   484 				pInfo->KillMe = true;
   485 				return true;
   486 			}
   487 			pList->erase(iter);
   488 			deleted = true;
   489 			break;
   490 		}
   491 	}
   492 
   493 	if (deleted)
   494 	{
   495 		_DecRefCounter();
   496 	}
   497 
   498 	return deleted;
   499 }
   500 
   501 void UserMessages::_DecRefCounter()
   502 {
   503 	if (--m_HookCount == 0)
   504 	{
   505 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   506 		SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Pre), false);
   507 		SH_REMOVE_HOOK(IVEngineServer, SendUserMessage, engine, SH_MEMBER(this, &UserMessages::OnSendUserMessage_Post), true);
   508 #else
   509 		SH_REMOVE_HOOK(IVEngineServer, UserMessageBegin, engine, SH_MEMBER(this, &UserMessages::OnStartMessage_Pre), false);
   510 		SH_REMOVE_HOOK(IVEngineServer, UserMessageBegin, engine, SH_MEMBER(this, &UserMessages::OnStartMessage_Post), true);
   511 		SH_REMOVE_HOOK(IVEngineServer, MessageEnd, engine, SH_MEMBER(this, &UserMessages::OnMessageEnd_Pre), false);
   512 		SH_REMOVE_HOOK(IVEngineServer, MessageEnd, engine, SH_MEMBER(this, &UserMessages::OnMessageEnd_Post), true);
   513 #endif
   514 	}
   515 }
   516 
   517 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   518 void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
   519 {
   520 #if SOURCE_ENGINE == SE_DOTA
   521 	OnStartMessage_Pre(&filter, msg_type, g_DotaUsermessageHelpers.GetName(msg_type));
   522 #elif SOURCE_ENGINE == SE_CSGO
   523 	OnStartMessage_Pre(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
   524 #endif
   525 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   526 	{
   527 		int size = msg.ByteSize();
   528 		uint8 *data = (uint8 *)stackalloc(size);
   529 		msg.SerializePartialToArray(data, size);
   530 		m_InterceptBuffer->ParsePartialFromArray(data, size);
   531 	}
   532 	else
   533 	{
   534 		m_FakeEngineBuffer = &const_cast<protobuf::Message &>(msg);
   535 	}
   536 
   537 #if SOURCE_ENGINE == SE_DOTA
   538 	OnStartMessage_Post(&filter, msg_type, g_DotaUsermessageHelpers.GetName(msg_type));
   539 #elif SOURCE_ENGINE == SE_CSGO
   540 	OnStartMessage_Post(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
   541 #endif
   542 
   543 	OnMessageEnd_Pre();
   544 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   545 		RETURN_META(MRES_SUPERCEDE);
   546 
   547 	RETURN_META(MRES_IGNORED);
   548 }
   549 
   550 void UserMessages::OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
   551 {
   552 	OnMessageEnd_Post();
   553 	RETURN_META(MRES_IGNORED);
   554 }
   555 #endif
   556 
   557 #ifdef USE_PROTOBUF_USERMESSAGES
   558 #define UM_RETURN_META_VALUE(res, val) \
   559 	m_FakeMetaRes = res;               \
   560 	return val;
   561 
   562 #define UM_RETURN_META(res)            \
   563 	m_FakeMetaRes = res;               \
   564 	return;
   565 #else
   566 #define UM_RETURN_META_VALUE(res, val) \
   567 	RETURN_META_VALUE(res, val)
   568 
   569 #define UM_RETURN_META(res)            \
   570 	RETURN_META(res)
   571 #endif
   572 
   573 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   574 protobuf::Message *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
   575 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
   576 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
   577 #else
   578 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   579 #endif
   580 {
   581 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   582 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   583 
   584 	if ((is_intercept_empty && is_hook_empty)
   585 		|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
   586 	{
   587 		m_InHook = false;
   588 		UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   589 	}
   590 
   591 	m_CurId = msg_type;
   592 	m_CurRecFilter = filter;
   593 	m_InHook = true;
   594 	m_BlockEndPost = false;
   595 
   596 	if (!is_intercept_empty)
   597 	{
   598 #ifdef USE_PROTOBUF_USERMESSAGES
   599 		if (m_InterceptBuffer)
   600 			delete m_InterceptBuffer;
   601 		m_InterceptBuffer = GetMessagePrototype(msg_type)->New();
   602 
   603 		UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer);
   604 #else
   605 		m_InterceptBuffer.Reset();
   606 		UM_RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   607 #endif
   608 	}
   609 
   610 	UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   611 }
   612 
   613 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   614 protobuf::Message *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
   615 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
   616 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
   617 #else
   618 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   619 #endif
   620 {
   621 	if (!m_InHook)
   622 	{
   623 		UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   624 	}
   625 
   626 #ifdef USE_PROTOBUF_USERMESSAGES
   627 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   628 		m_OrigBuffer = m_InterceptBuffer;
   629 	else
   630 		m_OrigBuffer = m_FakeEngineBuffer;
   631 #else
   632 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   633 #endif
   634 
   635 	UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   636 }
   637 
   638 void UserMessages::OnMessageEnd_Post()
   639 {
   640 	if (!m_InHook)
   641 	{
   642 		UM_RETURN_META(MRES_IGNORED);
   643 	}
   644 
   645 	MsgList *pList;
   646 	MsgIter iter;
   647 	ListenerInfo *pInfo;
   648 
   649 	m_InHook = false;
   650 
   651 	pList = &m_msgIntercepts[m_CurId];
   652 	for (iter=pList->begin(); iter!=pList->end(); )
   653 	{
   654 		pInfo = (*iter);
   655 		if (m_BlockEndPost && !pInfo->IsNew)
   656 		{
   657 			continue;
   658 		}
   659 		pInfo->IsHooked = true;
   660 		pInfo->Callback->OnUserMessageSent(m_CurId);
   661 		if (pInfo->IsNew)
   662 		{
   663 			pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
   664 		}
   665 
   666 		if (pInfo->KillMe)
   667 		{
   668 			iter = pList->erase(iter);
   669 			m_FreeListeners.push(pInfo);
   670 			_DecRefCounter();
   671 			continue;
   672 		}
   673 
   674 		pInfo->IsHooked = false;
   675 		iter++;
   676 	}
   677 
   678 	pList = &m_msgHooks[m_CurId];
   679 	for (iter=pList->begin(); iter!=pList->end(); )
   680 	{
   681 		pInfo = (*iter);
   682 		if (m_BlockEndPost && !pInfo->IsNew)
   683 		{
   684 			continue;
   685 		}
   686 		pInfo->IsHooked = true;
   687 		pInfo->Callback->OnUserMessageSent(m_CurId);
   688 		if (pInfo->IsNew)
   689 		{
   690 			pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
   691 		}
   692 
   693 		if (pInfo->KillMe)
   694 		{
   695 			iter = pList->erase(iter);
   696 			m_FreeListeners.push(pInfo);
   697 			_DecRefCounter();
   698 			continue;
   699 		}
   700 
   701 		pInfo->IsHooked = false;
   702 		iter++;
   703 	}
   704 }
   705 
   706 void UserMessages::OnMessageEnd_Pre()
   707 {
   708 	if (!m_InHook)
   709 	{
   710 		UM_RETURN_META(MRES_IGNORED);
   711 	}
   712 
   713 	MsgList *pList;
   714 	MsgIter iter;
   715 	ListenerInfo *pInfo;
   716 
   717 	ResultType res;
   718 	bool intercepted = false;
   719 	bool handled = false;
   720 
   721 	pList = &m_msgIntercepts[m_CurId];
   722 	for (iter=pList->begin(); iter!=pList->end(); )
   723 	{
   724 		pInfo = (*iter);
   725 		pInfo->IsHooked = true;
   726 #ifdef USE_PROTOBUF_USERMESSAGES
   727 		res = pInfo->Callback->InterceptUserMessage(m_CurId, m_InterceptBuffer, m_CurRecFilter);
   728 #else
   729 		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   730 #endif
   731 
   732 		intercepted = true;
   733 
   734 		switch (res)
   735 		{
   736 		case Pl_Stop:
   737 			{
   738 				if (pInfo->KillMe)
   739 				{
   740 					pList->erase(iter);
   741 					m_FreeListeners.push(pInfo);
   742 					_DecRefCounter();
   743 					goto supercede;
   744 				}
   745 				pInfo->IsHooked = false;
   746 				goto supercede;
   747 			}
   748 		case Pl_Handled:
   749 			{
   750 				handled = true;
   751 				if (pInfo->KillMe)
   752 				{
   753 					iter = pList->erase(iter);
   754 					m_FreeListeners.push(pInfo);
   755 					_DecRefCounter();
   756 					continue;
   757 				}
   758 				break;
   759 			}
   760 		default:
   761 			{
   762 				if (pInfo->KillMe)
   763 				{
   764 					iter = pList->erase(iter);
   765 					m_FreeListeners.push(pInfo);
   766 					_DecRefCounter();
   767 					continue;
   768 				}
   769 				break;
   770 			}
   771 		}
   772 		pInfo->IsHooked = false;
   773 		iter++;
   774 	}
   775 
   776 	if (!handled && intercepted)
   777 	{
   778 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   779 		ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
   780 #else
   781 		bf_write *engine_bfw;
   782 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   783 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId, g_SMAPI->GetUserMessage(m_CurId));
   784 #else
   785 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   786 #endif
   787 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   788 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   789 		ENGINE_CALL(MessageEnd)();
   790 #endif // SE_CSGO || SE_DOTA
   791 	}
   792 
   793 	{
   794 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   795 		int size = m_OrigBuffer->ByteSize();
   796 		uint8 *data = (uint8 *)stackalloc(size);
   797 		m_OrigBuffer->SerializePartialToArray(data, size);
   798 
   799 		protobuf::Message *pTempMsg = GetMessagePrototype(m_CurId)->New();
   800 		pTempMsg->ParsePartialFromArray(data, size);
   801 #else
   802 		bf_write *pTempMsg = m_OrigBuffer;
   803 #endif
   804 
   805 		pList = &m_msgHooks[m_CurId];
   806 		for (iter=pList->begin(); iter!=pList->end(); )
   807 		{
   808 			pInfo = (*iter);
   809 			pInfo->IsHooked = true;
   810 			pInfo->Callback->OnUserMessage(m_CurId, pTempMsg, m_CurRecFilter);
   811 
   812 			if (pInfo->KillMe)
   813 			{
   814 				iter = pList->erase(iter);
   815 				m_FreeListeners.push(pInfo);
   816 				_DecRefCounter();
   817 				continue;
   818 			}
   819 
   820 			pInfo->IsHooked = false;
   821 			iter++;
   822 		}
   823 
   824 #if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
   825 		delete pTempMsg;
   826 #endif
   827 	}
   828 
   829 	UM_RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
   830 supercede:
   831 	m_BlockEndPost = true;
   832 	UM_RETURN_META(MRES_SUPERCEDE);
   833 }