From 82fb2e4df123fd3c04d4c862a49f1e3b935d55a4 Mon Sep 17 00:00:00 2001 From: onebeastchris Date: Sun, 5 Oct 2025 00:50:21 +0200 Subject: [PATCH] Fix: Input processing on mobile devices with classic D-Pad control mode --- .../geyser/session/cache/InputCache.java | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java index 91327ad33..7042c4c2a 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java @@ -29,6 +29,7 @@ import lombok.Getter; import lombok.Setter; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.cloudburstmc.math.vector.Vector2f; +import org.cloudburstmc.protocol.bedrock.data.InputInteractionModel; import org.cloudburstmc.protocol.bedrock.data.InputMode; import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData; import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket; @@ -61,14 +62,54 @@ public final class InputCache { var oldInputPacket = this.inputPacket; this.inputMode = packet.getInputMode(); + /* + Brief introduction to how Bedrock sends movement inputs! It's mainly based on the following: + (as of 1.21.111) + 1. inputmode: + - MOUSE: same as Java edition; will send up/down/left/right inputs via input flags + - GAMEPAD: indicates the use of a controller with joysticks, sends an "analogue movement vector" instead + - TOUCH: see interaction model! + - MOTION_CONTROLLER: what even is this + + 2. Interaction model (here, only really relevant for us when the inputmode is "touch"): + - CLASSIC: shows "wasd" keys on the client; like input-mode MOUSE would, additionally up_left / up_right / down_left / down_right + - CROSSHAIR / TOUCH: NO wasd, analogue movement vector instead + + Hence, we'll also need to check for this fun edge-case! + */ + boolean isMobileAndClassicMovement = inputMode == InputMode.TOUCH && packet.getInputInteractionModel() == InputInteractionModel.CLASSIC; + boolean up, down, left, right; - if (this.inputMode == InputMode.MOUSE) { + if (this.inputMode == InputMode.MOUSE || isMobileAndClassicMovement) { up = bedrockInput.contains(PlayerAuthInputData.UP); down = bedrockInput.contains(PlayerAuthInputData.DOWN); left = bedrockInput.contains(PlayerAuthInputData.LEFT); right = bedrockInput.contains(PlayerAuthInputData.RIGHT); + + if (isMobileAndClassicMovement) { + // These are the buttons in the corners of the touch area + if (bedrockInput.contains(PlayerAuthInputData.UP_LEFT)) { + up = true; + left = true; + } + + if (bedrockInput.contains(PlayerAuthInputData.UP_RIGHT)) { + up = true; + right = true; + } + + if (bedrockInput.contains(PlayerAuthInputData.DOWN_LEFT)) { + down = true; + left = true; + } + + if (bedrockInput.contains(PlayerAuthInputData.DOWN_RIGHT)) { + down = true; + right = true; + } + } } else { - // The above flags don't fire TODO test console + // The above flags don't fire Vector2f analogMovement = packet.getAnalogMoveVector(); up = analogMovement.getY() > 0; down = analogMovement.getY() < 0; @@ -78,7 +119,6 @@ public final class InputCache { boolean sneaking = isSneaking(bedrockInput); - // TODO when is UP_LEFT, etc. used? this.inputPacket = this.inputPacket .withForward(up) .withBackward(down) @@ -133,7 +173,7 @@ public final class InputCache { public boolean isSneaking(Set authInputData) { // Flying doesn't send start / stop fly cases; might as well return early if (session.isFlying()) { - // Of course e.g. mobile handles it differently with a descend case, while + // Of course e.g. mobile devices handle it differently with a descend case, while // e.g. Win10 sends SNEAK_DOWN. Why? We'll never know. return authInputData.contains(PlayerAuthInputData.DESCEND) || authInputData.contains(PlayerAuthInputData.SNEAK_DOWN); }