Move block breaking to new system

This commit is contained in:
Camotoy 2024-05-17 14:50:21 -04:00
parent cbaa9cd2a0
commit a46332ace1
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
7 changed files with 1005 additions and 945 deletions

File diff suppressed because it is too large Load diff

View file

@ -41,10 +41,16 @@ public class Block {
public static final int JAVA_AIR_ID = 0; public static final int JAVA_AIR_ID = 0;
private final String javaIdentifier; private final String javaIdentifier;
private final boolean requiresCorrectToolForDrops;
private final boolean hasBlockEntity;
private final float destroyTime;
private int javaId = -1; private int javaId = -1;
public Block(String javaIdentifier, Builder builder) { public Block(String javaIdentifier, Builder builder) {
this.javaIdentifier = Identifier.formalize(javaIdentifier).intern(); this.javaIdentifier = Identifier.formalize(javaIdentifier).intern();
this.requiresCorrectToolForDrops = builder.requiresCorrectToolForDrops;
this.hasBlockEntity = builder.hasBlockEntity;
this.destroyTime = builder.destroyTime;
builder.build(this); builder.build(this);
} }
@ -52,6 +58,18 @@ public class Block {
return javaIdentifier; return javaIdentifier;
} }
public boolean requiresCorrectToolForDrops() {
return requiresCorrectToolForDrops;
}
public boolean hasBlockEntity() {
return hasBlockEntity;
}
public float destroyTime() {
return destroyTime;
}
public int javaId() { public int javaId() {
return javaId; return javaId;
} }
@ -77,6 +95,9 @@ public class Block {
public static final class Builder { public static final class Builder {
private final Map<Property<?>, List<Comparable<?>>> states = new LinkedHashMap<>(); private final Map<Property<?>, List<Comparable<?>>> states = new LinkedHashMap<>();
private boolean requiresCorrectToolForDrops = false;
private boolean hasBlockEntity = false;
private float destroyTime;
/** /**
* For states that we're just tracking for mirroring Java states. * For states that we're just tracking for mirroring Java states.
@ -107,6 +128,21 @@ public class Block {
return this; return this;
} }
public Builder requiresCorrectToolForDrops() {
this.requiresCorrectToolForDrops = true;
return this;
}
public Builder setBlockEntity() {
this.hasBlockEntity = true;
return this;
}
public Builder destroyTime(float destroyTime) {
this.destroyTime = destroyTime;
return this;
}
private void build(Block block) { private void build(Block block) {
if (states.isEmpty()) { if (states.isEmpty()) {
BlockRegistries.BLOCK_STATES.get().add(new BlockState(block, BlockRegistries.BLOCK_STATES.get().size())); BlockRegistries.BLOCK_STATES.get().add(new BlockState(block, BlockRegistries.BLOCK_STATES.get().size()));

View file

@ -51,6 +51,7 @@ import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState; import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.physics.PistonBehavior; import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.registry.type.BlockMapping;
@ -565,6 +566,15 @@ public final class BlockRegistryPopulator {
.isBlockEntity(javaBlockState.hasBlockEntity()) .isBlockEntity(javaBlockState.hasBlockEntity())
.build(); .build();
Block.Builder builder = Block.builder()
.destroyTime(javaBlockState.blockHardness());
if (!javaBlockState.canBreakWithHand()) {
builder.requiresCorrectToolForDrops();
}
if (javaBlockState.hasBlockEntity()) {
builder.setBlockEntity();
}
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier()); String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
String bedrockIdentifier = customBlockState.block().identifier(); String bedrockIdentifier = customBlockState.block().identifier();

View file

@ -25,15 +25,16 @@
package org.geysermc.geyser.session.cache; package org.geysermc.geyser.session.cache;
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntList;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag; import org.geysermc.geyser.session.cache.tags.BlockTag;
import org.geysermc.geyser.session.cache.tags.ItemTag; import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import java.util.EnumMap; import java.util.EnumMap;
@ -95,6 +96,17 @@ public final class TagCache {
} }
} }
/**
* @return true if the block tag is present and contains this block mapping's Java ID.
*/
public boolean is(BlockTag tag, Block block) {
IntList values = this.blocks.get(tag);
if (values != null) {
return values.contains(block.javaId());
}
return false;
}
/** /**
* @return true if the block tag is present and contains this block mapping's Java ID. * @return true if the block tag is present and contains this block mapping's Java ID.
*/ */

View file

@ -40,6 +40,7 @@ import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.registry.type.BlockMapping;
@ -160,7 +161,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
LevelEventPacket startBreak = new LevelEventPacket(); LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEvent.BLOCK_START_BREAK); startBreak.setType(LevelEvent.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat()); startBreak.setPosition(vector.toFloat());
double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.JAVA_BLOCKS.getOrDefault(blockState, BlockMapping.DEFAULT)) * 20; double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.BLOCK_STATES.getOrDefault(blockState, BlockState.of(Block.JAVA_AIR_ID)).block()) * 20;
// If the block is custom or the breaking item is custom, we must keep track of break time ourselves // If the block is custom or the breaking item is custom, we must keep track of break time ourselves
GeyserItemStack item = session.getPlayerInventory().getItemInHand(); GeyserItemStack item = session.getPlayerInventory().getItemInHand();
@ -212,7 +213,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
LevelEventPacket updateBreak = new LevelEventPacket(); LevelEventPacket updateBreak = new LevelEventPacket();
updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK); updateBreak.setType(LevelEvent.BLOCK_UPDATE_BREAK);
updateBreak.setPosition(vectorFloat); updateBreak.setPosition(vectorFloat);
double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.JAVA_BLOCKS.getOrDefault(breakingBlock, BlockMapping.DEFAULT)) * 20; double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.BLOCK_STATES.getOrDefault(breakingBlock, BlockState.of(Block.JAVA_AIR_ID)).block()) * 20;
// If the block is custom, we must keep track of when it should break ourselves // If the block is custom, we must keep track of when it should break ourselves

