core/CUserMessages.cpp
author David Anderson <dvander@alliedmods.net>
Fri Mar 02 08:34:13 2007 +0000 (2007-03-02)
changeset 558 fc0eec032efb
parent 553 a8efe0286237
child 565 343e1ea4eae2
permissions -rw-r--r--
added state change function
     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 "CUserMessages.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::OnSourceModAllShutdown()
    40 {
    41 	if (m_HookCount)
    42 	{
    43 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
    44 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
    45 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
    46 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
    47 	}
    48 	m_HookCount = 0;
    49 }
    50 
    51 int CUserMessages::GetMessageIndex(const char *msg)
    52 {
    53 	int msgid;
    54 	if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
    55 	{
    56 		char buf[255];
    57 		int dummy;
    58 		msgid = 0;
    59 
    60 		while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy))
    61 		{
    62 			if (strcmp(msg, buf) == 0)
    63 			{
    64 				sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
    65 				return msgid;
    66 			}
    67 			msgid++;
    68 		}
    69 
    70 		return INVALID_MESSAGE_ID;
    71 	}
    72 
    73 	return msgid;
    74 }
    75 
    76 bool CUserMessages::GetMessageName(int msgid, char *buffer, size_t maxlen) const
    77 {
    78 	int dummy;
    79 
    80 	return gamedll->GetUserMessageInfo(msgid, buffer, maxlen, dummy);
    81 }
    82 
    83 bf_write *CUserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
    84 {
    85 	bf_write *buffer;
    86 	
    87 	if (m_InExec)
    88 	{
    89 		return NULL;
    90 	}
    91 	if (msg_id < 0 || msg_id >= 255)
    92 	{
    93 		return NULL;
    94 	}
    95 
    96 	m_CellRecFilter.SetRecipientPtr(players, playersNum);
    97 
    98 	m_CurFlags = flags;
    99 	if (m_CurFlags & USERMSG_INITMSG)
   100 	{
   101 		m_CellRecFilter.SetInitMessage(true);
   102 	}
   103 	if (m_CurFlags & USERMSG_RELIABLE)
   104 	{
   105 		m_CellRecFilter.SetReliable(true);
   106 	}
   107 
   108 	m_InExec = true;
   109 
   110 	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
   111 	{
   112 		buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   113 	} else {
   114 		buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
   115 	}
   116 
   117 	return buffer;
   118 }
   119 
   120 bool CUserMessages::EndMessage()
   121 {
   122 	if (!m_InExec)
   123 	{
   124 		return false;
   125 	}
   126 
   127 	if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
   128 	{
   129 		engine->MessageEnd();
   130 	} else {
   131 		ENGINE_CALL(MessageEnd)();
   132 	}
   133 
   134 	m_InExec = false;
   135 	m_CurFlags = 0;
   136 	m_CellRecFilter.ResetFilter();
   137 
   138 	return true;
   139 }
   140 
   141 bool CUserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   142 {
   143 	if (msg_id < 0 || msg_id >= 255)
   144 	{
   145 		return false;
   146 	}
   147 
   148 	if (!m_HookCount++)
   149 	{
   150 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
   151 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
   152 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
   153 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
   154 	}
   155 
   156 	if (intercept)
   157 	{
   158 		m_msgIntercepts[msg_id].push_back(pListener);
   159 	} else {
   160 		m_msgHooks[msg_id].push_back(pListener);
   161 	}
   162 
   163 	return true;
   164 }
   165 
   166 bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   167 {
   168 	//:TODO: restrict user from unhooking stuff during a message hook to avoid iterator mess
   169 	List<IUserMessageListener *> *lst;
   170 	IUserMessageListener *listener;
   171 	MsgIter iter;
   172 	bool deleted = false;
   173 
   174 	if (msg_id < 0 || msg_id >= 255)
   175 	{
   176 		return false;
   177 	}
   178 
   179 	lst = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   180 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   181 	{
   182 		listener = (*iter);
   183 		if (listener == pListener)
   184 		{
   185 			lst->erase(iter);
   186 			deleted = true;
   187 			break;
   188 		}
   189 	}
   190 
   191 	if (deleted && !(--m_HookCount))
   192 	{
   193 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
   194 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
   195 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
   196 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
   197 	}
   198 
   199 	return (deleted) ? true : false;
   200 }
   201 
   202 bf_write *CUserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   203 {
   204 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   205 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   206 
   207 	if (is_intercept_empty && is_hook_empty)
   208 	{
   209 		m_InHook = false;
   210 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   211 	}
   212 
   213 	if ((m_InExec) && !(m_CurFlags & USERMSG_PASSTHRU_ALL))
   214 	{
   215 		m_InHook = false;
   216 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   217 	}
   218 
   219 	m_CurId = msg_type;
   220 	m_CurRecFilter = filter;
   221 	m_InterceptBuffer.Reset();
   222 	m_InHook = true;
   223 
   224 	if (!is_intercept_empty)
   225 	{
   226 		RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   227 	}
   228 
   229 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   230 }
   231 
   232 bf_write *CUserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   233 {
   234 	if (!m_InHook)
   235 	{
   236 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   237 	}
   238 
   239 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   240 
   241 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   242 }
   243 
   244 void CUserMessages::OnMessageEnd_Post()
   245 {
   246 	if (!m_InHook || (META_RESULT_STATUS == MRES_SUPERCEDE))
   247 	{
   248 		RETURN_META(MRES_IGNORED);
   249 	}
   250 
   251 	List<IUserMessageListener *> *lst;
   252 	IUserMessageListener *listener;
   253 	MsgIter iter;
   254 
   255 	lst = &m_msgIntercepts[m_CurId];
   256 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   257 	{
   258 		listener = (*iter);
   259 		listener->OnUserMessageSent(m_CurId);
   260 	}
   261 
   262 	lst = &m_msgHooks[m_CurId];
   263 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   264 	{
   265 		listener = (*iter);
   266 		listener->OnUserMessageSent(m_CurId);
   267 	}
   268 }
   269 
   270 void CUserMessages::OnMessageEnd_Pre()
   271 {
   272 	if (!m_InHook)
   273 	{
   274 		RETURN_META(MRES_IGNORED);
   275 	}
   276 
   277 	List<IUserMessageListener *> *lst;
   278 	IUserMessageListener *listener;
   279 	bf_write *engine_bfw;
   280 	MsgIter iter;
   281 	ResultType res;
   282 	bool intercepted = false;
   283 	bool handled = false;
   284 
   285 	lst = &m_msgIntercepts[m_CurId];
   286 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   287 	{
   288 		listener = (*iter);
   289 		res = listener->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   290 		if (res == Pl_Stop)
   291 		{
   292 			goto supercede;
   293 		} else if (res == Pl_Handled) {
   294 			handled = true;
   295 		}
   296 		intercepted = true;
   297 	}
   298 
   299 	if (handled)
   300 	{
   301 		goto supercede;
   302 	}
   303 
   304 	if (intercepted)
   305 	{
   306 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   307 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   308 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   309 		ENGINE_CALL(MessageEnd)();
   310 		goto supercede;
   311 	}
   312 
   313 	lst = &m_msgHooks[m_CurId];
   314 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   315 	{
   316 		listener = (*iter);
   317 		listener->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   318 	}
   319 
   320 	RETURN_META(MRES_IGNORED);
   321 supercede:
   322 	RETURN_META(MRES_SUPERCEDE);
   323 }