From 8c43dc8173e0a6122d0244b6e68def965c818466 Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 7 Mar 2026 11:18:23 +0000 Subject: [PATCH 1/8] Add removeElementData on the client removeElementData can now be called on the client which actually deletes (rather than just sets to nil or false) an element data. It also sends a new packet type to the server and syncs it with clients. --- Client/mods/deathmatch/logic/CNetAPI.h | 1 + .../logic/CStaticFunctionDefinitions.cpp | 26 ++++++- .../logic/luadefs/CLuaElementDefs.cpp | 4 +- .../logic/CPerfStat.RPCPacketUsage.cpp | 1 + .../mods/deathmatch/logic/CRPCFunctions.cpp | 72 +++++++++++++++++++ Server/mods/deathmatch/logic/CRPCFunctions.h | 2 + 6 files changed, 103 insertions(+), 3 deletions(-) diff --git a/Client/mods/deathmatch/logic/CNetAPI.h b/Client/mods/deathmatch/logic/CNetAPI.h index d3ed4cec57c..f587d966389 100644 --- a/Client/mods/deathmatch/logic/CNetAPI.h +++ b/Client/mods/deathmatch/logic/CNetAPI.h @@ -34,6 +34,7 @@ enum eServerRPCFunctions KEY_BIND, CURSOR_EVENT, REQUEST_STEALTH_KILL, + REMOVE_ELEMENT_DATA_RPC, }; class CNetAPI diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index e104e137404..2e24143f551 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1062,7 +1062,31 @@ bool CStaticFunctionDefinitions::SetElementData(CClientEntity& Entity, CStringNa bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStringName name) { - // TODO + assert(name); + assert(name->length() <= MAX_CUSTOMDATA_NAME_LENGTH); + + bool isSynced; + CLuaArgument* currentVariable = Entity.GetCustomData(name, false, &isSynced); + if (currentVariable) + { + if (isSynced && !Entity.IsLocalEntity()) + { + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + // Write element ID, name length and name for server-side removal handling + pBitStream->Write(Entity.GetID()); + uint16_t nameLength = static_cast(name->length()); + pBitStream->WriteCompressed(nameLength); + pBitStream->Write(name.ToCString(), nameLength); + + // Send RPC and deallocate + g_pClientGame->GetNetAPI()->RPC(REMOVE_ELEMENT_DATA_RPC, pBitStream); + g_pNet->DeallocateNetBitStream(pBitStream); + } + + Entity.DeleteCustomData(name); + return true; + } + return false; } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index 2fe3401fc7c..ab162fbbc56 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -77,7 +77,7 @@ void CLuaElementDefs::LoadFunctions() {"setElementID", SetElementID}, {"setElementParent", SetElementParent}, {"setElementData", SetElementData}, - // {"removeElementData", RemoveElementData}, TODO Clientside + {"removeElementData", RemoveElementData}, {"setElementMatrix", SetElementMatrix}, {"setElementPosition", SetElementPosition}, {"setElementRotation", SetElementRotation}, @@ -121,7 +121,7 @@ void CLuaElementDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "attach", "attachElements"); lua_classfunction(luaVM, "detach", "detachElements"); lua_classfunction(luaVM, "destroy", "destroyElement"); - + lua_classfunction(luaVM, "removeData", "removeElementData"); // Get functions lua_classfunction(luaVM, "getCollisionsEnabled", "getElementCollisionsEnabled"); lua_classfunction(luaVM, "isWithinColShape", "isElementWithinColShape"); diff --git a/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp b/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp index cdebcedcc24..9daf426ce8d 100644 --- a/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp +++ b/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp @@ -243,6 +243,7 @@ ADD_ENUM1(CRPCFunctions::PLAYER_WEAPON) ADD_ENUM1(CRPCFunctions::KEY_BIND) ADD_ENUM1(CRPCFunctions::CURSOR_EVENT) ADD_ENUM1(CRPCFunctions::REQUEST_STEALTH_KILL) +ADD_ENUM1(CRPCFunctions::REMOVE_ELEMENT_DATA_RPC) IMPLEMENT_ENUM_END("eRPCFunctions") struct SRPCPacketStat diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.cpp b/Server/mods/deathmatch/logic/CRPCFunctions.cpp index 2965dd3871e..6480b42279b 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -19,6 +19,7 @@ #include "CPerfStatManager.h" #include "CKeyBinds.h" #include "CStaticFunctionDefinitions.h" +#include "packets/CElementRPCPacket.h" #include "net/SyncStructures.h" CRPCFunctions* g_pRPCFunctions = NULL; @@ -57,6 +58,7 @@ void CRPCFunctions::AddHandlers() AddHandler(KEY_BIND, KeyBind); AddHandler(CURSOR_EVENT, CursorEvent); AddHandler(REQUEST_STEALTH_KILL, RequestStealthKill); + AddHandler(REMOVE_ELEMENT_DATA_RPC, RemoveElementData); } void CRPCFunctions::AddHandler(unsigned char ucID, pfnRPCHandler Callback) @@ -366,3 +368,73 @@ void CRPCFunctions::RequestStealthKill(NetBitStreamInterface& bitStream) } UNCLOCK("NetServerPulse::RPC", "RequestStealthKill"); } + +void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) +{ + CLOCK("NetServerPulse::RPC", "RemoveElementData"); + + if (!m_pSourcePlayer->IsJoined()) + { + UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); + return; + } + + ElementID elementId; + std::uint16_t nameLength; + if (bitStream.Read(elementId) && bitStream.ReadCompressed(nameLength) && nameLength > 0 && nameLength <= MAX_CUSTOMDATA_NAME_LENGTH) + { + char customDataName[MAX_CUSTOMDATA_NAME_LENGTH + 1]; + if (bitStream.Read(customDataName, nameLength)) + { + customDataName[nameLength] = 0; + + CElement* element = CElementIDs::GetElement(elementId); + if (element) + { + ESyncType lastSyncType = ESyncType::BROADCAST; + eCustomDataClientTrust clientChangesMode{}; + element->GetCustomData(customDataName, false, &lastSyncType, &clientChangesMode); + + const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() + : clientChangesMode == eCustomDataClientTrust::ALLOW; + if (!changesAllowed) + { + CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", m_pSourcePlayer->GetNick(), customDataName); + + CLuaArguments arguments; + arguments.PushElement(element); + arguments.PushString(customDataName); + arguments.PushArgument(CLuaArgument()); + m_pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); + UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); + return; + } + + if (element->DeleteCustomData(customDataName)) + { + if (lastSyncType != ESyncType::LOCAL) + { + CBitStream outBitStream; + outBitStream.pBitStream->WriteCompressed(nameLength); + outBitStream.pBitStream->Write(customDataName, nameLength); + outBitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) + + if (lastSyncType == ESyncType::BROADCAST) + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); + else + m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, + customDataName, m_pSourcePlayer); + + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName, m_pPlayerManager->Count(), + outBitStream.pBitStream->GetNumberOfBytesUsed()); + } + + if (lastSyncType == ESyncType::SUBSCRIBE) + m_pPlayerManager->ClearElementData(element, customDataName); + } + } + } + } + + UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); +} diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.h b/Server/mods/deathmatch/logic/CRPCFunctions.h index 20736c5adb6..fc6a908d1ba 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.h +++ b/Server/mods/deathmatch/logic/CRPCFunctions.h @@ -50,6 +50,7 @@ class CRPCFunctions DECLARE_RPC(KeyBind); DECLARE_RPC(CursorEvent); DECLARE_RPC(RequestStealthKill); + DECLARE_RPC(RemoveElementData); protected: static CPlayer* m_pSourcePlayer; @@ -66,5 +67,6 @@ class CRPCFunctions KEY_BIND, CURSOR_EVENT, REQUEST_STEALTH_KILL, + REMOVE_ELEMENT_DATA_RPC, }; }; From 3077a4ccce3d4e97a30230441ce3cb1e8e7764f7 Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 7 Mar 2026 11:20:54 +0000 Subject: [PATCH 2/8] Fix typo --- Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 2e24143f551..d663458fdbb 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1074,7 +1074,7 @@ bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStrin NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); // Write element ID, name length and name for server-side removal handling pBitStream->Write(Entity.GetID()); - uint16_t nameLength = static_cast(name->length()); + std::uint16_t nameLength = static_cast(name->length()); pBitStream->WriteCompressed(nameLength); pBitStream->Write(name.ToCString(), nameLength); From a8b0b10f154a429e79fc36e653d99d709f9c0f14 Mon Sep 17 00:00:00 2001 From: Arran Date: Mon, 9 Mar 2026 09:46:50 +0000 Subject: [PATCH 3/8] clang format fix --- Server/mods/deathmatch/logic/CRPCFunctions.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.cpp b/Server/mods/deathmatch/logic/CRPCFunctions.cpp index 6480b42279b..14b5d6a66f4 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -379,8 +379,8 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) return; } - ElementID elementId; - std::uint16_t nameLength; + ElementID elementId; + std::uint16_t nameLength; if (bitStream.Read(elementId) && bitStream.ReadCompressed(nameLength) && nameLength > 0 && nameLength <= MAX_CUSTOMDATA_NAME_LENGTH) { char customDataName[MAX_CUSTOMDATA_NAME_LENGTH + 1]; @@ -396,7 +396,7 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) element->GetCustomData(customDataName, false, &lastSyncType, &clientChangesMode); const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() - : clientChangesMode == eCustomDataClientTrust::ALLOW; + : clientChangesMode == eCustomDataClientTrust::ALLOW; if (!changesAllowed) { CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", m_pSourcePlayer->GetNick(), customDataName); @@ -423,10 +423,10 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); else m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, - customDataName, m_pSourcePlayer); + customDataName, m_pSourcePlayer); CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName, m_pPlayerManager->Count(), - outBitStream.pBitStream->GetNumberOfBytesUsed()); + outBitStream.pBitStream->GetNumberOfBytesUsed()); } if (lastSyncType == ESyncType::SUBSCRIBE) From 6fcbf6164d20d29bb3ae37a70f492a9f5533e18f Mon Sep 17 00:00:00 2001 From: Arran Date: Tue, 10 Mar 2026 13:29:24 +0000 Subject: [PATCH 4/8] Fix onElementDataChange not getting client --- Server/mods/deathmatch/logic/CElement.cpp | 4 ++-- Server/mods/deathmatch/logic/CElement.h | 2 +- Server/mods/deathmatch/logic/CRPCFunctions.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp index c30ddf5640b..9c470a0e02b 100644 --- a/Server/mods/deathmatch/logic/CElement.cpp +++ b/Server/mods/deathmatch/logic/CElement.cpp @@ -753,7 +753,7 @@ bool CElement::SetCustomData(const CStringName& name, const CLuaArgument& Variab return true; } -bool CElement::DeleteCustomData(const CStringName& name) +bool CElement::DeleteCustomData(const CStringName& name, CPlayer* pClient) { // Grab the old variable SCustomData* pData = m_CustomData.Get(name); @@ -770,7 +770,7 @@ bool CElement::DeleteCustomData(const CStringName& name) Arguments.PushString(name); Arguments.PushArgument(oldVariable); Arguments.PushArgument(CLuaArgument()); // Use nil as the new value to indicate the data has been removed - if (!CallEvent("onElementDataChange", Arguments)) + if (!CallEvent("onElementDataChange", Arguments, pClient)) { // Event was cancelled, restore previous value m_CustomData.Set(name, oldVariable, oldSyncType); diff --git a/Server/mods/deathmatch/logic/CElement.h b/Server/mods/deathmatch/logic/CElement.h index f607861b0fe..6a1a5d4a848 100644 --- a/Server/mods/deathmatch/logic/CElement.h +++ b/Server/mods/deathmatch/logic/CElement.h @@ -148,7 +148,7 @@ class CElement bool GetCustomDataBool(const CStringName& name, bool& bOut, bool bInheritData); bool SetCustomData(const CStringName& name, const CLuaArgument& Variable, ESyncType syncType = ESyncType::BROADCAST, CPlayer* pClient = NULL, bool bTriggerEvent = true); - bool DeleteCustomData(const CStringName& name); + bool DeleteCustomData(const CStringName& name, CPlayer* pClient = nullptr); void SendAllCustomData(CPlayer* pPlayer); CXMLNode* OutputToXML(CXMLNode* pNode); diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.cpp b/Server/mods/deathmatch/logic/CRPCFunctions.cpp index 14b5d6a66f4..687cab84fce 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -410,7 +410,7 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) return; } - if (element->DeleteCustomData(customDataName)) + if (element->DeleteCustomData(customDataName, m_pSourcePlayer)) { if (lastSyncType != ESyncType::LOCAL) { From de3a2d3dbd950a445b15a86b9ea697cd300ba466 Mon Sep 17 00:00:00 2001 From: Arran Date: Thu, 9 Apr 2026 12:27:31 +0100 Subject: [PATCH 5/8] Change to writeString --- .../logic/CStaticFunctionDefinitions.cpp | 4 +- .../mods/deathmatch/logic/CRPCFunctions.cpp | 85 +++++++++---------- 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index d663458fdbb..518665f66f9 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1074,9 +1074,7 @@ bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStrin NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); // Write element ID, name length and name for server-side removal handling pBitStream->Write(Entity.GetID()); - std::uint16_t nameLength = static_cast(name->length()); - pBitStream->WriteCompressed(nameLength); - pBitStream->Write(name.ToCString(), nameLength); + pBitStream->WriteString(name.ToCString()); // Send RPC and deallocate g_pClientGame->GetNetAPI()->RPC(REMOVE_ELEMENT_DATA_RPC, pBitStream); diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.cpp b/Server/mods/deathmatch/logic/CRPCFunctions.cpp index 687cab84fce..3f306d58cdb 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -379,59 +379,54 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) return; } - ElementID elementId; - std::uint16_t nameLength; - if (bitStream.Read(elementId) && bitStream.ReadCompressed(nameLength) && nameLength > 0 && nameLength <= MAX_CUSTOMDATA_NAME_LENGTH) + ElementID elementId; + std::string customDataName; + if (bitStream.Read(elementId) && bitStream.ReadString(customDataName) && !customDataName.empty() && customDataName.length() <= MAX_CUSTOMDATA_NAME_LENGTH) { - char customDataName[MAX_CUSTOMDATA_NAME_LENGTH + 1]; - if (bitStream.Read(customDataName, nameLength)) + CElement* element = CElementIDs::GetElement(elementId); + if (element) { - customDataName[nameLength] = 0; + ESyncType lastSyncType = ESyncType::BROADCAST; + eCustomDataClientTrust clientChangesMode{}; + element->GetCustomData(customDataName.c_str(), false, &lastSyncType, &clientChangesMode); - CElement* element = CElementIDs::GetElement(elementId); - if (element) + const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() + : clientChangesMode == eCustomDataClientTrust::ALLOW; + if (!changesAllowed) { - ESyncType lastSyncType = ESyncType::BROADCAST; - eCustomDataClientTrust clientChangesMode{}; - element->GetCustomData(customDataName, false, &lastSyncType, &clientChangesMode); + CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", m_pSourcePlayer->GetNick(), customDataName.c_str()); + + CLuaArguments arguments; + arguments.PushElement(element); + arguments.PushString(customDataName.c_str()); + arguments.PushArgument(CLuaArgument()); + m_pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); + UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); + return; + } - const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() - : clientChangesMode == eCustomDataClientTrust::ALLOW; - if (!changesAllowed) + if (element->DeleteCustomData(customDataName.c_str(), m_pSourcePlayer)) + { + if (lastSyncType != ESyncType::LOCAL) { - CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", m_pSourcePlayer->GetNick(), customDataName); - - CLuaArguments arguments; - arguments.PushElement(element); - arguments.PushString(customDataName); - arguments.PushArgument(CLuaArgument()); - m_pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); - UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); - return; + CBitStream outBitStream; + std::uint16_t nameLength = static_cast(customDataName.length()); + outBitStream.pBitStream->WriteCompressed(nameLength); + outBitStream.pBitStream->Write(customDataName.c_str(), nameLength); + outBitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) + + if (lastSyncType == ESyncType::BROADCAST) + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); + else + m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, + customDataName.c_str(), m_pSourcePlayer); + + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName.c_str(), m_pPlayerManager->Count(), + outBitStream.pBitStream->GetNumberOfBytesUsed()); } - if (element->DeleteCustomData(customDataName, m_pSourcePlayer)) - { - if (lastSyncType != ESyncType::LOCAL) - { - CBitStream outBitStream; - outBitStream.pBitStream->WriteCompressed(nameLength); - outBitStream.pBitStream->Write(customDataName, nameLength); - outBitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) - - if (lastSyncType == ESyncType::BROADCAST) - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); - else - m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, - customDataName, m_pSourcePlayer); - - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName, m_pPlayerManager->Count(), - outBitStream.pBitStream->GetNumberOfBytesUsed()); - } - - if (lastSyncType == ESyncType::SUBSCRIBE) - m_pPlayerManager->ClearElementData(element, customDataName); - } + if (lastSyncType == ESyncType::SUBSCRIBE) + m_pPlayerManager->ClearElementData(element, customDataName.c_str()); } } } From e0b87f92aec3171bbbbf98e3bff3f09f5bd527ca Mon Sep 17 00:00:00 2001 From: Arran Date: Thu, 9 Apr 2026 12:36:49 +0100 Subject: [PATCH 6/8] Add early returns --- .../logic/CStaticFunctionDefinitions.cpp | 28 +++--- .../mods/deathmatch/logic/CRPCFunctions.cpp | 95 ++++++++++--------- 2 files changed, 62 insertions(+), 61 deletions(-) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 518665f66f9..d20bfa99242 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1067,25 +1067,23 @@ bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStrin bool isSynced; CLuaArgument* currentVariable = Entity.GetCustomData(name, false, &isSynced); - if (currentVariable) - { - if (isSynced && !Entity.IsLocalEntity()) - { - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - // Write element ID, name length and name for server-side removal handling - pBitStream->Write(Entity.GetID()); - pBitStream->WriteString(name.ToCString()); + if (!currentVariable) + return false; - // Send RPC and deallocate - g_pClientGame->GetNetAPI()->RPC(REMOVE_ELEMENT_DATA_RPC, pBitStream); - g_pNet->DeallocateNetBitStream(pBitStream); - } + if (isSynced && !Entity.IsLocalEntity()) + { + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + // Write element ID, name length and name for server-side removal handling + pBitStream->Write(Entity.GetID()); + pBitStream->WriteString(name.ToCString()); - Entity.DeleteCustomData(name); - return true; + // Send RPC and deallocate + g_pClientGame->GetNetAPI()->RPC(REMOVE_ELEMENT_DATA_RPC, pBitStream); + g_pNet->DeallocateNetBitStream(pBitStream); } - return false; + Entity.DeleteCustomData(name); + return true; } bool CStaticFunctionDefinitions::SetElementMatrix(CClientEntity& Entity, const CMatrix& matrix) diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.cpp b/Server/mods/deathmatch/logic/CRPCFunctions.cpp index 3f306d58cdb..c83059df12e 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -371,64 +371,67 @@ void CRPCFunctions::RequestStealthKill(NetBitStreamInterface& bitStream) void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) { + if (!m_pSourcePlayer->IsJoined()) + return; + CLOCK("NetServerPulse::RPC", "RemoveElementData"); - if (!m_pSourcePlayer->IsJoined()) + ElementID elementId; + std::string customDataName; + if (!bitStream.Read(elementId) || !bitStream.ReadString(customDataName) || customDataName.empty() || customDataName.length() > MAX_CUSTOMDATA_NAME_LENGTH) { UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); return; } - ElementID elementId; - std::string customDataName; - if (bitStream.Read(elementId) && bitStream.ReadString(customDataName) && !customDataName.empty() && customDataName.length() <= MAX_CUSTOMDATA_NAME_LENGTH) + CElement* element = CElementIDs::GetElement(elementId); + if (!element) { - CElement* element = CElementIDs::GetElement(elementId); - if (element) - { - ESyncType lastSyncType = ESyncType::BROADCAST; - eCustomDataClientTrust clientChangesMode{}; - element->GetCustomData(customDataName.c_str(), false, &lastSyncType, &clientChangesMode); + UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); + return; + } - const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() - : clientChangesMode == eCustomDataClientTrust::ALLOW; - if (!changesAllowed) - { - CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", m_pSourcePlayer->GetNick(), customDataName.c_str()); - - CLuaArguments arguments; - arguments.PushElement(element); - arguments.PushString(customDataName.c_str()); - arguments.PushArgument(CLuaArgument()); - m_pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); - UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); - return; - } + ESyncType lastSyncType = ESyncType::BROADCAST; + eCustomDataClientTrust clientChangesMode{}; + element->GetCustomData(customDataName.c_str(), false, &lastSyncType, &clientChangesMode); - if (element->DeleteCustomData(customDataName.c_str(), m_pSourcePlayer)) - { - if (lastSyncType != ESyncType::LOCAL) - { - CBitStream outBitStream; - std::uint16_t nameLength = static_cast(customDataName.length()); - outBitStream.pBitStream->WriteCompressed(nameLength); - outBitStream.pBitStream->Write(customDataName.c_str(), nameLength); - outBitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) - - if (lastSyncType == ESyncType::BROADCAST) - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); - else - m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, - customDataName.c_str(), m_pSourcePlayer); - - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName.c_str(), m_pPlayerManager->Count(), - outBitStream.pBitStream->GetNumberOfBytesUsed()); - } + const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() + : clientChangesMode == eCustomDataClientTrust::ALLOW; + if (!changesAllowed) + { + CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", m_pSourcePlayer->GetNick(), customDataName.c_str()); - if (lastSyncType == ESyncType::SUBSCRIBE) - m_pPlayerManager->ClearElementData(element, customDataName.c_str()); - } + CLuaArguments arguments; + arguments.PushElement(element); + arguments.PushString(customDataName.c_str()); + arguments.PushArgument(CLuaArgument()); + m_pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); + UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); + return; + } + + if (element->DeleteCustomData(customDataName.c_str(), m_pSourcePlayer)) + { + if (lastSyncType != ESyncType::LOCAL) + { + CBitStream outBitStream; + std::uint16_t nameLength = static_cast(customDataName.length()); + outBitStream.pBitStream->WriteCompressed(nameLength); + outBitStream.pBitStream->Write(customDataName.c_str(), nameLength); + outBitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) + + if (lastSyncType == ESyncType::BROADCAST) + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); + else + m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, + customDataName.c_str(), m_pSourcePlayer); + + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName.c_str(), m_pPlayerManager->Count(), + outBitStream.pBitStream->GetNumberOfBytesUsed()); } + + if (lastSyncType == ESyncType::SUBSCRIBE) + m_pPlayerManager->ClearElementData(element, customDataName.c_str()); } UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); From c57ecbdd4ec454567df3b5caa9894e903844aeeb Mon Sep 17 00:00:00 2001 From: Arran Date: Thu, 9 Apr 2026 13:36:22 +0100 Subject: [PATCH 7/8] Fix typo --- Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index d20bfa99242..363bde52b4e 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1073,7 +1073,7 @@ bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStrin if (isSynced && !Entity.IsLocalEntity()) { NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - // Write element ID, name length and name for server-side removal handling + // Write element ID and name for server-side removal handling pBitStream->Write(Entity.GetID()); pBitStream->WriteString(name.ToCString()); From 5095e130d7893fd1a52adbe0daf3d3db71bf3081 Mon Sep 17 00:00:00 2001 From: Arran Date: Thu, 9 Apr 2026 15:15:45 +0100 Subject: [PATCH 8/8] String changes --- .../mods/deathmatch/logic/CStaticFunctionDefinitions.cpp | 2 +- Server/mods/deathmatch/logic/CRPCFunctions.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 363bde52b4e..744f4a080c7 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1075,7 +1075,7 @@ bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStrin NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); // Write element ID and name for server-side removal handling pBitStream->Write(Entity.GetID()); - pBitStream->WriteString(name.ToCString()); + pBitStream->WriteString(name.ToString()); // Send RPC and deallocate g_pClientGame->GetNetAPI()->RPC(REMOVE_ELEMENT_DATA_RPC, pBitStream); diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.cpp b/Server/mods/deathmatch/logic/CRPCFunctions.cpp index c83059df12e..66c2c084d4d 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -391,9 +391,10 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) return; } + const CStringName customDataNameId(customDataName); ESyncType lastSyncType = ESyncType::BROADCAST; eCustomDataClientTrust clientChangesMode{}; - element->GetCustomData(customDataName.c_str(), false, &lastSyncType, &clientChangesMode); + element->GetCustomData(customDataNameId, false, &lastSyncType, &clientChangesMode); const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !g_pGame->GetConfig()->IsElementDataWhitelisted() : clientChangesMode == eCustomDataClientTrust::ALLOW; @@ -410,7 +411,7 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) return; } - if (element->DeleteCustomData(customDataName.c_str(), m_pSourcePlayer)) + if (element->DeleteCustomData(customDataNameId, m_pSourcePlayer)) { if (lastSyncType != ESyncType::LOCAL) { @@ -424,14 +425,14 @@ void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), m_pSourcePlayer); else m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *outBitStream.pBitStream), element, - customDataName.c_str(), m_pSourcePlayer); + customDataNameId.ToCString(), m_pSourcePlayer); CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName.c_str(), m_pPlayerManager->Count(), outBitStream.pBitStream->GetNumberOfBytesUsed()); } if (lastSyncType == ESyncType::SUBSCRIBE) - m_pPlayerManager->ClearElementData(element, customDataName.c_str()); + m_pPlayerManager->ClearElementData(element, customDataNameId); } UNCLOCK("NetServerPulse::RPC", "RemoveElementData");