Skip to content

Fixes #3655 - prevent duplicate command registration #4313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5425520
Applying FileEx's reviews
MohabCodeX Jul 28, 2025
2bd4d7e
feat: configurable multi-command handler support with warning levels
MohabCodeX Jul 28, 2025
42afe48
Merge branch 'master' into commandhandler-controlling
MohabCodeX Jul 28, 2025
684a4a1
Refactor MultiCommandHandler logic per review:
MohabCodeX Jul 30, 2025
5aeb9a1
Refactor MultiCommandHandler logic per review:
MohabCodeX Jul 31, 2025
2fffd70
Merge remote-tracking branch 'upstream/master' into commandhandler-co…
MohabCodeX Jul 31, 2025
6c32701
Merge branch 'commandhandler-controlling' of https://github.com/Mohab…
MohabCodeX Jul 31, 2025
81556e7
Merge branch 'master' into commandhandler-controlling
MohabCodeX Jul 31, 2025
f21d915
Merge branch 'master' into commandhandler-controlling
MohabCodeX Jul 31, 2025
a0dd51f
Merge branch 'master' into commandhandler-controlling
MohabCodeX Jul 31, 2025
71de114
Merge branch 'master' into commandhandler-controlling
MohabCodeX Aug 1, 2025
8111848
Merge branch 'master' into commandhandler-controlling
MohabCodeX Aug 1, 2025
2d3f4b5
Merge branch 'master' into commandhandler-controlling
MohabCodeX Aug 2, 2025
9d92fb7
Merge remote-tracking branch 'upstream/master' into commandhandler-co…
MohabCodeX Aug 4, 2025
7478096
Fix bitstream compilation errors after upstream version update
MohabCodeX Aug 4, 2025
2b73cd5
Update CPacketHandler.cpp to handle MultiCommandHandlers without vers…
MohabCodeX Aug 4, 2025
a8fde77
Update CPacketHandler.cpp to handle MultiCommandHandlers without vers…
MohabCodeX Aug 4, 2025
0d87408
update
MohabCodeX Aug 4, 2025
cda0ac7
Resolve conflicts
MohabCodeX Aug 4, 2025
85365d1
Merge branch 'master' into commandhandler-controlling
MohabCodeX Aug 5, 2025
84d800a
Merge branch 'master' into commandhandler-controlling
MohabCodeX Aug 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo())

m_bCursorEventsEnabled = false;
m_bInitiallyFadedOut = true;
m_allowMultiCommandHandlers = MultiCommandHandlerPolicy::ENABLED;

m_bIsPlayingBack = false;
m_bFirstPlaybackFrame = false;
Expand Down Expand Up @@ -6139,6 +6140,7 @@ bool CClientGame::GetBirdsEnabled()
return m_bBirdsEnabled;
}


void CClientGame::SetWeaponRenderEnabled(bool enabled)
{
g_pGame->SetWeaponRenderEnabled(enabled);
Expand Down
11 changes: 11 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ class CClientGame
QUIT_CONNECTION_DESYNC,
QUIT_TIMEOUT,
};

enum class MultiCommandHandlerPolicy
{
DISABLED = 0,
ENABLED = 1
};
enum
{
GLITCH_QUICKRELOAD,
Expand Down Expand Up @@ -470,6 +476,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
Expand Down Expand Up @@ -873,6 +882,8 @@ class CClientGame
// Key is the task and value is the CClientPed*
RunNamedAnimTask_type m_mapOfRunNamedAnimTasks;

MultiCommandHandlerPolicy m_allowMultiCommandHandlers;

long long m_timeLastDiscordStateUpdate;
};

Expand Down
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5481,6 +5481,10 @@ void CPacketHandler::Packet_SyncSettings(NetBitStreamInterface& bitStream)
uchar ucAllowShotgunDamageFix = 0;
bitStream.Read(ucAllowShotgunDamageFix);

uchar allowMultiCommandHandlers = 1;
bitStream.Read(allowMultiCommandHandlers);
g_pClientGame->SetAllowMultiCommandHandlers(static_cast<CClientGame::MultiCommandHandlerPolicy>(allowMultiCommandHandlers));

