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