The Movement States system in Hytale tracks and manages player movement on the server. Unlike keybinds (which are client-only), movement states represent the actions players are performing, not which keys they’re pressing.
graph TD
A[Player Input] -->|Client Processes| B[ClientMovement Packet]
B -->|Network| C[Server Receives Action]
C --> D[MovementStates Updated]
D --> E[Server Logic Processes]
E --> F[Movement Validation]
E --> G[Physics Simulation]
E --> H[Event Dispatch]
| Packet Class | Direction | Purpose |
|---|---|---|
ClientMovement |
Client → Server | Sends player movement input and position updates |
SetMovementStates |
Server → Client | Updates movement state flags for a player |
UpdateMovementSettings |
Server → Client | Modifies movement physics (speed, jump height, etc.) |
MovementStates: Bitfield tracking active movement states (sprinting, crouching, swimming, etc.)SavedMovementStates: Persistent storage of movement states across sessionsMovementSettings: Physics parameters (walk speed, air control, jump force)MovementDirection: Enum for directional movement (FORWARD, BACKWARD, LEFT, RIGHT, UP, DOWN)MovementType: Movement mode classification (WALKING, SWIMMING, FLYING, CLIMBING)The MovementStates class uses bitflags to track multiple simultaneous movement actions:
// Conceptual example based on common ECS patterns
public class MovementStates {
private int stateFlags;
public boolean isSprinting() { return (stateFlags & SPRINT_FLAG) != 0; }
public boolean isCrouching() { return (stateFlags & CROUCH_FLAG) != 0; }
public boolean isJumping() { return (stateFlags & JUMP_FLAG) != 0; }
public boolean isSwimming() { return (stateFlags & SWIM_FLAG) != 0; }
// ... other states
}
@Override
public void setup() {
// Listen to player movement updates
getEventRegistry().register(PlayerMoveEvent.class, event -> {
Player player = event.getPlayer();
// Access movement states (conceptual)
MovementStates states = player.getMovementStates();
if (states.isSprinting()) {
// Player is sprinting
}
if (states.isCrouching()) {
// Player is sneaking
}
});
}
// Example: Slow down players in a specific zone
public void applySlowZone(Player player) {
MovementSettings settings = player.getMovementSettings();
// Create modified settings
MovementSettings slowSettings = settings.withMultiplier(0.5f);
// Apply to player
player.setMovementSettings(slowSettings);
// Server automatically syncs via UpdateMovementSettings packet
}
MovementSettings controls the physical properties of player movement:
walkSpeed: Base movement speed while walkingsprintMultiplier: Speed multiplier when sprintingcrouchMultiplier: Speed multiplier when crouchingjumpForce: Vertical velocity applied on jumpairControl: How much control players have while airbornefriction: Deceleration when not providing inputFrom BlockMovementSettings and FluidFXMovementSettings:
The MovementType enum categorizes how a player is currently moving:
stateDiagram-v2
[*] --> WALKING: On Ground
WALKING --> SPRINTING: Sprint Input
SPRINTING --> WALKING: Release Sprint
WALKING --> JUMPING: Jump Input
JUMPING --> FALLING: Apex Reached
FALLING --> WALKING: Land on Ground
WALKING --> SWIMMING: Enter Water
SWIMMING --> WALKING: Exit Water
SWIMMING --> DIVING: Submerge
DIVING --> SWIMMING: Surface
WALKING --> FLYING: Enable Flight
FLYING --> FALLING: Disable Flight
MovementDirection represents the direction of player input:
The server receives a combination of these directions in ClientMovement packets.
The ClientMovement packet contains:
The server can force movement state changes via:
SetMovementStates: Override specific movement flagsUpdateMovementSettings: Change movement physicsClientTeleport: Force position changeFrom ApplyMovementType and MovementForceRotationType:
The server can apply forces that affect movement:
// Example: Apply knockback
public void applyKnockback(Player player, Vector3 direction, float force) {
AppliedForce knockback = new AppliedForce()
.withType(ApplyMovementType.INSTANT)
.withDirection(direction)
.withMagnitude(force);
player.applyForce(knockback);
}
From MovementEffects:
Movement can trigger environmental effects:
Always validate movement server-side to prevent cheating:
public boolean isMovementValid(Player player, Vector3 newPosition) {
Vector3 oldPosition = player.getPosition();
float distance = oldPosition.distance(newPosition);
float maxDistance = player.getMovementSettings().getMaxDistancePerTick();
if (distance > maxDistance) {
// Potential speed hack, reject movement
player.teleport(oldPosition);
return false;
}
return true;
}
Movement states affect interactions:
Movement affects damage calculations:
AngledDamage)Movement is managed through ECS components:
EntityStorePlayers stuck in movement states:
Movement feels laggy:
Players moving through blocks: