From c9420a0ec9e3c16605cc3aa7bd7a430d6a828970 Mon Sep 17 00:00:00 2001 From: Symcryptc Date: Sun, 3 Aug 2025 16:04:30 +0300 Subject: [PATCH 1/4] Add player specific glitch function --- Client/mods/deathmatch/logic/CClientGame.cpp | 87 ++++++++++++++++++- Client/mods/deathmatch/logic/CClientGame.h | 5 ++ .../logic/luadefs/CLuaWorldDefs.cpp | 52 ++++++++++- .../deathmatch/logic/luadefs/CLuaWorldDefs.h | 4 + .../mods/deathmatch/logic/rpc/CWorldRPCs.cpp | 11 +++ Client/mods/deathmatch/logic/rpc/CWorldRPCs.h | 1 + Server/mods/deathmatch/logic/CGame.cpp | 28 ++++++ Server/mods/deathmatch/logic/CGame.h | 1 + .../deathmatch/logic/CPacketTranslator.cpp | 5 ++ .../logic/CPerfStat.RPCPacketUsage.cpp | 1 + .../logic/CStaticFunctionDefinitions.cpp | 30 +++++++ .../logic/CStaticFunctionDefinitions.h | 2 + .../logic/luadefs/CLuaWorldDefs.cpp | 55 ++++++++++++ .../deathmatch/logic/luadefs/CLuaWorldDefs.h | 2 + .../packets/CPlayerGlitchStatePacket.cpp | 17 ++++ .../logic/packets/CPlayerGlitchStatePacket.h | 34 ++++++++ Shared/mods/deathmatch/logic/Enums.cpp | 1 + Shared/sdk/net/Packets.h | 3 +- Shared/sdk/net/rpc_enums.h | 1 + 19 files changed, 335 insertions(+), 5 deletions(-) create mode 100644 Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp create mode 100644 Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 49343f0c9e2..4d42d6327c8 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -138,6 +138,25 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_Glitches[GLITCH_QUICKSTAND] = false; m_Glitches[GLITCH_KICKOUTOFVEHICLE_ONMODELREPLACE] = false; m_Glitches[GLITCH_VEHICLE_RAPID_STOP] = false; + + // Initialize per-player glitch overrides (initially match global state) + for (int i = 0; i < NUM_GLITCHES; i++) + { + m_PlayerGlitches[i] = m_Glitches[i]; + } + + // Glitch names (for Lua interface) + m_GlitchNames["quickreload"] = GLITCH_QUICKRELOAD; + m_GlitchNames["fastfire"] = GLITCH_FASTFIRE; + m_GlitchNames["fastmove"] = GLITCH_FASTMOVE; + m_GlitchNames["crouchbug"] = GLITCH_CROUCHBUG; + m_GlitchNames["highcloserangedamage"] = GLITCH_CLOSEDAMAGE; + m_GlitchNames["hitanim"] = GLITCH_HITANIM; + m_GlitchNames["fastsprint"] = GLITCH_FASTSPRINT; + m_GlitchNames["baddrivebyhitbox"] = GLITCH_BADDRIVEBYHITBOX; + m_GlitchNames["quickstand"] = GLITCH_QUICKSTAND; + m_GlitchNames["kickoutofvehicle_onmodelreplace"] = GLITCH_KICKOUTOFVEHICLE_ONMODELREPLACE; + m_GlitchNames["vehicle_rapid_stop"] = GLITCH_VEHICLE_RAPID_STOP; g_pMultiplayer->SetRapidVehicleStopFixEnabled(true); g_pMultiplayer->DisableBadDrivebyHitboxes(true); @@ -5992,12 +6011,21 @@ bool CClientGame::SetGlitchEnabled(unsigned char ucGlitch, bool bEnabled) if (ucGlitch < NUM_GLITCHES && bEnabled != m_Glitches[ucGlitch]) { m_Glitches[ucGlitch] = bEnabled; + + // If player has no override, also update player glitch state to match + // This maintains compatibility with existing behavior + if (m_PlayerGlitches[ucGlitch] == !bEnabled) + m_PlayerGlitches[ucGlitch] = bEnabled; + + // Apply the effective state (player override takes precedence) + bool bEffectiveState = m_PlayerGlitches[ucGlitch]; + if (ucGlitch == GLITCH_QUICKRELOAD) - g_pMultiplayer->DisableQuickReload(!bEnabled); + g_pMultiplayer->DisableQuickReload(!bEffectiveState); if (ucGlitch == GLITCH_CLOSEDAMAGE) - g_pMultiplayer->DisableCloseRangeDamage(!bEnabled); + g_pMultiplayer->DisableCloseRangeDamage(!bEffectiveState); if (ucGlitch == GLITCH_VEHICLE_RAPID_STOP) - g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEnabled); + g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEffectiveState); return true; } return false; @@ -6008,6 +6036,59 @@ bool CClientGame::IsGlitchEnabled(unsigned char ucGlitch) return ucGlitch < NUM_GLITCHES && m_Glitches[ucGlitch]; } +bool CClientGame::SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled) +{ + + auto it = m_GlitchNames.find(strGlitchName); + if (it == m_GlitchNames.end()) + return false; + + unsigned char ucGlitch = it->second; + if (ucGlitch >= NUM_GLITCHES) + return false; + + + if (m_PlayerGlitches[ucGlitch] == bEnabled) + return true; + + + m_PlayerGlitches[ucGlitch] = bEnabled; + + + bool bEffectiveState = bEnabled; + + if (ucGlitch == GLITCH_QUICKRELOAD) + g_pMultiplayer->DisableQuickReload(!bEffectiveState); + else if (ucGlitch == GLITCH_CLOSEDAMAGE) + g_pMultiplayer->DisableCloseRangeDamage(!bEffectiveState); + else if (ucGlitch == GLITCH_VEHICLE_RAPID_STOP) + g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEffectiveState); + + + if (auto stream = g_pNet->AllocateNetBitStream()) + { + stream->Write(strGlitchName); + stream->WriteBit(bEnabled); + g_pNet->SendPacket(PACKET_ID_PLAYER_GLITCH_STATE, stream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(stream); + } + + return true; +} + +bool CClientGame::IsPlayerGlitchEnabled(const std::string& strGlitchName) +{ + auto it = m_GlitchNames.find(strGlitchName); + if (it == m_GlitchNames.end()) + return false; + + unsigned char ucGlitch = it->second; + if (ucGlitch >= NUM_GLITCHES) + return false; + + return m_PlayerGlitches[ucGlitch]; +} + bool CClientGame::SetWorldSpecialProperty(const WorldSpecialProperty property, const bool enabled) noexcept { switch (property) diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 0c33091763b..83aa3b24da4 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -416,6 +416,9 @@ class CClientGame bool SetGlitchEnabled(unsigned char cGlitch, bool bEnabled); bool IsGlitchEnabled(unsigned char cGlitch); + bool SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled); + bool IsPlayerGlitchEnabled(const std::string& strGlitchName); + bool SetWorldSpecialProperty(const WorldSpecialProperty property, const bool enabled) noexcept; bool IsWorldSpecialProperty(const WorldSpecialProperty property); @@ -783,6 +786,8 @@ class CClientGame DWORD m_dwWanted; SFixedArray m_Glitches; + SFixedArray m_PlayerGlitches; + std::map m_GlitchNames; // Clouds Enabled bool m_bCloudsEnabled; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index caa53e4c674..d44415cd18d 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -149,7 +149,11 @@ void CLuaWorldDefs::LoadFunctions() {"isTimeFrozen", ArgumentParser}, {"isVolumetricShadowsEnabled", ArgumentParser}, {"isDynamicPedShadowsEnabled", ArgumentParser}, - {"testSphereAgainstWorld", ArgumentParser}}; + {"testSphereAgainstWorld", ArgumentParser}, + + // Per-player glitch functions + {"setPlayerGlitchEnabled", SetPlayerGlitchEnabled}, + {"isPlayerGlitchEnabled", IsPlayerGlitchEnabled}}; // Add functions for (const auto& [name, func] : functions) @@ -2352,3 +2356,49 @@ CLuaMultiReturnSetPlayerGlitchEnabled(strGlitch, bEnabled)) + { + lua_pushboolean(luaVM, true); + return 1; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaWorldDefs::IsPlayerGlitchEnabled(lua_State* luaVM) +{ + + SString strGlitch; + + CScriptArgReader argStream(luaVM); + argStream.ReadString(strGlitch); + + if (!argStream.HasErrors()) + { + bool bEnabled = g_pClientGame->IsPlayerGlitchEnabled(strGlitch); + lua_pushboolean(luaVM, bEnabled); + return 1; + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h index 5668ca6dd48..90379e48edd 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h @@ -149,4 +149,8 @@ class CLuaWorldDefs : public CLuaDefs static void RemoveGameWorld(); static void RestoreGameWorld(); + + + LUA_DECLARE(SetPlayerGlitchEnabled); + LUA_DECLARE(IsPlayerGlitchEnabled); }; diff --git a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp index 65b1fe589a0..fea51b0e4cc 100644 --- a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp @@ -39,6 +39,7 @@ void CWorldRPCs::LoadFunctions() AddHandler(SET_FPS_LIMIT, SetFPSLimit, "SetFPSLimit"); AddHandler(SET_GARAGE_OPEN, SetGarageOpen, "SetGarageOpen"); AddHandler(SET_GLITCH_ENABLED, SetGlitchEnabled, "SetGlitchEnabled"); + AddHandler(SET_PLAYER_GLITCH_ENABLED, SetPlayerGlitchEnabled, "SetPlayerGlitchEnabled"); AddHandler(SET_JETPACK_WEAPON_ENABLED, SetJetpackWeaponEnabled, "SetJetpackWeaponEnabled"); AddHandler(SET_CLOUDS_ENABLED, SetCloudsEnabled, "SetCloudsEnabled"); AddHandler(SET_TRAFFIC_LIGHT_STATE, SetTrafficLightState, "SetTrafficLightState"); @@ -241,6 +242,16 @@ void CWorldRPCs::SetGlitchEnabled(NetBitStreamInterface& bitStream) g_pClientGame->SetGlitchEnabled(eGlitch, (ucIsEnabled == 1)); } +void CWorldRPCs::SetPlayerGlitchEnabled(NetBitStreamInterface& bitStream) +{ + SString strGlitchName; + bool bEnabled; + if (bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) + { + g_pClientGame->SetPlayerGlitchEnabled(strGlitchName, bEnabled); + } +} + void CWorldRPCs::SetJetpackWeaponEnabled(NetBitStreamInterface& bitStream) { unsigned char ucWeaponID = 0; diff --git a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.h b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.h index 5c5c4514f1d..f9913865d51 100644 --- a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.h +++ b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.h @@ -37,6 +37,7 @@ class CWorldRPCs : public CRPCFunctions DECLARE_RPC(SetFPSLimit); DECLARE_RPC(SetGarageOpen); DECLARE_RPC(SetGlitchEnabled); + DECLARE_RPC(SetPlayerGlitchEnabled); DECLARE_RPC(SetJetpackWeaponEnabled); DECLARE_RPC(SetCloudsEnabled); DECLARE_RPC(SetTrafficLightState); diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 486ea429636..295cda127fa 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -59,6 +59,7 @@ #include "packets/CPlayerListPacket.h" #include "packets/CPlayerClothesPacket.h" #include "packets/CPlayerWorldSpecialPropertyPacket.h" +#include "packets/CPlayerGlitchStatePacket.h" #include "packets/CServerInfoSyncPacket.h" #include "packets/CLuaPacket.h" #include "../utils/COpenPortsTester.h" @@ -1328,6 +1329,12 @@ bool CGame::ProcessPacket(CPacket& Packet) return true; } + case PACKET_ID_PLAYER_GLITCH_STATE: + { + Packet_PlayerGlitchState(static_cast(Packet)); + return true; + } + default: break; } @@ -4186,6 +4193,27 @@ void CGame::Packet_PlayerWorldSpecialProperty(CPlayerWorldSpecialPropertyPacket& player->CallEvent("onPlayerChangesWorldSpecialProperty", arguments, nullptr); } +void CGame::Packet_PlayerGlitchState(CPlayerGlitchStatePacket& packet) noexcept +{ + CPlayer* player = packet.GetSourcePlayer(); + + if (!player) + return; + + const std::string& glitchName = packet.GetGlitchName(); + const bool enabled = packet.IsEnabled(); + + + if (!IsGlitch(glitchName)) + return; + + CLuaArguments arguments; + arguments.PushString(glitchName); + arguments.PushBoolean(enabled); + + player->CallEvent("onPlayerGlitchStateChange", arguments, nullptr); +} + void CGame::Packet_PlayerModInfo(CPlayerModInfoPacket& Packet) { CPlayer* pPlayer = Packet.GetSourcePlayer(); diff --git a/Server/mods/deathmatch/logic/CGame.h b/Server/mods/deathmatch/logic/CGame.h index af491039d06..c0ec2b3e7a7 100644 --- a/Server/mods/deathmatch/logic/CGame.h +++ b/Server/mods/deathmatch/logic/CGame.h @@ -523,6 +523,7 @@ class CGame void Packet_PlayerNetworkStatus(class CPlayerNetworkStatusPacket& Packet); void Packet_PlayerResourceStart(class CPlayerResourceStartPacket& Packet); void Packet_PlayerWorldSpecialProperty(class CPlayerWorldSpecialPropertyPacket& packet) noexcept; + void Packet_PlayerGlitchState(class CPlayerGlitchStatePacket& packet) noexcept; static void PlayerCompleteConnect(CPlayer* pPlayer); diff --git a/Server/mods/deathmatch/logic/CPacketTranslator.cpp b/Server/mods/deathmatch/logic/CPacketTranslator.cpp index 2d12509c502..c33eb292be0 100644 --- a/Server/mods/deathmatch/logic/CPacketTranslator.cpp +++ b/Server/mods/deathmatch/logic/CPacketTranslator.cpp @@ -49,6 +49,7 @@ #include "packets/CPlayerNetworkStatusPacket.h" #include "packets/CPlayerResourceStartPacket.h" #include "packets/CPlayerWorldSpecialPropertyPacket.h" +#include "packets/CPlayerGlitchStatePacket.h" CPacketTranslator::CPacketTranslator(CPlayerManager* pPlayerManager) { @@ -217,6 +218,10 @@ CPacket* CPacketTranslator::Translate(const NetServerPlayerID& Socket, ePacketID pTemp = new CPlayerWorldSpecialPropertyPacket; break; + case PACKET_ID_PLAYER_GLITCH_STATE: + pTemp = new CPlayerGlitchStatePacket; + break; + default: break; } diff --git a/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp b/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp index cdebcedcc24..a6987403fd7 100644 --- a/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp +++ b/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp @@ -149,6 +149,7 @@ ADD_ENUM1(SET_FPS_LIMIT) ADD_ENUM1(SET_GARAGE_OPEN) ADD_ENUM1(RESET_MAP_INFO) ADD_ENUM1(SET_GLITCH_ENABLED) +ADD_ENUM1(SET_PLAYER_GLITCH_ENABLED) ADD_ENUM1(SET_CLOUDS_ENABLED) ADD_ENUM1(REMOVE_ELEMENT_DATA) ADD_ENUM1(SET_VEHICLE_HANDLING) diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 14644dc1094..150a42d7b56 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -11254,6 +11254,36 @@ bool CStaticFunctionDefinitions::IsGlitchEnabled(const std::string& strGlitchNam return false; } +bool CStaticFunctionDefinitions::SetPlayerGlitchEnabled(CPlayer* pPlayer, const std::string& strGlitchName, bool bEnabled) +{ + assert(pPlayer); + + if (g_pGame->IsGlitch(strGlitchName)) + { + + CBitStream BitStream; + BitStream.pBitStream->Write(strGlitchName); + BitStream.pBitStream->WriteBit(bEnabled); + pPlayer->Send(CLuaPacket(SET_PLAYER_GLITCH_ENABLED, *BitStream.pBitStream)); + return true; + } + + return false; +} + +bool CStaticFunctionDefinitions::IsPlayerGlitchEnabled(CPlayer* pPlayer, const std::string& strGlitchName, bool& bEnabled) +{ + assert(pPlayer); + + + if (g_pGame->IsGlitch(strGlitchName)) + { + bEnabled = g_pGame->IsGlitchEnabled(strGlitchName); + return true; + } + return false; +} + bool CStaticFunctionDefinitions::IsWorldSpecialPropertyEnabled(WorldSpecialProperty property) { return g_pGame->IsWorldSpecialPropertyEnabled(property); diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h index c0d46d5a7ed..bc423e67f07 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -629,6 +629,8 @@ class CStaticFunctionDefinitions static bool SetGarageOpen(unsigned char ucGarageID, bool bIsOpen); static bool SetGlitchEnabled(const std::string& strGlitchName, bool bEnabled); static bool IsGlitchEnabled(const std::string& strGlitchName, bool& bEnabled); + static bool SetPlayerGlitchEnabled(CPlayer* pPlayer, const std::string& strGlitchName, bool bEnabled); + static bool IsPlayerGlitchEnabled(CPlayer* pPlayer, const std::string& strGlitchName, bool& bEnabled); static bool GetJetpackWeaponEnabled(eWeaponType weaponType, bool& bEnabled); static bool SetJetpackWeaponEnabled(eWeaponType weaponType, bool bEnabled); static bool SetCloudsEnabled(bool bEnabled); diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index c46e1872bc7..26d6b683051 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -56,6 +56,7 @@ void CLuaWorldDefs::LoadFunctions() {"setMinuteDuration", setMinuteDuration}, {"setGarageOpen", setGarageOpen}, {"setGlitchEnabled", setGlitchEnabled}, + {"setPlayerGlitchEnabled", setPlayerGlitchEnabled}, {"setCloudsEnabled", setCloudsEnabled}, {"setTrafficLightState", setTrafficLightState}, {"setTrafficLightsLocked", setTrafficLightsLocked}, @@ -92,6 +93,7 @@ void CLuaWorldDefs::LoadFunctions() // Check {"isGarageOpen", isGarageOpen}, {"isGlitchEnabled", isGlitchEnabled}, + {"isPlayerGlitchEnabled", isPlayerGlitchEnabled}, {"isWorldSpecialPropertyEnabled", ArgumentParserWarn}, {"areTrafficLightsLocked", areTrafficLightsLocked}}; @@ -1223,6 +1225,33 @@ int CLuaWorldDefs::setGlitchEnabled(lua_State* luaVM) return 1; } +int CLuaWorldDefs::setPlayerGlitchEnabled(lua_State* luaVM) +{ + + CPlayer* pPlayer; + SString strGlitch; + bool bEnabled; + + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPlayer); + argStream.ReadString(strGlitch); + argStream.ReadBool(bEnabled); + + if (!argStream.HasErrors()) + { + if (CStaticFunctionDefinitions::SetPlayerGlitchEnabled(pPlayer, strGlitch, bEnabled)) + { + lua_pushboolean(luaVM, true); + return 1; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaWorldDefs::isGlitchEnabled(lua_State* luaVM) { // bool isGlitchEnabled ( string glitchName ) @@ -1247,6 +1276,32 @@ int CLuaWorldDefs::isGlitchEnabled(lua_State* luaVM) return 1; } +int CLuaWorldDefs::isPlayerGlitchEnabled(lua_State* luaVM) +{ + // bool isPlayerGlitchEnabled ( player thePlayer, string glitchName ) + CPlayer* pPlayer; + SString strGlitch; + + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPlayer); + argStream.ReadString(strGlitch); + + if (!argStream.HasErrors()) + { + bool bEnabled; + if (CStaticFunctionDefinitions::IsPlayerGlitchEnabled(pPlayer, strGlitch, bEnabled)) + { + lua_pushboolean(luaVM, bEnabled); + return 1; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaWorldDefs::setJetpackWeaponEnabled(lua_State* luaVM) { eWeaponType weaponType; diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h b/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h index 49032579b38..f8e463770e4 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h +++ b/Server/mods/deathmatch/logic/luadefs/CLuaWorldDefs.h @@ -28,6 +28,7 @@ class CLuaWorldDefs : public CLuaDefs LUA_DECLARE(getMinuteDuration); LUA_DECLARE(isGarageOpen); LUA_DECLARE(isGlitchEnabled); + LUA_DECLARE(isPlayerGlitchEnabled); LUA_DECLARE(setJetpackWeaponEnabled); LUA_DECLARE(getJetpackWeaponEnabled); LUA_DECLARE(getCloudsEnabled); @@ -63,6 +64,7 @@ class CLuaWorldDefs : public CLuaDefs LUA_DECLARE(setMinuteDuration); LUA_DECLARE(setGarageOpen); LUA_DECLARE(setGlitchEnabled); + LUA_DECLARE(setPlayerGlitchEnabled); LUA_DECLARE(setCloudsEnabled); LUA_DECLARE(setTrafficLightState); LUA_DECLARE(setTrafficLightsLocked); diff --git a/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp b/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp new file mode 100644 index 00000000000..dbef2e78a0c --- /dev/null +++ b/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp @@ -0,0 +1,17 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CPlayerGlitchStatePacket.h" + +bool CPlayerGlitchStatePacket::Read(NetBitStreamInterface& stream) noexcept +{ + return stream.Read(m_glitchName) && stream.ReadBit(m_enabled); +} \ No newline at end of file diff --git a/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h b/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h new file mode 100644 index 00000000000..3f0e28670cc --- /dev/null +++ b/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h @@ -0,0 +1,34 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include +#include +#include "CPacket.h" + +class CPlayerGlitchStatePacket final : public CPacket +{ +public: + CPlayerGlitchStatePacket() noexcept {} + + ePacketID GetPacketID() const noexcept { return PACKET_ID_PLAYER_GLITCH_STATE; } + unsigned long GetFlags() const noexcept { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; } + virtual ePacketOrdering GetPacketOrdering() const noexcept { return PACKET_ORDERING_DEFAULT; } + + bool Read(NetBitStreamInterface& stream) noexcept; + + std::string GetGlitchName() const noexcept { return m_glitchName; } + bool IsEnabled() const noexcept { return m_enabled; } + +private: + std::string m_glitchName; + bool m_enabled; +}; \ No newline at end of file diff --git a/Shared/mods/deathmatch/logic/Enums.cpp b/Shared/mods/deathmatch/logic/Enums.cpp index 8bb99db974f..beeebcdf256 100644 --- a/Shared/mods/deathmatch/logic/Enums.cpp +++ b/Shared/mods/deathmatch/logic/Enums.cpp @@ -233,4 +233,5 @@ ADD_ENUM1(PACKET_ID_SERVER_INFO_SYNC) ADD_ENUM1(PACKET_ID_DISCORD_JOIN) ADD_ENUM1(PACKET_ID_PLAYER_RESOURCE_START) ADD_ENUM1(PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY) +ADD_ENUM1(PACKET_ID_PLAYER_GLITCH_STATE) IMPLEMENT_ENUM_END("ePacketID") diff --git a/Shared/sdk/net/Packets.h b/Shared/sdk/net/Packets.h index 2f587cad140..8bef027b057 100644 --- a/Shared/sdk/net/Packets.h +++ b/Shared/sdk/net/Packets.h @@ -177,5 +177,6 @@ enum ePacketID PACKET_ID_SERVER_INFO_SYNC, PACKET_ID_DISCORD_JOIN, PACKET_ID_PLAYER_RESOURCE_START, - PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY + PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY, + PACKET_ID_PLAYER_GLITCH_STATE }; diff --git a/Shared/sdk/net/rpc_enums.h b/Shared/sdk/net/rpc_enums.h index 84c7518bc45..0b3c88b0cb0 100644 --- a/Shared/sdk/net/rpc_enums.h +++ b/Shared/sdk/net/rpc_enums.h @@ -170,6 +170,7 @@ enum eElementRPCFunctions SET_GARAGE_OPEN, RESET_MAP_INFO, SET_GLITCH_ENABLED, + SET_PLAYER_GLITCH_ENABLED, SET_CLOUDS_ENABLED, REMOVE_ELEMENT_DATA, From d01c529160c27b2687421a501b8335593d5228f1 Mon Sep 17 00:00:00 2001 From: Symcryptc Date: Sun, 3 Aug 2025 16:17:21 +0300 Subject: [PATCH 2/4] Commit --- Client/mods/deathmatch/logic/CClientGame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 4d42d6327c8..41c0a472978 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -139,13 +139,13 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_Glitches[GLITCH_KICKOUTOFVEHICLE_ONMODELREPLACE] = false; m_Glitches[GLITCH_VEHICLE_RAPID_STOP] = false; - // Initialize per-player glitch overrides (initially match global state) + for (int i = 0; i < NUM_GLITCHES; i++) { m_PlayerGlitches[i] = m_Glitches[i]; } - // Glitch names (for Lua interface) + m_GlitchNames["quickreload"] = GLITCH_QUICKRELOAD; m_GlitchNames["fastfire"] = GLITCH_FASTFIRE; m_GlitchNames["fastmove"] = GLITCH_FASTMOVE; From 76106678b75331bda5143a70df1183932c5f771a Mon Sep 17 00:00:00 2001 From: Symcryptc Date: Sun, 3 Aug 2025 17:29:36 +0300 Subject: [PATCH 3/4] desync --- Client/mods/deathmatch/logic/CClientGame.cpp | 42 +++--- Client/mods/deathmatch/logic/CClientGame.h | 1 + .../logic/luadefs/CLuaWorldDefs.cpp | 7 +- .../mods/deathmatch/logic/rpc/CWorldRPCs.cpp | 18 ++- Server/mods/deathmatch/logic/CGame.cpp | 49 +++++-- Server/mods/deathmatch/logic/CGame.h | 3 +- .../deathmatch/logic/CPacketTranslator.cpp | 6 +- Server/mods/deathmatch/logic/CPlayer.cpp | 122 ++++++++++++++++++ Server/mods/deathmatch/logic/CPlayer.h | 11 +- .../logic/CStaticFunctionDefinitions.cpp | 16 +-- ...ket.cpp => CPlayerGlitchRequestPacket.cpp} | 6 +- ...ePacket.h => CPlayerGlitchRequestPacket.h} | 14 +- Shared/mods/deathmatch/logic/Enums.cpp | 2 +- Shared/sdk/net/Packets.h | 2 +- 14 files changed, 240 insertions(+), 59 deletions(-) rename Server/mods/deathmatch/logic/packets/{CPlayerGlitchStatePacket.cpp => CPlayerGlitchRequestPacket.cpp} (68%) rename Server/mods/deathmatch/logic/packets/{CPlayerGlitchStatePacket.h => CPlayerGlitchRequestPacket.h} (67%) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 41c0a472978..1343c962066 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -6036,6 +6036,29 @@ bool CClientGame::IsGlitchEnabled(unsigned char ucGlitch) return ucGlitch < NUM_GLITCHES && m_Glitches[ucGlitch]; } +bool CClientGame::RequestPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled) +{ + // Find the glitch index for validation + auto it = m_GlitchNames.find(strGlitchName); + if (it == m_GlitchNames.end()) + return false; + + unsigned char ucGlitch = it->second; + if (ucGlitch >= NUM_GLITCHES) + return false; + + // Send request to server (don't apply locally yet) + if (auto stream = g_pNet->AllocateNetBitStream()) + { + stream->Write(strGlitchName); + stream->WriteBit(bEnabled); + g_pNet->SendPacket(PACKET_ID_PLAYER_GLITCH_REQUEST, stream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(stream); + } + + return true; +} + bool CClientGame::SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled) { @@ -6054,24 +6077,13 @@ bool CClientGame::SetPlayerGlitchEnabled(const std::string& strGlitchName, bool m_PlayerGlitches[ucGlitch] = bEnabled; - - bool bEffectiveState = bEnabled; - + // apply to game engine? if (ucGlitch == GLITCH_QUICKRELOAD) - g_pMultiplayer->DisableQuickReload(!bEffectiveState); + g_pMultiplayer->DisableQuickReload(!bEnabled); else if (ucGlitch == GLITCH_CLOSEDAMAGE) - g_pMultiplayer->DisableCloseRangeDamage(!bEffectiveState); + g_pMultiplayer->DisableCloseRangeDamage(!bEnabled); else if (ucGlitch == GLITCH_VEHICLE_RAPID_STOP) - g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEffectiveState); - - - if (auto stream = g_pNet->AllocateNetBitStream()) - { - stream->Write(strGlitchName); - stream->WriteBit(bEnabled); - g_pNet->SendPacket(PACKET_ID_PLAYER_GLITCH_STATE, stream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(stream); - } + g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEnabled); return true; } diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 83aa3b24da4..efa891a8fe3 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -416,6 +416,7 @@ class CClientGame bool SetGlitchEnabled(unsigned char cGlitch, bool bEnabled); bool IsGlitchEnabled(unsigned char cGlitch); + bool RequestPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled); bool SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled); bool IsPlayerGlitchEnabled(const std::string& strGlitchName); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp index d44415cd18d..8d09c70ba1c 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaWorldDefs.cpp @@ -2359,7 +2359,7 @@ CLuaMultiReturnSetPlayerGlitchEnabled(strGlitch, bEnabled)) + // Send request to server + if (g_pClientGame->RequestPlayerGlitchEnabled(strGlitch, bEnabled)) { lua_pushboolean(luaVM, true); return 1; @@ -2384,7 +2385,7 @@ int CLuaWorldDefs::SetPlayerGlitchEnabled(lua_State* luaVM) int CLuaWorldDefs::IsPlayerGlitchEnabled(lua_State* luaVM) { - + // bool isPlayerGlitchEnabled ( string glitchName ) SString strGlitch; CScriptArgReader argStream(luaVM); diff --git a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp index fea51b0e4cc..504577d6f1e 100644 --- a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp @@ -244,11 +244,25 @@ void CWorldRPCs::SetGlitchEnabled(NetBitStreamInterface& bitStream) void CWorldRPCs::SetPlayerGlitchEnabled(NetBitStreamInterface& bitStream) { + ElementID sourcePlayerID; SString strGlitchName; bool bEnabled; - if (bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) + + // Check if this includes source player ID (for other players' states) + if (bitStream.Read(sourcePlayerID) && bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) { - g_pClientGame->SetPlayerGlitchEnabled(strGlitchName, bEnabled); + + return; + } + else + { + + bitStream.Reset(); + if (bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) + { + + g_pClientGame->SetPlayerGlitchEnabled(strGlitchName, bEnabled); + } } } diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 295cda127fa..bb62065458a 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -59,7 +59,7 @@ #include "packets/CPlayerListPacket.h" #include "packets/CPlayerClothesPacket.h" #include "packets/CPlayerWorldSpecialPropertyPacket.h" -#include "packets/CPlayerGlitchStatePacket.h" +#include "packets/CPlayerGlitchRequestPacket.h" #include "packets/CServerInfoSyncPacket.h" #include "packets/CLuaPacket.h" #include "../utils/COpenPortsTester.h" @@ -1329,9 +1329,9 @@ bool CGame::ProcessPacket(CPacket& Packet) return true; } - case PACKET_ID_PLAYER_GLITCH_STATE: + case PACKET_ID_PLAYER_GLITCH_REQUEST: { - Packet_PlayerGlitchState(static_cast(Packet)); + Packet_PlayerGlitchRequest(static_cast(Packet)); return true; } @@ -1487,6 +1487,21 @@ void CGame::InitialDataStream(CPlayer& Player) CLuaArguments Arguments; Player.CallEvent("onPlayerJoin", Arguments); + // Send existing per-player glitch states from all players + if (Player.IsJoined()) + { + list::const_iterator iter = m_pPlayerManager->IterBegin(); + for (; iter != m_pPlayerManager->IterEnd(); iter++) + { + CPlayer* pOtherPlayer = *iter; + if (pOtherPlayer != &Player && pOtherPlayer->IsJoined()) + { + // Send other players' glitch overrides to the new player + pOtherPlayer->SendAllPlayerGlitchStates(&Player); + } + } + } + marker.Set("onPlayerJoin"); // Register them on the lightweight sync manager. @@ -4193,25 +4208,31 @@ void CGame::Packet_PlayerWorldSpecialProperty(CPlayerWorldSpecialPropertyPacket& player->CallEvent("onPlayerChangesWorldSpecialProperty", arguments, nullptr); } -void CGame::Packet_PlayerGlitchState(CPlayerGlitchStatePacket& packet) noexcept +void CGame::Packet_PlayerGlitchRequest(CPlayerGlitchRequestPacket& packet) noexcept { - CPlayer* player = packet.GetSourcePlayer(); - - if (!player) + CPlayer* pPlayer = packet.GetSourcePlayer(); + if (!pPlayer) return; - const std::string& glitchName = packet.GetGlitchName(); - const bool enabled = packet.IsEnabled(); + const std::string& strGlitchName = packet.GetGlitchName(); + const bool bEnabled = packet.IsEnabled(); - if (!IsGlitch(glitchName)) + if (!IsGlitch(strGlitchName)) return; - CLuaArguments arguments; - arguments.PushString(glitchName); - arguments.PushBoolean(enabled); + + if (pPlayer->SetPlayerGlitchEnabled(strGlitchName, bEnabled)) + { + + pPlayer->SendPlayerGlitchState(strGlitchName); - player->CallEvent("onPlayerGlitchStateChange", arguments, nullptr); + + CLuaArguments arguments; + arguments.PushString(strGlitchName); + arguments.PushBoolean(bEnabled); + pPlayer->CallEvent("onPlayerGlitchStateChange", arguments, nullptr); + } } void CGame::Packet_PlayerModInfo(CPlayerModInfoPacket& Packet) diff --git a/Server/mods/deathmatch/logic/CGame.h b/Server/mods/deathmatch/logic/CGame.h index c0ec2b3e7a7..03c8ab306db 100644 --- a/Server/mods/deathmatch/logic/CGame.h +++ b/Server/mods/deathmatch/logic/CGame.h @@ -430,6 +430,7 @@ class CGame bool IsGlitchEnabled(eGlitchType cGlitch); eGlitchType GetGlitchIndex(const std::string& strGlitch) { return m_GlitchNames[strGlitch]; } bool IsGlitch(const std::string& strGlitch) { return m_GlitchNames.count(strGlitch) > 0; } + const std::map& GetGlitchNames() const { return m_GlitchNames; } bool IsWorldSpecialPropertyEnabled(WorldSpecialProperty property) { return m_WorldSpecialProps[property]; } void SetWorldSpecialPropertyEnabled(WorldSpecialProperty property, bool isEnabled) { m_WorldSpecialProps[property] = isEnabled; } @@ -523,7 +524,7 @@ class CGame void Packet_PlayerNetworkStatus(class CPlayerNetworkStatusPacket& Packet); void Packet_PlayerResourceStart(class CPlayerResourceStartPacket& Packet); void Packet_PlayerWorldSpecialProperty(class CPlayerWorldSpecialPropertyPacket& packet) noexcept; - void Packet_PlayerGlitchState(class CPlayerGlitchStatePacket& packet) noexcept; + void Packet_PlayerGlitchRequest(class CPlayerGlitchRequestPacket& packet) noexcept; static void PlayerCompleteConnect(CPlayer* pPlayer); diff --git a/Server/mods/deathmatch/logic/CPacketTranslator.cpp b/Server/mods/deathmatch/logic/CPacketTranslator.cpp index c33eb292be0..eba9b5e460d 100644 --- a/Server/mods/deathmatch/logic/CPacketTranslator.cpp +++ b/Server/mods/deathmatch/logic/CPacketTranslator.cpp @@ -49,7 +49,7 @@ #include "packets/CPlayerNetworkStatusPacket.h" #include "packets/CPlayerResourceStartPacket.h" #include "packets/CPlayerWorldSpecialPropertyPacket.h" -#include "packets/CPlayerGlitchStatePacket.h" +#include "packets/CPlayerGlitchRequestPacket.h" CPacketTranslator::CPacketTranslator(CPlayerManager* pPlayerManager) { @@ -218,8 +218,8 @@ CPacket* CPacketTranslator::Translate(const NetServerPlayerID& Socket, ePacketID pTemp = new CPlayerWorldSpecialPropertyPacket; break; - case PACKET_ID_PLAYER_GLITCH_STATE: - pTemp = new CPlayerGlitchStatePacket; + case PACKET_ID_PLAYER_GLITCH_REQUEST: + pTemp = new CPlayerGlitchRequestPacket; break; default: diff --git a/Server/mods/deathmatch/logic/CPlayer.cpp b/Server/mods/deathmatch/logic/CPlayer.cpp index e772b87b2de..fa376d563f0 100644 --- a/Server/mods/deathmatch/logic/CPlayer.cpp +++ b/Server/mods/deathmatch/logic/CPlayer.cpp @@ -120,6 +120,13 @@ CPlayer::CPlayer(CPlayerManager* pPlayerManager, class CScriptDebugging* pScript m_LastReceivedSyncTimer.SetUseModuleTickCount(true); m_ConnectedTimer.SetUseModuleTickCount(true); m_bIsLeavingServer = false; + + // Initialize per-player glitch states + for (int i = 0; i < NUM_GLITCHES; i++) + { + m_PlayerGlitches[i] = false; + m_bHasPlayerGlitchOverride[i] = false; + } } CPlayer::~CPlayer() @@ -1158,6 +1165,121 @@ CPlayer* GetDeletedMapKey(CPlayer**) // // ///////////////////////////////////////////////////////////////// +bool CPlayer::SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled) +{ + if (!g_pGame->IsGlitch(strGlitchName)) + return false; + + eGlitchType eGlitch = g_pGame->GetGlitchIndex(strGlitchName); + if (eGlitch >= NUM_GLITCHES) + return false; + + m_PlayerGlitches[eGlitch] = bEnabled; + m_bHasPlayerGlitchOverride[eGlitch] = true; + + return true; +} + +bool CPlayer::IsPlayerGlitchEnabled(const std::string& strGlitchName) const +{ + if (!g_pGame->IsGlitch(strGlitchName)) + return false; + + eGlitchType eGlitch = g_pGame->GetGlitchIndex(strGlitchName); + if (eGlitch >= NUM_GLITCHES) + return false; + + if (m_bHasPlayerGlitchOverride[eGlitch]) + return m_PlayerGlitches[eGlitch]; + + return g_pGame->IsGlitchEnabled(eGlitch); +} + +bool CPlayer::HasPlayerGlitchOverride(const std::string& strGlitchName) const +{ + if (!g_pGame->IsGlitch(strGlitchName)) + return false; + + eGlitchType eGlitch = g_pGame->GetGlitchIndex(strGlitchName); + if (eGlitch >= NUM_GLITCHES) + return false; + + return m_bHasPlayerGlitchOverride[eGlitch]; +} + +void CPlayer::SendPlayerGlitchState(const std::string& strGlitchName) const +{ + if (!g_pGame->IsGlitch(strGlitchName)) + return; + + eGlitchType eGlitch = g_pGame->GetGlitchIndex(strGlitchName); + if (eGlitch >= NUM_GLITCHES) + return; + + bool bEffectiveState = IsPlayerGlitchEnabled(strGlitchName); + + CBitStream BitStream; + BitStream.pBitStream->Write(strGlitchName); + BitStream.pBitStream->WriteBit(bEffectiveState); + Send(CLuaPacket(SET_PLAYER_GLITCH_ENABLED, *BitStream.pBitStream)); +} + +void CPlayer::SendAllPlayerGlitchStates(CPlayer* pTarget) const +{ + CPlayer* pSendTo = pTarget ? pTarget : const_cast(this); + + for (int i = 0; i < NUM_GLITCHES; i++) + { + if (m_bHasPlayerGlitchOverride[i]) + { + std::string strGlitchName; + for (const auto& pair : g_pGame->GetGlitchNames()) + { + if (pair.second == (eGlitchType)i) + { + strGlitchName = pair.first; + break; + } + } + + if (!strGlitchName.empty()) + { + CBitStream BitStream; + BitStream.pBitStream->Write(GetElementID()); // Source player ID + BitStream.pBitStream->Write(strGlitchName); + BitStream.pBitStream->WriteBit(m_PlayerGlitches[i]); + pSendTo->Send(CLuaPacket(SET_PLAYER_GLITCH_ENABLED, *BitStream.pBitStream)); + } + } + } +} + +void CPlayer::ResetPlayerGlitchOverrides() +{ + for (int i = 0; i < NUM_GLITCHES; i++) + { + if (m_bHasPlayerGlitchOverride[i]) + { + m_bHasPlayerGlitchOverride[i] = false; + + std::string strGlitchName; + for (const auto& pair : g_pGame->GetGlitchNames()) + { + if (pair.second == (eGlitchType)i) + { + strGlitchName = pair.first; + break; + } + } + + if (!strGlitchName.empty()) + { + SendPlayerGlitchState(strGlitchName); + } + } + } +} + CPlayerBitStream::CPlayerBitStream(CPlayer* pPlayer) { pBitStream = g_pNetServer->AllocateNetServerBitStream(pPlayer->GetBitStreamVersion()); diff --git a/Server/mods/deathmatch/logic/CPlayer.h b/Server/mods/deathmatch/logic/CPlayer.h index 97e944b7673..9a53fb87778 100644 --- a/Server/mods/deathmatch/logic/CPlayer.h +++ b/Server/mods/deathmatch/logic/CPlayer.h @@ -262,6 +262,13 @@ class CPlayer final : public CPed, public CClient bool GetTeleported() const noexcept { return m_teleported; } void SetTeleported(bool state) noexcept { m_teleported = state; } + bool SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled); + bool IsPlayerGlitchEnabled(const std::string& strGlitchName) const; + bool HasPlayerGlitchOverride(const std::string& strGlitchName) const; + void SendPlayerGlitchState(const std::string& strGlitchName) const; + void SendAllPlayerGlitchStates(CPlayer* pTarget = nullptr) const; + void ResetPlayerGlitchOverrides(); + protected: bool ReadSpecialData(const int iLine) override { return true; } @@ -342,7 +349,9 @@ class CPlayer final : public CPed, public CClient private: SLightweightSyncData m_lightweightSyncData; - + SFixedArray m_PlayerGlitches; + SFixedArray m_bHasPlayerGlitchOverride; + void WriteCameraModePacket(); void WriteCameraPositionPacket(); diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 150a42d7b56..ac472f29ed0 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -11261,13 +11261,14 @@ bool CStaticFunctionDefinitions::SetPlayerGlitchEnabled(CPlayer* pPlayer, const if (g_pGame->IsGlitch(strGlitchName)) { - CBitStream BitStream; - BitStream.pBitStream->Write(strGlitchName); - BitStream.pBitStream->WriteBit(bEnabled); - pPlayer->Send(CLuaPacket(SET_PLAYER_GLITCH_ENABLED, *BitStream.pBitStream)); - return true; + if (pPlayer->SetPlayerGlitchEnabled(strGlitchName, bEnabled)) + { + + pPlayer->SendPlayerGlitchState(strGlitchName); + return true; + } } - + return false; } @@ -11275,10 +11276,9 @@ bool CStaticFunctionDefinitions::IsPlayerGlitchEnabled(CPlayer* pPlayer, const s { assert(pPlayer); - if (g_pGame->IsGlitch(strGlitchName)) { - bEnabled = g_pGame->IsGlitchEnabled(strGlitchName); + bEnabled = pPlayer->IsPlayerGlitchEnabled(strGlitchName); return true; } return false; diff --git a/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp b/Server/mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.cpp similarity index 68% rename from Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp rename to Server/mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.cpp index dbef2e78a0c..ca2395e72d4 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.cpp @@ -2,16 +2,16 @@ * * PROJECT: Multi Theft Auto * LICENSE: See LICENSE in the top level directory - * FILE: mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.cpp + * FILE: mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.cpp * * Multi Theft Auto is available from https://www.multitheftauto.com/ * *****************************************************************************/ #include "StdInc.h" -#include "CPlayerGlitchStatePacket.h" +#include "CPlayerGlitchRequestPacket.h" -bool CPlayerGlitchStatePacket::Read(NetBitStreamInterface& stream) noexcept +bool CPlayerGlitchRequestPacket::Read(NetBitStreamInterface& stream) noexcept { return stream.Read(m_glitchName) && stream.ReadBit(m_enabled); } \ No newline at end of file diff --git a/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h b/Server/mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.h similarity index 67% rename from Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h rename to Server/mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.h index 3f0e28670cc..8909d89b3f5 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h +++ b/Server/mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.h @@ -2,7 +2,7 @@ * * PROJECT: Multi Theft Auto * LICENSE: See LICENSE in the top level directory - * FILE: mods/deathmatch/logic/packets/CPlayerGlitchStatePacket.h + * FILE: mods/deathmatch/logic/packets/CPlayerGlitchRequestPacket.h * * Multi Theft Auto is available from https://www.multitheftauto.com/ * @@ -14,21 +14,21 @@ #include #include "CPacket.h" -class CPlayerGlitchStatePacket final : public CPacket +class CPlayerGlitchRequestPacket final : public CPacket { public: - CPlayerGlitchStatePacket() noexcept {} + CPlayerGlitchRequestPacket() noexcept {} - ePacketID GetPacketID() const noexcept { return PACKET_ID_PLAYER_GLITCH_STATE; } + ePacketID GetPacketID() const noexcept { return PACKET_ID_PLAYER_GLITCH_REQUEST; } unsigned long GetFlags() const noexcept { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; } virtual ePacketOrdering GetPacketOrdering() const noexcept { return PACKET_ORDERING_DEFAULT; } bool Read(NetBitStreamInterface& stream) noexcept; - std::string GetGlitchName() const noexcept { return m_glitchName; } - bool IsEnabled() const noexcept { return m_enabled; } + const std::string& GetGlitchName() const noexcept { return m_glitchName; } + bool IsEnabled() const noexcept { return m_enabled; } private: std::string m_glitchName; - bool m_enabled; + bool m_enabled{}; }; \ No newline at end of file diff --git a/Shared/mods/deathmatch/logic/Enums.cpp b/Shared/mods/deathmatch/logic/Enums.cpp index beeebcdf256..2d80298bb4a 100644 --- a/Shared/mods/deathmatch/logic/Enums.cpp +++ b/Shared/mods/deathmatch/logic/Enums.cpp @@ -233,5 +233,5 @@ ADD_ENUM1(PACKET_ID_SERVER_INFO_SYNC) ADD_ENUM1(PACKET_ID_DISCORD_JOIN) ADD_ENUM1(PACKET_ID_PLAYER_RESOURCE_START) ADD_ENUM1(PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY) -ADD_ENUM1(PACKET_ID_PLAYER_GLITCH_STATE) +ADD_ENUM1(PACKET_ID_PLAYER_GLITCH_REQUEST) IMPLEMENT_ENUM_END("ePacketID") diff --git a/Shared/sdk/net/Packets.h b/Shared/sdk/net/Packets.h index 8bef027b057..64ef3e736f3 100644 --- a/Shared/sdk/net/Packets.h +++ b/Shared/sdk/net/Packets.h @@ -178,5 +178,5 @@ enum ePacketID PACKET_ID_DISCORD_JOIN, PACKET_ID_PLAYER_RESOURCE_START, PACKET_ID_PLAYER_WORLD_SPECIAL_PROPERTY, - PACKET_ID_PLAYER_GLITCH_STATE + PACKET_ID_PLAYER_GLITCH_REQUEST }; From 3ed511556c909df7310ba8e8e55d9aeb99b55071 Mon Sep 17 00:00:00 2001 From: Symcryptc Date: Sun, 3 Aug 2025 17:41:47 +0300 Subject: [PATCH 4/4] desync 2 --- Client/mods/deathmatch/logic/CClientGame.cpp | 57 ++++++++++--------- Client/mods/deathmatch/logic/CClientGame.h | 6 ++ .../mods/deathmatch/logic/rpc/CWorldRPCs.cpp | 17 +----- Server/mods/deathmatch/logic/CGame.cpp | 13 +---- Server/mods/deathmatch/logic/CPlayer.cpp | 9 ++- 5 files changed, 43 insertions(+), 59 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 1343c962066..d83e619a2fc 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -142,7 +142,8 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) for (int i = 0; i < NUM_GLITCHES; i++) { - m_PlayerGlitches[i] = m_Glitches[i]; + m_PlayerGlitches[i] = false; + m_bHasPlayerGlitchOverride[i] = false; } @@ -6012,20 +6013,8 @@ bool CClientGame::SetGlitchEnabled(unsigned char ucGlitch, bool bEnabled) { m_Glitches[ucGlitch] = bEnabled; - // If player has no override, also update player glitch state to match - // This maintains compatibility with existing behavior - if (m_PlayerGlitches[ucGlitch] == !bEnabled) - m_PlayerGlitches[ucGlitch] = bEnabled; - - // Apply the effective state (player override takes precedence) - bool bEffectiveState = m_PlayerGlitches[ucGlitch]; - - if (ucGlitch == GLITCH_QUICKRELOAD) - g_pMultiplayer->DisableQuickReload(!bEffectiveState); - if (ucGlitch == GLITCH_CLOSEDAMAGE) - g_pMultiplayer->DisableCloseRangeDamage(!bEffectiveState); - if (ucGlitch == GLITCH_VEHICLE_RAPID_STOP) - g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEffectiveState); + // Calculate and apply effective state + ApplyEffectiveGlitchState(ucGlitch); return true; } return false; @@ -6061,7 +6050,6 @@ bool CClientGame::RequestPlayerGlitchEnabled(const std::string& strGlitchName, b bool CClientGame::SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled) { - auto it = m_GlitchNames.find(strGlitchName); if (it == m_GlitchNames.end()) return false; @@ -6070,21 +6058,16 @@ bool CClientGame::SetPlayerGlitchEnabled(const std::string& strGlitchName, bool if (ucGlitch >= NUM_GLITCHES) return false; - - if (m_PlayerGlitches[ucGlitch] == bEnabled) + // Check if state is actually changing + if (m_bHasPlayerGlitchOverride[ucGlitch] && m_PlayerGlitches[ucGlitch] == bEnabled) return true; - + // Set per-player override m_PlayerGlitches[ucGlitch] = bEnabled; + m_bHasPlayerGlitchOverride[ucGlitch] = true; - // apply to game engine? - if (ucGlitch == GLITCH_QUICKRELOAD) - g_pMultiplayer->DisableQuickReload(!bEnabled); - else if (ucGlitch == GLITCH_CLOSEDAMAGE) - g_pMultiplayer->DisableCloseRangeDamage(!bEnabled); - else if (ucGlitch == GLITCH_VEHICLE_RAPID_STOP) - g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEnabled); - + // Apply effective state to game engine + ApplyEffectiveGlitchState(ucGlitch); return true; } @@ -6098,7 +6081,25 @@ bool CClientGame::IsPlayerGlitchEnabled(const std::string& strGlitchName) if (ucGlitch >= NUM_GLITCHES) return false; - return m_PlayerGlitches[ucGlitch]; + if (m_bHasPlayerGlitchOverride[ucGlitch]) + return m_PlayerGlitches[ucGlitch]; + + return m_Glitches[ucGlitch]; +} + +void CClientGame::ApplyEffectiveGlitchState(unsigned char ucGlitch) +{ + if (ucGlitch >= NUM_GLITCHES) + return; + + bool bEffectiveState = m_bHasPlayerGlitchOverride[ucGlitch] ? m_PlayerGlitches[ucGlitch] : m_Glitches[ucGlitch]; + + if (ucGlitch == GLITCH_QUICKRELOAD) + g_pMultiplayer->DisableQuickReload(!bEffectiveState); + else if (ucGlitch == GLITCH_CLOSEDAMAGE) + g_pMultiplayer->DisableCloseRangeDamage(!bEffectiveState); + else if (ucGlitch == GLITCH_VEHICLE_RAPID_STOP) + g_pMultiplayer->SetRapidVehicleStopFixEnabled(!bEffectiveState); } bool CClientGame::SetWorldSpecialProperty(const WorldSpecialProperty property, const bool enabled) noexcept diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index efa891a8fe3..4a4148a29a1 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -420,6 +420,11 @@ class CClientGame bool SetPlayerGlitchEnabled(const std::string& strGlitchName, bool bEnabled); bool IsPlayerGlitchEnabled(const std::string& strGlitchName); +private: + void ApplyEffectiveGlitchState(unsigned char ucGlitch); + +public: + bool SetWorldSpecialProperty(const WorldSpecialProperty property, const bool enabled) noexcept; bool IsWorldSpecialProperty(const WorldSpecialProperty property); @@ -788,6 +793,7 @@ class CClientGame SFixedArray m_Glitches; SFixedArray m_PlayerGlitches; + SFixedArray m_bHasPlayerGlitchOverride; std::map m_GlitchNames; // Clouds Enabled diff --git a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp index 504577d6f1e..56b7f5eb499 100644 --- a/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CWorldRPCs.cpp @@ -244,25 +244,12 @@ void CWorldRPCs::SetGlitchEnabled(NetBitStreamInterface& bitStream) void CWorldRPCs::SetPlayerGlitchEnabled(NetBitStreamInterface& bitStream) { - ElementID sourcePlayerID; SString strGlitchName; bool bEnabled; - // Check if this includes source player ID (for other players' states) - if (bitStream.Read(sourcePlayerID) && bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) + if (bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) { - - return; - } - else - { - - bitStream.Reset(); - if (bitStream.Read(strGlitchName) && bitStream.ReadBit(bEnabled)) - { - - g_pClientGame->SetPlayerGlitchEnabled(strGlitchName, bEnabled); - } + g_pClientGame->SetPlayerGlitchEnabled(strGlitchName, bEnabled); } } diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index bb62065458a..c4856cc95b8 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -1487,19 +1487,10 @@ void CGame::InitialDataStream(CPlayer& Player) CLuaArguments Arguments; Player.CallEvent("onPlayerJoin", Arguments); - // Send existing per-player glitch states from all players + // Send the joining player's own glitch states (if any were set before joining) if (Player.IsJoined()) { - list::const_iterator iter = m_pPlayerManager->IterBegin(); - for (; iter != m_pPlayerManager->IterEnd(); iter++) - { - CPlayer* pOtherPlayer = *iter; - if (pOtherPlayer != &Player && pOtherPlayer->IsJoined()) - { - // Send other players' glitch overrides to the new player - pOtherPlayer->SendAllPlayerGlitchStates(&Player); - } - } + Player.SendAllPlayerGlitchStates(); } marker.Set("onPlayerJoin"); diff --git a/Server/mods/deathmatch/logic/CPlayer.cpp b/Server/mods/deathmatch/logic/CPlayer.cpp index fa376d563f0..b9128af0631 100644 --- a/Server/mods/deathmatch/logic/CPlayer.cpp +++ b/Server/mods/deathmatch/logic/CPlayer.cpp @@ -1228,6 +1228,9 @@ void CPlayer::SendAllPlayerGlitchStates(CPlayer* pTarget) const { CPlayer* pSendTo = pTarget ? pTarget : const_cast(this); + if (pSendTo != this) + return; + for (int i = 0; i < NUM_GLITCHES; i++) { if (m_bHasPlayerGlitchOverride[i]) @@ -1244,11 +1247,7 @@ void CPlayer::SendAllPlayerGlitchStates(CPlayer* pTarget) const if (!strGlitchName.empty()) { - CBitStream BitStream; - BitStream.pBitStream->Write(GetElementID()); // Source player ID - BitStream.pBitStream->Write(strGlitchName); - BitStream.pBitStream->WriteBit(m_PlayerGlitches[i]); - pSendTo->Send(CLuaPacket(SET_PLAYER_GLITCH_ENABLED, *BitStream.pBitStream)); + SendPlayerGlitchState(strGlitchName); } } }