diff options
| author | Shauren <shauren.trinity@gmail.com> | 2025-12-28 14:14:47 +0100 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2025-12-28 14:14:47 +0100 |
| commit | 42e9847b99dbeeb5a167f4c17f7eb9153dba015e (patch) | |
| tree | 5018ded0e556d18922354846bd1d54f5e16b1c00 | |
| parent | 61ce403d6f2b6ee8d270f52301a026d9a6533c04 (diff) | |
Core/Objects: Refactor building SMSG_UPDATE_OBJECT to make CGObject fragment optional as well as making integrating additional entity fragments easier
| -rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 28 | ||||
| -rw-r--r-- | src/server/game/Entities/Item/Item.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/BaseEntity.cpp | 34 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/BaseEntity.h | 18 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 34 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Object.h | 7 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/ObjectGuid.h | 2 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Updates/UpdateMask.h | 10 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp | 48 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h | 84 | ||||
| -rw-r--r-- | src/server/game/Entities/Transport/Transport.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Maps/Map.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Maps/Map.h | 7 |
13 files changed, 184 insertions, 94 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index b25514ef657..8da8904b845 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3297,7 +3297,7 @@ void Creature::SetVendor(NPCFlags flags, bool apply) if (apply) { if (!m_vendorData) - m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld()); + m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld(), WowCS::FragmentSerializationTraits<&Creature::m_vendorData>{}); SetNpcFlag(flags); SetUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(vendorFlags)); @@ -3319,7 +3319,7 @@ void Creature::SetPetitioner(bool apply) if (apply) { if (!m_vendorData) - m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld()); + m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld(), WowCS::FragmentSerializationTraits<&Creature::m_vendorData>{}); SetNpcFlag(UNIT_NPC_FLAG_PETITIONER); SetUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(VendorDataTypeFlags::Petition)); @@ -3868,31 +3868,17 @@ void Creature::BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Pl { m_objectData->WriteCreate(*data, flags, this, target); m_unitData->WriteCreate(*data, flags, this, target); - - if (m_vendorData) - { - if constexpr (WowCS::IsIndirectFragment(WowCS::EntityFragment::FVendor_C)) - *data << uint8(1); // IndirectFragmentActive: FVendor_C - - m_vendorData->WriteCreate(*data, flags, this, target); - } } void Creature::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { - if (m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment::CGObject)) - { - *data << uint32(m_values.GetChangedObjectTypeMask()); - - if (m_values.HasChanged(TYPEID_OBJECT)) - m_objectData->WriteUpdate(*data, flags, this, target); + *data << uint32(m_values.GetChangedObjectTypeMask()); - if (m_values.HasChanged(TYPEID_UNIT)) - m_unitData->WriteUpdate(*data, flags, this, target); - } + if (m_values.HasChanged(TYPEID_OBJECT)) + m_objectData->WriteUpdate(*data, flags, this, target); - if (m_vendorData && m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment::FVendor_C)) - m_vendorData->WriteUpdate(*data, flags, this, target); + if (m_values.HasChanged(TYPEID_UNIT)) + m_unitData->WriteUpdate(*data, flags, this, target); } void Creature::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index a10d829c6b7..e6536e3c91e 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -1786,6 +1786,8 @@ bool Item::IsBindedNotWith(Player const* player) const void Item::BuildUpdate(UpdateDataMapType& data_map) { + Object::BuildUpdateChangesMask(); + if (Player* owner = GetOwner()) BuildFieldsUpdate(owner, data_map); ClearUpdateMask(false); diff --git a/src/server/game/Entities/Object/BaseEntity.cpp b/src/server/game/Entities/Object/BaseEntity.cpp index 3559c851d80..316ef1b3201 100644 --- a/src/server/game/Entities/Object/BaseEntity.cpp +++ b/src/server/game/Entities/Object/BaseEntity.cpp @@ -103,8 +103,16 @@ void BaseEntity::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* targe buf << uint32(0); buf << uint8(fieldFlags); BuildEntityFragments(&buf, m_entityFragments.GetIds()); - buf << uint8(1); // IndirectFragmentActive: CGObject - BuildValuesCreate(&buf, fieldFlags, target); + + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + WowCS::EntityFragment fragmentId = m_entityFragments.Updateable.Ids[i]; + if (WowCS::IsIndirectFragment(fragmentId)) + buf << uint8(1); // IndirectFragmentActive + + m_entityFragments.Updateable.SerializeCreate[i](this, buf, fieldFlags, target); + } + buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); data->AddUpdateBlock(); @@ -140,13 +148,20 @@ void BaseEntity::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* } buf << uint8(m_entityFragments.ContentsChangedMask); - BuildValuesUpdate(&buf, fieldFlags, target); + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + if (!(m_entityFragments.ContentsChangedMask & m_entityFragments.Updateable.Masks[i])) + continue; + + m_entityFragments.Updateable.SerializeUpdate[i](this, buf, fieldFlags, target); + } + buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); data->AddUpdateBlock(); } -void BaseEntity::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) +inline void BaseEntity::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) { data->append(fragments.data(), fragments.size()); *data << uint8(WowCS::EntityFragment::End); @@ -635,6 +650,17 @@ void BaseEntity::ClearUpdateMask(bool remove) } } +void BaseEntity::BuildUpdateChangesMask() +{ + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + if (m_entityFragments.Updateable.IsChanged[i](this)) + m_entityFragments.ContentsChangedMask |= m_entityFragments.Updateable.Masks[i]; + else + m_entityFragments.ContentsChangedMask &= ~m_entityFragments.Updateable.Masks[i]; + } +} + void BaseEntity::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const { UpdateDataMapType::iterator iter = data_map.emplace(player, player->GetMapId()).first; diff --git a/src/server/game/Entities/Object/BaseEntity.h b/src/server/game/Entities/Object/BaseEntity.h index b227a13f884..3a3976fc297 100644 --- a/src/server/game/Entities/Object/BaseEntity.h +++ b/src/server/game/Entities/Object/BaseEntity.h @@ -189,6 +189,7 @@ class TC_GAME_API BaseEntity bool IsDestroyedObject() const { return m_isDestroyedObject; } void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; } virtual void BuildUpdate([[maybe_unused]] UpdateDataMapType& data_map) { } + void BuildUpdateChangesMask(); void BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const; friend UF::UpdateFieldHolder; @@ -322,8 +323,6 @@ class TC_GAME_API BaseEntity void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const; virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const; - virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; - virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments); TypeID m_objectTypeId = static_cast<TypeID>(NUM_CLIENT_OBJECT_TYPES); @@ -365,7 +364,6 @@ template <typename Derived, typename T, int32 BlockBit, uint32 Bit> inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field) { BaseEntity* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); @@ -376,7 +374,6 @@ template <typename Derived, typename T, int32 BlockBit, uint32 Bit> inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) { BaseEntity* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); @@ -387,7 +384,6 @@ template <typename Derived, typename T, int32 BlockBit, uint32 Bit> inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field, uint32 /*dummy*/) { BaseEntity* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); @@ -403,13 +399,7 @@ inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit { BaseEntity* owner = GetOwner(); if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - { _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); - if (!_changesMask) - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - } - else - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask(); } @@ -419,13 +409,7 @@ inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, Block { BaseEntity* owner = GetOwner(); if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - { _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); - if (!_changesMask) - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - } - else - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); auto& uf = (static_cast<Derived*>(owner)->*field); if (uf.has_value()) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 9bf79150cb9..148f0ceca21 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -64,9 +64,9 @@ constexpr float VisibilityDistances[AsUnderlyingType(VisibilityDistanceType::Max Object::Object() : m_scriptRef(this, NoopObjectDeleter()) { m_objectTypeId = TYPEID_OBJECT; - m_updateFlag.Clear(); - m_entityFragments.Add(WowCS::EntityFragment::CGObject, false); + m_entityFragments.Add(WowCS::EntityFragment::CGObject, false, + &Object::BuildObjectFragmentCreate, &Object::BuildObjectFragmentUpdate, &Object::IsObjectFragmentChanged); } Object::~Object() = default; @@ -103,10 +103,15 @@ void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::Updat void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const { - uint8 contentsChangedMask = WowCS::CGObjectChangedMask; - for (WowCS::EntityFragment updateableFragmentId : m_entityFragments.GetUpdateableIds()) - if (WowCS::IsIndirectFragment(updateableFragmentId)) - contentsChangedMask |= m_entityFragments.GetUpdateMaskFor(updateableFragmentId) >> 1; // set the "fragment exists" bit + uint8 contentsChangedMask = 0; + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + if (WowCS::IsIndirectFragment(m_entityFragments.Updateable.Ids[i])) + contentsChangedMask |= m_entityFragments.Updateable.Masks[i] >> 1; // set the "fragment exists" bit + + if (m_entityFragments.Updateable.Ids[i] == WowCS::EntityFragment::CGObject) + contentsChangedMask |= m_entityFragments.Updateable.Masks[i]; + } *data << uint8(flags.HasFlag(UF::UpdateFieldFlag::Owner)); *data << uint8(false); // m_entityFragments.IdsChanged @@ -124,6 +129,21 @@ void Object::ClearUpdateMask(bool remove) BaseEntity::ClearUpdateMask(remove); } +void Object::BuildObjectFragmentCreate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) +{ + static_cast<Object const*>(entity)->BuildValuesCreate(&data, flags, target); +} + +void Object::BuildObjectFragmentUpdate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) +{ + static_cast<Object const*>(entity)->BuildValuesUpdate(&data, flags, target); +} + +bool Object::IsObjectFragmentChanged(BaseEntity const* entity) +{ + return entity->m_values.GetChangedObjectTypeMask() != 0; +} + std::string Object::GetDebugInfo() const { std::stringstream sstr; @@ -3097,6 +3117,8 @@ struct WorldObjectChangeAccumulator void WorldObject::BuildUpdate(UpdateDataMapType& data_map) { + Object::BuildUpdateChangesMask(); + WorldObjectChangeAccumulator notifier(*this, data_map); WorldObjectVisibleChangeVisitor visitor(notifier); //we must build packets for all visible players diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index f49614657f6..f5cdd6185e4 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -173,14 +173,17 @@ class TC_GAME_API Object : public BaseEntity protected: Object(); - void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0; - void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0; + virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; + virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const; public: virtual void BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const; private: + static void BuildObjectFragmentCreate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target); + static void BuildObjectFragmentUpdate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target); + static bool IsObjectFragmentChanged(BaseEntity const* entity); struct NoopObjectDeleter { void operator()(Object*) const { /*noop - not managed*/ } }; Trinity::unique_trackable_ptr<Object> m_scriptRef; diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index 0ba79357ecb..d24c228c872 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -78,7 +78,7 @@ enum TypeMask TYPEMASK_LOOT_OBJECT = 1 << TYPEID_LOOT_OBJECT, TYPEMASK_SEER = TYPEMASK_UNIT | TYPEMASK_PLAYER | TYPEMASK_DYNAMICOBJECT, - TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION + TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION | TYPEMASK_MESH_OBJECT }; inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES + 1> ObjectTypeMask = diff --git a/src/server/game/Entities/Object/Updates/UpdateMask.h b/src/server/game/Entities/Object/Updates/UpdateMask.h index 3071632d603..0e3fd9f32ae 100644 --- a/src/server/game/Entities/Object/Updates/UpdateMask.h +++ b/src/server/game/Entities/Object/Updates/UpdateMask.h @@ -19,7 +19,6 @@ #define UpdateMask_h__ #include "Define.h" -#include <algorithm> #include <cstring> // std::memset namespace UpdateMaskHelpers @@ -68,10 +67,11 @@ public: constexpr bool IsAnySet() const { - return std::ranges::any_of(_blocksMask, [](uint32 blockMask) - { - return blockMask != 0; - }); + for (uint32 i = 0; i < BlocksMaskCount; ++i) + if (_blocksMask[i]) + return true; + + return false; } constexpr void Reset(uint32 index) diff --git a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp index b89d7aab275..b80541d547d 100644 --- a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp +++ b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp @@ -21,7 +21,8 @@ namespace WowCS { -void EntityFragmentsHolder::Add(EntityFragment fragment, bool update) +void EntityFragmentsHolder::Add(EntityFragment fragment, bool update, + EntityFragmentSerializeFn serializeCreate, EntityFragmentSerializeFn serializeUpdate, EntityFragmentIsChangedFn isChanged) { ASSERT(Count < Ids.size()); @@ -43,23 +44,34 @@ void EntityFragmentsHolder::Add(EntityFragment fragment, bool update) if (IsUpdateableFragment(fragment)) { - ASSERT(UpdateableCount < UpdateableIds.size()); + ASSERT(UpdateableCount < Updateable.Ids.size()); + ASSERT(serializeCreate && serializeUpdate && isChanged); - auto insertedItr = insertSorted(UpdateableIds, UpdateableCount, fragment).first; - std::ptrdiff_t index = std::ranges::distance(UpdateableIds.begin(), insertedItr); + auto insertedItr = insertSorted(Updateable.Ids, UpdateableCount, fragment).first; + std::ptrdiff_t index = std::ranges::distance(Updateable.Ids.begin(), insertedItr); uint8 maskLowPart = ContentsChangedMask & ((1 << index) - 1); uint8 maskHighPart = (ContentsChangedMask & ~((1 << index) - 1)) << (1 + IsIndirectFragment(fragment)); ContentsChangedMask = maskLowPart | maskHighPart; for (uint8 i = 0, maskIndex = 0; i < UpdateableCount; ++i) { - UpdateableMasks[i] = 1 << maskIndex++; - if (IsIndirectFragment(UpdateableIds[i])) + Updateable.Masks[i] = 1 << maskIndex++; + if (IsIndirectFragment(Updateable.Ids[i])) { - ContentsChangedMask |= UpdateableMasks[i]; // set the first bit to true to activate fragment + ContentsChangedMask |= Updateable.Masks[i]; // set the first bit to true to activate fragment ++maskIndex; - UpdateableMasks[i] <<= 1; + Updateable.Masks[i] <<= 1; } } + + auto insertAtIndex = []<typename T, size_t N>(std::array<T, N>& arr, uint8 size, std::ptrdiff_t i, T value) + { + std::ranges::move_backward(arr.begin() + i, arr.begin() + size - 1, arr.begin() + size); + arr[i] = value; + }; + + insertAtIndex(Updateable.SerializeCreate, UpdateableCount, index, serializeCreate); + insertAtIndex(Updateable.SerializeUpdate, UpdateableCount, index, serializeUpdate); + insertAtIndex(Updateable.IsChanged, UpdateableCount, index, isChanged); } if (update) @@ -86,22 +98,32 @@ void EntityFragmentsHolder::Remove(EntityFragment fragment) if (IsUpdateableFragment(fragment)) { - auto [removedItr, removed] = removeSorted(UpdateableIds, UpdateableCount, fragment); + auto [removedItr, removed] = removeSorted(Updateable.Ids, UpdateableCount, fragment); if (removed) { - std::ptrdiff_t index = std::ranges::distance(UpdateableIds.begin(), removedItr); + std::ptrdiff_t index = std::ranges::distance(Updateable.Ids.begin(), removedItr); uint8 maskLowPart = ContentsChangedMask & ((1 << index) - 1); uint8 maskHighPart = (ContentsChangedMask & ~((1 << index) - 1)) >> (1 + IsIndirectFragment(fragment)); ContentsChangedMask = maskLowPart | maskHighPart; for (uint8 i = 0, maskIndex = 0; i < UpdateableCount; ++i) { - UpdateableMasks[i] = 1 << maskIndex++; - if (IsIndirectFragment(UpdateableIds[i])) + Updateable.Masks[i] = 1 << maskIndex++; + if (IsIndirectFragment(Updateable.Ids[i])) { ++maskIndex; - UpdateableMasks[i] <<= 1; + Updateable.Masks[i] <<= 1; } } + + auto removeAtIndex = []<typename T, size_t N>(std::array<T, N>& arr, uint8 oldSize, std::ptrdiff_t i, std::type_identity_t<T> value) + { + *std::ranges::move(arr.begin() + i + 1, arr.begin() + oldSize, arr.begin() + i).out = value; + }; + + uint8 oldSize = UpdateableCount + 1; + removeAtIndex(Updateable.SerializeCreate, oldSize, index, nullptr); + removeAtIndex(Updateable.SerializeUpdate, oldSize, index, nullptr); + removeAtIndex(Updateable.IsChanged, oldSize, index, nullptr); } } diff --git a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h index f3d8182ff83..d8e501e20ba 100644 --- a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h +++ b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h @@ -22,6 +22,15 @@ #include <array> #include <span> +class BaseEntity; +class ByteBuffer; +class Player; + +namespace UF +{ +enum class UpdateFieldFlag : uint8; +} + namespace WowCS { enum class EntityFragment : uint8 @@ -99,10 +108,35 @@ inline constexpr bool IsIndirectFragment(EntityFragment frag) || frag == EntityFragment::PlayerHouseInfoComponent_C; } -// common case optimization, make use of the fact that fragment arrays are sorted -inline constexpr uint8 CGObjectActiveMask = 0x1; -inline constexpr uint8 CGObjectChangedMask = 0x2; -inline constexpr uint8 CGObjectUpdateMask = CGObjectActiveMask | CGObjectChangedMask; +template <auto /*FragmentMemberPtr*/> +struct FragmentSerializationTraits +{ +}; + +template <typename Entity, typename Fragment, Fragment Entity::* FragmentData> +struct FragmentSerializationTraits<FragmentData> +{ + static void BuildCreate(BaseEntity const* baseEntity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) + { + Entity const* entity = static_cast<Entity const*>(baseEntity); + (entity->*FragmentData)->WriteCreate(data, flags, entity, target); + } + + static void BuildUpdate(BaseEntity const* baseEntity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) + { + Entity const* entity = static_cast<Entity const*>(baseEntity); + (entity->*FragmentData)->WriteUpdate(data, flags, entity, target); + } + + static bool IsChanged(BaseEntity const* baseEntity) + { + Entity const* entity = static_cast<Entity const*>(baseEntity); + return (entity->*FragmentData)->GetChangesMask().IsAnySet(); + } +}; + +using EntityFragmentSerializeFn = void (*)(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target); +using EntityFragmentIsChangedFn = bool (*)(BaseEntity const* entity); struct EntityFragmentsHolder { @@ -111,34 +145,42 @@ struct EntityFragmentsHolder EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End }; - uint8 Count = 0; - bool IdsChanged = false; - std::array<EntityFragment, 4> UpdateableIds = + template <std::size_t N> + struct UpdateableFragments { - EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End + std::array<EntityFragment, N> Ids = + { + EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End + }; + std::array<uint8, N> Masks = { }; + std::array<EntityFragmentSerializeFn, N> SerializeCreate = { }; + std::array<EntityFragmentSerializeFn, N> SerializeUpdate = { }; + std::array<EntityFragmentIsChangedFn, N> IsChanged = { }; }; - std::array<uint8, 4> UpdateableMasks = { }; + + UpdateableFragments<4> Updateable; + + uint8 Count = 0; + bool IdsChanged = false; + uint8 UpdateableCount = 0; uint8 ContentsChangedMask = 0; - void Add(EntityFragment fragment, bool update); - void Remove(EntityFragment fragment); + void Add(EntityFragment fragment, bool update, + EntityFragmentSerializeFn serializeCreate, EntityFragmentSerializeFn serializeUpdate, EntityFragmentIsChangedFn isChanged); - std::span<EntityFragment const> GetIds() const { return std::span(Ids.begin(), Count); } - std::span<EntityFragment const> GetUpdateableIds() const { return std::span(UpdateableIds.begin(), UpdateableCount); } + inline void Add(EntityFragment fragment, bool update) { Add(fragment, update, nullptr, nullptr, nullptr); } - uint8 GetUpdateMaskFor(EntityFragment fragment) const + template <typename SerializationTraits> + inline void Add(EntityFragment fragment, bool update, SerializationTraits) { - if (fragment == EntityFragment::CGObject) // common case optimization, make use of the fact that fragment arrays are sorted - return CGObjectChangedMask; + Add(fragment, update, &SerializationTraits::BuildCreate, &SerializationTraits::BuildUpdate, &SerializationTraits::IsChanged); + } - for (uint8 i = 1; i < UpdateableCount; ++i) - if (UpdateableIds[i] == fragment) - return UpdateableMasks[i]; + void Remove(EntityFragment fragment); - return 0; - } + std::span<EntityFragment const> GetIds() const { return std::span(Ids.begin(), Count); } }; enum class EntityFragmentSerializationType : uint8 diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 3bca6f5d9cf..b17921d3840 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -689,6 +689,8 @@ void Transport::UpdatePassengerPositions(PassengerSet const& passengers) void Transport::BuildUpdate(UpdateDataMapType& data_map) { + Object::BuildUpdateChangesMask(); + for (MapReference const& playerReference : GetMap()->GetPlayers()) if (playerReference.GetSource()->InSamePhase(this)) BuildFieldsUpdate(playerReference.GetSource(), data_map); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index c886f41ae44..0ea2ff54b80 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1967,7 +1967,7 @@ void Map::SendObjectUpdates() while (!_updateObjects.empty()) { - Object* obj = *_updateObjects.begin(); + BaseEntity* obj = *_updateObjects.begin(); ASSERT(obj->IsInWorld()); _updateObjects.erase(_updateObjects.begin()); obj->BuildUpdate(update_players); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index c3fcfeb1aa4..d78bb09700b 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -45,6 +45,7 @@ #include <set> #include <unordered_set> +class BaseEntity; class Battleground; class BattlegroundMap; class BattlegroundScript; @@ -567,12 +568,12 @@ class TC_GAME_API Map : public GridRefManager<NGridType> return GetGuidSequenceGenerator(high).GetNextAfterMaxUsed(); } - void AddUpdateObject(Object* obj) + void AddUpdateObject(BaseEntity* obj) { _updateObjects.insert(obj); } - void RemoveUpdateObject(Object* obj) + void RemoveUpdateObject(BaseEntity* obj) { _updateObjects.erase(obj); } @@ -821,7 +822,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType> std::unordered_map<ObjectGuid, Corpse*> _corpsesByPlayer; std::unordered_set<Corpse*> _corpseBones; - std::unordered_set<Object*> _updateObjects; + std::unordered_set<BaseEntity*> _updateObjects; MPSCQueue<FarSpellCallback> _farSpellCallbacks; |
