diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp index 6e190abe9f..d8cc25bf7e 100644 --- a/Server/mods/deathmatch/logic/CElement.cpp +++ b/Server/mods/deathmatch/logic/CElement.cpp @@ -709,22 +709,24 @@ bool CElement::GetCustomDataBool(const CStringName& name, bool& bOut, bool bInhe return false; } -void CElement::SetCustomData(const CStringName& name, const CLuaArgument& Variable, ESyncType syncType, CPlayer* pClient, bool bTriggerEvent) +bool CElement::SetCustomData(const CStringName& name, const CLuaArgument& Variable, ESyncType syncType, CPlayer* pClient, bool bTriggerEvent) { assert(name); if (name->length() > MAX_CUSTOMDATA_NAME_LENGTH) { // Don't allow it to be set if the name is too long CLogger::ErrorPrintf("Custom data name too long (%s)\n", *SStringX(name.ToCString()).Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); - return; + return false; } - // Grab the old variable + // Grab the old variable and sync type CLuaArgument oldVariable; + ESyncType oldSyncType = ESyncType::LOCAL; const SCustomData* pData = m_CustomData.Get(name); if (pData) { oldVariable = pData->Variable; + oldSyncType = pData->syncType; } // Set the new data @@ -737,18 +739,27 @@ void CElement::SetCustomData(const CStringName& name, const CLuaArgument& Variab Arguments.PushString(name); Arguments.PushArgument(oldVariable); Arguments.PushArgument(Variable); - CallEvent("onElementDataChange", Arguments, pClient); + if (!CallEvent("onElementDataChange", Arguments, pClient)) + { + // Event was cancelled, restore previous value + if (pData) + m_CustomData.Set(name, oldVariable, oldSyncType); + else + m_CustomData.Delete(name); + return false; + } } + return true; } -void CElement::DeleteCustomData(const CStringName& name) +bool CElement::DeleteCustomData(const CStringName& name) { // Grab the old variable SCustomData* pData = m_CustomData.Get(name); if (pData) { - CLuaArgument oldVariable; - oldVariable = pData->Variable; + CLuaArgument oldVariable = pData->Variable; + ESyncType oldSyncType = pData->syncType; // Delete the custom data m_CustomData.Delete(name); @@ -758,8 +769,15 @@ void 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 - CallEvent("onElementDataChange", Arguments); + if (!CallEvent("onElementDataChange", Arguments)) + { + // Event was cancelled, restore previous value + m_CustomData.Set(name, oldVariable, oldSyncType); + return false; + } + return true; } + return false; } // Used to send the root element data when a player joins diff --git a/Server/mods/deathmatch/logic/CElement.h b/Server/mods/deathmatch/logic/CElement.h index 42e33c8906..17e3a37ea0 100644 --- a/Server/mods/deathmatch/logic/CElement.h +++ b/Server/mods/deathmatch/logic/CElement.h @@ -145,9 +145,9 @@ class CElement bool GetCustomDataInt(const CStringName& name, int& iOut, bool bInheritData); bool GetCustomDataFloat(const CStringName& name, float& fOut, bool bInheritData); bool GetCustomDataBool(const CStringName& name, bool& bOut, bool bInheritData); - void SetCustomData(const CStringName& name, const CLuaArgument& Variable, ESyncType syncType = ESyncType::BROADCAST, CPlayer* pClient = NULL, + bool SetCustomData(const CStringName& name, const CLuaArgument& Variable, ESyncType syncType = ESyncType::BROADCAST, CPlayer* pClient = NULL, bool bTriggerEvent = true); - void DeleteCustomData(const CStringName& name); + bool DeleteCustomData(const CStringName& name); 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 b8fa80d619..afac77f2a4 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -2738,25 +2738,45 @@ void CGame::Packet_CustomData(CCustomDataPacket& Packet) return; } - if (lastSyncType != ESyncType::LOCAL) + if (pElement->SetCustomData(szName, Value, lastSyncType, pSourcePlayer)) { - // Tell our clients to update their data. Send to everyone but the one we got this packet from. + if (lastSyncType != ESyncType::LOCAL) + { + // 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); + + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(szName, m_pPlayerManager->Count(), + BitStream.pBitStream->GetNumberOfBytesUsed()); + } + } + 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); - 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); - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(szName, m_pPlayerManager->Count(), - BitStream.pBitStream->GetNumberOfBytesUsed()); + if (CLuaArgument* pServerValue = pElement->GetCustomData(szName, false)) + { + pServerValue->WriteToBitStream(*BitStream.pBitStream); + pSourcePlayer->Send(CElementRPCPacket(pElement, SET_ELEMENT_DATA, *BitStream.pBitStream)); + } + else + { + BitStream.pBitStream->WriteBit(false); + pSourcePlayer->Send(CElementRPCPacket(pElement, REMOVE_ELEMENT_DATA, *BitStream.pBitStream)); + } } - - pElement->SetCustomData(szName, Value, lastSyncType, pSourcePlayer); } } } diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index ca30ca88d5..bc072f5202 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1017,6 +1017,9 @@ bool CStaticFunctionDefinitions::SetElementData(CElement* pElement, CStringName if (!pCurrentVariable || *pCurrentVariable != Variable || lastSyncType != syncType) { + if (!pElement->SetCustomData(name, Variable, syncType)) + return false; // The server cancelled the change in onElementDataChange + if (syncType != ESyncType::LOCAL) { // Tell our clients to update their data @@ -1037,8 +1040,6 @@ bool CStaticFunctionDefinitions::SetElementData(CElement* pElement, CStringName if (lastSyncType == ESyncType::SUBSCRIBE && syncType != ESyncType::SUBSCRIBE) m_pPlayerManager->ClearElementData(pElement, name); - // Set its custom data - pElement->SetCustomData(name, Variable, syncType); return true; } return false; @@ -1053,6 +1054,9 @@ bool CStaticFunctionDefinitions::RemoveElementData(CElement* pElement, CStringNa // Check it exists if (pElement->GetCustomData(name, false)) { + if (!pElement->DeleteCustomData(name)) + return false; // The server cancelled the change in onElementDataChange + // Tell our clients to update their data unsigned short usNameLength = static_cast(name->length()); CBitStream BitStream; @@ -1064,8 +1068,6 @@ bool CStaticFunctionDefinitions::RemoveElementData(CElement* pElement, CStringNa // Clean up after the data removal m_pPlayerManager->ClearElementData(pElement, name); - // Delete here - pElement->DeleteCustomData(name); return true; }