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