physics: block client-side movement when server roots the player

When SMSG_FORCE_MOVE_ROOT sets ROOT in movementInfo.flags, the
camera controller was not aware and continued to accept directional
input. This caused position desync (client moves, server sees player
as rooted).

- Add movementRooted_ flag to CameraController with setter/getter.
- Block nowForward/nowBackward/nowStrafe when movementRooted_ is set.
- Sync isPlayerRooted() from GameHandler to CameraController each
  frame alongside the existing run-speed sync in application.cpp.
- Add GameHandler::isPlayerRooted() convenience accessor.
This commit is contained in:
Kelsi 2026-03-10 13:01:44 -07:00
parent ea291179dd
commit 21604461fc
4 changed files with 12 additions and 2 deletions

View file

@ -1152,6 +1152,9 @@ public:
bool isMounted() const { return currentMountDisplayId_ != 0; }
bool isHostileAttacker(uint64_t guid) const { return hostileAttackers_.count(guid) > 0; }
float getServerRunSpeed() const { return serverRunSpeed_; }
bool isPlayerRooted() const {
return (movementInfo.flags & static_cast<uint32_t>(MovementFlags::ROOT)) != 0;
}
void dismount();
// Taxi / Flight Paths

View file

@ -92,6 +92,8 @@ public:
void setMovementCallback(MovementCallback cb) { movementCallback = std::move(cb); }
void setUseWoWSpeed(bool use) { useWoWSpeed = use; }
void setRunSpeedOverride(float speed) { runSpeedOverride_ = speed; }
void setMovementRooted(bool rooted) { movementRooted_ = rooted; }
bool isMovementRooted() const { return movementRooted_; }
void setMounted(bool m) { mounted_ = m; }
void setMountHeightOffset(float offset) { mountHeightOffset_ = offset; }
void setExternalFollow(bool enabled) { externalFollow_ = enabled; }
@ -268,6 +270,8 @@ private:
// Server-driven run speed override (0 = use default WOW_RUN_SPEED)
float runSpeedOverride_ = 0.0f;
// Server-driven root state: when true, block all horizontal movement input.
bool movementRooted_ = false;
bool mounted_ = false;
float mountHeightOffset_ = 0.0f;
bool externalMoving_ = false;

View file

@ -1009,6 +1009,7 @@ void Application::update(float deltaTime) {
runInGameStage("post-update sync", [&] {
if (renderer && gameHandler && renderer->getCameraController()) {
renderer->getCameraController()->setRunSpeedOverride(gameHandler->getServerRunSpeed());
renderer->getCameraController()->setMovementRooted(gameHandler->isPlayerRooted());
}
bool onTaxi = gameHandler &&

View file

@ -275,8 +275,10 @@ void CameraController::update(float deltaTime) {
if (mouseAutorun) {
autoRunning = false;
}
bool nowForward = keyW || mouseAutorun || autoRunning;
bool nowBackward = keyS;
// When the server has rooted the player, suppress all horizontal movement input.
const bool movBlocked = movementRooted_;
bool nowForward = !movBlocked && (keyW || mouseAutorun || autoRunning);
bool nowBackward = !movBlocked && keyS;
bool nowStrafeLeft = false;
bool nowStrafeRight = false;
bool nowTurnLeft = false;