Skip to content

Commit 109ccc6

Browse files
committed
Fixed #400 and #404.
Added lazy-loading support to the internal logging listener. Fixed ListenerManager.clear not being properly finalized.
1 parent e6920e1 commit 109ccc6

File tree

5 files changed

+183
-121
lines changed

5 files changed

+183
-121
lines changed

src/core/modules/listeners/listeners_manager.cpp

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@
2828
// Includes.
2929
//-----------------------------------------------------------------------------
3030
#include "listeners_manager.h"
31+
#include "sp_main.h"
32+
33+
34+
//-----------------------------------------------------------------------------
35+
// Externals.
36+
//-----------------------------------------------------------------------------
37+
extern CServerOutputListenerManager* GetOnServerOutputListenerManager();
3138

3239

3340
//-----------------------------------------------------------------------------
@@ -149,7 +156,140 @@ object CListenerManager::__getitem__(unsigned int index)
149156

150157
void CListenerManager::clear()
151158
{
152-
m_vecCallables.RemoveAll();
159+
if (GetCount()) {
160+
m_vecCallables.RemoveAll();
161+
Finalize();
162+
}
163+
}
164+
165+
166+
//-----------------------------------------------------------------------------
167+
// Server output hook.
168+
//-----------------------------------------------------------------------------
169+
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
170+
SpewRetval_t SP_SpewOutput( SpewType_t spewType, const tchar *pMsg )
171+
{
172+
static CServerOutputListenerManager *pManager = GetOnServerOutputListenerManager();
173+
174+
bool block = false;
175+
EnterCriticalSection(&pManager->m_pCriticalSection); {
176+
block = pManager->CallCallbacks((MessageSeverity)spewType, pMsg);
177+
} LeaveCriticalSection(&pManager->m_pCriticalSection);
178+
179+
if (!block && pManager->m_pOldSpewOutputFunc) {
180+
return pManager->m_pOldSpewOutputFunc(spewType, pMsg);
181+
}
182+
183+
return SPEW_CONTINUE;
184+
}
185+
#else
186+
class SPLoggingListener: public ILoggingListener
187+
{
188+
public:
189+
virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
190+
{
191+
static CServerOutputListenerManager *pManager = GetOnServerOutputListenerManager();
192+
193+
bool block = false;
194+
EnterCriticalSection(&pManager->m_pCriticalSection); {
195+
block = pManager->CallCallbacks((MessageSeverity)pContext->m_Severity, pMessage);
196+
} LeaveCriticalSection(&pManager->m_pCriticalSection);
197+
198+
if (!block)
199+
{
200+
// Restore the old logging state before SP has been loaded
201+
LoggingSystem_PopLoggingState(false);
202+
203+
// Resend the log message. Our listener won't get called anymore
204+
LoggingSystem_LogDirect(
205+
pContext->m_ChannelID,
206+
pContext->m_Severity,
207+
pContext->m_Color,
208+
pMessage);
209+
210+
// Create a new logging state with only our listener being active
211+
#if defined(ENGINE_LEFT4DEAD2)
212+
LoggingSystem_PushLoggingState(false);
213+
#else
214+
LoggingSystem_PushLoggingState(false, true);
215+
#endif
216+
LoggingSystem_RegisterLoggingListener(this);
217+
218+
}
219+
}
220+
} g_LoggingListener;
221+
222+
#endif
223+
224+
225+
//-----------------------------------------------------------------------------
226+
// CServerOutputListenerManager constructor.
227+
//-----------------------------------------------------------------------------
228+
CServerOutputListenerManager::CServerOutputListenerManager()
229+
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
230+
:m_pOldSpewOutputFunc(NULL)
231+
#endif
232+
{
233+
InitializeCriticalSection(&m_pCriticalSection);
234+
}
235+
236+
237+
//-----------------------------------------------------------------------------
238+
// Called when the first callback is being registered.
239+
//-----------------------------------------------------------------------------
240+
void CServerOutputListenerManager::Initialize()
241+
{
242+
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
243+
DevMsg(1, MSG_PREFIX "Retrieving old output function...\n");
244+
m_pOldSpewOutputFunc = GetSpewOutputFunc();
245+
246+
DevMsg(1, MSG_PREFIX "Setting new output function...\n");
247+
SpewOutputFunc(SP_SpewOutput);
248+
#else
249+
DevMsg(1, MSG_PREFIX "Registering logging listener...\n");
250+
#if defined(ENGINE_LEFT4DEAD2)
251+
LoggingSystem_PushLoggingState(false);
252+
#else
253+
LoggingSystem_PushLoggingState(false, true);
254+
#endif
255+
LoggingSystem_RegisterLoggingListener(&g_LoggingListener);
256+
#endif
257+
}
258+
259+
260+
//-----------------------------------------------------------------------------
261+
// Called when the last callback is being unregistered.
262+
//-----------------------------------------------------------------------------
263+
void CServerOutputListenerManager::Finalize()
264+
{
265+
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
266+
if (m_pOldSpewOutputFunc) {
267+
DevMsg(1, MSG_PREFIX "Restoring old output function...\n");
268+
SpewOutputFunc(m_pOldSpewOutputFunc);
269+
}
270+
#else
271+
DevMsg(1, MSG_PREFIX "Restoring old logging state...\n");
272+
LoggingSystem_PopLoggingState(false);
273+
#endif
274+
}
275+
276+
277+
//-----------------------------------------------------------------------------
278+
// Calls all registered server output callbacks.
279+
//-----------------------------------------------------------------------------
280+
bool CServerOutputListenerManager::CallCallbacks(MessageSeverity severity, const tchar *pMsg)
281+
{
282+
bool block = false;
283+
FOR_EACH_VEC(m_vecCallables, i) {
284+
BEGIN_BOOST_PY()
285+
object return_value = m_vecCallables[i](severity, pMsg);
286+
287+
if (!return_value.is_none() && extract<OutputReturn>(return_value) == OUTPUT_BLOCK)
288+
block = true;
289+
290+
END_BOOST_PY_NORET()
291+
}
292+
return block;
153293
}
154294

