fix(Core/Movement): Add force speed ack to async movement and resolve stutter (#23371)

This commit is contained in:
killerwife
2025-10-24 23:48:58 +02:00
committed by GitHub
parent 77a1b45fc7
commit d58046032b
13 changed files with 173 additions and 263 deletions

View File

@@ -1999,7 +1999,7 @@ void Creature::setDeathState(DeathState state, bool despawn)
bool needsFalling = !despawn && (IsFlying() || IsHovering()) && !IsUnderWater();
SetHover(false);
SetDisableGravity(false, false, false);
SetDisableGravity(false);
if (needsFalling)
GetMotionMaster()->MoveFall(0, true);
@@ -3230,47 +3230,6 @@ bool Creature::SetWalk(bool enable)
return true;
}
/**
* @brief Enable or disable the creature's fly mode by adding or removing: MOVEMENTFLAG_FLYING. Infom also the client
*/
bool Creature::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/)
{
//! It's possible only a packet is sent but moveflags are not updated
//! Need more research on this
if (!packetOnly && !Unit::SetDisableGravity(disable))
return false;
if (m_movedByPlayer)
{
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
data << GetPackGUID();
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();
BuildMovementPacket(&data);
m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
return true;
}
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(disable ? SMSG_SPLINE_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE, 9);
data << GetPackGUID();
SendMessageToSet(&data, false);
return true;
}
bool Creature::SetSwim(bool enable)
{
if (!Unit::SetSwim(enable))
@@ -3385,19 +3344,23 @@ void Creature::UpdateMovementFlags()
if (GetMovementTemplate().IsFlightAllowed() && isInAir && !IsFalling())
{
if (GetMovementTemplate().Flight == CreatureFlightMovementType::CanFly)
if (GetMovementTemplate().Flight == CreatureFlightMovementType::CanFly && !m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY))
SetCanFly(true);
else
else if (!IsLevitating())
SetDisableGravity(true);
if (!HasHoverAura())
if (!HasHoverAura() && IsHovering())
SetHover(false);
}
else
{
SetCanFly(false);
SetDisableGravity(false);
if (IsAlive() && (CanHover() || HasHoverAura()))
if (m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY))
SetCanFly(false);
if (IsLevitating())
SetDisableGravity(false);
if (IsAlive() && (GetMovementTemplate().Ground == CreatureGroundMovementType::Hover || HasHoverAura()) && !IsHovering())
SetHover(true);
}

View File

@@ -83,7 +83,7 @@ public:
[[nodiscard]] bool CanWalk() const { return GetMovementTemplate().IsGroundAllowed(); }
[[nodiscard]] bool CanSwim() const override;
[[nodiscard]] bool CanEnterWater() const override;
[[nodiscard]] bool CanFly() const override { return GetMovementTemplate().IsFlightAllowed() || IsFlying(); }
[[nodiscard]] bool CanFly() const override { return GetMovementTemplate().IsFlightAllowed() || IsFlying(); }
[[nodiscard]] bool CanHover() const { return GetMovementTemplate().Ground == CreatureGroundMovementType::Hover || IsHovering(); }
[[nodiscard]] bool IsRooted() const { return GetMovementTemplate().IsRooted(); }
@@ -145,7 +145,6 @@ public:
[[nodiscard]] CreatureAI* AI() const { return (CreatureAI*)i_AI; }
bool SetWalk(bool enable) override;
bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true) override;
bool SetSwim(bool enable) override;
bool HasSpellFocus(Spell const* focusSpell = nullptr) const;

View File

