From 5425520368a9473f8ae1e07863c2787901964dd7 Mon Sep 17 00:00:00 2001 From: Mohab Date: Mon, 28 Jul 2025 18:31:00 +0300 Subject: [PATCH 1/8] 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 2/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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));