refactor(S-02/S-05): extract PlayerMovement component + fix thread safety
- Extract 11 movement fields from PlayerBindState into PlayerMovement component - Replace volatile isStruggling/struggleStartTick pair with atomic StruggleSnapshot record - Remove 5+2 misleading synchronized keywords (different monitors, all server-thread-only) - Update all 36 MovementStyleManager field accesses to use getMovement() getters/setters
This commit is contained in:
@@ -129,14 +129,14 @@ public class MovementStyleManager {
|
||||
player.isDeadOrDying() ||
|
||||
state.isStruggling()
|
||||
) {
|
||||
state.lastX = player.getX();
|
||||
state.lastY = player.getY();
|
||||
state.lastZ = player.getZ();
|
||||
state.getMovement().setLastX(player.getX());
|
||||
state.getMovement().setLastY(player.getY());
|
||||
state.getMovement().setLastZ(player.getZ());
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Pending pose restore (crawl deactivated but can't stand) ---
|
||||
if (state.pendingPoseRestore) {
|
||||
if (state.getMovement().isPendingPoseRestore()) {
|
||||
tryRestoreStandingPose(player, state);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public class MovementStyleManager {
|
||||
|
||||
// --- Compare with current active style ---
|
||||
MovementStyle newStyle = resolved.style();
|
||||
MovementStyle oldStyle = state.getActiveMovementStyle();
|
||||
MovementStyle oldStyle = state.getMovement().getActiveMovementStyle();
|
||||
|
||||
if (newStyle != oldStyle) {
|
||||
// Style changed: deactivate old, activate new
|
||||
@@ -161,14 +161,14 @@ public class MovementStyleManager {
|
||||
onDeactivate(player, state, oldStyle);
|
||||
}
|
||||
if (newStyle != null) {
|
||||
state.setResolvedMovementSpeed(resolved.speedMultiplier());
|
||||
state.setResolvedJumpDisabled(resolved.jumpDisabled());
|
||||
state.getMovement().setResolvedMovementSpeed(resolved.speedMultiplier());
|
||||
state.getMovement().setResolvedJumpDisabled(resolved.jumpDisabled());
|
||||
onActivate(player, state, newStyle);
|
||||
} else {
|
||||
state.setResolvedMovementSpeed(1.0f);
|
||||
state.setResolvedJumpDisabled(false);
|
||||
state.getMovement().setResolvedMovementSpeed(1.0f);
|
||||
state.getMovement().setResolvedJumpDisabled(false);
|
||||
}
|
||||
state.setActiveMovementStyle(newStyle);
|
||||
state.getMovement().setActiveMovementStyle(newStyle);
|
||||
|
||||
// Sync to all tracking clients (animation + crawl pose)
|
||||
ModNetwork.sendToAllTrackingAndSelf(
|
||||
@@ -178,13 +178,13 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
// --- Per-style tick ---
|
||||
if (state.getActiveMovementStyle() != null) {
|
||||
if (state.getMovement().getActiveMovementStyle() != null) {
|
||||
// Ladder suspension: skip style tick when on ladder
|
||||
// (ladder movement is controlled by BondageItemRestrictionHandler)
|
||||
if (player.onClimbable()) {
|
||||
state.lastX = player.getX();
|
||||
state.lastY = player.getY();
|
||||
state.lastZ = player.getZ();
|
||||
state.getMovement().setLastX(player.getX());
|
||||
state.getMovement().setLastY(player.getY());
|
||||
state.getMovement().setLastZ(player.getZ());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -192,9 +192,9 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
// Update last position for next tick's movement detection
|
||||
state.lastX = player.getX();
|
||||
state.lastY = player.getY();
|
||||
state.lastZ = player.getZ();
|
||||
state.getMovement().setLastX(player.getX());
|
||||
state.getMovement().setLastY(player.getY());
|
||||
state.getMovement().setLastZ(player.getZ());
|
||||
}
|
||||
|
||||
// ==================== Jump Suppression ====================
|
||||
@@ -216,7 +216,7 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null || !state.isResolvedJumpDisabled()) {
|
||||
if (state == null || !state.getMovement().isResolvedJumpDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
private static void tickStyle(ServerPlayer player, PlayerBindState state) {
|
||||
switch (state.getActiveMovementStyle()) {
|
||||
switch (state.getMovement().getActiveMovementStyle()) {
|
||||
case WADDLE -> tickWaddle(player, state);
|
||||
case SHUFFLE -> tickShuffle(player, state);
|
||||
case HOP -> tickHop(player, state);
|
||||
@@ -292,7 +292,7 @@ public class MovementStyleManager {
|
||||
player,
|
||||
WADDLE_SPEED_UUID,
|
||||
"tiedup.waddle_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ public class MovementStyleManager {
|
||||
player,
|
||||
SHUFFLE_SPEED_UUID,
|
||||
"tiedup.shuffle_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -347,11 +347,11 @@ public class MovementStyleManager {
|
||||
player,
|
||||
HOP_SPEED_UUID,
|
||||
"tiedup.hop_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
state.hopCooldown = 0;
|
||||
state.hopStartupPending = true;
|
||||
state.hopStartupTicks = HOP_STARTUP_DELAY_TICKS;
|
||||
state.getMovement().setHopCooldown(0);
|
||||
state.getMovement().setHopStartupPending(true);
|
||||
state.getMovement().setHopStartupTicks(HOP_STARTUP_DELAY_TICKS);
|
||||
}
|
||||
|
||||
private static void deactivateHop(
|
||||
@@ -359,10 +359,10 @@ public class MovementStyleManager {
|
||||
PlayerBindState state
|
||||
) {
|
||||
removeSpeedModifier(player, HOP_SPEED_UUID);
|
||||
state.hopCooldown = 0;
|
||||
state.hopStartupPending = false;
|
||||
state.hopStartupTicks = 0;
|
||||
state.hopNotMovingTicks = 0;
|
||||
state.getMovement().setHopCooldown(0);
|
||||
state.getMovement().setHopStartupPending(false);
|
||||
state.getMovement().setHopStartupTicks(0);
|
||||
state.getMovement().setHopNotMovingTicks(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,43 +375,44 @@ public class MovementStyleManager {
|
||||
* </ul>
|
||||
*/
|
||||
private static void tickHop(ServerPlayer player, PlayerBindState state) {
|
||||
var mov = state.getMovement();
|
||||
boolean isMoving =
|
||||
player.distanceToSqr(state.lastX, state.lastY, state.lastZ) >
|
||||
player.distanceToSqr(mov.getLastX(), mov.getLastY(), mov.getLastZ()) >
|
||||
MOVEMENT_THRESHOLD_SQ;
|
||||
|
||||
// Decrement cooldown
|
||||
if (state.hopCooldown > 0) {
|
||||
state.hopCooldown--;
|
||||
if (mov.getHopCooldown() > 0) {
|
||||
mov.setHopCooldown(mov.getHopCooldown() - 1);
|
||||
}
|
||||
|
||||
if (isMoving && player.onGround() && state.hopCooldown <= 0) {
|
||||
if (state.hopStartupPending) {
|
||||
if (isMoving && player.onGround() && mov.getHopCooldown() <= 0) {
|
||||
if (mov.isHopStartupPending()) {
|
||||
// Startup delay: decrement and wait (latched: completes even if
|
||||
// player briefly releases input during these 4 ticks)
|
||||
state.hopStartupTicks--;
|
||||
if (state.hopStartupTicks <= 0) {
|
||||
mov.setHopStartupTicks(mov.getHopStartupTicks() - 1);
|
||||
if (mov.getHopStartupTicks() <= 0) {
|
||||
// Startup complete: execute first hop
|
||||
state.hopStartupPending = false;
|
||||
mov.setHopStartupPending(false);
|
||||
executeHop(player, state);
|
||||
}
|
||||
} else {
|
||||
// Normal hop
|
||||
executeHop(player, state);
|
||||
}
|
||||
state.hopNotMovingTicks = 0;
|
||||
mov.setHopNotMovingTicks(0);
|
||||
} else if (!isMoving) {
|
||||
state.hopNotMovingTicks++;
|
||||
mov.setHopNotMovingTicks(mov.getHopNotMovingTicks() + 1);
|
||||
// Reset startup if not moving for >= 2 consecutive ticks
|
||||
if (
|
||||
state.hopNotMovingTicks >= HOP_STARTUP_RESET_TICKS &&
|
||||
!state.hopStartupPending
|
||||
mov.getHopNotMovingTicks() >= HOP_STARTUP_RESET_TICKS &&
|
||||
!mov.isHopStartupPending()
|
||||
) {
|
||||
state.hopStartupPending = true;
|
||||
state.hopStartupTicks = HOP_STARTUP_DELAY_TICKS;
|
||||
mov.setHopStartupPending(true);
|
||||
mov.setHopStartupTicks(HOP_STARTUP_DELAY_TICKS);
|
||||
}
|
||||
} else {
|
||||
// Moving but not on ground or cooldown active — reset not-moving counter
|
||||
state.hopNotMovingTicks = 0;
|
||||
mov.setHopNotMovingTicks(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +432,7 @@ public class MovementStyleManager {
|
||||
currentMotion.z + forward.z * HOP_FORWARD_IMPULSE
|
||||
);
|
||||
|
||||
state.hopCooldown = HOP_COOLDOWN_TICKS;
|
||||
state.getMovement().setHopCooldown(HOP_COOLDOWN_TICKS);
|
||||
|
||||
// Sync velocity to client to prevent rubber-banding
|
||||
player.connection.send(new ClientboundSetEntityMotionPacket(player));
|
||||
@@ -447,11 +448,11 @@ public class MovementStyleManager {
|
||||
player,
|
||||
CRAWL_SPEED_UUID,
|
||||
"tiedup.crawl_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
player.setForcedPose(Pose.SWIMMING);
|
||||
player.refreshDimensions();
|
||||
state.pendingPoseRestore = false;
|
||||
state.getMovement().setPendingPoseRestore(false);
|
||||
}
|
||||
|
||||
private static void deactivateCrawl(
|
||||
@@ -470,7 +471,7 @@ public class MovementStyleManager {
|
||||
player.refreshDimensions();
|
||||
} else {
|
||||
// Can't stand yet -- flag for periodic retry in tick flow (step 2)
|
||||
state.pendingPoseRestore = true;
|
||||
state.getMovement().setPendingPoseRestore(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +502,7 @@ public class MovementStyleManager {
|
||||
if (canStand) {
|
||||
player.setForcedPose(null);
|
||||
player.refreshDimensions();
|
||||
state.pendingPoseRestore = false;
|
||||
state.getMovement().setPendingPoseRestore(false);
|
||||
LOGGER.debug(
|
||||
"Restored standing pose for {} (pending pose restore cleared)",
|
||||
player.getName().getString()
|
||||
|
||||
Reference in New Issue
Block a user