fix(Core/Movement): Implement order counters (#23015)

This commit is contained in:
killerwife
2025-10-04 02:52:38 +02:00
committed by GitHub
parent 7015f51971
commit b80353d288
12 changed files with 546 additions and 500 deletions

View File

@@ -1998,7 +1998,7 @@ void Creature::setDeathState(DeathState state, bool despawn)
m_formation->FormationReset(true, false);
bool needsFalling = !despawn && (IsFlying() || IsHovering()) && !IsUnderWater();
SetHover(false, false, false);
SetHover(false);
SetDisableGravity(false, false, false);
if (needsFalling)
@@ -3244,8 +3244,9 @@ bool Creature::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool
{
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
data << m_movedByPlayer->ToPlayer()->GetSession()->GetOrderCounter(); // movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
m_movedByPlayer->ToPlayer()->GetSession()->IncrementOrderCounter();
data.Initialize(MSG_MOVE_GRAVITY_CHNG, 64);
data << GetPackGUID();
@@ -3318,107 +3319,6 @@ void Creature::RefreshSwimmingFlag(bool recheck)
SetUnitFlag(UNIT_FLAG_SWIMMING);
}
bool Creature::SetCanFly(bool enable, bool /*packetOnly*/ /* = false */)
{
if (!Unit::SetCanFly(enable))
return false;
if (m_movedByPlayer)
{
sScriptMgr->AnticheatSetCanFlybyServer(m_movedByPlayer->ToPlayer(), enable);
if (!enable)
m_movedByPlayer->ToPlayer()->SetFallInformation(GameTime::GetGameTime().count(), m_movedByPlayer->ToPlayer()->GetPositionZ());
WorldPacket data(enable ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
return true;
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9);
data << GetPackGUID();
SendMessageToSet(&data, false);
return true;
}
bool Creature::SetWaterWalking(bool enable, bool packetOnly /* = false */)
{
if (!packetOnly && !Unit::SetWaterWalking(enable))
return false;
if (m_movedByPlayer)
{
WorldPacket data(enable ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
data.Initialize(MSG_MOVE_WATER_WALK, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
return true;
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9);
data << GetPackGUID();
SendMessageToSet(&data, true);
return true;
}
bool Creature::SetFeatherFall(bool enable, bool packetOnly /* = false */)
{
if (!packetOnly && !Unit::SetFeatherFall(enable))
return false;
if (m_movedByPlayer)
{
WorldPacket data(enable ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
data.Initialize(MSG_MOVE_FEATHER_FALL, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
return true;
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL, 9);
data << GetPackGUID();
SendMessageToSet(&data, true);
return true;
}
bool Creature::SetHover(bool enable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/)
{
if (!packetOnly && !Unit::SetHover(enable))
return false;
if (updateAnimationTier && IsAlive() && !HasUnitState(UNIT_STATE_ROOT) && !IsRooted())
{
if (IsLevitating())
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
else if (IsHovering())
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER);
else
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9);
data << GetPackGUID();
SendMessageToSet(&data, false);
return true;
}
float Creature::GetAggroRange(Unit const* target) const
{
// Determines the aggro range for creatures

View File

@@ -143,10 +143,6 @@ public:
bool SetWalk(bool enable) override;
bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true) override;
bool SetSwim(bool enable) override;
bool SetCanFly(bool enable, bool packetOnly = false) override;
bool SetWaterWalking(bool enable, bool packetOnly = false) override;
bool SetFeatherFall(bool enable, bool packetOnly = false) override;
bool SetHover(bool enable, bool packetOnly = false, bool updateAnimationTier = true) override;
bool HasSpellFocus(Spell const* focusSpell = nullptr) const;
struct

View File

@@ -1337,9 +1337,10 @@ void Player::SendTeleportAckPacket()
{
WorldPacket data(MSG_MOVE_TELEPORT_ACK, 41);
data << GetPackGUID();
data << uint32(0); // this value increments every time
data << GetSession()->GetOrderCounter(); // movement counter
BuildMovementPacket(&data);
GetSession()->SendPacket(&data);
GetSession()->IncrementOrderCounter();
}
bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options /*= 0*/, Unit* target /*= nullptr*/, bool newInstance /*= false*/)
@@ -4425,27 +4426,29 @@ void Player::DeleteOldRecoveryItems(uint32 keepDays)
void Player::SetMovement(PlayerMovementType pType)
{
WorldPacket data;
const PackedGuid& guid = GetPackGUID();
switch (pType)
{
case MOVE_ROOT:
data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size() + 4);
data.Initialize(SMSG_FORCE_MOVE_ROOT, guid.size() + 4);
break;
case MOVE_UNROOT:
data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size() + 4);
data.Initialize(SMSG_FORCE_MOVE_UNROOT, guid.size() + 4);
break;
case MOVE_WATER_WALK:
data.Initialize(SMSG_MOVE_WATER_WALK, GetPackGUID().size() + 4);
data.Initialize(SMSG_MOVE_WATER_WALK, guid.size() + 4);
break;
case MOVE_LAND_WALK:
data.Initialize(SMSG_MOVE_LAND_WALK, GetPackGUID().size() + 4);
data.Initialize(SMSG_MOVE_LAND_WALK, guid.size() + 4);
break;
default:
LOG_ERROR("entities.player", "Player::SetMovement: Unsupported move type ({}), data not sent to client.", pType);
return;
}
data << GetPackGUID();
data << uint32(0);
data << guid;
data << GetSession()->GetOrderCounter(); // movement counter
GetSession()->SendPacket(&data);
GetSession()->IncrementOrderCounter();
}
/* Preconditions:
@@ -11701,13 +11704,56 @@ void Player::SendInitialPacketsAfterAddToMap()
if (HasStunAura())
SetMovement(MOVE_ROOT);
WorldPacket setCompoundState(SMSG_MULTIPLE_MOVES, 100);
setCompoundState << uint32(0); // size placeholder
// manual send package (have code in HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true); that must not be re-applied.
if (HasRootAura())
if (IsImmobilizedState())
{
WorldPacket data2(SMSG_FORCE_MOVE_ROOT, 10);
data2 << GetPackGUID();
data2 << (uint32)2;
SendMessageToSet(&data2, true);
auto const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_FORCE_MOVE_ROOT);
setCompoundState << GetPackGUID();
setCompoundState << uint32(counter);
GetSession()->IncrementOrderCounter();
}
if (HasAuraType(SPELL_AURA_FEATHER_FALL))
{
auto const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_MOVE_FEATHER_FALL);
setCompoundState << GetPackGUID();
setCompoundState << uint32(counter);
GetSession()->IncrementOrderCounter();
}
if (HasAuraType(SPELL_AURA_WATER_WALK))
{
auto const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_MOVE_WATER_WALK);
setCompoundState << GetPackGUID();
setCompoundState << uint32(counter);
GetSession()->IncrementOrderCounter();
}
if (HasAuraType(SPELL_AURA_HOVER))
{
auto const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_MOVE_SET_HOVER);
setCompoundState << GetPackGUID();
setCompoundState << uint32(counter);
GetSession()->IncrementOrderCounter();
}
// TODO: Pending mount protocol
if (setCompoundState.size() > 4)
{
setCompoundState.put<uint32>(0, setCompoundState.size() - 4);
SendDirectMessage(&setCompoundState);
}
SendEnchantmentDurations(); // must be after add to map
@@ -15932,8 +15978,9 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool /
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
data << GetSession()->GetOrderCounter(); // movement counter
SendDirectMessage(&data);
GetSession()->IncrementOrderCounter();
data.Initialize(MSG_MOVE_GRAVITY_CHNG, 64);
data << GetPackGUID();
@@ -15942,91 +15989,6 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool /
return true;
}
bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
{
sScriptMgr->AnticheatSetCanFlybyServer(this, apply);
if (!packetOnly && !Unit::SetCanFly(apply))
return false;
if (!apply)
SetFallInformation(GameTime::GetGameTime().count(), GetPositionZ());
WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
SendDirectMessage(&data);
data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
SendMessageToSet(&data, false);
return true;
}
bool Player::SetHover(bool apply, bool packetOnly /*= false*/, bool /*updateAnimationTier = true*/)
{
// moved inside, flag can be removed on landing and wont send appropriate packet to client when aura is removed
if (!packetOnly /* && !Unit::SetHover(apply)*/)
{
Unit::SetHover(apply);
// return false;
}
WorldPacket data(apply ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
SendDirectMessage(&data);
data.Initialize(MSG_MOVE_HOVER, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
SendMessageToSet(&data, false);
return true;
}
bool Player::SetWaterWalking(bool apply, bool packetOnly /*= false*/)
{
// moved inside, flag can be removed on landing and wont send appropriate packet to client when aura is removed
if (!packetOnly /* && !Unit::SetWaterWalking(apply)*/)
{
Unit::SetWaterWalking(apply);
// return false;
}
WorldPacket data(apply ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
SendDirectMessage(&data);
data.Initialize(MSG_MOVE_WATER_WALK, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
SendMessageToSet(&data, false);
return true;
}
bool Player::SetFeatherFall(bool apply, bool packetOnly /*= false*/)
{
// Xinef: moved inside, flag can be removed on landing and wont send appropriate packet to client when aura is removed
if (!packetOnly/* && !Unit::SetFeatherFall(apply)*/)
{
Unit::SetFeatherFall(apply);
//return false;
}
WorldPacket data(apply ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
SendDirectMessage(&data);
data.Initialize(MSG_MOVE_FEATHER_FALL, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
SendMessageToSet(&data, false);
return true;
}
Guild* Player::GetGuild() const
{
uint32 guildId = GetGuildId();

View File

@@ -2564,13 +2564,10 @@ public:
void RemoveFromWhisperWhiteList(ObjectGuid guid) { WhisperList.remove(guid); }
bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true) override;
bool SetCanFly(bool apply, bool packetOnly = false) override;
bool SetWaterWalking(bool apply, bool packetOnly = false) override;
bool SetFeatherFall(bool apply, bool packetOnly = false) override;
bool SetHover(bool enable, bool packetOnly = false, bool updateAnimationTier = true) override;
[[nodiscard]] bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
[[nodiscard]] bool CanEnterWater() const override { return true; }
bool IsFreeFlying() const { return HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || HasAuraType(SPELL_AURA_FLY); }
// saving
void AdditionalSavingAddMask(uint8 mask) { m_additionalSaveTimer = 2000; m_additionalSaveMask |= mask; }

View File

@@ -244,8 +244,6 @@ Unit::Unit() : WorldObject(),
_dualWieldMode = DualWieldMode::AUTO;
m_rootTimes = 0;
m_state = 0;
m_deathState = DeathState::Alive;
@@ -4332,10 +4330,10 @@ void Unit::ProcessTerrainStatusUpdate()
LiquidData const& liquidData = GetLiquidData();
// remove appropriate auras if we are swimming/not swimming respectively
if (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING)
// remove appropriate auras if we are swimming/not swimming respectively - exact mirror of client logic
if (liquidData.Status & MAP_LIQUID_STATUS_SWIMMING && (liquidData.Level - GetPositionZ()) > GetCollisionHeight() * 0.75f) // Shallow water at ~75% of collision height)
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER);
else if (!isSwimming())
else
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
// liquid aura handling
@@ -13607,9 +13605,10 @@ void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry)
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
data << GetPackGUID();
data << uint32(GameTime::GetGameTime().count()); // Packet counter
data << player->GetSession()->GetOrderCounter(); // movement counter
data << player->GetCollisionHeight();
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
}
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNT);
@@ -13623,13 +13622,14 @@ void Unit::Dismount()
SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
RemoveUnitFlag(UNIT_FLAG_MOUNT);
if (Player* thisPlayer = ToPlayer())
if (Player* player = ToPlayer())
{
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
data << GetPackGUID();
data << uint32(GameTime::GetGameTime().count()); // Packet counter
data << thisPlayer->GetCollisionHeight();
thisPlayer->GetSession()->SendPacket(&data);
data << player->GetSession()->GetOrderCounter(); // movement counter
data << player->GetCollisionHeight();
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
}
WorldPacket data(SMSG_DISMOUNT, 8);
@@ -14643,11 +14643,13 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
return;
}
data << GetPackGUID();
data << (uint32)0; // moveEvent, NUM_PMOVE_EVTS = 0x39
data << (IsPlayer() ? ToPlayer()->GetSession()->GetOrderCounter() : uint32(0)); // movement counter
if (mtype == MOVE_RUN)
data << uint8(0); // new 2.1.0
data << float(GetSpeed(mtype));
SendMessageToSet(&data, true);
if (IsPlayer()) // TODO: Resolve this mess
ToPlayer()->GetSession()->IncrementOrderCounter();
}
}
@@ -16806,6 +16808,15 @@ bool Unit::IsStandState() const
return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL;
}
bool Unit::IsStandUpOnMovementState() const
{
uint8 s = getStandState();
return
s == UNIT_STAND_STATE_SIT_CHAIR || s == UNIT_STAND_STATE_SIT_LOW_CHAIR ||
s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR ||
s == UNIT_STAND_STATE_SIT || s == UNIT_STAND_STATE_SLEEP;
}
void Unit::SetStandState(uint8 state)
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, state);
@@ -18302,9 +18313,6 @@ void Unit::SetRooted(bool apply, bool isStun)
{
if (apply)
{
if (m_rootTimes > 0) // blizzard internal check?
m_rootTimes++;
// MOVEMENTFLAG_ROOT cannot be used in conjunction with MOVEMENTFLAG_MASK_MOVING (tested 3.3.5a)
// this will freeze clients. That's why we remove MOVEMENTFLAG_MASK_MOVING before
// setting MOVEMENTFLAG_ROOT
@@ -18336,8 +18344,9 @@ void Unit::SetRooted(bool apply, bool isStun)
{
WorldPacket data(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size() + 4);
data << GetPackGUID();
data << m_rootTimes;
data << m_movedByPlayer->ToPlayer()->GetSession()->GetOrderCounter(); // movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
m_movedByPlayer->ToPlayer()->GetSession()->IncrementOrderCounter();
}
else
{
@@ -18356,8 +18365,9 @@ void Unit::SetRooted(bool apply, bool isStun)
{
WorldPacket data(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size() + 4);
data << GetPackGUID();
data << m_rootTimes;
data << m_movedByPlayer->ToPlayer()->GetSession()->GetOrderCounter(); // movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
m_movedByPlayer->ToPlayer()->GetSession()->IncrementOrderCounter();
}
else
{
@@ -19261,16 +19271,14 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ)
WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8 + 4 + 4 + 4 + 4 + 4));
data << GetPackGUID();
data << uint32(0); // counter
data << player->GetSession()->GetOrderCounter(); // movement counter
data << float(vcos); // x direction
data << float(vsin); // y direction
data << float(speedXY); // Horizontal speed
data << float(-speedZ); // Z Movement speed (vertical)
player->GetSession()->SendPacket(&data);
if (player->HasIncreaseMountedFlightSpeedAura() || player->HasFlyAura())
player->SetCanFly(true, true);
player->GetSession()->IncrementOrderCounter();
player->SetCanKnockback(true);
}
@@ -20464,39 +20472,168 @@ bool Unit::SetSwim(bool enable)
*
* Doesn't inform the client.
*/
bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */)
void Unit::SetCanFly(bool enable)
{
if (enable == HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
return false;
bool isClientControlled = IsClientControlled();
if (enable)
if (!isClientControlled)
{
AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
}
else
{
RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_MASK_MOVING_FLY);
if (enable)
m_movementInfo.AddMovementFlag(MOVEMENTFLAG_CAN_FLY);
else
m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_CAN_FLY);
}
return true;
if (!IsInWorld()) // is sent on add to map
return;
if (isClientControlled)
{
if (Player const* player = GetClientControlling())
{
auto const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, GetPackGUID().size() + 4);
data << GetPackGUID();
data << counter;
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
return;
}
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9);
data << GetPackGUID();
SendMessageToSet(&data, true);
}
/**
* @brief Allow to walk on water. Doesn't inform the client.
* Need to use SendMovementWaterWalking() if it's for players.
*/
bool Unit::SetWaterWalking(bool enable, bool /*packetOnly = false*/)
void Unit::SetFeatherFall(bool enable)
{
if (enable == HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING))
return false;
bool isClientControlled = IsClientControlled();
if (!isClientControlled)
{
if (enable)
m_movementInfo.AddMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
else
m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
}
if (!IsInWorld()) // is sent on add to map
return;
if (isClientControlled)
{
if (Player const* player = GetClientControlling())
{
auto const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, GetPackGUID().size() + 4);
data << GetPackGUID();
data << counter;
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
// start fall from current height
if (!enable)
const_cast<Player*>(player)->SetFallInformation(0, GetPositionZ());
return;
}
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL);
data << GetPackGUID();
SendMessageToSet(&data, true);
}
void Unit::SetHover(bool enable)
{
bool isClientControlled = IsClientControlled();
if (!isClientControlled)
{
if (enable)
m_movementInfo.AddMovementFlag(MOVEMENTFLAG_HOVER);
else
m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_HOVER);
}
float hoverHeight = GetHoverHeight();
if (enable)
AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
{
if (hoverHeight && GetPositionZ() - GetMap()->GetHeight(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ()) < hoverHeight)
Relocate(GetPositionX(), GetPositionY(), GetPositionZ() + hoverHeight);
}
else
RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
{
if (IsAlive() || !IsUnit())
{
float newZ = std::max<float>(GetMap()->GetHeight(GetPhaseMask(), GetPositionX(), GetPositionY(), GetPositionZ()), GetPositionZ() - hoverHeight);
UpdateAllowedPositionZ(GetPositionX(), GetPositionY(), newZ);
Relocate(GetPositionX(), GetPositionY(), newZ);
}
}
return true;
if (!IsInWorld()) // is sent on add to map
return;
if (isClientControlled)
{
if (Player const* player = GetClientControlling())
{
WorldPacket data(enable ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, GetPackGUID().size() + 4);
auto const counter = player->GetSession()->GetOrderCounter();
data << GetPackGUID();
data << counter;
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
return;
}
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9);
data << GetPackGUID();
SendMessageToSet(&data, true);
}
void Unit::SetWaterWalking(bool enable)
{
bool isClientControlled = IsClientControlled();
if (!isClientControlled)
{
if (enable)
m_movementInfo.AddMovementFlag(MOVEMENTFLAG_WATERWALKING);
else
m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_WATERWALKING);
}
if (!IsInWorld()) // is sent on add to map
return;
if (isClientControlled)
{
if (Player const* player = GetClientControlling())
{
auto const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, GetPackGUID().size() + 4);
data << GetPackGUID();
data << counter;
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
return;
}
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_WATER_WALK : SMSG_SPLINE_MOVE_LAND_WALK, 9);
data << GetPackGUID();
SendMessageToSet(&data, true);
}
void Unit::SendMovementWaterWalking(Player* sendTo)
@@ -20508,19 +20645,6 @@ void Unit::SendMovementWaterWalking(Player* sendTo)
sendTo->SendDirectMessage(&data);
}
bool Unit::SetFeatherFall(bool enable, bool /*packetOnly = false*/)
{
if (enable == HasUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW))
return false;
if (enable)
AddUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
else
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW);
return true;
}
void Unit::SendMovementFeatherFall(Player* sendTo)
{
if (!movespline->Initialized())
@@ -20530,34 +20654,6 @@ void Unit::SendMovementFeatherFall(Player* sendTo)
sendTo->SendDirectMessage(&data);
}
bool Unit::SetHover(bool enable, bool /*packetOnly = false*/, bool /*updateAnimationTier = true*/)
{
if (enable == HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
return false;
float hoverHeight = GetFloatValue(UNIT_FIELD_HOVERHEIGHT);
if (enable)
{
AddUnitMovementFlag(MOVEMENTFLAG_HOVER);
if (hoverHeight && GetPositionZ() - GetFloorZ() < hoverHeight)
UpdateHeight(GetPositionZ() + hoverHeight);
}
else
{
RemoveUnitMovementFlag(MOVEMENTFLAG_HOVER);
if (hoverHeight && (!isDying() || !IsCreature()))
{
float newZ = std::max<float>(GetFloorZ(), GetPositionZ() - hoverHeight);
UpdateAllowedPositionZ(GetPositionX(), GetPositionY(), newZ);
UpdateHeight(newZ);
}
SendMovementFlagUpdate(); // pussywizard: needed for falling after death (instead of falling onto air at hover height)
}
return true;
}
void Unit::SendMovementHover(Player* sendTo)
{
if (!movespline->Initialized())
@@ -21203,3 +21299,58 @@ std::string Unit::GetDebugInfo() const
<< " Class: " << std::to_string(getClass());
return sstr.str();
}
bool Unit::IsClientControlled(Player const* exactClient /*= nullptr*/) const
{
// Severvide method to check if unit is client controlled (optionally check for specific client in control)
// Applies only to player controlled units
if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
return false;
// These flags are meant to be used when server controls this unit, client control is taken away
if (HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING)))
return false;
// If unit is possessed, it has lost original control...
if (ObjectGuid const& guid = GetCharmerGUID())
{
// ... but if it is a possessing charm, then we have to check if some other player controls it
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) && guid.IsPlayer())
return (exactClient ? (exactClient->GetGUID() == guid) : true);
return false;
}
// By default: players have client control over themselves
if (IsPlayer())
return (exactClient ? (exactClient == this) : true);
return false;
}
Player const* Unit::GetClientControlling() const
{
// Serverside reverse "mover" deduction logic at controlled unit
// Applies only to player controlled units
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
{
// Charm always removes control from original client...
if (GetCharmerGUID())
{
// ... but if it is a possessing charm, some other client may have control
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED))
{
Unit const* charmer = GetCharmer();
if (charmer && charmer->IsPlayer())
return static_cast<Player const*>(charmer);
}
}
else if (IsPlayer())
{
// Check if anything prevents original client from controlling
if (IsClientControlled(static_cast<Player const*>(this)))
return static_cast<Player const*>(this);
}
}
return nullptr;
}

