Fix break time while submerged in water (#3110)

* Fix break time while submerged in water

* Review stuff

* LAYERS -> LEVELS
This commit is contained in:
David Choo 2022-07-02 21:17:14 -04:00 committed by GitHub
parent f2f894b1d1
commit dc810f1d39
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 67 additions and 45 deletions

View file

@ -99,21 +99,11 @@ public class FishingHookEntity extends ThrowableEntity {
} }
} }
int waterLevel = BlockStateValues.getWaterLevel(blockID); double waterHeight = BlockStateValues.getWaterHeight(blockID);
if (BlockRegistries.WATERLOGGED.get().contains(blockID)) { if (waterHeight != -1 && position.getY() <= (iter.getY() + waterHeight)) {
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; touchingWater = true;
} }
} }
}
if (!inWater && touchingWater) { if (!inWater && touchingWater) {
sendSplashSound(session); sendSplashSound(session);

View file

@ -77,6 +77,8 @@ public final class BlockStateValues {
public static int JAVA_SPAWNER_ID; public static int JAVA_SPAWNER_ID;
public static int JAVA_WATER_ID; public static int JAVA_WATER_ID;
public static final int NUM_WATER_LEVELS = 9;
/** /**
* Determines if the block state contains Bedrock block information * 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. * 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 * @param state BlockState of the block
* @return The water level or -1 if the block isn't water * @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); 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. * Get the slipperiness of a block.
* This is used in ItemEntity to calculate the friction on an item as it slides across the ground * This is used in ItemEntity to calculate the friction on an item as it slides across the ground

View file

@ -25,6 +25,7 @@
package org.geysermc.geyser.level.physics; package org.geysermc.geyser.level.physics;
import com.nukkitx.math.GenericMath;
import com.nukkitx.math.vector.Vector3d; import com.nukkitx.math.vector.Vector3d;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; 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; 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 * Updates scaffolding entity flags
* Scaffolding needs to be checked per-move since it's a flag in Bedrock but Java does it client-side * Scaffolding needs to be checked per-move since it's a flag in Bedrock but Java does it client-side

View file

@ -100,6 +100,7 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.connection.GeyserConnection; import org.geysermc.geyser.api.connection.GeyserConnection;
import org.geysermc.geyser.command.CommandSender; import org.geysermc.geyser.command.CommandSender;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.ItemFrameEntity;
@ -1767,6 +1768,17 @@ public class GeyserSession implements GeyserConnection, CommandSender {
sendUpstreamPacket(packet); 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() { public MinecraftCodecHelper getCodecHelper() {
return (MinecraftCodecHelper) this.downstream.getCodecHelper(); return (MinecraftCodecHelper) this.downstream.getCodecHelper();
} }

View file

@ -201,7 +201,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
// CraftBukkit+ check - see https://github.com/PaperMC/Paper/blob/458db6206daae76327a64f4e2a17b67a7e38b426/Spigot-Server-Patches/0532-Move-range-check-for-block-placing-up.patch // CraftBukkit+ check - see https://github.com/PaperMC/Paper/blob/458db6206daae76327a64f4e2a17b67a7e38b426/Spigot-Server-Patches/0532-Move-range-check-for-block-placing-up.patch
Vector3f playerPosition = session.getPlayerEntity().getPosition(); Vector3f playerPosition = session.getPlayerEntity().getPosition();
playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - getEyeHeight(session)); playerPosition = playerPosition.down(EntityDefinitions.PLAYER.offset() - session.getEyeHeight());
boolean creative = session.getGameMode() == GameMode.CREATIVE; boolean creative = session.getGameMode() == GameMode.CREATIVE;
@ -608,7 +608,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
// Use the bounding box's position since we need the player's position seen by the Java server // Use the bounding box's position since we need the player's position seen by the Java server
Vector3d playerPosition = session.getCollisionManager().getPlayerBoundingBox().getBottomCenter(); Vector3d playerPosition = session.getCollisionManager().getPlayerBoundingBox().getBottomCenter();
float xDiff = (float) (target.getX() - playerPosition.getX()); float xDiff = (float) (target.getX() - playerPosition.getX());
float yDiff = (float) (target.getY() - (playerPosition.getY() + getEyeHeight(session))); float yDiff = (float) (target.getY() - (playerPosition.getY() + session.getEyeHeight()));
float zDiff = (float) (target.getZ() - playerPosition.getZ()); float zDiff = (float) (target.getZ() - playerPosition.getZ());
// First triangle on the XZ plane // First triangle on the XZ plane
@ -637,15 +637,4 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
}, 150, TimeUnit.MILLISECONDS)); }, 150, TimeUnit.MILLISECONDS));
} }
} }
private float getEyeHeight(GeyserSession session) {
return switch (session.getPose()) {
case SNEAKING -> 1.27f;
case SWIMMING,
FALL_FLYING, // Elytra
SPIN_ATTACK -> 0.4f; // Trident spin attack
case SLEEPING -> 0.2f;
default -> EntityDefinitions.PLAYER.offset();
};
}
} }

View file

@ -36,6 +36,8 @@ import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.collision.BlockCollision; import org.geysermc.geyser.translator.collision.BlockCollision;
import javax.annotation.Nullable;
public final class BlockUtils { public final class BlockUtils {
private static boolean correctTool(GeyserSession session, BlockMapping blockMapping, String itemToolType) { private static boolean correctTool(GeyserSession session, BlockMapping blockMapping, String itemToolType) {
@ -101,7 +103,7 @@ public final class BlockUtils {
// https://minecraft.gamepedia.com/Breaking // https://minecraft.gamepedia.com/Breaking
private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool, boolean canTierMineBlock, private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool, boolean canTierMineBlock,
String toolType, boolean isShearsEffective, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel, 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 baseTime = (((correctTool && canTierMineBlock) || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness;
double speed = 1.0 / baseTime; double speed = 1.0 / baseTime;
@ -129,12 +131,11 @@ public final class BlockUtils {
} }
if (insideOfWaterWithoutAquaAffinity) speed *= 0.2; if (insideOfWaterWithoutAquaAffinity) speed *= 0.2;
if (outOfWaterButNotOnGround) speed *= 0.2; if (!onGround) speed *= 0.2;
if (insideWaterAndNotOnGround) speed *= 0.2;
return 1.0 / speed; 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 isShearsEffective = session.getTagCache().isShearsEffective(blockMapping); //TODO called twice
boolean canHarvestWithHand = blockMapping.isCanBreakWithHand(); boolean canHarvestWithHand = blockMapping.isCanBreakWithHand();
String toolType = ""; String toolType = "";
@ -154,36 +155,28 @@ public final class BlockUtils {
if (!isSessionPlayer) { if (!isSessionPlayer) {
// Another entity is currently mining; we have all the information we know // Another entity is currently mining; we have all the information we know
return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective, return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective,
toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, true);
false, false);
} }
hasteLevel = Math.max(session.getEffectCache().getHaste(), session.getEffectCache().getConduitPower()); hasteLevel = Math.max(session.getEffectCache().getHaste(), session.getEffectCache().getConduitPower());
miningFatigueLevel = session.getEffectCache().getMiningFatigue(); miningFatigueLevel = session.getEffectCache().getMiningFatigue();
boolean isInWater = session.getCollisionManager().isPlayerInWater(); boolean waterInEyes = session.getCollisionManager().isWaterInEyes();
boolean insideOfWaterWithoutAquaAffinity = waterInEyes &&
boolean insideOfWaterWithoutAquaAffinity = isInWater &&
ItemUtils.getEnchantmentLevel(session.getPlayerInventory().getItem(5).getNbt(), "minecraft:aqua_affinity") < 1; 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, return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective,
toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, session.getPlayerEntity().isOnGround());
outOfWaterButNotOnGround, insideWaterNotOnGround);
} }
public static double getSessionBreakTime(GeyserSession session, BlockMapping blockMapping) { public static double getSessionBreakTime(GeyserSession session, BlockMapping blockMapping) {
PlayerInventory inventory = session.getPlayerInventory(); PlayerInventory inventory = session.getPlayerInventory();
GeyserItemStack item = inventory.getItemInHand(); GeyserItemStack item = inventory.getItemInHand();
ItemMapping mapping; ItemMapping mapping = ItemMapping.AIR;
CompoundTag nbtData; CompoundTag nbtData = null;
if (item != null) { if (item != null) {
mapping = item.getMapping(session); mapping = item.getMapping(session);
nbtData = item.getNbt(); nbtData = item.getNbt();
} else {
mapping = ItemMapping.AIR;
nbtData = new CompoundTag("");
} }
return getBreakTime(session, blockMapping, mapping, nbtData, true); return getBreakTime(session, blockMapping, mapping, nbtData, true);
} }