diff options
37 files changed, 764 insertions, 155 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 5405bd38baa..90e49df4716 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -54,6 +54,8 @@ AreaTrigger::AreaTrigger() : WorldObject(false), MapObject(), _spawnId(0), _aurE m_updateFlag.Stationary = true; m_updateFlag.AreaTrigger = true; + + m_entityFragments.Add(WowCS::EntityFragment::Tag_AreaTrigger, false); } AreaTrigger::~AreaTrigger() @@ -193,8 +195,8 @@ bool AreaTrigger::Create(AreaTriggerCreatePropertiesId areaTriggerCreateProperti SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::VisualAnim).ModifyValue(&UF::VisualAnim::AnimationDataID), GetCreateProperties()->AnimId); SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::VisualAnim).ModifyValue(&UF::VisualAnim::AnimKitID), GetCreateProperties()->AnimKitId); - if (GetCreateProperties() && GetCreateProperties()->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::Unk3)) - SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::VisualAnim).ModifyValue(&UF::VisualAnim::Field_C), true); + if (GetCreateProperties()->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::VisualAnimIsDecay)) + SetUpdateFieldValue(areaTriggerData.ModifyValue(&UF::AreaTriggerData::VisualAnim).ModifyValue(&UF::VisualAnim::IsDecay), true); if (caster) PhasingHandler::InheritPhaseShift(this, caster); @@ -1393,6 +1395,7 @@ void AreaTrigger::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, void AreaTrigger::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::AreaTriggerData::Mask const& requestedAreaTriggerMask, Player const* target) const { + UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask<NUM_CLIENT_OBJECT_TYPES> valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); @@ -1403,6 +1406,7 @@ void AreaTrigger::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::Objec ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h index ba2455751ad..708fd242b33 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h @@ -164,7 +164,7 @@ class TC_GAME_API AreaTrigger final : public WorldObject, public GridObject<Area void UpdateShape(); - UF::UpdateField<UF::AreaTriggerData, 0, TYPEID_AREATRIGGER> m_areaTriggerData; + UF::UpdateField<UF::AreaTriggerData, int32(WowCS::EntityFragment::CGObject), TYPEID_AREATRIGGER> m_areaTriggerData; protected: void _UpdateDuration(int32 newDuration); diff --git a/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h b/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h index cbf281a3152..cde3d4f29bf 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h +++ b/src/server/game/Entities/AreaTrigger/AreaTriggerTemplate.h @@ -67,7 +67,7 @@ enum class AreaTriggerCreatePropertiesFlag : uint32 Unk1 = 0x00020, HasTargetRollPitchYaw = 0x00040, // NYI HasAnimId = 0x00080, // DEPRECATED - Unk3 = 0x00100, + VisualAnimIsDecay = 0x00100, HasAnimKitId = 0x00200, // DEPRECATED HasCircularMovement = 0x00400, // DEPRECATED Unk5 = 0x00800, diff --git a/src/server/game/Entities/Conversation/Conversation.cpp b/src/server/game/Entities/Conversation/Conversation.cpp index 0fdf9b708a3..41abd36fc54 100644 --- a/src/server/game/Entities/Conversation/Conversation.cpp +++ b/src/server/game/Entities/Conversation/Conversation.cpp @@ -40,6 +40,8 @@ Conversation::Conversation() : WorldObject(false), _duration(0), _textureKitId(0 m_updateFlag.Stationary = true; m_updateFlag.Conversation = true; + m_entityFragments.Add(WowCS::EntityFragment::Tag_Conversation, false); + _lastLineEndTimes.fill(Milliseconds::zero()); } @@ -373,6 +375,7 @@ void Conversation::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags void Conversation::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::ConversationData::Mask const& requestedConversationMask, Player const* target) const { + UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask<NUM_CLIENT_OBJECT_TYPES> valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); @@ -383,6 +386,7 @@ void Conversation::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::Obje ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/Conversation/Conversation.h b/src/server/game/Entities/Conversation/Conversation.h index 5d84b1818d3..4f85809b5ba 100644 --- a/src/server/game/Entities/Conversation/Conversation.h +++ b/src/server/game/Entities/Conversation/Conversation.h @@ -92,7 +92,7 @@ class TC_GAME_API Conversation final : public WorldObject, public GridObject<Con ConversationAI* AI() { return _ai.get(); } uint32 GetScriptId() const; - UF::UpdateField<UF::ConversationData, 0, TYPEID_CONVERSATION> m_conversationData; + UF::UpdateField<UF::ConversationData, int32(WowCS::EntityFragment::CGObject), TYPEID_CONVERSATION> m_conversationData; private: Position _stationaryPosition; diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 41ef20445f9..225650bb472 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -37,6 +37,8 @@ Corpse::Corpse(CorpseType type) : WorldObject(type != CORPSE_BONES), m_type(type m_updateFlag.Stationary = true; + m_entityFragments.Add(WowCS::EntityFragment::Tag_Corpse, false); + m_time = GameTime::GetGameTime(); lootRecipient = nullptr; @@ -258,6 +260,7 @@ void Corpse::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Play void Corpse::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::CorpseData::Mask const& requestedCorpseMask, Player const* target) const { + UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask<NUM_CLIENT_OBJECT_TYPES> valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); @@ -268,6 +271,7 @@ void Corpse::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index 6fe4d4df213..3af6cb7b58f 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -135,7 +135,7 @@ class TC_GAME_API Corpse final : public WorldObject, public GridObject<Corpse> bool IsExpired(time_t t) const; - UF::UpdateField<UF::CorpseData, 0, TYPEID_CORPSE> m_corpseData; + UF::UpdateField<UF::CorpseData, int32(WowCS::EntityFragment::CGObject), TYPEID_CORPSE> m_corpseData; private: CorpseType m_type; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 2a3767f865b..64e7242b34d 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -610,9 +610,17 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT) npcFlags |= sGameEventMgr->GetNPCFlag(this); + if (IsVendor() && !(npcFlags & UNIT_NPC_FLAG_VENDOR_MASK)) + SetVendor(UNIT_NPC_FLAG_VENDOR_MASK, false); + ReplaceAllNpcFlags(NPCFlags(npcFlags & 0xFFFFFFFF)); ReplaceAllNpcFlags2(NPCFlags2(npcFlags >> 32)); + if (npcFlags & UNIT_NPC_FLAG_VENDOR_MASK) + SetVendor(NPCFlags(npcFlags & UNIT_NPC_FLAG_VENDOR_MASK), true); + + SetPetitioner((npcFlags & UNIT_NPC_FLAG_PETITIONER) != 0); + // if unit is in combat, keep this flag unitFlags &= ~UNIT_FLAG_IN_COMBAT; if (IsInCombat()) @@ -3090,6 +3098,52 @@ uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 us return vCount->count; } +void Creature::SetVendor(NPCFlags flags, bool apply) +{ + flags &= UNIT_NPC_FLAG_VENDOR_MASK; + VendorDataTypeFlags vendorFlags = static_cast<VendorDataTypeFlags>(AsUnderlyingType(flags) >> 7); + if (apply) + { + if (!m_vendorData) + m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld()); + + SetNpcFlag(flags); + SetUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(vendorFlags)); + } + else if (m_vendorData) + { + RemoveNpcFlag(flags); + RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(vendorFlags)); + if (!m_vendorData->Flags) + { + RemoveOptionalUpdateFieldValue(m_values.ModifyValue(&Creature::m_vendorData)); + m_entityFragments.Remove(WowCS::EntityFragment::FVendor_C); + } + } +} + +void Creature::SetPetitioner(bool apply) +{ + if (apply) + { + if (!m_vendorData) + m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld()); + + SetNpcFlag(UNIT_NPC_FLAG_PETITIONER); + SetUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(VendorDataTypeFlags::Petition)); + } + else if (m_vendorData) + { + RemoveNpcFlag(UNIT_NPC_FLAG_PETITIONER); + RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(VendorDataTypeFlags::Petition)); + if (!m_vendorData->Flags) + { + RemoveOptionalUpdateFieldValue(m_values.ModifyValue(&Creature::m_vendorData)); + m_entityFragments.Remove(WowCS::EntityFragment::FVendor_C); + } + } +} + // overwrite WorldObject function for proper name localization std::string Creature::GetNameForLocaleIdx(LocaleConstant locale) const { @@ -3612,17 +3666,26 @@ 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) + m_vendorData->WriteCreate(*data, flags, this, target); } void Creature::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { - *data << uint32(m_values.GetChangedObjectTypeMask()); + 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); - if (m_values.HasChanged(TYPEID_OBJECT)) - m_objectData->WriteUpdate(*data, flags, this, target); + if (m_values.HasChanged(TYPEID_UNIT)) + m_unitData->WriteUpdate(*data, flags, this, target); + } - if (m_values.HasChanged(TYPEID_UNIT)) - m_unitData->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); } void Creature::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const @@ -3653,6 +3716,7 @@ void Creature::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectDa ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) @@ -3676,3 +3740,11 @@ void Creature::ValuesUpdateForPlayerWithMaskSender::operator()(Player const* pla udata.BuildPacket(&packet); player->SendDirectMessage(&packet); } + +void Creature::ClearUpdateMask(bool remove) +{ + if (m_vendorData) + m_values.ClearChangesMask(&Creature::m_vendorData); + + Unit::ClearUpdateMask(remove); +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 601df6b2ae6..67769057270 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -59,6 +59,18 @@ enum class VendorInventoryReason : uint8 Empty = 1 }; +enum class VendorDataTypeFlags : int32 +{ + Generic = 0x01, + Ammo = 0x02, + Food = 0x04, + Poison = 0x08, + Reagent = 0x10, + Petition = 0x20, +}; + +DEFINE_ENUM_FLAG(VendorDataTypeFlags); + static constexpr uint8 WILD_BATTLE_PET_DEFAULT_LEVEL = 1; static constexpr size_t CREATURE_TAPPERS_SOFT_CAP = 1; // Classic Only - only allow one tapper at a time @@ -238,6 +250,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma VendorItemData const* GetVendorItems() const; uint32 GetVendorItemCurrentCount(VendorItem const* vItem); uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count); + void SetVendor(NPCFlags flags, bool apply); + void SetPetitioner(bool apply); CreatureTemplate const* GetCreatureTemplate() const { return m_creatureInfo; } CreatureData const* GetCreatureData() const { return m_creatureData; } @@ -463,6 +477,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void InitializeInteractSpellId(); void SetInteractSpellId(int32 interactSpellId) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::InteractSpellID), interactSpellId); } + UF::OptionalUpdateField<UF::VendorData, int32(WowCS::EntityFragment::FVendor_C), 0> m_vendorData; + protected: void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override; void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override; @@ -484,6 +500,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma }; protected: + void ClearUpdateMask(bool remove) override; + bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); bool InitEntry(uint32 entry, CreatureData const* data = nullptr); diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index 6344c627386..0d6ae38f199 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -38,6 +38,8 @@ DynamicObject::DynamicObject(bool isWorldObject) : WorldObject(isWorldObject), m_objectTypeId = TYPEID_DYNAMICOBJECT; m_updateFlag.Stationary = true; + + m_entityFragments.Add(WowCS::EntityFragment::Tag_DynamicObject, false); } DynamicObject::~DynamicObject() @@ -271,6 +273,7 @@ void DynamicObject::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flag void DynamicObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::DynamicObjectData::Mask const& requestedDynamicObjectMask, Player const* target) const { + UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask<NUM_CLIENT_OBJECT_TYPES> valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); @@ -281,6 +284,7 @@ void DynamicObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::Obj ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h index 1f8bb2a0b1c..79c66f9a460 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -83,7 +83,7 @@ class TC_GAME_API DynamicObject final : public WorldObject, public GridObject<Dy ObjectGuid GetOwnerGUID() const override { return GetCasterGUID(); } float GetRadius() const { return m_dynamicObjectData->Radius; } - UF::UpdateField<UF::DynamicObjectData, 0, TYPEID_DYNAMICOBJECT> m_dynamicObjectData; + UF::UpdateField<UF::DynamicObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_DYNAMICOBJECT> m_dynamicObjectData; protected: Aura* _aura; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 786823df9cd..ccca77a0f2a 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -842,6 +842,8 @@ GameObject::GameObject() : WorldObject(false), MapObject(), m_updateFlag.Stationary = true; m_updateFlag.Rotation = true; + m_entityFragments.Add(WowCS::EntityFragment::Tag_GameObject, false); + m_respawnTime = 0; m_respawnDelayTime = 300; m_despawnDelay = 0; @@ -1100,6 +1102,7 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD } case GAMEOBJECT_TYPE_TRANSPORT: { + m_updateFlag.GameObject = true; m_goTypeImpl = std::make_unique<GameObjectType::Transport>(*this); if (goInfo->transport.startOpen) SetGoState(GO_STATE_TRANSPORT_STOPPED); @@ -3980,6 +3983,7 @@ void GameObject::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, void GameObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::GameObjectData::Mask const& requestedGameObjectMask, Player const* target) const { + UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask<NUM_CLIENT_OBJECT_TYPES> valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); @@ -3990,6 +3994,7 @@ void GameObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::Object ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) @@ -4030,20 +4035,7 @@ std::vector<uint32> const* GameObject::GetPauseTimes() const void GameObject::SetPathProgressForClient(float progress) { - DoWithSuppressingObjectUpdates([&]() - { - UF::ObjectData::Base dynflagMask; - dynflagMask.MarkChanged(&UF::ObjectData::DynamicFlags); - bool marked = (m_objectData->GetChangesMask() & dynflagMask.GetChangesMask()).IsAnySet(); - - uint32 dynamicFlags = GetDynamicFlags(); - dynamicFlags &= 0xFFFF; // remove high bits - dynamicFlags |= uint32(progress * 65535.0f) << 16; - ReplaceAllDynamicFlags(dynamicFlags); - - if (!marked) - const_cast<UF::ObjectData&>(*m_objectData).ClearChanged(&UF::ObjectData::DynamicFlags); - }); + m_transportPathProgress = progress; } void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 10670b70c50..e00e103ae67 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -288,6 +288,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> static void SetGoArtKit(uint32 artkit, GameObject* go, ObjectGuid::LowType lowguid = UI64LIT(0)); std::vector<uint32> const* GetPauseTimes() const; + Optional<float> GetPathProgressForClient() const { return m_transportPathProgress; } void SetPathProgressForClient(float progress); void EnableCollision(bool enable); @@ -450,7 +451,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const& command) const; - UF::UpdateField<UF::GameObjectData, 0, TYPEID_GAMEOBJECT> m_gameObjectData; + UF::UpdateField<UF::GameObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_GAMEOBJECT> m_gameObjectData; protected: void CreateModel(); @@ -511,6 +512,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> bool m_respawnCompatibilityMode; uint16 _animKitId; uint32 _worldEffectID; + Optional<float> m_transportPathProgress; std::unique_ptr<Vignettes::VignetteData> m_vignette; diff --git a/src/server/game/Entities/Item/Container/Bag.cpp b/src/server/game/Entities/Item/Container/Bag.cpp index 14ff0bc52d4..d885623eeed 100644 --- a/src/server/game/Entities/Item/Container/Bag.cpp +++ b/src/server/game/Entities/Item/Container/Bag.cpp @@ -30,6 +30,8 @@ Bag::Bag(): Item() m_objectType |= TYPEMASK_CONTAINER; m_objectTypeId = TYPEID_CONTAINER; + m_entityFragments.Add(WowCS::EntityFragment::Tag_Container, false); + memset(m_bagslot, 0, sizeof(Item*) * MAX_BAG_SIZE); } @@ -222,6 +224,7 @@ void Bag::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::M ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h index 12d40c84553..502560d1b24 100644 --- a/src/server/game/Entities/Item/Container/Bag.h +++ b/src/server/game/Entities/Item/Container/Bag.h @@ -76,7 +76,7 @@ class TC_GAME_API Bag : public Item std::string GetDebugInfo() const override; - UF::UpdateField<UF::ContainerData, 0, TYPEID_CONTAINER> m_containerData; + UF::UpdateField<UF::ContainerData, int32(WowCS::EntityFragment::CGObject), TYPEID_CONTAINER> m_containerData; protected: void SetBagSize(uint32 numSlots) { SetUpdateFieldValue(m_values.ModifyValue(&Bag::m_containerData).ModifyValue(&UF::ContainerData::NumSlots), numSlots); } diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 79d5af6e708..0891ac897c5 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -302,6 +302,8 @@ Item::Item() m_objectType |= TYPEMASK_ITEM; m_objectTypeId = TYPEID_ITEM; + m_entityFragments.Add(WowCS::EntityFragment::Tag_Item, false); + m_slot = 0; uState = ITEM_NEW; uQueuePos = -1; @@ -1498,6 +1500,7 @@ void Item::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData:: ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 1634a7441d6..9066b46ba45 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -162,10 +162,10 @@ class TC_GAME_API Item : public Object void RemoveItemFlag(ItemFieldFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags), flags); } void ReplaceAllItemFlags(ItemFieldFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags), flags); } - bool HasItemFlag2(ItemFieldFlags2 flag) const { return (*m_itemData->DynamicFlags2 & flag) != 0; } - void SetItemFlag2(ItemFieldFlags2 flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags2), flags); } - void RemoveItemFlag2(ItemFieldFlags2 flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags2), flags); } - void ReplaceAllItemFlags2(ItemFieldFlags2 flags) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags2), flags); } + bool HasItemZoneFlag(ItemZoneFlags flag) const { return (*m_itemData->ZoneFlags & flag) != 0; } + void SetItemZoneFlag(ItemZoneFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ZoneFlags), flags); } + void RemoveItemZoneFlag(ItemZoneFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ZoneFlags), flags); } + void ReplaceAllItemZoneFlags(ItemZoneFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ZoneFlags), flags); } bool IsSoulBound() const { return HasItemFlag(ITEM_FIELD_FLAG_SOULBOUND); } bool IsBoundAccountWide() const { return GetTemplate()->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT); } @@ -381,7 +381,7 @@ class TC_GAME_API Item : public Object std::string GetDebugInfo() const override; - UF::UpdateField<UF::ItemData, 0, TYPEID_ITEM> m_itemData; + UF::UpdateField<UF::ItemData, uint32(WowCS::EntityFragment::CGObject), TYPEID_ITEM> m_itemData; protected: BonusData _bonusData; diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 99da24c81d3..8656c7a65a5 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -170,12 +170,12 @@ enum ItemFieldFlags : uint32 DEFINE_ENUM_FLAG(ItemFieldFlags); -enum ItemFieldFlags2 : uint32 +enum ItemZoneFlags : uint32 { ITEM_FIELD_FLAG2_EQUIPPED = 0x1 }; -DEFINE_ENUM_FLAG(ItemFieldFlags2); +DEFINE_ENUM_FLAG(ItemZoneFlags); enum ItemFlags : uint32 { diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index b4dfb63d707..a819e0f82e9 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -76,6 +76,8 @@ Object::Object() : m_scriptRef(this, NoopObjectDeleter()) m_objectType = TYPEMASK_OBJECT; m_updateFlag.Clear(); + m_entityFragments.Add(WowCS::EntityFragment::CGObject, false); + m_inWorld = false; m_isNewObject = false; m_isDestroyedObject = false; @@ -182,6 +184,8 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c std::size_t sizePos = buf.wpos(); buf << uint32(0); buf << uint8(fieldFlags); + BuildEntityFragments(&buf, m_entityFragments.GetIds()); + buf << uint8(1); // IndirectFragmentActive: CGObject BuildValuesCreate(&buf, fieldFlags, target); buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); @@ -209,6 +213,15 @@ void Object::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* tar EnumFlag<UF::UpdateFieldFlag> fieldFlags = GetUpdateFieldFlagsFor(target); std::size_t sizePos = buf.wpos(); buf << uint32(0); + buf << uint8(fieldFlags.HasFlag(UF::UpdateFieldFlag::Owner)); + buf << uint8(m_entityFragments.IdsChanged); + if (m_entityFragments.IdsChanged) + { + buf << uint8(WowCS::EntityFragmentSerializationType::Full); + BuildEntityFragments(&buf, m_entityFragments.GetIds()); + } + buf << uint8(m_entityFragments.ContentsChangedMask); + BuildValuesUpdate(&buf, fieldFlags, target); buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); @@ -221,12 +234,26 @@ void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::Updat std::size_t sizePos = buf.wpos(); buf << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buf, flags); BuildValuesUpdateWithFlag(&buf, flags, target); buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); data->AddUpdateBlock(); } +void Object::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) +{ + data->append(fragments.data(), fragments.size()); + *data << WorldPackets::As<uint8>(WowCS::EntityFragment::End); +} + +void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) +{ + *data << uint8(flags.HasFlag(UF::UpdateFieldFlag::Owner)); + *data << uint8(false); // m_entityFragments.IdsChanged + *data << uint8(WowCS::CGObjectUpdateMask); +} + void Object::BuildDestroyUpdateBlock(UpdateData* data) const { data->AddDestroyObject(GetGUID()); @@ -273,6 +300,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe if (GameObject const* go = ToGameObject()) PauseTimes = go->GetPauseTimes(); + data->WriteBit(IsWorldObject()); // HasPositionFragment data->WriteBit(flags.NoBirthAnim); data->WriteBit(flags.EnablePortals); data->WriteBit(flags.PlayHoverAnim); @@ -469,6 +497,66 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe *data << areaTrigger->GetRollPitchYaw().PositionXYZStream(); + switch (shape.Type) + { + case AreaTriggerShapeType::Sphere: + *data << int8(0); + *data << float(shape.SphereDatas.Radius); + *data << float(shape.SphereDatas.RadiusTarget); + break; + case AreaTriggerShapeType::Box: + *data << int8(1); + *data << float(shape.BoxDatas.Extents[0]); + *data << float(shape.BoxDatas.Extents[1]); + *data << float(shape.BoxDatas.Extents[2]); + *data << float(shape.BoxDatas.ExtentsTarget[0]); + *data << float(shape.BoxDatas.ExtentsTarget[1]); + *data << float(shape.BoxDatas.ExtentsTarget[2]); + break; + case AreaTriggerShapeType::Polygon: + *data << int8(3); + *data << int32(shape.PolygonVertices.size()); + *data << int32(shape.PolygonVerticesTarget.size()); + *data << float(shape.PolygonDatas.Height); + *data << float(shape.PolygonDatas.HeightTarget); + + for (TaggedPosition<Position::XY> const& vertice : shape.PolygonVertices) + *data << vertice; + + for (TaggedPosition<Position::XY> const& vertice : shape.PolygonVerticesTarget) + *data << vertice; + break; + case AreaTriggerShapeType::Cylinder: + *data << int8(4); + *data << float(shape.CylinderDatas.Radius); + *data << float(shape.CylinderDatas.RadiusTarget); + *data << float(shape.CylinderDatas.Height); + *data << float(shape.CylinderDatas.HeightTarget); + *data << float(shape.CylinderDatas.LocationZOffset); + *data << float(shape.CylinderDatas.LocationZOffsetTarget); + break; + case AreaTriggerShapeType::Disk: + *data << int8(7); + *data << float(shape.DiskDatas.InnerRadius); + *data << float(shape.DiskDatas.InnerRadiusTarget); + *data << float(shape.DiskDatas.OuterRadius); + *data << float(shape.DiskDatas.OuterRadiusTarget); + *data << float(shape.DiskDatas.Height); + *data << float(shape.DiskDatas.HeightTarget); + *data << float(shape.DiskDatas.LocationZOffset); + *data << float(shape.DiskDatas.LocationZOffsetTarget); + break; + case AreaTriggerShapeType::BoundedPlane: + *data << int8(8); + *data << float(shape.BoundedPlaneDatas.Extents[0]); + *data << float(shape.BoundedPlaneDatas.Extents[1]); + *data << float(shape.BoundedPlaneDatas.ExtentsTarget[0]); + *data << float(shape.BoundedPlaneDatas.ExtentsTarget[1]); + break; + default: + break; + } + bool hasAbsoluteOrientation = createProperties && createProperties->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::HasAbsoluteOrientation); bool hasDynamicShape = createProperties && createProperties->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::HasDynamicShape); bool hasAttached = createProperties && createProperties->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::HasAttached); @@ -482,15 +570,9 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe bool hasFacingCurveID = createProperties && createProperties->FacingCurveId != 0; bool hasMoveCurveID = createProperties && createProperties->MoveCurveId != 0; bool hasAnimation = createProperties && createProperties->AnimId; - bool hasUnk3 = createProperties && createProperties->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::Unk3); + bool visualAnimIsDecay = createProperties && createProperties->Flags.HasFlag(AreaTriggerCreatePropertiesFlag::VisualAnimIsDecay); bool hasAnimKitID = createProperties && createProperties->AnimKitId; bool hasAnimProgress = false; - bool hasAreaTriggerSphere = shape.IsSphere(); - bool hasAreaTriggerBox = shape.IsBox(); - bool hasAreaTriggerPolygon = shape.IsPolygon(); - bool hasAreaTriggerCylinder = shape.IsCylinder(); - bool hasDisk = shape.IsDisk(); - bool hasBoundedPlane = shape.IsBoundedPlane(); bool hasAreaTriggerSpline = areaTrigger->HasSplines(); bool hasOrbit = areaTrigger->HasOrbit(); bool hasMovementScript = false; @@ -511,19 +593,13 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe data->WriteBit(hasPositionalSoundKitID); data->WriteBit(hasAnimation); data->WriteBit(hasAnimKitID); - data->WriteBit(hasUnk3); + data->WriteBit(visualAnimIsDecay); data->WriteBit(hasAnimProgress); - data->WriteBit(hasAreaTriggerSphere); - data->WriteBit(hasAreaTriggerBox); - data->WriteBit(hasAreaTriggerPolygon); - data->WriteBit(hasAreaTriggerCylinder); - data->WriteBit(hasDisk); - data->WriteBit(hasBoundedPlane); data->WriteBit(hasAreaTriggerSpline); data->WriteBit(hasOrbit); data->WriteBit(hasMovementScript); - if (hasUnk3) + if (visualAnimIsDecay) data->WriteBit(false); data->FlushBits(); @@ -563,66 +639,6 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe if (hasAnimProgress) *data << uint32(0); - if (hasAreaTriggerSphere) - { - *data << float(shape.SphereDatas.Radius); - *data << float(shape.SphereDatas.RadiusTarget); - } - - if (hasAreaTriggerBox) - { - *data << float(shape.BoxDatas.Extents[0]); - *data << float(shape.BoxDatas.Extents[1]); - *data << float(shape.BoxDatas.Extents[2]); - *data << float(shape.BoxDatas.ExtentsTarget[0]); - *data << float(shape.BoxDatas.ExtentsTarget[1]); - *data << float(shape.BoxDatas.ExtentsTarget[2]); - } - - if (hasAreaTriggerPolygon) - { - *data << int32(shape.PolygonVertices.size()); - *data << int32(shape.PolygonVerticesTarget.size()); - *data << float(shape.PolygonDatas.Height); - *data << float(shape.PolygonDatas.HeightTarget); - - for (TaggedPosition<Position::XY> const& vertice : shape.PolygonVertices) - *data << vertice; - - for (TaggedPosition<Position::XY> const& vertice : shape.PolygonVerticesTarget) - *data << vertice; - } - - if (hasAreaTriggerCylinder) - { - *data << float(shape.CylinderDatas.Radius); - *data << float(shape.CylinderDatas.RadiusTarget); - *data << float(shape.CylinderDatas.Height); - *data << float(shape.CylinderDatas.HeightTarget); - *data << float(shape.CylinderDatas.LocationZOffset); - *data << float(shape.CylinderDatas.LocationZOffsetTarget); - } - - if (hasDisk) - { - *data << float(shape.DiskDatas.InnerRadius); - *data << float(shape.DiskDatas.InnerRadiusTarget); - *data << float(shape.DiskDatas.OuterRadius); - *data << float(shape.DiskDatas.OuterRadiusTarget); - *data << float(shape.DiskDatas.Height); - *data << float(shape.DiskDatas.HeightTarget); - *data << float(shape.DiskDatas.LocationZOffset); - *data << float(shape.DiskDatas.LocationZOffsetTarget); - } - - if (hasBoundedPlane) - { - *data << float(shape.BoundedPlaneDatas.Extents[0]); - *data << float(shape.BoundedPlaneDatas.Extents[1]); - *data << float(shape.BoundedPlaneDatas.ExtentsTarget[0]); - *data << float(shape.BoundedPlaneDatas.ExtentsTarget[1]); - } - //if (hasMovementScript) // *data << *areaTrigger->GetMovementScript(); // AreaTriggerMovementScriptInfo @@ -632,17 +648,32 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe if (flags.GameObject) { - bool bit8 = false; - uint32 Int1 = 0; - GameObject const* gameObject = ToGameObject(); + Transport const* transport = gameObject->ToTransport(); + + bool bit8 = false; *data << uint32(gameObject->GetWorldEffectID()); data->WriteBit(bit8); + data->WriteBit(transport != nullptr); + data->WriteBit(gameObject->GetPathProgressForClient().has_value()); data->FlushBits(); + if (transport) + { + *data << uint32(transport->GetTransportPeriod()); + *data << uint32(transport->GetTimer()); + data->WriteBit(transport->IsStopRequested()); + data->WriteBit(transport->IsStopped()); + data->WriteBit(false); + data->FlushBits(); + } + if (bit8) - *data << uint32(Int1); + *data << uint32(0); + + if (gameObject->GetPathProgressForClient()) + *data << float(*gameObject->GetPathProgressForClient()); } if (flags.SmoothPhasing) @@ -837,6 +868,8 @@ void Object::AddToObjectUpdateIfNeeded() void Object::ClearUpdateMask(bool remove) { m_values.ClearChangesMask(&Object::m_objectData); + m_entityFragments.IdsChanged = false; + m_entityFragments.ContentsChangedMask = WowCS::CGObjectActiveMask; if (m_objectUpdated) { diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 0fdeed66e7d..ea660a2d243 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -34,6 +34,7 @@ #include "SpellDefines.h" #include "UniqueTrackablePtr.h" #include "UpdateFields.h" +#include "WowCSEntityDefinitions.h" #include <list> #include <unordered_map> @@ -114,8 +115,17 @@ namespace UF inline MutableFieldReference<T, false> ModifyValue(UpdateField<T, BlockBit, Bit>(Derived::* field)); template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline OptionalUpdateFieldSetter<T> ModifyValue(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field)); + + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline MutableFieldReference<T, false> ModifyValue(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field), uint32 /*dummy*/); + + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> inline void ClearChangesMask(UpdateField<T, BlockBit, Bit>(Derived::* field)); + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline void ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field)); + uint32 GetChangedObjectTypeMask() const { return _changesMask; } bool HasChanged(uint32 index) const { return (_changesMask & UpdateMaskHelpers::GetBlockFlag(index)) != 0; } @@ -290,7 +300,7 @@ class TC_GAME_API Object friend UF::UpdateFieldHolder; UF::UpdateFieldHolder m_values; - UF::UpdateField<UF::ObjectData, 0, TYPEID_OBJECT> m_objectData; + UF::UpdateField<UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT> m_objectData; template<typename T> void ForceUpdateFieldChange(UF::UpdateFieldSetter<T> const& /*setter*/) @@ -419,6 +429,8 @@ class TC_GAME_API Object 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); + static void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags); public: virtual void BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const; @@ -428,6 +440,7 @@ class TC_GAME_API Object TypeID m_objectTypeId; CreateObjectBits m_updateFlag; + WowCS::EntityFragmentsHolder m_entityFragments; virtual bool AddToObjectUpdate() = 0; virtual void RemoveFromObjectUpdate() = 0; @@ -468,18 +481,61 @@ template <typename Derived, typename T, int32 BlockBit, uint32 Bit> inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field) { Object* owner = GetOwner(); - _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + return { (static_cast<Derived*>(owner)->*field)._value }; } template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) +{ + Object* 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); + + return { static_cast<Derived*>(owner)->*field }; +} + +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*/) +{ + Object* 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); + + auto& uf = (static_cast<Derived*>(owner)->*field); + if (!uf.has_value()) + uf.ConstructValue(); + + return { *uf._value }; +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit> Derived::* field) { Object* owner = GetOwner(); - _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); + owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); + (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask(); } +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) +{ + Object* 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); + + (static_cast<Derived*>(owner)->*field)._value->ClearChangesMask(); +} + template <class T_VALUES, class T_FLAGS, class FLAG_TYPE, size_t ARRAY_SIZE> class FlaggedValuesArray32 { diff --git a/src/server/game/Entities/Object/Updates/UpdateData.cpp b/src/server/game/Entities/Object/Updates/UpdateData.cpp index 44104a2dcb7..34fe6d86f89 100644 --- a/src/server/game/Entities/Object/Updates/UpdateData.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateData.cpp @@ -42,8 +42,9 @@ bool UpdateData::BuildPacket(WorldPacket* packet) ASSERT(packet->empty()); // shouldn't happen packet->Initialize(SMSG_UPDATE_OBJECT, 4 + 2 + 1 + (2 + 4 + 17 * (m_destroyGUIDs.size() + m_outOfRangeGUIDs.size())) + m_data.wpos()); - *packet << uint32(m_blockCount); *packet << uint16(m_map); + *packet << uint32(m_blockCount); + packet->WriteBit(true); // unk if (packet->WriteBit(!m_outOfRangeGUIDs.empty() || !m_destroyGUIDs.empty())) { diff --git a/src/server/game/Entities/Object/Updates/UpdateField.h b/src/server/game/Entities/Object/Updates/UpdateField.h index ecdb1c94c55..b6d0f7abe2a 100644 --- a/src/server/game/Entities/Object/Updates/UpdateField.h +++ b/src/server/game/Entities/Object/Updates/UpdateField.h @@ -907,6 +907,8 @@ namespace UF template<typename F> friend struct OptionalUpdateFieldSetter; + friend class UpdateFieldHolder; + public: using value_type = T; using IsLarge = std::integral_constant<bool, sizeof(void*) * 3 < sizeof(T)>; diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp index 8ae3f460fd8..dde4f01ec78 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp @@ -5659,6 +5659,36 @@ void ConversationData::ClearChangesMask() _changesMask.ResetAll(); } +void VendorData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Creature const* owner, Player const* receiver) const +{ + data << int32(Flags); +} + +void VendorData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Creature const* owner, Player const* receiver) const +{ + WriteUpdate(data, _changesMask, false, owner, receiver); +} + +void VendorData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Creature const* owner, Player const* receiver) const +{ + data.WriteBits(changesMask.GetBlock(0), 2); + + data.FlushBits(); + if (changesMask[0]) + { + if (changesMask[1]) + { + data << int32(Flags); + } + } +} + +void VendorData::ClearChangesMask() +{ + Base::ClearChangesMask(Flags); + _changesMask.ResetAll(); +} + } #if TRINITY_COMPILER == TRINITY_COMPILER_GNU diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h index 7cb26c6eef0..e7eb5763a4e 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -1058,6 +1058,15 @@ struct ConversationData : public IsUpdateFieldStructureTag, public HasChangesMas void ClearChangesMask(); }; +struct VendorData : public IsUpdateFieldStructureTag, public HasChangesMask<2> +{ + UpdateField<int32, 0, 1> Flags; + + void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Creature const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Creature const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Creature const* owner, Player const* receiver) const; + void ClearChangesMask(); +}; } #endif // UpdateFields_h__ diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h index dd70a7cf392..9c173d45f22 100644 --- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h +++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h @@ -86,8 +86,7 @@ public: } else if (GameObject const* gameObject = object->ToGameObject()) { - uint16 dynFlags = 0; - uint16 pathProgress = 0xFFFF; + uint32 dynFlags = 0; switch (gameObject->GetGoType()) { case GAMEOBJECT_TYPE_BUTTON: @@ -113,13 +112,6 @@ public: if (gameObject->HasConditionalInteraction() && gameObject->CanActivateForPlayer(receiver)) dynFlags |= GO_DYNFLAG_LO_SPARKLE; break; - case GAMEOBJECT_TYPE_TRANSPORT: - case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT: - { - dynFlags = dynamicFlags & 0xFFFF; - pathProgress = dynamicFlags >> 16; - break; - } case GAMEOBJECT_TYPE_CAPTURE_POINT: if (!gameObject->CanInteractWithCapturePoint(receiver)) dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; @@ -150,7 +142,7 @@ public: dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; } - dynamicFlags = (uint32(pathProgress) << 16) | uint32(dynFlags); + dynamicFlags = dynFlags; } return dynamicFlags; @@ -208,6 +200,77 @@ public: }; template<> +class ViewerDependentValue<UF::UnitData::StateWorldEffectIDsTag> +{ +public: + using value_type = UF::UnitData::StateWorldEffectIDsTag::value_type const*; + + static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* /*receiver*/) + { + //if (unit->IsCreature()) + // if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) + // return &spawnTrackingStateData->StateWorldEffects; + + return &*unitData->StateWorldEffectIDs; + } +}; + +template<> +class ViewerDependentValue<UF::UnitData::StateSpellVisualIDTag> +{ +public: + using value_type = UF::UnitData::StateSpellVisualIDTag::value_type; + + static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* /*receiver*/) + { + value_type stateSpellVisual = unitData->StateSpellVisualID; + + //if (unit->IsCreature()) + // if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) + // stateSpellVisual = spawnTrackingStateData->StateSpellVisualId.value_or(0); + + return stateSpellVisual; + } +}; + +template<> +class ViewerDependentValue<UF::UnitData::StateAnimIDTag> +{ +public: + using value_type = UF::UnitData::StateAnimIDTag::value_type; + + static value_type GetValue(UF::UnitData const* /*unitData*/, Unit const* /*unit*/, Player const* /*receiver*/) + { + value_type stateAnimId = sDB2Manager.GetEmptyAnimStateID(); + + //if (unit->IsCreature()) + // if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) + // stateAnimId = spawnTrackingStateData->StateAnimId.value_or(stateAnimId); + + return stateAnimId; + } +}; + +template<> +class ViewerDependentValue<UF::UnitData::StateAnimKitIDTag> +{ +public: + using value_type = UF::UnitData::StateAnimKitIDTag::value_type; + + static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* /*receiver*/) + { + value_type stateAnimKitId = unitData->StateAnimKitID; + + //if (unit->IsCreature()) + // if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) + // stateAnimKitId = spawnTrackingStateData->StateAnimKitId.value_or(0); + + return stateAnimKitId; + } +}; + + +template<> class ViewerDependentValue<UF::UnitData::FactionTemplateTag> { public: @@ -280,6 +343,18 @@ public: }; template<> +class ViewerDependentValue<UF::UnitData::Flags4Tag> +{ +public: + using value_type = UF::UnitData::Flags4Tag::value_type; + + static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* /*receiver*/) + { + return unitData->Flags4; + } +}; + +template<> class ViewerDependentValue<UF::UnitData::AuraStateTag> { public: @@ -394,6 +469,72 @@ public: }; template<> +class ViewerDependentValue<UF::GameObjectData::StateWorldEffectIDsTag> +{ +public: + using value_type = UF::GameObjectData::StateWorldEffectIDsTag::value_type const*; + + static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* /*gameObject*/ , Player const* /*receiver*/) + { + //if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) + // return &spawnTrackingStateData->StateWorldEffects; + + return &*gameObjectData->StateWorldEffectIDs; + } +}; + +template<> +class ViewerDependentValue<UF::GameObjectData::StateSpellVisualIDTag> +{ +public: + using value_type = UF::GameObjectData::StateSpellVisualIDTag::value_type; + + static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* /*gameObject*/, Player const* /*receiver*/) + { + value_type stateSpellVisual = gameObjectData->StateSpellVisualID; + + //if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) + // stateSpellVisual = spawnTrackingStateData->StateSpellVisualId.value_or(0); + + return stateSpellVisual; + } +}; + +template<> +class ViewerDependentValue<UF::GameObjectData::SpawnTrackingStateAnimIDTag> +{ +public: + using value_type = UF::GameObjectData::SpawnTrackingStateAnimIDTag::value_type; + + static value_type GetValue(UF::GameObjectData const* /*gameObjectData*/, GameObject const* /*gameObject*/, Player const* /*receiver*/) + { + value_type stateAnimId = sDB2Manager.GetEmptyAnimStateID(); + + //if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) + // stateAnimId = spawnTrackingStateData->StateAnimId.value_or(stateAnimId); + + return stateAnimId; + } +}; + +template<> +class ViewerDependentValue<UF::GameObjectData::SpawnTrackingStateAnimKitIDTag> +{ +public: + using value_type = UF::GameObjectData::SpawnTrackingStateAnimKitIDTag::value_type; + + static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* /*gameObject*/, Player const* /*receiver*/) + { + value_type stateAnimKitId = gameObjectData->SpawnTrackingStateAnimKitID; + + //if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) + // stateAnimKitId = spawnTrackingStateData->StateAnimKitId.value_or(0); + + return stateAnimKitId; + } +}; + +template<> class ViewerDependentValue<UF::GameObjectData::FlagsTag> { public: diff --git a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp new file mode 100644 index 00000000000..91c1ab39de3 --- /dev/null +++ b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp @@ -0,0 +1,101 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "WowCSEntityDefinitions.h" +#include "Errors.h" +#include <algorithm> + +namespace WowCS +{ +void EntityFragmentsHolder::Add(EntityFragment fragment, bool update) +{ + ASSERT(Count < Ids.size()); + + auto insertSorted = [](auto& arr, uint8& count, EntityFragment f) + { + auto where = std::ranges::lower_bound(arr.begin(), arr.begin() + count, f); + if (*where == f) + return std::pair(where, false); + std::ranges::rotate(where, arr.begin() + count, arr.begin() + count + 1); + ++count; + *where = f; + return std::pair(where, true); + }; + + if (!insertSorted(Ids, Count, fragment).second) + return; + + if (IsUpdateableFragment(fragment)) + { + ASSERT(UpdateableCount < UpdateableIds.size()); + + auto insertedItr = insertSorted(UpdateableIds, UpdateableCount, fragment).first; + std::ptrdiff_t index = std::distance(UpdateableIds.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])) + UpdateableMasks[i] |= 1 << maskIndex++; + } + } + + if (update) + IdsChanged = true; +} + +void EntityFragmentsHolder::Remove(EntityFragment fragment) +{ + auto removeSorted = [](auto& arr, uint8& count, EntityFragment f) + { + auto where = std::ranges::find(arr.begin(), arr.begin() + count, f); + if (where != arr.end()) + { + *where = EntityFragment::End; + std::ranges::rotate(where, where + 1, arr.begin() + count); + --count; + return std::pair(where, true); + } + return std::pair(where, false); + }; + + if (!removeSorted(Ids, Count, fragment).second) + return; + + if (IsUpdateableFragment(fragment)) + { + auto [removedItr, removed] = removeSorted(UpdateableIds, UpdateableCount, fragment); + if (removed) + { + std::ptrdiff_t index = std::distance(UpdateableIds.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])) + UpdateableMasks[i] |= 1 << maskIndex++; + } + } + } + + IdsChanged = true; +} +} diff --git a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h new file mode 100644 index 00000000000..a98afa79a2d --- /dev/null +++ b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h @@ -0,0 +1,114 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITYCORE_WOWCS_ENTITY_DEFINITIONS_H +#define TRINITYCORE_WOWCS_ENTITY_DEFINITIONS_H + +#include "Define.h" +#include <span> + +namespace WowCS +{ +enum class EntityFragment : uint8 +{ + CGObject = 0, // UPDATEABLE, INDIRECT, + Tag_Item = 1, // TAG, + Tag_Container = 2, // TAG, + Tag_AzeriteEmpoweredItem = 3, // TAG, + Tag_AzeriteItem = 4, // TAG, + Tag_Unit = 5, // TAG, + Tag_Player = 6, // TAG, + Tag_GameObject = 7, // TAG, + Tag_DynamicObject = 8, // TAG, + Tag_Corpse = 9, // TAG, + Tag_AreaTrigger = 10, // TAG, + Tag_SceneObject = 11, // TAG, + Tag_Conversation = 12, // TAG, + Tag_AIGroup = 13, // TAG, + Tag_Scenario = 14, // TAG, + Tag_LootObject = 15, // TAG, + Tag_ActivePlayer = 16, // TAG, + Tag_ActiveClient_S = 17, // TAG, + Tag_ActiveObject_C = 18, // TAG, + Tag_VisibleObject_C = 19, // TAG, + Tag_UnitVehicle = 20, // TAG, + FEntityPosition = 112, + FEntityLocalMatrix = 113, + FEntityWorldMatrix = 114, + CActor = 115, // INDIRECT, + FVendor_C = 117, // UPDATEABLE, + FMirroredObject_C = 119, + End = 255, +}; + +inline constexpr bool IsUpdateableFragment(EntityFragment frag) +{ + return frag == EntityFragment::CGObject || frag == EntityFragment::FVendor_C; +} + +inline constexpr bool IsIndirectFragment(EntityFragment frag) +{ + return frag == EntityFragment::CGObject || frag == EntityFragment::CActor; +} + +// 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; + +struct EntityFragmentsHolder +{ + std::array<EntityFragment, 8> Ids = + { + 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, 2> UpdateableIds = { EntityFragment::End, EntityFragment::End }; + std::array<uint8, 2> UpdateableMasks = { }; + uint8 UpdateableCount = 0; + uint8 ContentsChangedMask = CGObjectActiveMask; + + void Add(EntityFragment fragment, bool update); + void Remove(EntityFragment fragment); + + std::span<EntityFragment const> GetIds() const { return std::span(Ids.begin(), Count); } + std::span<EntityFragment const> GetUpdateableIds() const { return std::span(UpdateableIds.begin(), UpdateableCount); } + + uint8 GetUpdateMaskFor(EntityFragment fragment) const + { + if (fragment == EntityFragment::CGObject) // common case optimization, make use of the fact that fragment arrays are sorted + return CGObjectChangedMask; + + for (uint8 i = 1; i < UpdateableCount; ++i) + if (UpdateableIds[i] == fragment) + return UpdateableMasks[i]; + + return 0; + } +}; + +enum class EntityFragmentSerializationType : uint8 +{ + Full = 0, + Partial = 1 +}; +} + +#endif // TRINITYCORE_WOWCS_ENTITY_DEFINITIONS_H diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 63130b64ee6..0fda74f236f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -158,6 +158,8 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this) m_objectType |= TYPEMASK_PLAYER; m_objectTypeId = TYPEID_PLAYER; + m_entityFragments.Add(WowCS::EntityFragment::Tag_Player, false); + m_session = session; m_ingametime = 0; @@ -3358,6 +3360,7 @@ void Player::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) @@ -5859,8 +5862,8 @@ void Player::CheckAreaExplore() uint32 offset = areaEntry->AreaBit / PLAYER_EXPLORED_ZONES_BITS; uint64 val = UI64LIT(1) << (areaEntry->AreaBit % PLAYER_EXPLORED_ZONES_BITS); - if (offset >= m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].size() - || !(m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX][offset] & val)) + if (offset >= m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values.size() + || !(m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values[offset] & val)) { AddExploredZones(offset, val); @@ -5907,7 +5910,7 @@ void Player::AddExploredZones(uint32 pos, uint64 mask) .ModifyValue(&Player::m_activePlayerData) .ModifyValue(&UF::ActivePlayerData::BitVectors) .ModifyValue(&UF::BitVectors::Values, PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX) - .ModifyValue(pos), mask); + .ModifyValue(&UF::BitVector::Values, pos), mask); } void Player::RemoveExploredZones(uint32 pos, uint64 mask) @@ -5916,7 +5919,7 @@ void Player::RemoveExploredZones(uint32 pos, uint64 mask) .ModifyValue(&Player::m_activePlayerData) .ModifyValue(&UF::ActivePlayerData::BitVectors) .ModifyValue(&UF::BitVectors::Values, PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX) - .ModifyValue(pos), mask); + .ModifyValue(&UF::BitVector::Values, pos), mask); } bool Player::HasExploredZone(uint32 areaId) const @@ -5929,11 +5932,11 @@ bool Player::HasExploredZone(uint32 areaId) const return false; size_t playerIndexOffset = size_t(area->AreaBit) / PLAYER_EXPLORED_ZONES_BITS; - if (playerIndexOffset >= m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].size()) + if (playerIndexOffset >= m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values.size()) return false; uint64 mask = uint64(1) << (area->AreaBit % PLAYER_EXPLORED_ZONES_BITS); - return (m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX][playerIndexOffset] & mask) != 0; + return (m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values[playerIndexOffset] & mask) != 0; } void Player::UpdateZoneAndAreaId() @@ -10848,7 +10851,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) } } - pItem->SetItemFlag2(ITEM_FIELD_FLAG2_EQUIPPED); + pItem->SetItemZoneFlag(ITEM_FIELD_FLAG2_EQUIPPED); if (IsInWorld() && update) { @@ -11027,7 +11030,7 @@ void Player::QuickEquipItem(uint16 pos, Item* pItem) uint8 slot = pos & 255; VisualizeItem(slot, pItem); - pItem->SetItemFlag2(ITEM_FIELD_FLAG2_EQUIPPED); + pItem->SetItemZoneFlag(ITEM_FIELD_FLAG2_EQUIPPED); if (IsInWorld()) { @@ -11122,7 +11125,7 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update) _ApplyItemMods(pItem, slot, false, update); - pItem->RemoveItemFlag2(ITEM_FIELD_FLAG2_EQUIPPED); + pItem->RemoveItemZoneFlag(ITEM_FIELD_FLAG2_EQUIPPED); // remove item dependent auras and casts (only weapon and armor slots) if (slot < PROFESSION_SLOT_END) @@ -19013,10 +19016,10 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba stmt->setUInt32(index++, GetLootSpecId()); ss.str(""); - for (size_t i = 0; i < m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].size(); ++i) + for (size_t i = 0; i < m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values.size(); ++i) { - ss << uint32(m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX][i] & 0xFFFFFFFF) << ' '; - ss << uint32((m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX][i] >> 32) & 0xFFFFFFFF) << ' '; + ss << uint32(m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values[i] & 0xFFFFFFFF) << ' '; + ss << uint32((m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values[i] >> 32) & 0xFFFFFFFF) << ' '; } stmt->setString(index++, ss.str()); @@ -19153,10 +19156,10 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba stmt->setUInt32(index++, GetLootSpecId()); ss.str(""); - for (size_t i = 0; i < m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].size(); ++i) + for (size_t i = 0; i < m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values.size(); ++i) { - ss << uint32(m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX][i] & 0xFFFFFFFF) << ' '; - ss << uint32((m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX][i] >> 32) & 0xFFFFFFFF) << ' '; + ss << uint32(m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values[i] & 0xFFFFFFFF) << ' '; + ss << uint32((m_activePlayerData->BitVectors->Values[PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX].Values[i] >> 32) & 0xFFFFFFFF) << ' '; } stmt->setString(index++, ss.str()); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index bdb9d6295d2..db7006dc199 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -133,7 +133,7 @@ enum PlayerSkillsConstants enum PlayerDataFlagConstants { - PLAYER_EXPLORED_ZONES_BITS = UF::size_of_value_type<decltype(UF::BitVectors::Values)::value_type>() * 8, + PLAYER_EXPLORED_ZONES_BITS = UF::size_of_value_type<decltype(UF::BitVector::Values)>() * 8, PLAYER_DATA_FLAG_EXPLORED_ZONES_INDEX = 1, PLAYER_DATA_FLAG_CHARACTER_DATA_INDEX = 2, @@ -2823,8 +2823,8 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> std::string GetDebugInfo() const override; - UF::UpdateField<UF::PlayerData, 0, TYPEID_PLAYER> m_playerData; - UF::UpdateField<UF::ActivePlayerData, 0, TYPEID_ACTIVE_PLAYER> m_activePlayerData; + UF::UpdateField<UF::PlayerData, int32(WowCS::EntityFragment::CGObject), TYPEID_PLAYER> m_playerData; + UF::UpdateField<UF::ActivePlayerData, int32(WowCS::EntityFragment::CGObject), TYPEID_ACTIVE_PLAYER> m_activePlayerData; void SetAreaSpiritHealer(Creature* creature); ObjectGuid const& GetSpiritHealerGUID() const { return _areaSpiritHealerGUID; } diff --git a/src/server/game/Entities/SceneObject/SceneObject.cpp b/src/server/game/Entities/SceneObject/SceneObject.cpp index b8b5367426a..9ac7394fd81 100644 --- a/src/server/game/Entities/SceneObject/SceneObject.cpp +++ b/src/server/game/Entities/SceneObject/SceneObject.cpp @@ -33,6 +33,8 @@ SceneObject::SceneObject() : WorldObject(false) m_updateFlag.Stationary = true; m_updateFlag.SceneObject = true; + + m_entityFragments.Add(WowCS::EntityFragment::Tag_SceneObject, false); } SceneObject::~SceneObject() = default; @@ -156,6 +158,7 @@ void SceneObject::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, void SceneObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::SceneObjectData::Mask const& requestedSceneObjectMask, Player const* target) const { + UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask<NUM_CLIENT_OBJECT_TYPES> valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); @@ -166,6 +169,7 @@ void SceneObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::Objec ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) diff --git a/src/server/game/Entities/SceneObject/SceneObject.h b/src/server/game/Entities/SceneObject/SceneObject.h index 73d4fb31c87..8932e6f235f 100644 --- a/src/server/game/Entities/SceneObject/SceneObject.h +++ b/src/server/game/Entities/SceneObject/SceneObject.h @@ -77,7 +77,7 @@ public: void SetCreatedBySpellCast(ObjectGuid castId) { _createdBySpellCast = castId; } - UF::UpdateField<UF::SceneObjectData, 0, TYPEID_SCENEOBJECT> m_sceneObjectData; + UF::UpdateField<UF::SceneObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_SCENEOBJECT> m_sceneObjectData; private: bool ShouldBeRemoved() const; diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 0eef23e4fb9..dadbd4e960c 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -92,6 +92,7 @@ Transport::Transport() : GameObject(), m_updateFlag.ServerTime = true; m_updateFlag.Stationary = true; m_updateFlag.Rotation = true; + m_updateFlag.GameObject = true; } Transport::~Transport() @@ -140,7 +141,6 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, float x, float } _pathProgress = !goinfo->moTransport.allowstopping ? getMSTime() /*might be called before world update loop begins, don't use GameTime*/ % tInfo->TotalPathTime : 0; - SetPathProgressForClient(float(_pathProgress) / float(tInfo->TotalPathTime)); SetObjectScale(goinfo->size); SetPeriod(tInfo->TotalPathTime); SetEntry(goinfo->entry); @@ -203,8 +203,6 @@ void Transport::Update(uint32 diff) _eventsToTrigger->set(); } - SetPathProgressForClient(float(_pathProgress) / float(GetTransportPeriod())); - uint32 timer = _pathProgress % GetTransportPeriod(); size_t eventToTriggerIndex = _eventsToTrigger->find_first(); diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 8a4f272d135..0c78785377d 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -89,6 +89,8 @@ class TC_GAME_API Transport final : public GameObject, public TransportBase uint32 GetTransportPeriod() const { return m_gameObjectData->Level; } void SetPeriod(uint32 period) { SetLevel(period); } uint32 GetTimer() const { return _pathProgress; } + bool IsStopRequested() const { return _requestStopTimestamp.has_value(); } + bool IsStopped() const { return HasDynamicFlag(GO_DYNFLAG_LO_STOPPED); } void UpdatePosition(float x, float y, float z, float o); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index bb3439d96ec..555920f4237 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -320,6 +320,8 @@ Unit::Unit(bool isWorldObject) : m_updateFlag.MovementUpdate = true; + m_entityFragments.Add(WowCS::EntityFragment::Tag_Unit, false); + m_baseAttackSpeed = { }; m_attackTimer = { }; m_modAttackSpeedPct.fill(1.0f); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 0b255d4b7ea..67bb1acd327 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1827,7 +1827,7 @@ class TC_GAME_API Unit : public WorldObject std::string GetDebugInfo() const override; - UF::UpdateField<UF::UnitData, 0, TYPEID_UNIT> m_unitData; + UF::UpdateField<UF::UnitData, int32(WowCS::EntityFragment::CGObject), TYPEID_UNIT> m_unitData; protected: explicit Unit (bool isWorldObject); diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index dd557a841d9..d705885a08e 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -330,6 +330,9 @@ enum NPCFlags : uint32 DEFINE_ENUM_FLAG(NPCFlags); +inline constexpr NPCFlags UNIT_NPC_FLAG_VENDOR_MASK = UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_VENDOR_AMMO | UNIT_NPC_FLAG_VENDOR_FOOD + | UNIT_NPC_FLAG_VENDOR_POISON | UNIT_NPC_FLAG_VENDOR_REAGENT; + // EnumUtils: DESCRIBE THIS enum NPCFlags2 : uint32 { diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index d2294bbd2b7..7462ecf0c06 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -2069,12 +2069,14 @@ public: if (owner->HasAchieved(ACHIEVEMENT_PONY_UP) && !me->HasAura(SPELL_AURA_TIRED_S) && !me->HasAura(SPELL_AURA_TIRED_G)) { - me->SetNpcFlag(UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_MAILBOX | UNIT_NPC_FLAG_VENDOR); + me->SetVendor(UNIT_NPC_FLAG_VENDOR, true); + me->SetNpcFlag(UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_MAILBOX); return; } } - me->RemoveNpcFlag(UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_MAILBOX | UNIT_NPC_FLAG_VENDOR); + me->SetVendor(UNIT_NPC_FLAG_VENDOR_MASK, false); + me->RemoveNpcFlag(UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_MAILBOX); } bool OnGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override @@ -2083,7 +2085,8 @@ public: { case GOSSIP_OPTION_BANK: { - me->RemoveNpcFlag(UNIT_NPC_FLAG_MAILBOX | UNIT_NPC_FLAG_VENDOR); + me->SetVendor(UNIT_NPC_FLAG_VENDOR_MASK, false); + me->RemoveNpcFlag(UNIT_NPC_FLAG_MAILBOX); uint32 _bankAura = IsArgentSquire() ? SPELL_AURA_BANK_S : SPELL_AURA_BANK_G; if (!me->HasAura(_bankAura)) DoCastSelf(_bankAura); @@ -2105,7 +2108,8 @@ public: } case GOSSIP_OPTION_MAIL: { - me->RemoveNpcFlag(UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_VENDOR); + me->SetVendor(UNIT_NPC_FLAG_VENDOR_MASK, false); + me->RemoveNpcFlag(UNIT_NPC_FLAG_BANKER); uint32 _mailAura = IsArgentSquire() ? SPELL_AURA_POSTMAN_S : SPELL_AURA_POSTMAN_G; if (!me->HasAura(_mailAura)) DoCastSelf(_mailAura); |