diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index b34bc1a9..2607f216 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1686,6 +1686,12 @@ private: uint32_t lastMovementTimestampMs_ = 0; bool serverMovementAllowed_ = true; + // Fall/jump tracking for movement packet correctness. + // fallTime must be the elapsed ms since the FALLING flag was set; the server + // uses it for fall-damage calculations and anti-cheat validation. + bool isFalling_ = false; + uint32_t fallStartMs_ = 0; // movementInfo.time value when FALLING started + // Inventory Inventory inventory; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 76a4a053..046bdb02 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -6113,6 +6113,13 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) { movementClockStart_ = std::chrono::steady_clock::now(); lastMovementTimestampMs_ = 0; movementInfo.time = nextMovementTimestampMs(); + isFalling_ = false; + fallStartMs_ = 0; + movementInfo.fallTime = 0; + movementInfo.jumpVelocity = 0.0f; + movementInfo.jumpSinAngle = 0.0f; + movementInfo.jumpCosAngle = 0.0f; + movementInfo.jumpXYSpeed = 0.0f; resurrectPending_ = false; resurrectRequestPending_ = false; onTaxiFlight_ = false; @@ -7230,6 +7237,21 @@ void GameHandler::sendMovement(Opcode opcode) { break; case Opcode::MSG_MOVE_JUMP: movementInfo.flags |= static_cast(MovementFlags::FALLING); + // Record fall start and capture horizontal velocity for jump fields. + isFalling_ = true; + fallStartMs_ = movementInfo.time; + movementInfo.fallTime = 0; + // jumpVelocity: WoW convention is the upward speed at launch. + movementInfo.jumpVelocity = 7.96f; // WOW_JUMP_VELOCITY from CameraController + { + // Facing direction encodes the horizontal movement direction at launch. + const float facingRad = movementInfo.orientation; + movementInfo.jumpCosAngle = std::cos(facingRad); + movementInfo.jumpSinAngle = std::sin(facingRad); + // Use server run speed as the horizontal speed at jump time. + const bool isWalking = (movementInfo.flags & static_cast(MovementFlags::WALKING)) != 0; + movementInfo.jumpXYSpeed = isWalking ? 2.5f : (serverRunSpeed_ > 0.0f ? serverRunSpeed_ : 7.0f); + } break; case Opcode::MSG_MOVE_START_TURN_LEFT: movementInfo.flags |= static_cast(MovementFlags::TURN_LEFT); @@ -7243,6 +7265,13 @@ void GameHandler::sendMovement(Opcode opcode) { break; case Opcode::MSG_MOVE_FALL_LAND: movementInfo.flags &= ~static_cast(MovementFlags::FALLING); + isFalling_ = false; + fallStartMs_ = 0; + movementInfo.fallTime = 0; + movementInfo.jumpVelocity = 0.0f; + movementInfo.jumpSinAngle = 0.0f; + movementInfo.jumpCosAngle = 0.0f; + movementInfo.jumpXYSpeed = 0.0f; break; case Opcode::MSG_MOVE_HEARTBEAT: // No flag changes — just sends current position @@ -7251,6 +7280,24 @@ void GameHandler::sendMovement(Opcode opcode) { break; } + // Keep fallTime current: it must equal the elapsed milliseconds since FALLING + // was set, so the server can compute fall damage correctly. + if (isFalling_ && movementInfo.hasFlag(MovementFlags::FALLING)) { + // movementInfo.time is the strictly-increasing client clock (ms). + // Subtract fallStartMs_ to get elapsed fall time; clamp to non-negative. + uint32_t elapsed = (movementInfo.time >= fallStartMs_) + ? (movementInfo.time - fallStartMs_) + : 0u; + movementInfo.fallTime = elapsed; + } else if (!movementInfo.hasFlag(MovementFlags::FALLING)) { + // Ensure fallTime is zeroed whenever we're not falling. + if (isFalling_) { + isFalling_ = false; + fallStartMs_ = 0; + } + movementInfo.fallTime = 0; + } + if (onTaxiFlight_ || taxiMountActive_ || taxiActivatePending_ || taxiClientActive_) { sanitizeMovementForTaxi(); }