SMiscGameSettings miscGameSettings;
miscGameSettings.bUseAltPulseOrder = (ucUseAltPulseOrder != 0);
miscGameSettings.bAllowFastSprintFix = (ucAllowFastSprintFix != 0);
Expand Down
22 changes: 19 additions & 3 deletions Client/mods/deathmatch/logic/CRegisteredCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*****************************************************************************/

#include "StdInc.h"
#include "CClientGame.h"

using std::list;

Expand All @@ -27,6 +28,21 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons
assert(pLuaMain);
assert(szKey);

if (CommandExists(szKey, nullptr))
{
CClientGame::MultiCommandHandlerPolicy allowMultiHandlers = g_pClientGame->GetAllowMultiCommandHandlers();

// If not allowing duplicate handlers, throw error and block
if (allowMultiHandlers == CClientGame::MultiCommandHandlerPolicy::DISABLED)
{
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::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);
if (pCommand)
Expand Down Expand Up @@ -126,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)
Expand Down Expand Up @@ -183,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)
Expand All @@ -204,7 +220,7 @@ void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunct
while (arg)
{
Arguments.PushString(arg);
arg = strtok(NULL, " ");
arg = strtok(nullptr, " ");
}
delete[] szTempArguments;
}
Expand Down
3 changes: 2 additions & 1 deletion Server/mods/deathmatch/logic/CGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4700,9 +4700,10 @@ void CGame::SendSyncSettings(CPlayer* pPlayer)
uchar ucAllowFastSprintFix = true;
uchar ucAllowDrivebyAnimFix = true;
uchar ucAllowShotgunDamageFix = true;
uchar allowMultiCommandHandlers = m_pMainConfig->GetAllowMultiCommandHandlers();

CSyncSettingsPacket packet(weaponTypesUsingBulletSync, ucVehExtrapolateEnabled, sVehExtrapolateBaseMs, sVehExtrapolatePercent, sVehExtrapolateMaxMs,
ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix);
ucUseAltPulseOrder, ucAllowFastSprintFix, ucAllowDrivebyAnimFix, ucAllowShotgunDamageFix, allowMultiCommandHandlers);
if (pPlayer)
pPlayer->Send(packet);
else
Expand Down
3 changes: 3 additions & 0 deletions Server/mods/deathmatch/logic/CMainConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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();

