core/UserMessages.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Tue Jan 22 23:18:27 2013 -0500 (2013-01-22)
changeset 3749 4dc3d723c490
parent 3743 a98686a77974
child 3757 aa69f3b742c7
permissions -rw-r--r--
Triggering build against new mac libprotobuf.
     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 		protobuf::Message *msg = OnStartMessage_Pre(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, g_SMAPI->GetUserMessage(msg_id));
   266 		switch (m_FakeMetaRes)
   267 		{
   268 		case MRES_IGNORED:
   269 		case MRES_HANDLED:
   270 			m_FakeEngineBuffer = g_Cstrike15UsermessageHelpers.GetPrototype(msg_id)->New();
   271 			buffer = m_FakeEngineBuffer;
   272 			break;		
   273 
   274 		case MRES_OVERRIDE:
   275 			m_FakeEngineBuffer = g_Cstrike15UsermessageHelpers.GetPrototype(msg_id)->New();
   276 		// fallthrough
   277 		case MRES_SUPERCEDE:
   278 			buffer = msg;
   279 			break;
   280 		}
   281 		
   282 		OnStartMessage_Post(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id, g_SMAPI->GetUserMessage(msg_id));
   283 	}
   284 
   285 	return buffer;
   286 #endif // USE_PROTOBUF_USERMESSAGES
   287 }
   288 
   289 bool UserMessages::EndMessage()
   290 {
   291 	if (!m_InExec)
   292 	{
   293 		return false;
   294 	}
   295 
   296 #if SOURCE_ENGINE == SE_CSGO
   297 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   298 	{
   299 		ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
   300 		delete m_FakeEngineBuffer;
   301 		m_FakeEngineBuffer = NULL;
   302 	} else {
   303 		OnMessageEnd_Pre();
   304 
   305 		switch (m_FakeMetaRes)
   306 		{
   307 		case MRES_IGNORED:
   308 		case MRES_HANDLED:
   309 		case MRES_OVERRIDE:
   310 			engine->SendUserMessage(static_cast<IRecipientFilter &>(m_CellRecFilter), m_CurId, *m_FakeEngineBuffer);
   311 			delete m_FakeEngineBuffer;
   312 			m_FakeEngineBuffer = NULL;
   313 			break;
   314 		//case MRES_SUPERCEDE:
   315 		}
   316 
   317 		OnMessageEnd_Post();
   318 	}
   319 #else
   320 	if (m_CurFlags & USERMSG_BLOCKHOOKS)
   321 	{
   322 		ENGINE_CALL(MessageEnd)();
   323 	} else {
   324 		engine->MessageEnd();
   325 	}
   326 #endif // SE_CSGO
   327 
   328 	m_InExec = false;
   329 	m_CurFlags = 0;
   330 	m_CellRecFilter.Reset();
   331 
   332 	return true;
   333 }
   334 
   335 UserMessageType UserMessages::GetUserMessageType() const
   336 {
   337 #ifdef USE_PROTOBUF_USERMESSAGES
   338 	return UM_Protobuf;
   339 #else
   340 	return UM_BitBuf;
   341 #endif
   342 }
   343 
   344 bool UserMessages::HookUserMessage2(int msg_id,
   345 									IUserMessageListener *pListener,
   346 									bool intercept)
   347 {
   348 #ifdef USE_PROTOBUF_USERMESSAGES
   349 	return InternalHook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, true);
   350 #else
   351 	return InternalHook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, true);
   352 #endif
   353 }
   354 
   355 bool UserMessages::UnhookUserMessage2(int msg_id,
   356 									  IUserMessageListener *pListener,
   357 									  bool intercept)
   358 {
   359 #ifdef USE_PROTOBUF_USERMESSAGES
   360 	return InternalUnhook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, true);
   361 #else
   362 	return InternalUnhook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, true);
   363 #endif
   364 }
   365 
   366 bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   367 {
   368 #ifdef USE_PROTOBUF_USERMESSAGES
   369 	return InternalHook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, false);
   370 #else
   371 	return InternalHook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, false);
   372 #endif
   373 }
   374 
   375 bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   376 {	
   377 #ifdef USE_PROTOBUF_USERMESSAGES
   378 	return InternalUnhook(msg_id, (IProtobufUserMessageListener *)pListener, intercept, false);
   379 #else
   380 	return InternalUnhook(msg_id, (IBitBufUserMessageListener *)pListener, intercept, false);
   381 #endif
   382 }
   383 
   384 #ifdef USE_PROTOBUF_USERMESSAGES
   385 bool UserMessages::InternalHook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew)
   386 #else
   387 bool UserMessages::InternalHook(int msg_id, IBitBufUserMessageListener *pListener, bool intercept, bool isNew)
   388 #endif
   389 {
   390 	if (msg_id < 0 || msg_id >= 255)
   391 	{
   392 		return false;
   393 	}
   394 
   395 	ListenerInfo *pInfo;
   396 	if (m_FreeListeners.empty())
   397 	{
   398 		pInfo = new ListenerInfo;
   399 	} else {
   400 		pInfo = m_FreeListeners.front();
   401 		m_FreeListeners.pop();
   402 	}
   403 
   404 	pInfo->Callback = pListener;
   405 	pInfo->IsHooked = false;
   406 	pInfo->KillMe = false;
   407 	pInfo->IsNew = isNew;
   408 
   409 	if (!m_HookCount++)
   410 	{
   411 #if SOURCE_ENGINE == SE_CSGO
   412 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, SendUserMessage, engine, this, &UserMessages::OnSendUserMessage_Pre, false);
   413 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, SendUserMessage, engine, this, &UserMessages::OnSendUserMessage_Post, true);
   414 #else
   415 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
   416 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
   417 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
   418 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
   419 #endif
   420 	}
   421 
   422 	if (intercept)
   423 	{
   424 		m_msgIntercepts[msg_id].push_back(pInfo);
   425 	} else {
   426 		m_msgHooks[msg_id].push_back(pInfo);
   427 	}
   428 
   429 	return true;
   430 }
   431 
   432 #ifdef USE_PROTOBUF_USERMESSAGES
   433 bool UserMessages::InternalUnhook(int msg_id, IProtobufUserMessageListener *pListener, bool intercept, bool isNew)
   434 #else
   435 bool UserMessages::InternalUnhook(int msg_id, IBitBufUserMessageListener *pListener, bool intercept, bool isNew)
   436 #endif
   437 {
   438 	MsgList *pList;
   439 	MsgIter iter;
   440 	ListenerInfo *pInfo;
   441 	bool deleted = false;
   442 
   443 	if (msg_id < 0 || msg_id >= 255)
   444 	{
   445 		return false;
   446 	}
   447 
   448 	pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   449 	for (iter=pList->begin(); iter!=pList->end(); iter++)
   450 	{
   451 		pInfo = (*iter);
   452 		if (pInfo->Callback == pListener && pInfo->IsNew == isNew)
   453 		{
   454 			if (pInfo->IsHooked)
   455 			{
   456 				pInfo->KillMe = true;
   457 				return true;
   458 			}
   459 			pList->erase(iter);
   460 			deleted = true;
   461 			break;
   462 		}
   463 	}
   464 
   465 	if (deleted)
   466 	{
   467 		_DecRefCounter();
   468 	}
   469 
   470 	return deleted;
   471 }
   472 
   473 void UserMessages::_DecRefCounter()
   474 {
   475 	if (--m_HookCount == 0)
   476 	{
   477 #if SOURCE_ENGINE == SE_CSGO
   478 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, SendUserMessage, engine, this, &UserMessages::OnSendUserMessage_Pre, false);
   479 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, SendUserMessage, engine, this, &UserMessages::OnSendUserMessage_Post, true);
   480 #else
   481 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
   482 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
   483 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
   484 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
   485 #endif
   486 	}
   487 }
   488 
   489 #ifdef USE_PROTOBUF_USERMESSAGES
   490 // Unlike protobuf's Message::CopyFrom:
   491 // - Does not require to/from message descriptors to be identical.
   492 // - Fields are copied by name, rather than index.
   493 // - String fields are retrieved for copy with GetStringReference, to avoid destucting libprotobuf std::string in sm.
   494 // - Unknown fields in |from| message are not copied to |to| message.
   495 // - Copying Message fields recursively calls this function, rather than Message::MergeFrom.
   496 static void _CopyProtobufMessage(const protobuf::Message &from, protobuf::Message *to)
   497 {
   498 	const protobuf::Descriptor* descriptor = from.GetDescriptor();
   499 
   500 	const protobuf::Reflection* from_reflection = from.GetReflection();
   501 	const protobuf::Reflection* to_reflection = to->GetReflection();
   502 
   503 	protobuf::UnknownFieldSet toUnknownFields;
   504 
   505 	int fromFieldCount = descriptor->field_count();
   506 	for (int i = 0; i < fromFieldCount; i++)
   507 	{
   508 		const protobuf::FieldDescriptor* fromField = descriptor->field(i);
   509 		const protobuf::FieldDescriptor* toField = to->GetDescriptor()->FindFieldByName(fromField->name());
   510 
   511 		// This will stripped new fields off of intercepted usermessages.
   512 		// TODO: fix that.
   513 		if (!toField)
   514 		{
   515 			continue;
   516 		}
   517 
   518 		if (fromField->is_repeated())
   519 		{
   520 			int count = from_reflection->FieldSize(from, fromField);
   521 			for (int j = 0; j < count; j++)
   522 			{
   523 				switch (fromField->cpp_type())
   524 				{
   525 #define HANDLE_TYPE(CPPTYPE, METHOD)                                           \
   526 				case protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE:             \
   527 					to_reflection->Add##METHOD(to, toField,                    \
   528 					from_reflection->GetRepeated##METHOD(from, fromField, j)); \
   529 					break;
   530 				HANDLE_TYPE(INT32 , Int32 );
   531 				HANDLE_TYPE(INT64 , Int64 );
   532 				HANDLE_TYPE(UINT32, UInt32);
   533 				HANDLE_TYPE(UINT64, UInt64);
   534 				HANDLE_TYPE(FLOAT , Float );
   535 				HANDLE_TYPE(DOUBLE, Double);
   536 				HANDLE_TYPE(BOOL  , Bool  );
   537 				HANDLE_TYPE(ENUM  , Enum  );
   538 #undef HANDLE_TYPE
   539 				case protobuf::FieldDescriptor::CPPTYPE_STRING:
   540 					{
   541 						std::string buffer;
   542 						to_reflection->AddString(to, toField,
   543 							from_reflection->GetRepeatedStringReference(from, fromField, j, &buffer));
   544 					}
   545 					break;
   546 				case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   547 					_CopyProtobufMessage(
   548 						from_reflection->GetRepeatedMessage(from, fromField, j),
   549 						to_reflection->AddMessage(to, toField)
   550 						);
   551 					break;
   552 				}
   553 			}
   554 		}
   555 		else
   556 		{
   557 			switch (fromField->cpp_type())
   558 			{
   559 #define HANDLE_TYPE(CPPTYPE, METHOD)                            \
   560 			case protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE:  \
   561 				to_reflection->Set##METHOD(to, toField,         \
   562 				from_reflection->Get##METHOD(from, fromField)); \
   563 				break;
   564 			HANDLE_TYPE(INT32 , Int32 );
   565 			HANDLE_TYPE(INT64 , Int64 );
   566 			HANDLE_TYPE(UINT32, UInt32);
   567 			HANDLE_TYPE(UINT64, UInt64);
   568 			HANDLE_TYPE(FLOAT , Float );
   569 			HANDLE_TYPE(DOUBLE, Double);
   570 			HANDLE_TYPE(BOOL  , Bool  );
   571 			HANDLE_TYPE(ENUM  , Enum  );
   572 #undef HANDLE_TYPE
   573 			case protobuf::FieldDescriptor::CPPTYPE_STRING:
   574 				{
   575 					std::string buffer;
   576 					to_reflection->SetString(to, toField,
   577 						from_reflection->GetStringReference(from, fromField, &buffer));
   578 				}
   579 				break;
   580 			case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   581 				_CopyProtobufMessage(
   582 					from_reflection->GetMessage(from, fromField),
   583 					to_reflection->MutableMessage(to, toField)
   584 					);
   585 				break;
   586 			}
   587 		}
   588 	}
   589 }
   590 #endif
   591 
   592 #if SOURCE_ENGINE == SE_CSGO
   593 void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
   594 {
   595 	OnStartMessage_Pre(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
   596 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   597 	{
   598 		_CopyProtobufMessage(msg, m_InterceptBuffer);
   599 	}
   600 	else
   601 	{
   602 		OnStartMessage_Post(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
   603 	}
   604 
   605 	OnMessageEnd_Pre();
   606 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   607 		RETURN_META(MRES_SUPERCEDE);
   608 
   609 	RETURN_META(MRES_IGNORED);
   610 }
   611 
   612 void UserMessages::OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
   613 {
   614 	OnMessageEnd_Post();
   615 	RETURN_META(MRES_IGNORED);
   616 }
   617 #endif
   618 
   619 #ifdef USE_PROTOBUF_USERMESSAGES
   620 #define UM_RETURN_META_VALUE(res, val) \
   621 	m_FakeMetaRes = res;               \
   622 	return val;
   623 
   624 #define UM_RETURN_META(res)            \
   625 	m_FakeMetaRes = res;               \
   626 	return;
   627 #else
   628 #define UM_RETURN_META_VALUE(res, val) \
   629 	RETURN_META_VALUE(res, val)
   630 
   631 #define UM_RETURN_META(res)            \
   632 	RETURN_META(res)
   633 #endif
   634 
   635 #if SOURCE_ENGINE == SE_CSGO
   636 protobuf::Message *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
   637 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
   638 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
   639 #else
   640 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   641 #endif
   642 {
   643 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   644 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   645 
   646 	if ((is_intercept_empty && is_hook_empty)
   647 		|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
   648 	{
   649 		m_InHook = false;
   650 		UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   651 	}
   652 
   653 	m_CurId = msg_type;
   654 	m_CurRecFilter = filter;
   655 	m_InHook = true;
   656 	m_BlockEndPost = false;
   657 
   658 	if (!is_intercept_empty)
   659 	{
   660 #ifdef USE_PROTOBUF_USERMESSAGES
   661 		if (m_InterceptBuffer)
   662 			delete m_InterceptBuffer;
   663 		m_InterceptBuffer = g_Cstrike15UsermessageHelpers.GetPrototype(msg_type)->New();
   664 		UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer);
   665 #else
   666 		m_InterceptBuffer.Reset();
   667 		UM_RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   668 #endif
   669 	}
   670 
   671 	UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   672 }
   673 
   674 #if SOURCE_ENGINE == SE_CSGO
   675 protobuf::Message *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
   676 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
   677 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
   678 #else
   679 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   680 #endif
   681 {
   682 	if (!m_InHook)
   683 	{
   684 		UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   685 	}
   686 
   687 #ifdef USE_PROTOBUF_USERMESSAGES
   688 	m_FakeEngineBuffer = g_Cstrike15UsermessageHelpers.GetPrototype(msg_type)->New();
   689 	m_OrigBuffer = m_FakeEngineBuffer;
   690 #else
   691 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   692 #endif
   693 
   694 	UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   695 }
   696 
   697 void UserMessages::OnMessageEnd_Post()
   698 {
   699 	if (!m_InHook)
   700 	{
   701 		UM_RETURN_META(MRES_IGNORED);
   702 	}
   703 
   704 	MsgList *pList;
   705 	MsgIter iter;
   706 	ListenerInfo *pInfo;
   707 
   708 	m_InHook = false;
   709 
   710 	pList = &m_msgIntercepts[m_CurId];
   711 	for (iter=pList->begin(); iter!=pList->end(); )
   712 	{
   713 		pInfo = (*iter);
   714 		if (m_BlockEndPost && !pInfo->IsNew)
   715 		{
   716 			continue;
   717 		}
   718 		pInfo->IsHooked = true;
   719 		pInfo->Callback->OnUserMessageSent(m_CurId);
   720 		if (pInfo->IsNew)
   721 		{
   722 			pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
   723 		}
   724 
   725 		if (pInfo->KillMe)
   726 		{
   727 			iter = pList->erase(iter);
   728 			m_FreeListeners.push(pInfo);
   729 			_DecRefCounter();
   730 			continue;
   731 		}
   732 
   733 		pInfo->IsHooked = false;
   734 		iter++;
   735 	}
   736 
   737 	pList = &m_msgHooks[m_CurId];
   738 	for (iter=pList->begin(); iter!=pList->end(); )
   739 	{
   740 		pInfo = (*iter);
   741 		if (m_BlockEndPost && !pInfo->IsNew)
   742 		{
   743 			continue;
   744 		}
   745 		pInfo->IsHooked = true;
   746 		pInfo->Callback->OnUserMessageSent(m_CurId);
   747 		if (pInfo->IsNew)
   748 		{
   749 			pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
   750 		}
   751 
   752 		if (pInfo->KillMe)
   753 		{
   754 			iter = pList->erase(iter);
   755 			m_FreeListeners.push(pInfo);
   756 			_DecRefCounter();
   757 			continue;
   758 		}
   759 
   760 		pInfo->IsHooked = false;
   761 		iter++;
   762 	}
   763 }
   764 
   765 void UserMessages::OnMessageEnd_Pre()
   766 {
   767 	if (!m_InHook)
   768 	{
   769 		UM_RETURN_META(MRES_IGNORED);
   770 	}
   771 
   772 	MsgList *pList;
   773 	MsgIter iter;
   774 	ListenerInfo *pInfo;
   775 
   776 	ResultType res;
   777 	bool intercepted = false;
   778 	bool handled = false;
   779 
   780 	pList = &m_msgIntercepts[m_CurId];
   781 	for (iter=pList->begin(); iter!=pList->end(); )
   782 	{
   783 		pInfo = (*iter);
   784 		pInfo->IsHooked = true;
   785 #ifdef USE_PROTOBUF_USERMESSAGES
   786 		res = pInfo->Callback->InterceptUserMessage(m_CurId, m_InterceptBuffer, m_CurRecFilter);
   787 #else
   788 		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   789 #endif
   790 
   791 		intercepted = true;
   792 
   793 		switch (res)
   794 		{
   795 		case Pl_Stop:
   796 			{
   797 				if (pInfo->KillMe)
   798 				{
   799 					pList->erase(iter);
   800 					m_FreeListeners.push(pInfo);
   801 					_DecRefCounter();
   802 					goto supercede;
   803 				}
   804 				pInfo->IsHooked = false;
   805 				goto supercede;
   806 			}
   807 		case Pl_Handled:
   808 			{
   809 				handled = true;
   810 				if (pInfo->KillMe)
   811 				{
   812 					iter = pList->erase(iter);
   813 					m_FreeListeners.push(pInfo);
   814 					_DecRefCounter();
   815 					continue;
   816 				}
   817 				break;
   818 			}
   819 		default:
   820 			{
   821 				if (pInfo->KillMe)
   822 				{
   823 					iter = pList->erase(iter);
   824 					m_FreeListeners.push(pInfo);
   825 					_DecRefCounter();
   826 					continue;
   827 				}
   828 				break;
   829 			}
   830 		}
   831 		pInfo->IsHooked = false;
   832 		iter++;
   833 	}
   834 
   835 	if (!handled && intercepted)
   836 	{
   837 #if SOURCE_ENGINE == SE_CSGO
   838 		ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
   839 		delete m_InterceptBuffer;
   840 		m_InterceptBuffer = NULL;
   841 #else
   842 		bf_write *engine_bfw;
   843 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   844 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId, g_SMAPI->GetUserMessage(m_CurId));
   845 #else
   846 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   847 #endif
   848 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   849 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   850 		ENGINE_CALL(MessageEnd)();
   851 #endif // SE_CSGO
   852 	}
   853 
   854 	pList = &m_msgHooks[m_CurId];
   855 	for (iter=pList->begin(); iter!=pList->end(); )
   856 	{
   857 		pInfo = (*iter);
   858 		pInfo->IsHooked = true;
   859 		pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   860 
   861 		if (pInfo->KillMe)
   862 		{
   863 			iter = pList->erase(iter);
   864 			m_FreeListeners.push(pInfo);
   865 			_DecRefCounter();
   866 			continue;
   867 		}
   868 
   869 		pInfo->IsHooked = false;
   870 		iter++;
   871 	}
   872 
   873 	UM_RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
   874 supercede:
   875 	m_BlockEndPost = true;
   876 	UM_RETURN_META(MRES_SUPERCEDE);
   877 }