mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Prevent player from jumping out of vehicle by input locking (#5908)
* Initial changes to input locking. * Working vehicle dismount locks. * Locking jump dismount, more work on server-sided dismount. * Just lock jump input, locking dismount cause unintended behaviour. * Revert some old changes. * Oops. * Rename this to doesJumpDismount. * Sort this.
This commit is contained in:
@@ -29,6 +29,7 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
|||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
|
||||||
|
import org.geysermc.geyser.input.InputLocksFlag;
|
||||||
import org.geysermc.geyser.api.entity.EntityData;
|
import org.geysermc.geyser.api.entity.EntityData;
|
||||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||||
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
|
||||||
@@ -87,7 +88,8 @@ public class GeyserEntityData implements EntityData {
|
|||||||
movementLockOwners.remove(owner);
|
movementLockOwners.remove(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.lockInputs(session.camera().isCameraLocked(), isMovementLocked());
|
session.setLockInput(InputLocksFlag.MOVEMENT, isMovementLocked());
|
||||||
|
session.updateInputLocks();
|
||||||
return isMovementLocked();
|
return isMovementLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -614,6 +614,14 @@ public class Entity implements GeyserEntity {
|
|||||||
protected boolean isShaking() {
|
protected boolean isShaking() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* If true, the entity can be dismounted by pressing jump.
|
||||||
|
*
|
||||||
|
* @return whether the entity can be dismounted when pressing jump.
|
||||||
|
*/
|
||||||
|
public boolean doesJumpDismount() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* x = Pitch, y = Yaw, z = HeadYaw
|
* x = Pitch, y = Yaw, z = HeadYaw
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
|||||||
import org.geysermc.geyser.entity.EntityDefinition;
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||||
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
|
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
|
||||||
|
import org.geysermc.geyser.input.InputLocksFlag;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.item.Items;
|
import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.geyser.item.type.Item;
|
import org.geysermc.geyser.item.type.Item;
|
||||||
@@ -85,6 +86,15 @@ public class AbstractHorseEntity extends AnimalEntity {
|
|||||||
// Shows the jump meter
|
// Shows the jump meter
|
||||||
setFlag(EntityFlag.CAN_POWER_JUMP, saddled);
|
setFlag(EntityFlag.CAN_POWER_JUMP, saddled);
|
||||||
super.updateSaddled(saddled);
|
super.updateSaddled(saddled);
|
||||||
|
|
||||||
|
// We want to allow player to press jump again if pressing jump doesn't dismount the entity.
|
||||||
|
this.session.setLockInput(InputLocksFlag.JUMP, this.doesJumpDismount());
|
||||||
|
this.session.updateInputLocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doesJumpDismount() {
|
||||||
|
return !this.getFlag(EntityFlag.SADDLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHorseFlags(ByteEntityMetadata entityMetadata) {
|
public void setHorseFlags(ByteEntityMetadata entityMetadata) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
|||||||
import org.geysermc.geyser.entity.type.BoatEntity;
|
import org.geysermc.geyser.entity.type.BoatEntity;
|
||||||
import org.geysermc.geyser.entity.type.Entity;
|
import org.geysermc.geyser.entity.type.Entity;
|
||||||
import org.geysermc.geyser.entity.type.LivingEntity;
|
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||||
|
import org.geysermc.geyser.input.InputLocksFlag;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.item.Items;
|
import org.geysermc.geyser.item.Items;
|
||||||
import org.geysermc.geyser.level.block.Blocks;
|
import org.geysermc.geyser.level.block.Blocks;
|
||||||
@@ -476,6 +477,10 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||||||
this.vehicle.updateBedrockMetadata();
|
this.vehicle.updateBedrockMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bedrock player can dismount by pressing jump while Java cannot, so we need to prevent player from jumping to match vanilla behaviour.
|
||||||
|
this.session.setLockInput(InputLocksFlag.JUMP, entity != null && entity.doesJumpDismount());
|
||||||
|
this.session.updateInputLocks();
|
||||||
|
|
||||||
super.setVehicle(entity);
|
super.setVehicle(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
|||||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||||
import org.geysermc.geyser.api.bedrock.camera.GuiElement;
|
import org.geysermc.geyser.api.bedrock.camera.GuiElement;
|
||||||
|
import org.geysermc.geyser.input.InputLocksFlag;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||||
|
|
||||||
@@ -251,7 +252,8 @@ public class GeyserCameraData implements CameraData {
|
|||||||
this.cameraLockOwners.remove(owner);
|
this.cameraLockOwners.remove(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.lockInputs(isCameraLocked(), session.entities().isMovementLocked());
|
session.setLockInput(InputLocksFlag.CAMERA, isCameraLocked());
|
||||||
|
session.updateInputLocks();
|
||||||
return isCameraLocked();
|
return isCameraLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.input;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
// This is taken from (https://gist.github.com/wan-adrian/e919b46be3889d865801eb8883407587) or (https://github.com/PowerNukkitX/PowerNukkitX/blob/master/src/main/java/cn/nukkit/network/protocol/types/ClientInputLocksFlag.java)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum InputLocksFlag {
|
||||||
|
RESET(0),
|
||||||
|
CAMERA(2),
|
||||||
|
MOVEMENT(4),
|
||||||
|
LATERAL_MOVEMENT(16),
|
||||||
|
SNEAK(32),
|
||||||
|
JUMP(64),
|
||||||
|
MOUNT(128),
|
||||||
|
DISMOUNT(256),
|
||||||
|
MOVE_FORWARD(512),
|
||||||
|
MOVE_BACKWARD(1024),
|
||||||
|
MOVE_LEFT(2048),
|
||||||
|
MOVE_RIGHT(4096);
|
||||||
|
|
||||||
|
private final int offset;
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import org.cloudburstmc.math.vector.Vector3d;
|
|||||||
import org.cloudburstmc.math.vector.Vector3f;
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateClientInputLocksPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.UpdateClientInputLocksPacket;
|
||||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
@@ -220,12 +221,12 @@ public class CollisionManager {
|
|||||||
|
|
||||||
public void recalculatePosition() {
|
public void recalculatePosition() {
|
||||||
PlayerEntity entity = session.getPlayerEntity();
|
PlayerEntity entity = session.getPlayerEntity();
|
||||||
|
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||||
// This does the job and won't interrupt velocity + rotation.
|
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||||
UpdateClientInputLocksPacket inputLocksPacket = new UpdateClientInputLocksPacket();
|
movePlayerPacket.setPosition(entity.getPosition());
|
||||||
inputLocksPacket.setLockComponentData(0); // Don't actually lock anything.
|
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||||
inputLocksPacket.setServerPosition(entity.getPosition());
|
movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL);
|
||||||
session.sendUpstreamPacket(inputLocksPacket);
|
session.sendUpstreamPacket(movePlayerPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockPositionIterator collidableBlocksIterator(BoundingBox box) {
|
public BlockPositionIterator collidableBlocksIterator(BoundingBox box) {
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ import org.geysermc.cumulus.form.util.FormBuilder;
|
|||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||||
|
import org.geysermc.geyser.input.InputLocksFlag;
|
||||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||||
import org.geysermc.geyser.api.entity.EntityData;
|
import org.geysermc.geyser.api.entity.EntityData;
|
||||||
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
||||||
@@ -234,6 +235,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -748,6 +750,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
@Accessors(fluent = true)
|
@Accessors(fluent = true)
|
||||||
private boolean hasAcceptedCodeOfConduct = false;
|
private boolean hasAcceptedCodeOfConduct = false;
|
||||||
|
|
||||||
|
private final Set<InputLocksFlag> inputLocksSet = EnumSet.noneOf(InputLocksFlag.class);
|
||||||
|
private boolean inputLockDirty;
|
||||||
|
|
||||||
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop tickEventLoop) {
|
public GeyserSession(GeyserImpl geyser, BedrockServerSession bedrockServerSession, EventLoop tickEventLoop) {
|
||||||
this.geyser = geyser;
|
this.geyser = geyser;
|
||||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||||
@@ -2377,17 +2382,21 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
entities().showEmote(emoter, emoteId);
|
entities().showEmote(emoter, emoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lockInputs(boolean camera, boolean movement) {
|
public void setLockInput(InputLocksFlag flag, boolean value) {
|
||||||
|
this.inputLockDirty |= value ? this.inputLocksSet.add(flag) : this.inputLocksSet.remove(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateInputLocks() {
|
||||||
|
if (!this.inputLockDirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.inputLockDirty = false;
|
||||||
|
|
||||||
UpdateClientInputLocksPacket packet = new UpdateClientInputLocksPacket();
|
UpdateClientInputLocksPacket packet = new UpdateClientInputLocksPacket();
|
||||||
final int cameraOffset = 1 << 1;
|
|
||||||
final int movementOffset = 1 << 2;
|
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
if (camera) {
|
for (InputLocksFlag other : this.inputLocksSet) {
|
||||||
result |= cameraOffset;
|
result |= other.getOffset();
|
||||||
}
|
|
||||||
if (movement) {
|
|
||||||
result |= movementOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.setLockComponentData(result);
|
packet.setLockComponentData(result);
|
||||||
@@ -2396,6 +2405,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
sendUpstreamPacket(packet);
|
sendUpstreamPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getLockedInput(InputLocksFlag flag) {
|
||||||
|
return this.inputLocksSet.contains(flag);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull CameraData camera() {
|
public @NonNull CameraData camera() {
|
||||||
return this.cameraData;
|
return this.cameraData;
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ public final class InputCache {
|
|||||||
// https://mojang.github.io/bedrock-protocol-docs/html/enums.html
|
// https://mojang.github.io/bedrock-protocol-docs/html/enums.html
|
||||||
// using the "raw" values allows us sending key presses even with locked input
|
// using the "raw" values allows us sending key presses even with locked input
|
||||||
// There appear to be cases where the raw value is not sent - e.g. sneaking with a shield on mobile (1.21.80)
|
// There appear to be cases where the raw value is not sent - e.g. sneaking with a shield on mobile (1.21.80)
|
||||||
.withJump(bedrockInput.contains(PlayerAuthInputData.JUMP_CURRENT_RAW) || bedrockInput.contains(PlayerAuthInputData.JUMP_DOWN))
|
// We also need to check for water auto jumping, since bedrock don't send jumping value in those cases.
|
||||||
|
.withJump(bedrockInput.contains(PlayerAuthInputData.JUMP_CURRENT_RAW) || bedrockInput.contains(PlayerAuthInputData.JUMP_DOWN) || bedrockInput.contains(PlayerAuthInputData.AUTO_JUMPING_IN_WATER))
|
||||||
.withShift(session.isShouldSendSneak() || sneaking)
|
.withShift(session.isShouldSendSneak() || sneaking)
|
||||||
.withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINT_DOWN));
|
.withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINT_DOWN));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user