core/UserMessages.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sun Jan 27 12:29:16 2013 -0500 (2013-01-27)
changeset 3759 bf45d8b36818
parent 3758 2ce920431e4a
child 3778 4c210a5b3824
permissions -rw-r--r--
Fixed pb non-intercept hook callbacks being passed handle with NULL pb msg (bug 5579, bug 5590).
     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 #ifdef USE_PROTOBUF_USERMESSAGES
   497 // Unlike protobuf's Message::CopyFrom:
   498 // - Does not require to/from message descriptors to be identical.
   499 // - Fields are copied by name, rather than index.
   500 // - String fields are retrieved for copy with GetStringReference, to avoid destucting libprotobuf std::string in sm.
   501 // - Unknown fields in |from| message are not copied to |to| message.
   502 // - Copying Message fields recursively calls this function, rather than Message::MergeFrom.
   503 static void _CopyProtobufMessage(const protobuf::Message &from, protobuf::Message *to)
   504 {
   505 	const protobuf::Descriptor* descriptor = from.GetDescriptor();
   506 
   507 	const protobuf::Reflection* from_reflection = from.GetReflection();
   508 	const protobuf::Reflection* to_reflection = to->GetReflection();
   509 
   510 	protobuf::UnknownFieldSet toUnknownFields;
   511 
   512 	int fromFieldCount = descriptor->field_count();
   513 	for (int i = 0; i < fromFieldCount; i++)
   514 	{
   515 		const protobuf::FieldDescriptor* fromField = descriptor->field(i);
   516 		const protobuf::FieldDescriptor* toField = to->GetDescriptor()->FindFieldByName(fromField->name());
   517 
   518 		// This will stripped new fields off of intercepted usermessages.
   519 		// TODO: fix that.
   520 		if (!toField)
   521 		{
   522 			continue;
   523 		}
   524 
   525 		if (fromField->is_repeated())
   526 		{
   527 			int count = from_reflection->FieldSize(from, fromField);
   528 			for (int j = 0; j < count; j++)
   529 			{
   530 				switch (fromField->cpp_type())
   531 				{
   532 #define HANDLE_TYPE(CPPTYPE, METHOD)                                           \
   533 				case protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE:             \
   534 					to_reflection->Add##METHOD(to, toField,                    \
   535 					from_reflection->GetRepeated##METHOD(from, fromField, j)); \
   536 					break;
   537 				HANDLE_TYPE(INT32 , Int32 );
   538 				HANDLE_TYPE(INT64 , Int64 );
   539 				HANDLE_TYPE(UINT32, UInt32);
   540 				HANDLE_TYPE(UINT64, UInt64);
   541 				HANDLE_TYPE(FLOAT , Float );
   542 				HANDLE_TYPE(DOUBLE, Double);
   543 				HANDLE_TYPE(BOOL  , Bool  );
   544 				HANDLE_TYPE(ENUM  , Enum  );
   545 #undef HANDLE_TYPE
   546 				case protobuf::FieldDescriptor::CPPTYPE_STRING:
   547 					{
   548 						std::string buffer;
   549 						to_reflection->AddString(to, toField,
   550 							from_reflection->GetRepeatedStringReference(from, fromField, j, &buffer));
   551 					}
   552 					break;
   553 				case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   554 					_CopyProtobufMessage(
   555 						from_reflection->GetRepeatedMessage(from, fromField, j),
   556 						to_reflection->AddMessage(to, toField)
   557 						);
   558 					break;
   559 				}
   560 			}
   561 		}
   562 		else
   563 		{
   564 			switch (fromField->cpp_type())
   565 			{
   566 #define HANDLE_TYPE(CPPTYPE, METHOD)                            \
   567 			case protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE:  \
   568 				to_reflection->Set##METHOD(to, toField,         \
   569 				from_reflection->Get##METHOD(from, fromField)); \
   570 				break;
   571 			HANDLE_TYPE(INT32 , Int32 );
   572 			HANDLE_TYPE(INT64 , Int64 );
   573 			HANDLE_TYPE(UINT32, UInt32);
   574 			HANDLE_TYPE(UINT64, UInt64);
   575 			HANDLE_TYPE(FLOAT , Float );
   576 			HANDLE_TYPE(DOUBLE, Double);
   577 			HANDLE_TYPE(BOOL  , Bool  );
   578 			HANDLE_TYPE(ENUM  , Enum  );
   579 #undef HANDLE_TYPE
   580 			case protobuf::FieldDescriptor::CPPTYPE_STRING:
   581 				{
   582 					std::string buffer;
   583 					to_reflection->SetString(to, toField,
   584 						from_reflection->GetStringReference(from, fromField, &buffer));
   585 				}
   586 				break;
   587 			case protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   588 				_CopyProtobufMessage(
   589 					from_reflection->GetMessage(from, fromField),
   590 					to_reflection->MutableMessage(to, toField)
   591 					);
   592 				break;
   593 			}
   594 		}
   595 	}
   596 }
   597 #endif
   598 
   599 #if SOURCE_ENGINE == SE_CSGO
   600 void UserMessages::OnSendUserMessage_Pre(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
   601 {
   602 	OnStartMessage_Pre(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
   603 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   604 	{
   605 		_CopyProtobufMessage(msg, m_InterceptBuffer);
   606 	}
   607 	else
   608 	{
   609 		m_FakeEngineBuffer = &const_cast<protobuf::Message &>(msg);
   610 		OnStartMessage_Post(&filter, msg_type, g_Cstrike15UsermessageHelpers.GetName(msg_type));
   611 	}
   612 
   613 	OnMessageEnd_Pre();
   614 	if (m_FakeMetaRes == MRES_SUPERCEDE)
   615 		RETURN_META(MRES_SUPERCEDE);
   616 
   617 	RETURN_META(MRES_IGNORED);
   618 }
   619 
   620 void UserMessages::OnSendUserMessage_Post(IRecipientFilter &filter, int msg_type, const protobuf::Message &msg)
   621 {
   622 	OnMessageEnd_Post();
   623 	RETURN_META(MRES_IGNORED);
   624 }
   625 #endif
   626 
   627 #ifdef USE_PROTOBUF_USERMESSAGES
   628 #define UM_RETURN_META_VALUE(res, val) \
   629 	m_FakeMetaRes = res;               \
   630 	return val;
   631 
   632 #define UM_RETURN_META(res)            \
   633 	m_FakeMetaRes = res;               \
   634 	return;
   635 #else
   636 #define UM_RETURN_META_VALUE(res, val) \
   637 	RETURN_META_VALUE(res, val)
   638 
   639 #define UM_RETURN_META(res)            \
   640 	RETURN_META(res)
   641 #endif
   642 
   643 #if SOURCE_ENGINE == SE_CSGO
   644 protobuf::Message *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
   645 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
   646 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type, const char *msg_name)
   647 #else
   648 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   649 #endif
   650 {
   651 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   652 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   653 
   654 	if ((is_intercept_empty && is_hook_empty)
   655 		|| (m_InExec && (m_CurFlags & USERMSG_BLOCKHOOKS)))
   656 	{
   657 		m_InHook = false;
   658 		UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   659 	}
   660 
   661 	m_CurId = msg_type;
   662 	m_CurRecFilter = filter;
   663 	m_InHook = true;
   664 	m_BlockEndPost = false;
   665 
   666 	if (!is_intercept_empty)
   667 	{
   668 #ifdef USE_PROTOBUF_USERMESSAGES
   669 		if (m_InterceptBuffer)
   670 			delete m_InterceptBuffer;
   671 		m_InterceptBuffer = g_Cstrike15UsermessageHelpers.GetPrototype(msg_type)->New();
   672 		UM_RETURN_META_VALUE(MRES_SUPERCEDE, m_InterceptBuffer);
   673 #else
   674 		m_InterceptBuffer.Reset();
   675 		UM_RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   676 #endif
   677 	}
   678 
   679 	UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   680 }
   681 
   682 #if SOURCE_ENGINE == SE_CSGO
   683 protobuf::Message *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
   684 #elif SOURCE_ENGINE >= SE_LEFT4DEAD
   685 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type, const char *msg_name)
   686 #else
   687 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   688 #endif
   689 {
   690 	if (!m_InHook)
   691 	{
   692 		UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   693 	}
   694 
   695 #ifdef USE_PROTOBUF_USERMESSAGES
   696 	m_OrigBuffer = m_FakeEngineBuffer;
   697 #else
   698 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   699 #endif
   700 
   701 	UM_RETURN_META_VALUE(MRES_IGNORED, NULL);
   702 }
   703 
   704 void UserMessages::OnMessageEnd_Post()
   705 {
   706 	if (!m_InHook)
   707 	{
   708 		UM_RETURN_META(MRES_IGNORED);
   709 	}
   710 
   711 	MsgList *pList;
   712 	MsgIter iter;
   713 	ListenerInfo *pInfo;
   714 
   715 	m_InHook = false;
   716 
   717 	pList = &m_msgIntercepts[m_CurId];
   718 	for (iter=pList->begin(); iter!=pList->end(); )
   719 	{
   720 		pInfo = (*iter);
   721 		if (m_BlockEndPost && !pInfo->IsNew)
   722 		{
   723 			continue;
   724 		}
   725 		pInfo->IsHooked = true;
   726 		pInfo->Callback->OnUserMessageSent(m_CurId);
   727 		if (pInfo->IsNew)
   728 		{
   729 			pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
   730 		}
   731 
   732 		if (pInfo->KillMe)
   733 		{
   734 			iter = pList->erase(iter);
   735 			m_FreeListeners.push(pInfo);
   736 			_DecRefCounter();
   737 			continue;
   738 		}
   739 
   740 		pInfo->IsHooked = false;
   741 		iter++;
   742 	}
   743 
   744 	pList = &m_msgHooks[m_CurId];
   745 	for (iter=pList->begin(); iter!=pList->end(); )
   746 	{
   747 		pInfo = (*iter);
   748 		if (m_BlockEndPost && !pInfo->IsNew)
   749 		{
   750 			continue;
   751 		}
   752 		pInfo->IsHooked = true;
   753 		pInfo->Callback->OnUserMessageSent(m_CurId);
   754 		if (pInfo->IsNew)
   755 		{
   756 			pInfo->Callback->OnPostUserMessage(m_CurId, !m_BlockEndPost);
   757 		}
   758 
   759 		if (pInfo->KillMe)
   760 		{
   761 			iter = pList->erase(iter);
   762 			m_FreeListeners.push(pInfo);
   763 			_DecRefCounter();
   764 			continue;
   765 		}
   766 
   767 		pInfo->IsHooked = false;
   768 		iter++;
   769 	}
   770 }
   771 
   772 void UserMessages::OnMessageEnd_Pre()
   773 {
   774 	if (!m_InHook)
   775 	{
   776 		UM_RETURN_META(MRES_IGNORED);
   777 	}
   778 
   779 	MsgList *pList;
   780 	MsgIter iter;
   781 	ListenerInfo *pInfo;
   782 
   783 	ResultType res;
   784 	bool intercepted = false;
   785 	bool handled = false;
   786 
   787 	pList = &m_msgIntercepts[m_CurId];
   788 	for (iter=pList->begin(); iter!=pList->end(); )
   789 	{
   790 		pInfo = (*iter);
   791 		pInfo->IsHooked = true;
   792 #ifdef USE_PROTOBUF_USERMESSAGES
   793 		res = pInfo->Callback->InterceptUserMessage(m_CurId, m_InterceptBuffer, m_CurRecFilter);
   794 #else
   795 		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   796 #endif
   797 
   798 		intercepted = true;
   799 
   800 		switch (res)
   801 		{
   802 		case Pl_Stop:
   803 			{
   804 				if (pInfo->KillMe)
   805 				{
   806 					pList->erase(iter);
   807 					m_FreeListeners.push(pInfo);
   808 					_DecRefCounter();
   809 					goto supercede;
   810 				}
   811 				pInfo->IsHooked = false;
   812 				goto supercede;
   813 			}
   814 		case Pl_Handled:
   815 			{
   816 				handled = true;
   817 				if (pInfo->KillMe)
   818 				{
   819 					iter = pList->erase(iter);
   820 					m_FreeListeners.push(pInfo);
   821 					_DecRefCounter();
   822 					continue;
   823 				}
   824 				break;
   825 			}
   826 		default:
   827 			{
   828 				if (pInfo->KillMe)
   829 				{
   830 					iter = pList->erase(iter);
   831 					m_FreeListeners.push(pInfo);
   832 					_DecRefCounter();
   833 					continue;
   834 				}
   835 				break;
   836 			}
   837 		}
   838 		pInfo->IsHooked = false;
   839 		iter++;
   840 	}
   841 
   842 	if (!handled && intercepted)
   843 	{
   844 #if SOURCE_ENGINE == SE_CSGO
   845 		ENGINE_CALL(SendUserMessage)(static_cast<IRecipientFilter &>(*m_CurRecFilter), m_CurId, *m_InterceptBuffer);
   846 		delete m_InterceptBuffer;
   847 		m_InterceptBuffer = NULL;
   848 #else
   849 		bf_write *engine_bfw;
   850 #if SOURCE_ENGINE >= SE_LEFT4DEAD
   851 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId, g_SMAPI->GetUserMessage(m_CurId));
   852 #else
   853 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   854 #endif
   855 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   856 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   857 		ENGINE_CALL(MessageEnd)();
   858 #endif // SE_CSGO
   859 	}
   860 
   861 	pList = &m_msgHooks[m_CurId];
   862 	for (iter=pList->begin(); iter!=pList->end(); )
   863 	{
   864 		pInfo = (*iter);
   865 		pInfo->IsHooked = true;
   866 		pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   867 
   868 		if (pInfo->KillMe)
   869 		{
   870 			iter = pList->erase(iter);
   871 			m_FreeListeners.push(pInfo);
   872 			_DecRefCounter();
   873 			continue;
   874 		}
   875 
   876 		pInfo->IsHooked = false;
   877 		iter++;
   878 	}
   879 
   880 	UM_RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
   881 supercede:
   882 	m_BlockEndPost = true;
   883 	UM_RETURN_META(MRES_SUPERCEDE);
   884 }