@@ -11704,7 +11704,7 @@ void Player::SendInitialPacketsAfterAddToMap()
// manual send package (have code in HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true); that must not be re-applied.
if (IsImmobilizedState())
{
auto const counter = GetSession()->GetOrderCounter();
uint32 const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_FORCE_MOVE_ROOT);
setCompoundState << GetPackGUID();
@@ -11714,7 +11714,7 @@ void Player::SendInitialPacketsAfterAddToMap()
if (HasAuraType(SPELL_AURA_FEATHER_FALL))
{
auto const counter = GetSession()->GetOrderCounter();
uint32 const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_MOVE_FEATHER_FALL);
setCompoundState << GetPackGUID();
@@ -11724,7 +11724,7 @@ void Player::SendInitialPacketsAfterAddToMap()
if (HasAuraType(SPELL_AURA_WATER_WALK))
{
auto const counter = GetSession()->GetOrderCounter();
uint32 const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_MOVE_WATER_WALK);
setCompoundState << GetPackGUID();
@@ -11734,7 +11734,7 @@ void Player::SendInitialPacketsAfterAddToMap()
if (HasAuraType(SPELL_AURA_HOVER))
{
auto const counter = GetSession()->GetOrderCounter();
uint32 const counter = GetSession()->GetOrderCounter();
setCompoundState << uint8(2 + GetPackGUID().size() + 4);
setCompoundState << uint16(SMSG_MOVE_SET_HOVER);
setCompoundState << GetPackGUID();
@@ -16034,24 +16034,6 @@ bool Player::IsInWhisperWhiteList(ObjectGuid guid)
return false;
}
bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool /*updateAnimationTier = true*/)
{
if (!packetOnly && !Unit::SetDisableGravity(disable))
return false;
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
data << GetPackGUID();
data << GetSession()->GetOrderCounter(); // movement counter
SendDirectMessage(&data);
GetSession()->IncrementOrderCounter();
data.Initialize(MSG_MOVE_GRAVITY_CHNG, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
SendMessageToSet(&data, false);
return true;
}
Guild* Player::GetGuild() const
{
uint32 guildId = GetGuildId();

View File

@@ -2566,8 +2566,6 @@ public:
bool IsInWhisperWhiteList(ObjectGuid guid);
void RemoveFromWhisperWhiteList(ObjectGuid guid) { WhisperList.remove(guid); }
bool SetDisableGravity(bool disable, 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); }

View File

@@ -14523,116 +14523,55 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
propagateSpeedChange();
WorldPacket data;
if (!forced)
{
switch (mtype)
{
case MOVE_WALK:
data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_RUN:
data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_RUN_BACK:
data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_SWIM:
data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_SWIM_BACK:
data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_TURN_RATE:
data.Initialize(MSG_MOVE_SET_TURN_RATE, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_FLIGHT:
data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_FLIGHT_BACK:
data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
case MOVE_PITCH_RATE:
data.Initialize(MSG_MOVE_SET_PITCH_RATE, 8 + 4 + 2 + 4 + 4 + 4 + 4 + 4 + 4 + 4);
break;
default:
LOG_ERROR("entities.unit", "Unit::SetSpeed: Unsupported move type ({}), data not sent to client.", mtype);
return;
}
SpeedOpcodePair const& speedOpcodes = SetSpeed2Opc_table[mtype];
if (forced && IsClientControlled())
{
Player* player = const_cast<Player*>(GetClientControlling());
uint32 const counter = player->GetSession()->GetOrderCounter();
// register forced speed changes for WorldSession::HandleForceSpeedChangeAck
// and do it only for real sent packets and use run for run/mounted as client expected
++player->m_forced_speed_changes[mtype];
WorldPacket data(speedOpcodes[static_cast<size_t>(SpeedOpcodeIndex::PC)], 18);
data << GetPackGUID();
data << counter;
if (mtype == MOVE_RUN)
data << uint8(0); // new 2.1.0
data << GetSpeed(mtype);
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
}
else if (forced)
{
WorldPacket data(speedOpcodes[static_cast<size_t>(SpeedOpcodeIndex::NPC)], 12);
data << GetPackGUID();
BuildMovementPacket(&data);
data << float(GetSpeed(mtype));
SendMessageToSet(&data, true);
}
else
if (IsPlayer())
{
if (IsPlayer())
// Xinef: update speed of pet also
if (!IsInCombat())
{
// register forced speed changes for WorldSession::HandleForceSpeedChangeAck
// and do it only for real sent packets and use run for run/mounted as client expected
++ToPlayer()->m_forced_speed_changes[mtype];
Unit* pet = ToPlayer()->GetPet();
if (!pet)
pet = GetCharm();
// Xinef: update speed of pet also
if (!IsInCombat())
{
Unit* pet = ToPlayer()->GetPet();
if (!pet)
pet = GetCharm();
// xinef: do not affect vehicles and possesed pets
if (pet && (pet->HasUnitFlag(UNIT_FLAG_POSSESSED) || pet->IsVehicle()))
pet = nullptr;
// xinef: do not affect vehicles and possesed pets
if (pet && (pet->HasUnitFlag(UNIT_FLAG_POSSESSED) || pet->IsVehicle()))
pet = nullptr;
if (pet && pet->IsCreature() && !pet->IsInCombat() && pet->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE)
pet->UpdateSpeed(mtype, forced);
if (pet && pet->IsCreature() && !pet->IsInCombat() && pet->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE)
pet->UpdateSpeed(mtype, forced);
if (Unit* critter = ObjectAccessor::GetUnit(*this, GetCritterGUID()))
critter->UpdateSpeed(mtype, forced);
}
ToPlayer()->SetCanTeleport(true);
if (Unit* critter = ObjectAccessor::GetUnit(*this, GetCritterGUID()))
critter->UpdateSpeed(mtype, forced);
}
switch (mtype)
{
case MOVE_WALK:
data.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE, 16);
break;
case MOVE_RUN:
data.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE, 17);
break;
case MOVE_RUN_BACK:
data.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, 16);
break;
case MOVE_SWIM:
data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, 16);
break;
case MOVE_SWIM_BACK:
data.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, 16);
break;
case MOVE_TURN_RATE:
data.Initialize(SMSG_FORCE_TURN_RATE_CHANGE, 16);
break;
case MOVE_FLIGHT:
data.Initialize(SMSG_FORCE_FLIGHT_SPEED_CHANGE, 16);
break;
case MOVE_FLIGHT_BACK:
data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16);
break;
case MOVE_PITCH_RATE:
data.Initialize(SMSG_FORCE_PITCH_RATE_CHANGE, 16);
break;
default:
LOG_ERROR("entities.unit", "Unit::SetSpeed: Unsupported move type ({}), data not sent to client.", mtype);
return;
}
data << GetPackGUID();
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();
ToPlayer()->SetCanTeleport(true);
}
}
@@ -18343,7 +18282,7 @@ void Unit::SendMoveRoot(bool apply)
// Wrath+ force root: when unit is controlled by a player
else
{
auto const counter = client->GetSession()->GetOrderCounter();
uint32 const counter = client->GetSession()->GetOrderCounter();
WorldPacket data(apply ? SMSG_FORCE_MOVE_ROOT : SMSG_FORCE_MOVE_UNROOT, guid.size() + 4);
data << guid;
@@ -20381,22 +20320,39 @@ bool Unit::SetWalk(bool enable)
return true;
}
bool Unit::SetDisableGravity(bool disable, bool /*packetOnly = false*/, bool /*updateAnimationTier = true*/)
void Unit::SetDisableGravity(bool enable)
{
if (disable == IsLevitating())
return false;
bool isClientControlled = IsClientControlled();
if (disable)
if (!isClientControlled)
{
AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
}
else
{
RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
if (enable)
m_movementInfo.AddMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
else
m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
}
return true;
if (!IsInWorld()) // is sent on add to map
return;
if (isClientControlled)
{
if (Player const* player = GetClientControlling())
{
uint32 const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, GetPackGUID().size() + 4);
data << GetPackGUID();
data << counter;
player->GetSession()->SendPacket(&data);
player->GetSession()->IncrementOrderCounter();
return;
}
}
WorldPacket data(enable ? SMSG_SPLINE_MOVE_GRAVITY_DISABLE : SMSG_SPLINE_MOVE_GRAVITY_ENABLE, 9);
data << GetPackGUID();
SendMessageToSet(&data, true);
}
bool Unit::SetSwim(bool enable)
@@ -20443,7 +20399,7 @@ void Unit::SetCanFly(bool enable)
{
if (Player const* player = GetClientControlling())
{
auto const counter = player->GetSession()->GetOrderCounter();
uint32 const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, GetPackGUID().size() + 4);
data << GetPackGUID();
@@ -20478,7 +20434,7 @@ void Unit::SetFeatherFall(bool enable)
{
if (Player const* player = GetClientControlling())
{
auto const counter = player->GetSession()->GetOrderCounter();
uint32 const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_FEATHER_FALL : SMSG_MOVE_NORMAL_FALL, GetPackGUID().size() + 4);
@@ -20538,7 +20494,7 @@ void Unit::SetHover(bool enable)
{
WorldPacket data(enable ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, GetPackGUID().size() + 4);
auto const counter = player->GetSession()->GetOrderCounter();
uint32 const counter = player->GetSession()->GetOrderCounter();
data << GetPackGUID();
data << counter;
@@ -20572,7 +20528,7 @@ void Unit::SetWaterWalking(bool enable)
{
if (Player const* player = GetClientControlling())
{
auto const counter = player->GetSession()->GetOrderCounter();
uint32 const counter = player->GetSession()->GetOrderCounter();
WorldPacket data(enable ? SMSG_MOVE_WATER_WALK : SMSG_MOVE_LAND_WALK, GetPackGUID().size() + 4);
data << GetPackGUID();

View File

@@ -624,6 +624,28 @@ typedef std::unordered_map<uint32, uint32> PacketCooldowns;
struct SpellProcEventEntry; // used only privately
enum class SpeedOpcodeIndex : uint32
{
PC,
NPC,
ACK_RESPONSE,
MAX
};
typedef const Opcodes SpeedOpcodePair[static_cast<size_t>(SpeedOpcodeIndex::MAX)];
SpeedOpcodePair SetSpeed2Opc_table[MAX_MOVE_TYPE] =
{
{SMSG_FORCE_WALK_SPEED_CHANGE, SMSG_SPLINE_SET_WALK_SPEED, MSG_MOVE_SET_WALK_SPEED},
{SMSG_FORCE_RUN_SPEED_CHANGE, SMSG_SPLINE_SET_RUN_SPEED, MSG_MOVE_SET_RUN_SPEED},
{SMSG_FORCE_RUN_BACK_SPEED_CHANGE, SMSG_SPLINE_SET_RUN_BACK_SPEED, MSG_MOVE_SET_RUN_BACK_SPEED},
{SMSG_FORCE_SWIM_SPEED_CHANGE, SMSG_SPLINE_SET_SWIM_SPEED, MSG_MOVE_SET_SWIM_SPEED},
{SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, SMSG_SPLINE_SET_SWIM_BACK_SPEED, MSG_MOVE_SET_SWIM_BACK_SPEED},
{SMSG_FORCE_TURN_RATE_CHANGE, SMSG_SPLINE_SET_TURN_RATE, MSG_MOVE_SET_TURN_RATE},
{SMSG_FORCE_FLIGHT_SPEED_CHANGE, SMSG_SPLINE_SET_FLIGHT_SPEED, MSG_MOVE_SET_FLIGHT_SPEED},
{SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, SMSG_SPLINE_SET_FLIGHT_BACK_SPEED, MSG_MOVE_SET_FLIGHT_BACK_SPEED},
{SMSG_FORCE_PITCH_RATE_CHANGE, SMSG_SPLINE_SET_PITCH_RATE, MSG_MOVE_SET_PITCH_RATE},
};
class Unit : public WorldObject
{
public:
@@ -1681,7 +1703,7 @@ public:
void MonsterMoveWithSpeed(float x, float y, float z, float speed); // Not to be used outside of cinematics
virtual bool SetWalk(bool enable);
virtual bool SetDisableGravity(bool disable, bool packetOnly = false, bool updateAnimationTier = true);
void SetDisableGravity(bool disable);
virtual bool SetSwim(bool enable);
void SetCanFly(bool enable);
void SetWaterWalking(bool enable);