core/UserMessages.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 01 02:12:47 2007 +0000 (2007-08-01)
changeset 1231 17badaf66ce4
parent 1132 e8d4e82e9541
child 1250 8241c207b514
permissions -rw-r--r--
updated more header files (mostly internal now)
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * ================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or 
     9  * modify it under the terms of the GNU General Public License, 
    10  * version 3.0, as published by the Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include "UserMessages.h"
    34 #include "sm_stringutil.h"
    35 
    36 UserMessages g_UserMsgs;
    37 
    38 SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
    39 SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
    40 
    41 UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
    42 {
    43 	m_Names = sm_trie_create();
    44 	m_HookCount = 0;
    45 	m_InExec = false;
    46 	m_InHook = false;
    47 	m_CurFlags = 0;
    48 	m_CurId = INVALID_MESSAGE_ID;
    49 }
    50 
    51 UserMessages::~UserMessages()
    52 {
    53 	sm_trie_destroy(m_Names);
    54 
    55 	CStack<ListenerInfo *>::iterator iter;
    56 	for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
    57 	{
    58 		delete (*iter);
    59 	}
    60 	m_FreeListeners.popall();
    61 }
    62 
    63 void UserMessages::OnSourceModStartup(bool late)
    64 {
    65 	/* -1 means SourceMM was unable to get the user message list */
    66 	m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
    67 }
    68 
    69 void UserMessages::OnSourceModAllInitialized()
    70 {
    71 	g_ShareSys.AddInterface(NULL, this);
    72 }
    73 
    74 void UserMessages::OnSourceModAllShutdown()
    75 {
    76 	if (m_HookCount)
    77 	{
    78 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
    79 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
    80 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
    81 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
    82 	}
    83 	m_HookCount = 0;
    84 }
    85 
    86 int UserMessages::GetMessageIndex(const char *msg)
    87 {
    88 	int msgid;
    89 
    90 	if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
    91 	{
    92 		if (m_FallbackSearch)
    93 		{
    94 			char msgbuf[64];
    95 			int size;
    96 			msgid = 0;
    97 
    98 			while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
    99 			{
   100 				if (strcmp(msgbuf, msg) == 0)
   101 				{
   102 					sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
   103 					return msgid;
   104 				}
   105 				msgid++;
   106 			}
   107 		}
   108 
   109 		msgid = g_SMAPI->FindUserMessage(msg);
   110 
   111 		if (msgid != INVALID_MESSAGE_ID)
   112 		{
   113 			sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
   114 		}
   115 	}
   116 
   117 	return msgid;
   118 }
   119 
   120 bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
   121 {
   122 	if (m_FallbackSearch)
   123 	{
   124 		int size;
   125 		return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
   126 	}
   127 
   128 	const char *msg = g_SMAPI->GetUserMessage(msgid);
   129 
   130 	if (msg)
   131 	{
   132 		strncopy(buffer, msg, maxlength);
   133 		return true;
   134 	}
   135 
   136 	return false;
   137 }
   138 
   139 bf_write *UserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
   140 {
   141 	bf_write *buffer;
   142 
   143 	if (m_InExec || m_InHook)
   144 	{
   145 		return NULL;
   146 	}
   147 	if (msg_id < 0 || msg_id >= 255)
   148 	{
   149 		return NULL;
   150 	}
   151 
   152 	m_CellRecFilter.Initialize(players, playersNum);
   153 
   154 	m_CurFlags = flags;
   155 	if (m_CurFlags & USERMSG_INITMSG)
   156 	{
   157 		m_CellRecFilter.SetToInit(true);
   158 	}
   159 	if (m_CurFlags & USERMSG_RELIABLE)
   160 	{
   161 		m_CellRecFilter.SetToReliable(true);
   162 	}
   163 
   164 	m_InExec = true;
   165 
   166 	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
   167 	{
   168 		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   169 	} else {
   170 		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   171 	}
   172 
   173 	return buffer;
   174 }
   175 
   176 bool UserMessages::EndMessage()
   177 {
   178 	if (!m_InExec)
   179 	{
   180 		return false;
   181 	}
   182 
   183 	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
   184 	{
   185 		engine->MessageEnd();
   186 	} else {
   187 		ENGINE_CALL(MessageEnd)();
   188 	}
   189 
   190 	m_InExec = false;
   191 	m_CurFlags = 0;
   192 	m_CellRecFilter.Reset();
   193 
   194 	return true;
   195 }
   196 
   197 bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   198 {
   199 	if (msg_id < 0 || msg_id >= 255)
   200 	{
   201 		return false;
   202 	}
   203 
   204 	ListenerInfo *pInfo;
   205 	if (m_FreeListeners.empty())
   206 	{
   207 		pInfo = new ListenerInfo;
   208 	} else {
   209 		pInfo = m_FreeListeners.front();
   210 		m_FreeListeners.pop();
   211 	}
   212 
   213 	pInfo->Callback = pListener;
   214 	pInfo->IsHooked = false;
   215 	pInfo->KillMe = false;
   216 
   217 	if (!m_HookCount++)
   218 	{
   219 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
   220 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
   221 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
   222 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
   223 	}
   224 
   225 	if (intercept)
   226 	{
   227 		m_msgIntercepts[msg_id].push_back(pInfo);
   228 	} else {
   229 		m_msgHooks[msg_id].push_back(pInfo);
   230 	}
   231 
   232 	return true;
   233 }
   234 
   235 bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   236 {	
   237 	MsgList *pList;
   238 	MsgIter iter;
   239 	ListenerInfo *pInfo;
   240 	bool deleted = false;
   241 
   242 	if (msg_id < 0 || msg_id >= 255)
   243 	{
   244 		return false;
   245 	}
   246 
   247 	pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   248 	for (iter=pList->begin(); iter!=pList->end(); iter++)
   249 	{
   250 		pInfo = (*iter);
   251 		if (pInfo->Callback == pListener)
   252 		{
   253 			if (pInfo->IsHooked)
   254 			{
   255 				pInfo->KillMe = true;
   256 				return true;
   257 			}
   258 			pList->erase(iter);
   259 			deleted = true;
   260 			break;
   261 		}
   262 	}
   263 
   264 	if (deleted)
   265 	{
   266 		_DecRefCounter();
   267 	}
   268 
   269 	return deleted;
   270 }
   271 
   272 void UserMessages::_DecRefCounter()
   273 {
   274 	if (--m_HookCount == 0)
   275 	{
   276 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
   277 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
   278 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
   279 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
   280 	}
   281 }
   282 
   283 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   284 {
   285 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   286 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   287 
   288 	if ((is_intercept_empty && is_hook_empty)
   289 		|| (m_InExec && !(m_CurFlags & USERMSG_PASSTHRU_ALL)))
   290 	{
   291 		m_InHook = false;
   292 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   293 	}
   294 
   295 	m_CurId = msg_type;
   296 	m_CurRecFilter = filter;
   297 	m_InHook = true;
   298 	m_BlockEndPost = false;
   299 
   300 	if (!is_intercept_empty)
   301 	{
   302 		m_InterceptBuffer.Reset();
   303 		RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   304 	}
   305 
   306 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   307 }
   308 
   309 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   310 {
   311 	if (!m_InHook)
   312 	{
   313 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   314 	}
   315 
   316 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   317 
   318 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   319 }
   320 
   321 void UserMessages::OnMessageEnd_Post()
   322 {
   323 	if (!m_InHook || m_BlockEndPost)
   324 	{
   325 		RETURN_META(MRES_IGNORED);
   326 	}
   327 
   328 	MsgList *pList;
   329 	MsgIter iter;
   330 	ListenerInfo *pInfo;
   331 
   332 	pList = &m_msgIntercepts[m_CurId];
   333 	for (iter=pList->begin(); iter!=pList->end(); )
   334 	{
   335 		pInfo = (*iter);
   336 		pInfo->IsHooked = true;
   337 		pInfo->Callback->OnUserMessageSent(m_CurId);
   338 
   339 		if (pInfo->KillMe)
   340 		{
   341 			iter = pList->erase(iter);
   342 			m_FreeListeners.push(pInfo);
   343 			_DecRefCounter();
   344 			continue;
   345 		}
   346 
   347 		pInfo->IsHooked = false;
   348 		iter++;
   349 	}
   350 
   351 	m_InHook = false;
   352 
   353 	pList = &m_msgHooks[m_CurId];
   354 	for (iter=pList->begin(); iter!=pList->end(); )
   355 	{
   356 		pInfo = (*iter);
   357 		pInfo->IsHooked = true;
   358 		pInfo->Callback->OnUserMessageSent(m_CurId);
   359 
   360 		if (pInfo->KillMe)
   361 		{
   362 			iter = pList->erase(iter);
   363 			m_FreeListeners.push(pInfo);
   364 			_DecRefCounter();
   365 			continue;
   366 		}
   367 
   368 		pInfo->IsHooked = false;
   369 		iter++;
   370 	}
   371 }
   372 
   373 void UserMessages::OnMessageEnd_Pre()
   374 {
   375 	if (!m_InHook)
   376 	{
   377 		RETURN_META(MRES_IGNORED);
   378 	}
   379 
   380 	MsgList *pList;
   381 	MsgIter iter;
   382 	ListenerInfo *pInfo;
   383 
   384 	ResultType res;
   385 	bool intercepted = false;
   386 	bool handled = false;
   387 
   388 	pList = &m_msgIntercepts[m_CurId];
   389 	for (iter=pList->begin(); iter!=pList->end(); )
   390 	{
   391 		pInfo = (*iter);
   392 		pInfo->IsHooked = true;
   393 		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   394 
   395 		intercepted = true;
   396 
   397 		switch (res)
   398 		{
   399 		case Pl_Stop:
   400 			{
   401 				if (pInfo->KillMe)
   402 				{
   403 					pList->erase(iter);
   404 					m_FreeListeners.push(pInfo);
   405 					_DecRefCounter();
   406 					goto supercede;
   407 				}
   408 				pInfo->IsHooked = false;
   409 				goto supercede;
   410 			}
   411 		case Pl_Handled:
   412 			{
   413 				handled = true;
   414 				if (pInfo->KillMe)
   415 				{
   416 					iter = pList->erase(iter);
   417 					m_FreeListeners.push(pInfo);
   418 					_DecRefCounter();
   419 					continue;
   420 				}
   421 				break;
   422 			}
   423 		default:
   424 			{
   425 				if (pInfo->KillMe)
   426 				{
   427 					iter = pList->erase(iter);
   428 					m_FreeListeners.push(pInfo);
   429 					_DecRefCounter();
   430 					continue;
   431 				}
   432 				break;
   433 			}
   434 		}
   435 		pInfo->IsHooked = false;
   436 		iter++;
   437 	}
   438 
   439 	if (handled)
   440 	{
   441 		goto supercede;
   442 	}
   443 
   444 	if (intercepted)
   445 	{
   446 		bf_write *engine_bfw;
   447 
   448 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   449 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   450 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   451 		ENGINE_CALL(MessageEnd)();
   452 	}
   453 
   454 	pList = &m_msgHooks[m_CurId];
   455 	for (iter=pList->begin(); iter!=pList->end(); )
   456 	{
   457 		pInfo = (*iter);
   458 		pInfo->IsHooked = true;
   459 		pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   460 
   461 		if (pInfo->KillMe)
   462 		{
   463 			iter = pList->erase(iter);
   464 			m_FreeListeners.push(pInfo);
   465 			_DecRefCounter();
   466 			continue;
   467 		}
   468 
   469 		pInfo->IsHooked = false;
   470 		iter++;
   471 	}
   472 
   473 	RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
   474 supercede:
   475 	m_InHook = false;
   476 	m_BlockEndPost = true;
   477 	RETURN_META(MRES_SUPERCEDE);
   478 }