View File

@@ -744,6 +744,7 @@ public:
void SetExtraUnitMovementFlags(uint16 f) { m_movementInfo.flags2 = f; }
inline bool IsCrowdControlled() const { return HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_STUNNED)); }
inline bool IsImmobilizedState() const { return HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED); }
/*********************************************************/
/*** UNIT TYPES, CLASSES, RACES... ***/
@@ -826,6 +827,11 @@ public:
bool IsValidAssistTarget(Unit const* target) const;
bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const;
// Client controlled: check if unit currently is under client control (has active "mover"), optionally check for specific client (server-side)
bool IsClientControlled(Player const* exactClient = nullptr) const;
// Controlling client: server PoV on which client (player) controls movement of the unit at the moment, obtain "mover" (server-side)
Player const* GetClientControlling() const;
// Combat range
[[nodiscard]] float GetBoundaryRadius() const { return m_floatValues[UNIT_FIELD_BOUNDINGRADIUS]; }
[[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; }
@@ -1676,10 +1682,10 @@ public:
virtual bool SetWalk(bool enable);
virtual bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true);
virtual bool SetSwim(bool enable);
virtual bool SetCanFly(bool enable, bool packetOnly = false);
virtual bool SetWaterWalking(bool enable, bool packetOnly = false);
virtual bool SetFeatherFall(bool enable, bool packetOnly = false);
virtual bool SetHover(bool enable, bool packetOnly = false, bool updateAnimationTier = true);
void SetCanFly(bool enable);
void SetWaterWalking(bool enable);
void SetFeatherFall(bool enable);
void SetHover(bool enable);
MotionMaster* GetMotionMaster() { return i_motionMaster; }
[[nodiscard]] const MotionMaster* GetMotionMaster() const { return i_motionMaster; }
@@ -1706,6 +1712,7 @@ public:
[[nodiscard]] uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
[[nodiscard]] bool IsSitState() const;
[[nodiscard]] bool IsStandState() const;
[[nodiscard]] bool IsStandUpOnMovementState() const;
void SetStandState(uint8 state);
void SetStandFlags(uint8 flags) { SetByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, flags); }
@@ -2129,8 +2136,6 @@ protected:
bool m_applyResilience;
bool _instantCast;
uint32 m_rootTimes;
private:
bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo);
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, ProcEventInfo const& eventInfo);

View File

@@ -412,6 +412,8 @@ enum MovementFlags
MOVEMENTFLAG_MASK_PLAYER_ONLY =
MOVEMENTFLAG_FLYING,
MOVEMENTFLAG_MASK_MOVING_OR_TURN = MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING,
/// Movement flags that have change status opcodes associated for players
MOVEMENTFLAG_MASK_HAS_PLAYER_STATUS_OPCODE = MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_ROOT |
MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_WATERWALKING | MOVEMENTFLAG_FALLING_SLOW | MOVEMENTFLAG_HOVER