diff --git a/addons/source-python/packages/source-python/listeners/tick.py b/addons/source-python/packages/source-python/listeners/tick.py index 333fef15e..b84eaff82 100644 --- a/addons/source-python/packages/source-python/listeners/tick.py +++ b/addons/source-python/packages/source-python/listeners/tick.py @@ -23,6 +23,14 @@ ) +# ============================================================================= +# >> FORWARD IMPORTS +# ============================================================================= +# Source.Python Imports +# Listeners +from _listeners._tick import ThreadPoker + + # ============================================================================= # >> ALL DECLARATION # ============================================================================= @@ -31,6 +39,7 @@ 'GameThread', 'Repeat', 'RepeatStatus', + 'ThreadPoker', ) @@ -60,6 +69,10 @@ def _unload_instance(self): f'Thread "{self.name}" ({self.ident}) from "{self._caller}" ' f'is running even though its plugin has been unloaded!') + def run(self, *args, **kwargs): + with ThreadPoker(): + super().run(*args, **kwargs) + # ============================================================================= # >> DELAY CLASSES diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 65cd0411f..73c96fcb9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -314,11 +314,14 @@ Set(SOURCEPYTHON_KEYVALUES_MODULE_SOURCES # ------------------------------------------------------------------ Set(SOURCEPYTHON_LISTENERS_MODULE_HEADERS core/modules/listeners/listeners_manager.h + core/modules/listeners/listeners_tick.h ) Set(SOURCEPYTHON_LISTENERS_MODULE_SOURCES core/modules/listeners/listeners_manager.cpp core/modules/listeners/listeners_wrap.cpp + core/modules/listeners/listeners_tick.cpp + core/modules/listeners/listeners_tick_wrap.cpp ) # ------------------------------------------------------------------ diff --git a/src/core/modules/listeners/listeners_tick.cpp b/src/core/modules/listeners/listeners_tick.cpp new file mode 100644 index 000000000..9f345d8e4 --- /dev/null +++ b/src/core/modules/listeners/listeners_tick.cpp @@ -0,0 +1,68 @@ +/** +* ============================================================================= +* Source Python +* Copyright (C) 2012-2023 Source Python Development Team. All rights reserved. +* ============================================================================= +* +* This program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License, version 3.0, as published by the +* Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +* details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see . +* +* As a special exception, the Source Python Team gives you permission +* to link the code of this program (as well as its derivative works) to +* "Half-Life 2," the "Source Engine," and any Game MODs that run on software +* by the Valve Corporation. You must obey the GNU General Public License in +* all respects for all other code used. Additionally, the Source.Python +* Development Team grants this exception to all derivative works. +*/ + +//----------------------------------------------------------------------------- +// Includes. +//----------------------------------------------------------------------------- +#include "listeners_tick.h" + + +//----------------------------------------------------------------------------- +// CThreadPoker initialization. +//----------------------------------------------------------------------------- +unsigned long CThreadPoker::s_nRefCount = 0; + + +//----------------------------------------------------------------------------- +// CThreadPoker class. +//----------------------------------------------------------------------------- +PyObject *CThreadPoker::__enter__(PyObject *pSelf) +{ + ++s_nRefCount; + + Py_INCREF(pSelf); + return pSelf; +} + +void CThreadPoker::__exit__(PyObject *pSelf, PyObject *, PyObject *, PyObject *) +{ + if (!s_nRefCount) { + return; + } + + --s_nRefCount; +} + +void CThreadPoker::Poke() +{ + if (!s_nRefCount) { + return; + } + + PyThreadState *pState = PyEval_SaveThread(); + system(";"); + PyEval_RestoreThread(pState); +} diff --git a/src/core/modules/listeners/listeners_tick.h b/src/core/modules/listeners/listeners_tick.h new file mode 100644 index 000000000..6c704555a --- /dev/null +++ b/src/core/modules/listeners/listeners_tick.h @@ -0,0 +1,52 @@ +/** +* ============================================================================= +* Source Python +* Copyright (C) 2012-2023 Source Python Development Team. All rights reserved. +* ============================================================================= +* +* This program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License, version 3.0, as published by the +* Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +* details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see . +* +* As a special exception, the Source Python Team gives you permission +* to link the code of this program (as well as its derivative works) to +* "Half-Life 2," the "Source Engine," and any Game MODs that run on software +* by the Valve Corporation. You must obey the GNU General Public License in +* all respects for all other code used. Additionally, the Source.Python +* Development Team grants this exception to all derivative works. +*/ + +#ifndef _LISTENERS_TICK_H +#define _LISTENERS_TICK_H + +//----------------------------------------------------------------------------- +// Includes. +//----------------------------------------------------------------------------- +#include "boost/python.hpp" + + +//----------------------------------------------------------------------------- +// CThreadPoker class. +//----------------------------------------------------------------------------- +class CThreadPoker +{ +private: + static unsigned long s_nRefCount; + +public: + static PyObject *__enter__(PyObject *pSelf); + static void __exit__(PyObject *pSelf, PyObject *, PyObject *, PyObject *); + + static void Poke(); +}; + + +#endif // _LISTENERS_TICK_H diff --git a/src/core/modules/listeners/listeners_tick_wrap.cpp b/src/core/modules/listeners/listeners_tick_wrap.cpp new file mode 100644 index 000000000..6e2884750 --- /dev/null +++ b/src/core/modules/listeners/listeners_tick_wrap.cpp @@ -0,0 +1,72 @@ +/** +* ============================================================================= +* Source Python +* Copyright (C) 2012-2023 Source Python Development Team. All rights reserved. +* ============================================================================= +* +* This program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License, version 3.0, as published by the +* Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +* details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see . +* +* As a special exception, the Source Python Team gives you permission +* to link the code of this program (as well as its derivative works) to +* "Half-Life 2," the "Source Engine," and any Game MODs that run on software +* by the Valve Corporation. You must obey the GNU General Public License in +* all respects for all other code used. Additionally, the Source.Python +* Development Team grants this exception to all derivative works. +*/ + +//----------------------------------------------------------------------------- +// Includes. +//----------------------------------------------------------------------------- +#include "export_main.h" +#include "listeners_tick.h" +#include "modules/memory/memory_tools.h" + + +//----------------------------------------------------------------------------- +// Namespaces. +//----------------------------------------------------------------------------- +using namespace boost::python; + + +//----------------------------------------------------------------------------- +// Forward declarations. +//----------------------------------------------------------------------------- +static void export_thread_poker(scope); + + +//----------------------------------------------------------------------------- +// Declare the _listeners._tick module. +//----------------------------------------------------------------------------- +DECLARE_SP_SUBMODULE(_listeners, _tick) +{ + export_thread_poker(_tick); +} + + +//----------------------------------------------------------------------------- +// Exports CThreadPoker. +//----------------------------------------------------------------------------- +void export_thread_poker(scope _tick) +{ + class_ ThreadPoker("ThreadPoker"); + + // Special methods... + ThreadPoker.def("__enter__", &CThreadPoker::__enter__); + ThreadPoker.def("__exit__", &CThreadPoker::__exit__); + + // Methods... + ThreadPoker.def("poke", &CThreadPoker::Poke).staticmethod("poke"); + + // Add memory tools... + ThreadPoker ADD_MEM_TOOLS(CThreadPoker); +} diff --git a/src/core/sp_main.cpp b/src/core/sp_main.cpp index 6f236449a..c6ff50cdb 100755 --- a/src/core/sp_main.cpp +++ b/src/core/sp_main.cpp @@ -58,6 +58,7 @@ #include "manager.h" #include "modules/listeners/listeners_manager.h" +#include "modules/listeners/listeners_tick.h" #include "utilities/conversions.h" #include "modules/entities/entities.h" #include "modules/entities/entities_entity.h" @@ -381,6 +382,9 @@ void CSourcePython::ServerActivate( edict_t *pEdictList, int edictCount, int cli void CSourcePython::GameFrame( bool simulating ) { CALL_LISTENERS(OnTick); + + // Poke active game threads + CThreadPoker::Poke(); } //-----------------------------------------------------------------------------