diff --git a/src/server/game/Entities/Object/BaseEntity.h b/src/server/game/Entities/Object/BaseEntity.h index bed8fa3ae5..e776c3fcbe 100644 --- a/src/server/game/Entities/Object/BaseEntity.h +++ b/src/server/game/Entities/Object/BaseEntity.h @@ -127,15 +127,27 @@ namespace UF } template - inline void RemoveMapUpdateFieldValue(MapUpdateFieldSetter& setter, std::type_identity_t const& key) + inline bool RemoveMapUpdateFieldValue(MapUpdateFieldSetter& setter, std::type_identity_t const& key) { - setter.RemoveKey(key); + return setter.RemoveKey(key); } template - inline void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter& setter) + inline bool InsertSetUpdateFieldValue(SetUpdateFieldSetter& setter, std::type_identity_t const& key) { - setter.RemoveValue(); + return setter.Insert(key); + } + + template + inline bool RemoveSetUpdateFieldValue(SetUpdateFieldSetter& setter, std::type_identity_t const& key) + { + return setter.Remove(key); + } + + template + inline bool RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter& setter) + { + return setter.RemoveValue(); } } @@ -250,13 +262,6 @@ class TC_GAME_API BaseEntity UF::RemoveDynamicUpdateFieldValue(setter, index); } - template - void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter setter, std::type_identity_t const& key) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveMapUpdateFieldValue(setter, key); - } - template void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter setter) { @@ -264,11 +269,32 @@ class TC_GAME_API BaseEntity UF::ClearDynamicUpdateFieldValues(setter); } + template + void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter setter, std::type_identity_t const& key) + { + if (UF::RemoveMapUpdateFieldValue(setter, key)) + AddToObjectUpdateIfNeeded(); + } + + template + void InsertSetUpdateFieldValue(UF::SetUpdateFieldSetter setter, std::type_identity_t const& key) + { + if (UF::InsertSetUpdateFieldValue(setter, key)) + AddToObjectUpdateIfNeeded(); + } + + template + void RemoveSetUpdateFieldValue(UF::SetUpdateFieldSetter setter, std::type_identity_t const& key) + { + if (UF::RemoveSetUpdateFieldValue(setter, key)) + AddToObjectUpdateIfNeeded(); + } + template void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter setter) { - AddToObjectUpdateIfNeeded(); - UF::RemoveOptionalUpdateFieldValue(setter); + if (UF::RemoveOptionalUpdateFieldValue(setter)) + AddToObjectUpdateIfNeeded(); } // stat system helpers diff --git a/src/server/game/Entities/Object/Updates/UpdateField.h b/src/server/game/Entities/Object/Updates/UpdateField.h index 8f351f8a37..acf6409d55 100644 --- a/src/server/game/Entities/Object/Updates/UpdateField.h +++ b/src/server/game/Entities/Object/Updates/UpdateField.h @@ -71,6 +71,12 @@ namespace UF template class MapUpdateField; + template + class SetUpdateFieldBase; + + template + class SetUpdateField; + template class OptionalUpdateFieldBase; @@ -260,34 +266,70 @@ namespace UF struct MapUpdateFieldSetter { template - friend void RemoveMapUpdateFieldValue(MapUpdateFieldSetter& setter, std::type_identity_t const& key); + friend bool RemoveMapUpdateFieldValue(MapUpdateFieldSetter& setter, std::type_identity_t const& key); MapUpdateFieldSetter(std::unordered_map& values) : _values(values) { } private: - void RemoveKey(K const& key) + bool RemoveKey(K const& key) { auto itr = _values.find(key); if (itr != _values.end()) + { itr->second.state = MapUpdateFieldState::Deleted; + return true; + } + return false; } std::unordered_map& _values; }; + template + struct SetUpdateFieldSetter + { + template + friend bool RemoveSetUpdateFieldValue(SetUpdateFieldSetter& setter, std::type_identity_t const& key); + + SetUpdateFieldSetter(std::unordered_map& values) : _values(values) { } + + private: + bool Insert(T const& key) + { + return _values.emplace(key, MapUpdateFieldState::Changed).second; + } + + bool Remove(T const& key) + { + auto itr = _values.find(key); + if (itr != _values.end()) + { + itr->second = MapUpdateFieldState::Deleted; + return true; + } + return false; + } + + std::unordered_map& _values; + }; + template struct OptionalUpdateFieldSetter { template - friend void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter& setter); + friend bool RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter& setter); OptionalUpdateFieldSetter(OptionalUpdateFieldBase& field) : _field(field) { } private: - void RemoveValue() + bool RemoveValue() { if (_field.has_value()) + { _field.DestroyValue(); + return true; + } + return false; } OptionalUpdateFieldBase& _field; @@ -397,6 +439,16 @@ namespace UF return { itr->second.value }; } + template + SetUpdateFieldSetter ModifyValue(SetUpdateField(T::* field)) + { + if constexpr (BlockBit >= 0) + _value._changesMask.Set(BlockBit); + + _value._changesMask.Set(Bit); + return { (_value.*field)._values }; + } + template OptionalUpdateFieldSetter ModifyValue(OptionalUpdateField(T::* field)) { @@ -639,6 +691,21 @@ namespace UF itr->second.state = MapUpdateFieldState::Changed; } + template + void MarkChanged(SetUpdateField(Derived::* field), std::type_identity_t const& key) + { + static_assert(std::is_base_of_v, "Given field argument must belong to the same structure as this HasChangesMask"); + + if constexpr (BlockBit >= 0) + _changesMask.Set(BlockBit); + + _changesMask.Set(Bit); + SetUpdateField& uf = (static_cast(this)->*field); + auto itr = uf._values.find(key); + if (itr != uf._values.end() && itr->second == MapUpdateFieldState::Unchanged) + itr->second = MapUpdateFieldState::Changed; + } + template void MarkChanged(OptionalUpdateField(Derived::*)) { @@ -704,6 +771,17 @@ namespace UF itr->second.state = MapUpdateFieldState::Unchanged; } + template + void ClearChanged(SetUpdateField(Derived::* field), std::type_identity_t const& key) + { + static_assert(std::is_base_of_v, "Given field argument must belong to the same structure as this HasChangesMask"); + + SetUpdateField& uf = (static_cast(this)->*field); + auto itr = uf._values.find(key); + if (itr != uf._values.end() && itr->second == MapUpdateFieldState::Changed) + itr->second = MapUpdateFieldState::Unchanged; + } + template void ClearChanged(OptionalUpdateField(Derived::*)) { @@ -759,7 +837,7 @@ namespace UF break; case MapUpdateFieldState::Changed: if constexpr (std::is_base_of_v) - itr->first.ClearChangesMask(); + const_cast(itr->first).ClearChangesMask(); if constexpr (std::is_base_of_v) itr->second.value.ClearChangesMask(); @@ -777,6 +855,32 @@ namespace UF } } + template + static inline void ClearChangesMask(SetUpdateFieldBase& field) + { + for (auto itr = field._values.begin(); itr != field._values.end(); ) + { + switch (itr->second) + { + case MapUpdateFieldState::Unchanged: + break; + case MapUpdateFieldState::Changed: + if constexpr (std::is_base_of_v) + const_cast(itr->first).ClearChangesMask(); + + itr->second = MapUpdateFieldState::Unchanged; + break; + case MapUpdateFieldState::Deleted: + itr = field._values.erase(itr++); + continue; + default: + break; + } + + ++itr; + } + } + template static inline void ClearChangesMask(OptionalUpdateFieldBase& field) { @@ -1085,6 +1189,55 @@ namespace UF { }; + template + class SetUpdateFieldBase : public IsUpdateFieldHolderTag + { + template + friend struct MutableFieldReferenceWithChangesMask; + + template + friend struct MutableFieldReferenceNoChangesMask; + + template + friend struct MutableNestedFieldReference; + + template + friend class HasChangesMask; + + public: + using key_type = T; + using mapped_type = MapUpdateFieldState; + using value_type = std::pair; + + typename std::unordered_map::const_iterator begin() const + { + return _values.begin(); + } + + typename std::unordered_map::const_iterator end() const + { + return _values.end(); + } + + bool empty() const + { + return _values.empty(); + } + + std::size_t size() const + { + return _values.size(); + } + + private: + std::unordered_map _values; + }; + + template + class SetUpdateField : public SetUpdateFieldBase + { + }; + template class OptionalUpdateFieldBase : public IsUpdateFieldHolderTag { diff --git a/src/server/game/Entities/Object/Updates/UpdateFieldImpl.h b/src/server/game/Entities/Object/Updates/UpdateFieldImpl.h index da0e68c3f3..5d81a7c8e5 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFieldImpl.h +++ b/src/server/game/Entities/Object/Updates/UpdateFieldImpl.h @@ -107,7 +107,7 @@ inline void WriteMapFieldUpdate(MapUpdateFieldBase const& map, ByteBuffer& ++changesCount; if constexpr (std::is_base_of_v) - k.WriteUpdate(data, true /*ignoreChangesMask*/, owner, receiver); + k.WriteUpdate(data, false, owner, receiver); else data << k; @@ -116,7 +116,7 @@ inline void WriteMapFieldUpdate(MapUpdateFieldBase const& map, ByteBuffer& continue; if constexpr (std::is_base_of_v) - v.value.WriteUpdate(data, true /*ignoreChangesMask*/, owner, receiver); // client bug replaces unchanged values with 0/default so send everything as if it changed + v.value.WriteUpdate(data, false, owner, receiver); else data << v.value; } @@ -124,6 +124,50 @@ inline void WriteMapFieldUpdate(MapUpdateFieldBase const& map, ByteBuffer& data.put(changesCountPos, changesCount); } } + +template +inline void WriteSetFieldCreate(SetUpdateFieldBase const& set, ByteBuffer& data, O const* owner, Player const* receiver) +{ + data << uint32(set.size()); + for (auto const& [k, _] : set) + { + if constexpr (std::is_base_of_v) + k.WriteCreate(data, owner, receiver); + else + data << k; + } +} + +template +inline void WriteSetFieldUpdate(SetUpdateFieldBase const& set, ByteBuffer& data, bool ignoreChangesMask, O const* owner, Player const* receiver) +{ + data << uint8(ignoreChangesMask ? 1 : 0); + if (ignoreChangesMask) + UF::WriteSetFieldCreate(set, data, owner, receiver); + else + { + uint16 changesCount = 0; + size_t changesCountPos = data.wpos(); + data << uint16(changesCount); + + for (auto const& [k, state] : set) + { + if (state == MapUpdateFieldState::Unchanged) + continue; + + ++changesCount; + + if constexpr (std::is_base_of_v) + k.WriteUpdate(data, false, owner, receiver); + else + data << k; + + data << uint8(state); + } + + data.put(changesCountPos, changesCount); + } +} } #endif // TRINITYCORE_UPDATE_FIELD_IMPL_H