core/UserMessages.cpp
author David Anderson <dvander@alliedmods.net>
Mon Mar 12 00:06:20 2007 +0000 (2007-03-12)
changeset 599 3a098eff4890
parent 597 2309b109e2af
child 616 d5b1dcf38c07
permissions -rw-r--r--
used a better error number
     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 
    17 CUserMessages g_UserMsgs;
    18 
    19 SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
    20 SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
    21 
    22 //:TODO: USE NEW MM IFACE TO SEARCH FOR MESSAGE NAMES ETC! faluco--> i'll do this when i have some spare time
    23 
    24 CUserMessages::CUserMessages() : m_InterceptBuffer(m_pBase, 2500)
    25 {
    26 	m_Names = sm_trie_create();
    27 	m_HookCount = 0;
    28 	m_InExec = false;
    29 	m_InHook = false;
    30 	m_CurFlags = 0;
    31 	m_CurId = INVALID_MESSAGE_ID;
    32 }
    33 
    34 CUserMessages::~CUserMessages()
    35 {
    36 	sm_trie_destroy(m_Names);
    37 }
    38 
    39 void CUserMessages::OnSourceModAllInitialized()
    40 {
    41 	g_ShareSys.AddInterface(NULL, this);
    42 }
    43 
    44 void CUserMessages::OnSourceModAllShutdown()
    45 {
    46 	if (m_HookCount)
    47 	{
    48 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
    49 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
    50 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
    51 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
    52 	}
    53 	m_HookCount = 0;
    54 }
    55 
    56 int CUserMessages::GetMessageIndex(const char *msg)
    57 {
    58 	int msgid;
    59 	if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
    60 	{
    61 		char buf[255];
    62 		int dummy;
    63 		msgid = 0;
    64 
    65 		while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy))
    66 		{
    67 			if (strcmp(msg, buf) == 0)
    68 			{
    69 				sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
    70 				return msgid;
    71 			}
    72 			msgid++;
    73 		}
    74 
    75 		return INVALID_MESSAGE_ID;
    76 	}
    77 
    78 	return msgid;
    79 }
    80 
    81 bool CUserMessages::GetMessageName(int msgid, char *buffer, size_t maxlen) const
    82 {
    83 	int dummy;
    84 
    85 	return gamedll->GetUserMessageInfo(msgid, buffer, maxlen, dummy);
    86 }
    87 
    88 bf_write *CUserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
    89 {
    90 	bf_write *buffer;
    91 
    92 	if (m_InExec || m_InHook)
    93 	{
    94 		return NULL;
    95 	}
    96 	if (msg_id < 0 || msg_id >= 255)
    97 	{
    98 		return NULL;
    99 	}
   100 
   101 	m_CellRecFilter.Initialize(players, playersNum);
   102 
   103 	m_CurFlags = flags;
   104 	if (m_CurFlags & USERMSG_INITMSG)
   105 	{
   106 		m_CellRecFilter.SetToInit(true);
   107 	}
   108 	if (m_CurFlags & USERMSG_RELIABLE)
   109 	{
   110 		m_CellRecFilter.SetToReliable(true);
   111 	}
   112 
   113 	m_InExec = true;
   114 
   115 	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
   116 	{
   117 		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   118 	} else {
   119 		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   120 	}
   121 
   122 	return buffer;
   123 }
   124 
   125 bool CUserMessages::EndMessage()
   126 {
   127 	if (!m_InExec)
   128 	{
   129 		return false;
   130 	}
   131 
   132 	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
   133 	{
   134 		engine->MessageEnd();
   135 	} else {
   136 		ENGINE_CALL(MessageEnd)();
   137 	}
   138 
   139 	m_InExec = false;
   140 	m_CurFlags = 0;
   141 	m_CellRecFilter.Reset();
   142 
   143 	return true;
   144 }
   145 
   146 bool CUserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   147 {
   148 	if (msg_id < 0 || msg_id >= 255)
   149 	{
   150 		return false;
   151 	}
   152 
   153 	ListenerInfo *pInfo;
   154 	if (m_FreeListeners.empty())
   155 	{
   156 		pInfo = new ListenerInfo;
   157 	} else {
   158 		pInfo = m_FreeListeners.front();
   159 		m_FreeListeners.pop();
   160 	}
   161 
   162 	pInfo->Callback = pListener;
   163 	pInfo->IsHooked = false;
   164 	pInfo->KillMe = false;
   165 
   166 	if (!m_HookCount++)
   167 	{
   168 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
   169 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
   170 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
   171 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
   172 	}
   173 
   174 	if (intercept)
   175 	{
   176 		m_msgIntercepts[msg_id].push_back(pInfo);
   177 	} else {
   178 		m_msgHooks[msg_id].push_back(pInfo);
   179 	}
   180 
   181 	return true;
   182 }
   183 
   184 bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   185 {	
   186 	MsgList *pList;
   187 	MsgIter iter;
   188 	ListenerInfo *pInfo;
   189 	bool deleted = false;
   190 
   191 	if (msg_id < 0 || msg_id >= 255)
   192 	{
   193 		return false;
   194 	}
   195 
   196 	pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   197 	for (iter=pList->begin(); iter!=pList->end(); iter++)
   198 	{
   199 		pInfo = (*iter);
   200 		if (pInfo->Callback == pListener)
   201 		{
   202 			if (pInfo->IsHooked)
   203 			{
   204 				pInfo->KillMe = true;
   205 				return true;
   206 			}
   207 			pList->erase(iter);
   208 			deleted = true;
   209 			break;
   210 		}
   211 	}
   212 
   213 	if (deleted)
   214 	{
   215 		_DecRefCounter();
   216 	}
   217 
   218 	return deleted;
   219 }
   220 
   221 void CUserMessages::_DecRefCounter()
   222 {
   223 	if (--m_HookCount == 0)
   224 	{
   225 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
   226 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
   227 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
   228 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
   229 	}
   230 }
   231 
   232 bf_write *CUserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   233 {
   234 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   235 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   236 
   237 	if ((is_intercept_empty && is_hook_empty)
   238 		|| (m_InExec && !(m_CurFlags & USERMSG_PASSTHRU_ALL)))
   239 	{
   240 		m_InHook = false;
   241 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   242 	}
   243 
   244 	m_CurId = msg_type;
   245 	m_CurRecFilter = filter;
   246 	m_InHook = true;
   247 	m_BlockEndPost = false;
   248 
   249 	if (!is_intercept_empty)
   250 	{
   251 		m_InterceptBuffer.Reset();
   252 		RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   253 	}
   254 
   255 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   256 }
   257 
   258 bf_write *CUserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   259 {
   260 	if (!m_InHook)
   261 	{
   262 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   263 	}
   264 
   265 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   266 
   267 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   268 }
   269 
   270 void CUserMessages::OnMessageEnd_Post()
   271 {
   272 	if (!m_InHook || m_BlockEndPost)
   273 	{
   274 		RETURN_META(MRES_IGNORED);
   275 	}
   276 
   277 	MsgList *pList;
   278 	MsgIter iter;
   279 	ListenerInfo *pInfo;
   280 
   281 	pList = &m_msgIntercepts[m_CurId];
   282 	for (iter=pList->begin(); iter!=pList->end(); )
   283 	{
   284 		pInfo = (*iter);
   285 		pInfo->IsHooked = true;
   286 		pInfo->Callback->OnUserMessageSent(m_CurId);
   287 
   288 		if (pInfo->KillMe)
   289 		{
   290 			iter = pList->erase(iter);
   291 			m_FreeListeners.push(pInfo);
   292 			_DecRefCounter();
   293 			continue;
   294 		}
   295 
   296 		pInfo->IsHooked = false;
   297 		iter++;
   298 	}
   299 
   300 	pList = &m_msgHooks[m_CurId];
   301 	for (iter=pList->begin(); iter!=pList->end(); )
   302 	{
   303 		pInfo = (*iter);
   304 		pInfo->IsHooked = true;
   305 		pInfo->Callback->OnUserMessageSent(m_CurId);
   306 
   307 		if (pInfo->KillMe)
   308 		{
   309 			iter = pList->erase(iter);
   310 			m_FreeListeners.push(pInfo);
   311 			_DecRefCounter();
   312 			continue;
   313 		}
   314 
   315 		pInfo->IsHooked = false;
   316 		iter++;
   317 	}
   318 
   319 	m_InHook = false;
   320 }
   321 
   322 void CUserMessages::OnMessageEnd_Pre()
   323 {
   324 	if (!m_InHook)
   325 	{
   326 		RETURN_META(MRES_IGNORED);
   327 	}
   328 
   329 	MsgList *pList;
   330 	MsgIter iter;
   331 	ListenerInfo *pInfo;
   332 
   333 	ResultType res;
   334 	bool intercepted = false;
   335 	bool handled = false;
   336 
   337 	pList = &m_msgIntercepts[m_CurId];
   338 	for (iter=pList->begin(); iter!=pList->end(); )
   339 	{
   340 		pInfo = (*iter);
   341 		pInfo->IsHooked = true;
   342 		res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   343 
   344 		intercepted = true;
   345 
   346 		switch (res)
   347 		{
   348 		case Pl_Stop:
   349 			{
   350 				if (pInfo->KillMe)
   351 				{
   352 					pList->erase(iter);
   353 					m_FreeListeners.push(pInfo);
   354 					_DecRefCounter();
   355 					goto supercede;
   356 				}
   357 				pInfo->IsHooked = false;
   358 				goto supercede;
   359 			}
   360 		case Pl_Handled:
   361 			{
   362 				handled = true;
   363 				if (pInfo->KillMe)
   364 				{
   365 					iter = pList->erase(iter);
   366 					m_FreeListeners.push(pInfo);
   367 					_DecRefCounter();
   368 					continue;
   369 				}
   370 				break;
   371 			}
   372 		default:
   373 			{
   374 				if (pInfo->KillMe)
   375 				{
   376 					iter = pList->erase(iter);
   377 					m_FreeListeners.push(pInfo);
   378 					_DecRefCounter();
   379 					continue;
   380 				}
   381 				break;
   382 			}
   383 		}
   384 		pInfo->IsHooked = false;
   385 		iter++;
   386 	}
   387 
   388 	if (handled)
   389 	{
   390 		goto supercede;
   391 	}
   392 
   393 	if (intercepted)
   394 	{
   395 		bf_write *engine_bfw;
   396 
   397 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   398 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   399 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   400 		ENGINE_CALL(MessageEnd)();
   401 	}
   402 
   403 	pList = &m_msgHooks[m_CurId];
   404 	for (iter=pList->begin(); iter!=pList->end(); )
   405 	{
   406 		pInfo = (*iter);
   407 		pInfo->IsHooked = true;
   408 		pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   409 
   410 		if (pInfo->KillMe)
   411 		{
   412 			iter = pList->erase(iter);
   413 			m_FreeListeners.push(pInfo);
   414 			_DecRefCounter();
   415 			continue;
   416 		}
   417 
   418 		pInfo->IsHooked = false;
   419 		iter++;
   420 	}
   421 
   422 	RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
   423 supercede:
   424 	m_InHook = false;
   425 	m_BlockEndPost = true;
   426 	RETURN_META(MRES_SUPERCEDE);
   427 }