From 8c43dc8173e0a6122d0244b6e68def965c818466 Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 7 Mar 2026 11:18:23 +0000 Subject: [PATCH 1/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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/9] 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"); From bca549bad8a5c5df0764be2d28c6709dfb4e64ca Mon Sep 17 00:00:00 2001 From: Arran Date: Mon, 11 May 2026 22:21:21 +0100 Subject: [PATCH 9/9] Add removeElementData client side without a new RPC --- .../mods/deathmatch/logic/CClientEntity.cpp | 11 +- Client/mods/deathmatch/logic/CClientEntity.h | 2 +- Client/mods/deathmatch/logic/CNetAPI.h | 1 - .../logic/CStaticFunctionDefinitions.cpp | 27 ++-- Server/mods/deathmatch/logic/CElement.cpp | 12 +- Server/mods/deathmatch/logic/CElement.h | 2 +- Server/mods/deathmatch/logic/CGame.cpp | 135 ++++++++++++------ Server/mods/deathmatch/logic/CGame.h | 2 +- .../logic/CPerfStat.RPCPacketUsage.cpp | 1 - .../mods/deathmatch/logic/CRPCFunctions.cpp | 70 --------- Server/mods/deathmatch/logic/CRPCFunctions.h | 2 - .../logic/packets/CCustomDataPacket.cpp | 23 +-- .../logic/packets/CCustomDataPacket.h | 6 +- 13 files changed, 140 insertions(+), 154 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientEntity.cpp b/Client/mods/deathmatch/logic/CClientEntity.cpp index 33b74e9b166..3ea7b77bc40 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.cpp +++ b/Client/mods/deathmatch/logic/CClientEntity.cpp @@ -498,14 +498,14 @@ void CClientEntity::SetCustomData(const CStringName& name, const CLuaArgument& V CallEvent("onClientElementDataChange", Arguments, true); } -void CClientEntity::DeleteCustomData(const CStringName& name) +bool CClientEntity::DeleteCustomData(const CStringName& name) { // Grab the old variable - SCustomData* pData = m_pCustomData->Get(name); - if (pData) + auto* data = m_pCustomData->Get(name); + if (data) { CLuaArgument oldVariable; - oldVariable = pData->Variable; + oldVariable = data->Variable; // Delete the custom data m_pCustomData->Delete(name); @@ -516,7 +516,10 @@ void CClientEntity::DeleteCustomData(const CStringName& name) Arguments.PushArgument(oldVariable); Arguments.PushArgument(CLuaArgument()); // Use nil as the new value to indicate the data has been removed CallEvent("onClientElementDataChange", Arguments, true); + return true; } + + return false; } bool CClientEntity::GetMatrix(CMatrix& matrix) const diff --git a/Client/mods/deathmatch/logic/CClientEntity.h b/Client/mods/deathmatch/logic/CClientEntity.h index 4ddd8baefb2..723da075e7a 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.h +++ b/Client/mods/deathmatch/logic/CClientEntity.h @@ -209,7 +209,7 @@ class CClientEntity : public CClientEntityBase bool GetCustomDataInt(const CStringName& name, int& iOut, bool bInheritData); bool GetCustomDataBool(const CStringName& name, bool& bOut, bool bInheritData); void SetCustomData(const CStringName& name, const CLuaArgument& Variable, bool bSynchronized = true); - void DeleteCustomData(const CStringName& name); + bool DeleteCustomData(const CStringName& name); virtual bool GetMatrix(CMatrix& matrix) const; virtual bool SetMatrix(const CMatrix& matrix); diff --git a/Client/mods/deathmatch/logic/CNetAPI.h b/Client/mods/deathmatch/logic/CNetAPI.h index 714f7c1dc4b..3753650c2a8 100644 --- a/Client/mods/deathmatch/logic/CNetAPI.h +++ b/Client/mods/deathmatch/logic/CNetAPI.h @@ -34,7 +34,6 @@ 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 95a3d54894b..38db42db2a0 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1059,25 +1059,28 @@ bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, CStrin assert(name); assert(name->length() <= MAX_CUSTOMDATA_NAME_LENGTH); - bool isSynced; - CLuaArgument* currentVariable = Entity.GetCustomData(name, false, &isSynced); + if (Entity.IsLocalEntity()) + return Entity.DeleteCustomData(name); + + bool isSynced = false; + auto* currentVariable = Entity.GetCustomData(name, false, &isSynced); if (!currentVariable) return false; - if (isSynced && !Entity.IsLocalEntity()) + if (isSynced) { - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - // Write element ID and name for server-side removal handling - pBitStream->Write(Entity.GetID()); - pBitStream->WriteString(name.ToString()); + auto bitStream = g_pNet->AllocateNetBitStream(); + // Omitting the value reuses the custom data packet for removals without adding another client-to-server RPC. + bitStream->Write(Entity.GetID()); + std::uint16_t nameLength = static_cast(name->length()); + bitStream->WriteCompressed(nameLength); + bitStream->Write(name.ToCString(), nameLength); - // Send RPC and deallocate - g_pClientGame->GetNetAPI()->RPC(REMOVE_ELEMENT_DATA_RPC, pBitStream); - g_pNet->DeallocateNetBitStream(pBitStream); + g_pNet->SendPacket(PACKET_ID_CUSTOM_DATA, bitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(bitStream); } - Entity.DeleteCustomData(name); - return true; + return Entity.DeleteCustomData(name); } bool CStaticFunctionDefinitions::SetElementMatrix(CClientEntity& Entity, const CMatrix& matrix) diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp index 1de1a94942b..fc5858903bb 100644 --- a/Server/mods/deathmatch/logic/CElement.cpp +++ b/Server/mods/deathmatch/logic/CElement.cpp @@ -752,14 +752,14 @@ bool CElement::SetCustomData(const CStringName& name, const CLuaArgument& Variab return true; } -bool CElement::DeleteCustomData(const CStringName& name, CPlayer* pClient) +bool CElement::DeleteCustomData(const CStringName& name, CPlayer* client) { // Grab the old variable - SCustomData* pData = m_CustomData.Get(name); - if (pData) + auto* data = m_CustomData.Get(name); + if (data) { - CLuaArgument oldVariable = pData->Variable; - ESyncType oldSyncType = pData->syncType; + CLuaArgument oldVariable = data->Variable; + ESyncType oldSyncType = data->syncType; // Delete the custom data m_CustomData.Delete(name); @@ -769,7 +769,7 @@ bool CElement::DeleteCustomData(const CStringName& name, CPlayer* pClient) 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, pClient)) + if (!CallEvent("onElementDataChange", Arguments, client)) { // 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 6a1a5d4a848..521246fbcd1 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, CPlayer* pClient = nullptr); + bool DeleteCustomData(const CStringName& name, CPlayer* client = nullptr); void SendAllCustomData(CPlayer* pPlayer); CXMLNode* OutputToXML(CXMLNode* pNode); diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 6bc5889a551..71a086506d2 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -2712,85 +2712,134 @@ void CGame::Packet_LuaEvent(CLuaEventPacket& Packet) } } -void CGame::Packet_CustomData(CCustomDataPacket& Packet) +void CGame::Packet_CustomData(CCustomDataPacket& packet) { // Got a valid source? - CPlayer* pSourcePlayer = Packet.GetSourcePlayer(); - if (pSourcePlayer) + auto* sourcePlayer = packet.GetSourcePlayer(); + if (sourcePlayer) { // Grab the element - ElementID ID = Packet.GetElementID(); - CElement* pElement = CElementIDs::GetElement(ID); - if (pElement) + ElementID elementId = packet.GetElementID(); + auto* element = CElementIDs::GetElement(elementId); + if (element) { // Change the data - const char* szName = Packet.GetName(); - CLuaArgument& Value = Packet.GetValue(); + const char* name = packet.GetName(); + CLuaArgument& value = packet.GetValue(); + const bool isDelete = packet.IsDelete(); // Ignore if the wrong length - if (strlen(szName) > MAX_CUSTOMDATA_NAME_LENGTH) + if (strlen(name) > MAX_CUSTOMDATA_NAME_LENGTH) { - CLogger::ErrorPrintf("Received oversized custom data name from %s (%s)\n", Packet.GetSourcePlayer()->GetNick(), - *SStringX(szName).Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); + CLogger::ErrorPrintf("Received oversized custom data name from %s (%s)\n", sourcePlayer->GetNick(), + *SStringX(name).Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); return; } ESyncType lastSyncType = ESyncType::BROADCAST; eCustomDataClientTrust clientChangesMode{}; - pElement->GetCustomData(szName, false, &lastSyncType, &clientChangesMode); + element->GetCustomData(name, false, &lastSyncType, &clientChangesMode); const bool changesAllowed = clientChangesMode == eCustomDataClientTrust::UNSET ? !m_pMainConfig->IsElementDataWhitelisted() : clientChangesMode == eCustomDataClientTrust::ALLOW; if (!changesAllowed) { - CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", Packet.GetSourcePlayer()->GetNick(), szName); + CLogger::ErrorPrintf("Client trying to change protected element data %s (%s)\n", sourcePlayer->GetNick(), name); CLuaArguments arguments; - arguments.PushElement(pElement); - arguments.PushString(szName); - arguments.PushArgument(Value); - pSourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); + arguments.PushElement(element); + arguments.PushString(name); + arguments.PushArgument(value); + sourcePlayer->CallEvent("onPlayerChangesProtectedData", arguments); return; } - if (pElement->SetCustomData(szName, Value, lastSyncType, pSourcePlayer)) + if (isDelete) { - if (lastSyncType != ESyncType::LOCAL) + if (element->DeleteCustomData(name, sourcePlayer)) { - // Tell our clients to update their data. Send to everyone but the one we got this packet from. - unsigned short usNameLength = static_cast(strlen(szName)); - CBitStream BitStream; - BitStream.pBitStream->WriteCompressed(usNameLength); - BitStream.pBitStream->Write(szName, usNameLength); - Value.WriteToBitStream(*BitStream.pBitStream); - if (lastSyncType == ESyncType::BROADCAST) - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pElement, SET_ELEMENT_DATA, *BitStream.pBitStream), pSourcePlayer); - else - m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(pElement, SET_ELEMENT_DATA, *BitStream.pBitStream), pElement, szName, - pSourcePlayer); + if (lastSyncType != ESyncType::LOCAL) + { + // Tell our clients to remove their data. Send to everyone but the one we got this packet from. + std::uint16_t nameLength = static_cast(strlen(name)); + CBitStream bitStream; + bitStream.pBitStream->WriteCompressed(nameLength); + bitStream.pBitStream->Write(name, nameLength); + bitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) + if (lastSyncType == ESyncType::BROADCAST) + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *bitStream.pBitStream), sourcePlayer); + else + m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *bitStream.pBitStream), element, name, + sourcePlayer); - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(szName, m_pPlayerManager->Count(), - BitStream.pBitStream->GetNumberOfBytesUsed()); + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(name, m_pPlayerManager->Count(), + bitStream.pBitStream->GetNumberOfBytesUsed()); + } + + if (lastSyncType == ESyncType::SUBSCRIBE) + m_pPlayerManager->ClearElementData(element, name); + } + else + { + // Restore the element data on the client, because the server cancelled the change in onElementDataChange. + std::uint16_t nameLength = static_cast(strlen(name)); + CBitStream bitStream; + bitStream.pBitStream->WriteCompressed(nameLength); + bitStream.pBitStream->Write(name, nameLength); + + if (auto* serverValue = element->GetCustomData(name, false)) + { + serverValue->WriteToBitStream(*bitStream.pBitStream); + sourcePlayer->Send(CElementRPCPacket(element, SET_ELEMENT_DATA, *bitStream.pBitStream)); + } + else + { + bitStream.pBitStream->WriteBit(false); + sourcePlayer->Send(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *bitStream.pBitStream)); + } } } else { - // Event was cancelled; sync the authoritative value back to the source player - unsigned short usNameLength = static_cast(strlen(szName)); - CBitStream BitStream; - BitStream.pBitStream->WriteCompressed(usNameLength); - BitStream.pBitStream->Write(szName, usNameLength); - - if (CLuaArgument* pServerValue = pElement->GetCustomData(szName, false)) + if (element->SetCustomData(name, value, lastSyncType, sourcePlayer)) { - pServerValue->WriteToBitStream(*BitStream.pBitStream); - pSourcePlayer->Send(CElementRPCPacket(pElement, SET_ELEMENT_DATA, *BitStream.pBitStream)); + if (lastSyncType != ESyncType::LOCAL) + { + // Tell our clients to update their data. Send to everyone but the one we got this packet from. + std::uint16_t nameLength = static_cast(strlen(name)); + CBitStream bitStream; + bitStream.pBitStream->WriteCompressed(nameLength); + bitStream.pBitStream->Write(name, nameLength); + value.WriteToBitStream(*bitStream.pBitStream); + if (lastSyncType == ESyncType::BROADCAST) + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(element, SET_ELEMENT_DATA, *bitStream.pBitStream), sourcePlayer); + else + m_pPlayerManager->BroadcastOnlySubscribed(CElementRPCPacket(element, SET_ELEMENT_DATA, *bitStream.pBitStream), element, name, + sourcePlayer); + + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(name, m_pPlayerManager->Count(), + bitStream.pBitStream->GetNumberOfBytesUsed()); + } } else { - BitStream.pBitStream->WriteBit(false); - pSourcePlayer->Send(CElementRPCPacket(pElement, REMOVE_ELEMENT_DATA, *BitStream.pBitStream)); + // Restore the element data on the client, because the server cancelled the change in onElementDataChange. + std::uint16_t nameLength = static_cast(strlen(name)); + CBitStream bitStream; + bitStream.pBitStream->WriteCompressed(nameLength); + bitStream.pBitStream->Write(name, nameLength); + + if (auto* serverValue = element->GetCustomData(name, false)) + { + serverValue->WriteToBitStream(*bitStream.pBitStream); + sourcePlayer->Send(CElementRPCPacket(element, SET_ELEMENT_DATA, *bitStream.pBitStream)); + } + else + { + bitStream.pBitStream->WriteBit(false); + sourcePlayer->Send(CElementRPCPacket(element, REMOVE_ELEMENT_DATA, *bitStream.pBitStream)); + } } } } diff --git a/Server/mods/deathmatch/logic/CGame.h b/Server/mods/deathmatch/logic/CGame.h index 9d0ace7b1aa..0330c4e40c0 100644 --- a/Server/mods/deathmatch/logic/CGame.h +++ b/Server/mods/deathmatch/logic/CGame.h @@ -510,7 +510,7 @@ class CGame void Packet_Vehicle_InOut(class CVehicleInOutPacket& Packet); void Packet_VehicleTrailer(class CVehicleTrailerPacket& Packet); void Packet_LuaEvent(class CLuaEventPacket& Packet); - void Packet_CustomData(class CCustomDataPacket& Packet); + void Packet_CustomData(class CCustomDataPacket& packet); void Packet_Voice_Data(class CVoiceDataPacket& Packet); void Packet_Voice_End(class CVoiceEndPacket& Packet); void Packet_CameraSync(class CCameraSyncPacket& Packet); diff --git a/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp b/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp index 9daf426ce8d..cdebcedcc24 100644 --- a/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp +++ b/Server/mods/deathmatch/logic/CPerfStat.RPCPacketUsage.cpp @@ -243,7 +243,6 @@ 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 66c2c084d4d..c95111be6ab 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.cpp +++ b/Server/mods/deathmatch/logic/CRPCFunctions.cpp @@ -58,7 +58,6 @@ 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) @@ -368,72 +367,3 @@ void CRPCFunctions::RequestStealthKill(NetBitStreamInterface& bitStream) } UNCLOCK("NetServerPulse::RPC", "RequestStealthKill"); } - -void CRPCFunctions::RemoveElementData(NetBitStreamInterface& bitStream) -{ - if (!m_pSourcePlayer->IsJoined()) - return; - - CLOCK("NetServerPulse::RPC", "RemoveElementData"); - - 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; - } - - CElement* element = CElementIDs::GetElement(elementId); - if (!element) - { - UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); - return; - } - - const CStringName customDataNameId(customDataName); - ESyncType lastSyncType = ESyncType::BROADCAST; - eCustomDataClientTrust clientChangesMode{}; - element->GetCustomData(customDataNameId, 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.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(customDataNameId, 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, - customDataNameId.ToCString(), m_pSourcePlayer); - - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(customDataName.c_str(), m_pPlayerManager->Count(), - outBitStream.pBitStream->GetNumberOfBytesUsed()); - } - - if (lastSyncType == ESyncType::SUBSCRIBE) - m_pPlayerManager->ClearElementData(element, customDataNameId); - } - - UNCLOCK("NetServerPulse::RPC", "RemoveElementData"); -} diff --git a/Server/mods/deathmatch/logic/CRPCFunctions.h b/Server/mods/deathmatch/logic/CRPCFunctions.h index fc6a908d1ba..20736c5adb6 100644 --- a/Server/mods/deathmatch/logic/CRPCFunctions.h +++ b/Server/mods/deathmatch/logic/CRPCFunctions.h @@ -50,7 +50,6 @@ class CRPCFunctions DECLARE_RPC(KeyBind); DECLARE_RPC(CursorEvent); DECLARE_RPC(RequestStealthKill); - DECLARE_RPC(RemoveElementData); protected: static CPlayer* m_pSourcePlayer; @@ -67,6 +66,5 @@ class CRPCFunctions KEY_BIND, CURSOR_EVENT, REQUEST_STEALTH_KILL, - REMOVE_ELEMENT_DATA_RPC, }; }; diff --git a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp index 064b8f2f99c..b9f8955e5dc 100644 --- a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp @@ -16,6 +16,7 @@ CCustomDataPacket::CCustomDataPacket() { m_szName = NULL; + m_isDelete = false; } CCustomDataPacket::~CCustomDataPacket() @@ -24,26 +25,28 @@ CCustomDataPacket::~CCustomDataPacket() m_szName = NULL; } -bool CCustomDataPacket::Read(NetBitStreamInterface& BitStream) +bool CCustomDataPacket::Read(NetBitStreamInterface& bitStream) { - unsigned short usNameLength; - if (BitStream.Read(m_ElementID) && BitStream.ReadCompressed(usNameLength) && usNameLength > 0 && usNameLength <= MAX_CUSTOMDATA_NAME_LENGTH) + std::uint16_t nameLength; + if (bitStream.Read(m_ElementID) && bitStream.ReadCompressed(nameLength) && nameLength > 0 && nameLength <= MAX_CUSTOMDATA_NAME_LENGTH) { - m_szName = new char[usNameLength + 1]; - if (BitStream.Read(m_szName, usNameLength)) + m_szName = new char[nameLength + 1]; + if (bitStream.Read(m_szName, nameLength)) { - m_szName[usNameLength] = 0; - if (m_Value.ReadFromBitStream(BitStream)) - { + m_szName[nameLength] = 0; + if (m_Value.ReadFromBitStream(bitStream)) return true; - } + + // Clients leave out the value when requesting deletion, so the existing custom-data packet can also serve as RemoveElementData. + m_isDelete = true; + return true; } } return false; } -bool CCustomDataPacket::Write(NetBitStreamInterface& BitStream) const +bool CCustomDataPacket::Write(NetBitStreamInterface& bitStream) const { return true; } diff --git a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h index 922a2cee6ce..ae81a12de23 100644 --- a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h +++ b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h @@ -23,15 +23,17 @@ class CCustomDataPacket final : public CPacket ePacketID GetPacketID() const { return PACKET_ID_CUSTOM_DATA; }; unsigned long GetFlags() const { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; }; - bool Read(NetBitStreamInterface& BitStream); - bool Write(NetBitStreamInterface& BitStream) const; + bool Read(NetBitStreamInterface& bitStream); + bool Write(NetBitStreamInterface& bitStream) const; ElementID GetElementID() { return m_ElementID; } char* GetName() { return m_szName; } CLuaArgument& GetValue() { return m_Value; } + bool IsDelete() const { return m_isDelete; } private: ElementID m_ElementID; char* m_szName; CLuaArgument m_Value; + bool m_isDelete; };