155295

src/core/modules/listeners/listeners_manager.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,16 @@
3131
// Includes.
3232
//-----------------------------------------------------------------------------
3333
#include "utilities/wrap_macros.h"
34+
#include "utilities/baseentity.h"
35+
#include "modules/core/core.h"
36+
3437
#include "utlvector.h"
38+
#include "dbg.h"
39+
#include "tier0/threadtools.h"
40+
41+
#ifdef _WIN32
42+
#include <Windows.h>
43+
#endif
3544

3645

3746
//-----------------------------------------------------------------------------
@@ -99,6 +108,28 @@ class CListenerManager: public wrapper<CListenerManager>
99108
};
100109

101110

111+
//-----------------------------------------------------------------------------
112+
// CServerOutputListenerManager class.
113+
//-----------------------------------------------------------------------------
114+
class CServerOutputListenerManager: public CListenerManager
115+
{
116+
public:
117+
CServerOutputListenerManager();
118+
119+
virtual void Initialize();
120+
virtual void Finalize();
121+
122+
bool CallCallbacks(MessageSeverity severity, const tchar *pMsg);
123+
124+
public:
125+
CRITICAL_SECTION m_pCriticalSection;
126+
127+
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
128+
SpewOutputFunc_t m_pOldSpewOutputFunc;
129+
#endif
130+
};
131+
132+
102133
//-----------------------------------------------------------------------------
103134
// Functions
104135
//-----------------------------------------------------------------------------

src/core/modules/listeners/listeners_wrap.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ DEFINE_MANAGER_ACCESSOR(OnNetworkedEntityDeleted)
6161
DEFINE_MANAGER_ACCESSOR(OnDataLoaded)
6262
DEFINE_MANAGER_ACCESSOR(OnCombinerPreCache)
6363
DEFINE_MANAGER_ACCESSOR(OnDataUnloaded)
64-
DEFINE_MANAGER_ACCESSOR(OnServerOutput)
6564
DEFINE_MANAGER_ACCESSOR(OnPlayerRunCommand)
6665
DEFINE_MANAGER_ACCESSOR(OnButtonStateChanged)
6766

