3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
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
15 #include "UserMessages.h"
17 CUserMessages g_UserMsgs;
19 SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
20 SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
22 //:TODO: USE NEW MM IFACE TO SEARCH FOR MESSAGE NAMES ETC! faluco--> i'll do this when i have some spare time
24 CUserMessages::CUserMessages() : m_InterceptBuffer(m_pBase, 2500)
26 m_Names = sm_trie_create();
31 m_CurId = INVALID_MESSAGE_ID;
34 CUserMessages::~CUserMessages()
36 sm_trie_destroy(m_Names);
39 void CUserMessages::OnSourceModAllInitialized()
41 g_ShareSys.AddInterface(NULL, this);
44 void CUserMessages::OnSourceModAllShutdown()
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);
56 int CUserMessages::GetMessageIndex(const char *msg)
59 if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
65 while (gamedll->GetUserMessageInfo(msgid, buf, sizeof(buf), dummy))
67 if (strcmp(msg, buf) == 0)
69 sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
75 return INVALID_MESSAGE_ID;
81 bool CUserMessages::GetMessageName(int msgid, char *buffer, size_t maxlen) const
85 return gamedll->GetUserMessageInfo(msgid, buffer, maxlen, dummy);
88 bf_write *CUserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
92 if (m_InExec || m_InHook)
96 if (msg_id < 0 || msg_id >= 255)
101 m_CellRecFilter.Initialize(players, playersNum);
104 if (m_CurFlags & USERMSG_INITMSG)
106 m_CellRecFilter.SetToInit(true);
108 if (m_CurFlags & USERMSG_RELIABLE)
110 m_CellRecFilter.SetToReliable(true);
115 if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
117 buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
119 buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
125 bool CUserMessages::EndMessage()
132 if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
134 engine->MessageEnd();
136 ENGINE_CALL(MessageEnd)();
141 m_CellRecFilter.Reset();
146 bool CUserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
148 if (msg_id < 0 || msg_id >= 255)
154 if (m_FreeListeners.empty())
156 pInfo = new ListenerInfo;
158 pInfo = m_FreeListeners.front();
159 m_FreeListeners.pop();
162 pInfo->Callback = pListener;
163 pInfo->IsHooked = false;
164 pInfo->KillMe = false;
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);
176 m_msgIntercepts[msg_id].push_back(pInfo);
178 m_msgHooks[msg_id].push_back(pInfo);
184 bool CUserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
189 bool deleted = false;
191 if (msg_id < 0 || msg_id >= 255)
196 pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
197 for (iter=pList->begin(); iter!=pList->end(); iter++)
200 if (pInfo->Callback == pListener)
204 pInfo->KillMe = true;
221 void CUserMessages::_DecRefCounter()
223 if (--m_HookCount == 0)
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);
232 bf_write *CUserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
234 bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
235 bool is_hook_empty = m_msgHooks[msg_type].empty();
237 if ((is_intercept_empty && is_hook_empty)
238 || (m_InExec && !(m_CurFlags & USERMSG_PASSTHRU_ALL)))
241 RETURN_META_VALUE(MRES_IGNORED, NULL);
245 m_CurRecFilter = filter;
247 m_BlockEndPost = false;
249 if (!is_intercept_empty)
251 m_InterceptBuffer.Reset();
252 RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
255 RETURN_META_VALUE(MRES_IGNORED, NULL);
258 bf_write *CUserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
262 RETURN_META_VALUE(MRES_IGNORED, NULL);
265 m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
267 RETURN_META_VALUE(MRES_IGNORED, NULL);
270 void CUserMessages::OnMessageEnd_Post()
272 if (!m_InHook || m_BlockEndPost)
274 RETURN_META(MRES_IGNORED);
281 pList = &m_msgIntercepts[m_CurId];
282 for (iter=pList->begin(); iter!=pList->end(); )
285 pInfo->IsHooked = true;
286 pInfo->Callback->OnUserMessageSent(m_CurId);
290 iter = pList->erase(iter);
291 m_FreeListeners.push(pInfo);
296 pInfo->IsHooked = false;
300 pList = &m_msgHooks[m_CurId];
301 for (iter=pList->begin(); iter!=pList->end(); )
304 pInfo->IsHooked = true;
305 pInfo->Callback->OnUserMessageSent(m_CurId);
309 iter = pList->erase(iter);
310 m_FreeListeners.push(pInfo);
315 pInfo->IsHooked = false;
322 void CUserMessages::OnMessageEnd_Pre()
326 RETURN_META(MRES_IGNORED);
334 bool intercepted = false;
335 bool handled = false;
337 pList = &m_msgIntercepts[m_CurId];
338 for (iter=pList->begin(); iter!=pList->end(); )
341 pInfo->IsHooked = true;
342 res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
353 m_FreeListeners.push(pInfo);
357 pInfo->IsHooked = false;
365 iter = pList->erase(iter);
366 m_FreeListeners.push(pInfo);
376 iter = pList->erase(iter);
377 m_FreeListeners.push(pInfo);
384 pInfo->IsHooked = false;
395 bf_write *engine_bfw;
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)();
403 pList = &m_msgHooks[m_CurId];
404 for (iter=pList->begin(); iter!=pList->end(); )
407 pInfo->IsHooked = true;
408 pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
412 iter = pList->erase(iter);
413 m_FreeListeners.push(pInfo);
418 pInfo->IsHooked = false;
422 RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
425 m_BlockEndPost = true;
426 RETURN_META(MRES_SUPERCEDE);