diff options
| author | Shauren <shauren.trinity@gmail.com> | 2024-10-30 14:41:27 +0100 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2024-10-30 14:41:27 +0100 |
| commit | 68db469ee1f992bcdc81de64d6af1007d303be05 (patch) | |
| tree | 776b61e7c2eaf0a07e1d8a711c09c1131603a13c /src/server/game/Entities/Object | |
| parent | 91c12c64037ca906c30f9718fadab619359f1616 (diff) | |
Core/PacketIO: Updated SMSG_UPDATE_OBJECT for 11.0.5
Diffstat (limited to 'src/server/game/Entities/Object')
9 files changed, 509 insertions, 126 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 2a3a84db61c..66f5c5734aa 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); @@ -481,12 +569,6 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe bool hasMorphCurveID = createProperties && createProperties->MorphCurveId != 0; bool hasFacingCurveID = createProperties && createProperties->FacingCurveId != 0; bool hasMoveCurveID = createProperties && createProperties->MoveCurveId != 0; - 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; @@ -505,12 +587,6 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe data->WriteBit(hasFacingCurveID); data->WriteBit(hasMoveCurveID); data->WriteBit(hasPositionalSoundKitID); - 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); @@ -543,66 +619,6 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe if (hasPositionalSoundKitID) *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 @@ -612,17 +628,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) @@ -806,6 +837,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 a5de196a6ab..4b553171706 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 074ae923b02..d2c72c3f945 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp @@ -1114,8 +1114,8 @@ void UnitData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisi data << uint32(ChannelObjects.size()); data << int32(FlightCapabilityID); data << float(GlideEventSpeedDivisor); - data << uint32(MaxHealthModifierFlatNeg); - data << uint32(MaxHealthModifierFlatPos); + data << int32(MaxHealthModifierFlatNeg); + data << int32(MaxHealthModifierFlatPos); data << uint32(SilencedSchoolMask); data << uint32(CurrentAreaID); data << float(Field_31C); @@ -1716,11 +1716,11 @@ void UnitData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignor } if (changesMask[125]) { - data << uint32(MaxHealthModifierFlatNeg); + data << int32(MaxHealthModifierFlatNeg); } if (changesMask[126]) { - data << uint32(MaxHealthModifierFlatPos); + data << int32(MaxHealthModifierFlatPos); } if (changesMask[127]) { @@ -2955,50 +2955,84 @@ void SkillInfo::ClearChangesMask() _changesMask.ResetAll(); } -void BitVectors::WriteCreate(ByteBuffer& data, Player const* owner, Player const* receiver) const +void BitVector::WriteCreate(ByteBuffer& data, Player const* owner, Player const* receiver) const { - for (uint32 i = 0; i < 11; ++i) + data << uint32(Values.size()); + for (uint32 i = 0; i < Values.size(); ++i) { - data << uint32(Values[i].size()); - for (uint32 j = 0; j < Values[i].size(); ++j) - { - data << uint64(Values[i][j]); - } + data << uint64(Values[i]); } } -void BitVectors::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Player const* owner, Player const* receiver) const +void BitVector::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Player const* owner, Player const* receiver) const { Mask changesMask = _changesMask; if (ignoreChangesMask) changesMask.SetAll(); - data.WriteBits(changesMask.GetBlock(0), 1); + data.WriteBits(changesMask.GetBlock(0), 2); if (changesMask[0]) { - for (uint32 i = 0; i < 11; ++i) + if (changesMask[1]) { if (!ignoreChangesMask) - Values[i].WriteUpdateMask(data); + Values.WriteUpdateMask(data); else - WriteCompleteDynamicFieldUpdateMask(Values[i].size(), data); + WriteCompleteDynamicFieldUpdateMask(Values.size(), data); } } + data.FlushBits(); if (changesMask[0]) { - for (uint32 i = 0; i < 11; ++i) + if (changesMask[1]) { - for (uint32 j = 0; j < Values[i].size(); ++j) + for (uint32 i = 0; i < Values.size(); ++i) { - if (Values[i].HasChanged(j) || ignoreChangesMask) + if (Values.HasChanged(i) || ignoreChangesMask) { - data << uint64(Values[i][j]); + data << uint64(Values[i]); } } } } +} + +void BitVector::ClearChangesMask() +{ + Base::ClearChangesMask(Values); + _changesMask.ResetAll(); +} + +void BitVectors::WriteCreate(ByteBuffer& data, Player const* owner, Player const* receiver) const +{ + for (uint32 i = 0; i < 13; ++i) + { + Values[i].WriteCreate(data, owner, receiver); + } +} + +void BitVectors::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Player const* owner, Player const* receiver) const +{ + Mask changesMask = _changesMask; + if (ignoreChangesMask) + changesMask.SetAll(); + + data.WriteBits(changesMask.GetBlocksMask(0), 1); + if (changesMask.GetBlock(0)) + data.WriteBits(changesMask.GetBlock(0), 32); + data.FlushBits(); + if (changesMask[0]) + { + for (uint32 i = 0; i < 13; ++i) + { + if (changesMask[1 + i]) + { + Values[i].WriteUpdate(data, ignoreChangesMask, owner, receiver); + } + } + } } void BitVectors::ClearChangesMask() @@ -4587,7 +4621,7 @@ bool WalkInData::operator==(WalkInData const& right) const void DelveData::WriteCreate(ByteBuffer& data, Player const* owner, Player const* receiver) const { data << int32(Field_0); - data << int64(Field_8); + data << uint64(Field_8); data << int32(Field_10); data << int32(SpellID); data << uint32(Owners.size()); @@ -4603,7 +4637,7 @@ void DelveData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Player con { data.FlushBits(); data << int32(Field_0); - data << int64(Field_8); + data << uint64(Field_8); data << int32(Field_10); data << int32(SpellID); data << uint32(Owners.size()); @@ -4761,7 +4795,7 @@ void ActivePlayerData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> f { data << uint32(BankBagSlotFlags[i]); } - for (uint32 i = 0; i < 960; ++i) + for (uint32 i = 0; i < 1000; ++i) { data << uint64(QuestCompleted[i]); } @@ -5005,8 +5039,8 @@ void ActivePlayerData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bo { for (uint32 i = 0; i < 1; ++i) data << uint32(changesMask.GetBlocksMask(i)); - data.WriteBits(changesMask.GetBlocksMask(1), 15); - for (uint32 i = 0; i < 47; ++i) + data.WriteBits(changesMask.GetBlocksMask(1), 16); + for (uint32 i = 0; i < 48; ++i) if (changesMask.GetBlock(i)) data.WriteBits(changesMask.GetBlock(i), 32); @@ -6232,7 +6266,7 @@ void ActivePlayerData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bo } if (changesMask[497]) { - for (uint32 i = 0; i < 960; ++i) + for (uint32 i = 0; i < 1000; ++i) { if (changesMask[498 + i]) { @@ -6240,11 +6274,11 @@ void ActivePlayerData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bo } } } - if (changesMask[1458]) + if (changesMask[1498]) { for (uint32 i = 0; i < 17; ++i) { - if (changesMask[1459 + i]) + if (changesMask[1499 + i]) { data << float(ItemUpgradeHighWatermark[i]); } @@ -7329,6 +7363,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 cf5a167706b..e3bf583c73a 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -37,6 +37,7 @@ class Bag; class ByteBuffer; class Conversation; class Corpse; +class Creature; class DynamicObject; class GameObject; class Item; @@ -390,8 +391,8 @@ struct UnitData : public IsUpdateFieldStructureTag, public HasChangesMask<220> UpdateField<ObjectGuid, 96, 122> GuildGUID; UpdateField<int32, 96, 123> FlightCapabilityID; UpdateField<float, 96, 124> GlideEventSpeedDivisor; // Movement speed gets divided by this value when evaluating what GlideEvents to use - UpdateField<uint32, 96, 125> MaxHealthModifierFlatNeg; - UpdateField<uint32, 96, 126> MaxHealthModifierFlatPos; + UpdateField<int32, 96, 125> MaxHealthModifierFlatNeg; + UpdateField<int32, 96, 126> MaxHealthModifierFlatPos; UpdateField<uint32, 96, 127> SilencedSchoolMask; UpdateField<uint32, 128, 129> CurrentAreaID; UpdateField<float, 128, 130> Field_31C; @@ -590,9 +591,18 @@ struct SkillInfo : public IsUpdateFieldStructureTag, public HasChangesMask<1793> void ClearChangesMask(); }; -struct BitVectors : public IsUpdateFieldStructureTag, public HasChangesMask<1> +struct BitVector : public IsUpdateFieldStructureTag, public HasChangesMask<2> { - UpdateFieldArray<DynamicUpdateFieldBase<uint64>, 11, 0, -1> Values; + DynamicUpdateField<uint64, 0, 1> Values; + + void WriteCreate(ByteBuffer& data, Player const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Player const* owner, Player const* receiver) const; + void ClearChangesMask(); +}; + +struct BitVectors : public IsUpdateFieldStructureTag, public HasChangesMask<14> +{ + UpdateFieldArray<UF::BitVector, 13, 0, 1> Values; void WriteCreate(ByteBuffer& data, Player const* owner, Player const* receiver) const; void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Player const* owner, Player const* receiver) const; @@ -995,7 +1005,7 @@ struct DelveData : public IsUpdateFieldStructureTag { std::vector<ObjectGuid> Owners; int32 Field_0; - int64 Field_8; + uint64 Field_8; int32 Field_10; int32 SpellID; uint32 Started; // Restricts rewards to players in m_owners if set to true. Intended to prevent rewarwding players that join in-progress delve? @@ -1016,7 +1026,7 @@ struct Research : public IsUpdateFieldStructureTag bool operator!=(Research const& right) const { return !(*this == right); } }; -struct ActivePlayerData : public IsUpdateFieldStructureTag, public HasChangesMask<1476> +struct ActivePlayerData : public IsUpdateFieldStructureTag, public HasChangesMask<1516> { UpdateField<bool, 0, 1> BackpackAutoSortDisabled; UpdateField<bool, 0, 2> BackpackSellJunkDisabled; @@ -1170,8 +1180,8 @@ struct ActivePlayerData : public IsUpdateFieldStructureTag, public HasChangesMas UpdateFieldArray<int32, 2, 480, 481> ProfessionSkillLine; UpdateFieldArray<uint32, 5, 483, 484> BagSlotFlags; UpdateFieldArray<uint32, 7, 489, 490> BankBagSlotFlags; - UpdateFieldArray<uint64, 960, 497, 498> QuestCompleted; - UpdateFieldArray<float, 17, 1458, 1459> ItemUpgradeHighWatermark; + UpdateFieldArray<uint64, 1000, 497, 498> QuestCompleted; + UpdateFieldArray<float, 17, 1498, 1499> ItemUpgradeHighWatermark; void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Player const* owner, Player const* receiver) const; void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Player const* owner, Player const* receiver) const; @@ -1370,6 +1380,16 @@ 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 c19e91c30f3..ccaa715adeb 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: @@ -111,13 +110,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; @@ -147,7 +139,7 @@ public: dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; } - dynamicFlags = (uint32(pathProgress) << 16) | uint32(dynFlags); + dynamicFlags = dynFlags; } return dynamicFlags; 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 |
