Allow for crawling and moving in one-block spaces where possible (#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).
This commit is contained in:
Camotoy 2021-04-12 00:35:53 -04:00 committed by GitHub
parent 7dc9c031c2
commit 120769c7f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 229 additions and 44 deletions

View File

@ -263,7 +263,7 @@ public class Entity {
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
@ -297,16 +297,37 @@ public class Entity {
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;
}
}

View File

@ -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;
@ -35,6 +38,10 @@ import java.util.UUID;
* 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;
@ -43,7 +50,6 @@ public class SessionPlayerEntity extends PlayerEntity {
valid = true;
this.session = session;
this.session.getCollisionManager().updatePlayerBoundingBox(position);
}
@Override
@ -64,4 +70,27 @@ public class SessionPlayerEntity extends PlayerEntity {
}
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;
}
}
}

View File

@ -35,6 +35,7 @@ import com.github.steveice10.mc.auth.service.MsaAuthenticationService;
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;
@ -55,6 +56,8 @@ import com.nukkitx.protocol.bedrock.BedrockPacket;
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;
@ -78,6 +81,8 @@ import org.geysermc.connector.command.CommandSender;
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;
@ -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;
@ -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.
@ -427,8 +454,7 @@ public class GeyserSession implements CommandSender {
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;
@ -829,8 +855,22 @@ public class GeyserSession implements CommandSender {
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
@ -838,6 +878,35 @@ public class GeyserSession implements CommandSender {
}
}
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.
*/

View File

@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.bedrock;
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;
@ -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);
}
}
}

View File

@ -38,7 +38,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye
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;
@ -150,13 +149,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
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();

View File

@ -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) {
@ -84,10 +82,14 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
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
@ -114,7 +116,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
}
session.sendDownstreamPacket(useItemPacket);
session.getPlayerEntity().getMetadata().getFlags().setFlag(EntityFlag.BLOCKING, true);
session.getPlayerEntity().updateBedrockMetadata(session);
// metadata will be updated when sneaking
}
session.setSneaking(true);
@ -128,7 +130,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
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);

View File

