From 02e34907fc86036228d132526edcd8f847a0650a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordan=20Bri=C3=A8re?= Date: Thu, 7 Oct 2021 01:51:43 -0400 Subject: [PATCH] Fixed ConVar/ServerCommand registration conflicts. Fixed Client/Say/Server commands decorators from raising on unload when no callback was registered. Remove the public/private hack. Fixed server commands from potentially unregistering overrides rather than themselves. Added the ability to override convars as server commands and prevent their assignment from the console. Fixed memory leak for new convars allocated by the interpreter. Fixed execute_server_command, queue_server_command and insert_server_command being executed with an extra empty space. --- .../source-python/commands/command.py | 10 +- src/core/modules/commands/commands.h | 30 +---- src/core/modules/commands/commands_client.cpp | 5 +- src/core/modules/commands/commands_client.h | 2 +- .../modules/commands/commands_client_wrap.cpp | 3 - src/core/modules/commands/commands_say.cpp | 11 +- src/core/modules/commands/commands_say.h | 4 +- .../modules/commands/commands_say_wrap.cpp | 3 - src/core/modules/commands/commands_server.cpp | 32 +++-- src/core/modules/commands/commands_server.h | 6 +- .../modules/commands/commands_server_wrap.cpp | 3 - src/core/modules/commands/commands_wrap.cpp | 11 +- src/core/modules/cvars/cvars.h | 55 +++++++-- src/core/modules/cvars/cvars_wrap.cpp | 9 +- src/core/modules/engines/engines_server.cpp | 7 +- src/core/utilities/convar.h | 111 ++++++++++++++++++ 16 files changed, 209 insertions(+), 93 deletions(-) create mode 100644 src/core/utilities/convar.h diff --git a/addons/source-python/packages/source-python/commands/command.py b/addons/source-python/packages/source-python/commands/command.py index 24c1ad944..cec15f8c6 100644 --- a/addons/source-python/packages/source-python/commands/command.py +++ b/addons/source-python/packages/source-python/commands/command.py @@ -53,4 +53,12 @@ def __call__(self, callback): def _unload_instance(self): """Unregister the commands.""" - self._manager_class.unregister_commands(self.names, self.callback) + # Get the registered callback + callback = self.callback + + # Was no callback registered? + if callback is None: + return + + # Unregister the commands + self._manager_class.unregister_commands(self.names, callback) diff --git a/src/core/modules/commands/commands.h b/src/core/modules/commands/commands.h index 24d1cd936..57aa4d470 100644 --- a/src/core/modules/commands/commands.h +++ b/src/core/modules/commands/commands.h @@ -30,7 +30,7 @@ // Includes. //----------------------------------------------------------------------------- #include "utilities/wrap_macros.h" -#include "convar.h" +#include "utilities/convar.h" #include "utilities/ipythongenerator.h" #include "boost/typeof/typeof.hpp" @@ -99,34 +99,6 @@ class CCommandExt }; -//----------------------------------------------------------------------------- -// ConCommandBase extension class. -//----------------------------------------------------------------------------- -class ConCommandBaseExt -{ -public: - static int GetFlags(ConCommandBase* command) - { - return command->m_nFlags; - } - - static void SetFlags(ConCommandBase* command, int flags) - { - command->m_nFlags = flags; - } - - static void AddFlags(ConCommandBase* command, int flags) - { - command->m_nFlags |= flags; - } - - static void RemoveFlags(ConCommandBase* command, int flags) - { - command->m_nFlags &= ~flags; - } -}; - - //----------------------------------------------------------------------------- // ConCommand extension class. //----------------------------------------------------------------------------- diff --git a/src/core/modules/commands/commands_client.cpp b/src/core/modules/commands/commands_client.cpp index 6a7b75d2f..18bd838fe 100644 --- a/src/core/modules/commands/commands_client.cpp +++ b/src/core/modules/commands/commands_client.cpp @@ -27,14 +27,11 @@ //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "boost/unordered_map.hpp" #include "commands_client.h" #include "commands.h" #include "edict.h" -#include "convar.h" +#include "utilities/convar.h" #include "engine/iserverplugin.h" #include "utilities/call_python.h" #include "boost/python/call.hpp" diff --git a/src/core/modules/commands/commands_client.h b/src/core/modules/commands/commands_client.h index 8048b905d..4e3b4e6c0 100644 --- a/src/core/modules/commands/commands_client.h +++ b/src/core/modules/commands/commands_client.h @@ -56,7 +56,7 @@ class CClientCommandManager const char* GetName(); -private: +public: CListenerManager m_vecCallables; const char* m_Name; }; diff --git a/src/core/modules/commands/commands_client_wrap.cpp b/src/core/modules/commands/commands_client_wrap.cpp index de7481c15..bf3d7018b 100644 --- a/src/core/modules/commands/commands_client_wrap.cpp +++ b/src/core/modules/commands/commands_client_wrap.cpp @@ -27,9 +27,6 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "export_main.h" #include "utilities/wrap_macros.h" #include "modules/memory/memory_tools.h" diff --git a/src/core/modules/commands/commands_say.cpp b/src/core/modules/commands/commands_say.cpp index 9705f9676..d8b58d9a9 100644 --- a/src/core/modules/commands/commands_say.cpp +++ b/src/core/modules/commands/commands_say.cpp @@ -27,9 +27,6 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include #include #include "utilities/call_python.h" @@ -38,7 +35,7 @@ #include "boost/unordered_map.hpp" #include "sp_main.h" #include "modules/listeners/listeners_manager.h" -#include "convar.h" +#include "utilities/convar.h" #include "commands_say.h" #include "commands.h" @@ -158,7 +155,7 @@ SayConCommand* SayConCommand::CreateCommand(const char* szName, const char* szHe { // Store the current command's help text and flags szHelpTextCopy = strdup(pConCommand->GetHelpText()); - iFlags = pConCommand->m_nFlags; + iFlags = GetConCommandFlags(pConCommand); // Unregister the old command g_pCVar->UnregisterConCommand(pConCommand); @@ -192,8 +189,8 @@ SayConCommand::~SayConCommand() // Get the ConCommand instance ConCommand* pConCommand = g_pCVar->FindCommand(m_Name); - // Was the command overwritten as a ConVar or by another DLL? - if (pConCommand && pConCommand->GetDLLIdentifier() == CVarDLLIdentifier()) + // Make sure we only unregister ourselves + if (pConCommand == this) { // Unregister the ConCommand g_pCVar->UnregisterConCommand(pConCommand); diff --git a/src/core/modules/commands/commands_say.h b/src/core/modules/commands/commands_say.h index 0d3b88576..1d8525108 100644 --- a/src/core/modules/commands/commands_say.h +++ b/src/core/modules/commands/commands_say.h @@ -50,7 +50,7 @@ class SayConCommand: public ConCommand protected: void Dispatch( const CCommand& command ); -private: +public: SayConCommand(ConCommand* pConCommand, const char* szName, const char* szHelpText, int iFlags); const char* m_Name; ConCommand* m_pOldCommand; @@ -86,7 +86,7 @@ class CSayCommandManager const char* GetName(); -private: +public: const char* m_Name; CListenerManager m_vecCallables; }; diff --git a/src/core/modules/commands/commands_say_wrap.cpp b/src/core/modules/commands/commands_say_wrap.cpp index b1d7d47e8..70b4ad893 100644 --- a/src/core/modules/commands/commands_say_wrap.cpp +++ b/src/core/modules/commands/commands_say_wrap.cpp @@ -27,9 +27,6 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "export_main.h" #include "utilities/wrap_macros.h" #include "modules/memory/memory_tools.h" diff --git a/src/core/modules/commands/commands_server.cpp b/src/core/modules/commands/commands_server.cpp index 7cff3bf96..c28797681 100644 --- a/src/core/modules/commands/commands_server.cpp +++ b/src/core/modules/commands/commands_server.cpp @@ -27,9 +27,6 @@ //----------------------------------------------------------------------------- // Includes //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "utilities/call_python.h" #include "boost/unordered_map.hpp" @@ -56,8 +53,12 @@ class CPluginConVarAccessor : public IConCommandBaseAccessor public: virtual bool RegisterConCommandBase(ConCommandBase* pCommand) { - g_pCVar->RegisterConCommand(pCommand); - return true; + if (!g_pCVar->FindCommandBase(pCommand->GetName())) { + g_pCVar->RegisterConCommand(pCommand); + return true; + } + + return false; } }; @@ -121,15 +122,17 @@ CServerCommandManager* CServerCommandManager::CreateCommand(const char* szName, char* szHelpTextCopy = NULL; // FInd if the command already exists - ConCommand* pConCommand = g_pCVar->FindCommand(szName); + ConCommandBase* pConCommand = g_pCVar->FindCommandBase(szName); if( pConCommand ) { // Store the current command's help text and flags szHelpTextCopy = strdup(pConCommand->GetHelpText()); - iFlags = pConCommand->m_nFlags; + iFlags = GetConCommandFlags(pConCommand); // Unregister the old command - g_pCVar->UnregisterConCommand(pConCommand); + if (pConCommand->IsRegistered()) { + g_pCVar->UnregisterConCommand(pConCommand); + } } else if( szHelpText != NULL ) { @@ -144,7 +147,7 @@ CServerCommandManager* CServerCommandManager::CreateCommand(const char* szName, //----------------------------------------------------------------------------- // CServerCommandManager constructor. //----------------------------------------------------------------------------- -CServerCommandManager::CServerCommandManager(ConCommand* pConCommand, +CServerCommandManager::CServerCommandManager(ConCommandBase* pConCommand, const char* szName, const char* szHelpText, int iFlags): ConCommand(szName, (FnCommandCallback_t)NULL, szHelpText, iFlags), m_pOldCommand(pConCommand) @@ -163,8 +166,8 @@ CServerCommandManager::~CServerCommandManager() // Get the ConCommand instance ConCommand* pConCommand = g_pCVar->FindCommand(m_Name); - // Was the command overwritten as a ConVar or by another DLL? - if (pConCommand && pConCommand->GetDLLIdentifier() == CVarDLLIdentifier()) + // Make sure we only unregister ourselves + if (pConCommand == this) { // Unregister the ConCommand g_pCVar->UnregisterConCommand(pConCommand); @@ -236,7 +239,12 @@ void CServerCommandManager::Dispatch( const CCommand& command ) // Was the command previously registered? if(m_pOldCommand) { - m_pOldCommand->Dispatch(command); + if (m_pOldCommand->IsCommand()) { + static_cast(m_pOldCommand)->Dispatch(command); + } + else { + static_cast(m_pOldCommand)->SetValue(command.ArgS()); + } } // Post hook callbacks diff --git a/src/core/modules/commands/commands_server.h b/src/core/modules/commands/commands_server.h index d2f8ecc94..7e8c002a2 100644 --- a/src/core/modules/commands/commands_server.h +++ b/src/core/modules/commands/commands_server.h @@ -50,11 +50,11 @@ class CServerCommandManager : public ConCommand protected: void Dispatch( const CCommand& command); -private: - CServerCommandManager(ConCommand* pConCommand, const char* szName, const char* szHelpString = 0, int iFlags = 0); +public: + CServerCommandManager(ConCommandBase* pConCommand, const char* szName, const char* szHelpString = 0, int iFlags = 0); std::map< HookType_t, CListenerManager* > m_vecCallables; const char* m_Name; - ConCommand* m_pOldCommand; + ConCommandBase* m_pOldCommand; }; #endif // _COMMANDS_SERVER_H diff --git a/src/core/modules/commands/commands_server_wrap.cpp b/src/core/modules/commands/commands_server_wrap.cpp index 2a57e1721..e5d62ed46 100644 --- a/src/core/modules/commands/commands_server_wrap.cpp +++ b/src/core/modules/commands/commands_server_wrap.cpp @@ -27,9 +27,6 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "boost/unordered_map.hpp" #include "utilities/wrap_macros.h" #include "commands_server.h" diff --git a/src/core/modules/commands/commands_wrap.cpp b/src/core/modules/commands/commands_wrap.cpp index 94b48dee0..e07045fbd 100644 --- a/src/core/modules/commands/commands_wrap.cpp +++ b/src/core/modules/commands/commands_wrap.cpp @@ -27,9 +27,6 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "utilities/wrap_macros.h" #include "export_main.h" #include "modules/memory/memory_tools.h" @@ -170,20 +167,20 @@ void export_concommandbase(scope _commands) ) .def("add_flags", - &ConCommandBaseExt::AddFlags, + &AddConCommandFlags, "Adds the given flags to the ConVar.", args("flag") ) .def("remove_flags", - &ConCommandBaseExt::RemoveFlags, + &RemoveConCommandFlags, "Removes the given flags from the ConVar.", args("flag") ) .add_property("flags", - &ConCommandBaseExt::GetFlags, - &ConCommandBaseExt::SetFlags, + &GetConCommandFlags, + &SetConCommandFlags, "Returns its flags." ) diff --git a/src/core/modules/cvars/cvars.h b/src/core/modules/cvars/cvars.h index 33af20881..96bd014a4 100644 --- a/src/core/modules/cvars/cvars.h +++ b/src/core/modules/cvars/cvars.h @@ -30,18 +30,37 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -#include "convar.h" +#include "icvar.h" +#include "utilities/convar.h" #include "utilities/sp_util.h" +#include "modules/commands/commands_server.h" //----------------------------------------------------------------------------- -// Returns Source.Python's DLL identifier. +// ICvar shared extension class. //----------------------------------------------------------------------------- -inline CVarDLLIdentifier_t CVarDLLIdentifier() +class ICVarSharedExt { - static CVarDLLIdentifier_t s_nDLLIdentifier = ConCommandBase().GetDLLIdentifier(); - return s_nDLLIdentifier; -} +public: + static ConVar *FindVar(ICvar *pCVar, const char *szName) + { + ConCommandBase *pBase = pCVar->FindCommandBase(szName); + if (pBase) + { + if (pBase->IsCommand()) { + CServerCommandManager *pManager = dynamic_cast(pBase); + if (pManager && pManager->m_pOldCommand && !pManager->m_pOldCommand->IsCommand()) { + return static_cast(pManager->m_pOldCommand); + } + } + else { + return static_cast(pBase); + } + } + + return NULL; + }; +}; //----------------------------------------------------------------------------- @@ -73,18 +92,34 @@ class ConVarExt PyErr_Clear(); } - ConVar *pConVar = g_pCVar->FindVar(name); + ConVar *pConVar = ICVarSharedExt::FindVar(g_pCVar, name); if (!pConVar) { ConVar* pConVar = new ConVar(strdup(name), strdup(value), flags, strdup(description), !min_value.is_none(), fMin, !max_value.is_none(), fMax); - return boost::shared_ptr(pConVar, &NeverDeleteDeleter); + return boost::shared_ptr(pConVar, &Deleter); } return boost::shared_ptr(pConVar, &NeverDeleteDeleter); } + static void Deleter(ConVar *pConVar) + { + ConCommandBase *pBase = g_pCVar->FindCommandBase(pConVar->GetName()); + if (pBase) { + CServerCommandManager *pManager = dynamic_cast(pBase); + if (pManager && pManager->m_pOldCommand == pConVar) { + pManager->m_pOldCommand = NULL; + } + else if (pBase == pConVar) { + g_pCVar->UnregisterConCommand(pConVar); + } + } + + delete pConVar; + } + static bool HasMin(ConVar* pConVar) { float fMin; @@ -118,13 +153,13 @@ class ConVarExt static void MakePublic(ConVar* pConVar) { - pConVar->m_nFlags |= FCVAR_NOTIFY; + AddConCommandFlags(pConVar, FCVAR_NOTIFY); g_pCVar->CallGlobalChangeCallbacks(pConVar, pConVar->GetString(), pConVar->GetFloat()); } static void RemovePublic(ConVar* pConVar) { - pConVar->m_nFlags &= ~FCVAR_NOTIFY; + RemoveConCommandFlags(pConVar, FCVAR_NOTIFY); g_pCVar->CallGlobalChangeCallbacks(pConVar, pConVar->GetString(), pConVar->GetFloat()); } }; diff --git a/src/core/modules/cvars/cvars_wrap.cpp b/src/core/modules/cvars/cvars_wrap.cpp index 30a69016f..ebf244752 100644 --- a/src/core/modules/cvars/cvars_wrap.cpp +++ b/src/core/modules/cvars/cvars_wrap.cpp @@ -27,13 +27,10 @@ //----------------------------------------------------------------------------- // Includes. //----------------------------------------------------------------------------- -// This is required for accessing m_nFlags without patching convar.h -#define private public - #include "export_main.h" #include "modules/memory/memory_tools.h" #include "icvar.h" -#include "convar.h" +#include "utilities/convar.h" #include "cvars.h" #include ENGINE_INCLUDE_PATH(cvars.h) @@ -78,7 +75,7 @@ void export_cvar_interface(scope _cvars) .def("register_base", &ICvar::RegisterConCommand, "Register a console command.", - arg("con_command") + (arg("con_command")=true) ) .def("unregister_base", @@ -106,7 +103,7 @@ void export_cvar_interface(scope _cvars) ) .def("find_var", - GET_METHOD(ConVar*, ICvar, FindVar, const char *), + &ICVarSharedExt::FindVar, "Find the ConVar instance of console variable.\n\n" ":return: Return ``None`` if the console variable was not found.\n" ":rtype: ConVar", diff --git a/src/core/modules/engines/engines_server.cpp b/src/core/modules/engines/engines_server.cpp index 21193d0fd..34363dce0 100644 --- a/src/core/modules/engines/engines_server.cpp +++ b/src/core/modules/engines/engines_server.cpp @@ -69,11 +69,14 @@ void prepare_command(tuple args, dict kwargs, ConCommand** pCommandOut, std::str *pCommandOut = pCommand; szCommandOut->clear(); - for(int i=0; i < len(args); ++i) + + int length = len(args); + for(int i=0; i < length; ++i) { const char* temp = extract(str(args[i])); *szCommandOut += temp; - *szCommandOut += " "; + if ((i + 1) < length) + *szCommandOut += " "; } } diff --git a/src/core/utilities/convar.h b/src/core/utilities/convar.h new file mode 100644 index 000000000..1330cd79d --- /dev/null +++ b/src/core/utilities/convar.h @@ -0,0 +1,111 @@ +/** +* ============================================================================= +* Source Python +* Copyright (C) 2015 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 _UTILITIES_CONVAR_H +#define _UTILITIES_CONVAR_H + +//----------------------------------------------------------------------------- +// Includes. +//----------------------------------------------------------------------------- +#ifndef CONVAR_H + #include "public/tier1/convar.h" +#endif // CONVAR_H + +//----------------------------------------------------------------------------- +// Classes. +//----------------------------------------------------------------------------- +class CCvar : public ConCommandBase +{ +public: + int GetFlags() + { + return m_nFlags; + } + + void SetFlags(int nFlags) + { + m_nFlags = nFlags; + } + + void AddFlags(int nFlags) + { + m_nFlags |= nFlags; + } + + void RemoveFlags(int nFlags) + { + m_nFlags &= ~nFlags; + } +}; + + +//----------------------------------------------------------------------------- +// Returns the flags of a ConVar/ConCommand. +//----------------------------------------------------------------------------- +inline int GetConCommandFlags(ConCommandBase *pBase) +{ + return reinterpret_cast(pBase)->GetFlags(); +} + + +//----------------------------------------------------------------------------- +// Assigns the flags of a ConVar/ConCommand. +//----------------------------------------------------------------------------- +inline void SetConCommandFlags(ConCommandBase *pBase, int nFlags) +{ + reinterpret_cast(pBase)->SetFlags(nFlags); +} + + +//----------------------------------------------------------------------------- +// Adds the given flags to a ConVar/ConCommand. +//----------------------------------------------------------------------------- +inline void AddConCommandFlags(ConCommandBase *pBase, int nFlags) +{ + reinterpret_cast(pBase)->AddFlags(nFlags); +} + + +//----------------------------------------------------------------------------- +// Removes the given flags to a ConVar/ConCommand. +//----------------------------------------------------------------------------- +inline void RemoveConCommandFlags(ConCommandBase *pBase, int nFlags) +{ + reinterpret_cast(pBase)->RemoveFlags(nFlags); +} + + +//----------------------------------------------------------------------------- +// Returns Source.Python's DLL identifier. +//----------------------------------------------------------------------------- +inline CVarDLLIdentifier_t CVarDLLIdentifier() +{ + static CVarDLLIdentifier_t s_nDLLIdentifier = ConCommandBase().GetDLLIdentifier(); + return s_nDLLIdentifier; +} + + +#endif // _UTILITIES_CONVAR_H