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