From 5425520368a9473f8ae1e07863c2787901964dd7 Mon Sep 17 00:00:00 2001 From: Mohab Date: Mon, 28 Jul 2025 18:31:00 +0300 Subject: [PATCH 01/13] Applying FileEx's reviews --- Client/mods/deathmatch/logic/CRegisteredCommands.cpp | 7 +++++++ Server/mods/deathmatch/logic/CRegisteredCommands.cpp | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp index 4faa169071e..6fc5db598d0 100644 --- a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -9,6 +9,7 @@ *****************************************************************************/ #include "StdInc.h" +#include "CClientGame.h" using std::list; @@ -27,6 +28,12 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons assert(pLuaMain); assert(szKey); + if (CommandExists(szKey, NULL)) + { + g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); + return false; + } + // Check if we already have this key and handler SCommand* pCommand = GetCommand(szKey, pLuaMain); if (pCommand) diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp index 50706204feb..92b01a74314 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -18,6 +18,8 @@ #include "CClient.h" #include "CConsoleClient.h" #include "CPlayer.h" +#include "CGame.h" +#include "CScriptDebugging.h" CRegisteredCommands::CRegisteredCommands(CAccessControlListManager* pACLManager) { @@ -35,11 +37,17 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons assert(pLuaMain); assert(szKey); + if (CommandExists(szKey, NULL)) + { + g_pGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); + return false; + } + // Check if we already have this key and handler SCommand* pCommand = GetCommand(szKey, pLuaMain); if (pCommand && iLuaFunction == pCommand->iLuaFunction) - return false; + return false; // Create the entry pCommand = new SCommand; From 2bd4d7e89d2777598793154f0611611615fcc474 Mon Sep 17 00:00:00 2001 From: Mohab Date: Tue, 29 Jul 2025 01:10:48 +0300 Subject: [PATCH 02/13] feat: configurable multi-command handler support with warning levels --- Client/mods/deathmatch/logic/CClientGame.cpp | 11 +++++++++++ Client/mods/deathmatch/logic/CClientGame.h | 11 +++++++++++ Client/mods/deathmatch/logic/CPacketHandler.cpp | 12 ++++++++++++ .../deathmatch/logic/CRegisteredCommands.cpp | 15 +++++++++++++-- Server/mods/deathmatch/logic/CGame.cpp | 3 ++- Server/mods/deathmatch/logic/CMainConfig.cpp | 3 +++ Server/mods/deathmatch/logic/CMainConfig.h | 2 ++ .../deathmatch/logic/CRegisteredCommands.cpp | 16 ++++++++++++++-- .../logic/packets/CSyncSettingsPacket.cpp | 5 ++++- .../logic/packets/CSyncSettingsPacket.h | 3 ++- Server/mods/deathmatch/mtaserver.conf | 5 +++++ 11 files changed, 79 insertions(+), 7 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 9145708156c..822b91caeb0 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -120,6 +120,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_bCursorEventsEnabled = false; m_bInitiallyFadedOut = true; + m_eAllowMultiCommandHandlers = MULTI_COMMAND_ENABLED; m_bIsPlayingBack = false; m_bFirstPlaybackFrame = false; @@ -6137,6 +6138,16 @@ bool CClientGame::GetBirdsEnabled() return m_bBirdsEnabled; } +void CClientGame::SetAllowMultiCommandHandlers(CClientGame::eMultiCommandHandlerPolicy policy) +{ + m_eAllowMultiCommandHandlers = policy; +} + +CClientGame::eMultiCommandHandlerPolicy CClientGame::GetAllowMultiCommandHandlers() const +{ + return m_eAllowMultiCommandHandlers; +} + void CClientGame::SetWeaponRenderEnabled(bool enabled) { g_pGame->SetWeaponRenderEnabled(enabled); diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 97f1a39ba22..ccf9f80514d 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -192,6 +192,12 @@ class CClientGame QUIT_CONNECTION_DESYNC, QUIT_TIMEOUT, }; + + enum eMultiCommandHandlerPolicy + { + MULTI_COMMAND_DISABLED = 0, + MULTI_COMMAND_ENABLED = 1 + }; enum { GLITCH_QUICKRELOAD, @@ -469,6 +475,9 @@ class CClientGame void ReinitMarkers(); void OnWindowFocusChange(bool state); + + void SetAllowMultiCommandHandlers(eMultiCommandHandlerPolicy policy); + eMultiCommandHandlerPolicy GetAllowMultiCommandHandlers() const; private: // CGUI Callbacks @@ -872,6 +881,8 @@ class CClientGame // Key is the task and value is the CClientPed* RunNamedAnimTask_type m_mapOfRunNamedAnimTasks; + eMultiCommandHandlerPolicy m_eAllowMultiCommandHandlers; + long long m_timeLastDiscordStateUpdate; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index f62e9955b4a..5d502c8befc 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -10,6 +10,7 @@ *****************************************************************************/ #include "StdInc.h" +#include "CClientGame.h" #include #include #include @@ -23,6 +24,13 @@ #include "net/SyncStructures.h" #include "CServerInfo.h" + +enum eMultiCommandHandlerPolicy +{ + BLOCK_DUPLICATE_HANDLERS, + ALLOW_MULTI_HANDLERS +}; + using std::list; class CCore; @@ -5567,6 +5575,10 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) if (bitStream.Can(eBitStreamVersion::ShotgunDamageFix)) bitStream.Read(ucAllowShotgunDamageFix); + uchar ucAllowMultiCommandHandlers = 1; + bitStream.Read(ucAllowMultiCommandHandlers); + g_pClientGame->SetAllowMultiCommandHandlers(static_cast(ucAllowMultiCommandHandlers)); + SMiscGameSettings miscGameSettings; miscGameSettings.bUseAltPulseOrder = (ucUseAltPulseOrder != 0); miscGameSettings.bAllowFastSprintFix = (ucAllowFastSprintFix != 0); diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp index 6fc5db598d0..403892c521f 100644 --- a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -30,8 +30,19 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons if (CommandExists(szKey, NULL)) { - g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); - return false; + CClientGame::eMultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers(); + + // If not allowing duplicate handlers, show warning and block + if (allowMultiHandlers == CClientGame::MULTI_COMMAND_DISABLED) + { + g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); + return false; + } + // If allowing with warning (default), log warning and proceed + else if (allowMultiHandlers == CClientGame::MULTI_COMMAND_ENABLED) + { + g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); + } } // Check if we already have this key and handler diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 873a9f71695..e5cd0f201dd 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -4831,9 +4831,10 @@ void CGame::SendSyncSettings(CPlayer* pPlayer) uchar ucAllowFastSprintFix = true; uchar ucAllowDrivebyAnimFix = true; uchar ucAllowShotgunDamageFix = true; + uchar ucAllowMultiCommandHandlers = m_pMainConfig->GetAllowMultiCommandHandlers(); CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs, - ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix); + ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, ucAllowMultiCommandHandlers); if (pPlayer) pPlayer->Send(packet); else diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp index affe0441532..1d906f0c477 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.cpp +++ b/Server/mods/deathmatch/logic/CMainConfig.cpp @@ -81,6 +81,7 @@ CMainConfig::CMainConfig(CConsole* pConsole) : CXMLConfig(NULL) m_bSyncMapElementData = true; m_elementDataWhitelisted = false; m_checkDuplicateSerials = true; + m_iAllowMultiCommandHandlers = 1; } bool CMainConfig::Load() @@ -539,6 +540,8 @@ bool CMainConfig::Load() GetBoolean(m_pRootNode, "elementdata_whitelisted", m_elementDataWhitelisted); GetBoolean(m_pRootNode, "check_duplicate_serials", m_checkDuplicateSerials); + GetInteger(m_pRootNode, "allow_multi_command_handlers", m_iAllowMultiCommandHandlers); + m_iAllowMultiCommandHandlers = Clamp(0, m_iAllowMultiCommandHandlers, 2); ApplyNetOptions(); diff --git a/Server/mods/deathmatch/logic/CMainConfig.h b/Server/mods/deathmatch/logic/CMainConfig.h index ffb0b8de8dd..f93ef4cc1d3 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.h +++ b/Server/mods/deathmatch/logic/CMainConfig.h @@ -150,6 +150,7 @@ class CMainConfig : public CXMLConfig int GetPlayerTriggeredEventInterval() const { return m_iPlayerTriggeredEventIntervalMs; } int GetMaxPlayerTriggeredEventsPerInterval() const { return m_iMaxPlayerTriggeredEventsPerInterval; } + int GetAllowMultiCommandHandlers() const { return m_iAllowMultiCommandHandlers; } private: void RegisterCommand(const char* szName, FCommandHandler* pFunction, bool bRestricted, const char* szConsoleHelpText); @@ -235,4 +236,5 @@ class CMainConfig : public CXMLConfig bool m_elementDataWhitelisted; bool m_checkDuplicateSerials; int m_checkResourceClientFiles; + int m_iAllowMultiCommandHandlers; }; diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp index 92b01a74314..0caf95156f6 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -20,6 +20,7 @@ #include "CPlayer.h" #include "CGame.h" #include "CScriptDebugging.h" +#include "CMainConfig.h" CRegisteredCommands::CRegisteredCommands(CAccessControlListManager* pACLManager) { @@ -39,8 +40,19 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons if (CommandExists(szKey, NULL)) { - g_pGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); - return false; + int iAllowMultiCommandHandlers = g_pGame->GetConfig()->GetAllowMultiCommandHandlers(); + + if (iAllowMultiCommandHandlers == 0) + { + // Block silently + return false; + } + else if (iAllowMultiCommandHandlers == 1) + { + // Allow with warning (default behavior) + g_pGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "Attempt to register duplicate command '%s'", szKey); + } + // For iAllowMultiCommandHandlers == 2, allow silently (no action needed) } // Check if we already have this key and handler diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp index 5c1ea2be6c1..0229421cba7 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp @@ -15,7 +15,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix) + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar ucAllowMultiCommandHandlers) { m_weaponTypesUsingBulletSync = weaponTypesUsingBulletSync; m_ucVehExtrapolateEnabled = ucVehExtrapolateEnabled; @@ -26,6 +26,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponType m_ucAllowFastSprintFix = ucAllowFastSprintFix; m_ucAllowDrivebyAnimationFix = ucAllowDrivebyAnimationFix; m_ucAllowShotgunDamageFix = ucAllowShotgunDamageFix; + m_ucAllowMultiCommandHandlers = ucAllowMultiCommandHandlers; } bool CSyncSettingsPacket::Read(NetBitStreamInterface& BitStream) @@ -71,5 +72,7 @@ bool CSyncSettingsPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(m_ucAllowShotgunDamageFix); } + BitStream.Write(m_ucAllowMultiCommandHandlers); + return true; } diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h index b4c22df18f1..6f11c1bb7ab 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h @@ -19,7 +19,7 @@ class CSyncSettingsPacket final : public CPacket CSyncSettingsPacket(){}; CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix); + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar ucAllowMultiCommandHandlers); ePacketID GetPacketID() const { return PACKET_ID_SYNC_SETTINGS; }; unsigned long GetFlags() const { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; }; @@ -36,4 +36,5 @@ class CSyncSettingsPacket final : public CPacket uchar m_ucAllowFastSprintFix; uchar m_ucAllowDrivebyAnimationFix; uchar m_ucAllowShotgunDamageFix; + uchar m_ucAllowMultiCommandHandlers; }; diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf index 2dd6960ae0b..21b736a6bb1 100644 --- a/Server/mods/deathmatch/mtaserver.conf +++ b/Server/mods/deathmatch/mtaserver.conf @@ -94,6 +94,11 @@ Available values: special detection (SD) codes ; (e.g. To enable special detection #15 use: 15) --> + + 1 + + + 1 + 1 + diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index 3c45f47ad64..27dbb71fa38 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -628,6 +628,10 @@ enum class eBitStreamVersion : unsigned short // 2025-06-05 Glitch_VehicleRapidStop, + // Add support for multiple command handlers configuration + // 2025-07-30 + MultiCommandHandlers, + // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next, From 5aeb9a14b3b034206ab0ec5d80bdc9b7fdcc06e9 Mon Sep 17 00:00:00 2001 From: Mohab Date: Thu, 31 Jul 2025 03:11:35 +0300 Subject: [PATCH 04/13] Refactor MultiCommandHandler logic per review: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Removed Hungarian notation • Switched to enum class • Marked funcs noexcept, moved to header • Used nullptr instead of NULL • Added BitStream.Can check • Minor cleanup: braces, naming • Updated config templates • throwing error when blocking duplicate command handlers instead of a warning --- Client/mods/deathmatch/logic/CClientGame.cpp | 11 +--------- Client/mods/deathmatch/logic/CClientGame.h | 12 +++++------ .../mods/deathmatch/logic/CPacketHandler.cpp | 12 ++++------- .../deathmatch/logic/CRegisteredCommands.cpp | 20 +++++++++---------- Server/mods/deathmatch/logic/CGame.cpp | 4 ++-- Server/mods/deathmatch/logic/CMainConfig.cpp | 6 +++--- Server/mods/deathmatch/logic/CMainConfig.h | 4 ++-- .../deathmatch/logic/CRegisteredCommands.cpp | 20 +++++++++---------- .../logic/packets/CSyncSettingsPacket.cpp | 7 ++++--- .../logic/packets/CSyncSettingsPacket.h | 4 ++-- .../mods/deathmatch/mtaserver.conf.template | 6 ++++++ Shared/sdk/net/bitstream.h | 4 ++++ 12 files changed, 52 insertions(+), 58 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 822b91caeb0..d175366f085 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -120,7 +120,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_bCursorEventsEnabled = false; m_bInitiallyFadedOut = true; - m_eAllowMultiCommandHandlers = MULTI_COMMAND_ENABLED; + m_allowMultiCommandHandlers = MultiCommandHandlerPolicy::ENABLED; m_bIsPlayingBack = false; m_bFirstPlaybackFrame = false; @@ -6138,15 +6138,6 @@ bool CClientGame::GetBirdsEnabled() return m_bBirdsEnabled; } -void CClientGame::SetAllowMultiCommandHandlers(CClientGame::eMultiCommandHandlerPolicy policy) -{ - m_eAllowMultiCommandHandlers = policy; -} - -CClientGame::eMultiCommandHandlerPolicy CClientGame::GetAllowMultiCommandHandlers() const -{ - return m_eAllowMultiCommandHandlers; -} void CClientGame::SetWeaponRenderEnabled(bool enabled) { diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index ccf9f80514d..6c6c5d21642 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -193,10 +193,10 @@ class CClientGame QUIT_TIMEOUT, }; - enum eMultiCommandHandlerPolicy + enum class MultiCommandHandlerPolicy { - MULTI_COMMAND_DISABLED = 0, - MULTI_COMMAND_ENABLED = 1 + DISABLED = 0, + ENABLED = 1 }; enum { @@ -476,8 +476,8 @@ class CClientGame void OnWindowFocusChange(bool state); - void SetAllowMultiCommandHandlers(eMultiCommandHandlerPolicy policy); - eMultiCommandHandlerPolicy GetAllowMultiCommandHandlers() const; + void SetAllowMultiCommandHandlers(MultiCommandHandlerPolicy policy) noexcept { m_allowMultiCommandHandlers = policy; } + MultiCommandHandlerPolicy GetAllowMultiCommandHandlers() const noexcept { return m_allowMultiCommandHandlers; } private: // CGUI Callbacks @@ -881,7 +881,7 @@ class CClientGame // Key is the task and value is the CClientPed* RunNamedAnimTask_type m_mapOfRunNamedAnimTasks; - eMultiCommandHandlerPolicy m_eAllowMultiCommandHandlers; + MultiCommandHandlerPolicy m_allowMultiCommandHandlers; long long m_timeLastDiscordStateUpdate; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 5d502c8befc..ee485aacb3d 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -25,11 +25,6 @@ #include "CServerInfo.h" -enum eMultiCommandHandlerPolicy -{ - BLOCK_DUPLICATE_HANDLERS, - ALLOW_MULTI_HANDLERS -}; using std::list; @@ -5575,9 +5570,10 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) if (bitStream.Can(eBitStreamVersion::ShotgunDamageFix)) bitStream.Read(ucAllowShotgunDamageFix); - uchar ucAllowMultiCommandHandlers = 1; - bitStream.Read(ucAllowMultiCommandHandlers); - g_pClientGame->SetAllowMultiCommandHandlers(static_cast(ucAllowMultiCommandHandlers)); + uchar allowMultiCommandHandlers = 1; + if (bitStream.Can(eBitStreamVersion::MultiCommandHandlers)) + bitStream.Read(allowMultiCommandHandlers); + g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); SMiscGameSettings miscGameSettings; miscGameSettings.bUseAltPulseOrder = (ucUseAltPulseOrder != 0); diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp index 403892c521f..3bd96f69eeb 100644 --- a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -28,21 +28,19 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons assert(pLuaMain); assert(szKey); - if (CommandExists(szKey, NULL)) + if (CommandExists(szKey, nullptr)) { - CClientGame::eMultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers(); + CClientGame::MultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers(); - // If not allowing duplicate handlers, show warning and block - if (allowMultiHandlers == CClientGame::MULTI_COMMAND_DISABLED) + // If not allowing duplicate handlers, throw error and block + if (allowMultiHandlers == CClientGame::MultiCommandHandlerPolicy::DISABLED) { - g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); + g_pClientGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); return false; } // If allowing with warning (default), log warning and proceed - else if (allowMultiHandlers == CClientGame::MULTI_COMMAND_ENABLED) - { + else if (allowMultiHandlers == CClientGame::MultiCommandHandlerPolicy::ENABLED) g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); - } } // Check if we already have this key and handler @@ -144,7 +142,7 @@ bool CRegisteredCommands::CommandExists(const char* szKey, CLuaMain* pLuaMain) { assert(szKey); - return GetCommand(szKey, pLuaMain) != NULL; + return GetCommand(szKey, pLuaMain) != nullptr; } bool CRegisteredCommands::ProcessCommand(const char* szKey, const char* szArguments) @@ -201,7 +199,7 @@ CRegisteredCommands::SCommand* CRegisteredCommands::GetCommand(const char* szKey } // Doesn't exist - return NULL; + return nullptr; } void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunctionRef& iLuaFunction, const char* szKey, const char* szArguments) @@ -222,7 +220,7 @@ void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunct while (arg) { Arguments.PushString(arg); - arg = strtok(NULL, " "); + arg = strtok(nullptr, " "); } delete[] szTempArguments; } diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index e5cd0f201dd..242d3d06d90 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -4831,10 +4831,10 @@ void CGame::SendSyncSettings(CPlayer* pPlayer) uchar ucAllowFastSprintFix = true; uchar ucAllowDrivebyAnimFix = true; uchar ucAllowShotgunDamageFix = true; - uchar ucAllowMultiCommandHandlers = m_pMainConfig->GetAllowMultiCommandHandlers(); + uchar allowMultiCommandHandlers = m_pMainConfig->GetAllowMultiCommandHandlers(); CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs, - ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, ucAllowMultiCommandHandlers); + ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, allowMultiCommandHandlers); if (pPlayer) pPlayer->Send(packet); else diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp index 1d906f0c477..01311931747 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.cpp +++ b/Server/mods/deathmatch/logic/CMainConfig.cpp @@ -81,7 +81,7 @@ CMainConfig::CMainConfig(CConsole* pConsole) : CXMLConfig(NULL) m_bSyncMapElementData = true; m_elementDataWhitelisted = false; m_checkDuplicateSerials = true; - m_iAllowMultiCommandHandlers = 1; + m_allowMultiCommandHandlers = 1; } bool CMainConfig::Load() @@ -540,8 +540,8 @@ bool CMainConfig::Load() GetBoolean(m_pRootNode, "elementdata_whitelisted", m_elementDataWhitelisted); GetBoolean(m_pRootNode, "check_duplicate_serials", m_checkDuplicateSerials); - GetInteger(m_pRootNode, "allow_multi_command_handlers", m_iAllowMultiCommandHandlers); - m_iAllowMultiCommandHandlers = Clamp(0, m_iAllowMultiCommandHandlers, 2); + GetInteger(m_pRootNode, "allow_multi_command_handlers", m_allowMultiCommandHandlers); + m_allowMultiCommandHandlers = Clamp(0, m_allowMultiCommandHandlers, 2); ApplyNetOptions(); diff --git a/Server/mods/deathmatch/logic/CMainConfig.h b/Server/mods/deathmatch/logic/CMainConfig.h index f93ef4cc1d3..ea280c2f175 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.h +++ b/Server/mods/deathmatch/logic/CMainConfig.h @@ -150,7 +150,7 @@ class CMainConfig : public CXMLConfig int GetPlayerTriggeredEventInterval() const { return m_iPlayerTriggeredEventIntervalMs; } int GetMaxPlayerTriggeredEventsPerInterval() const { return m_iMaxPlayerTriggeredEventsPerInterval; } - int GetAllowMultiCommandHandlers() const { return m_iAllowMultiCommandHandlers; } + int GetAllowMultiCommandHandlers() const noexcept { return m_allowMultiCommandHandlers; } private: void RegisterCommand(const char* szName, FCommandHandler* pFunction, bool bRestricted, const char* szConsoleHelpText); @@ -236,5 +236,5 @@ class CMainConfig : public CXMLConfig bool m_elementDataWhitelisted; bool m_checkDuplicateSerials; int m_checkResourceClientFiles; - int m_iAllowMultiCommandHandlers; + int m_allowMultiCommandHandlers; }; diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp index 0caf95156f6..3f9769f2c86 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -38,21 +38,19 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons assert(pLuaMain); assert(szKey); - if (CommandExists(szKey, NULL)) + if (CommandExists(szKey, nullptr)) { - int iAllowMultiCommandHandlers = g_pGame->GetConfig()->GetAllowMultiCommandHandlers(); + int allowMultiCommandHandlers = g_pGame->GetConfig()->GetAllowMultiCommandHandlers(); - if (iAllowMultiCommandHandlers == 0) + if (allowMultiCommandHandlers == 0) { - // Block silently + g_pGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); return false; } - else if (iAllowMultiCommandHandlers == 1) - { + else if (allowMultiCommandHandlers == 1) // Allow with warning (default behavior) g_pGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "Attempt to register duplicate command '%s'", szKey); - } - // For iAllowMultiCommandHandlers == 2, allow silently (no action needed) + // For allowMultiCommandHandlers == 2, allow silently (no action needed) } // Check if we already have this key and handler @@ -159,7 +157,7 @@ bool CRegisteredCommands::CommandExists(const char* szKey, CLuaMain* pLuaMain) { assert(szKey); - return GetCommand(szKey, pLuaMain) != NULL; + return GetCommand(szKey, pLuaMain) != nullptr; } bool CRegisteredCommands::ProcessCommand(const char* szKey, const char* szArguments, CClient* pClient) @@ -223,7 +221,7 @@ CRegisteredCommands::SCommand* CRegisteredCommands::GetCommand(const char* szKey } // Doesn't exist - return NULL; + return nullptr; } void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunctionRef& iLuaFunction, const char* szKey, const char* szArguments, @@ -273,7 +271,7 @@ void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunct while (arg) { Arguments.PushString(arg); - arg = strtok(NULL, " "); + arg = strtok(nullptr, " "); } delete[] szTempArguments; diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp index 0229421cba7..9250b9d925a 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp @@ -15,7 +15,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar ucAllowMultiCommandHandlers) + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar allowMultiCommandHandlers) { m_weaponTypesUsingBulletSync = weaponTypesUsingBulletSync; m_ucVehExtrapolateEnabled = ucVehExtrapolateEnabled; @@ -26,7 +26,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponType m_ucAllowFastSprintFix = ucAllowFastSprintFix; m_ucAllowDrivebyAnimationFix = ucAllowDrivebyAnimationFix; m_ucAllowShotgunDamageFix = ucAllowShotgunDamageFix; - m_ucAllowMultiCommandHandlers = ucAllowMultiCommandHandlers; + m_allowMultiCommandHandlers = allowMultiCommandHandlers; } bool CSyncSettingsPacket::Read(NetBitStreamInterface& BitStream) @@ -72,7 +72,8 @@ bool CSyncSettingsPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(m_ucAllowShotgunDamageFix); } - BitStream.Write(m_ucAllowMultiCommandHandlers); + if (BitStream.Can(eBitStreamVersion::MultiCommandHandlers)) + BitStream.Write(m_allowMultiCommandHandlers); return true; } diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h index 6f11c1bb7ab..66029330c90 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h @@ -19,7 +19,7 @@ class CSyncSettingsPacket final : public CPacket CSyncSettingsPacket(){}; CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar ucAllowMultiCommandHandlers); + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar allowMultiCommandHandlers); ePacketID GetPacketID() const { return PACKET_ID_SYNC_SETTINGS; }; unsigned long GetFlags() const { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; }; @@ -36,5 +36,5 @@ class CSyncSettingsPacket final : public CPacket uchar m_ucAllowFastSprintFix; uchar m_ucAllowDrivebyAnimationFix; uchar m_ucAllowShotgunDamageFix; - uchar m_ucAllowMultiCommandHandlers; + uchar m_allowMultiCommandHandlers; }; diff --git a/Server/mods/deathmatch/mtaserver.conf.template b/Server/mods/deathmatch/mtaserver.conf.template index 35cab835032..6eb5ab679d7 100644 --- a/Server/mods/deathmatch/mtaserver.conf.template +++ b/Server/mods/deathmatch/mtaserver.conf.template @@ -95,6 +95,11 @@ Available values: special detection (SD) codes ; (e.g. To enable special detection #15 use: 15) --> + + 1 + 1 + diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index 3c45f47ad64..27dbb71fa38 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -628,6 +628,10 @@ enum class eBitStreamVersion : unsigned short // 2025-06-05 Glitch_VehicleRapidStop, + // Add support for multiple command handlers configuration + // 2025-07-30 + MultiCommandHandlers, + // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next, From 7478096e80bf9127c39ea1112027671e3cb77b10 Mon Sep 17 00:00:00 2001 From: Mohab Date: Mon, 4 Aug 2025 14:46:05 +0300 Subject: [PATCH 05/13] Fix bitstream compilation errors after upstream version update --- .../logic/packets/CSyncSettingsPacket.cpp | 39 +++++-------------- Shared/sdk/net/bitstream.h | 10 +---- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp index 9250b9d925a..2ba5ab0ca79 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp @@ -44,36 +44,15 @@ bool CSyncSettingsPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write((uchar)*iter); } - if (BitStream.Version() >= 0x35) - { - BitStream.Write(m_ucVehExtrapolateEnabled); - BitStream.Write(m_sVehExtrapolateBaseMs); - BitStream.Write(m_sVehExtrapolatePercent); - BitStream.Write(m_sVehExtrapolateMaxMs); - } - - if (BitStream.Version() >= 0x3D) - { - BitStream.Write(m_ucUseAltPulseOrder); - } - - if (BitStream.Version() >= 0x58) - { - BitStream.Write(m_ucAllowFastSprintFix); - } - - if (BitStream.Version() >= 0x59) - { - BitStream.Write(m_ucAllowDrivebyAnimationFix); - } - - if (BitStream.Can(eBitStreamVersion::ShotgunDamageFix)) - { - BitStream.Write(m_ucAllowShotgunDamageFix); - } - - if (BitStream.Can(eBitStreamVersion::MultiCommandHandlers)) - BitStream.Write(m_allowMultiCommandHandlers); + BitStream.Write(m_ucVehExtrapolateEnabled); + BitStream.Write(m_sVehExtrapolateBaseMs); + BitStream.Write(m_sVehExtrapolatePercent); + BitStream.Write(m_sVehExtrapolateMaxMs); + BitStream.Write(m_ucUseAltPulseOrder); + BitStream.Write(m_ucAllowFastSprintFix); + BitStream.Write(m_ucAllowDrivebyAnimationFix); + BitStream.Write(m_ucAllowShotgunDamageFix); + BitStream.Write(m_allowMultiCommandHandlers); return true; } diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index d84dd57c0b5..a22bc574417 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -405,14 +405,6 @@ enum class eBitStreamVersion : unsigned short // YYYY-MM-DD // Name, - // Add support for multiple command handlers configuration - // 2025-07-30 - MultiCommandHandlers, - - // Add support for multiple command handlers configuration - // 2025-07-30 - MultiCommandHandlers, - // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next, @@ -441,4 +433,4 @@ struct ISyncStructure virtual ~ISyncStructure() {} virtual bool Read(NetBitStreamInterface& bitStream) = 0; virtual void Write(NetBitStreamInterface& bitStream) const = 0; -}; +}; \ No newline at end of file From 2b73cd5cb83fe4d98433e660d2d6e903e2e0347f Mon Sep 17 00:00:00 2001 From: Mohab Date: Mon, 4 Aug 2025 14:56:34 +0300 Subject: [PATCH 06/13] Update CPacketHandler.cpp to handle MultiCommandHandlers without version checks --- Client/mods/deathmatch/logic/CPacketHandler.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 75eab474842..9d75aa6b2bd 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -10,7 +10,6 @@ *****************************************************************************/ #include "StdInc.h" -#include "CClientGame.h" #include #include #include @@ -24,8 +23,6 @@ #include "net/SyncStructures.h" #include "CServerInfo.h" - - using std::list; class CCore; @@ -5484,9 +5481,8 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) uchar ucAllowShotgunDamageFix = 0; bitStream.Read(ucAllowShotgunDamageFix); - uchar allowMultiCommandHandlers = 1; - if (bitStream.Can(eBitStreamVersion::MultiCommandHandlers)) - bitStream.Read(allowMultiCommandHandlers); + uchar allowMultiCommandHandlers = 1; // default to enabled + bitStream.Read(allowMultiCommandHandlers); g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); SMiscGameSettings miscGameSettings; From a8fde77c605c024a7c2db0de93d89a26182ab810 Mon Sep 17 00:00:00 2001 From: Mohab Date: Mon, 4 Aug 2025 15:29:59 +0300 Subject: [PATCH 07/13] Update CPacketHandler.cpp to handle MultiCommandHandlers without version checks --- Client/mods/deathmatch/logic/CPacketHandler.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 75eab474842..9d75aa6b2bd 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -10,7 +10,6 @@ *****************************************************************************/ #include "StdInc.h" -#include "CClientGame.h" #include #include #include @@ -24,8 +23,6 @@ #include "net/SyncStructures.h" #include "CServerInfo.h" - - using std::list; class CCore; @@ -5484,9 +5481,8 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) uchar ucAllowShotgunDamageFix = 0; bitStream.Read(ucAllowShotgunDamageFix); - uchar allowMultiCommandHandlers = 1; - if (bitStream.Can(eBitStreamVersion::MultiCommandHandlers)) - bitStream.Read(allowMultiCommandHandlers); + uchar allowMultiCommandHandlers = 1; // default to enabled + bitStream.Read(allowMultiCommandHandlers); g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); SMiscGameSettings miscGameSettings; From 0d874082a856d0292d5a51c3a929806a46b97824 Mon Sep 17 00:00:00 2001 From: Mohab Date: Mon, 4 Aug 2025 15:30:33 +0300 Subject: [PATCH 08/13] update --- Client/mods/deathmatch/logic/CPacketHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 9d75aa6b2bd..1d9ac51b96d 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -5481,7 +5481,7 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) uchar ucAllowShotgunDamageFix = 0; bitStream.Read(ucAllowShotgunDamageFix); - uchar allowMultiCommandHandlers = 1; // default to enabled + uchar allowMultiCommandHandlers = 1; bitStream.Read(allowMultiCommandHandlers); g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); From ddab1b8f4c31f94ce6ccffc29e36f35ef11da2b8 Mon Sep 17 00:00:00 2001 From: Mohab <133429578+MohabCodeX@users.noreply.github.com> Date: Thu, 14 Aug 2025 20:43:32 +0300 Subject: [PATCH 09/13] Refactor code, update related logic and settings. --- Client/mods/deathmatch/logic/CClientGame.cpp | 2 +- Client/mods/deathmatch/logic/CClientGame.h | 5 ---- .../mods/deathmatch/logic/CPacketHandler.cpp | 2 +- .../deathmatch/logic/CRegisteredCommands.cpp | 23 ++++++++++------- .../deathmatch/logic/CRegisteredCommands.h | 8 ++++++ Server/mods/deathmatch/logic/CGame.cpp | 4 +-- Server/mods/deathmatch/logic/CMainConfig.cpp | 6 +++++ Server/mods/deathmatch/logic/CMainConfig.h | 1 + .../deathmatch/logic/CRegisteredCommands.cpp | 25 +++++++++++-------- .../deathmatch/logic/CRegisteredCommands.h | 8 ++++++ .../logic/packets/CSyncSettingsPacket.cpp | 6 ++--- .../logic/packets/CSyncSettingsPacket.h | 4 +-- 12 files changed, 61 insertions(+), 33 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 5e095df1a13..8203fc9f3a5 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -120,7 +120,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_bCursorEventsEnabled = false; m_bInitiallyFadedOut = true; - m_allowMultiCommandHandlers = MultiCommandHandlerPolicy::ENABLED; + m_allowMultiCommandHandlers = MultiCommandHandlerPolicy::WARN; m_bIsPlayingBack = false; m_bFirstPlaybackFrame = false; diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index cd12bd3d48a..84c10099171 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -194,11 +194,6 @@ class CClientGame QUIT_TIMEOUT, }; - enum class MultiCommandHandlerPolicy - { - DISABLED = 0, - ENABLED = 1 - }; enum { GLITCH_QUICKRELOAD, diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 1d9ac51b96d..52db37a3e0e 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -5483,7 +5483,7 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) uchar allowMultiCommandHandlers = 1; bitStream.Read(allowMultiCommandHandlers); - g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); + g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); SMiscGameSettings miscGameSettings; miscGameSettings.bUseAltPulseOrder = (ucUseAltPulseOrder != 0); diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp index 3bd96f69eeb..a990ef43f90 100644 --- a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -30,21 +30,26 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons if (CommandExists(szKey, nullptr)) { - CClientGame::MultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers(); + const MultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers(); - // If not allowing duplicate handlers, throw error and block - if (allowMultiHandlers == CClientGame::MultiCommandHandlerPolicy::DISABLED) + switch (allowMultiHandlers) { - g_pClientGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); - return false; + case MultiCommandHandlerPolicy::BLOCK: + g_pClientGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); + return false; + + case MultiCommandHandlerPolicy::WARN: + g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); + break; + + case MultiCommandHandlerPolicy::ALLOW: + default: + break; } - // If allowing with warning (default), log warning and proceed - else if (allowMultiHandlers == CClientGame::MultiCommandHandlerPolicy::ENABLED) - g_pClientGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "addCommandHandler: Attempt to register duplicate command '%s'", szKey); } // Check if we already have this key and handler - SCommand* pCommand = GetCommand(szKey, pLuaMain); + auto* pCommand = GetCommand(szKey, pLuaMain); if (pCommand) { if (pCommand->iLuaFunction == iLuaFunction) diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.h b/Client/mods/deathmatch/logic/CRegisteredCommands.h index 5583e8d1d16..1fe63e25cfb 100644 --- a/Client/mods/deathmatch/logic/CRegisteredCommands.h +++ b/Client/mods/deathmatch/logic/CRegisteredCommands.h @@ -10,12 +10,20 @@ #pragma once +#include #include #include #define MAX_REGISTERED_COMMAND_LENGTH 64 #define MAX_REGISTERED_COMMANDHANDLER_LENGTH 64 +enum class MultiCommandHandlerPolicy : std::uint8_t +{ + BLOCK = 0, + WARN = 1, + ALLOW = 2 +}; + class CRegisteredCommands { struct SCommand diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 142154dfe47..7832a91999b 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -4701,10 +4701,10 @@ void CGame::SendSyncSettings(CPlayer* pPlayer) uchar ucAllowFastSprintFix = true; uchar ucAllowDrivebyAnimFix = true; uchar ucAllowShotgunDamageFix = true; - uchar allowMultiCommandHandlers = m_pMainConfig->GetAllowMultiCommandHandlers(); + const std::uint8_t multiCommandHandlerPolicy = static_cast(m_pMainConfig->GetAllowMultiCommandHandlers()); CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs, - ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, allowMultiCommandHandlers); + ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, multiCommandHandlerPolicy); if (pPlayer) pPlayer->Send(packet); else diff --git a/Server/mods/deathmatch/logic/CMainConfig.cpp b/Server/mods/deathmatch/logic/CMainConfig.cpp index 01311931747..e93ffdd87f1 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.cpp +++ b/Server/mods/deathmatch/logic/CMainConfig.cpp @@ -1534,6 +1534,7 @@ const std::vector& CMainConfig::GetIntSettingList() {true, true, 50, 1000, 5000, "player_triggered_event_interval", &m_iPlayerTriggeredEventIntervalMs, &CMainConfig::OnPlayerTriggeredEventIntervalChange}, {true, true, 1, 100, 1000, "max_player_triggered_events_per_interval", &m_iMaxPlayerTriggeredEventsPerInterval, &CMainConfig::OnPlayerTriggeredEventIntervalChange}, {true, true, 0, 1, 1, "resource_client_file_checks", &m_checkResourceClientFiles, nullptr}, + {true, true, 0, 1, 2, "allow_multi_command_handlers", &m_allowMultiCommandHandlers, &CMainConfig::OnAllowMultiCommandHandlersChange}, }; static std::vector settingsList; @@ -1583,6 +1584,11 @@ void CMainConfig::OnPlayerTriggeredEventIntervalChange() g_pGame->ApplyPlayerTriggeredEventIntervalChange(); } +void CMainConfig::OnAllowMultiCommandHandlersChange() +{ + g_pGame->SendSyncSettings(); +} + void CGame::ApplyPlayerTriggeredEventIntervalChange() { m_iClientTriggeredEventsIntervalMs = m_pMainConfig->GetPlayerTriggeredEventInterval(); diff --git a/Server/mods/deathmatch/logic/CMainConfig.h b/Server/mods/deathmatch/logic/CMainConfig.h index ea280c2f175..cec5ea61714 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.h +++ b/Server/mods/deathmatch/logic/CMainConfig.h @@ -147,6 +147,7 @@ class CMainConfig : public CXMLConfig void OnTickRateChange(); void OnAseSettingChange(); void OnPlayerTriggeredEventIntervalChange(); + void OnAllowMultiCommandHandlersChange(); int GetPlayerTriggeredEventInterval() const { return m_iPlayerTriggeredEventIntervalMs; } int GetMaxPlayerTriggeredEventsPerInterval() const { return m_iMaxPlayerTriggeredEventsPerInterval; } diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp index 3f9769f2c86..ba584e22820 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -40,24 +40,29 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons if (CommandExists(szKey, nullptr)) { - int allowMultiCommandHandlers = g_pGame->GetConfig()->GetAllowMultiCommandHandlers(); - - if (allowMultiCommandHandlers == 0) + const auto policy = static_cast(g_pGame->GetConfig()->GetAllowMultiCommandHandlers()); + + switch (policy) { - g_pGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); - return false; + case MultiCommandHandlerPolicy::BLOCK: + g_pGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey); + return false; + + case MultiCommandHandlerPolicy::WARN: + g_pGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "Attempt to register duplicate command '%s'", szKey); + break; + + case MultiCommandHandlerPolicy::ALLOW: + default: + break; } - else if (allowMultiCommandHandlers == 1) - // Allow with warning (default behavior) - g_pGame->GetScriptDebugging()->LogWarning(pLuaMain->GetVM(), "Attempt to register duplicate command '%s'", szKey); - // For allowMultiCommandHandlers == 2, allow silently (no action needed) } // Check if we already have this key and handler SCommand* pCommand = GetCommand(szKey, pLuaMain); if (pCommand && iLuaFunction == pCommand->iLuaFunction) - return false; + return false; // Create the entry pCommand = new SCommand; diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.h b/Server/mods/deathmatch/logic/CRegisteredCommands.h index a79f067f6cd..c786b74b4de 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.h +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.h @@ -12,12 +12,20 @@ #pragma once #include "lua/CLuaFunctionRef.h" +#include #include #include #define MAX_REGISTERED_COMMAND_LENGTH 64 #define MAX_REGISTERED_COMMANDHANDLER_LENGTH 64 +enum class MultiCommandHandlerPolicy : std::uint8_t +{ + BLOCK = 0, + WARN = 1, + ALLOW = 2 +}; + class CRegisteredCommands { struct SCommand diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp index 2ba5ab0ca79..55886337c0a 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp @@ -15,7 +15,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar allowMultiCommandHandlers) + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, std::uint8_t multiCommandHandlerPolicy) { m_weaponTypesUsingBulletSync = weaponTypesUsingBulletSync; m_ucVehExtrapolateEnabled = ucVehExtrapolateEnabled; @@ -26,7 +26,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set& weaponType m_ucAllowFastSprintFix = ucAllowFastSprintFix; m_ucAllowDrivebyAnimationFix = ucAllowDrivebyAnimationFix; m_ucAllowShotgunDamageFix = ucAllowShotgunDamageFix; - m_allowMultiCommandHandlers = allowMultiCommandHandlers; + m_multiCommandHandlerPolicy = multiCommandHandlerPolicy; } bool CSyncSettingsPacket::Read(NetBitStreamInterface& BitStream) @@ -52,7 +52,7 @@ bool CSyncSettingsPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(m_ucAllowFastSprintFix); BitStream.Write(m_ucAllowDrivebyAnimationFix); BitStream.Write(m_ucAllowShotgunDamageFix); - BitStream.Write(m_allowMultiCommandHandlers); + BitStream.Write(m_multiCommandHandlerPolicy); return true; } diff --git a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h index 66029330c90..f20ef002c59 100644 --- a/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h +++ b/Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h @@ -19,7 +19,7 @@ class CSyncSettingsPacket final : public CPacket CSyncSettingsPacket(){}; CSyncSettingsPacket(const std::set& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs, short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix, - uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar allowMultiCommandHandlers); + uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, std::uint8_t multiCommandHandlerPolicy); ePacketID GetPacketID() const { return PACKET_ID_SYNC_SETTINGS; }; unsigned long GetFlags() const { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; }; @@ -36,5 +36,5 @@ class CSyncSettingsPacket final : public CPacket uchar m_ucAllowFastSprintFix; uchar m_ucAllowDrivebyAnimationFix; uchar m_ucAllowShotgunDamageFix; - uchar m_allowMultiCommandHandlers; + std::uint8_t m_multiCommandHandlerPolicy; }; From 619ee1927cf38c3fb201d724b83f323a941a1178 Mon Sep 17 00:00:00 2001 From: Mohab <133429578+MohabCodeX@users.noreply.github.com> Date: Thu, 14 Aug 2025 20:51:48 +0300 Subject: [PATCH 10/13] Revert to explicit type for SCommand* in client side --- Client/mods/deathmatch/logic/CRegisteredCommands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp index a990ef43f90..370176b8797 100644 --- a/Client/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Client/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -49,7 +49,7 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons } // Check if we already have this key and handler - auto* pCommand = GetCommand(szKey, pLuaMain); + SCommand* pCommand = GetCommand(szKey, pLuaMain); if (pCommand) { if (pCommand->iLuaFunction == iLuaFunction) From 123fa4ff01a21c67574bada998f278fb41aa2bc6 Mon Sep 17 00:00:00 2001 From: Mohab <133429578+MohabCodeX@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:00:41 +0300 Subject: [PATCH 11/13] Update mtaserver.conf.template --- Server/mods/deathmatch/mtaserver.conf.template | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/mods/deathmatch/mtaserver.conf.template b/Server/mods/deathmatch/mtaserver.conf.template index 6eb5ab679d7..13b032ed100 100644 --- a/Server/mods/deathmatch/mtaserver.conf.template +++ b/Server/mods/deathmatch/mtaserver.conf.template @@ -322,5 +322,4 @@ 1 - From 457cbfd4ba11625d8319bc42fdcf0655b3c2e9db Mon Sep 17 00:00:00 2001 From: Mohab <133429578+MohabCodeX@users.noreply.github.com> Date: Mon, 18 Aug 2025 22:54:32 +0300 Subject: [PATCH 12/13] Remove deprecated mtaserver.conf.template file --- .../mods/deathmatch/mtaserver.conf.template | 325 ------------------ 1 file changed, 325 deletions(-) delete mode 100644 Server/mods/deathmatch/mtaserver.conf.template diff --git a/Server/mods/deathmatch/mtaserver.conf.template b/Server/mods/deathmatch/mtaserver.conf.template deleted file mode 100644 index 13b032ed100..00000000000 --- a/Server/mods/deathmatch/mtaserver.conf.template +++ /dev/null @@ -1,325 +0,0 @@ - - - - Default MTA Server - - - - - - - - - - - - - - - - - - - - - - - - - - - auto - - - 22003 - - - 32 - - - 22005 - - - - - - 5 - - - 20 - - - - - - none - - - - - - - - - - - - - 1 - - - - - - 1 - - - - - - 1 - - - 0 - - - - - - medium - - - - 100 - - 1500 - - 500 - - 400 - - 2000 - - 400 - - 100 - - 100 - - - 1 - - - 30 - - - 100 - - - 0 - - - 150 - - - 0 - - - server-id.keys - - - logs/server.log - - - logs/server_auth.log - - - logs/db.log - - - - - - acl.xml - - - logs/scripts.log - - - 0 - - - 0 - - - 1 - - - 60 - - - 0 - - - 1 - - - 4 - - - - - - backups - - - 3 - - - 10 - - - 1 - - - 1 - - - Admin - - - 1 - - - 127.0.0.1 - - - 1 - - - 0 - - - 1 - - - 1000 - 100 - - - 1 - From 2c921fb8621ac129354ec19a456476da39f20e72 Mon Sep 17 00:00:00 2001 From: Mohab <133429578+MohabCodeX@users.noreply.github.com> Date: Mon, 18 Aug 2025 22:59:16 +0300 Subject: [PATCH 13/13] Refactor --- Client/mods/deathmatch/logic/CClientGame.cpp | 7 +++---- Server/mods/deathmatch/logic/CGame.cpp | 2 +- Server/mods/deathmatch/logic/CRegisteredCommands.cpp | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 8203fc9f3a5..3a2aa71519f 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -410,7 +410,7 @@ CClientGame::~CClientGame() m_bBeingDeleted = true; // Remove active projectile references to local player if (auto pLocalPlayer = g_pClientGame->GetLocalPlayer()) - g_pGame->GetProjectileInfo()->RemoveEntityReferences(pLocalPlayer->GetGameEntity()); + g_pGame->GetProjectileInfo()->RemoveEntityReferences(pLocalPlayer->GetGameEntity()); // Stop all explosions. Unfortunately this doesn't fix the crash // if a vehicle is destroyed while it explodes. @@ -1025,7 +1025,7 @@ void CClientGame::DoPulsePostFrame() } auto taskManager = pLocalPlayer->GetTaskManager(); - auto task = taskManager->GetActiveTask(); + auto task = taskManager->GetActiveTask(); auto pVehicle = pLocalPlayer->GetOccupiedVehicle(); bool useZoneName = true; @@ -1079,7 +1079,7 @@ void CClientGame::DoPulsePostFrame() discordState = taskState.strState; useZoneName = taskState.bUseZone; - } + } if (useZoneName) { @@ -6140,7 +6140,6 @@ bool CClientGame::GetBirdsEnabled() return m_bBirdsEnabled; } - void CClientGame::SetWeaponRenderEnabled(bool enabled) { g_pGame->SetWeaponRenderEnabled(enabled); diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 7832a91999b..4faf0086cb3 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -4701,7 +4701,7 @@ void CGame::SendSyncSettings(CPlayer* pPlayer) uchar ucAllowFastSprintFix = true; uchar ucAllowDrivebyAnimFix = true; uchar ucAllowShotgunDamageFix = true; - const std::uint8_t multiCommandHandlerPolicy = static_cast(m_pMainConfig->GetAllowMultiCommandHandlers()); + std::uint8_t multiCommandHandlerPolicy = static_cast(m_pMainConfig->GetAllowMultiCommandHandlers()); CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs, ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, multiCommandHandlerPolicy); diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp index ba584e22820..7a6e9f26f9f 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -40,7 +40,7 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons if (CommandExists(szKey, nullptr)) { - const auto policy = static_cast(g_pGame->GetConfig()->GetAllowMultiCommandHandlers()); + auto policy = static_cast(g_pGame->GetConfig()->GetAllowMultiCommandHandlers()); switch (policy) {