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