mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-23 15:00:37 +01:00
Fix break time while submerged in water (#3110)
* Fix break time while submerged in water * Review stuff * LAYERS -> LEVELS
This commit is contained in:
parent
f2f894b1d1
commit
dc810f1d39
6 changed files with 67 additions and 45 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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;
|
||||
|
||||
|
@ -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
|
||||
Vector3d playerPosition = session.getCollisionManager().getPlayerBoundingBox().getBottomCenter();
|
||||
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());
|
||||
|
||||
// First triangle on the XZ plane
|
||||
|
@ -637,15 +637,4 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
}, 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();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue