Skip to content

Commit

Permalink
Allow for crawling and moving in one-block spaces where possible (Gey…
Browse files Browse the repository at this point in the history
…serMC#1814)

This commit brings full support for crawling, sneaking under 1.5-block-tall spaces, and swimming in one-block areas. There is a check in place that decreases the player's speed to something comparable to Java if they are in a situation where they would otherwise go at normal walking speed (for example: without the check, a Bedrock player would go at full walking speed while crawling).
  • Loading branch information
Camotoy authored Apr 12, 2021
1 parent 7dc9c03 commit 120769c
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 44 deletions.
41 changes: 31 additions & 10 deletions connector/src/main/java/org/geysermc/connector/entity/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
metadata.getFlags().setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !metadata.getFlags().getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire
metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02);
metadata.getFlags().setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08);
metadata.getFlags().setFlag(EntityFlag.SWIMMING, ((xd & 0x10) == 0x10) && metadata.getFlags().getFlag(EntityFlag.SPRINTING)); // Otherwise swimming is enabled on older servers
// Swimming is ignored here and instead we rely on the pose
metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);

// Armour stands are handled in their own class
Expand Down Expand Up @@ -297,16 +297,37 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
case 5: // no gravity
metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, !(boolean) entityMetadata.getValue());
break;
case 6: // Pose change
if (entityMetadata.getValue().equals(Pose.SLEEPING)) {
metadata.getFlags().setFlag(EntityFlag.SLEEPING, true);
metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f);
} else if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
metadata.getFlags().setFlag(EntityFlag.SLEEPING, false);
metadata.put(EntityData.BOUNDING_BOX_WIDTH, getEntityType().getWidth());
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, getEntityType().getHeight());
case 6: // Pose change - typically used for bounding box and not animation
Pose pose = (Pose) entityMetadata.getValue();

metadata.getFlags().setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING));
// Triggered when crawling
metadata.getFlags().setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING));
float width = entityType.getWidth();
float height = entityType.getHeight();
switch (pose) {
case SLEEPING:
if (this instanceof LivingEntity) {
width = 0.2f;
height = 0.2f;
}
break;
case SNEAKING:
if (entityType == EntityType.PLAYER) {
height = 1.5f;
}
break;
case FALL_FLYING:
case SPIN_ATTACK:
case SWIMMING:
if (entityType == EntityType.PLAYER) {
// Seems like this is only cared about for players; nothing else
height = 0.6f;
}
break;
}
metadata.put(EntityData.BOUNDING_BOX_WIDTH, width);
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height);
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
package org.geysermc.connector.entity.player;

import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.network.session.GeyserSession;

import java.util.UUID;
Expand All @@ -35,6 +38,10 @@
* The entity class specifically for a {@link GeyserSession}'s player.
*/
public class SessionPlayerEntity extends PlayerEntity {
/**
* Whether to check for updated speed after all entity metadata has been processed
*/
private boolean refreshSpeed = false;

private final GeyserSession session;

Expand All @@ -43,7 +50,6 @@ public SessionPlayerEntity(GeyserSession session) {

valid = true;
this.session = session;
this.session.getCollisionManager().updatePlayerBoundingBox(position);
}

@Override
Expand All @@ -64,4 +70,27 @@ public void setPosition(Vector3f position) {
}
super.setPosition(position);
}

@Override
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
super.updateBedrockMetadata(entityMetadata, session);
if (entityMetadata.getId() == 0) {
session.setSwimmingInWater((((byte) entityMetadata.getValue()) & 0x10) == 0x10 && metadata.getFlags().getFlag(EntityFlag.SPRINTING));
refreshSpeed = true;
} else if (entityMetadata.getId() == 6) {
session.setPose((Pose) entityMetadata.getValue());
refreshSpeed = true;
}
}

