diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 230baa6204..67bcb07001 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_allowMultiCommandHandlers = MultiCommandHandlerPolicy::WARN; m_bIsPlayingBack = false; m_bFirstPlaybackFrame = false; @@ -409,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. @@ -1024,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; @@ -1078,7 +1079,7 @@ void CClientGame::DoPulsePostFrame() discordState = taskState.strState; useZoneName = taskState.bUseZone; - } + } if (useZoneName) { diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 0c33091763..84c1009917 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -193,6 +193,7 @@ class CClientGame QUIT_CONNECTION_DESYNC, QUIT_TIMEOUT, }; + enum { GLITCH_QUICKRELOAD, @@ -470,6 +471,9 @@ class CClientGame void ReinitMarkers(); void OnWindowFocusChange(bool state); + + void SetAllowMultiCommandHandlers(MultiCommandHandlerPolicy policy) noexcept { m_allowMultiCommandHandlers = policy; } + MultiCommandHandlerPolicy GetAllowMultiCommandHandlers() const noexcept { return m_allowMultiCommandHandlers; } private: // CGUI Callbacks @@ -873,6 +877,8 @@ class CClientGame // Key is the task and value is the CClientPed* RunNamedAnimTask_type m_mapOfRunNamedAnimTasks; + MultiCommandHandlerPolicy m_allowMultiCommandHandlers; + long long m_timeLastDiscordStateUpdate; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 054750b525..71c5cf4620 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -5482,6 +5482,10 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream) uchar ucAllowShotgunDamageFix = 0; bitStream.Read(ucAllowShotgunDamageFix); + uchar allowMultiCommandHandlers = 1; + bitStream.Read(allowMultiCommandHandlers); + g_pClientGame->SetAllowMultiCommandHandlers(static_cast(allowMultiCommandHandlers)); + 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 4faa169071..370176b879 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,26 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons assert(pLuaMain); assert(szKey); + if (CommandExists(szKey, nullptr)) + { + const MultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers(); + + switch (allowMultiHandlers) + { + 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; + } + } + // Check if we already have this key and handler SCommand* pCommand = GetCommand(szKey, pLuaMain); if (pCommand) @@ -126,7 +147,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) @@ -183,7 +204,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) @@ -204,7 +225,7 @@ void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunct while (arg) { Arguments.PushString(arg); - arg = strtok(NULL, " "); + arg = strtok(nullptr, " "); } delete[] szTempArguments; } diff --git a/Client/mods/deathmatch/logic/CRegisteredCommands.h b/Client/mods/deathmatch/logic/CRegisteredCommands.h index 5583e8d1d1..1fe63e25cf 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 910a1722dd..a74b919b78 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -4720,9 +4720,10 @@ void CGame::SendSyncSettings(CPlayer* pPlayer) uchar ucAllowFastSprintFix = true; uchar ucAllowDrivebyAnimFix = true; uchar ucAllowShotgunDamageFix = true; + std::uint8_t multiCommandHandlerPolicy = static_cast(m_pMainConfig->GetAllowMultiCommandHandlers()); CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs, - ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix); + 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 affe044153..e93ffdd87f 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_allowMultiCommandHandlers = 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_allowMultiCommandHandlers); + m_allowMultiCommandHandlers = Clamp(0, m_allowMultiCommandHandlers, 2); ApplyNetOptions(); @@ -1531,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; @@ -1580,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 ffb0b8de8d..cec5ea6171 100644 --- a/Server/mods/deathmatch/logic/CMainConfig.h +++ b/Server/mods/deathmatch/logic/CMainConfig.h @@ -147,9 +147,11 @@ class CMainConfig : public CXMLConfig void OnTickRateChange(); void OnAseSettingChange(); void OnPlayerTriggeredEventIntervalChange(); + void OnAllowMultiCommandHandlersChange(); int GetPlayerTriggeredEventInterval() const { return m_iPlayerTriggeredEventIntervalMs; } int GetMaxPlayerTriggeredEventsPerInterval() const { return m_iMaxPlayerTriggeredEventsPerInterval; } + int GetAllowMultiCommandHandlers() const noexcept { return m_allowMultiCommandHandlers; } private: void RegisterCommand(const char* szName, FCommandHandler* pFunction, bool bRestricted, const char* szConsoleHelpText); @@ -235,4 +237,5 @@ class CMainConfig : public CXMLConfig bool m_elementDataWhitelisted; bool m_checkDuplicateSerials; int m_checkResourceClientFiles; + int m_allowMultiCommandHandlers; }; diff --git a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp index 50706204fe..7a6e9f26f9 100644 --- a/Server/mods/deathmatch/logic/CRegisteredCommands.cpp +++ b/Server/mods/deathmatch/logic/CRegisteredCommands.cpp @@ -18,6 +18,9 @@ #include "CClient.h" #include "CConsoleClient.h" #include "CPlayer.h" +#include "CGame.h" +#include "CScriptDebugging.h" +#include "CMainConfig.h" CRegisteredCommands::CRegisteredCommands(CAccessControlListManager* pACLManager) { @@ -35,6 +38,26 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons assert(pLuaMain); assert(szKey); + if (CommandExists(szKey, nullptr)) + { + auto policy = static_cast(g_pGame->GetConfig()->GetAllowMultiCommandHandlers()); + + switch (policy) + { + 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; + } + } + // Check if we already have this key and handler SCommand* pCommand = GetCommand(szKey, pLuaMain); @@ -139,7 +162,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) @@ -203,7 +226,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, @@ -253,7 +276,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/CRegisteredCommands.h b/Server/mods/deathmatch/logic/CRegisteredCommands.h index a79f067f6c..c786b74b4d 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 a4b959e94d..55886337c0 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, std::uint8_t multiCommandHandlerPolicy) { 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_multiCommandHandlerPolicy = multiCommandHandlerPolicy; } bool CSyncSettingsPacket::Read(NetBitStreamInterface& BitStream) @@ -51,5 +52,7 @@ bool CSyncSettingsPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(m_ucAllowFastSprintFix); BitStream.Write(m_ucAllowDrivebyAnimationFix); BitStream.Write(m_ucAllowShotgunDamageFix); + 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 b4c22df18f..f20ef002c5 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, 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,4 +36,5 @@ class CSyncSettingsPacket final : public CPacket uchar m_ucAllowFastSprintFix; uchar m_ucAllowDrivebyAnimationFix; uchar m_ucAllowShotgunDamageFix; + std::uint8_t m_multiCommandHandlerPolicy; }; diff --git a/Server/mods/deathmatch/mtaserver.conf b/Server/mods/deathmatch/mtaserver.conf index 2dd6960ae0..21b736a6bb 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 +