@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.collision;
import com.nukkitx.math.vector.Vector3d;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
@ -38,7 +39,9 @@ import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
@ -66,6 +69,10 @@ public class CollisionManager {
* This check doesn't allow players right up against the block, so they must be pushed slightly away.
*/
public static final double COLLISION_TOLERANCE = 0.00001;
/**
* Trims Y coordinates when jumping to prevent rounding issues being sent to the server.
*/
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.#####");
public CollisionManager(GeyserSession session) {
this.session = session;
@ -104,17 +111,14 @@ public class CollisionManager {
} else {
playerPosition = session.getPlayerEntity().getPosition();
}
playerBoundingBox = new BoundingBox(playerPosition.getX(), playerPosition.getY() + 0.9, playerPosition.getZ(), 0.6, 1.8, 0.6);
playerBoundingBox = new BoundingBox(playerPosition.getX(), playerPosition.getY() + 0.9, playerPosition.getZ(),
EntityType.PLAYER.getWidth(), EntityType.PLAYER.getHeight(), EntityType.PLAYER.getLength());
} else {
// According to the Minecraft Wiki, when sneaking:
// - In Bedrock Edition, the height becomes 1.65 blocks, allowing movement through spaces as small as 1.75 (2 - 14) blocks high.
// - In Java Edition, the height becomes 1.5 blocks.
// TODO: Have this depend on the player's literal bounding box variable
if (session.isSneaking()) {
playerBoundingBox.setSizeY(1.5);
} else {
playerBoundingBox.setSizeY(1.8);
}
// Other instances have the player's bounding box become as small as 0.6 or 0.2.
playerBoundingBox.setSizeY(session.getPlayerEntity().getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT));
}
}
@ -148,6 +152,11 @@ public class CollisionManager {
position = Vector3d.from(playerBoundingBox.getMiddleX(),
playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2),
playerBoundingBox.getMiddleZ());
if (!onGround) {
// Trim the position to prevent rounding errors that make Java think we are clipping into a block
position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ());
}
} else {
// When chunk caching is off, we have to rely on this
// It rounds the Y position up to the nearest 0.5
@ -246,16 +255,48 @@ public class CollisionManager {
}
}
updateScaffoldingFlags();
updateScaffoldingFlags(true);
return true;
}
/**
* @return true if the block located at the player's floor position plus 1 would intersect with the player,
* were they not sneaking
*/
public boolean isUnderSlab() {
if (!session.getConnector().getConfig().isCacheChunks()) {
// We can't reliably determine this
return false;
}
Vector3i position = session.getPlayerEntity().getPosition().toInt();
BlockCollision collision = CollisionTranslator.getCollisionAt(session, position.getX(), position.getY(), position.getZ());
if (collision != null) {
// Determine, if the player's bounding box *were* at full height, if it would intersect with the block
// at the current location.
playerBoundingBox.setSizeY(EntityType.PLAYER.getHeight());
boolean result = collision.checkIntersection(playerBoundingBox);
playerBoundingBox.setSizeY(session.getPlayerEntity().getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT));
return result;
}
return false;
}
/**
* @return if the player is currently in a water block
*/
public boolean isPlayerInWater() {
return session.getConnector().getConfig().isCacheChunks()
&& session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID;
}
/**
* Updates scaffolding entity flags
* Scaffolding needs to be checked per-move since it's a flag in Bedrock but Java does it client-side
*
* @param updateMetadata whether we should update metadata if something changed
*/
public void updateScaffoldingFlags() {
public void updateScaffoldingFlags(boolean updateMetadata) {
EntityFlags flags = session.getPlayerEntity().getMetadata().getFlags();
boolean flagsChanged;
boolean isSneakingWithScaffolding = (touchingScaffolding || onScaffolding) && session.isSneaking();
@ -269,7 +310,7 @@ public class CollisionManager {
flagsChanged |= flags.getFlag(EntityFlag.IN_SCAFFOLDING) != touchingScaffolding;
flags.setFlag(EntityFlag.IN_SCAFFOLDING, touchingScaffolding);
if (flagsChanged) {
if (flagsChanged && updateMetadata) {
session.getPlayerEntity().updateBedrockMetadata(session);
}
}

View File

@ -25,13 +25,12 @@
package org.geysermc.connector.network.translators.java.entity;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPositionPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPositionPacket;
@Translator(packet = ServerEntityPositionPacket.class)
public class JavaEntityPositionTranslator extends PacketTranslator<ServerEntityPositionPacket> {

View File

@ -67,7 +67,12 @@ public class JavaEntityPropertiesTranslator extends PacketTranslator<ServerEntit
entity.getAttributes().put(AttributeType.MOVEMENT_SPEED, AttributeType.MOVEMENT_SPEED.getAttribute((float) AttributeUtils.calculateValue(attribute)));
break;
case GENERIC_MOVEMENT_SPEED:
entity.getAttributes().put(AttributeType.MOVEMENT_SPEED, AttributeType.MOVEMENT_SPEED.getAttribute((float) AttributeUtils.calculateValue(attribute)));
float value = (float) AttributeUtils.calculateValue(attribute);
entity.getAttributes().put(AttributeType.MOVEMENT_SPEED, AttributeType.MOVEMENT_SPEED.getAttribute(value));
if (isSessionPlayer) {
session.setOriginalSpeedAttribute(value);
session.adjustSpeed();
}
break;
case GENERIC_FOLLOW_RANGE:
entity.getAttributes().put(AttributeType.FOLLOW_RANGE, AttributeType.FOLLOW_RANGE.getAttribute((float) AttributeUtils.calculateValue(attribute)));

View File

@ -52,6 +52,7 @@ public abstract class BlockTranslator {
* The Java block runtime ID of air
*/
public static final int JAVA_AIR_ID = 0;
public static final int JAVA_WATER_ID;
/**
* The Bedrock block runtime ID of air
*/
@ -134,6 +135,7 @@ public abstract class BlockTranslator {
int furnaceLitRuntimeId = -1;
int spawnerRuntimeId = -1;
int uniqueJavaId = -1;
int waterRuntimeId = -1;
Iterator<Map.Entry<String, JsonNode>> blocksIterator = BLOCKS_JSON.fields();
while (blocksIterator.hasNext()) {
javaRuntimeId++;
@ -199,6 +201,9 @@ public abstract class BlockTranslator {
} else if (javaId.startsWith("minecraft:spawner")) {
spawnerRuntimeId = javaRuntimeId;
} else if ("minecraft:water[level=0]".equals(javaId)) {
waterRuntimeId = javaRuntimeId;
}
}
@ -222,6 +227,11 @@ public abstract class BlockTranslator {
}
JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId;
if (waterRuntimeId == -1) {
throw new AssertionError("Unable to find Java water in palette");
}
JAVA_WATER_ID = waterRuntimeId;
BlockTranslator1_16_100.init();
BlockTranslator1_16_210.init();
BLOCKS_JSON = null; // We no longer require this so let it garbage collect away

View File

@ -133,8 +133,7 @@ public class BlockUtils {
hasteLevel = session.getEffectCache().getEffectLevel(Effect.FASTER_DIG);
miningFatigueLevel = session.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
boolean isInWater = session.getConnector().getConfig().isCacheChunks()
&& session.getBlockTranslator().getBedrockBlockId(session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt())) == session.getBlockTranslator().getBedrockWaterId();
boolean isInWater = session.getCollisionManager().isPlayerInWater();
boolean insideOfWaterWithoutAquaAffinity = isInWater &&
ItemUtils.getEnchantmentLevel(session.getPlayerInventory().getItem(5).getNbt(), "minecraft:aqua_affinity") < 1;