@Override
public void updateBedrockMetadata(GeyserSession session) {
super.updateBedrockMetadata(session);
if (refreshSpeed) {
if (session.adjustSpeed()) {
updateBedrockAttributes(session);
}
refreshSpeed = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.github.steveice10.mc.protocol.MinecraftConstants;
import com.github.steveice10.mc.protocol.MinecraftProtocol;
import com.github.steveice10.mc.protocol.data.SubProtocol;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.recipe.Recipe;
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
Expand All @@ -55,6 +56,8 @@
import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.v431.Bedrock_v431;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
Expand All @@ -78,6 +81,8 @@
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.Tickable;
import org.geysermc.connector.entity.attribute.Attribute;
import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.entity.player.SessionPlayerEntity;
import org.geysermc.connector.entity.player.SkullPlayerEntity;
import org.geysermc.connector.inventory.Inventory;
Expand Down Expand Up @@ -219,6 +224,12 @@ public class GeyserSession implements CommandSender {

private boolean sneaking;

/**
* Stores the Java pose that the server and/or Geyser believes the player currently has.
*/
@Setter
private Pose pose = Pose.STANDING;

@Setter
private boolean sprinting;

Expand All @@ -228,6 +239,22 @@ public class GeyserSession implements CommandSender {
@Setter
private boolean jumping;

/**
* Whether the player is swimming in water.
* Used to update speed when crawling.
*/
@Setter
private boolean swimmingInWater;

/**
* Tracks the original speed attribute.
*
* We need to do this in order to emulate speeds when sneaking under 1.5-blocks-tall areas if the player isn't sneaking,
* and when crawling.
*/
@Setter
private float originalSpeedAttribute;

/**
* The dimension of the player.
* As all entities are in the same world, this can be safely applied to all other entities.
Expand Down Expand Up @@ -427,8 +454,7 @@ public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServ
this.collisionManager = new CollisionManager(this);

this.playerEntity = new SessionPlayerEntity(this);
this.worldCache = new WorldCache(this);
this.windowCache = new WindowCache(this);
collisionManager.updatePlayerBoundingBox(this.playerEntity.getPosition());

this.playerInventory = new PlayerInventory();
this.openInventory = null;
Expand Down Expand Up @@ -829,15 +855,58 @@ public void setAuthenticationData(AuthData authData) {

public void setSneaking(boolean sneaking) {
this.sneaking = sneaking;
collisionManager.updatePlayerBoundingBox();
collisionManager.updateScaffoldingFlags();

// Update pose and bounding box on our end
if (!sneaking && adjustSpeed()) {
// Update attributes since we're still "sneaking" under a 1.5-block-tall area
playerEntity.updateBedrockAttributes(this);
// the server *should* update our pose once it has returned to normal
} else {
this.pose = sneaking ? Pose.SNEAKING : Pose.STANDING;
playerEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, sneaking ? 1.5f : playerEntity.getEntityType().getHeight());
playerEntity.getMetadata().getFlags().setFlag(EntityFlag.SNEAKING, sneaking);

collisionManager.updatePlayerBoundingBox();
collisionManager.updateScaffoldingFlags(false);
}

playerEntity.updateBedrockMetadata(this);

if (mouseoverEntity != null) {
// Horses, etc can change their property depending on if you're sneaking
InteractiveTagManager.updateTag(this, mouseoverEntity);
}
}

public void setSwimming(boolean swimming) {
this.pose = swimming ? Pose.SWIMMING : Pose.STANDING;
playerEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, swimming ? 0.6f : playerEntity.getEntityType().getHeight());
playerEntity.getMetadata().getFlags().setFlag(EntityFlag.SWIMMING, swimming);
playerEntity.updateBedrockMetadata(this);
}

/**
* Adjusts speed if the player is crawling.
*
* @return true if attributes should be updated.
*/
public boolean adjustSpeed() {
Attribute currentPlayerSpeed = playerEntity.getAttributes().get(AttributeType.MOVEMENT_SPEED);
if (currentPlayerSpeed != null) {
if ((pose.equals(Pose.SNEAKING) && !sneaking && collisionManager.isUnderSlab()) ||
(!swimmingInWater && playerEntity.getMetadata().getFlags().getFlag(EntityFlag.SWIMMING) && !collisionManager.isPlayerInWater())) {
// Either of those conditions means that Bedrock goes zoom when they shouldn't be
currentPlayerSpeed.setValue(originalSpeedAttribute / 3.32f);
return true;
} else if (originalSpeedAttribute != currentPlayerSpeed.getValue()) {
// Speed has reset to normal
currentPlayerSpeed.setValue(originalSpeedAttribute);
return true;
}
}
return false;
}

/**
* Will be overwritten for GeyserConnect.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerAbilitiesPacket;
import com.nukkitx.protocol.bedrock.data.AdventureSetting;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
Expand All @@ -37,8 +38,13 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator<Adventu

@Override
public void translate(AdventureSettingsPacket packet, GeyserSession session) {
ClientPlayerAbilitiesPacket abilitiesPacket =
new ClientPlayerAbilitiesPacket(packet.getSettings().contains(AdventureSetting.FLYING));
boolean isFlying = packet.getSettings().contains(AdventureSetting.FLYING);
ClientPlayerAbilitiesPacket abilitiesPacket = new ClientPlayerAbilitiesPacket(isFlying);
session.sendDownstreamPacket(abilitiesPacket);

if (isFlying && session.getPlayerEntity().getMetadata().getFlags().getFlag(EntityFlag.SWIMMING)) {
// Bedrock can fly and swim at the same time? Make sure that can't happen
session.setSwimming(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
Expand Down Expand Up @@ -150,13 +149,18 @@ public void translate(InventoryTransactionPacket packet, GeyserSession session)
EntityFlags flags = session.getPlayerEntity().getMetadata().getFlags();

// Adjust position for current eye height
if (flags.getFlag(EntityFlag.SNEAKING)) {
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 1.27f), 0);
} else if (flags.getFlag(EntityFlag.SWIMMING) || flags.getFlag(EntityFlag.GLIDING) || flags.getFlag(EntityFlag.DAMAGE_NEARBY_MOBS)) {
// Swimming, gliding, or using the trident spin attack
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.4f), 0);
} else if (flags.getFlag(EntityFlag.SLEEPING)) {
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.2f), 0);
switch (session.getPose()) {
case SNEAKING:
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 1.27f), 0);
break;
case SWIMMING:
case FALL_FLYING: // Elytra
case SPIN_ATTACK: // Trident spin attack
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.4f), 0);
break;
case SLEEPING:
playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.2f), 0);
break;
} // else, we don't have to modify the position

float diffX = playerPosition.getX() - packet.getBlockPosition().getX();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
@Override
public void translate(PlayerActionPacket packet, GeyserSession session) {
Entity entity = session.getPlayerEntity();
if (entity == null)
return;

// Send book update before any player action
if (packet.getAction() != PlayerActionType.RESPAWN) {
Expand All @@ -84,10 +82,14 @@ public void translate(PlayerActionPacket packet, GeyserSession session) {
case START_SWIMMING:
ClientPlayerStatePacket startSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
session.sendDownstreamPacket(startSwimPacket);

session.setSwimming(true);
break;
case STOP_SWIMMING:
ClientPlayerStatePacket stopSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.STOP_SPRINTING);
session.sendDownstreamPacket(stopSwimPacket);

session.setSwimming(false);
break;
case START_GLIDE:
// Otherwise gliding will not work in creative
Expand All @@ -114,7 +116,7 @@ public void translate(PlayerActionPacket packet, GeyserSession session) {
}
session.sendDownstreamPacket(useItemPacket);
session.getPlayerEntity().getMetadata().getFlags().setFlag(EntityFlag.BLOCKING, true);
session.getPlayerEntity().updateBedrockMetadata(session);
// metadata will be updated when sneaking
}

session.setSneaking(true);
Expand All @@ -128,7 +130,7 @@ public void translate(PlayerActionPacket packet, GeyserSession session) {
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, BlockUtils.POSITION_ZERO, BlockFace.DOWN);
session.sendDownstreamPacket(releaseItemPacket);
session.getPlayerEntity().getMetadata().getFlags().setFlag(EntityFlag.BLOCKING, false);
session.getPlayerEntity().updateBedrockMetadata(session);
// metadata will be updated when sneaking
}

session.setSneaking(false);
Expand Down
Loading

0 comments on commit 120769c

Please sign in to comment.