aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2025-12-28 14:14:47 +0100
committerShauren <shauren.trinity@gmail.com>2025-12-28 14:14:47 +0100
commit42e9847b99dbeeb5a167f4c17f7eb9153dba015e (patch)
tree5018ded0e556d18922354846bd1d54f5e16b1c00
parent61ce403d6f2b6ee8d270f52301a026d9a6533c04 (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.cpp28
-rw-r--r--src/server/game/Entities/Item/Item.cpp2
-rw-r--r--src/server/game/Entities/Object/BaseEntity.cpp34
-rw-r--r--src/server/game/Entities/Object/BaseEntity.h18
-rw-r--r--src/server/game/Entities/Object/Object.cpp34
-rw-r--r--src/server/game/Entities/Object/Object.h7
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h2
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateMask.h10
-rw-r--r--src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp48
-rw-r--r--src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h84
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp2
-rw-r--r--src/server/game/Maps/Map.cpp2
-rw-r--r--src/server/game/Maps/Map.h7
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;