67+
static CServerOutputListenerManager s_OnServerOutput;
68+
CServerOutputListenerManager *GetOnServerOutputListenerManager()
69+
{
70+
return &s_OnServerOutput;
71+
}
72+
6873

6974
//-----------------------------------------------------------------------------
7075
// Forward declarations.
@@ -169,7 +174,7 @@ void export_listener_managers(scope _listeners)
169174
_listeners.attr("on_combiner_pre_cache_listener_manager") = object(ptr(GetOnCombinerPreCacheListenerManager()));
170175
_listeners.attr("on_data_unloaded_listener_manager") = object(ptr(GetOnDataUnloadedListenerManager()));
171176

172-
_listeners.attr("on_server_output_listener_manager") = object(ptr(GetOnServerOutputListenerManager()));
177+
_listeners.attr("on_server_output_listener_manager") = object(ptr((CListenerManager *)GetOnServerOutputListenerManager()));
173178

174179
_listeners.attr("on_player_run_command_listener_manager") = object(ptr(GetOnPlayerRunCommandListenerManager()));
175180
_listeners.attr("on_button_state_changed_listener_manager") = object(ptr(GetOnButtonStateChangedListenerManager()));

src/core/sp_main.cpp

Lines changed: 4 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ extern ICvar* g_pCVar;
111111
extern void InitCommands();
112112
extern void ClearAllCommands();
113113
extern PLUGIN_RESULT DispatchClientCommand(edict_t *pEntity, const CCommand &command);
114+
extern CServerOutputListenerManager* GetOnServerOutputListenerManager();
114115

115116
//-----------------------------------------------------------------------------
116117
// The plugin is a static singleton that is exported as an interface
@@ -185,100 +186,13 @@ bool GetInterfaces( InterfaceHelper_t* pInterfaceList, CreateInterfaceFn factory
185186
}
186187

187188

188-
//-----------------------------------------------------------------------------
189-
// Server output hook.
190-
//-----------------------------------------------------------------------------
191-
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
192-
SpewRetval_t SP_SpewOutput( SpewType_t spewType, const tchar *pMsg )
193-
{
194-
bool block = false;
195-
196-
// Only filter outputs from the main thread. See issues #400 and #404.
197-
if (ThreadInMainThread()) {
198-
extern CListenerManager* GetOnServerOutputListenerManager();
199-
200-
for(int i = 0; i < GetOnServerOutputListenerManager()->m_vecCallables.Count(); i++)
201-
{
202-
BEGIN_BOOST_PY()
203-
object return_value = GetOnServerOutputListenerManager()->m_vecCallables[i](
204-
(MessageSeverity) spewType,
205-
pMsg);
206-
207-
if (!return_value.is_none() && extract<OutputReturn>(return_value) == OUTPUT_BLOCK)
208-
block = true;
209-
210-
END_BOOST_PY_NORET()
211-
}
212-
}
213-
214-
if (!block && g_SourcePythonPlugin.m_pOldSpewOutputFunc)
215-
return g_SourcePythonPlugin.m_pOldSpewOutputFunc(spewType, pMsg);
216-
217-
return SPEW_CONTINUE;
218-
}
219-
#else
220-
class SPLoggingListener: public ILoggingListener
221-
{
222-
public:
223-
virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
224-
{
225-
bool block = false;
226-
227-
// Only filter outputs from the main thread. See issues #400 and #404.
228-
if (ThreadInMainThread()) {
229-
extern CListenerManager* GetOnServerOutputListenerManager();
230-
231-
for(int i = 0; i < GetOnServerOutputListenerManager()->m_vecCallables.Count(); i++)
232-
{
233-
BEGIN_BOOST_PY()
234-
object return_value = GetOnServerOutputListenerManager()->m_vecCallables[i](
235-
(MessageSeverity) pContext->m_Severity,
236-
pMessage);
237-
238-
if (!return_value.is_none() && extract<OutputReturn>(return_value) == OUTPUT_BLOCK)
239-
block = true;
240-
241-
END_BOOST_PY_NORET()
242-
}
243-
}
244-
245-
if (!block)
246-
{
247-
// Restore the old logging state before SP has been loaded
248-
LoggingSystem_PopLoggingState(false);
249-
250-
// Resend the log message. Our listener won't get called anymore
251-
LoggingSystem_LogDirect(
252-
pContext->m_ChannelID,
253-
pContext->m_Severity,
254-
pContext->m_Color,
255-
pMessage);
256-
257-
// Create a new logging state with only our listener being active
258-
#if defined(ENGINE_LEFT4DEAD2)
259-
LoggingSystem_PushLoggingState(false);
260-
#else
261-
LoggingSystem_PushLoggingState(false, true);
262-
#endif
263-
LoggingSystem_RegisterLoggingListener(this);
264-
265-
}
266-
}
267-
} g_LoggingListener;
268-
269-
#endif
270-
271189
//-----------------------------------------------------------------------------
272190
// Purpose: constructor/destructor
273191
//-----------------------------------------------------------------------------
274192
CSourcePython::CSourcePython()
275193
{
276194
m_iClientCommandIndex = 0;
277195
m_pOldMDLCacheNotifier = NULL;
278-
279-
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
280-
m_pOldSpewOutputFunc = NULL;
281-
#endif
282196
}
283197

