From dc810f1d3945591aa7038785b506f8501121d7bb Mon Sep 17 00:00:00 2001 From: David Choo Date: Sat, 2 Jul 2022 21:17:14 -0400 Subject: [PATCH] Fix break time while submerged in water (#3110) * Fix break time while submerged in water * Review stuff * LAYERS -> LEVELS --- .../geyser/entity/type/FishingHookEntity.java | 16 ++-------- .../geyser/level/block/BlockStateValues.java | 27 ++++++++++++++++- .../level/physics/CollisionManager.java | 13 +++++++++ .../geyser/session/GeyserSession.java | 12 ++++++++ ...BedrockInventoryTransactionTranslator.java | 15 ++-------- .../org/geysermc/geyser/util/BlockUtils.java | 29 +++++++------------ 6 files changed, 67 insertions(+), 45 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java index 75bdd9021..65662bbe4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/FishingHookEntity.java @@ -99,19 +99,9 @@ public class FishingHookEntity extends ThrowableEntity { } } - int waterLevel = BlockStateValues.getWaterLevel(blockID); - if (BlockRegistries.WATERLOGGED.get().contains(blockID)) { - waterLevel = 0; - } - if (waterLevel >= 0) { - double waterMaxY = iter.getY() + 1 - (waterLevel + 1) / 9.0; - // Falling water is a full block - if (waterLevel >= 8) { - waterMaxY = iter.getY() + 1; - } - if (position.getY() <= waterMaxY) { - touchingWater = true; - } + double waterHeight = BlockStateValues.getWaterHeight(blockID); + if (waterHeight != -1 && position.getY() <= (iter.getY() + waterHeight)) { + touchingWater = true; } } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java index adb81761f..e4772df80 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockStateValues.java @@ -77,6 +77,8 @@ public final class BlockStateValues { public static int JAVA_SPAWNER_ID; public static int JAVA_WATER_ID; + public static final int NUM_WATER_LEVELS = 9; + /** * Determines if the block state contains Bedrock block information * @@ -449,7 +451,6 @@ public final class BlockStateValues { /** * Get the level of water from the block state. - * This is used in FishingHookEntity to create splash sounds when the hook hits the water. * * @param state BlockState of the block * @return The water level or -1 if the block isn't water @@ -458,6 +459,30 @@ public final class BlockStateValues { return WATER_LEVEL.getOrDefault(state, -1); } + /** + * Get the height of water from the block state + * This is used in FishingHookEntity to create splash sounds when the hook hits the water. In addition, + * CollisionManager uses this to determine if the player's eyes are in water. + * + * @param state BlockState of the block + * @return The water height or -1 if the block does not contain water + */ + public static double getWaterHeight(int state) { + int waterLevel = BlockStateValues.getWaterLevel(state); + if (BlockRegistries.WATERLOGGED.get().contains(state)) { + waterLevel = 0; + } + if (waterLevel >= 0) { + double waterHeight = 1 - (waterLevel + 1) / ((double) NUM_WATER_LEVELS); + // Falling water is a full block + if (waterLevel >= 8) { + waterHeight = 1; + } + return waterHeight; + } + return -1; + } + /** * Get the slipperiness of a block. * This is used in ItemEntity to calculate the friction on an item as it slides across the ground diff --git a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java index 2617b2750..a91c2b083 100644 --- a/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/physics/CollisionManager.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.level.physics; +import com.nukkitx.math.GenericMath; import com.nukkitx.math.vector.Vector3d; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -405,6 +406,18 @@ public class CollisionManager { return session.getGeyser().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockStateValues.JAVA_WATER_ID; } + public boolean isWaterInEyes() { + double eyeX = playerBoundingBox.getMiddleX(); + double eyeY = playerBoundingBox.getMiddleY() - playerBoundingBox.getSizeY() / 2d + session.getEyeHeight(); + double eyeZ = playerBoundingBox.getMiddleZ(); + + eyeY -= 1 / ((double) BlockStateValues.NUM_WATER_LEVELS); // Subtract the height of one water layer + int blockID = session.getGeyser().getWorldManager().getBlockAt(session, GenericMath.floor(eyeX), GenericMath.floor(eyeY), GenericMath.floor(eyeZ)); + double waterHeight = BlockStateValues.getWaterHeight(blockID); + + return waterHeight != -1 && eyeY < (Math.floor(eyeY) + waterHeight); + } + /** * Updates scaffolding entity flags * Scaffolding needs to be checked per-move since it's a flag in Bedrock but Java does it client-side diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 705ee6416..93ef62c56 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -100,6 +100,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; +import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.ItemFrameEntity; @@ -1767,6 +1768,17 @@ public class GeyserSession implements GeyserConnection, CommandSender { sendUpstreamPacket(packet); } + public float getEyeHeight() { + return switch (pose) { + case SNEAKING -> 1.27f; + case SWIMMING, + FALL_FLYING, // Elytra + SPIN_ATTACK -> 0.4f; // Trident spin attack + case SLEEPING -> 0.2f; + default -> EntityDefinitions.PLAYER.offset(); + }; + } + public MinecraftCodecHelper getCodecHelper() { return (MinecraftCodecHelper) this.downstream.getCodecHelper(); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index d9603ae8e..1adf123bf 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -201,7 +201,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator 1.27f; - case SWIMMING, - FALL_FLYING, // Elytra - SPIN_ATTACK -> 0.4f; // Trident spin attack - case SLEEPING -> 0.2f; - default -> EntityDefinitions.PLAYER.offset(); - }; - } } diff --git a/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java b/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java index 7059c9a8b..c0d484919 100644 --- a/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/BlockUtils.java @@ -36,6 +36,8 @@ import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.collision.BlockCollision; +import javax.annotation.Nullable; + public final class BlockUtils { private static boolean correctTool(GeyserSession session, BlockMapping blockMapping, String itemToolType) { @@ -101,7 +103,7 @@ public final class BlockUtils { // https://minecraft.gamepedia.com/Breaking private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool, boolean canTierMineBlock, String toolType, boolean isShearsEffective, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel, - boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround, boolean insideWaterAndNotOnGround) { + boolean insideOfWaterWithoutAquaAffinity, boolean onGround) { double baseTime = (((correctTool && canTierMineBlock) || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness; double speed = 1.0 / baseTime; @@ -129,12 +131,11 @@ public final class BlockUtils { } if (insideOfWaterWithoutAquaAffinity) speed *= 0.2; - if (outOfWaterButNotOnGround) speed *= 0.2; - if (insideWaterAndNotOnGround) speed *= 0.2; + if (!onGround) speed *= 0.2; return 1.0 / speed; } - public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemMapping item, CompoundTag nbtData, boolean isSessionPlayer) { + public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemMapping item, @Nullable CompoundTag nbtData, boolean isSessionPlayer) { boolean isShearsEffective = session.getTagCache().isShearsEffective(blockMapping); //TODO called twice boolean canHarvestWithHand = blockMapping.isCanBreakWithHand(); String toolType = ""; @@ -154,36 +155,28 @@ public final class BlockUtils { if (!isSessionPlayer) { // Another entity is currently mining; we have all the information we know return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective, - toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, - false, false); + toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, true); } hasteLevel = Math.max(session.getEffectCache().getHaste(), session.getEffectCache().getConduitPower()); miningFatigueLevel = session.getEffectCache().getMiningFatigue(); - boolean isInWater = session.getCollisionManager().isPlayerInWater(); - - boolean insideOfWaterWithoutAquaAffinity = isInWater && + boolean waterInEyes = session.getCollisionManager().isWaterInEyes(); + boolean insideOfWaterWithoutAquaAffinity = waterInEyes && ItemUtils.getEnchantmentLevel(session.getPlayerInventory().getItem(5).getNbt(), "minecraft:aqua_affinity") < 1; - boolean outOfWaterButNotOnGround = (!isInWater) && (!session.getPlayerEntity().isOnGround()); - boolean insideWaterNotOnGround = isInWater && !session.getPlayerEntity().isOnGround(); return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective, - toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, - outOfWaterButNotOnGround, insideWaterNotOnGround); + toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, session.getPlayerEntity().isOnGround()); } public static double getSessionBreakTime(GeyserSession session, BlockMapping blockMapping) { PlayerInventory inventory = session.getPlayerInventory(); GeyserItemStack item = inventory.getItemInHand(); - ItemMapping mapping; - CompoundTag nbtData; + ItemMapping mapping = ItemMapping.AIR; + CompoundTag nbtData = null; if (item != null) { mapping = item.getMapping(session); nbtData = item.getNbt(); - } else { - mapping = ItemMapping.AIR; - nbtData = new CompoundTag(""); } return getBreakTime(session, blockMapping, mapping, nbtData, true); }