View file

@ -25,16 +25,17 @@
package org.geysermc.geyser.translator.protocol.java.level; package org.geysermc.geyser.translator.protocol.java.level;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockDestructionPacket;
import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.BlockUtils; import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundBlockDestructionPacket;
@Translator(packet = ClientboundBlockDestructionPacket.class) @Translator(packet = ClientboundBlockDestructionPacket.class)
public class JavaBlockDestructionTranslator extends PacketTranslator<ClientboundBlockDestructionPacket> { public class JavaBlockDestructionTranslator extends PacketTranslator<ClientboundBlockDestructionPacket> {
@ -42,7 +43,7 @@ public class JavaBlockDestructionTranslator extends PacketTranslator<Clientbound
@Override @Override
public void translate(GeyserSession session, ClientboundBlockDestructionPacket packet) { public void translate(GeyserSession session, ClientboundBlockDestructionPacket packet) {
int state = session.getGeyser().getWorldManager().getBlockAt(session, packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ()); int state = session.getGeyser().getWorldManager().getBlockAt(session, packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ());
int breakTime = (int) (65535 / Math.ceil(BlockUtils.getBreakTime(session, BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT), ItemMapping.AIR, null, false) * 20)); int breakTime = (int) (65535 / Math.ceil(BlockUtils.getBreakTime(session, BlockRegistries.BLOCK_STATES.getOrDefault(state, BlockState.of(Block.JAVA_AIR_ID)).block(), ItemMapping.AIR, null, false) * 20));
LevelEventPacket levelEventPacket = new LevelEventPacket(); LevelEventPacket levelEventPacket = new LevelEventPacket();
levelEventPacket.setPosition(packet.getPosition().toFloat()); levelEventPacket.setPosition(packet.getPosition().toFloat());
levelEventPacket.setType(LevelEvent.BLOCK_START_BREAK); levelEventPacket.setType(LevelEvent.BLOCK_START_BREAK);

View file

@ -30,9 +30,9 @@ import org.cloudburstmc.math.vector.Vector3i;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.PlayerInventory;
import org.geysermc.geyser.inventory.item.Enchantment; import org.geysermc.geyser.inventory.item.Enchantment;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag; import org.geysermc.geyser.session.cache.tags.BlockTag;
@ -41,14 +41,14 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponen
public final class BlockUtils { public final class BlockUtils {
private static boolean correctTool(GeyserSession session, BlockMapping blockMapping, String itemToolType) { private static boolean correctTool(GeyserSession session, Block block, String itemToolType) {
return switch (itemToolType) { return switch (itemToolType) {
case "axe" -> session.getTagCache().is(BlockTag.AXE_EFFECTIVE, blockMapping); case "axe" -> session.getTagCache().is(BlockTag.AXE_EFFECTIVE, block);
case "hoe" -> session.getTagCache().is(BlockTag.HOE_EFFECTIVE, blockMapping); case "hoe" -> session.getTagCache().is(BlockTag.HOE_EFFECTIVE, block);
case "pickaxe" -> session.getTagCache().is(BlockTag.PICKAXE_EFFECTIVE, blockMapping); case "pickaxe" -> session.getTagCache().is(BlockTag.PICKAXE_EFFECTIVE, block);
case "shears" -> session.getTagCache().is(BlockTag.LEAVES, blockMapping) || session.getTagCache().is(BlockTag.WOOL, blockMapping); case "shears" -> session.getTagCache().is(BlockTag.LEAVES, block) || session.getTagCache().is(BlockTag.WOOL, block);
case "shovel" -> session.getTagCache().is(BlockTag.SHOVEL_EFFECTIVE, blockMapping); case "shovel" -> session.getTagCache().is(BlockTag.SHOVEL_EFFECTIVE, block);
case "sword" -> blockMapping.getJavaBlockId() == BlockStateValues.JAVA_COBWEB_ID; case "sword" -> block == Blocks.COBWEB;
default -> { default -> {
session.getGeyser().getLogger().warning("Unknown tool type: " + itemToolType); session.getGeyser().getLogger().warning("Unknown tool type: " + itemToolType);
yield false; yield false;
@ -71,7 +71,7 @@ public final class BlockUtils {
}; };
} }
private static boolean canToolTierBreakBlock(GeyserSession session, BlockMapping blockMapping, String toolTier) { private static boolean canToolTierBreakBlock(GeyserSession session, Block block, String toolTier) {
if (toolTier.equals("netherite") || toolTier.equals("diamond")) { if (toolTier.equals("netherite") || toolTier.equals("diamond")) {
// As of 1.17, these tiers can mine everything that is mineable // As of 1.17, these tiers can mine everything that is mineable
return true; return true;
@ -80,15 +80,15 @@ public final class BlockUtils {
switch (toolTier) { switch (toolTier) {
// Use intentional fall-throughs to check each tier with this block // Use intentional fall-throughs to check each tier with this block
default: default:
if (session.getTagCache().is(BlockTag.NEEDS_STONE_TOOL, blockMapping)) { if (session.getTagCache().is(BlockTag.NEEDS_STONE_TOOL, block)) {
return false; return false;
} }
case "stone": case "stone":
if (session.getTagCache().is(BlockTag.NEEDS_IRON_TOOL, blockMapping)) { if (session.getTagCache().is(BlockTag.NEEDS_IRON_TOOL, block)) {
return false; return false;
} }
case "iron": case "iron":
if (session.getTagCache().is(BlockTag.NEEDS_DIAMOND_TOOL, blockMapping)) { if (session.getTagCache().is(BlockTag.NEEDS_DIAMOND_TOOL, block)) {
return false; return false;
} }
} }
@ -131,9 +131,9 @@ public final class BlockUtils {
return 1.0 / speed; return 1.0 / speed;
} }
public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemMapping item, @Nullable DataComponents components, boolean isSessionPlayer) { public static double getBreakTime(GeyserSession session, Block block, ItemMapping item, @Nullable DataComponents components, boolean isSessionPlayer) {
boolean isShearsEffective = session.getTagCache().is(BlockTag.LEAVES, blockMapping) || session.getTagCache().is(BlockTag.WOOL, blockMapping); //TODO called twice boolean isShearsEffective = session.getTagCache().is(BlockTag.LEAVES, block) || session.getTagCache().is(BlockTag.WOOL, block); //TODO called twice
boolean canHarvestWithHand = blockMapping.isCanBreakWithHand(); boolean canHarvestWithHand = !block.requiresCorrectToolForDrops();
String toolType = ""; String toolType = "";
String toolTier = ""; String toolTier = "";
boolean correctTool = false; boolean correctTool = false;
@ -141,8 +141,8 @@ public final class BlockUtils {
if (item.isTool()) { if (item.isTool()) {
toolType = item.getToolType(); toolType = item.getToolType();
toolTier = item.getToolTier(); toolTier = item.getToolTier();
correctTool = correctTool(session, blockMapping, toolType); correctTool = correctTool(session, block, toolType);
toolCanBreak = canToolTierBreakBlock(session, blockMapping, toolTier); toolCanBreak = canToolTierBreakBlock(session, block, toolTier);
} }
int toolEfficiencyLevel = ItemUtils.getEnchantmentLevel(components, Enchantment.JavaEnchantment.EFFICIENCY); int toolEfficiencyLevel = ItemUtils.getEnchantmentLevel(components, Enchantment.JavaEnchantment.EFFICIENCY);
@ -151,7 +151,7 @@ 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(block.destroyTime(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective,
toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, true); toolEfficiencyLevel, hasteLevel, miningFatigueLevel, false, true);
} }
@ -162,11 +162,11 @@ public final class BlockUtils {
boolean insideOfWaterWithoutAquaAffinity = waterInEyes && boolean insideOfWaterWithoutAquaAffinity = waterInEyes &&
ItemUtils.getEnchantmentLevel(session.getPlayerInventory().getItem(5).getComponents(), Enchantment.JavaEnchantment.AQUA_AFFINITY) < 1; ItemUtils.getEnchantmentLevel(session.getPlayerInventory().getItem(5).getComponents(), Enchantment.JavaEnchantment.AQUA_AFFINITY) < 1;
return calculateBreakTime(blockMapping.getHardness(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective, return calculateBreakTime(block.destroyTime(), toolTier, canHarvestWithHand, correctTool, toolCanBreak, toolType, isShearsEffective,
toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, session.getPlayerEntity().isOnGround()); toolEfficiencyLevel, hasteLevel, miningFatigueLevel, insideOfWaterWithoutAquaAffinity, session.getPlayerEntity().isOnGround());
} }
public static double getSessionBreakTime(GeyserSession session, BlockMapping blockMapping) { public static double getSessionBreakTime(GeyserSession session, Block block) {
PlayerInventory inventory = session.getPlayerInventory(); PlayerInventory inventory = session.getPlayerInventory();
GeyserItemStack item = inventory.getItemInHand(); GeyserItemStack item = inventory.getItemInHand();
ItemMapping mapping = ItemMapping.AIR; ItemMapping mapping = ItemMapping.AIR;
@ -175,7 +175,7 @@ public final class BlockUtils {
mapping = item.getMapping(session); mapping = item.getMapping(session);
components = item.getComponents(); components = item.getComponents();
} }
return getBreakTime(session, blockMapping, mapping, components, true); return getBreakTime(session, block, mapping, components, true);
} }
/** /**