diff --git a/Client/mods/deathmatch/logic/CClientEntity.cpp b/Client/mods/deathmatch/logic/CClientEntity.cpp index 849f218e25..d377d5bc76 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.cpp +++ b/Client/mods/deathmatch/logic/CClientEntity.cpp @@ -279,12 +279,10 @@ void CClientEntity::SetID(ElementID ID) } } -CLuaArgument* CClientEntity::GetCustomData(const char* szName, bool bInheritData, bool* pbIsSynced) +const CLuaArgument* CClientEntity::GetCustomData(const SString& strName, bool bInheritData, bool* pbIsSynced) { - assert(szName); - // Grab it and return a pointer to the variable - SCustomData* pData = m_pCustomData->Get(szName); + const SCustomData* pData = m_pCustomData->Get(strName); if (pData) { if (pbIsSynced) @@ -295,22 +293,22 @@ CLuaArgument* CClientEntity::GetCustomData(const char* szName, bool bInheritData // If none, try returning parent's custom data if (bInheritData && m_pParent) { - return m_pParent->GetCustomData(szName, true, pbIsSynced); + return m_pParent->GetCustomData(strName, true, pbIsSynced); } // None available - return NULL; + return {}; } CLuaArguments* CClientEntity::GetAllCustomData(CLuaArguments* table) { assert(table); - for (auto it = m_pCustomData->IterBegin(); it != m_pCustomData->IterEnd(); it++) + for (const auto& [key, data] : m_pCustomData->GetData()) { - table->PushString(it->first); // key - table->PushArgument(it->second.Variable); // value - } + table->PushString(key); // key + table->PushArgument(data.Variable); // value + } return table; } @@ -318,7 +316,7 @@ CLuaArguments* CClientEntity::GetAllCustomData(CLuaArguments* table) bool CClientEntity::GetCustomDataString(const char* szName, SString& strOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -353,7 +351,7 @@ bool CClientEntity::GetCustomDataString(const char* szName, SString& strOut, boo bool CClientEntity::GetCustomDataInt(const char* szName, int& iOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -391,7 +389,7 @@ bool CClientEntity::GetCustomDataInt(const char* szName, int& iOut, bool bInheri bool CClientEntity::GetCustomDataFloat(const char* szName, float& fOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -418,7 +416,7 @@ bool CClientEntity::GetCustomDataFloat(const char* szName, float& fOut, bool bIn bool CClientEntity::GetCustomDataBool(const char* szName, bool& bOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -470,54 +468,59 @@ bool CClientEntity::GetCustomDataBool(const char* szName, bool& bOut, bool bInhe return false; } -void CClientEntity::SetCustomData(const char* szName, const CLuaArgument& Variable, bool bSynchronized) +bool CClientEntity::SetCustomData(SString&& strName, CLuaArgument&& Variable, bool bSynchronized, bool bSendPacket) { - assert(szName); - if (strlen(szName) > 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)", *SStringX(szName).Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); - return; - } + // SCustomData is 64(88 in debug) bytes long. A static storage can be more efficient. + static SCustomData oldValue; - // Grab the old variable - CLuaArgument oldVariable; - SCustomData* pData = m_pCustomData->Get(szName); - if (pData) + if (const auto result = m_pCustomData->Set(std::move(strName), std::move(Variable), bSynchronized, &oldValue)) { - oldVariable = pData->Variable; - } + const auto& newName = result.GetName(); + const auto& newValue = result.GetData(); - // Set the new data - m_pCustomData->Set(szName, Variable, bSynchronized); + // Trigger the onClientElementDataChange event on us + CLuaArguments Arguments; + Arguments.PushString(newName); + Arguments.PushArgumentWeak(&oldValue.Variable); + Arguments.PushArgumentWeak(&newValue.Variable); + CallEvent("onClientElementDataChange", Arguments, true); - // Trigger the onClientElementDataChange event on us - CLuaArguments Arguments; - Arguments.PushString(szName); - Arguments.PushArgument(oldVariable); - Arguments.PushArgument(Variable); - CallEvent("onClientElementDataChange", Arguments, true); + if (bSendPacket && bSynchronized && !IsLocalEntity()) + { + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + // Write element ID, name length and the name. Also write the variable. + pBitStream->Write(GetID()); + const unsigned short usNameLength = static_cast(newName.length()); + pBitStream->WriteCompressed(usNameLength); + pBitStream->Write(newName.c_str(), usNameLength); + newValue.Variable.WriteToBitStream(*pBitStream); + + // Send the packet and deallocate + g_pNet->SendPacket(PACKET_ID_CUSTOM_DATA, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(pBitStream); + } + + return true; + } + + return false; } -void CClientEntity::DeleteCustomData(const char* szName) +void CClientEntity::DeleteCustomData(const SString& strName) { - // Grab the old variable - SCustomData* pData = m_pCustomData->Get(szName); - if (pData) - { - CLuaArgument oldVariable; - oldVariable = pData->Variable; - - // Delete the custom data - m_pCustomData->Delete(szName); + // SCustomData is 64(88 in debug) bytes long. A static storage can be more efficient. + static SCustomData oldData; + // Delete the custom data + if (m_pCustomData->Delete(strName, &oldData)) + { // Trigger the onClientElementDataChange event on us CLuaArguments Arguments; - Arguments.PushString(szName); - Arguments.PushArgument(oldVariable); - Arguments.PushArgument(CLuaArgument()); // Use nil as the new value to indicate the data has been removed + Arguments.PushString(strName); + Arguments.PushArgumentWeak(&oldData.Variable); + Arguments.PushArgument(CLuaArgument{}); // Use nil as the new value to indicate the data has been removed CallEvent("onClientElementDataChange", Arguments, true); - } + } } bool CClientEntity::GetMatrix(CMatrix& matrix) const diff --git a/Client/mods/deathmatch/logic/CClientEntity.h b/Client/mods/deathmatch/logic/CClientEntity.h index 0d84526d8b..cd20fff79a 100644 --- a/Client/mods/deathmatch/logic/CClientEntity.h +++ b/Client/mods/deathmatch/logic/CClientEntity.h @@ -201,14 +201,14 @@ class CClientEntity : public CClientEntityBase void SetID(ElementID ID); CCustomData* GetCustomDataPointer() { return m_pCustomData; } - CLuaArgument* GetCustomData(const char* szName, bool bInheritData, bool* pbIsSynced = nullptr); + const CLuaArgument* GetCustomData(const SString& strName, bool bInheritData, bool* pbIsSynced = nullptr); CLuaArguments* GetAllCustomData(CLuaArguments* table); bool GetCustomDataString(const char* szKey, SString& strOut, bool bInheritData); bool GetCustomDataFloat(const char* szKey, float& fOut, bool bInheritData); bool GetCustomDataInt(const char* szKey, int& iOut, bool bInheritData); bool GetCustomDataBool(const char* szKey, bool& bOut, bool bInheritData); - void SetCustomData(const char* szName, const CLuaArgument& Variable, bool bSynchronized = true); - void DeleteCustomData(const char* szName); + bool SetCustomData(SString&& strName, CLuaArgument&& Variable, bool bSynchronized = true, bool bSendPacket = false); + void DeleteCustomData(const SString& strName); virtual bool GetMatrix(CMatrix& matrix) const; virtual bool SetMatrix(const CMatrix& matrix); diff --git a/Client/mods/deathmatch/logic/CCustomData.cpp b/Client/mods/deathmatch/logic/CCustomData.cpp index 8f44103078..0ee6a8c25b 100644 --- a/Client/mods/deathmatch/logic/CCustomData.cpp +++ b/Client/mods/deathmatch/logic/CCustomData.cpp @@ -14,52 +14,55 @@ void CCustomData::Copy(CCustomData* pCustomData) { - std::map::const_iterator iter = pCustomData->IterBegin(); - for (; iter != pCustomData->IterEnd(); iter++) - { - Set(iter->first.c_str(), iter->second.Variable); - } + for (const auto& [key, data] : pCustomData->GetData()) + Set(SString(key), CLuaArgument(data.Variable)); } -SCustomData* CCustomData::Get(const char* szName) +const SCustomData* CCustomData::Get(const SString& strName) { - assert(szName); - - std::map::iterator it = m_Data.find(szName); - if (it != m_Data.end()) + if (auto it = m_Data.find(strName); it != m_Data.end()) return &it->second; - return NULL; + return {}; } -void CCustomData::Set(const char* szName, const CLuaArgument& Variable, bool bSynchronized) +SCustomDataResult CCustomData::Set(SString&& strName, CLuaArgument&& Variable, bool bSynchronized, SCustomData* oldValue) { - assert(szName); - - // Grab the item with the given name - SCustomData* pData = Get(szName); - if (pData) - { - // Update existing - pData->Variable = Variable; - pData->bSynchronized = bSynchronized; - } - else + if (strName.length() > MAX_CUSTOMDATA_NAME_LENGTH) { - // Add new - SCustomData newData; - newData.Variable = Variable; - newData.bSynchronized = bSynchronized; - m_Data[szName] = newData; + // Don't allow it to be set if the name is too long + CLogger::ErrorPrintf("Custom data name too long (%s)", *strName.Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); + return {}; } + + auto iter = m_Data.try_emplace(std::move(strName)).first; + SCustomData& pCurrentVariable = iter->second; + + if (pCurrentVariable.Variable.IsEmpty() || pCurrentVariable.bSynchronized != bSynchronized || pCurrentVariable.Variable != Variable ) + { + // Save the old variable + if (oldValue) + *oldValue = std::move(pCurrentVariable); + + // Set the new data + pCurrentVariable.Variable = std::move(Variable); + pCurrentVariable.bSynchronized = bSynchronized; + + return SCustomDataResult(iter); + } + + return {}; } -bool CCustomData::Delete(const char* szName) +bool CCustomData::Delete(const SString& strName, SCustomData* pOldData) { // Find the item and delete it - std::map::iterator it = m_Data.find(szName); + auto it = m_Data.find(strName); if (it != m_Data.end()) { + if (pOldData) + *pOldData = it->second; + m_Data.erase(it); return true; } diff --git a/Client/mods/deathmatch/logic/CCustomData.h b/Client/mods/deathmatch/logic/CCustomData.h index 01e5eb81cd..26485460fd 100644 --- a/Client/mods/deathmatch/logic/CCustomData.h +++ b/Client/mods/deathmatch/logic/CCustomData.h @@ -17,7 +17,28 @@ struct SCustomData { CLuaArgument Variable; - bool bSynchronized; + bool bSynchronized{true}; +}; + +struct SCustomDataResult +{ + using iterator = std::unordered_map::iterator; + + SCustomDataResult() = default; + + SCustomDataResult(const iterator& iter) : + Iter(iter), + bValid(true) + { + } + + const SString& GetName() const { return Iter->first; } + const SCustomData& GetData() const { return Iter->second; } + + operator bool() const { return bValid; } + + iterator Iter; + bool bValid{}; }; class CCustomData @@ -25,14 +46,13 @@ class CCustomData public: void Copy(CCustomData* pCustomData); - SCustomData* Get(const char* szName); - void Set(const char* szName, const CLuaArgument& Variable, bool bSynchronized = true); + const SCustomData* Get(const SString& strName); + SCustomDataResult Set(SString&& strName, CLuaArgument&& Variable, bool bSynchronized = true, SCustomData* oldValue = {}); - bool Delete(const char* szName); + bool Delete(const SString& strName, SCustomData* pOldData = {}); - std::map::const_iterator IterBegin() { return m_Data.begin(); } - std::map::const_iterator IterEnd() { return m_Data.end(); } + const std::unordered_map& GetData() const { return m_Data; } private: - std::map m_Data; + std::unordered_map m_Data; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index dd95351ed6..bbdebf89a7 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -2875,7 +2875,7 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) CLuaArgument Argument; Argument.ReadFromBitStream(bitStream); - pCustomData->Set(strName, Argument); + pCustomData->Set(std::move(strName), std::move(Argument)); } else { diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 0bda456827..3bb48934f2 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1017,38 +1017,6 @@ bool CStaticFunctionDefinitions::SetElementID(CClientEntity& Entity, const char* return false; } -bool CStaticFunctionDefinitions::SetElementData(CClientEntity& Entity, const char* szName, CLuaArgument& Variable, bool bSynchronize) -{ - assert(szName); - assert(strlen(szName) <= MAX_CUSTOMDATA_NAME_LENGTH); - - bool bIsSynced; - CLuaArgument* pCurrentVariable = Entity.GetCustomData(szName, false, &bIsSynced); - if (!pCurrentVariable || Variable != *pCurrentVariable || bIsSynced != bSynchronize) - { - if (bSynchronize && !Entity.IsLocalEntity()) - { - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - // Write element ID, name length and the name. Also write the variable. - pBitStream->Write(Entity.GetID()); - unsigned short usNameLength = static_cast(strlen(szName)); - pBitStream->WriteCompressed(usNameLength); - pBitStream->Write(szName, usNameLength); - Variable.WriteToBitStream(*pBitStream); - - // Send the packet and deallocate - g_pNet->SendPacket(PACKET_ID_CUSTOM_DATA, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(pBitStream); - } - - // Set its custom data - Entity.SetCustomData(szName, Variable, bSynchronize); - return true; - } - - return false; -} - bool CStaticFunctionDefinitions::RemoveElementData(CClientEntity& Entity, const char* szName) { // TODO diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h index 2ffe1ff0a5..c1f974f0fe 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -81,7 +81,6 @@ class CStaticFunctionDefinitions static CClientDummy* CreateElement(CResource& Resource, const char* szTypeName, const char* szID); static bool DestroyElement(CClientEntity& Entity); static bool SetElementID(CClientEntity& Entity, const char* szID); - static bool SetElementData(CClientEntity& Entity, const char* szName, CLuaArgument& Variable, bool bSynchronize); static bool RemoveElementData(CClientEntity& Entity, const char* szName); static bool SetElementMatrix(CClientEntity& Entity, const CMatrix& matrix); static bool SetElementPosition(CClientEntity& Entity, const CVector& vecPosition, bool bWarp = true); diff --git a/Client/mods/deathmatch/logic/lua/CLuaArgument.cpp b/Client/mods/deathmatch/logic/lua/CLuaArgument.cpp index 3a5f50df3f..ad3af3d752 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaArgument.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaArgument.cpp @@ -29,7 +29,8 @@ using namespace std; // Prevent the warning issued when doing unsigned short -> void* #pragma warning(disable:4312) -CLuaArgument::CLuaArgument() +CLuaArgument::CLuaArgument(CLuaArguments* pOwner) : + m_pOwner(pOwner) { m_iType = LUA_TNIL; m_iIndex = -1; @@ -37,20 +38,31 @@ CLuaArgument::CLuaArgument() m_pUserData = NULL; } -CLuaArgument::CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables) +CLuaArgument::CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) { // Initialize and call our = on the argument m_pTableData = NULL; CopyRecursive(Argument, pKnownTables); } -CLuaArgument::CLuaArgument(NetBitStreamInterface& bitStream, std::vector* pKnownTables) +CLuaArgument::CLuaArgument(CLuaArgument&& Argument, CFastHashMap* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) +{ + // Initialize and call our = on the argument + m_pTableData = NULL; + MoveRecursive(std::move(Argument)); +} + +CLuaArgument::CLuaArgument(NetBitStreamInterface& bitStream, std::vector* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) { m_pTableData = NULL; ReadFromBitStream(bitStream, pKnownTables); } -CLuaArgument::CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables) +CLuaArgument::CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) { // Read the argument out of the lua VM m_pTableData = NULL; @@ -67,7 +79,7 @@ CLuaArgument::~CLuaArgument() void CLuaArgument::CopyRecursive(const CLuaArgument& Argument, CFastHashMap* pKnownTables) { // Clear the string - m_strString = ""; + m_strString.clear(); // Destroy our old tabledata if neccessary DeleteTableData(); @@ -126,6 +138,71 @@ void CLuaArgument::CopyRecursive(const CLuaArgument& Argument, CFastHashMap* pKnownTables) +{ + // Clear the string + m_strString.clear(); + + // Destroy our old tabledata if neccessary + DeleteTableData(); + +#ifdef MTA_DEBUG + // Copy over line and filename too + m_strFilename = std::move(Argument.m_strFilename); + m_iLine = std::exchange(Argument.m_iLine, 0); +#endif + + // Set our variable equally to the copy class + m_iType = Argument.m_iType; + switch (m_iType) + { + case LUA_TBOOLEAN: + { + m_bBoolean = std::exchange(Argument.m_bBoolean, false); + break; + } + + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + { + m_pUserData = std::exchange(Argument.m_pUserData, nullptr); + break; + } + + case LUA_TNUMBER: + { + m_Number = std::exchange(Argument.m_Number, 0); + break; + } + + case LUA_TTABLE: + { + if (pKnownTables && (m_pTableData = MapFindRef(*pKnownTables, Argument.m_pTableData))) + { + m_bWeakTableRef = true; + } + else + { + m_pTableData = std::exchange(Argument.m_pTableData, nullptr); + m_bWeakTableRef = false; + } + break; + } + + case LUA_TSTRING: + { + m_strString = std::move(Argument.m_strString); + break; + } + + default: + break; + } + + Argument.m_iType = LUA_TNIL; + Argument.m_iIndex = -1; +} + const CLuaArgument& CLuaArgument::operator=(const CLuaArgument& Argument) { CopyRecursive(Argument); @@ -134,6 +211,14 @@ const CLuaArgument& CLuaArgument::operator=(const CLuaArgument& Argument) return Argument; } +const CLuaArgument& CLuaArgument::operator=(CLuaArgument&& Argument) +{ + MoveRecursive(std::move(Argument)); + + // Return the given class allowing for chaining + return Argument; +} + bool CLuaArgument::operator==(const CLuaArgument& Argument) { std::set knownTables; diff --git a/Client/mods/deathmatch/logic/lua/CLuaArgument.h b/Client/mods/deathmatch/logic/lua/CLuaArgument.h index a0b8a416ed..3db092f02d 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaArgument.h +++ b/Client/mods/deathmatch/logic/lua/CLuaArgument.h @@ -26,14 +26,17 @@ class CLuaArguments; class CLuaArgument { + friend class CLuaArguments; public: - CLuaArgument(); - CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables = NULL); - CLuaArgument(NetBitStreamInterface& bitStream, std::vector* pKnownTables = NULL); - CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables = NULL); + CLuaArgument(CLuaArguments* pOwner = {}); + CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables = NULL, CLuaArguments* pOwner = {}); + CLuaArgument(CLuaArgument&& Argument, CFastHashMap* pKnownTables = NULL, CLuaArguments* pOwner = {}); + CLuaArgument(NetBitStreamInterface& bitStream, std::vector* pKnownTables = NULL, CLuaArguments* pOwner = {}); + CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables = NULL, CLuaArguments* pOwner = {}); ~CLuaArgument(); const CLuaArgument& operator=(const CLuaArgument& Argument); + const CLuaArgument& operator=(CLuaArgument&& Argument); bool operator==(const CLuaArgument& Argument); bool operator!=(const CLuaArgument& Argument); @@ -51,9 +54,11 @@ class CLuaArgument int GetType() const { return m_iType; }; int GetIndex() const { return m_iIndex; }; + bool IsEmpty() const { return m_iType == LUA_TNIL; } + bool GetBoolean() const { return m_bBoolean; }; lua_Number GetNumber() const { return m_Number; }; - const SString& GetString() { return m_strString; }; + const SString& GetString() const { return m_strString; }; void* GetUserData() const { return m_pUserData; }; CClientEntity* GetElement() const; @@ -66,14 +71,16 @@ class CLuaArgument private: void LogUnableToPacketize(const char* szMessage) const; + CLuaArguments* m_pOwner{}; + int m_iType; int m_iIndex; bool m_bBoolean; + bool m_bWeakTableRef; lua_Number m_Number; SString m_strString; void* m_pUserData; CLuaArguments* m_pTableData; - bool m_bWeakTableRef; #ifdef MTA_DEBUG std::string m_strFilename; @@ -81,6 +88,7 @@ class CLuaArgument #endif void CopyRecursive(const CLuaArgument& Argument, CFastHashMap* pKnownTables = NULL); + void MoveRecursive(CLuaArgument&& Argument, CFastHashMap* pKnownTables = NULL); bool CompareRecursive(const CLuaArgument& Argument, std::set* pKnownTables = NULL); void DeleteTableData(); }; diff --git a/Client/mods/deathmatch/logic/lua/CLuaArguments.cpp b/Client/mods/deathmatch/logic/lua/CLuaArguments.cpp index c93cc86bb4..71c6ef7eba 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaArguments.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaArguments.cpp @@ -65,7 +65,7 @@ void CLuaArguments::CopyRecursive(const CLuaArguments& Arguments, CFastHashMap::const_iterator iter = Arguments.m_Arguments.begin(); for (; iter != Arguments.m_Arguments.end(); iter++) { - CLuaArgument* pArgument = new CLuaArgument(**iter, pKnownTables); + CLuaArgument* pArgument = new CLuaArgument(**iter, pKnownTables, this); m_Arguments.push_back(pArgument); } @@ -84,7 +84,7 @@ void CLuaArguments::ReadArguments(lua_State* luaVM, signed int uiIndexBegin) while (lua_type(luaVM, uiIndexBegin) != LUA_TNONE) { // Create an argument, let it read out the argument and add it to our vector - CLuaArgument* pArgument = new CLuaArgument(luaVM, uiIndexBegin++, &knownTables); + CLuaArgument* pArgument = new CLuaArgument(luaVM, uiIndexBegin++, &knownTables, this); m_Arguments.push_back(pArgument); knownTables.clear(); @@ -113,10 +113,10 @@ void CLuaArguments::ReadTable(lua_State* luaVM, int iIndexBegin, CFastHashMap::const_iterator iter = Arguments.IterBegin(); for (; iter != Arguments.IterEnd(); iter++) { - CLuaArgument* pArgument = new CLuaArgument(**iter); + CLuaArgument* pArgument = new CLuaArgument(**iter, {}, this); m_Arguments.push_back(pArgument); } } @@ -316,14 +316,14 @@ bool CLuaArguments::CallGlobal(CLuaMain* pLuaMain, const char* szFunction, CLuaA CLuaArgument* CLuaArguments::PushNil() { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); m_Arguments.push_back(pArgument); return pArgument; } CLuaArgument* CLuaArguments::PushBoolean(bool bBool) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadBool(bBool); m_Arguments.push_back(pArgument); return pArgument; @@ -331,7 +331,7 @@ CLuaArgument* CLuaArguments::PushBoolean(bool bBool) CLuaArgument* CLuaArguments::PushNumber(double dNumber) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadNumber(dNumber); m_Arguments.push_back(pArgument); return pArgument; @@ -339,7 +339,7 @@ CLuaArgument* CLuaArguments::PushNumber(double dNumber) CLuaArgument* CLuaArguments::PushString(const std::string& strString) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadString(strString); m_Arguments.push_back(pArgument); return pArgument; @@ -347,7 +347,7 @@ CLuaArgument* CLuaArguments::PushString(const std::string& strString) CLuaArgument* CLuaArguments::PushResource(CResource* pResource) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pResource->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -355,7 +355,7 @@ CLuaArgument* CLuaArguments::PushResource(CResource* pResource) CLuaArgument* CLuaArguments::PushElement(CClientEntity* pElement) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadElement(pElement); m_Arguments.push_back(pArgument); return pArgument; @@ -363,14 +363,26 @@ CLuaArgument* CLuaArguments::PushElement(CClientEntity* pElement) CLuaArgument* CLuaArguments::PushArgument(const CLuaArgument& Argument) { - CLuaArgument* pArgument = new CLuaArgument(Argument); + CLuaArgument* pArgument = new CLuaArgument(Argument, {}, this); m_Arguments.push_back(pArgument); return pArgument; } +CLuaArgument* CLuaArguments::PushArgument(CLuaArgument&& argument) +{ + CLuaArgument* pArgument = new CLuaArgument(std::move(argument), {}, this); + m_Arguments.push_back(pArgument); + return pArgument; +} + +void CLuaArguments::PushArgumentWeak(const CLuaArgument* pArgument) +{ + m_Arguments.push_back(const_cast(pArgument)); +} + CLuaArgument* CLuaArguments::PushTable(CLuaArguments* table) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadTable(table); m_Arguments.push_back(pArgument); return pArgument; @@ -383,7 +395,8 @@ void CLuaArguments::DeleteArguments() vector::iterator iter = m_Arguments.begin(); for (; iter != m_Arguments.end(); iter++) { - delete *iter; + if ((*iter)->m_pOwner == this) + delete *iter; } // Clear the vector @@ -394,8 +407,9 @@ void CLuaArguments::DeleteArguments() void CLuaArguments::Pop() { // Delete the last element - CLuaArgument* item = m_Arguments.back(); - delete item; + auto item = m_Arguments.back(); + if (item->m_pOwner == this) + delete item; // Pop it out of the vector m_Arguments.pop_back(); @@ -413,11 +427,13 @@ void CLuaArguments::ValidateTableKeys() { // TODO - Handle ref in KnownTables // Remove pair - delete *iter; + if ((*iter)->m_pOwner == this) + delete *iter; iter = m_Arguments.erase(iter); if (iter != m_Arguments.end()) { - delete *iter; + if ((*iter)->m_pOwner == this) + delete *iter; iter = m_Arguments.erase(iter); } // Check if end @@ -452,7 +468,7 @@ bool CLuaArguments::ReadFromBitStream(NetBitStreamInterface& bitStream, std::vec pKnownTables->push_back(this); for (unsigned int ui = 0; ui < uiNumArgs; ++ui) { - CLuaArgument* pArgument = new CLuaArgument(bitStream, pKnownTables); + CLuaArgument* pArgument = new CLuaArgument(bitStream, pKnownTables, this); m_Arguments.push_back(pArgument); } } @@ -644,7 +660,7 @@ bool CLuaArguments::ReadFromJSONString(const char* szJSON) for (uint i = 0; i < json_object_array_length(object); i++) { json_object* arrayObject = json_object_array_get_idx(object, i); - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); bSuccess = pArgument->ReadFromJSONObject(arrayObject, &knownTables); m_Arguments.push_back(pArgument); // then the value if (!bSuccess) @@ -656,7 +672,7 @@ bool CLuaArguments::ReadFromJSONString(const char* szJSON) else if (json_object_get_type(object) == json_type_object) { std::vector knownTables; - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); bool bSuccess = pArgument->ReadFromJSONObject(object, &knownTables); m_Arguments.push_back(pArgument); // value json_object_put(object); @@ -690,7 +706,7 @@ bool CLuaArguments::ReadFromJSONObject(json_object* object, std::vectorReadString(key); m_Arguments.push_back(pArgument); // push the key first pArgument = new CLuaArgument(); @@ -729,11 +745,11 @@ bool CLuaArguments::ReadFromJSONArray(json_object* object, std::vectorReadNumber(i + 1); // push the key m_Arguments.push_back(pArgument); - pArgument = new CLuaArgument(); + pArgument = new CLuaArgument(this); bSuccess = pArgument->ReadFromJSONObject(arrayObject, pKnownTables); m_Arguments.push_back(pArgument); // then the valoue if (!bSuccess) diff --git a/Client/mods/deathmatch/logic/lua/CLuaArguments.h b/Client/mods/deathmatch/logic/lua/CLuaArguments.h index 89011983f2..4630ed77fb 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaArguments.h +++ b/Client/mods/deathmatch/logic/lua/CLuaArguments.h @@ -58,6 +58,8 @@ class CLuaArguments CLuaArgument* PushString(const std::string& strString); CLuaArgument* PushElement(CClientEntity* pElement); CLuaArgument* PushArgument(const CLuaArgument& argument); + CLuaArgument* PushArgument(CLuaArgument&& argument); + void PushArgumentWeak(const CLuaArgument* pArgument); CLuaArgument* PushResource(CResource* pResource); CLuaArgument* PushTable(CLuaArguments* table); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index e88f8f6751..d4276b09e4 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -1781,7 +1781,7 @@ int CLuaElementDefs::SetElementData(lua_State* luaVM) strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); } - if (CStaticFunctionDefinitions::SetElementData(*pEntity, strKey, value, bSynchronize)) + if (pEntity->SetCustomData(std::move(strKey), std::move(value), bSynchronize, true)) { lua_pushboolean(luaVM, true); return 1; diff --git a/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp index 1b5da5c780..dacdf34209 100644 --- a/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CElementRPCs.cpp @@ -97,7 +97,7 @@ void CElementRPCs::SetElementData(CClientEntity* pSource, NetBitStreamInterface& CLuaArgument Argument; if (bitStream.ReadStringCharacters(strName, usNameLength) && Argument.ReadFromBitStream(bitStream)) { - pSource->SetCustomData(strName, Argument); + pSource->SetCustomData(std::move(strName), std::move(Argument)); } } } diff --git a/Server/mods/deathmatch/logic/CCustomData.cpp b/Server/mods/deathmatch/logic/CCustomData.cpp index f18bbd37d1..2642c4c8d9 100644 --- a/Server/mods/deathmatch/logic/CCustomData.cpp +++ b/Server/mods/deathmatch/logic/CCustomData.cpp @@ -14,39 +14,35 @@ void CCustomData::Copy(CCustomData* pCustomData) { - std::map::const_iterator iter = pCustomData->IterBegin(); - for (; iter != pCustomData->IterEnd(); iter++) - { - Set(iter->first.c_str(), iter->second.Variable); - } + for (const auto& [key, data] : pCustomData->GetData()) + Set(SString(key), CLuaArgument(data.Variable)); } -SCustomData* CCustomData::Get(const char* szName) +SCustomData* CCustomData::Get(const SString& strName, bool bCreate) { - assert(szName); - - std::map::const_iterator it = m_Data.find(szName); + auto it = m_Data.find(strName); if (it != m_Data.end()) - return (SCustomData*)&it->second; + return &it->second; - return NULL; + if (bCreate) + return &m_Data[strName]; + + return {}; } -SCustomData* CCustomData::GetSynced(const char* szName) +SCustomData* CCustomData::GetSynced(const SString& strName) { - assert(szName); - - std::map::const_iterator it = m_SyncedData.find(szName); + auto it = m_SyncedData.find(strName); if (it != m_SyncedData.end()) - return (SCustomData*)&it->second; + return &it->second; - return NULL; + return {}; } -bool CCustomData::DeleteSynced(const char* szName) +bool CCustomData::DeleteSynced(const SString& strName) { // Find the item and delete it - std::map::iterator iter = m_SyncedData.find(szName); + auto iter = m_SyncedData.find(strName); if (iter != m_SyncedData.end()) { m_SyncedData.erase(iter); @@ -57,11 +53,11 @@ bool CCustomData::DeleteSynced(const char* szName) return false; } -void CCustomData::UpdateSynced(const char* szName, const CLuaArgument& Variable, ESyncType syncType) +void CCustomData::UpdateSynced(const SString& strName, const CLuaArgument& Variable, ESyncType syncType) { if (syncType == ESyncType::BROADCAST) { - SCustomData* pDataSynced = GetSynced(szName); + SCustomData* pDataSynced = GetSynced(strName); if (pDataSynced) { pDataSynced->Variable = Variable; @@ -72,46 +68,53 @@ void CCustomData::UpdateSynced(const char* szName, const CLuaArgument& Variable, SCustomData newData; newData.Variable = Variable; newData.syncType = syncType; - m_SyncedData[szName] = newData; + m_SyncedData[strName] = newData; } } else { - DeleteSynced(szName); + DeleteSynced(strName); } } -void CCustomData::Set(const char* szName, const CLuaArgument& Variable, ESyncType syncType) +SCustomDataResult CCustomData::Set(SString&& strName, CLuaArgument&& Variable, ESyncType syncType, SCustomData* pOldData) { - assert(szName); + if (strName.length() > MAX_CUSTOMDATA_NAME_LENGTH) + return {}; - // Grab the item with the given name - SCustomData* pData = Get(szName); - if (pData) - { - // Update existing - pData->Variable = Variable; - pData->syncType = syncType; - UpdateSynced(szName, Variable, syncType); - } - else + auto iter = m_Data.try_emplace(std::move(strName)).first; + SCustomData& pCurrentVariable = iter->second; + + if (pCurrentVariable.Variable.IsEmpty() || pCurrentVariable.syncType != syncType || pCurrentVariable.Variable != Variable) { - // Add new - SCustomData newData; - newData.Variable = Variable; - newData.syncType = syncType; - m_Data[szName] = newData; - UpdateSynced(szName, Variable, syncType); - } + if (syncType == ESyncType::PERSISTENT) + syncType = pCurrentVariable.syncType; + + // Save the old variable + if (pOldData) + *pOldData = std::move(pCurrentVariable); + + // Set the new data + pCurrentVariable.Variable = std::move(Variable); + pCurrentVariable.syncType = syncType; + UpdateSynced(strName, Variable, syncType); + + return SCustomDataResult(iter); + } + + return {}; } -bool CCustomData::Delete(const char* szName) +bool CCustomData::Delete(const SString& strName, SCustomData* pOldData) { // Find the item and delete it - std::map::iterator it = m_Data.find(szName); + auto it = m_Data.find(strName); if (it != m_Data.end()) { - DeleteSynced(szName); + if (pOldData) + *pOldData = it->second; + + DeleteSynced(strName); m_Data.erase(it); return true; } @@ -122,37 +125,32 @@ bool CCustomData::Delete(const char* szName) CXMLNode* CCustomData::OutputToXML(CXMLNode* pNode) { - std::map::const_iterator iter = m_Data.begin(); - for (; iter != m_Data.end(); iter++) + for (const auto& [key, data] : m_Data) { - CLuaArgument* arg = (CLuaArgument*)&iter->second.Variable; + const CLuaArgument& arg = data.Variable; - switch (arg->GetType()) + switch (arg.GetType()) { case LUA_TSTRING: { - CXMLAttribute* attr = pNode->GetAttributes().Create(iter->first.c_str()); - attr->SetValue(arg->GetString().c_str()); + CXMLAttribute* attr = pNode->GetAttributes().Create(key.c_str()); + attr->SetValue(arg.GetString().c_str()); break; } case LUA_TNUMBER: { - CXMLAttribute* attr = pNode->GetAttributes().Create(iter->first.c_str()); - attr->SetValue((float)arg->GetNumber()); + CXMLAttribute* attr = pNode->GetAttributes().Create(key.c_str()); + attr->SetValue((float)arg.GetNumber()); break; } case LUA_TBOOLEAN: { - CXMLAttribute* attr = pNode->GetAttributes().Create(iter->first.c_str()); - attr->SetValue(arg->GetBoolean()); + CXMLAttribute* attr = pNode->GetAttributes().Create(key.c_str()); + attr->SetValue(arg.GetBoolean()); break; } } } + return pNode; } - -unsigned short CCustomData::CountOnlySynchronized() -{ - return static_cast(m_SyncedData.size()); -} diff --git a/Server/mods/deathmatch/logic/CCustomData.h b/Server/mods/deathmatch/logic/CCustomData.h index bfb17ff529..bb531837db 100644 --- a/Server/mods/deathmatch/logic/CCustomData.h +++ b/Server/mods/deathmatch/logic/CCustomData.h @@ -23,12 +23,34 @@ enum class ESyncType LOCAL, BROADCAST, SUBSCRIBE, + PERSISTENT }; struct SCustomData { CLuaArgument Variable; - ESyncType syncType; + ESyncType syncType{ESyncType::BROADCAST}; +}; + +struct SCustomDataResult +{ + using iterator = std::unordered_map::iterator; + + SCustomDataResult() = default; + + SCustomDataResult(const iterator& iter) : + Iter(iter), + bValid(true) + { + } + + const SString& GetName() const { return Iter->first; } + const SCustomData& GetData() const { return Iter->second; } + + operator bool() const { return bValid; } + + iterator Iter; + bool bValid{}; }; class CCustomData @@ -36,26 +58,21 @@ class CCustomData public: void Copy(CCustomData* pCustomData); - SCustomData* Get(const char* szName); - SCustomData* GetSynced(const char* szName); - void Set(const char* szName, const CLuaArgument& Variable, ESyncType syncType = ESyncType::BROADCAST); + SCustomData* Get(const SString& strName, bool bCreate = false); + SCustomData* GetSynced(const SString& strName); + SCustomDataResult Set(SString&& strName, CLuaArgument&& Variable, ESyncType syncType = ESyncType::BROADCAST, SCustomData* pOldData = {}); - bool Delete(const char* szName); - - unsigned short CountOnlySynchronized(); + bool Delete(const SString& strName, SCustomData* pOldData = {}); CXMLNode* OutputToXML(CXMLNode* pNode); - std::map::const_iterator IterBegin() { return m_Data.begin(); } - std::map::const_iterator IterEnd() { return m_Data.end(); } - - std::map::const_iterator SyncedIterBegin() { return m_SyncedData.begin(); } - std::map::const_iterator SyncedIterEnd() { return m_SyncedData.end(); } + const std::unordered_map& GetData() const { return m_Data; } + const std::unordered_map& GetSyncedData() const { return m_SyncedData; } private: - bool DeleteSynced(const char* szName); - void UpdateSynced(const char* szName, const CLuaArgument& Variable, ESyncType syncType); + bool DeleteSynced(const SString& strName); + void UpdateSynced(const SString& strName, const CLuaArgument& Variable, ESyncType syncType); - std::map m_Data; - std::map m_SyncedData; + std::unordered_map m_Data; + std::unordered_map m_SyncedData; }; diff --git a/Server/mods/deathmatch/logic/CElement.cpp b/Server/mods/deathmatch/logic/CElement.cpp index 59086a2f14..e281b3b46c 100644 --- a/Server/mods/deathmatch/logic/CElement.cpp +++ b/Server/mods/deathmatch/logic/CElement.cpp @@ -23,6 +23,7 @@ #include "CElementRefManager.h" #include "CLogger.h" #include "CSpatialDatabase.h" +#include "CPerfStatModule.h" #include "packets/CElementRPCPacket.h" #include "Utils.h" #include "lua/CLuaFunctionParseHelpers.h" @@ -503,17 +504,15 @@ void CElement::ReadCustomData(CEvents* pEvents, CXMLNode& Node) args.PushString(pAttribute->GetValue().c_str()); // Don't trigger onElementDataChanged event - ESyncType syncType = g_pGame->GetConfig()->GetSyncMapElementData() ? ESyncType::BROADCAST : ESyncType::LOCAL; - SetCustomData(pAttribute->GetName().c_str(), *args[0], syncType, NULL, false); + const ESyncType syncType = g_pGame->GetConfig()->GetSyncMapElementData() ? ESyncType::BROADCAST : ESyncType::LOCAL; + SetCustomData(pAttribute->GetName(), std::move(*args[0]), syncType, {}, false); } } -CLuaArgument* CElement::GetCustomData(const char* szName, bool bInheritData, ESyncType* pSyncType) +const CLuaArgument* CElement::GetCustomData(const SString& strName, bool bInheritData, ESyncType* pSyncType) { - assert(szName); - // Grab it and return a pointer to the variable - SCustomData* pData = m_CustomData.Get(szName); + SCustomData* pData = m_CustomData.Get(strName); if (pData) { if (pSyncType) @@ -524,11 +523,11 @@ CLuaArgument* CElement::GetCustomData(const char* szName, bool bInheritData, ESy // If none, try returning parent's custom data if (bInheritData && m_pParent) { - return m_pParent->GetCustomData(szName, true, pSyncType); + return m_pParent->GetCustomData(strName, true, pSyncType); } // None available - return NULL; + return {}; } CLuaArguments* CElement::GetAllCustomData(CLuaArguments* table) @@ -536,12 +535,11 @@ CLuaArguments* CElement::GetAllCustomData(CLuaArguments* table) assert(table); // Grab it and return a pointer to the variable - map::const_iterator iter = m_CustomData.IterBegin(); - for (; iter != m_CustomData.IterEnd(); iter++) + for (const auto& [key, data] : m_CustomData.GetData()) { - table->PushString(iter->first.c_str()); // key - table->PushArgument(iter->second.Variable); // value - } + table->PushString(key); // key + table->PushArgument(data.Variable); // value + } return table; } @@ -549,7 +547,7 @@ CLuaArguments* CElement::GetAllCustomData(CLuaArguments* table) bool CElement::GetCustomDataString(const char* szName, char* pOut, size_t sizeBuffer, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Make sure it gets 0 terminated @@ -588,7 +586,7 @@ bool CElement::GetCustomDataString(const char* szName, char* pOut, size_t sizeBu bool CElement::GetCustomDataInt(const char* szName, int& iOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -626,7 +624,7 @@ bool CElement::GetCustomDataInt(const char* szName, int& iOut, bool bInheritData bool CElement::GetCustomDataFloat(const char* szName, float& fOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -653,7 +651,7 @@ bool CElement::GetCustomDataFloat(const char* szName, float& fOut, bool bInherit bool CElement::GetCustomDataBool(const char* szName, bool& bOut, bool bInheritData) { // Grab the custom data variable - CLuaArgument* pData = GetCustomData(szName, bInheritData); + const CLuaArgument* pData = GetCustomData(szName, bInheritData); if (pData) { // Write the content depending on what type it is @@ -705,67 +703,89 @@ bool CElement::GetCustomDataBool(const char* szName, bool& bOut, bool bInheritDa return false; } -void CElement::SetCustomData(const char* szName, const CLuaArgument& Variable, ESyncType syncType, CPlayer* pClient, bool bTriggerEvent) +bool CElement::SetCustomData(SString&& strName, CLuaArgument&& Variable, ESyncType syncType, CPlayer* pClient, + bool bTriggerEvent, EElementDataPacketType packetType) { - assert(szName); - if (strlen(szName) > MAX_CUSTOMDATA_NAME_LENGTH) + if (strName.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(szName).Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); - return; + CLogger::ErrorPrintf("Custom data name too long (%s)\n", *strName.Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); + return false; } - // Grab the old variable - CLuaArgument oldVariable; - const SCustomData* pData = m_CustomData.Get(szName); - if (pData) - { - oldVariable = pData->Variable; - } + // SCustomData is 64(88 in debug) bytes long. A static storage can be more efficient. + static SCustomData oldData; + + const SCustomDataResult result = m_CustomData.Set(std::move(strName), std::move(Variable), syncType, &oldData); + if (!result) + return false; - // Set the new data - m_CustomData.Set(szName, Variable, syncType); + const auto& strNewName = result.GetName(); + const auto& newData = result.GetData(); if (bTriggerEvent) { // Trigger the onElementDataChange event on us CLuaArguments Arguments; - Arguments.PushString(szName); - Arguments.PushArgument(oldVariable); - Arguments.PushArgument(Variable); + Arguments.PushString(strNewName); + Arguments.PushArgumentWeak(&oldData.Variable); + Arguments.PushArgumentWeak(&newData.Variable); CallEvent("onElementDataChange", Arguments, pClient); } -} -void CElement::DeleteCustomData(const char* szName) -{ - // Grab the old variable - SCustomData* pData = m_CustomData.Get(szName); - if (pData) + CPlayerManager* pPlayerManager = g_pGame->GetPlayerManager(); + + if (packetType != EElementDataPacketType::DoNotSend && newData.syncType != ESyncType::LOCAL) { - CLuaArgument oldVariable; - oldVariable = pData->Variable; + // Tell our clients to update their data + const unsigned short usNameLength = static_cast(strNewName.length()); + CBitStream BitStream; + BitStream.pBitStream->WriteCompressed(usNameLength); + BitStream.pBitStream->Write(strNewName.c_str(), usNameLength); + newData.Variable.WriteToBitStream(*BitStream.pBitStream); + + const CElementRPCPacket packet(this, SET_ELEMENT_DATA, *BitStream.pBitStream); + const size_t numPlayers = syncType == ESyncType::BROADCAST ? pPlayerManager->BroadcastOnlyJoined(packet, pClient) + : pPlayerManager->BroadcastOnlySubscribed(packet, this, strNewName, pClient); + if (packetType == EElementDataPacketType::Broadcast) + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageOut(strNewName, numPlayers, BitStream.pBitStream->GetNumberOfBytesUsed()); + else + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageRelayed(strNewName, numPlayers, BitStream.pBitStream->GetNumberOfBytesUsed()); + } + + // Unsubscribe all the players + if (oldData.syncType == ESyncType::SUBSCRIBE && newData.syncType != ESyncType::SUBSCRIBE) + pPlayerManager->ClearElementData(this, strNewName); - // Delete the custom data - m_CustomData.Delete(szName); + return true; +} +bool CElement::DeleteCustomData(const SString& strName) +{ + // SCustomData is 64(88 in debug) bytes long. A static storage can be more efficient. + static SCustomData oldData; + + // Delete the custom data + if (m_CustomData.Delete(strName, &oldData)) + { // Trigger the onElementDataChange event on us CLuaArguments Arguments; - Arguments.PushString(szName); - Arguments.PushArgument(oldVariable); - Arguments.PushArgument(CLuaArgument()); // Use nil as the new value to indicate the data has been removed + Arguments.PushString(strName); + Arguments.PushArgumentWeak(&oldData.Variable); + Arguments.PushArgument(CLuaArgument{}); // Use nil as the new value to indicate the data has been removed CallEvent("onElementDataChange", Arguments); + + return true; } + + return false; } // Used to send the root element data when a player joins void CElement::SendAllCustomData(CPlayer* pPlayer) { - for (map::const_iterator iter = m_CustomData.SyncedIterBegin(); iter != m_CustomData.SyncedIterEnd(); ++iter) + for (const auto& [strName, customData] : m_CustomData.GetSyncedData()) { - const std::string& strName = iter->first; - const SCustomData& customData = iter->second; - if (customData.syncType == ESyncType::LOCAL) continue; @@ -778,7 +798,7 @@ void CElement::SendAllCustomData(CPlayer* pPlayer) if (customData.syncType == ESyncType::BROADCAST || pPlayer->IsSubscribed(this, strName)) pPlayer->Send(CElementRPCPacket(this, SET_ELEMENT_DATA, *BitStream.pBitStream)); - } + } } CXMLNode* CElement::OutputToXML(CXMLNode* pNodeParent) diff --git a/Server/mods/deathmatch/logic/CElement.h b/Server/mods/deathmatch/logic/CElement.h index e3a0fa4d07..f9560d952a 100644 --- a/Server/mods/deathmatch/logic/CElement.h +++ b/Server/mods/deathmatch/logic/CElement.h @@ -49,6 +49,13 @@ typedef CFastList CElementListType; typedef std::vector CElementListSnapshot; typedef std::shared_ptr CElementListSnapshotRef; +enum class EElementDataPacketType +{ + DoNotSend = 0, + Broadcast, + Relay +}; + class CElement { friend class CPerPlayerEntity; @@ -136,15 +143,16 @@ class CElement void ReadCustomData(CEvents* pEvents, CXMLNode& Node); CCustomData& GetCustomDataManager() { return m_CustomData; } - CLuaArgument* GetCustomData(const char* szName, bool bInheritData, ESyncType* pSyncType = NULL); + const CLuaArgument* GetCustomData(const SString& strName, bool bInheritData, ESyncType* pSyncType = {}); CLuaArguments* GetAllCustomData(CLuaArguments* table); bool GetCustomDataString(const char* szName, char* pOut, size_t sizeBuffer, bool bInheritData); bool GetCustomDataInt(const char* szName, int& iOut, bool bInheritData); bool GetCustomDataFloat(const char* szName, float& fOut, bool bInheritData); bool GetCustomDataBool(const char* szName, bool& bOut, bool bInheritData); - void SetCustomData(const char* szName, const CLuaArgument& Variable, ESyncType syncType = ESyncType::BROADCAST, CPlayer* pClient = NULL, - bool bTriggerEvent = true); - void DeleteCustomData(const char* szName); + // Note that returned SCustomData* cannot be used with recursive algorithms! + bool SetCustomData(SString&& strName, CLuaArgument&& Variable, ESyncType syncType = ESyncType::BROADCAST, CPlayer* pClient = {}, + bool bTriggerEvent = true, EElementDataPacketType packetType = EElementDataPacketType::DoNotSend); + bool DeleteCustomData(const SString& strName); 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 75fc4aaae6..8d4498a825 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -2610,42 +2610,7 @@ void CGame::Packet_CustomData(CCustomDataPacket& Packet) ElementID ID = Packet.GetElementID(); CElement* pElement = CElementIDs::GetElement(ID); if (pElement) - { - // Change the data - const char* szName = Packet.GetName(); - CLuaArgument& Value = Packet.GetValue(); - - // Ignore if the wrong length - if (strlen(szName) > MAX_CUSTOMDATA_NAME_LENGTH) - { - CLogger::ErrorPrintf("Received oversized custom data name from %s (%s)", Packet.GetSourcePlayer()->GetNick(), - *SStringX(szName).Left(MAX_CUSTOMDATA_NAME_LENGTH + 1)); - return; - } - - ESyncType lastSyncType = ESyncType::BROADCAST; - pElement->GetCustomData(szName, false, &lastSyncType); - - 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()); - } - - pElement->SetCustomData(szName, Value, lastSyncType, pSourcePlayer); - } + pElement->SetCustomData(std::move(Packet.GetName()), std::move(Packet.GetValue()), ESyncType::PERSISTENT, pSourcePlayer, true, EElementDataPacketType::Relay); } } diff --git a/Server/mods/deathmatch/logic/CPerfStat.EventPacketUsage.cpp b/Server/mods/deathmatch/logic/CPerfStat.EventPacketUsage.cpp index 206c81e2fe..5d50b499db 100644 --- a/Server/mods/deathmatch/logic/CPerfStat.EventPacketUsage.cpp +++ b/Server/mods/deathmatch/logic/CPerfStat.EventPacketUsage.cpp @@ -43,9 +43,9 @@ class CPerfStatEventPacketUsageImpl : public CPerfStatEventPacketUsage virtual void GetStats(CPerfStatResult* pOutResult, const std::map& optionMap, const SString& strFilter); // CPerfStatEventPacketUsage - virtual void UpdateElementDataUsageOut(const char* szName, uint uiNumPlayers, uint uiSize); - virtual void UpdateElementDataUsageRelayed(const char* szName, uint uiNumPlayers, uint uiSize); - virtual void UpdateEventUsageOut(const char* szName, uint uiNumPlayers); + virtual void UpdateElementDataUsageOut(const SString& strName, uint uiNumPlayers, uint uiSize); + virtual void UpdateElementDataUsageRelayed(const SString& strName, uint uiNumPlayers, uint uiSize); + virtual void UpdateEventUsageOut(const SString& strName, uint uiNumPlayers); // CPerfStatEventPacketUsageImpl void MaybeRecordStats(); @@ -128,12 +128,12 @@ void CPerfStatEventPacketUsageImpl::DoPulse() // // /////////////////////////////////////////////////////////////// -void CPerfStatEventPacketUsageImpl::UpdateElementDataUsageOut(const char* szName, uint uiNumPlayers, uint uiSize) +void CPerfStatEventPacketUsageImpl::UpdateElementDataUsageOut(const SString& strName, uint uiNumPlayers, uint uiSize) { if (!m_bEnabled) return; - SEventUsage& usage = MapGet(m_EventUsageLiveMap, szName); + SEventUsage& usage = MapGet(m_EventUsageLiveMap, strName); usage.iTotal += uiNumPlayers; usage.iElementDataOut += uiNumPlayers; } @@ -145,12 +145,12 @@ void CPerfStatEventPacketUsageImpl::UpdateElementDataUsageOut(const char* szName // // /////////////////////////////////////////////////////////////// -void CPerfStatEventPacketUsageImpl::UpdateElementDataUsageRelayed(const char* szName, uint uiNumPlayers, uint uiSize) +void CPerfStatEventPacketUsageImpl::UpdateElementDataUsageRelayed(const SString& strName, uint uiNumPlayers, uint uiSize) { if (!m_bEnabled) return; - SEventUsage& usage = MapGet(m_EventUsageLiveMap, szName); + SEventUsage& usage = MapGet(m_EventUsageLiveMap, strName); usage.iTotal += uiNumPlayers; usage.iElementDataRelay += uiNumPlayers; } @@ -162,12 +162,12 @@ void CPerfStatEventPacketUsageImpl::UpdateElementDataUsageRelayed(const char* sz // // /////////////////////////////////////////////////////////////// -void CPerfStatEventPacketUsageImpl::UpdateEventUsageOut(const char* szName, uint uiNumPlayers) +void CPerfStatEventPacketUsageImpl::UpdateEventUsageOut(const SString& strName, uint uiNumPlayers) { if (!m_bEnabled) return; - SEventUsage& usage = MapGet(m_EventUsageLiveMap, szName); + SEventUsage& usage = MapGet(m_EventUsageLiveMap, strName); usage.iTotal += uiNumPlayers; usage.iEventOut += uiNumPlayers; } diff --git a/Server/mods/deathmatch/logic/CPerfStatModule.h b/Server/mods/deathmatch/logic/CPerfStatModule.h index 795df5daaf..f42783ecb6 100644 --- a/Server/mods/deathmatch/logic/CPerfStatModule.h +++ b/Server/mods/deathmatch/logic/CPerfStatModule.h @@ -223,9 +223,9 @@ class CPerfStatEventPacketUsage : public CPerfStatModule virtual void GetStats(CPerfStatResult* pOutResult, const std::map& optionMap, const SString& strFilter) = 0; // CPerfStatRPCPacketUsage - virtual void UpdateElementDataUsageOut(const char* szName, uint uiNumPlayers, uint uiSize) = 0; - virtual void UpdateElementDataUsageRelayed(const char* szName, uint uiNumPlayers, uint uiSize) = 0; - virtual void UpdateEventUsageOut(const char* szName, uint uiNumPlayers) = 0; + virtual void UpdateElementDataUsageOut(const SString& strName, uint uiNumPlayers, uint uiSize) = 0; + virtual void UpdateElementDataUsageRelayed(const SString& strName, uint uiNumPlayers, uint uiSize) = 0; + virtual void UpdateEventUsageOut(const SString& strName, uint uiNumPlayers) = 0; static CPerfStatEventPacketUsage* GetSingleton(); }; diff --git a/Server/mods/deathmatch/logic/CPlayerManager.cpp b/Server/mods/deathmatch/logic/CPlayerManager.cpp index abdf923df7..c2ea91eb76 100644 --- a/Server/mods/deathmatch/logic/CPlayerManager.cpp +++ b/Server/mods/deathmatch/logic/CPlayerManager.cpp @@ -188,7 +188,7 @@ size_t CPlayerManager::BroadcastDimensionOnlyJoined(const CPacket& Packet, ushor return sendList.size(); } -size_t CPlayerManager::BroadcastOnlySubscribed(const CPacket& Packet, CElement* pElement, const char* szName, CPlayer* pSkip) +size_t CPlayerManager::BroadcastOnlySubscribed(const CPacket& Packet, CElement* pElement, const SString& strName, CPlayer* pSkip) { // Make a list of players to send this packet to CSendList sendList; @@ -198,7 +198,7 @@ size_t CPlayerManager::BroadcastOnlySubscribed(const CPacket& Packet, CElement* for (; iter != m_Players.end(); iter++) { CPlayer* pPlayer = *iter; - if (pPlayer != pSkip && pPlayer->IsJoined() && pPlayer->IsSubscribed(pElement, szName)) + if (pPlayer != pSkip && pPlayer->IsJoined() && pPlayer->IsSubscribed(pElement, strName)) { sendList.push_back(pPlayer); } @@ -347,20 +347,14 @@ bool CPlayerManager::IsValidPlayerModel(unsigned short model) void CPlayerManager::ClearElementData(CElement* pElement, const std::string& name) { - list::const_iterator iter = m_Players.begin(); - for (; iter != m_Players.end(); iter++) - { - CPlayer* pPlayer = *iter; - pPlayer->UnsubscribeElementData(pElement, name); - } + for (auto* pPlayer : m_Players) + pPlayer->UnsubscribeElementData(pElement, name); } void CPlayerManager::ClearElementData(CElement* pElement) { - for (auto pPlayer : m_Players) - { + for (auto* pPlayer : m_Players) pPlayer->UnsubscribeElementData(pElement); - } } void CPlayerManager::ResetAll() diff --git a/Server/mods/deathmatch/logic/CPlayerManager.h b/Server/mods/deathmatch/logic/CPlayerManager.h index bd503380f4..1470381911 100644 --- a/Server/mods/deathmatch/logic/CPlayerManager.h +++ b/Server/mods/deathmatch/logic/CPlayerManager.h @@ -46,7 +46,7 @@ class CPlayerManager size_t BroadcastOnlyJoined(const CPacket& Packet, CPlayer* pSkip = NULL); size_t BroadcastDimensionOnlyJoined(const CPacket& Packet, ushort usDimension, CPlayer* pSkip = NULL); - size_t BroadcastOnlySubscribed(const CPacket& Packet, CElement* pElement, const char* szName, CPlayer* pSkip = NULL); + size_t BroadcastOnlySubscribed(const CPacket& Packet, CElement* pElement, const SString& strName, CPlayer* pSkip = NULL); static void Broadcast(const CPacket& Packet, const std::set& sendList); static void Broadcast(const CPacket& Packet, const std::list& sendList); diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 58cdfbce4a..1fe43682bb 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -475,15 +475,6 @@ CElement* CStaticFunctionDefinitions::GetElementByIndex(const char* szType, unsi return m_pMapManager->GetRootElement()->FindChildByType(szType, uiIndex, true); } -CLuaArgument* CStaticFunctionDefinitions::GetElementData(CElement* pElement, const char* szName, bool bInherit) -{ - assert(pElement); - assert(szName); - - // Return its custom data - return pElement->GetCustomData(szName, bInherit); -} - CLuaArguments* CStaticFunctionDefinitions::GetAllElementData(CElement* pElement, CLuaArguments* table) { assert(pElement); @@ -954,97 +945,54 @@ bool CStaticFunctionDefinitions::SetElementID(CElement* pElement, const char* sz return true; } -bool CStaticFunctionDefinitions::SetElementData(CElement* pElement, const char* szName, const CLuaArgument& Variable, ESyncType syncType) -{ - assert(pElement); - assert(szName); - assert(strlen(szName) <= MAX_CUSTOMDATA_NAME_LENGTH); - - ESyncType lastSyncType = ESyncType::BROADCAST; - CLuaArgument* pCurrentVariable = pElement->GetCustomData(szName, false, &lastSyncType); - - if (!pCurrentVariable || *pCurrentVariable != Variable || lastSyncType != syncType) - { - if (syncType != ESyncType::LOCAL) - { - // Tell our clients to update their data - unsigned short usNameLength = static_cast(strlen(szName)); - CBitStream BitStream; - BitStream.pBitStream->WriteCompressed(usNameLength); - BitStream.pBitStream->Write(szName, usNameLength); - Variable.WriteToBitStream(*BitStream.pBitStream); - - const CElementRPCPacket packet(pElement, SET_ELEMENT_DATA, *BitStream.pBitStream); - const size_t numPlayers = syncType == ESyncType::BROADCAST ? m_pPlayerManager->BroadcastOnlyJoined(packet) - : m_pPlayerManager->BroadcastOnlySubscribed(packet, pElement, szName); - - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageOut(szName, numPlayers, BitStream.pBitStream->GetNumberOfBytesUsed()); - } - - // Unsubscribe all the players - if (lastSyncType == ESyncType::SUBSCRIBE && syncType != ESyncType::SUBSCRIBE) - m_pPlayerManager->ClearElementData(pElement, szName); - - // Set its custom data - pElement->SetCustomData(szName, Variable, syncType); - return true; - } - return false; -} - -bool CStaticFunctionDefinitions::RemoveElementData(CElement* pElement, const char* szName) +bool CStaticFunctionDefinitions::RemoveElementData(CElement* pElement, const SString& strName) { assert(pElement); - assert(szName); - assert(strlen(szName) <= MAX_CUSTOMDATA_NAME_LENGTH); + assert(strName.length() <= MAX_CUSTOMDATA_NAME_LENGTH); - // Check it exists - if (pElement->GetCustomData(szName, false)) + if (pElement->DeleteCustomData(strName)) { // Tell our clients to update their data - unsigned short usNameLength = static_cast(strlen(szName)); + unsigned short usNameLength = static_cast(strName.length()); CBitStream BitStream; BitStream.pBitStream->WriteCompressed(usNameLength); - BitStream.pBitStream->Write(szName, usNameLength); + BitStream.pBitStream->Write(strName.c_str(), usNameLength); BitStream.pBitStream->WriteBit(false); // Unused (was recursive flag) m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pElement, REMOVE_ELEMENT_DATA, *BitStream.pBitStream)); // Clean up after the data removal - m_pPlayerManager->ClearElementData(pElement, szName); + m_pPlayerManager->ClearElementData(pElement, strName); - // Delete here - pElement->DeleteCustomData(szName); return true; - } + } // Failed return false; } -bool CStaticFunctionDefinitions::AddElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer) +bool CStaticFunctionDefinitions::AddElementDataSubscriber(CElement* pElement, const SString& strName, CPlayer* pPlayer) { assert(pElement); - assert(szName); assert(pPlayer); ESyncType lastSyncType = ESyncType::LOCAL; - CLuaArgument* pCurrentVariable = pElement->GetCustomData(szName, false, &lastSyncType); + const CLuaArgument* pCurrentVariable = pElement->GetCustomData(strName, false, &lastSyncType); if (pCurrentVariable != nullptr && lastSyncType == ESyncType::SUBSCRIBE) { - if (!pPlayer->SubscribeElementData(pElement, szName)) + if (!pPlayer->SubscribeElementData(pElement, strName)) return false; // Tell our clients to update their data - unsigned short usNameLength = static_cast(strlen(szName)); + unsigned short usNameLength = static_cast(strName.length()); CBitStream BitStream; BitStream.pBitStream->WriteCompressed(usNameLength); - BitStream.pBitStream->Write(szName, usNameLength); + BitStream.pBitStream->Write(strName.c_str(), usNameLength); pCurrentVariable->WriteToBitStream(*BitStream.pBitStream); pPlayer->Send(CElementRPCPacket(pElement, SET_ELEMENT_DATA, *BitStream.pBitStream)); - CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageOut(szName, 1, BitStream.pBitStream->GetNumberOfBytesUsed()); + CPerfStatEventPacketUsage::GetSingleton()->UpdateElementDataUsageOut(strName, 1, BitStream.pBitStream->GetNumberOfBytesUsed()); return true; } @@ -1052,22 +1000,20 @@ bool CStaticFunctionDefinitions::AddElementDataSubscriber(CElement* pElement, co return false; } -bool CStaticFunctionDefinitions::RemoveElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer) +bool CStaticFunctionDefinitions::RemoveElementDataSubscriber(CElement* pElement, const SString& strName, CPlayer* pPlayer) { assert(pElement); - assert(szName); assert(pPlayer); - return pPlayer->UnsubscribeElementData(pElement, szName); + return pPlayer->UnsubscribeElementData(pElement, strName); } -bool CStaticFunctionDefinitions::HasElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer) +bool CStaticFunctionDefinitions::HasElementDataSubscriber(CElement* pElement, const SString& strName, CPlayer* pPlayer) { assert(pElement); - assert(szName); assert(pPlayer); - return pPlayer->IsSubscribed(pElement, szName); + return pPlayer->IsSubscribed(pElement, strName); } bool CStaticFunctionDefinitions::SetElementParent(CElement* pElement, CElement* pParent) diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h index db6fb1874e..d34b986ab9 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -51,7 +51,6 @@ class CStaticFunctionDefinitions static CElement* GetElementByIndex(const char* szType, unsigned int uiIndex); static CElement* GetElementChild(CElement* pElement, unsigned int uiIndex); static bool GetElementChildrenCount(CElement* pElement, unsigned int& uiCount); - static CLuaArgument* GetElementData(CElement* pElement, const char* szName, bool bInherit); static CLuaArguments* GetAllElementData(CElement* pElement, CLuaArguments* table); static CElement* GetElementParent(CElement* pElement); static bool GetElementMatrix(CElement* pElement, CMatrix& matrix); @@ -83,11 +82,10 @@ class CStaticFunctionDefinitions // Element set funcs static bool ClearElementVisibleTo(CElement* pElement); static bool SetElementID(CElement* pElement, const char* szID); - static bool SetElementData(CElement* pElement, const char* szName, const CLuaArgument& Variable, ESyncType syncType); - static bool RemoveElementData(CElement* pElement, const char* szName); - static bool AddElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer); - static bool RemoveElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer); - static bool HasElementDataSubscriber(CElement* pElement, const char* szName, CPlayer* pPlayer); + static bool RemoveElementData(CElement* pElement, const SString& strName); + static bool AddElementDataSubscriber(CElement* pElement, const SString& strName, CPlayer* pPlayer); + static bool RemoveElementDataSubscriber(CElement* pElement, const SString& strName, CPlayer* pPlayer); + static bool HasElementDataSubscriber(CElement* pElement, const SString& strName, CPlayer* pPlayer); static bool SetElementParent(CElement* pElement, CElement* pParent); static bool SetElementMatrix(CElement* pElement, const CMatrix& matrix); static bool SetElementPosition(CElement* pElement, const CVector& vecPosition, bool bWarp = true); diff --git a/Server/mods/deathmatch/logic/lua/CLuaArgument.cpp b/Server/mods/deathmatch/logic/lua/CLuaArgument.cpp index 6c2f02c377..f20956a041 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaArgument.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaArgument.cpp @@ -26,21 +26,32 @@ extern CGame* g_pGame; using namespace std; -CLuaArgument::CLuaArgument() +CLuaArgument::CLuaArgument(CLuaArguments* pOwner) : + m_pOwner(pOwner) { m_iType = LUA_TNIL; m_pTableData = NULL; m_pUserData = NULL; } -CLuaArgument::CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables) +CLuaArgument::CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) { // Initialize and call our = on the argument m_pTableData = NULL; CopyRecursive(Argument, pKnownTables); } -CLuaArgument::CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables) +CLuaArgument::CLuaArgument(CLuaArgument&& Argument, CFastHashMap* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) +{ + // Initialize and call our = on the argument + m_pTableData = NULL; + MoveRecursive(std::move(Argument), pKnownTables); +} + +CLuaArgument::CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables, CLuaArguments* pOwner) : + m_pOwner(pOwner) { // Read the argument out of the lua VM m_pTableData = NULL; @@ -53,6 +64,69 @@ CLuaArgument::~CLuaArgument() DeleteTableData(); } +void CLuaArgument::MoveRecursive(CLuaArgument&& Argument, CFastHashMap* pKnownTables) +{ + // Clear the string + m_strString.clear(); + + // Destroy our old tabledata if neccessary + DeleteTableData(); + +#ifdef MTA_DEBUG + m_strFilename = std::move(Argument.m_strFilename); + m_iLine = std::exchange(Argument.m_iLine, 0); +#endif + + // Set our variable equally to the copy class + m_iType = Argument.m_iType; + switch (m_iType) + { + case LUA_TBOOLEAN: + { + m_bBoolean = std::exchange(Argument.m_bBoolean, false); + break; + } + + case LUA_TLIGHTUSERDATA: + case LUA_TUSERDATA: + { + m_pUserData = std::exchange(Argument.m_pUserData, nullptr); + break; + } + + case LUA_TNUMBER: + { + m_Number = std::exchange(Argument.m_Number, 0); + break; + } + + case LUA_TTABLE: + { + if (pKnownTables && (m_pTableData = MapFindRef(*pKnownTables, Argument.m_pTableData))) + { + m_bWeakTableRef = true; + } + else + { + m_pTableData = std::exchange(Argument.m_pTableData, nullptr); + m_bWeakTableRef = false; + } + break; + } + + case LUA_TSTRING: + { + m_strString = std::move(Argument.m_strString); + break; + } + + default: + break; + } + + Argument.m_iType = LUA_TNIL; +} + void CLuaArgument::CopyRecursive(const CLuaArgument& Argument, CFastHashMap* pKnownTables) { // Clear the string @@ -123,6 +197,14 @@ const CLuaArgument& CLuaArgument::operator=(const CLuaArgument& Argument) return Argument; } +const CLuaArgument& CLuaArgument::operator=(CLuaArgument&& Argument) +{ + MoveRecursive(std::move(Argument)); + + // Return the given class allowing for chaining + return Argument; +} + bool CLuaArgument::operator==(const CLuaArgument& Argument) const { std::set knownTables; diff --git a/Server/mods/deathmatch/logic/lua/CLuaArgument.h b/Server/mods/deathmatch/logic/lua/CLuaArgument.h index bb6c8581f8..ea3ad9bc58 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaArgument.h +++ b/Server/mods/deathmatch/logic/lua/CLuaArgument.h @@ -28,13 +28,16 @@ class CLuaArguments; class CLuaArgument { + friend class CLuaArguments; public: - CLuaArgument(); - CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables = NULL); - CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables = NULL); + CLuaArgument(CLuaArguments* pOwner = {}); + CLuaArgument(const CLuaArgument& Argument, CFastHashMap* pKnownTables = NULL, CLuaArguments* pOwner = {}); + CLuaArgument(CLuaArgument&& Argument, CFastHashMap* pKnownTables = NULL, CLuaArguments* pOwner = {}); + CLuaArgument(lua_State* luaVM, int iArgument, CFastHashMap* pKnownTables = NULL, CLuaArguments* pOwner = {}); ~CLuaArgument(); const CLuaArgument& operator=(const CLuaArgument& Argument); + const CLuaArgument& operator=(CLuaArgument&& Argument); bool operator==(const CLuaArgument& Argument) const; bool operator!=(const CLuaArgument& Argument) const; @@ -51,9 +54,11 @@ class CLuaArgument int GetType() const { return m_iType; }; + bool IsEmpty() const { return m_iType == LUA_TNIL; } + bool GetBoolean() const { return m_bBoolean; }; lua_Number GetNumber() const { return m_Number; }; - const std::string& GetString() { return m_strString; }; + const std::string& GetString() const { return m_strString; }; void* GetUserData() const { return m_pUserData; }; CElement* GetElement() const; bool GetAsString(SString& strBuffer); @@ -69,19 +74,22 @@ class CLuaArgument private: void LogUnableToPacketize(const char* szMessage) const; + CLuaArguments* m_pOwner{}; + int m_iType; bool m_bBoolean; + bool m_bWeakTableRef; lua_Number m_Number; std::string m_strString; void* m_pUserData; - CLuaArguments* m_pTableData; - bool m_bWeakTableRef; + CLuaArguments* m_pTableData; #ifdef MTA_DEBUG std::string m_strFilename; int m_iLine; #endif - void CopyRecursive(const CLuaArgument& Argument, CFastHashMap* pKnownTables = NULL); + void MoveRecursive(CLuaArgument&& Argument, CFastHashMap* pKnownTables = {}); + void CopyRecursive(const CLuaArgument& Argument, CFastHashMap* pKnownTables = {}); void DeleteTableData(); }; diff --git a/Server/mods/deathmatch/logic/lua/CLuaArguments.cpp b/Server/mods/deathmatch/logic/lua/CLuaArguments.cpp index 3feadfeedb..2cfb8d0899 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaArguments.cpp +++ b/Server/mods/deathmatch/logic/lua/CLuaArguments.cpp @@ -71,7 +71,7 @@ void CLuaArguments::CopyRecursive(const CLuaArguments& Arguments, CFastHashMap::const_iterator iter = Arguments.m_Arguments.begin(); for (; iter != Arguments.m_Arguments.end(); ++iter) { - CLuaArgument* pArgument = new CLuaArgument(**iter, pKnownTables); + CLuaArgument* pArgument = new CLuaArgument(**iter, pKnownTables, this); m_Arguments.push_back(pArgument); } @@ -90,7 +90,7 @@ void CLuaArguments::ReadArguments(lua_State* luaVM, signed int uiIndexBegin) while (lua_type(luaVM, uiIndexBegin) != LUA_TNONE) { // Create an argument, let it read out the argument and add it to our vector - CLuaArgument* pArgument = new CLuaArgument(luaVM, uiIndexBegin++, &knownTables); + CLuaArgument* pArgument = new CLuaArgument(luaVM, uiIndexBegin++, &knownTables, this); m_Arguments.push_back(pArgument); knownTables.clear(); @@ -119,10 +119,10 @@ void CLuaArguments::ReadTable(lua_State* luaVM, int iIndexBegin, CFastHashMap::const_iterator iter = Arguments.IterBegin(); for (; iter != Arguments.IterEnd(); ++iter) { - CLuaArgument* pArgument = new CLuaArgument(**iter); + CLuaArgument* pArgument = new CLuaArgument(**iter, {}, this); m_Arguments.push_back(pArgument); } } @@ -322,14 +322,14 @@ bool CLuaArguments::CallGlobal(CLuaMain* pLuaMain, const char* szFunction, CLuaA CLuaArgument* CLuaArguments::PushNil() { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); m_Arguments.push_back(pArgument); return pArgument; } CLuaArgument* CLuaArguments::PushBoolean(bool bBool) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadBool(bBool); m_Arguments.push_back(pArgument); return pArgument; @@ -337,7 +337,7 @@ CLuaArgument* CLuaArguments::PushBoolean(bool bBool) CLuaArgument* CLuaArguments::PushTable(CLuaArguments* table) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadTable(table); m_Arguments.push_back(pArgument); return pArgument; @@ -345,7 +345,7 @@ CLuaArgument* CLuaArguments::PushTable(CLuaArguments* table) CLuaArgument* CLuaArguments::PushNumber(double dNumber) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadNumber(dNumber); m_Arguments.push_back(pArgument); return pArgument; @@ -353,14 +353,19 @@ CLuaArgument* CLuaArguments::PushNumber(double dNumber) CLuaArgument* CLuaArguments::PushArgument(const CLuaArgument& argument) { - CLuaArgument* pArgument = new CLuaArgument(argument); // create a copy + CLuaArgument* pArgument = new CLuaArgument(argument, {}, this); // create a copy m_Arguments.push_back(pArgument); return pArgument; } +void CLuaArguments::PushArgumentWeak(const CLuaArgument* argument) +{ + m_Arguments.push_back(const_cast(argument)); +} + CLuaArgument* CLuaArguments::PushString(const std::string& strString) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadString(strString); m_Arguments.push_back(pArgument); return pArgument; @@ -368,7 +373,7 @@ CLuaArgument* CLuaArguments::PushString(const std::string& strString) CLuaArgument* CLuaArguments::PushElement(CElement* pElement) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadElement(pElement); m_Arguments.push_back(pArgument); return pArgument; @@ -376,7 +381,7 @@ CLuaArgument* CLuaArguments::PushElement(CElement* pElement) CLuaArgument* CLuaArguments::PushBan(CBan* pBan) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pBan->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -384,7 +389,7 @@ CLuaArgument* CLuaArguments::PushBan(CBan* pBan) CLuaArgument* CLuaArguments::PushACL(CAccessControlList* pACL) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pACL->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -392,7 +397,7 @@ CLuaArgument* CLuaArguments::PushACL(CAccessControlList* pACL) CLuaArgument* CLuaArguments::PushACLGroup(CAccessControlListGroup* pACLGroup) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pACLGroup->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -400,7 +405,7 @@ CLuaArgument* CLuaArguments::PushACLGroup(CAccessControlListGroup* pACLGroup) CLuaArgument* CLuaArguments::PushAccount(CAccount* pAccount) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pAccount->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -408,7 +413,7 @@ CLuaArgument* CLuaArguments::PushAccount(CAccount* pAccount) CLuaArgument* CLuaArguments::PushResource(CResource* pResource) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pResource->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -416,7 +421,7 @@ CLuaArgument* CLuaArguments::PushResource(CResource* pResource) CLuaArgument* CLuaArguments::PushTextDisplay(CTextDisplay* pTextDisplay) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pTextDisplay->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -424,7 +429,7 @@ CLuaArgument* CLuaArguments::PushTextDisplay(CTextDisplay* pTextDisplay) CLuaArgument* CLuaArguments::PushTextItem(CTextItem* pTextItem) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pTextItem->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -432,7 +437,7 @@ CLuaArgument* CLuaArguments::PushTextItem(CTextItem* pTextItem) CLuaArgument* CLuaArguments::PushTimer(CLuaTimer* pLuaTimer) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pLuaTimer->GetScriptID()); m_Arguments.push_back(pArgument); return pArgument; @@ -440,7 +445,7 @@ CLuaArgument* CLuaArguments::PushTimer(CLuaTimer* pLuaTimer) CLuaArgument* CLuaArguments::PushDbQuery(CDbJobData* pJobData) { - CLuaArgument* pArgument = new CLuaArgument; + CLuaArgument* pArgument = new CLuaArgument(this); pArgument->ReadScriptID(pJobData->GetId()); m_Arguments.push_back(pArgument); return pArgument; @@ -452,7 +457,8 @@ void CLuaArguments::DeleteArguments() vector::iterator iter = m_Arguments.begin(); for (; iter != m_Arguments.end(); ++iter) { - delete *iter; + if ((*iter)->m_pOwner == this) + delete *iter; } // Clear the vector @@ -464,7 +470,8 @@ void CLuaArguments::Pop() { // Delete the last element CLuaArgument* item = m_Arguments.back(); - delete item; + if (item->m_pOwner == this) + delete item; // Pop it out of the vector m_Arguments.pop_back(); @@ -482,11 +489,13 @@ void CLuaArguments::ValidateTableKeys() { // TODO - Handle ref in KnownTables // Remove pair - delete *iter; + if ((*iter)->m_pOwner == this) + delete *iter; iter = m_Arguments.erase(iter); if (iter != m_Arguments.end()) { - delete *iter; + if ((*iter)->m_pOwner == this) + delete *iter; iter = m_Arguments.erase(iter); } // Check if end @@ -521,7 +530,7 @@ bool CLuaArguments::ReadFromBitStream(NetBitStreamInterface& bitStream, std::vec pKnownTables->push_back(this); for (unsigned int ui = 0; ui < uiNumArgs; ++ui) { - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); if (!pArgument->ReadFromBitStream(bitStream, pKnownTables)) { delete pArgument; @@ -720,7 +729,7 @@ bool CLuaArguments::ReadFromJSONString(const char* szJSON) for (uint i = 0; i < json_object_array_length(object); i++) { json_object* arrayObject = json_object_array_get_idx(object, i); - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); bSuccess = pArgument->ReadFromJSONObject(arrayObject, &knownTables); m_Arguments.push_back(pArgument); // then the value if (!bSuccess) @@ -732,7 +741,7 @@ bool CLuaArguments::ReadFromJSONString(const char* szJSON) else if (json_object_get_type(object) == json_type_object) { std::vector knownTables; - CLuaArgument* pArgument = new CLuaArgument(); + CLuaArgument* pArgument = new CLuaArgument(this); bool bSuccess = pArgument->ReadFromJSONObject(object, &knownTables); m_Arguments.push_back(pArgument); // value json_object_put(object); @@ -766,10 +775,10 @@ bool CLuaArguments::ReadFromJSONObject(json_object* object, std::vectorReadString(key); m_Arguments.push_back(pArgument); // push the key first - pArgument = new CLuaArgument(); + pArgument = new CLuaArgument(this); bSuccess = pArgument->ReadFromJSONObject(val, pKnownTables); // then the value m_Arguments.push_back(pArgument); if (!bSuccess) @@ -805,11 +814,11 @@ bool CLuaArguments::ReadFromJSONArray(json_object* object, std::vectorReadNumber(i + 1); // push the key m_Arguments.push_back(pArgument); - pArgument = new CLuaArgument(); + pArgument = new CLuaArgument(this); bSuccess = pArgument->ReadFromJSONObject(arrayObject, pKnownTables); m_Arguments.push_back(pArgument); // then the valoue if (!bSuccess) diff --git a/Server/mods/deathmatch/logic/lua/CLuaArguments.h b/Server/mods/deathmatch/logic/lua/CLuaArguments.h index 87fd92be20..8907b1ccd8 100644 --- a/Server/mods/deathmatch/logic/lua/CLuaArguments.h +++ b/Server/mods/deathmatch/logic/lua/CLuaArguments.h @@ -80,6 +80,7 @@ class CLuaArguments CLuaArgument* PushDbQuery(CDbJobData* pJobData); CLuaArgument* PushArgument(const CLuaArgument& argument); + void PushArgumentWeak(const CLuaArgument* argument); CLuaArgument* PushTable(CLuaArguments* table); void DeleteArguments(); diff --git a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp index 6cacbdcc98..d6b8bbe057 100644 --- a/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp +++ b/Server/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp @@ -1566,7 +1566,7 @@ int CLuaElementDefs::setElementData(lua_State* luaVM) strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); } - if (CStaticFunctionDefinitions::SetElementData(pElement, strKey, value, syncType)) + if (pElement->SetCustomData(std::move(strKey), std::move(value), syncType, {}, true, EElementDataPacketType::Broadcast)) { lua_pushboolean(luaVM, true); return 1; diff --git a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp index d2bfb1ee4a..86d9911409 100644 --- a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.cpp @@ -15,13 +15,10 @@ CCustomDataPacket::CCustomDataPacket() { - m_szName = NULL; } CCustomDataPacket::~CCustomDataPacket() { - delete[] m_szName; - m_szName = NULL; } bool CCustomDataPacket::Read(NetBitStreamInterface& BitStream) @@ -29,10 +26,10 @@ bool CCustomDataPacket::Read(NetBitStreamInterface& BitStream) unsigned short usNameLength; if (BitStream.Read(m_ElementID) && BitStream.ReadCompressed(usNameLength) && usNameLength > 0 && usNameLength <= MAX_CUSTOMDATA_NAME_LENGTH) { - m_szName = new char[usNameLength + 1]; - if (BitStream.Read(m_szName, usNameLength)) + m_strName.resize(usNameLength); + + if (BitStream.Read(m_strName.data(), usNameLength)) { - m_szName[usNameLength] = 0; if (m_Value.ReadFromBitStream(BitStream)) { return true; diff --git a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h index 96b18b9867..25ef48202e 100644 --- a/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h +++ b/Server/mods/deathmatch/logic/packets/CCustomDataPacket.h @@ -26,12 +26,12 @@ class CCustomDataPacket final : public CPacket bool Read(NetBitStreamInterface& BitStream); bool Write(NetBitStreamInterface& BitStream) const; - ElementID GetElementID() { return m_ElementID; } - char* GetName() { return m_szName; } + const ElementID GetElementID() const { return m_ElementID; } + SString& GetName() { return m_strName; } CLuaArgument& GetValue() { return m_Value; } private: ElementID m_ElementID; - char* m_szName; + SString m_strName; CLuaArgument m_Value; }; diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index ff56896864..c88deb8e55 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -170,19 +170,16 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.WriteBit(pElement->IsCallPropagationEnabled()); // Write custom data - CCustomData& pCustomData = pElement->GetCustomDataManager(); - BitStream.WriteCompressed(pCustomData.CountOnlySynchronized()); - map::const_iterator iter = pCustomData.SyncedIterBegin(); - for (; iter != pCustomData.SyncedIterEnd(); ++iter) + const CCustomData& pCustomData = pElement->GetCustomDataManager(); + const uint16_t usSyncronizedDataCount = static_cast(pCustomData.GetSyncedData().size()); + BitStream.WriteCompressed(usSyncronizedDataCount); + for (const auto& [key, data] : pCustomData.GetSyncedData()) { - const char* szName = iter->first.c_str(); - const CLuaArgument* pArgument = &iter->second.Variable; - - unsigned char ucNameLength = static_cast(strlen(szName)); + const unsigned char ucNameLength = static_cast(key.length()); BitStream.Write(ucNameLength); - BitStream.Write(szName, ucNameLength); - pArgument->WriteToBitStream(BitStream); - } + BitStream.Write(key.c_str(), ucNameLength); + data.Variable.WriteToBitStream(BitStream); + } // Grab its name char szEmpty[1]; diff --git a/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp b/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp index 7d33116301..f6f819d926 100644 --- a/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp +++ b/Shared/mods/deathmatch/logic/luadefs/CLuaElementDefsShared.cpp @@ -40,11 +40,7 @@ int CLuaElementDefs::GetElementData(lua_State* luaVM) strKey = strKey.Left(MAX_CUSTOMDATA_NAME_LENGTH); } -#ifdef MTA_CLIENT - CLuaArgument* pVariable = pElement->GetCustomData(strKey, bInherit); -#else - CLuaArgument* pVariable = CStaticFunctionDefinitions::GetElementData(pElement, strKey, bInherit); -#endif + const CLuaArgument* pVariable = pElement->GetCustomData(strKey, bInherit); if (pVariable) { pVariable->Push(luaVM);