3 * ================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ================================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License,
10 * version 3.0, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to
21 * link the code of this program (as well as its derivative works) to
22 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
23 * Game MODs that run on software by the Valve Corporation. You must
24 * obey the GNU General Public License in all respects for all other
25 * code used. Additionally, AlliedModders LLC grants this exception
26 * to all derivative works. AlliedModders LLC defines further
27 * exceptions, found in LICENSE.txt (as of this writing, version
28 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
33 #include "UserMessages.h"
34 #include "sm_stringutil.h"
36 UserMessages g_UserMsgs;
38 SH_DECL_HOOK2(IVEngineServer, UserMessageBegin, SH_NOATTRIB, 0, bf_write *, IRecipientFilter *, int);
39 SH_DECL_HOOK0_void(IVEngineServer, MessageEnd, SH_NOATTRIB, 0);
41 UserMessages::UserMessages() : m_InterceptBuffer(m_pBase, 2500)
43 m_Names = sm_trie_create();
48 m_CurId = INVALID_MESSAGE_ID;
51 UserMessages::~UserMessages()
53 sm_trie_destroy(m_Names);
55 CStack<ListenerInfo *>::iterator iter;
56 for (iter=m_FreeListeners.begin(); iter!=m_FreeListeners.end(); iter++)
60 m_FreeListeners.popall();
63 void UserMessages::OnSourceModStartup(bool late)
65 /* -1 means SourceMM was unable to get the user message list */
66 m_FallbackSearch = (g_SMAPI->GetUserMessageCount() == -1);
69 void UserMessages::OnSourceModAllInitialized()
71 g_ShareSys.AddInterface(NULL, this);
74 void UserMessages::OnSourceModAllShutdown()
78 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
79 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
80 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
81 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
86 int UserMessages::GetMessageIndex(const char *msg)
90 if (!sm_trie_retrieve(m_Names, msg, reinterpret_cast<void **>(&msgid)))
98 while (gamedll->GetUserMessageInfo(msgid, msgbuf, sizeof(msgbuf), size))
100 if (strcmp(msgbuf, msg) == 0)
102 sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
109 msgid = g_SMAPI->FindUserMessage(msg);
111 if (msgid != INVALID_MESSAGE_ID)
113 sm_trie_insert(m_Names, msg, reinterpret_cast<void *>(msgid));
120 bool UserMessages::GetMessageName(int msgid, char *buffer, size_t maxlength) const
122 if (m_FallbackSearch)
125 return gamedll->GetUserMessageInfo(msgid, buffer, maxlength, size);
128 const char *msg = g_SMAPI->GetUserMessage(msgid);
132 strncopy(buffer, msg, maxlength);
139 bf_write *UserMessages::StartMessage(int msg_id, cell_t players[], unsigned int playersNum, int flags)
143 if (m_InExec || m_InHook)
147 if (msg_id < 0 || msg_id >= 255)
152 m_CellRecFilter.Initialize(players, playersNum);
155 if (m_CurFlags & USERMSG_INITMSG)
157 m_CellRecFilter.SetToInit(true);
159 if (m_CurFlags & USERMSG_RELIABLE)
161 m_CellRecFilter.SetToReliable(true);
166 if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
168 buffer = engine->UserMessageBegin(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
170 buffer = ENGINE_CALL(UserMessageBegin)(static_cast<IRecipientFilter *>(&m_CellRecFilter), msg_id);
176 bool UserMessages::EndMessage()
183 if (m_CurFlags & (USERMSG_PASSTHRU_ALL|USERMSG_PASSTHRU))
185 engine->MessageEnd();
187 ENGINE_CALL(MessageEnd)();
192 m_CellRecFilter.Reset();
197 bool UserMessages::HookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
199 if (msg_id < 0 || msg_id >= 255)
205 if (m_FreeListeners.empty())
207 pInfo = new ListenerInfo;
209 pInfo = m_FreeListeners.front();
210 m_FreeListeners.pop();
213 pInfo->Callback = pListener;
214 pInfo->IsHooked = false;
215 pInfo->KillMe = false;
219 SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
220 SH_ADD_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
221 SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
222 SH_ADD_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
227 m_msgIntercepts[msg_id].push_back(pInfo);
229 m_msgHooks[msg_id].push_back(pInfo);
235 bool UserMessages::UnhookUserMessage(int msg_id, IUserMessageListener *pListener, bool intercept)
240 bool deleted = false;
242 if (msg_id < 0 || msg_id >= 255)
247 pList = (intercept) ? &m_msgIntercepts[msg_id] : &m_msgHooks[msg_id];
248 for (iter=pList->begin(); iter!=pList->end(); iter++)
251 if (pInfo->Callback == pListener)
255 pInfo->KillMe = true;
272 void UserMessages::_DecRefCounter()
274 if (--m_HookCount == 0)
276 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Pre, false);
277 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, UserMessageBegin, engine, this, &UserMessages::OnStartMessage_Post, true);
278 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Pre, false);
279 SH_REMOVE_HOOK_MEMFUNC(IVEngineServer, MessageEnd, engine, this, &UserMessages::OnMessageEnd_Post, true);
283 bf_write *UserMessages::OnStartMessage_Pre(IRecipientFilter *filter, int msg_type)
285 bool is_intercept_empty = m_msgIntercepts[msg_type].empty();
286 bool is_hook_empty = m_msgHooks[msg_type].empty();
288 if ((is_intercept_empty && is_hook_empty)
289 || (m_InExec && !(m_CurFlags & USERMSG_PASSTHRU_ALL)))
292 RETURN_META_VALUE(MRES_IGNORED, NULL);
296 m_CurRecFilter = filter;
298 m_BlockEndPost = false;
300 if (!is_intercept_empty)
302 m_InterceptBuffer.Reset();
303 RETURN_META_VALUE(MRES_SUPERCEDE, &m_InterceptBuffer);
306 RETURN_META_VALUE(MRES_IGNORED, NULL);
309 bf_write *UserMessages::OnStartMessage_Post(IRecipientFilter *filter, int msg_type)
313 RETURN_META_VALUE(MRES_IGNORED, NULL);
316 m_OrigBuffer = META_RESULT_ORIG_RET(bf_write *);
318 RETURN_META_VALUE(MRES_IGNORED, NULL);
321 void UserMessages::OnMessageEnd_Post()
323 if (!m_InHook || m_BlockEndPost)
325 RETURN_META(MRES_IGNORED);
332 pList = &m_msgIntercepts[m_CurId];
333 for (iter=pList->begin(); iter!=pList->end(); )
336 pInfo->IsHooked = true;
337 pInfo->Callback->OnUserMessageSent(m_CurId);
341 iter = pList->erase(iter);
342 m_FreeListeners.push(pInfo);
347 pInfo->IsHooked = false;
353 pList = &m_msgHooks[m_CurId];
354 for (iter=pList->begin(); iter!=pList->end(); )
357 pInfo->IsHooked = true;
358 pInfo->Callback->OnUserMessageSent(m_CurId);
362 iter = pList->erase(iter);
363 m_FreeListeners.push(pInfo);
368 pInfo->IsHooked = false;
373 void UserMessages::OnMessageEnd_Pre()
377 RETURN_META(MRES_IGNORED);
385 bool intercepted = false;
386 bool handled = false;
388 pList = &m_msgIntercepts[m_CurId];
389 for (iter=pList->begin(); iter!=pList->end(); )
392 pInfo->IsHooked = true;
393 res = pInfo->Callback->InterceptUserMessage(m_CurId, &m_InterceptBuffer, m_CurRecFilter);
404 m_FreeListeners.push(pInfo);
408 pInfo->IsHooked = false;
416 iter = pList->erase(iter);
417 m_FreeListeners.push(pInfo);
427 iter = pList->erase(iter);
428 m_FreeListeners.push(pInfo);
435 pInfo->IsHooked = false;
446 bf_write *engine_bfw;
448 engine_bfw = ENGINE_CALL(UserMessageBegin)(m_CurRecFilter, m_CurId);
449 m_ReadBuffer.StartReading(m_InterceptBuffer.GetBasePointer(), m_InterceptBuffer.GetNumBytesWritten());
450 engine_bfw->WriteBitsFromBuffer(&m_ReadBuffer, m_InterceptBuffer.GetNumBitsWritten());
451 ENGINE_CALL(MessageEnd)();
454 pList = &m_msgHooks[m_CurId];
455 for (iter=pList->begin(); iter!=pList->end(); )
458 pInfo->IsHooked = true;
459 pInfo->Callback->OnUserMessage(m_CurId, m_OrigBuffer, m_CurRecFilter);
463 iter = pList->erase(iter);
464 m_FreeListeners.push(pInfo);
469 pInfo->IsHooked = false;
473 RETURN_META((intercepted) ? MRES_SUPERCEDE : MRES_IGNORED);
476 m_BlockEndPost = true;
477 RETURN_META(MRES_SUPERCEDE);