diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 73219a924..90f2b2b03 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15417,10 +15417,12 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ) } } - if (!player) - { + // While feared/confused the client has no control over the unit, + // so a SMSG_MOVE_KNOCK_BACK would be ignored or immediately overridden by the server + // side fleeing/confused splines. Perform the knockback server side instead; the + // fleeing/confused movement generator resumes once this spline is finalized + if (!player || !IsClientControlled()) GetMotionMaster()->MoveKnockbackFrom(x, y, speedXY, speedZ); - } else { float vcos, vsin; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 3bca8bbfc..46140f5a4 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -840,14 +840,20 @@ void WorldSession::HandleMoveKnockBackAck(WorldPacket& recvData) movementInfo.guid = guid; ReadMovementInfo(recvData, &movementInfo); - mover->m_movementInfo = movementInfo; + // Relocate the mover to the acknowledged position. Otherwise the server (and the + // MSG_MOVE_KNOCK_BACK broadcast below) keeps using the pre-knockback position until + // the next regular movement packet arrives, desyncing the unit for nearby clients + if (!ProcessMovementInfo(movementInfo, mover, mover->ToPlayer(), recvData)) + { + recvData.rfinish(); // prevent warnings spam + return; + } if (mover->IsPlayer() && static_cast(mover)->IsFreeFlying()) mover->SetCanFly(true); WorldPacket data(MSG_MOVE_KNOCK_BACK, 66); - data << guid.WriteAsPacked(); - _player->m_mover->BuildMovementPacket(&data); + WriteMovementInfo(&data, &movementInfo); _player->SetCanTeleport(true); // knockback specific info data << movementInfo.jump.sinAngle; diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index aba6e2c7b..96b7f470f 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -620,7 +620,7 @@ void MotionMaster::MoveTakeoff(uint32 id, float x, float y, float z, float speed void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ) { //this function may make players fall below map - if (_owner->IsPlayer()) + if (_owner->IsPlayer() && _owner->IsClientControlled()) return; if (speedXY <= 0.1f) @@ -640,6 +640,15 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa init.SetOrientationFixed(true); init.SetVelocity(speedXY); + // Do not mutate an active fleeing/confused movement generator, + // doing so breaks the movement upon landing from the knockback + MovementGeneratorType slotType = GetMotionSlotType(MOTION_SLOT_CONTROLLED); + if (slotType == FLEEING_MOTION_TYPE || slotType == CONFUSED_MOTION_TYPE) + { + init.Launch(); + return; + } + Mutate(new EffectMovementGenerator(init, 0), MOTION_SLOT_CONTROLLED); }