diff options
| author | Shauren <shauren.trinity@gmail.com> | 2025-12-27 11:43:04 +0100 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2025-12-27 11:43:04 +0100 |
| commit | 0ff5cf4d002be13cd1457ce910db93a1a303f7ae (patch) | |
| tree | 53d04e67ccd7dbd50b35ea835510a782d72c368f /src/server | |
| parent | 3b722eb93e18e88b49583f22b175b34768fc532a (diff) | |
Core/Objects: Create a base class for Object to support creating new client visible object types
Diffstat (limited to 'src/server')
18 files changed, 1121 insertions, 1031 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 14812acb868..1a97401096a 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -138,7 +138,7 @@ bool AreaTrigger::Create(AreaTriggerCreatePropertiesId areaTriggerCreateProperti _areaTriggerTemplate = _areaTriggerCreateProperties->Template; - Object::_Create(ObjectGuid::Create<HighGuid::AreaTrigger>(GetMapId(), GetTemplate() ? GetTemplate()->Id.Id : 0, GetMap()->GenerateLowGuid<HighGuid::AreaTrigger>())); + _Create(ObjectGuid::Create<HighGuid::AreaTrigger>(GetMapId(), GetTemplate() ? GetTemplate()->Id.Id : 0, GetMap()->GenerateLowGuid<HighGuid::AreaTrigger>())); if (GetTemplate()) SetEntry(GetTemplate()->Id.Id); diff --git a/src/server/game/Entities/Conversation/Conversation.cpp b/src/server/game/Entities/Conversation/Conversation.cpp index 7891bd9bd68..1900374b243 100644 --- a/src/server/game/Entities/Conversation/Conversation.cpp +++ b/src/server/game/Entities/Conversation/Conversation.cpp @@ -176,7 +176,7 @@ void Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Relocate(pos); RelocateStationaryPosition(pos); - Object::_Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid)); + _Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid)); PhasingHandler::InheritPhaseShift(this, creator); UpdatePositionData(); diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 95e8fb5ba0a..3f3038c214c 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -65,7 +65,7 @@ void Corpse::RemoveFromWorld() bool Corpse::Create(ObjectGuid::LowType guidlow, Map* map) { - Object::_Create(ObjectGuid::Create<HighGuid::Corpse>(map->GetId(), 0, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Corpse>(map->GetId(), 0, guidlow)); return true; } @@ -82,7 +82,7 @@ bool Corpse::Create(ObjectGuid::LowType guidlow, Player* owner) return false; } - Object::_Create(ObjectGuid::Create<HighGuid::Corpse>(owner->GetMapId(), 0, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Corpse>(owner->GetMapId(), 0, guidlow)); SetObjectScale(1.0f); SetOwnerGUID(owner->GetGUID()); @@ -190,7 +190,7 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) float o = fields[3].GetFloat(); uint32 mapId = fields[4].GetUInt16(); - Object::_Create(ObjectGuid::Create<HighGuid::Corpse>(mapId, 0, guid)); + _Create(ObjectGuid::Create<HighGuid::Corpse>(mapId, 0, guid)); SetObjectScale(1.0f); SetDisplayId(fields[5].GetUInt32()); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 739beed45c6..b25514ef657 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1829,9 +1829,9 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu SetOriginalEntry(entry); if (vehId || cinfo->VehicleId) - Object::_Create(ObjectGuid::Create<HighGuid::Vehicle>(GetMapId(), entry, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Vehicle>(GetMapId(), entry, guidlow)); else - Object::_Create(ObjectGuid::Create<HighGuid::Creature>(GetMapId(), entry, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Creature>(GetMapId(), entry, guidlow)); if (!UpdateEntry(entry, data)) return false; diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index 4a86d6c0e91..d59de6ae949 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -92,7 +92,7 @@ bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caste return false; } - WorldObject::_Create(ObjectGuid::Create<HighGuid::DynamicObject>(GetMapId(), spell->Id, guidlow)); + _Create(ObjectGuid::Create<HighGuid::DynamicObject>(GetMapId(), spell->Id, guidlow)); PhasingHandler::InheritPhaseShift(this, caster); UpdatePositionData(); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index a5742dc17eb..00ef5e065c2 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1023,7 +1023,7 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD m_updateFlag.ServerTime = true; } - Object::_Create(guid); + _Create(guid); m_goInfo = goInfo; m_goTemplateAddon = sObjectMgr->GetGameObjectTemplateAddon(entry); diff --git a/src/server/game/Entities/Item/Container/Bag.cpp b/src/server/game/Entities/Item/Container/Bag.cpp index 15d5c6795b2..70ae39e471f 100644 --- a/src/server/game/Entities/Item/Container/Bag.cpp +++ b/src/server/game/Entities/Item/Container/Bag.cpp @@ -75,7 +75,7 @@ bool Bag::Create(ObjectGuid::LowType guidlow, uint32 itemid, ItemContext context if (!itemProto || itemProto->GetContainerSlots() > MAX_BAG_SIZE) return false; - Object::_Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); _bonusData.Initialize(itemProto); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index cb7bdad249b..a10d829c6b7 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -506,7 +506,7 @@ Item::~Item() = default; bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext context, Player const* owner) { - Object::_Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); SetEntry(itemId); SetObjectScale(1.0f); @@ -922,7 +922,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie // create item before any checks for store correct guid // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB - Object::_Create(ObjectGuid::Create<HighGuid::Item>(guid)); + _Create(ObjectGuid::Create<HighGuid::Item>(guid)); // Set entry, MUST be before proto check SetEntry(entry); diff --git a/src/server/game/Entities/Object/BaseEntity.cpp b/src/server/game/Entities/Object/BaseEntity.cpp new file mode 100644 index 00000000000..e52fafbe806 --- /dev/null +++ b/src/server/game/Entities/Object/BaseEntity.cpp @@ -0,0 +1,647 @@ +/* + * 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 "BaseEntity.h" +#include "Conversation.h" +#include "DB2Structure.h" +#include "Errors.h" +#include "GameTime.h" +#include "Log.h" +#include "MovementPackets.h" +#include "Player.h" +#include "SmoothPhasing.h" +#include "Transport.h" +#include "UpdateData.h" +#include "Vehicle.h" + +BaseEntity::BaseEntity() = default; + +BaseEntity::~BaseEntity() +{ + if (IsInWorld()) + { + TC_LOG_FATAL("misc", "BaseEntity::~BaseEntity {} deleted but still in world!!", GetGUID()); + ABORT(); + } + + if (m_objectUpdated) + { + TC_LOG_FATAL("misc", "BaseEntity::~BaseEntity {} deleted but still in update list!!", GetGUID()); + ABORT(); + } +} + +void BaseEntity::AddToWorld() +{ + if (m_inWorld) + return; + + m_inWorld = true; + + // synchronize values mirror with values array (changes will send in updatecreate opcode any way + ASSERT(!m_objectUpdated); + ClearUpdateMask(false); +} + +void BaseEntity::RemoveFromWorld() +{ + if (!m_inWorld) + return; + + m_inWorld = false; + + // if we remove from world then sending changes not required + ClearUpdateMask(true); +} + +void BaseEntity::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const +{ + if (!target) + return; + + uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT; + uint8 objectType = m_objectTypeId; + CreateObjectBits flags = m_updateFlag; + + if (target == this) // building packet for yourself + { + flags.ThisIsYou = true; + flags.ActivePlayer = true; + objectType = TYPEID_ACTIVE_PLAYER; + } + + if (IsWorldObject()) + { + WorldObject const* worldObject = static_cast<WorldObject const*>(this); + if (worldObject->GetSmoothPhasing() && worldObject->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID())) + flags.SmoothPhasing = true; + } + + ByteBuffer& buf = data->GetBuffer(); + buf << uint8(updateType); + buf << GetGUID(); + buf << uint8(objectType); + + BuildMovementUpdate(&buf, flags, target); + + UF::UpdateFieldFlag fieldFlags = GetUpdateFieldFlagsFor(target); + 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); + + data->AddUpdateBlock(); +} + +void BaseEntity::SendUpdateToPlayer(Player* player) const +{ + // send create update to player + UpdateData upd(player->GetMapId()); + WorldPacket packet; + + if (player->HaveAtClient(this)) + BuildValuesUpdateBlockForPlayer(&upd, player); + else + BuildCreateUpdateBlockForPlayer(&upd, player); + upd.BuildPacket(&packet); + player->SendDirectMessage(&packet); +} + +void BaseEntity::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const +{ + ByteBuffer& buf = PrepareValuesUpdateBuffer(data); + + 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); + + data->AddUpdateBlock(); +} + +void BaseEntity::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) +{ + data->append(fragments.data(), fragments.size()); + *data << uint8(WowCS::EntityFragment::End); +} + +void BaseEntity::BuildDestroyUpdateBlock(UpdateData* data) const +{ + data->AddDestroyObject(GetGUID()); +} + +void BaseEntity::BuildOutOfRangeUpdateBlock(UpdateData* data) const +{ + data->AddOutOfRangeGUID(GetGUID()); +} + +ByteBuffer& BaseEntity::PrepareValuesUpdateBuffer(UpdateData* data) const +{ + ByteBuffer& buffer = data->GetBuffer(); + buffer << uint8(UPDATETYPE_VALUES); + buffer << GetGUID(); + return buffer; +} + +void BaseEntity::DestroyForPlayer(Player const* target) const +{ + ASSERT(target); + + UpdateData updateData(target->GetMapId()); + BuildDestroyUpdateBlock(&updateData); + WorldPacket packet; + updateData.BuildPacket(&packet); + target->SendDirectMessage(&packet); +} + +void BaseEntity::SendOutOfRangeForPlayer(Player const* target) const +{ + ASSERT(target); + + UpdateData updateData(target->GetMapId()); + BuildOutOfRangeUpdateBlock(&updateData); + WorldPacket packet; + updateData.BuildPacket(&packet); + target->SendDirectMessage(&packet); +} + +void BaseEntity::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const +{ + std::span<uint32 const> PauseTimes; + if (IsGameObject()) + PauseTimes = static_cast<GameObject const*>(this)->GetPauseTimes(); + + data->WriteBit(IsWorldObject()); // HasPositionFragment + data->WriteBit(flags.NoBirthAnim); + data->WriteBit(flags.EnablePortals); + data->WriteBit(flags.PlayHoverAnim); + data->WriteBit(flags.ThisIsYou); + data->WriteBit(flags.MovementUpdate); + data->WriteBit(flags.MovementTransport); + data->WriteBit(flags.Stationary); + data->WriteBit(flags.CombatVictim); + data->WriteBit(flags.ServerTime); + data->WriteBit(flags.Vehicle); + data->WriteBit(flags.AnimKit); + data->WriteBit(flags.Rotation); + data->WriteBit(flags.GameObject); + data->WriteBit(flags.SmoothPhasing); + data->WriteBit(flags.SceneObject); + data->WriteBit(flags.ActivePlayer); + data->WriteBit(flags.Conversation); + data->WriteBit(flags.Room); + data->WriteBit(flags.Decor); + data->WriteBit(flags.MeshObject); + data->FlushBits(); + + if (flags.MovementUpdate) + { + Unit const* unit = static_cast<Unit const*>(this); + bool HasFallDirection = unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING); + bool HasFall = HasFallDirection || unit->m_movementInfo.jump.fallTime != 0; + bool HasSpline = unit->IsSplineEnabled(); + bool HasInertia = unit->m_movementInfo.inertia.has_value(); + bool HasAdvFlying = unit->m_movementInfo.advFlying.has_value(); + bool HasDriveStatus = unit->m_movementInfo.driveStatus.has_value(); + bool HasStandingOnGameObjectGUID = unit->m_movementInfo.standingOnGameObjectGUID.has_value(); + + *data << GetGUID(); // MoverGUID + + *data << uint32(unit->GetUnitMovementFlags()); + *data << uint32(unit->GetExtraUnitMovementFlags()); + *data << uint32(unit->GetExtraUnitMovementFlags2()); + + *data << uint32(unit->m_movementInfo.time); // MoveTime + *data << float(unit->GetPositionX()); + *data << float(unit->GetPositionY()); + *data << float(unit->GetPositionZ()); + *data << float(unit->GetOrientation()); + + *data << float(unit->m_movementInfo.pitch); // Pitch + *data << float(unit->m_movementInfo.stepUpStartElevation); // StepUpStartElevation + + *data << uint32(0); // RemoveForcesIDs.size() + *data << uint32(0); // MoveIndex + + //for (std::size_t i = 0; i < RemoveForcesIDs.size(); ++i) + // *data << ObjectGuid(RemoveForcesIDs); + + data->WriteBit(HasStandingOnGameObjectGUID); // HasStandingOnGameObjectGUID + data->WriteBit(!unit->m_movementInfo.transport.guid.IsEmpty()); // HasTransport + data->WriteBit(HasFall); // HasFall + data->WriteBit(HasSpline); // HasSpline - marks that the unit uses spline movement + data->WriteBit(false); // HeightChangeFailed + data->WriteBit(false); // RemoteTimeValid + data->WriteBit(HasInertia); // HasInertia + data->WriteBit(HasAdvFlying); // HasAdvFlying + data->WriteBit(HasDriveStatus); // HasDriveStatus + data->FlushBits(); + + if (!unit->m_movementInfo.transport.guid.IsEmpty()) + *data << unit->m_movementInfo.transport; + + if (HasStandingOnGameObjectGUID) + *data << *unit->m_movementInfo.standingOnGameObjectGUID; + + if (HasInertia) + { + *data << unit->m_movementInfo.inertia->id; + *data << unit->m_movementInfo.inertia->force.PositionXYZStream(); + *data << uint32(unit->m_movementInfo.inertia->lifetime); + } + + if (HasAdvFlying) + { + *data << float(unit->m_movementInfo.advFlying->forwardVelocity); + *data << float(unit->m_movementInfo.advFlying->upVelocity); + } + + if (HasFall) + { + *data << uint32(unit->m_movementInfo.jump.fallTime); // Time + *data << float(unit->m_movementInfo.jump.zspeed); // JumpVelocity + + if (data->WriteBit(HasFallDirection)) + { + *data << float(unit->m_movementInfo.jump.sinAngle); // Direction + *data << float(unit->m_movementInfo.jump.cosAngle); + *data << float(unit->m_movementInfo.jump.xyspeed); // Speed + } + } + + if (HasDriveStatus) + { + *data << float(unit->m_movementInfo.driveStatus->speed); + *data << float(unit->m_movementInfo.driveStatus->movementAngle); + data->WriteBit(unit->m_movementInfo.driveStatus->accelerating); + data->WriteBit(unit->m_movementInfo.driveStatus->drifting); + data->FlushBits(); + } + + *data << float(unit->GetSpeed(MOVE_WALK)); + *data << float(unit->GetSpeed(MOVE_RUN)); + *data << float(unit->GetSpeed(MOVE_RUN_BACK)); + *data << float(unit->GetSpeed(MOVE_SWIM)); + *data << float(unit->GetSpeed(MOVE_SWIM_BACK)); + *data << float(unit->GetSpeed(MOVE_FLIGHT)); + *data << float(unit->GetSpeed(MOVE_FLIGHT_BACK)); + *data << float(unit->GetSpeed(MOVE_TURN_RATE)); + *data << float(unit->GetSpeed(MOVE_PITCH_RATE)); + + if (MovementForces const* movementForces = unit->GetMovementForces()) + { + *data << uint32(movementForces->GetForces()->size()); + *data << float(movementForces->GetModMagnitude()); // MovementForcesModMagnitude + } + else + { + *data << uint32(0); + *data << float(1.0f); // MovementForcesModMagnitude + } + + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_AIR_FRICTION)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_MAX_VEL)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LIFT_COEFFICIENT)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_DOUBLE_JUMP_VEL_MOD)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_GLIDE_START_MIN_HEIGHT)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_ADD_IMPULSE_MAX_SPEED)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_BANKING_RATE)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_BANKING_RATE)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_DOWN)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_DOWN)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_UP)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_UP)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_SURFACE_FRICTION)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_OVER_MAX_DECELERATION)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LAUNCH_SPEED_COEFFICIENT)); + + data->WriteBit(HasSpline); + data->FlushBits(); + + if (MovementForces const* movementForces = unit->GetMovementForces()) + for (MovementForce const& force : *movementForces->GetForces()) + WorldPackets::Movement::CommonMovement::WriteMovementForceWithDirection(force, *data, unit); + + if (HasSpline) + WorldPackets::Movement::CommonMovement::WriteCreateObjectSplineDataBlock(*unit->movespline, *data); + } + + *data << uint32(PauseTimes.size()); + + if (flags.Stationary) + { + WorldObject const* self = static_cast<WorldObject const*>(this); + *data << self->GetStationaryPosition().PositionXYZOStream(); + } + + if (flags.CombatVictim) + { + Unit const* unit = static_cast<Unit const*>(this); + *data << unit->GetVictim()->GetGUID(); // CombatVictim + } + + if (flags.ServerTime) + *data << uint32(GameTime::GetGameTimeMS()); + + if (flags.Vehicle) + { + Unit const* unit = static_cast<Unit const*>(this); + *data << uint32(unit->GetVehicleKit()->GetVehicleInfo()->ID); // RecID + *data << float(unit->GetOrientation()); // InitialRawFacing + } + + if (flags.AnimKit) + { + WorldObject const* self = static_cast<WorldObject const*>(this); + *data << uint16(self->GetAIAnimKitId()); // AiID + *data << uint16(self->GetMovementAnimKitId()); // MovementID + *data << uint16(self->GetMeleeAnimKitId()); // MeleeID + } + + if (flags.Rotation) + { + GameObject const* gameObject = static_cast<GameObject const*>(this); + *data << uint64(gameObject->GetPackedLocalRotation()); // Rotation + } + + //if (flags.Room) + // *data << ObjectGuid(HouseGUID); + + //if (flags.Decor) + // *data << ObjectGuid(RoomGUID); + + //if (flags.MeshObject) + //{ + // *data << ObjectGuid(AttachParentGUID); + // *data << TaggedPosition<Position::XYZ>(PositionLocalSpace); + // *data << QuaternionData(RotationLocalSpace); + // *data << float(ScaleLocalSpace); + // *data << uint8(AttachmentFlags); + //} + + if (!PauseTimes.empty()) + data->append(PauseTimes.data(), PauseTimes.size()); + + if (flags.MovementTransport) + { + WorldObject const* self = static_cast<WorldObject const*>(this); + *data << self->m_movementInfo.transport; + } + + if (flags.GameObject) + { + GameObject const* gameObject = static_cast<GameObject const*>(this); + + bool bit8 = false; + bool isTransport = gameObject->GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT; + + *data << uint32(gameObject->GetWorldEffectID()); + + data->WriteBit(bit8); + data->WriteBit(isTransport); + data->WriteBit(gameObject->GetPathProgressForClient().has_value()); + data->FlushBits(); + if (isTransport) + { + Transport const* transport = static_cast<Transport const*>(gameObject); + uint32 period = transport->GetTransportPeriod(); + + *data << uint32((((int64(transport->GetTimer()) - int64(GameTime::GetGameTimeMS())) % period) + period) % period); // TimeOffset + *data << uint32(transport->GetNextStopTimestamp().value_or(0)); + data->WriteBit(transport->GetNextStopTimestamp().has_value()); + data->WriteBit(transport->IsStopped()); + data->WriteBit(false); + data->FlushBits(); + } + + if (bit8) + *data << uint32(0); + + if (gameObject->GetPathProgressForClient()) + *data << float(*gameObject->GetPathProgressForClient()); + } + + if (flags.SmoothPhasing) + { + SmoothPhasingInfo const* smoothPhasingInfo = static_cast<WorldObject const*>(this)->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID()); + ASSERT(smoothPhasingInfo); + + data->WriteBit(smoothPhasingInfo->ReplaceActive); + data->WriteBit(smoothPhasingInfo->StopAnimKits); + data->WriteBit(smoothPhasingInfo->ReplaceObject.has_value()); + data->FlushBits(); + if (smoothPhasingInfo->ReplaceObject) + *data << *smoothPhasingInfo->ReplaceObject; + } + + if (flags.SceneObject) + { + data->WriteBit(false); // HasLocalScriptData + data->WriteBit(false); // HasPetBattleFullUpdate + data->FlushBits(); + + // if (HasLocalScriptData) + // { + // data->WriteBits(Data.length(), 7); + // data->FlushBits(); + // data->WriteString(Data); + // } + + // if (HasPetBattleFullUpdate) + // { + // for (std::size_t i = 0; i < 2; ++i) + // { + // *data << ObjectGuid(Players[i].CharacterID); + // *data << int32(Players[i].TrapAbilityID); + // *data << int32(Players[i].TrapStatus); + // *data << uint16(Players[i].RoundTimeSecs); + // *data << int8(Players[i].FrontPet); + // *data << uint8(Players[i].InputFlags); + + // data->WriteBits(Players[i].Pets.size(), 2); + // data->FlushBits(); + // for (std::size_t j = 0; j < Players[i].Pets.size(); ++j) + // { + // *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID); + // *data << int32(Players[i].Pets[j].SpeciesID); + // *data << int32(Players[i].Pets[j].CreatureID); + // *data << int32(Players[i].Pets[j].DisplayID); + // *data << int16(Players[i].Pets[j].Level); + // *data << int16(Players[i].Pets[j].Xp); + // *data << int32(Players[i].Pets[j].CurHealth); + // *data << int32(Players[i].Pets[j].MaxHealth); + // *data << int32(Players[i].Pets[j].Power); + // *data << int32(Players[i].Pets[j].Speed); + // *data << int32(Players[i].Pets[j].NpcTeamMemberID); + // *data << uint8(Players[i].Pets[j].BreedQuality); + // *data << uint16(Players[i].Pets[j].StatusFlags); + // *data << int8(Players[i].Pets[j].Slot); + + // *data << uint32(Players[i].Pets[j].Abilities.size()); + // *data << uint32(Players[i].Pets[j].Auras.size()); + // *data << uint32(Players[i].Pets[j].States.size()); + // for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k) + // { + // *data << int32(Players[i].Pets[j].Abilities[k].AbilityID); + // *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining); + // *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining); + // *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex); + // *data << uint8(Players[i].Pets[j].Abilities[k].Pboid); + // } + + // for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k) + // { + // *data << int32(Players[i].Pets[j].Auras[k].AbilityID); + // *data << uint32(Players[i].Pets[j].Auras[k].InstanceID); + // *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining); + // *data << int32(Players[i].Pets[j].Auras[k].CurrentRound); + // *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID); + // } + + // for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k) + // { + // *data << uint32(Players[i].Pets[j].States[k].StateID); + // *data << int32(Players[i].Pets[j].States[k].StateValue); + // } + + // data->WriteBits(Players[i].Pets[j].CustomName.length(), 7); + // data->FlushBits(); + // data->WriteString(Players[i].Pets[j].CustomName); + // } + // } + + // for (std::size_t i = 0; i < 3; ++i) + // { + // *data << uint32(Enviros[j].Auras.size()); + // *data << uint32(Enviros[j].States.size()); + // for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j) + // { + // *data << int32(Enviros[j].Auras[j].AbilityID); + // *data << uint32(Enviros[j].Auras[j].InstanceID); + // *data << int32(Enviros[j].Auras[j].RoundsRemaining); + // *data << int32(Enviros[j].Auras[j].CurrentRound); + // *data << uint8(Enviros[j].Auras[j].CasterPBOID); + // } + + // for (std::size_t j = 0; j < Enviros[j].States.size(); ++j) + // { + // *data << uint32(Enviros[i].States[j].StateID); + // *data << int32(Enviros[i].States[j].StateValue); + // } + // } + + // *data << uint16(WaitingForFrontPetsMaxSecs); + // *data << uint16(PvpMaxRoundTime); + // *data << int32(CurRound); + // *data << uint32(NpcCreatureID); + // *data << uint32(NpcDisplayID); + // *data << int8(CurPetBattleState); + // *data << uint8(ForfeitPenalty); + // *data << ObjectGuid(InitialWildPetGUID); + // data->WriteBit(IsPVP); + // data->WriteBit(CanAwardXP); + // data->FlushBits(); + // } + } + + if (flags.ActivePlayer) + { + Player const* player = static_cast<Player const*>(this); + + bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty(); + bool HasRuneState = player->GetPowerIndex(POWER_RUNES) != MAX_POWERS; + + data->WriteBit(HasSceneInstanceIDs); + data->WriteBit(HasRuneState); + data->FlushBits(); + if (HasSceneInstanceIDs) + { + *data << uint32(player->GetSceneMgr().GetSceneTemplateByInstanceMap().size()); + for (auto const& [sceneInstanceId, _] : player->GetSceneMgr().GetSceneTemplateByInstanceMap()) + *data << uint32(sceneInstanceId); + } + if (HasRuneState) + { + float baseCd = float(player->GetRuneBaseCooldown()); + uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES)); + + *data << uint8((1 << maxRunes) - 1); + *data << uint8(player->GetRunesState()); + *data << uint32(maxRunes); + for (uint32 i = 0; i < maxRunes; ++i) + *data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); + } + } + + if (flags.Conversation) + { + Conversation const* self = static_cast<Conversation const*>(this); + if (data->WriteBit(self->GetTextureKitId() != 0)) + *data << uint32(self->GetTextureKitId()); + + data->FlushBits(); + } +} + +UF::UpdateFieldFlag BaseEntity::GetUpdateFieldFlagsFor(Player const* /*target*/) const +{ + return UF::UpdateFieldFlag::None; +} + +void BaseEntity::AddToObjectUpdateIfNeeded() +{ + if (m_inWorld && !m_objectUpdated) + m_objectUpdated = AddToObjectUpdate(); +} + +void BaseEntity::ClearUpdateMask(bool remove) +{ + m_entityFragments.IdsChanged = false; + + if (m_objectUpdated) + { + if (remove) + RemoveFromObjectUpdate(); + m_objectUpdated = false; + } +} + +void BaseEntity::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const +{ + UpdateDataMapType::iterator iter = data_map.try_emplace(player, player->GetMapId()).first; + BuildValuesUpdateBlockForPlayer(&iter->second, iter->first); +} + +std::string BaseEntity::GetDebugInfo() const +{ + return Trinity::StringFormat("{}", GetGUID()); +} diff --git a/src/server/game/Entities/Object/BaseEntity.h b/src/server/game/Entities/Object/BaseEntity.h new file mode 100644 index 00000000000..7844fa9295d --- /dev/null +++ b/src/server/game/Entities/Object/BaseEntity.h @@ -0,0 +1,434 @@ +/* + * 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_BASE_ENTITY_H +#define TRINITYCORE_BASE_ENTITY_H + +#include "Common.h" +#include "ObjectGuid.h" +#include "UpdateFields.h" +#include "WowCSEntityDefinitions.h" +#include <unordered_map> + +class BaseEntity; +class Player; +class UpdateData; +class WorldPacket; + +typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType; + +struct CreateObjectBits +{ + bool NoBirthAnim : 1; + bool EnablePortals : 1; + bool PlayHoverAnim : 1; + bool MovementUpdate : 1; + bool MovementTransport : 1; + bool Stationary : 1; + bool CombatVictim : 1; + bool ServerTime : 1; + bool Vehicle : 1; + bool AnimKit : 1; + bool Rotation : 1; + bool GameObject : 1; + bool SmoothPhasing : 1; + bool ThisIsYou : 1; + bool SceneObject : 1; + bool ActivePlayer : 1; + bool Conversation : 1; + bool Room : 1; + bool Decor : 1; + bool MeshObject : 1; + + void Clear() + { + memset(this, 0, sizeof(CreateObjectBits)); + } +}; + +namespace UF +{ + class UpdateFieldHolder + { + public: + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + 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; } + + inline BaseEntity* GetOwner(); + + private: + friend BaseEntity; + + // This class is tightly tied to BaseEntity::m_values member, do not construct elsewhere + UpdateFieldHolder() = default; + + uint32 _changesMask = 0; // changes mask for data of Object subclasses + }; + + template<typename T> + inline bool SetUpdateFieldValue(UpdateFieldPrivateSetter<T>& setter, typename UpdateFieldPrivateSetter<T>::value_type&& value) + { + return setter.SetValue(std::move(value)); + } + + template<typename T> + inline typename DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter) + { + return setter.AddValue(); + } + + template<typename T> + inline typename DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) + { + return setter.InsertValue(index); + } + + template<typename T> + inline void RemoveDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) + { + setter.RemoveValue(index); + } + + template<typename T> + inline void ClearDynamicUpdateFieldValues(DynamicUpdateFieldSetter<T>& setter) + { + setter.Clear(); + } + + template<typename K, typename V> + inline void RemoveMapUpdateFieldValue(MapUpdateFieldSetter<K, V>& setter, std::type_identity_t<K> const& key) + { + setter.RemoveKey(key); + } + + template<typename T> + inline void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter<T>& setter) + { + setter.RemoveValue(); + } +} + +class TC_GAME_API BaseEntity +{ + ObjectGuid m_guid; + + public: + virtual ~BaseEntity(); + + bool IsInWorld() const { return m_inWorld; } + + virtual void AddToWorld(); + virtual void RemoveFromWorld(); + + static ObjectGuid GetGUID(BaseEntity const* o) { return o ? o->GetGUID() : ObjectGuid::Empty; } + ObjectGuid const& GetGUID() const { return m_guid; } + + TypeID GetTypeId() const { return m_objectTypeId; } + bool isType(TypeMask mask) const { return (ObjectTypeMask[m_objectTypeId] & mask) != 0; } + + inline bool IsWorldObject() const { return isType(TYPEMASK_WORLDOBJECT); } + inline bool IsItem() const { return isType(TYPEMASK_ITEM); } + inline bool IsUnit() const { return isType(TYPEMASK_UNIT); } + inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; } + inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; } + inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; } + inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; } + inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; } + inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; } + inline bool IsSceneObject() const { return GetTypeId() == TYPEID_SCENEOBJECT; } + inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; } + inline bool IsMeshObject() const { return GetTypeId() == TYPEID_MESH_OBJECT; } + + virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const; + void SendUpdateToPlayer(Player* player) const; + + void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const; + void BuildDestroyUpdateBlock(UpdateData* data) const; + void BuildOutOfRangeUpdateBlock(UpdateData* data) const; + ByteBuffer& PrepareValuesUpdateBuffer(UpdateData* data) const; + + virtual void DestroyForPlayer(Player const* target) const; + void SendOutOfRangeForPlayer(Player const* target) const; + + virtual void ClearUpdateMask(bool remove); + + virtual std::string GetNameForLocaleIdx(LocaleConstant locale) const = 0; + + void SetIsNewObject(bool enable) { m_isNewObject = enable; } + bool IsDestroyedObject() const { return m_isDestroyedObject; } + void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; } + virtual void BuildUpdate([[maybe_unused]] UpdateDataMapType& data_map) { } + void BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const; + + friend UF::UpdateFieldHolder; + UF::UpdateFieldHolder m_values; + + template<typename T> + void ForceUpdateFieldChange(UF::UpdateFieldPrivateSetter<T> const& /*setter*/) + { + AddToObjectUpdateIfNeeded(); + } + + virtual std::string GetDebugInfo() const; + + protected: + BaseEntity(); + + void _Create(ObjectGuid const& guid) { m_guid = guid; } + + template<typename T> + void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) + { + if (UF::SetUpdateFieldValue(setter, std::move(value))) + AddToObjectUpdateIfNeeded(); + } + + template<typename T> + void SetUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) + { + static_assert(std::is_integral_v<T>, "SetUpdateFieldFlagValue must be used with integral types"); + this->SetUpdateFieldValue(setter, setter.GetValue() | flag); + } + + template<typename T> + void RemoveUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) + { + static_assert(std::is_integral_v<T>, "RemoveUpdateFieldFlagValue must be used with integral types"); + this->SetUpdateFieldValue(setter, setter.GetValue() & ~flag); + } + + template<typename T> + typename UF::DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter) + { + AddToObjectUpdateIfNeeded(); + return UF::AddDynamicUpdateFieldValue(setter); + } + + template<typename T> + typename UF::DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) + { + AddToObjectUpdateIfNeeded(); + return UF::InsertDynamicUpdateFieldValue(setter, index); + } + + template<typename T> + void RemoveDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) + { + AddToObjectUpdateIfNeeded(); + UF::RemoveDynamicUpdateFieldValue(setter, index); + } + + template<typename K, typename V> + void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter<K, V> setter, std::type_identity_t<K> const& key) + { + AddToObjectUpdateIfNeeded(); + UF::RemoveMapUpdateFieldValue(setter, key); + } + + template<typename T> + void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter<T> setter) + { + AddToObjectUpdateIfNeeded(); + UF::ClearDynamicUpdateFieldValues(setter); + } + + template<typename T> + void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter<T> setter) + { + AddToObjectUpdateIfNeeded(); + UF::RemoveOptionalUpdateFieldValue(setter); + } + + // stat system helpers + template<typename T> + void SetUpdateFieldStatValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) + { + static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types"); + this->SetUpdateFieldValue(setter, std::max(value, T(0))); + } + + template<typename T> + void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type mod, bool apply) + { + static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types"); + + T value = setter.GetValue(); + if (apply) + value += mod; + else + value -= mod; + + this->SetUpdateFieldValue(setter, value); + } + + template<typename T> + void ApplyPercentModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, float percent, bool apply) + { + static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types"); + + T value = setter.GetValue(); + + // don't want to include Util.h here + //ApplyPercentModFloatVar(value, percent, apply); + if (percent == -100.0f) + percent = -99.99f; + value *= (apply ? (100.0f + percent) / 100.0f : 100.0f / (100.0f + percent)); + + this->SetUpdateFieldValue(setter, value); + } + + template<typename Action> + void DoWithSuppressingObjectUpdates(Action&& action) + { + bool wasUpdatedBeforeAction = m_objectUpdated; + action(); + if (m_objectUpdated && !wasUpdatedBeforeAction) + { + RemoveFromObjectUpdate(); + m_objectUpdated = false; + } + } + + void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const; + virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const; + virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; + virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; + static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments); + + TypeID m_objectTypeId = static_cast<TypeID>(NUM_CLIENT_OBJECT_TYPES); + CreateObjectBits m_updateFlag = {}; + WowCS::EntityFragmentsHolder m_entityFragments; + + virtual bool AddToObjectUpdate() = 0; + virtual void RemoveFromObjectUpdate() = 0; + void AddToObjectUpdateIfNeeded(); + + bool m_objectUpdated = false; + + private: + bool m_inWorld = false; + bool m_isNewObject = false; + bool m_isDestroyedObject = false; + + BaseEntity(BaseEntity const& right) = delete; + BaseEntity(BaseEntity&& right) = delete; + BaseEntity& operator=(BaseEntity const& right) = delete; + BaseEntity& operator=(BaseEntity&& right) = delete; +}; + +inline BaseEntity* UF::UpdateFieldHolder::GetOwner() +{ +#if TRINITY_COMPILER == TRINITY_COMPILER_GNU +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + + return reinterpret_cast<BaseEntity*>(reinterpret_cast<std::byte*>(this) - offsetof(BaseEntity, m_values)); + +#if TRINITY_COMPILER == TRINITY_COMPILER_GNU +#pragma GCC diagnostic pop +#endif +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field) +{ + BaseEntity* owner = GetOwner(); + owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + 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) +{ + BaseEntity* owner = GetOwner(); + owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + 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*/) +{ + BaseEntity* owner = GetOwner(); + owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + 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) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + { + _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); + if (!_changesMask) + owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + } + else + owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + + (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask(); +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + { + _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); + if (!_changesMask) + owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + } + else + owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + + auto& uf = (static_cast<Derived*>(owner)->*field); + if (uf.has_value()) + uf._value->ClearChangesMask(); +} + +#endif // TRINITYCORE_BASE_ENTITY_H diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 3f68036a278..bb506f62bd9 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -20,19 +20,16 @@ #include "CellImpl.h" #include "CinematicMgr.h" #include "CombatLogPackets.h" -#include "Common.h" -#include "Creature.h" +#include "Corpse.h" #include "CreatureGroups.h" #include "DB2Stores.h" -#include "GameTime.h" +#include "DynamicObject.h" +#include "GameObject.h" #include "GridNotifiersImpl.h" -#include "G3DPosition.hpp" #include "InstanceScenario.h" -#include "Item.h" #include "Log.h" +#include "Map.h" #include "MiscPackets.h" -#include "MovementPackets.h" -#include "MovementTypedefs.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "OutdoorPvPMgr.h" @@ -41,17 +38,14 @@ #include "Player.h" #include "ReputationMgr.h" #include "SmoothPhasing.h" +#include "Spell.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "SpellPackets.h" #include "TemporarySummon.h" #include "Totem.h" -#include "Transport.h" -#include "Unit.h" #include "UpdateData.h" -#include "Util.h" #include "VMapFactory.h" -#include "Vehicle.h" #include "VMapManager.h" #include "World.h" #include <G3D/Vector3.h> @@ -73,46 +67,13 @@ Object::Object() : m_scriptRef(this, NoopObjectDeleter()) m_updateFlag.Clear(); m_entityFragments.Add(WowCS::EntityFragment::CGObject, false); - - m_inWorld = false; - m_isNewObject = false; - m_isDestroyedObject = false; - m_objectUpdated = false; } -Object::~Object() -{ - if (IsInWorld()) - { - TC_LOG_FATAL("misc", "Object::~Object {} deleted but still in world!!", GetGUID().ToString()); - if (Item* item = ToItem()) - TC_LOG_FATAL("misc", "Item slot {}", item->GetSlot()); - ABORT(); - } - - if (m_objectUpdated) - { - TC_LOG_FATAL("misc", "Object::~Object {} deleted but still in update list!!", GetGUID().ToString()); - ABORT(); - } -} - -void Object::_Create(ObjectGuid const& guid) -{ - m_objectUpdated = false; - m_guid = guid; -} +Object::~Object() = default; void Object::AddToWorld() { - if (m_inWorld) - return; - - m_inWorld = true; - - // synchronize values mirror with values array (changes will send in updatecreate opcode any way - ASSERT(!m_objectUpdated); - ClearUpdateMask(false); + BaseEntity::AddToWorld(); // Set new ref when adding to world (except if we already have one - also set in constructor to allow scripts to work in initialization phase) // Changing the ref when adding/removing from world prevents accessing players on different maps (possibly from another thread) @@ -122,95 +83,11 @@ void Object::AddToWorld() void Object::RemoveFromWorld() { - if (!m_inWorld) - return; - - m_inWorld = false; - - // if we remove from world then sending changes not required - ClearUpdateMask(true); + BaseEntity::RemoveFromWorld(); m_scriptRef = nullptr; } -void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const -{ - if (!target) - return; - - uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT; - uint8 objectType = m_objectTypeId; - CreateObjectBits flags = m_updateFlag; - - if (target == this) // building packet for yourself - { - flags.ThisIsYou = true; - flags.ActivePlayer = true; - objectType = TYPEID_ACTIVE_PLAYER; - } - - if (IsWorldObject()) - { - WorldObject const* worldObject = static_cast<WorldObject const*>(this); - if (worldObject->GetSmoothPhasing() && worldObject->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID())) - flags.SmoothPhasing = true; - } - - ByteBuffer& buf = data->GetBuffer(); - buf << uint8(updateType); - buf << GetGUID(); - buf << uint8(objectType); - - BuildMovementUpdate(&buf, flags, target); - - UF::UpdateFieldFlag fieldFlags = GetUpdateFieldFlagsFor(target); - 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); - - data->AddUpdateBlock(); -} - -void Object::SendUpdateToPlayer(Player* player) -{ - // send create update to player - UpdateData upd(player->GetMapId()); - WorldPacket packet; - - if (player->HaveAtClient(this)) - BuildValuesUpdateBlockForPlayer(&upd, player); - else - BuildCreateUpdateBlockForPlayer(&upd, player); - upd.BuildPacket(&packet); - player->SendDirectMessage(&packet); -} - -void Object::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const -{ - ByteBuffer& buf = PrepareValuesUpdateBuffer(data); - - 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); - - data->AddUpdateBlock(); -} - void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::UpdateFieldFlag flags, Player const* target) const { ByteBuffer& buf = PrepareValuesUpdateBuffer(data); @@ -224,12 +101,6 @@ void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::Updat data->AddUpdateBlock(); } -void Object::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) -{ - data->append(fragments.data(), fragments.size()); - *data << uint8(WowCS::EntityFragment::End); -} - void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const { uint8 contentsChangedMask = WowCS::CGObjectChangedMask; @@ -242,505 +113,22 @@ void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* da *data << uint8(contentsChangedMask); } -void Object::BuildDestroyUpdateBlock(UpdateData* data) const -{ - data->AddDestroyObject(GetGUID()); -} - -void Object::BuildOutOfRangeUpdateBlock(UpdateData* data) const -{ - data->AddOutOfRangeGUID(GetGUID()); -} - -ByteBuffer& Object::PrepareValuesUpdateBuffer(UpdateData* data) const -{ - ByteBuffer& buffer = data->GetBuffer(); - buffer << uint8(UPDATETYPE_VALUES); - buffer << GetGUID(); - return buffer; -} - -void Object::DestroyForPlayer(Player const* target) const -{ - ASSERT(target); - - UpdateData updateData(target->GetMapId()); - BuildDestroyUpdateBlock(&updateData); - WorldPacket packet; - updateData.BuildPacket(&packet); - target->SendDirectMessage(&packet); -} - -void Object::SendOutOfRangeForPlayer(Player const* target) const -{ - ASSERT(target); - - UpdateData updateData(target->GetMapId()); - BuildOutOfRangeUpdateBlock(&updateData); - WorldPacket packet; - updateData.BuildPacket(&packet); - target->SendDirectMessage(&packet); -} - -void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const -{ - std::span<uint32 const> PauseTimes; - if (IsGameObject()) - PauseTimes = static_cast<GameObject const*>(this)->GetPauseTimes(); - - data->WriteBit(IsWorldObject()); // HasPositionFragment - data->WriteBit(flags.NoBirthAnim); - data->WriteBit(flags.EnablePortals); - data->WriteBit(flags.PlayHoverAnim); - data->WriteBit(flags.ThisIsYou); - data->WriteBit(flags.MovementUpdate); - data->WriteBit(flags.MovementTransport); - data->WriteBit(flags.Stationary); - data->WriteBit(flags.CombatVictim); - data->WriteBit(flags.ServerTime); - data->WriteBit(flags.Vehicle); - data->WriteBit(flags.AnimKit); - data->WriteBit(flags.Rotation); - data->WriteBit(flags.GameObject); - data->WriteBit(flags.SmoothPhasing); - data->WriteBit(flags.SceneObject); - data->WriteBit(flags.ActivePlayer); - data->WriteBit(flags.Conversation); - data->WriteBit(flags.Room); - data->WriteBit(flags.Decor); - data->WriteBit(flags.MeshObject); - data->FlushBits(); - - if (flags.MovementUpdate) - { - Unit const* unit = static_cast<Unit const*>(this); - bool HasFallDirection = unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING); - bool HasFall = HasFallDirection || unit->m_movementInfo.jump.fallTime != 0; - bool HasSpline = unit->IsSplineEnabled(); - bool HasInertia = unit->m_movementInfo.inertia.has_value(); - bool HasAdvFlying = unit->m_movementInfo.advFlying.has_value(); - bool HasDriveStatus = unit->m_movementInfo.driveStatus.has_value(); - bool HasStandingOnGameObjectGUID = unit->m_movementInfo.standingOnGameObjectGUID.has_value(); - - *data << GetGUID(); // MoverGUID - - *data << uint32(unit->GetUnitMovementFlags()); - *data << uint32(unit->GetExtraUnitMovementFlags()); - *data << uint32(unit->GetExtraUnitMovementFlags2()); - - *data << uint32(unit->m_movementInfo.time); // MoveTime - *data << float(unit->GetPositionX()); - *data << float(unit->GetPositionY()); - *data << float(unit->GetPositionZ()); - *data << float(unit->GetOrientation()); - - *data << float(unit->m_movementInfo.pitch); // Pitch - *data << float(unit->m_movementInfo.stepUpStartElevation); // StepUpStartElevation - - *data << uint32(0); // RemoveForcesIDs.size() - *data << uint32(0); // MoveIndex - - //for (std::size_t i = 0; i < RemoveForcesIDs.size(); ++i) - // *data << ObjectGuid(RemoveForcesIDs); - - data->WriteBit(HasStandingOnGameObjectGUID); // HasStandingOnGameObjectGUID - data->WriteBit(!unit->m_movementInfo.transport.guid.IsEmpty()); // HasTransport - data->WriteBit(HasFall); // HasFall - data->WriteBit(HasSpline); // HasSpline - marks that the unit uses spline movement - data->WriteBit(false); // HeightChangeFailed - data->WriteBit(false); // RemoteTimeValid - data->WriteBit(HasInertia); // HasInertia - data->WriteBit(HasAdvFlying); // HasAdvFlying - data->WriteBit(HasDriveStatus); // HasDriveStatus - data->FlushBits(); - - if (!unit->m_movementInfo.transport.guid.IsEmpty()) - *data << unit->m_movementInfo.transport; - - if (HasStandingOnGameObjectGUID) - *data << *unit->m_movementInfo.standingOnGameObjectGUID; - - if (HasInertia) - { - *data << unit->m_movementInfo.inertia->id; - *data << unit->m_movementInfo.inertia->force.PositionXYZStream(); - *data << uint32(unit->m_movementInfo.inertia->lifetime); - } - - if (HasAdvFlying) - { - *data << float(unit->m_movementInfo.advFlying->forwardVelocity); - *data << float(unit->m_movementInfo.advFlying->upVelocity); - } - - if (HasFall) - { - *data << uint32(unit->m_movementInfo.jump.fallTime); // Time - *data << float(unit->m_movementInfo.jump.zspeed); // JumpVelocity - - if (data->WriteBit(HasFallDirection)) - { - *data << float(unit->m_movementInfo.jump.sinAngle); // Direction - *data << float(unit->m_movementInfo.jump.cosAngle); - *data << float(unit->m_movementInfo.jump.xyspeed); // Speed - } - } - - if (HasDriveStatus) - { - *data << float(unit->m_movementInfo.driveStatus->speed); - *data << float(unit->m_movementInfo.driveStatus->movementAngle); - data->WriteBit(unit->m_movementInfo.driveStatus->accelerating); - data->WriteBit(unit->m_movementInfo.driveStatus->drifting); - data->FlushBits(); - } - - *data << float(unit->GetSpeed(MOVE_WALK)); - *data << float(unit->GetSpeed(MOVE_RUN)); - *data << float(unit->GetSpeed(MOVE_RUN_BACK)); - *data << float(unit->GetSpeed(MOVE_SWIM)); - *data << float(unit->GetSpeed(MOVE_SWIM_BACK)); - *data << float(unit->GetSpeed(MOVE_FLIGHT)); - *data << float(unit->GetSpeed(MOVE_FLIGHT_BACK)); - *data << float(unit->GetSpeed(MOVE_TURN_RATE)); - *data << float(unit->GetSpeed(MOVE_PITCH_RATE)); - - if (MovementForces const* movementForces = unit->GetMovementForces()) - { - *data << uint32(movementForces->GetForces()->size()); - *data << float(movementForces->GetModMagnitude()); // MovementForcesModMagnitude - } - else - { - *data << uint32(0); - *data << float(1.0f); // MovementForcesModMagnitude - } - - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_AIR_FRICTION)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_MAX_VEL)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LIFT_COEFFICIENT)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_DOUBLE_JUMP_VEL_MOD)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_GLIDE_START_MIN_HEIGHT)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_ADD_IMPULSE_MAX_SPEED)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_BANKING_RATE)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_BANKING_RATE)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_DOWN)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_DOWN)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_UP)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_UP)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_SURFACE_FRICTION)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_OVER_MAX_DECELERATION)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LAUNCH_SPEED_COEFFICIENT)); - - data->WriteBit(HasSpline); - data->FlushBits(); - - if (MovementForces const* movementForces = unit->GetMovementForces()) - for (MovementForce const& force : *movementForces->GetForces()) - WorldPackets::Movement::CommonMovement::WriteMovementForceWithDirection(force, *data, unit); - - if (HasSpline) - WorldPackets::Movement::CommonMovement::WriteCreateObjectSplineDataBlock(*unit->movespline, *data); - } - - *data << uint32(PauseTimes.size()); - - if (flags.Stationary) - { - WorldObject const* self = static_cast<WorldObject const*>(this); - *data << self->GetStationaryPosition().PositionXYZOStream(); - } - - if (flags.CombatVictim) - { - Unit const* unit = static_cast<Unit const*>(this); - *data << unit->GetVictim()->GetGUID(); // CombatVictim - } - - if (flags.ServerTime) - *data << uint32(GameTime::GetGameTimeMS()); - - if (flags.Vehicle) - { - Unit const* unit = static_cast<Unit const*>(this); - *data << uint32(unit->GetVehicleKit()->GetVehicleInfo()->ID); // RecID - *data << float(unit->GetOrientation()); // InitialRawFacing - } - - if (flags.AnimKit) - { - WorldObject const* self = static_cast<WorldObject const*>(this); - *data << uint16(self->GetAIAnimKitId()); // AiID - *data << uint16(self->GetMovementAnimKitId()); // MovementID - *data << uint16(self->GetMeleeAnimKitId()); // MeleeID - } - - if (flags.Rotation) - { - GameObject const* gameObject = static_cast<GameObject const*>(this); - *data << uint64(gameObject->GetPackedLocalRotation()); // Rotation - } - - //if (flags.Room) - // *data << ObjectGuid(HouseGUID); - - //if (flags.Decor) - // *data << ObjectGuid(RoomGUID); - - //if (flags.MeshObject) - //{ - // *data << ObjectGuid(AttachParentGUID); - // *data << TaggedPosition<Position::XYZ>(PositionLocalSpace); - // *data << QuaternionData(RotationLocalSpace); - // *data << float(ScaleLocalSpace); - // *data << uint8(AttachmentFlags); - //} - - if (!PauseTimes.empty()) - data->append(PauseTimes.data(), PauseTimes.size()); - - if (flags.MovementTransport) - { - WorldObject const* self = static_cast<WorldObject const*>(this); - *data << self->m_movementInfo.transport; - } - - if (flags.GameObject) - { - GameObject const* gameObject = static_cast<GameObject const*>(this); - 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) - { - uint32 period = transport->GetTransportPeriod(); - - *data << uint32((((int64(transport->GetTimer()) - int64(GameTime::GetGameTimeMS())) % period) + period) % period); // TimeOffset - *data << uint32(transport->GetNextStopTimestamp().value_or(0)); - data->WriteBit(transport->GetNextStopTimestamp().has_value()); - data->WriteBit(transport->IsStopped()); - data->WriteBit(false); - data->FlushBits(); - } - - if (bit8) - *data << uint32(0); - - if (gameObject->GetPathProgressForClient()) - *data << float(*gameObject->GetPathProgressForClient()); - } - - if (flags.SmoothPhasing) - { - SmoothPhasingInfo const* smoothPhasingInfo = static_cast<WorldObject const*>(this)->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID()); - ASSERT(smoothPhasingInfo); - - data->WriteBit(smoothPhasingInfo->ReplaceActive); - data->WriteBit(smoothPhasingInfo->StopAnimKits); - data->WriteBit(smoothPhasingInfo->ReplaceObject.has_value()); - data->FlushBits(); - if (smoothPhasingInfo->ReplaceObject) - *data << *smoothPhasingInfo->ReplaceObject; - } - - if (flags.SceneObject) - { - data->WriteBit(false); // HasLocalScriptData - data->WriteBit(false); // HasPetBattleFullUpdate - data->FlushBits(); - - // if (HasLocalScriptData) - // { - // data->WriteBits(Data.length(), 7); - // data->FlushBits(); - // data->WriteString(Data); - // } - - // if (HasPetBattleFullUpdate) - // { - // for (std::size_t i = 0; i < 2; ++i) - // { - // *data << ObjectGuid(Players[i].CharacterID); - // *data << int32(Players[i].TrapAbilityID); - // *data << int32(Players[i].TrapStatus); - // *data << uint16(Players[i].RoundTimeSecs); - // *data << int8(Players[i].FrontPet); - // *data << uint8(Players[i].InputFlags); - - // data->WriteBits(Players[i].Pets.size(), 2); - // data->FlushBits(); - // for (std::size_t j = 0; j < Players[i].Pets.size(); ++j) - // { - // *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID); - // *data << int32(Players[i].Pets[j].SpeciesID); - // *data << int32(Players[i].Pets[j].CreatureID); - // *data << int32(Players[i].Pets[j].DisplayID); - // *data << int16(Players[i].Pets[j].Level); - // *data << int16(Players[i].Pets[j].Xp); - // *data << int32(Players[i].Pets[j].CurHealth); - // *data << int32(Players[i].Pets[j].MaxHealth); - // *data << int32(Players[i].Pets[j].Power); - // *data << int32(Players[i].Pets[j].Speed); - // *data << int32(Players[i].Pets[j].NpcTeamMemberID); - // *data << uint8(Players[i].Pets[j].BreedQuality); - // *data << uint16(Players[i].Pets[j].StatusFlags); - // *data << int8(Players[i].Pets[j].Slot); - - // *data << uint32(Players[i].Pets[j].Abilities.size()); - // *data << uint32(Players[i].Pets[j].Auras.size()); - // *data << uint32(Players[i].Pets[j].States.size()); - // for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k) - // { - // *data << int32(Players[i].Pets[j].Abilities[k].AbilityID); - // *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining); - // *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining); - // *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex); - // *data << uint8(Players[i].Pets[j].Abilities[k].Pboid); - // } - - // for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k) - // { - // *data << int32(Players[i].Pets[j].Auras[k].AbilityID); - // *data << uint32(Players[i].Pets[j].Auras[k].InstanceID); - // *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining); - // *data << int32(Players[i].Pets[j].Auras[k].CurrentRound); - // *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID); - // } - - // for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k) - // { - // *data << uint32(Players[i].Pets[j].States[k].StateID); - // *data << int32(Players[i].Pets[j].States[k].StateValue); - // } - - // data->WriteBits(Players[i].Pets[j].CustomName.length(), 7); - // data->FlushBits(); - // data->WriteString(Players[i].Pets[j].CustomName); - // } - // } - - // for (std::size_t i = 0; i < 3; ++i) - // { - // *data << uint32(Enviros[j].Auras.size()); - // *data << uint32(Enviros[j].States.size()); - // for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j) - // { - // *data << int32(Enviros[j].Auras[j].AbilityID); - // *data << uint32(Enviros[j].Auras[j].InstanceID); - // *data << int32(Enviros[j].Auras[j].RoundsRemaining); - // *data << int32(Enviros[j].Auras[j].CurrentRound); - // *data << uint8(Enviros[j].Auras[j].CasterPBOID); - // } - - // for (std::size_t j = 0; j < Enviros[j].States.size(); ++j) - // { - // *data << uint32(Enviros[i].States[j].StateID); - // *data << int32(Enviros[i].States[j].StateValue); - // } - // } - - // *data << uint16(WaitingForFrontPetsMaxSecs); - // *data << uint16(PvpMaxRoundTime); - // *data << int32(CurRound); - // *data << uint32(NpcCreatureID); - // *data << uint32(NpcDisplayID); - // *data << int8(CurPetBattleState); - // *data << uint8(ForfeitPenalty); - // *data << ObjectGuid(InitialWildPetGUID); - // data->WriteBit(IsPVP); - // data->WriteBit(CanAwardXP); - // data->FlushBits(); - // } - } - - if (flags.ActivePlayer) - { - Player const* player = static_cast<Player const*>(this); - - bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty(); - bool HasRuneState = player->GetPowerIndex(POWER_RUNES) != MAX_POWERS; - - data->WriteBit(HasSceneInstanceIDs); - data->WriteBit(HasRuneState); - data->FlushBits(); - if (HasSceneInstanceIDs) - { - *data << uint32(player->GetSceneMgr().GetSceneTemplateByInstanceMap().size()); - for (auto const& [sceneInstanceId, _] : player->GetSceneMgr().GetSceneTemplateByInstanceMap()) - *data << uint32(sceneInstanceId); - } - if (HasRuneState) - { - float baseCd = float(player->GetRuneBaseCooldown()); - uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES)); - - *data << uint8((1 << maxRunes) - 1); - *data << uint8(player->GetRunesState()); - *data << uint32(maxRunes); - for (uint32 i = 0; i < maxRunes; ++i) - *data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); - } - } - - if (flags.Conversation) - { - Conversation const* self = static_cast<Conversation const*>(this); - if (data->WriteBit(self->GetTextureKitId() != 0)) - *data << uint32(self->GetTextureKitId()); - - data->FlushBits(); - } -} - -UF::UpdateFieldFlag Object::GetUpdateFieldFlagsFor(Player const* /*target*/) const -{ - return UF::UpdateFieldFlag::None; -} - void Object::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag /*flags*/, Player const* /*target*/) const { *data << uint32(0); } -void Object::AddToObjectUpdateIfNeeded() -{ - if (m_inWorld && !m_objectUpdated) - m_objectUpdated = AddToObjectUpdate(); -} - void Object::ClearUpdateMask(bool remove) { m_values.ClearChangesMask(&Object::m_objectData); - m_entityFragments.IdsChanged = false; - - if (m_objectUpdated) - { - if (remove) - RemoveFromObjectUpdate(); - m_objectUpdated = false; - } -} - -void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const -{ - UpdateDataMapType::iterator iter = data_map.try_emplace(player, player->GetMapId()).first; - BuildValuesUpdateBlockForPlayer(&iter->second, iter->first); + BaseEntity::ClearUpdateMask(remove); } std::string Object::GetDebugInfo() const { std::stringstream sstr; - sstr << GetGUID().ToString() + " Entry " << GetEntry(); - return sstr.str(); + sstr << BaseEntity::GetDebugInfo() << " Entry " << GetEntry(); + return std::move(sstr).str(); } void MovementInfo::OutDebug() diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 6f484f765c4..f49614657f6 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -18,6 +18,7 @@ #ifndef _OBJECT_H #define _OBJECT_H +#include "BaseEntity.h" #include "Common.h" #include "Duration.h" #include "Errors.h" @@ -26,17 +27,13 @@ #include "ModelIgnoreFlags.h" #include "MovementInfo.h" #include "ObjectDefines.h" -#include "ObjectGuid.h" #include "Optional.h" #include "PhaseShift.h" #include "Position.h" #include "SharedDefines.h" #include "SpellDefines.h" #include "UniqueTrackablePtr.h" -#include "UpdateFields.h" -#include "WowCSEntityDefinitions.h" #include <list> -#include <unordered_map> class AreaTrigger; class Conversation; @@ -78,132 +75,17 @@ namespace WorldPackets } } -typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType; - -struct CreateObjectBits -{ - bool NoBirthAnim : 1; - bool EnablePortals : 1; - bool PlayHoverAnim : 1; - bool MovementUpdate : 1; - bool MovementTransport : 1; - bool Stationary : 1; - bool CombatVictim : 1; - bool ServerTime : 1; - bool Vehicle : 1; - bool AnimKit : 1; - bool Rotation : 1; - bool GameObject : 1; - bool SmoothPhasing : 1; - bool ThisIsYou : 1; - bool SceneObject : 1; - bool ActivePlayer : 1; - bool Conversation : 1; - bool Room : 1; - bool Decor : 1; - bool MeshObject : 1; - - void Clear() - { - memset(this, 0, sizeof(CreateObjectBits)); - } -}; - -namespace UF -{ - class UpdateFieldHolder - { - public: - template<typename Derived, typename T, int32 BlockBit, uint32 Bit> - 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; } - - inline Object* GetOwner(); - - private: - friend Object; - - // This class is tightly tied to Object::m_values member, do not construct elsewhere - UpdateFieldHolder() : _changesMask(0) { } - - uint32 _changesMask; - }; - - template<typename T> - inline bool SetUpdateFieldValue(UpdateFieldPrivateSetter<T>& setter, typename UpdateFieldPrivateSetter<T>::value_type&& value) - { - return setter.SetValue(std::move(value)); - } - - template<typename T> - inline typename DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter) - { - return setter.AddValue(); - } - - template<typename T> - inline typename DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) - { - return setter.InsertValue(index); - } - - template<typename T> - inline void RemoveDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) - { - setter.RemoveValue(index); - } - - template<typename T> - inline void ClearDynamicUpdateFieldValues(DynamicUpdateFieldSetter<T>& setter) - { - setter.Clear(); - } - - template<typename K, typename V> - inline void RemoveMapUpdateFieldValue(MapUpdateFieldSetter<K, V>& setter, std::type_identity_t<K> const& key) - { - setter.RemoveKey(key); - } - - template<typename T> - inline void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter<T>& setter) - { - setter.RemoveValue(); - } -} - float const DEFAULT_COLLISION_HEIGHT = 2.03128f; // Most common value in dbc static constexpr Milliseconds const HEARTBEAT_INTERVAL = 5s + 200ms; -class TC_GAME_API Object +class TC_GAME_API Object : public BaseEntity { - ObjectGuid m_guid; - public: virtual ~Object(); - bool IsInWorld() const { return m_inWorld; } - - virtual void AddToWorld(); - virtual void RemoveFromWorld(); + void AddToWorld() override; + void RemoveFromWorld() override; - static ObjectGuid GetGUID(Object const* o) { return o ? o->GetGUID() : ObjectGuid::Empty; } - ObjectGuid const& GetGUID() const { return m_guid; } uint32 GetEntry() const { return m_objectData->EntryID; } void SetEntry(uint32 entry) { SetUpdateFieldValue(m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::EntryID), entry); } @@ -216,110 +98,71 @@ class TC_GAME_API Object void RemoveDynamicFlag(uint32 flag) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags), flag); } void ReplaceAllDynamicFlags(uint32 flag) { SetUpdateFieldValue(m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags), flag); } - TypeID GetTypeId() const { return m_objectTypeId; } - bool isType(TypeMask mask) const { return (ObjectTypeMask[m_objectTypeId] & mask) != 0; } - - virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const; - void SendUpdateToPlayer(Player* player); - - void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const; void BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::UpdateFieldFlag flags, Player const* target) const; - void BuildDestroyUpdateBlock(UpdateData* data) const; - void BuildOutOfRangeUpdateBlock(UpdateData* data) const; - ByteBuffer& PrepareValuesUpdateBuffer(UpdateData* data) const; - - virtual void DestroyForPlayer(Player const* target) const; - void SendOutOfRangeForPlayer(Player const* target) const; - virtual void ClearUpdateMask(bool remove); - - virtual std::string GetNameForLocaleIdx(LocaleConstant locale) const = 0; + void ClearUpdateMask(bool remove) override; virtual bool hasQuest(uint32 /* quest_id */) const { return false; } virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; } - void SetIsNewObject(bool enable) { m_isNewObject = enable; } - bool IsDestroyedObject() const { return m_isDestroyedObject; } - void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; } - virtual void BuildUpdate(UpdateDataMapType&) { } - void BuildFieldsUpdate(Player*, UpdateDataMapType &) const; - inline bool IsWorldObject() const { return isType(TYPEMASK_WORLDOBJECT); } static WorldObject* ToWorldObject(Object* o) { return o ? o->ToWorldObject() : nullptr; } static WorldObject const* ToWorldObject(Object const* o) { return o ? o->ToWorldObject() : nullptr; } WorldObject* ToWorldObject() { if (IsWorldObject()) return reinterpret_cast<WorldObject*>(this); else return nullptr; } WorldObject const* ToWorldObject() const { if (IsWorldObject()) return reinterpret_cast<WorldObject const*>(this); else return nullptr; } - inline bool IsItem() const { return isType(TYPEMASK_ITEM); } static Item* ToItem(Object* o) { return o ? o->ToItem() : nullptr; } static Item const* ToItem(Object const* o) { return o ? o->ToItem() : nullptr; } Item* ToItem() { if (IsItem()) return reinterpret_cast<Item*>(this); else return nullptr; } Item const* ToItem() const { if (IsItem()) return reinterpret_cast<Item const*>(this); else return nullptr; } - inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; } static Player* ToPlayer(Object* o) { return o ? o->ToPlayer() : nullptr; } static Player const* ToPlayer(Object const* o) { return o ? o->ToPlayer() : nullptr; } Player* ToPlayer() { if (IsPlayer()) return reinterpret_cast<Player*>(this); else return nullptr; } Player const* ToPlayer() const { if (IsPlayer()) return reinterpret_cast<Player const*>(this); else return nullptr; } - inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; } static Creature* ToCreature(Object* o) { return o ? o->ToCreature() : nullptr; } static Creature const* ToCreature(Object const* o) { return o ? o->ToCreature() : nullptr; } Creature* ToCreature() { if (IsCreature()) return reinterpret_cast<Creature*>(this); else return nullptr; } Creature const* ToCreature() const { if (IsCreature()) return reinterpret_cast<Creature const*>(this); else return nullptr; } - inline bool IsUnit() const { return isType(TYPEMASK_UNIT); } static Unit* ToUnit(Object* o) { return o ? o->ToUnit() : nullptr; } static Unit const* ToUnit(Object const* o) { return o ? o->ToUnit() : nullptr; } Unit* ToUnit() { if (IsUnit()) return reinterpret_cast<Unit*>(this); else return nullptr; } Unit const* ToUnit() const { if (IsUnit()) return reinterpret_cast<Unit const*>(this); else return nullptr; } - inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; } static GameObject* ToGameObject(Object* o) { return o ? o->ToGameObject() : nullptr; } static GameObject const* ToGameObject(Object const* o) { return o ? o->ToGameObject() : nullptr; } GameObject* ToGameObject() { if (IsGameObject()) return reinterpret_cast<GameObject*>(this); else return nullptr; } GameObject const* ToGameObject() const { if (IsGameObject()) return reinterpret_cast<GameObject const*>(this); else return nullptr; } - inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; } static Corpse* ToCorpse(Object* o) { return o ? o->ToCorpse() : nullptr; } static Corpse const* ToCorpse(Object const* o) { return o ? o->ToCorpse() : nullptr; } Corpse* ToCorpse() { if (IsCorpse()) return reinterpret_cast<Corpse*>(this); else return nullptr; } Corpse const* ToCorpse() const { if (IsCorpse()) return reinterpret_cast<Corpse const*>(this); else return nullptr; } - inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; } static DynamicObject* ToDynObject(Object* o) { return o ? o->ToDynObject() : nullptr; } static DynamicObject const* ToDynObject(Object const* o) { return o ? o->ToDynObject() : nullptr; } DynamicObject* ToDynObject() { if (IsDynObject()) return reinterpret_cast<DynamicObject*>(this); else return nullptr; } DynamicObject const* ToDynObject() const { if (IsDynObject()) return reinterpret_cast<DynamicObject const*>(this); else return nullptr; } - inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; } static AreaTrigger* ToAreaTrigger(Object* o) { return o ? o->ToAreaTrigger() : nullptr; } static AreaTrigger const* ToAreaTrigger(Object const* o) { return o ? o->ToAreaTrigger() : nullptr; } AreaTrigger* ToAreaTrigger() { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger*>(this); else return nullptr; } AreaTrigger const* ToAreaTrigger() const { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger const*>(this); else return nullptr; } - inline bool IsSceneObject() const { return GetTypeId() == TYPEID_SCENEOBJECT; } static SceneObject* ToSceneObject(Object* o) { return o ? o->ToSceneObject() : nullptr; } static SceneObject const* ToSceneObject(Object const* o) { return o ? o->ToSceneObject() : nullptr; } SceneObject* ToSceneObject() { if (IsSceneObject()) return reinterpret_cast<SceneObject*>(this); else return nullptr; } SceneObject const* ToSceneObject() const { if (IsSceneObject()) return reinterpret_cast<SceneObject const*>(this); else return nullptr; } - inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; } static Conversation* ToConversation(Object* o) { return o ? o->ToConversation() : nullptr; } static Conversation const* ToConversation(Object const* o) { return o ? o->ToConversation() : nullptr; } Conversation* ToConversation() { if (IsConversation()) return reinterpret_cast<Conversation*>(this); else return nullptr; } Conversation const* ToConversation() const { if (IsConversation()) return reinterpret_cast<Conversation const*>(this); else return nullptr; } - friend UF::UpdateFieldHolder; - UF::UpdateFieldHolder m_values; UF::UpdateField<UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT> m_objectData; - template<typename T> - void ForceUpdateFieldChange(UF::UpdateFieldPrivateSetter<T> const& /*setter*/) - { - AddToObjectUpdateIfNeeded(); - } - - virtual std::string GetDebugInfo() const; + std::string GetDebugInfo() const override; Trinity::unique_weak_ptr<Object> GetWeakPtr() const { return m_scriptRef; } @@ -330,242 +173,19 @@ class TC_GAME_API Object protected: Object(); - void _Create(ObjectGuid const& guid); - - template<typename T> - void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) - { - if (UF::SetUpdateFieldValue(setter, std::move(value))) - AddToObjectUpdateIfNeeded(); - } - - template<typename T> - void SetUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) - { - static_assert(std::is_integral<T>::value, "SetUpdateFieldFlagValue must be used with integral types"); - SetUpdateFieldValue(setter, setter.GetValue() | flag); - } - - template<typename T> - void RemoveUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) - { - static_assert(std::is_integral<T>::value, "RemoveUpdateFieldFlagValue must be used with integral types"); - SetUpdateFieldValue(setter, setter.GetValue() & ~flag); - } - - template<typename T> - typename UF::DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter) - { - AddToObjectUpdateIfNeeded(); - return UF::AddDynamicUpdateFieldValue(setter); - } - - template<typename T> - typename UF::DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) - { - AddToObjectUpdateIfNeeded(); - return UF::InsertDynamicUpdateFieldValue(setter, index); - } - - template<typename T> - void RemoveDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveDynamicUpdateFieldValue(setter, index); - } - - template<typename K, typename V> - void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter<K, V> setter, std::type_identity_t<K> const& key) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveMapUpdateFieldValue(setter, key); - } - - template<typename T> - void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter<T> setter) - { - AddToObjectUpdateIfNeeded(); - UF::ClearDynamicUpdateFieldValues(setter); - } - - template<typename T> - void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter<T> setter) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveOptionalUpdateFieldValue(setter); - } - - // stat system helpers - template<typename T> - void SetUpdateFieldStatValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) - { - static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types"); - SetUpdateFieldValue(setter, std::max(value, T(0))); - } - - template<typename T> - void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type mod, bool apply) - { - static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types"); - - T value = setter.GetValue(); - if (apply) - value += mod; - else - value -= mod; - - SetUpdateFieldValue(setter, value); - } - - template<typename T> - void ApplyPercentModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, float percent, bool apply) - { - static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types"); - - T value = setter.GetValue(); - - // don't want to include Util.h here - //ApplyPercentModFloatVar(value, percent, apply); - if (percent == -100.0f) - percent = -99.99f; - value *= (apply ? (100.0f + percent) / 100.0f : 100.0f / (100.0f + percent)); - - SetUpdateFieldValue(setter, value); - } - - template<typename Action> - void DoWithSuppressingObjectUpdates(Action&& action) - { - bool wasUpdatedBeforeAction = m_objectUpdated; - action(); - if (m_objectUpdated && !wasUpdatedBeforeAction) - { - RemoveFromObjectUpdate(); - m_objectUpdated = false; - } - } - - void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const; - virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const; - virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; - virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; - static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments); + void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0; + void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0; void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const; public: virtual void BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const; - protected: - TypeID m_objectTypeId; - CreateObjectBits m_updateFlag; - WowCS::EntityFragmentsHolder m_entityFragments; - - virtual bool AddToObjectUpdate() = 0; - virtual void RemoveFromObjectUpdate() = 0; - void AddToObjectUpdateIfNeeded(); - - bool m_objectUpdated; - private: - bool m_inWorld; - bool m_isNewObject; - bool m_isDestroyedObject; struct NoopObjectDeleter { void operator()(Object*) const { /*noop - not managed*/ } }; Trinity::unique_trackable_ptr<Object> m_scriptRef; - - Object(Object const& right) = delete; - Object(Object&& right) = delete; - Object& operator=(Object const& right) = delete; - Object& operator=(Object&& right) = delete; }; -inline Object* UF::UpdateFieldHolder::GetOwner() -{ -#if TRINITY_COMPILER == TRINITY_COMPILER_GNU -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - - return reinterpret_cast<Object*>(reinterpret_cast<std::byte*>(this) - offsetof(Object, m_values)); - -#if TRINITY_COMPILER == TRINITY_COMPILER_GNU -#pragma GCC diagnostic pop -#endif -} - -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(); - 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(); - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - { - _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); - if (!_changesMask) - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - } - else - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - - (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask(); -} - -template <typename Derived, typename T, int32 BlockBit, uint32 Bit> -inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) -{ - Object* owner = GetOwner(); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - { - _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); - if (!_changesMask) - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - } - else - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - - auto& uf = (static_cast<Derived*>(owner)->*field); - if (uf.has_value()) - uf._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/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index 98cf4c5632a..0ba79357ecb 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -81,7 +81,7 @@ enum TypeMask TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION }; -inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES> ObjectTypeMask = +inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES + 1> ObjectTypeMask = { TYPEMASK_OBJECT, TYPEMASK_OBJECT | TYPEMASK_ITEM, @@ -101,6 +101,7 @@ inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES> ObjectTypeMask = TYPEMASK_OBJECT | TYPEMASK_AI_GROUP, TYPEMASK_OBJECT | TYPEMASK_SCENARIO, TYPEMASK_OBJECT | TYPEMASK_LOOT_OBJECT, + 0, }; enum class HighGuid diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 949743f16ae..669fcb36287 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1683,7 +1683,7 @@ bool Pet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry, uint32 /*p SetMap(map); // TODO: counter should be constructed as (summon_count << 32) | petNumber - Object::_Create(ObjectGuid::Create<HighGuid::Pet>(map->GetId(), Entry, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Pet>(map->GetId(), Entry, guidlow)); m_spawnId = guidlow; m_originalEntry = Entry; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c579bca7400..68013f65654 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -387,7 +387,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac //FIXME: outfitId not used in player creating /// @todo need more checks against packet modifications - Object::_Create(ObjectGuid::Create<HighGuid::Player>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Player>(guidlow)); m_name = createInfo->Name; @@ -17856,7 +17856,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol return false; } - Object::_Create(guid); + _Create(guid); m_name = std::move(fields.name); @@ -24097,7 +24097,7 @@ uint8 Player::GetStartLevel(uint8 race, uint8 playerClass, Optional<int32> chara return startLevel; } -bool Player::HaveAtClient(Object const* u) const +bool Player::HaveAtClient(BaseEntity const* u) const { return u == this || m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end(); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 93ccffcce1b..ead0c144f7f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2631,7 +2631,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> GuidUnorderedSet m_clientGUIDs; GuidUnorderedSet m_visibleTransports; - bool HaveAtClient(Object const* u) const; + bool HaveAtClient(BaseEntity const* u) const; bool IsNeverVisibleFor(WorldObject const* seer, bool allowServersideObjects = false) const override; diff --git a/src/server/game/Entities/SceneObject/SceneObject.cpp b/src/server/game/Entities/SceneObject/SceneObject.cpp index 46875c51b06..3d8a7bca451 100644 --- a/src/server/game/Entities/SceneObject/SceneObject.cpp +++ b/src/server/game/Entities/SceneObject/SceneObject.cpp @@ -117,7 +117,7 @@ bool SceneObject::Create(ObjectGuid::LowType lowGuid, SceneType type, uint32 sce SetPrivateObjectOwner(privateObjectOwner); - Object::_Create(ObjectGuid::Create<HighGuid::SceneObject>(GetMapId(), sceneId, lowGuid)); + _Create(ObjectGuid::Create<HighGuid::SceneObject>(GetMapId(), sceneId, lowGuid)); PhasingHandler::InheritPhaseShift(this, creator); UpdatePositionData(); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index f7ac68ef840..3bca6f5d9cf 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -107,7 +107,7 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, float x, float return false; } - Object::_Create(ObjectGuid::Create<HighGuid::Transport>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Transport>(guidlow)); GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); if (!goinfo) |
