Skip to content

Commit 428f49f

Browse files
authored
Added lazy-loading support to ListenerManager. (#307)
* Added lazy-loading support to ListenerManager. Made OnEntityOutput lazy-loaded to fix loading conflict with SourceMod (see #306). * Update __init__.py Fixed a typo (thank @CookStar). Added missing entry to __all__ declaration. * Replaced the static dispatchers with virtual overrides (thanks @Ayuto). Removed redundant attribute retrievals.
1 parent 18d6c56 commit 428f49f

File tree

5 files changed

+78
-6
lines changed

5 files changed

+78
-6
lines changed

addons/source-python/packages/source-python/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,26 +158,25 @@ def setup_data():
158158
'BaseEntityOutput',
159159
GameConfigObj(SP_DATA_PATH / 'entity_output' / 'CBaseEntityOutput.ini'))
160160

161+
from _entities import BaseEntityOutput
161162
try:
162163
_fire_output = entities._BaseEntityOutput.fire_output
163164

164-
from _entities import BaseEntityOutput
165165
BaseEntityOutput.fire_output = _fire_output
166166
except ValueError:
167167
from warnings import warn
168168
warn(
169169
'Did not find address for BaseEntityOutput.fire_output. '
170170
'OnEntityOutput listener will not fire.'
171171
)
172+
BaseEntityOutput.fire_output = NotImplemented
172173
except AttributeError:
173174
from warnings import warn
174175
warn(
175176
'BaseEntityOutput.fire_output not found. '
176177
'OnEntityOutput listener will not fire.'
177178
)
178-
else:
179-
import listeners
180-
_fire_output.add_pre_hook(listeners._pre_fire_output)
179+
BaseEntityOutput.fire_output = NotImplemented
181180

182181

183182
# =============================================================================

addons/source-python/packages/source-python/listeners/__init__.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from cvars import cvar
2525
# Engines
2626
from engines.server import server_game_dll
27+
# Entities
28+
from entities import BaseEntityOutput
2729
from entities.datamaps import Variant
2830
from entities.helpers import find_output_name
2931
# Memory
@@ -91,6 +93,7 @@
9193
'OnEntityCreated',
9294
'OnEntityDeleted',
9395
'OnEntityOutput',
96+
'OnEntityOutputListenerManager',
9497
'OnEntityPreSpawned',
9598
'OnEntitySpawned',
9699
'OnLevelInit',
@@ -157,7 +160,6 @@
157160
on_plugin_loading_manager = ListenerManager()
158161
on_plugin_unloading_manager = ListenerManager()
159162
on_level_end_listener_manager = ListenerManager()
160-
on_entity_output_listener_manager = ListenerManager()
161163

162164
_check_for_update = ConVar(
163165
'sp_check_for_update',
@@ -281,6 +283,36 @@ class OnClientSettingsChanged(ListenerManagerDecorator):
281283
manager = on_client_settings_changed_listener_manager
282284

283285

286+
class OnEntityOutputListenerManager(ListenerManager):
287+
"""Register/unregister an EntityOutput listener."""
288+
289+
def initialize(self):
290+
"""Called when the first callback is being registered."""
291+
# Get the fire_output method
292+
fire_output = BaseEntityOutput.fire_output
293+
294+
# If the fire_output method is not implemented, exit the call
295+
if fire_output is NotImplemented:
296+
return
297+
298+
# Register the hook on fire_output
299+
fire_output.add_pre_hook(_pre_fire_output)
300+
301+
def finalize(self):
302+
"""Called when the last callback is being unregistered."""
303+
# Get the fire_output method
304+
fire_output = BaseEntityOutput.fire_output
305+
306+
# If the fire_output method is not implemented, exit the call
307+
if fire_output is NotImplemented:
308+
return
309+
310+
# Unregister the hook on fire_output
311+
fire_output.remove_pre_hook(_pre_fire_output)
312+
313+
on_entity_output_listener_manager = OnEntityOutputListenerManager()
314+
315+
284316
class OnEntityOutput(ListenerManagerDecorator):
285317
"""Register/unregister an EntityOutput listener."""
286318

src/core/modules/listeners/listeners_manager.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ void CListenerManager::RegisterListener(PyObject* pCallable)
4141
// Is the callable already in the vector?
4242
if( !IsRegistered(oCallable) )
4343
{
44+
if (!GetCount())
45+
Initialize();
46+
4447
m_vecCallables.AddToTail(oCallable);
4548
}
4649
else {
@@ -64,6 +67,9 @@ void CListenerManager::UnregisterListener(PyObject* pCallable)
6467
}
6568
else {
6669
m_vecCallables.Remove(index);
70+
71+
if (!GetCount())
72+
Finalize();
6773
}
6874
}
6975

@@ -92,6 +98,28 @@ int CListenerManager::GetCount()
9298
}
9399

94100

101+
//-----------------------------------------------------------------------------
102+
// Called when the first callback is being registered.
103+
//-----------------------------------------------------------------------------
104+
void CListenerManager::Initialize()
105+
{
106+
override initialize = get_override("initialize");
107+
if (!initialize.is_none())
108+
initialize();
109+
}
110+
111+
112+
//-----------------------------------------------------------------------------
113+
// Called when the last callback is being unregistered.
114+
//-----------------------------------------------------------------------------
115+
void CListenerManager::Finalize()
116+
{
117+
override finalize = get_override("finalize");
118+
if (!finalize.is_none())
119+
finalize();
120+
}
121+
122+
95123
//-----------------------------------------------------------------------------
96124
// Return whether or not the given callback is registered.
97125
//-----------------------------------------------------------------------------

src/core/modules/listeners/listeners_manager.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
//-----------------------------------------------------------------------------
8080
// CListenerManager class.
8181
//-----------------------------------------------------------------------------
82-
class CListenerManager
82+
class CListenerManager: public wrapper<CListenerManager>
8383
{
8484
public:
8585
void RegisterListener(PyObject* pCallable);
@@ -90,6 +90,9 @@ class CListenerManager
9090
object __getitem__(unsigned int index);
9191
void clear();
9292

93+
virtual void Initialize();
94+
virtual void Finalize();
95+
9396
int FindCallback(object oCallback);
9497

9598
public:

src/core/modules/listeners/listeners_wrap.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ void export_listener_managers(scope _listeners)
119119
&CListenerManager::clear,
120120
"Remove all registered callbacks."
121121
)
122+
123+
.def("initialize",
124+
&CListenerManager::Initialize,
125+
"Called when the first callback is being registered."
126+
)
127+
128+
.def("finalize",
129+
&CListenerManager::Finalize,
130+
"Called when the last callback is being unregistered."
131+
)
122132
;
123133

124134
_listeners.attr("on_client_active_listener_manager") = object(ptr(GetOnClientActiveListenerManager()));

0 commit comments

Comments
 (0)