core/UserMessages.cpp
author David Anderson <dvander@alliedmods.net>
Sat Mar 10 21:26:04 2007 +0000 (2007-03-10)
changeset 592 c2a2c38f1331
child 593 507e74843261
permissions -rw-r--r--
part 2 of singleton renaming
     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)
    93 	{
    94 		return NULL;
    95 	}
    96 	if (msg_id < 0 || msg_id >= 255)
    97 	{
    98 		return NULL;
    99 	}
   100 
   101 	m_CellRecFilter.SetRecipientPtr(players, playersNum);
   102 
   103 	m_CurFlags = flags;
   104 	if (m_CurFlags & USERMSG_INITMSG)
   105 	{
   106 		m_CellRecFilter.SetInitMessage(true);
   107 	}
   108 	if (m_CurFlags & USERMSG_RELIABLE)
   109 	{
   110 		m_CellRecFilter.SetReliable(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.ResetFilter();
   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 	if (!m_HookCount++)
   154 	{
   155 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
   156 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
   157 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
   158 		SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
   159 	}
   160 
   161 	if (intercept)
   162 	{
   163 		m_msgIntercepts[msg_id].push_back(pListener);
   164 	} else {
   165 		m_msgHooks[msg_id].push_back(pListener);
   166 	}
   167 
   168 	return true;
   169 }
   170 
   171 bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
   172 {
   173 	//:TODO: restrict user from unhooking stuff during a message hook to avoid iterator mess
   174 	List<IUserMessageListener *> *lst;
   175 	IUserMessageListener *listener;
   176 	MsgIter iter;
   177 	bool deleted = false;
   178 
   179 	if (msg_id < 0 || msg_id >= 255)
   180 	{
   181 		return false;
   182 	}
   183 
   184 	lst = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
   185 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   186 	{
   187 		listener = (*iter);
   188 		if (listener == pListener)
   189 		{
   190 			lst->erase(iter);
   191 			deleted = true;
   192 			break;
   193 		}
   194 	}
   195 
   196 	if (deleted && !(--m_HookCount))
   197 	{
   198 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Pre, false);
   199 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &CUserMessages::OnStartMessage_Post, true);
   200 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Pre, false);
   201 		SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &CUserMessages::OnMessageEnd_Post, true);
   202 	}
   203 
   204 	return (deleted) ? true : false;
   205 }
   206 
   207 bf_write *CUserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
   208 {
   209 	bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
   210 	bool is_hook_empty = m_msgHooks[msg_type].empty();
   211 
   212 	if (is_intercept_empty && is_hook_empty)
   213 	{
   214 		m_InHook = false;
   215 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   216 	}
   217 
   218 	if ((m_InExec) && !(m_CurFlags & USERMSG_PASSTHRU_ALL))
   219 	{
   220 		m_InHook = false;
   221 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   222 	}
   223 
   224 	m_CurId = msg_type;
   225 	m_CurRecFilter = filter;
   226 	m_InterceptBuffer.Reset();
   227 	m_InHook = true;
   228 
   229 	if (!is_intercept_empty)
   230 	{
   231 		RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
   232 	}
   233 
   234 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   235 }
   236 
   237 bf_write *CUserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
   238 {
   239 	if (!m_InHook)
   240 	{
   241 		RETURN_META_VALUE(MRES_IGNORED, NULL);
   242 	}
   243 
   244 	m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
   245 
   246 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   247 }
   248 
   249 void CUserMessages::OnMessageEnd_Post()
   250 {
   251 	if (!m_InHook || (META_RESULT_STATUS == MRES_SUPERCEDE))
   252 	{
   253 		RETURN_META(MRES_IGNORED);
   254 	}
   255 
   256 	List<IUserMessageListener *> *lst;
   257 	IUserMessageListener *listener;
   258 	MsgIter iter;
   259 
   260 	lst = &m_msgIntercepts[m_CurId];
   261 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   262 	{
   263 		listener = (*iter);
   264 		listener->OnUserMessageSent(m_CurId);
   265 	}
   266 
   267 	lst = &m_msgHooks[m_CurId];
   268 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   269 	{
   270 		listener = (*iter);
   271 		listener->OnUserMessageSent(m_CurId);
   272 	}
   273 }
   274 
   275 void CUserMessages::OnMessageEnd_Pre()
   276 {
   277 	if (!m_InHook)
   278 	{
   279 		RETURN_META(MRES_IGNORED);
   280 	}
   281 
   282 	List<IUserMessageListener *> *lst;
   283 	IUserMessageListener *listener;
   284 	bf_write *engine_bfw;
   285 	MsgIter iter;
   286 	ResultType res;
   287 	bool intercepted = false;
   288 	bool handled = false;
   289 
   290 	lst = &m_msgIntercepts[m_CurId];
   291 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   292 	{
   293 		listener = (*iter);
   294 		res = listener->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
   295 		if (res == Pl_Stop)
   296 		{
   297 			goto supercede;
   298 		} else if (res == Pl_Handled) {
   299 			handled = true;
   300 		}
   301 		intercepted = true;
   302 	}
   303 
   304 	if (handled)
   305 	{
   306 		goto supercede;
   307 	}
   308 
   309 	if (intercepted)
   310 	{
   311 		engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
   312 		m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
   313 		engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
   314 		ENGINE_CALL(MessageEnd)();
   315 		goto supercede;
   316 	}
   317 
   318 	lst = &m_msgHooks[m_CurId];
   319 	for (iter=lst->begin(); iter!=lst->end(); iter++)
   320 	{
   321 		listener = (*iter);
   322 		listener->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
   323 	}
   324 
   325 	RETURN_META(MRES_IGNORED);
   326 supercede:
   327 	RETURN_META(MRES_SUPERCEDE);
   328 }