284198
CSourcePython::~CSourcePython()
@@ -333,22 +247,6 @@ bool CSourcePython::Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn
333247
return false;
334248
}
335249

336-
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
337-
DevMsg(1, MSG_PREFIX "Retrieving old output function...\n");
338-
m_pOldSpewOutputFunc = GetSpewOutputFunc();
339-
340-
DevMsg(1, MSG_PREFIX "Setting new output function...\n");
341-
SpewOutputFunc(SP_SpewOutput);
342-
#else
343-
DevMsg(1, MSG_PREFIX "Registering logging listener...\n");
344-
#if defined(ENGINE_LEFT4DEAD2)
345-
LoggingSystem_PushLoggingState(false);
346-
#else
347-
LoggingSystem_PushLoggingState(false, true);
348-
#endif
349-
LoggingSystem_RegisterLoggingListener(&g_LoggingListener);
350-
#endif
351-
352250
// TODO: Don't hardcode the 64 bytes offset
353251
#ifdef ENGINE_LEFT4DEAD2
354252
#define CACHE_NOTIFY_OFFSET 68
@@ -380,23 +278,15 @@ void CSourcePython::Unload( void )
380278
{
381279
Msg(MSG_PREFIX "Unloading...\n");
382280

383-
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
384-
if (m_pOldSpewOutputFunc)
385-
{
386-
DevMsg(1, MSG_PREFIX "Restoring old output function...\n");
387-
SpewOutputFunc(m_pOldSpewOutputFunc);
388-
}
389-
#else
390-
DevMsg(1, MSG_PREFIX "Restoring old logging state...\n");
391-
LoggingSystem_PopLoggingState(false);
392-
#endif
393-
394281
DevMsg(1, MSG_PREFIX "Resetting cache notifier...\n");
395282
modelcache->SetCacheNotify(m_pOldMDLCacheNotifier);
396283

397284
DevMsg(1, MSG_PREFIX "Shutting down python...\n");
398285
g_PythonManager.Shutdown();
399286

287+
DevMsg(1, MSG_PREFIX "Clearing server output listeners...\n");
288+
GetOnServerOutputListenerManager()->clear();
289+
400290
DevMsg(1, MSG_PREFIX "Unhooking all functions...\n");
401291
GetHookManager()->UnhookAllFunctions();
402292

src/core/sp_main.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,6 @@ class CSourcePython: public IServerPluginCallbacks, public IEntityListener, publ
125125
public:
126126
int m_iClientCommandIndex;
127127
IMDLCacheNotify* m_pOldMDLCacheNotifier;
128-
129-
#if defined(ENGINE_ORANGEBOX) || defined(ENGINE_BMS) || defined(ENGINE_GMOD)
130-
SpewOutputFunc_t m_pOldSpewOutputFunc;
131-
#endif
132128
};
133129

134130

0 commit comments

Comments
 (0)