Expand Down
2 changes: 2 additions & 0 deletions Server/mods/deathmatch/logic/CMainConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class CMainConfig : public CXMLConfig

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);
Expand Down Expand Up @@ -235,4 +236,5 @@ class CMainConfig : public CXMLConfig
bool m_elementDataWhitelisted;
bool m_checkDuplicateSerials;
int m_checkResourceClientFiles;
int m_allowMultiCommandHandlers;
};
26 changes: 22 additions & 4 deletions Server/mods/deathmatch/logic/CRegisteredCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -35,11 +38,26 @@ bool CRegisteredCommands::AddCommand(CLuaMain* pLuaMain, const char* szKey, cons
assert(pLuaMain);
assert(szKey);

if (CommandExists(szKey, nullptr))
{
int allowMultiCommandHandlers = g_pGame->GetConfig()->GetAllowMultiCommandHandlers();

if (allowMultiCommandHandlers == 0)
{
g_pGame->GetScriptDebugging()->LogError(pLuaMain->GetVM(), "addCommandHandler: Duplicate command registration blocked for '%s' (multiple handlers disabled)", szKey);
return false;
}
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;
Expand Down Expand Up @@ -139,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)
Expand Down Expand Up @@ -203,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,
Expand Down Expand Up @@ -253,7 +271,7 @@ void CRegisteredCommands::CallCommandHandler(CLuaMain* pLuaMain, const CLuaFunct
while (arg)
{
Arguments.PushString(arg);
arg = strtok(NULL, " ");
arg = strtok(nullptr, " ");
}

delete[] szTempArguments;
Expand Down
5 changes: 4 additions & 1 deletion Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

CSyncSettingsPacket::CSyncSettingsPacket(const std::set<eWeaponType>& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs,
short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix,
uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix)
uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix, uchar allowMultiCommandHandlers)
{
m_weaponTypesUsingBulletSync = weaponTypesUsingBulletSync;
m_ucVehExtrapolateEnabled = ucVehExtrapolateEnabled;
Expand All @@ -26,6 +26,7 @@ CSyncSettingsPacket::CSyncSettingsPacket(const std::set<eWeaponType>& weaponType
m_ucAllowFastSprintFix = ucAllowFastSprintFix;
m_ucAllowDrivebyAnimationFix = ucAllowDrivebyAnimationFix;
m_ucAllowShotgunDamageFix = ucAllowShotgunDamageFix;
m_allowMultiCommandHandlers = allowMultiCommandHandlers;
}

bool CSyncSettingsPacket::Read(NetBitStreamInterface& BitStream)
Expand All @@ -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_allowMultiCommandHandlers);

return true;
}
3 changes: 2 additions & 1 deletion Server/mods/deathmatch/logic/packets/CSyncSettingsPacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CSyncSettingsPacket final : public CPacket
CSyncSettingsPacket(){};
CSyncSettingsPacket(const std::set<eWeaponType>& weaponTypesUsingBulletSync, uchar ucVehExtrapolateEnabled, short sVehExtrapolateBaseMs,
short sVehExtrapolatePercent, short sVehExtrapolateMaxMs, uchar ucUseAltPulseOrder, uchar ucAllowFastSprintFix,
uchar ucAllowDrivebyAnimationFix, uchar ucAllowShotgunDamageFix);
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; };
Expand All @@ -36,4 +36,5 @@ class CSyncSettingsPacket final : public CPacket
uchar m_ucAllowFastSprintFix;
uchar m_ucAllowDrivebyAnimationFix;
uchar m_ucAllowShotgunDamageFix;
uchar m_allowMultiCommandHandlers;
};
5 changes: 5 additions & 0 deletions Server/mods/deathmatch/mtaserver.conf
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@
Available values: special detection (SD) codes ; (e.g. To enable special detection #15 use: 15) -->
<enablesd></enablesd>

<!-- This parameter controls how duplicate command handlers are handled.
Some resources may need to register multiple handlers for the same command.
Values: 0 - disallow duplicate handlers, 1 - allow with warning, 2 - allow silently ; default value: 1 -->
<allow_multi_command_handlers>1</allow_multi_command_handlers>

<!-- Minimum client version. Clients with a lower version will not be allowed to connect. After
disconnection, clients will be given an opportunity to download an update.
If left blank, this setting is disabled and there are no restrictions on who can connect.
Expand Down
6 changes: 6 additions & 0 deletions Server/mods/deathmatch/mtaserver.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@
Available values: special detection (SD) codes ; (e.g. To enable special detection #15 use: 15) -->
<enablesd></enablesd>

<!-- This parameter controls how duplicate command handlers are handled.
Some resources may need to register multiple handlers for the same command.
Values: 0 - disallow duplicate handlers, 1 - allow with warning, 2 - allow silently ; default value: 1 -->
<allow_multi_command_handlers>1</allow_multi_command_handlers>

<!-- Minimum client version. Clients with a lower version will not be allowed to connect. After
disconnection, clients will be given an opportunity to download an update.
If left blank, this setting is disabled and there are no restrictions on who can connect.
Expand Down Expand Up @@ -317,4 +322,5 @@
<!-- This parameter determines if resource client files that end in PNG, DFF and TXD should be checked for errors;
Values: 0 - Off, 1 - Enabled. Default - 1 -->
<resource_client_file_checks>1</resource_client_file_checks>

</config>
2 changes: 1 addition & 1 deletion Shared/sdk/net/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,4 @@ struct ISyncStructure
virtual ~ISyncStructure() {}
virtual bool Read(NetBitStreamInterface& bitStream) = 0;
virtual void Write(NetBitStreamInterface& bitStream) const = 0;
};
};
Loading