From d85549c38dd404ade6aa364990c53548f8f4fea3 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sun, 19 May 2024 20:24:19 -0400 Subject: [PATCH] BlockMapping is removed --- .../java/org/geysermc/geyser/GeyserImpl.java | 11 - .../geyser/entity/type/ItemEntity.java | 3 +- .../type/living/merchant/VillagerEntity.java | 39 ++-- .../entity/type/player/SkullPlayerEntity.java | 26 ++- .../geysermc/geyser/inventory/Container.java | 5 +- .../geyser/inventory/Generic3X3Container.java | 8 +- .../holder/BlockInventoryHolder.java | 2 +- .../geysermc/geyser/level/WorldManager.java | 6 +- .../geyser/level/block/BlockStateValues.java | 220 ++---------------- .../geysermc/geyser/level/block/Blocks.java | 82 +++---- .../geyser/level/block/DoubleChestValue.java | 40 ---- .../geyser/level/block/type/Block.java | 37 ++- .../geyser/level/block/type/BlockState.java | 10 +- .../level/block/type/FlowerPotBlock.java | 16 +- .../geyser/level/block/type/SkullBlock.java | 46 +++- .../block/type/WallSkullBlock.java} | 34 +-- .../geyser/registry/BlockRegistries.java | 15 +- .../populator/BlockRegistryPopulator.java | 176 ++++++-------- .../geyser/registry/type/BlockMappings.java | 3 +- .../geyser/session/cache/SkullCache.java | 27 ++- .../chest/DoubleChestInventoryTranslator.java | 28 +-- .../entity/SkullBlockEntityTranslator.java | 15 +- .../BedrockBlockPickRequestTranslator.java | 16 +- ...BedrockInventoryTransactionTranslator.java | 23 +- .../player/BedrockActionTranslator.java | 28 +-- .../BedrockLevelSoundEventTranslator.java | 15 +- .../level/JavaBlockEntityDataTranslator.java | 2 +- .../java/level/JavaBlockUpdateTranslator.java | 10 +- .../JavaLevelChunkWithLightTranslator.java | 5 +- .../geysermc/geyser/util/StatisticsUtils.java | 2 +- .../util/collection/FixedInt2BooleanMap.java | 120 ---------- .../util/collection/FixedInt2ByteMap.java | 121 ---------- .../util/collection/FixedInt2IntMap.java | 120 ---------- .../util/collection/LecternHasBookMap.java | 75 ------ .../geyser/util/collection/package-info.java | 34 --- core/src/main/resources/mappings | 2 +- .../collection/GeyserCollectionsTest.java | 181 -------------- 37 files changed, 378 insertions(+), 1225 deletions(-) delete mode 100644 core/src/main/java/org/geysermc/geyser/level/block/DoubleChestValue.java rename core/src/main/java/org/geysermc/geyser/{registry/type/BlockMapping.java => level/block/type/WallSkullBlock.java} (58%) delete mode 100644 core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2BooleanMap.java delete mode 100644 core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2ByteMap.java delete mode 100644 core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2IntMap.java delete mode 100644 core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java delete mode 100644 core/src/main/java/org/geysermc/geyser/util/collection/package-info.java delete mode 100644 core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 7be1fe15b..a60a14ea0 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -256,17 +256,6 @@ public class GeyserImpl implements GeyserApi { } VersionCheckUtils.checkForOutdatedJava(logger); - - for (int i = 0; i < BlockRegistries.JAVA_BLOCKS.get().length; i++) { - String oldIdentifier = BlockRegistries.JAVA_BLOCKS.get(i).getJavaIdentifier(); - String newIdentifier = BlockRegistries.BLOCK_STATES.get(i).toString(); - if (!oldIdentifier.equals(newIdentifier)) { - System.out.println("Check block " + oldIdentifier + " " + newIdentifier); - break; - } - } - System.out.println(BlockRegistries.JAVA_BLOCKS.get().length); - System.out.println(BlockRegistries.BLOCK_STATES.get().size()); } private void startInstance() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java index ead717b34..49eb9ddc4 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemEntity.java @@ -34,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AddItemEntityPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; @@ -137,7 +138,7 @@ public class ItemEntity extends ThrowableEntity { protected float getDrag() { if (isOnGround()) { Vector3i groundBlockPos = position.toInt().down(1); - int blockState = session.getGeyser().getWorldManager().getBlockAt(session, groundBlockPos); + BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, groundBlockPos); return BlockStateValues.getSlipperiness(blockState) * 0.98f; } return 0.98f; diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/merchant/VillagerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/merchant/VillagerEntity.java index 3c2160a2b..fb07572e6 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/merchant/VillagerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/merchant/VillagerEntity.java @@ -33,8 +33,8 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; import org.geysermc.geyser.entity.EntityDefinition; -import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.registry.type.BlockMapping; +import org.geysermc.geyser.level.block.property.Properties; +import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.VillagerData; @@ -119,28 +119,29 @@ public class VillagerEntity extends AbstractMerchantEntity { } // The bed block - int blockId = session.getGeyser().getWorldManager().getBlockAt(session, bedPosition); - String fullIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockId, BlockMapping.DEFAULT).getJavaIdentifier(); + BlockState state = session.getGeyser().getWorldManager().blockAt(session, bedPosition); // Set the correct position offset and rotation when sleeping int bedRotation = 0; float xOffset = 0; float zOffset = 0; - if (fullIdentifier.contains("facing=south")) { - // bed is facing south - bedRotation = 180; - zOffset = -.5f; - } else if (fullIdentifier.contains("facing=east")) { - // bed is facing east - bedRotation = 90; - xOffset = -.5f; - } else if (fullIdentifier.contains("facing=west")) { - // bed is facing west - bedRotation = 270; - xOffset = .5f; - } else if (fullIdentifier.contains("facing=north")) { - // rotation does not change because north is 0 - zOffset = .5f; + switch (state.getValue(Properties.HORIZONTAL_FACING)) { + case SOUTH -> { + bedRotation = 180; + zOffset = -.5f; + } + case EAST -> { + bedRotation = 90; + xOffset = -.5f; + } + case WEST -> { + bedRotation = 270; + xOffset = .5f; + } + case NORTH -> { + // rotation does not change because north is 0 + zOffset = .5f; + } } setYaw(yaw); diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java index 89f9c37d6..f2f93b266 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SkullPlayerEntity.java @@ -34,7 +34,10 @@ import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; -import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.block.property.Properties; +import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.level.block.type.WallSkullBlock; +import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.SkullCache; import org.geysermc.geyser.skin.SkullSkinManager; @@ -137,20 +140,19 @@ public class SkullPlayerEntity extends PlayerEntity { float z = skull.getPosition().getZ() + .5f; float rotation; - int blockState = skull.getBlockState(); - byte floorRotation = BlockStateValues.getSkullRotation(blockState); - if (floorRotation == -1) { - // Wall skull + BlockState blockState = skull.getBlockState(); + if (blockState.block() instanceof WallSkullBlock) { y += 0.25f; - rotation = BlockStateValues.getSkullWallDirections().get(blockState); - switch ((int) rotation) { - case 180 -> z += 0.24f; // North - case 0 -> z -= 0.24f; // South - case 90 -> x += 0.24f; // West - case 270 -> x -= 0.24f; // East + Direction direction = blockState.getValue(Properties.HORIZONTAL_FACING); + rotation = WallSkullBlock.getDegrees(direction); + switch (direction) { + case NORTH -> z += 0.24f; + case SOUTH -> z -= 0.24f; + case WEST -> x += 0.24f; + case EAST -> x -= 0.24f; } } else { - rotation = (180f + (floorRotation * 22.5f)) % 360; + rotation = (180f + (blockState.getValue(Properties.ROTATION_16) * 22.5f)) % 360; } moveAbsolute(Vector3f.from(x, y, z), rotation, 0, rotation, true, true); diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Container.java b/core/src/main/java/org/geysermc/geyser/inventory/Container.java index 209aeb24f..81818613f 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Container.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Container.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.inventory; +import org.geysermc.geyser.level.block.type.Block; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; @@ -83,9 +84,9 @@ public class Container extends Inventory { * Will be overwritten for droppers. * * @param usingRealBlock whether this container is using a real container or not - * @param javaBlockId the Java block string of the block, if real + * @param block the Java block, if real */ - public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) { + public void setUsingRealBlock(boolean usingRealBlock, Block block) { isUsingRealBlock = usingRealBlock; } } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/Generic3X3Container.java b/core/src/main/java/org/geysermc/geyser/inventory/Generic3X3Container.java index 65e47d877..0b14d1105 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/Generic3X3Container.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/Generic3X3Container.java @@ -26,6 +26,8 @@ package org.geysermc.geyser.inventory; import lombok.Getter; +import org.geysermc.geyser.level.block.Blocks; +import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.inventory.Generic3X3InventoryTranslator; import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType; @@ -44,10 +46,10 @@ public class Generic3X3Container extends Container { } @Override - public void setUsingRealBlock(boolean usingRealBlock, String javaBlockId) { - super.setUsingRealBlock(usingRealBlock, javaBlockId); + public void setUsingRealBlock(boolean usingRealBlock, Block block) { + super.setUsingRealBlock(usingRealBlock, block); if (usingRealBlock) { - isDropper = javaBlockId.startsWith("minecraft:dropper"); + isDropper = block == Blocks.DROPPER; } } } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java index 686fe39ad..0a51d43ef 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/holder/BlockInventoryHolder.java @@ -90,7 +90,7 @@ public class BlockInventoryHolder extends InventoryHolder { if (isValidBlock(javaBlockString)) { // We can safely use this block inventory.setHolderPosition(session.getLastInteractionBlockPosition()); - ((Container) inventory).setUsingRealBlock(true, javaBlockString[0]); + ((Container) inventory).setUsingRealBlock(true, state.block()); setCustomName(session, session.getLastInteractionBlockPosition(), inventory, state); return true; diff --git a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java index 5edce21dc..6cd9c3e26 100644 --- a/core/src/main/java/org/geysermc/geyser/level/WorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/WorldManager.java @@ -59,10 +59,12 @@ import java.util.function.Function; */ public abstract class WorldManager { - public final BlockState blockAt(GeyserSession session, Vector3i vector3i) { - return BlockState.of(this.getBlockAt(session, vector3i)); + @NonNull + public final BlockState blockAt(GeyserSession session, Vector3i vector) { + return this.blockAt(session, vector.getX(), vector.getY(), vector.getZ()); } + @NonNull public BlockState blockAt(GeyserSession session, int x, int y, int z) { return BlockState.of(this.getBlockAt(session, x, y, z)); } 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 9b390f6ee..8bc9cf1e2 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 @@ -25,7 +25,6 @@ package org.geysermc.geyser.level.block; -import com.fasterxml.jackson.databind.JsonNode; import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -36,10 +35,6 @@ import org.geysermc.geyser.level.block.type.PistonBlock; import org.geysermc.geyser.level.physics.Direction; import org.geysermc.geyser.level.physics.PistonBehavior; import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.registry.type.BlockMapping; -import org.geysermc.geyser.util.collection.FixedInt2ByteMap; -import org.geysermc.geyser.util.collection.FixedInt2IntMap; -import org.geysermc.geyser.util.collection.LecternHasBookMap; import java.util.Locale; @@ -47,35 +42,13 @@ import java.util.Locale; * Used for block entities if the Java block state contains Bedrock block information. */ public final class BlockStateValues { - private static final IntSet ALL_CAULDRONS = new IntOpenHashSet(); - private static final Int2IntMap BANNER_COLORS = new FixedInt2IntMap(); - private static final Int2ByteMap BED_COLORS = new FixedInt2ByteMap(); - private static final Int2IntMap BRUSH_PROGRESS = new Int2IntOpenHashMap(); - private static final Int2ObjectMap DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>(); - private static final Int2ObjectMap FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>(); private static final IntSet HORIZONTAL_FACING_JIGSAWS = new IntOpenHashSet(); - private static final LecternHasBookMap LECTERN_BOOK_STATES = new LecternHasBookMap(); - private static final IntSet NON_WATER_CAULDRONS = new IntOpenHashSet(); - private static final Int2IntMap NOTEBLOCK_PITCHES = new FixedInt2IntMap(); - private static final Int2BooleanMap PISTON_VALUES = new Int2BooleanOpenHashMap(); private static final IntSet STICKY_PISTONS = new IntOpenHashSet(); private static final Object2IntMap PISTON_HEADS = new Object2IntOpenHashMap<>(); private static final Int2ObjectMap PISTON_ORIENTATION = new Int2ObjectOpenHashMap<>(); private static final IntSet ALL_PISTON_HEADS = new IntOpenHashSet(); - private static final IntSet MOVING_PISTONS = new IntOpenHashSet(); - private static final Int2ByteMap SKULL_VARIANTS = new FixedInt2ByteMap(); - private static final IntSet SKULL_POWERED = new IntOpenHashSet(); - private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap(); - private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap(); - private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new FixedInt2ByteMap(); private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap(); - private static final IntSet UPPER_DOORS = new IntOpenHashSet(); - public static int JAVA_FURNACE_ID; - public static int JAVA_FURNACE_LIT_ID; - public static int JAVA_HONEY_BLOCK_ID; - public static int JAVA_SLIME_BLOCK_ID; - public static int JAVA_SPAWNER_ID; public static int JAVA_WATER_ID; public static final int NUM_WATER_LEVELS = 9; @@ -85,66 +58,9 @@ public final class BlockStateValues { * * @param javaId The Java Identifier of the block * @param javaBlockState the Java Block State of the block - * @param blockData JsonNode of info about the block from blocks.json */ - public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) { - JsonNode bannerColor = blockData.get("banner_color"); - if (bannerColor != null) { - BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); - return; // There will never be a banner color and a skull variant - } - - JsonNode bedColor = blockData.get("bed_color"); - if (bedColor != null) { - BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); - return; - } - - JsonNode bedrockStates = blockData.get("bedrock_states"); - if (bedrockStates != null) { - JsonNode brushedProgress = bedrockStates.get("brushed_progress"); - if (brushedProgress != null) { - BRUSH_PROGRESS.put(javaBlockState, brushedProgress.intValue()); - return; - } - } - - if (blockData.get("double_chest_position") != null) { - boolean isX = (blockData.get("x") != null); - boolean isDirectionPositive = ((blockData.get("x") != null && blockData.get("x").asBoolean()) || - (blockData.get("z") != null && blockData.get("z").asBoolean())); - boolean isLeft = (blockData.get("double_chest_position").asText().contains("left")); - DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft)); - return; - } - - if (javaId.startsWith("minecraft:potted_") || javaId.equals("minecraft:flower_pot")) { - String name = javaId.replace("potted_", ""); - if (name.contains("azalea")) { - // Exception to the rule - name = name.replace("_bush", ""); - } - FLOWER_POT_VALUES.put(javaBlockState, name); - return; - } - - if (javaId.startsWith("minecraft:lectern")) { - LECTERN_BOOK_STATES.put(javaBlockState, javaId.contains("has_book=true")); - return; - } - - JsonNode notePitch = blockData.get("note_pitch"); - if (notePitch != null) { - NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue()); - return; - } - + public static void storeBlockStateValues(String javaId, int javaBlockState) { if (javaId.contains("piston[")) { // minecraft:moving_piston, minecraft:sticky_piston, minecraft:piston - if (javaId.startsWith("minecraft:moving_piston")) { - MOVING_PISTONS.add(javaBlockState); - } else { - PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true")); - } if (javaId.contains("sticky")) { STICKY_PISTONS.add(javaBlockState); } @@ -158,40 +74,6 @@ public final class BlockStateValues { return; } - JsonNode skullVariation = blockData.get("variation"); - if (skullVariation != null) { - SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); - } - - JsonNode skullRotation = blockData.get("skull_rotation"); - if (skullRotation != null) { - SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); - } - - if (javaId.startsWith("minecraft:dragon_head[") || javaId.startsWith("minecraft:piglin_head[") - || javaId.startsWith("minecraft:dragon_wall_head[") || javaId.startsWith("minecraft:piglin_wall_head[")) { - if (javaId.contains("powered=true")) { - SKULL_POWERED.add(javaBlockState); - } - } - - if (javaId.contains("wall_skull") || javaId.contains("wall_head")) { - String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7, javaId.lastIndexOf("powered=") - 1); - int rotation = switch (direction) { - case "north" -> 180; - case "west" -> 90; - case "east" -> 270; - default -> 0; // Also south - }; - SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation); - } - - JsonNode shulkerDirection = blockData.get("shulker_direction"); - if (shulkerDirection != null) { - BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue()); - return; - } - if (javaId.startsWith("minecraft:water") && !javaId.contains("cauldron")) { String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1); int level = Integer.parseInt(strLevel); @@ -205,48 +87,7 @@ public final class BlockStateValues { if (direction.isHorizontal()) { HORIZONTAL_FACING_JIGSAWS.add(javaBlockState); } - return; } - - if (javaId.contains("cauldron")) { - ALL_CAULDRONS.add(javaBlockState); - } - if (javaId.contains("_cauldron") && !javaId.contains("water_")) { - NON_WATER_CAULDRONS.add(javaBlockState); - } - - if (javaId.contains("_door[") && javaId.contains("half=upper")) { - UPPER_DOORS.add(javaBlockState); - } - } - - /** - * Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. - * This gives an integer color that Bedrock can use. - * - * @param state BlockState of the block - * @return Banner color integer or -1 if no color - */ - public static int getBannerColor(int state) { - return BANNER_COLORS.getOrDefault(state, -1); - } - - /** - * @return if this Java block state is a non-empty non-water cauldron - */ - public static boolean isNonWaterCauldron(int state) { - return NON_WATER_CAULDRONS.contains(state); - } - - /** - * Cauldrons (since Bedrock 1.18.30) must have a block entity packet sent on chunk load to fix rendering issues. - *

- * When using a bucket on a cauldron sending a ServerboundUseItemPacket can result in the liquid being placed. - * - * @return if this Java block state is a cauldron - */ - public static boolean isCauldron(int state) { - return ALL_CAULDRONS.contains(state); } /** @@ -256,13 +97,6 @@ public final class BlockStateValues { return HORIZONTAL_FACING_JIGSAWS; } - /** - * @return the lectern book state map pointing to book present state - */ - public static LecternHasBookMap getLecternBookStates() { - return LECTERN_BOOK_STATES; - } - public static boolean isStickyPiston(int blockState) { return STICKY_PISTONS.contains(blockState); } @@ -354,38 +188,6 @@ public final class BlockStateValues { }; } - /** - * Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. - * This gives a byte variant ID that Bedrock can use. - * - * @param state BlockState of the block - * @return Skull variant byte or -1 if no variant - */ - public static byte getSkullVariant(int state) { - return SKULL_VARIANTS.getOrDefault(state, (byte) -1); - } - - /** - * Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. - * This gives a byte rotation that Bedrock can use. - * - * @param state BlockState of the block - * @return Skull rotation value or -1 if no value - */ - public static byte getSkullRotation(int state) { - return SKULL_ROTATIONS.getOrDefault(state, (byte) -1); - } - - /** - * Skull rotations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. - * This gives a integer rotation that Bedrock can use. - * - * @return Skull wall rotation value with the blockstate - */ - public static Int2IntMap getSkullWallDirections() { - return SKULL_WALL_DIRECTIONS; - } - /** * Get the level of water from the block state. * @@ -427,14 +229,18 @@ public final class BlockStateValues { * @param state BlockState of the block * @return The block's slipperiness */ - public static float getSlipperiness(int state) { - String blockIdentifier = BlockRegistries.JAVA_BLOCKS.getOrDefault(state, BlockMapping.DEFAULT).getJavaIdentifier(); - return switch (blockIdentifier) { - case "minecraft:slime_block" -> 0.8f; - case "minecraft:ice", "minecraft:packed_ice" -> 0.98f; - case "minecraft:blue_ice" -> 0.989f; - default -> 0.6f; - }; + public static float getSlipperiness(BlockState state) { + Block block = state.block(); + if (block == Blocks.SLIME_BLOCK) { + return 0.8f; + } + if (block == Blocks.ICE || block == Blocks.PACKED_ICE) { + return 0.98f; + } + if (block == Blocks.BLUE_ICE) { + return 0.989f; + } + return 0.6f; } private static Direction getBlockDirection(String javaId) { diff --git a/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java index 485d52161..178887754 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/Blocks.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.level.block; +import org.geysermc.geyser.item.Items; import org.geysermc.geyser.level.block.property.ChestType; import org.geysermc.geyser.level.block.type.*; import org.geysermc.geyser.level.physics.Axis; @@ -308,7 +309,7 @@ public final class Blocks { public static final Block FERN = register(new Block("fern", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block DEAD_BUSH = register(new Block("dead_bush", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block SEAGRASS = register(new Block("seagrass", builder().pushReaction(PistonBehavior.DESTROY))); - public static final Block TALL_SEAGRASS = register(new Block("tall_seagrass", builder().pushReaction(PistonBehavior.DESTROY) + public static final Block TALL_SEAGRASS = register(new Block("tall_seagrass", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.SEAGRASS) .enumState(DOUBLE_BLOCK_HALF, "upper", "lower"))); public static final Block PISTON = register(new PistonBlock("piston", builder().destroyTime(1.5f).pushReaction(PistonBehavior.BLOCK) .booleanState(EXTENDED) @@ -758,9 +759,9 @@ public final class Blocks { .booleanState(WEST))); public static final Block PUMPKIN = register(new Block("pumpkin", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY))); public static final Block MELON = register(new Block("melon", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY))); - public static final Block ATTACHED_PUMPKIN_STEM = register(new Block("attached_pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY) + public static final Block ATTACHED_PUMPKIN_STEM = register(new Block("attached_pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.PUMPKIN_SEEDS) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); - public static final Block ATTACHED_MELON_STEM = register(new Block("attached_melon_stem", builder().pushReaction(PistonBehavior.DESTROY) + public static final Block ATTACHED_MELON_STEM = register(new Block("attached_melon_stem", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.MELON_SEEDS) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST))); public static final Block PUMPKIN_STEM = register(new Block("pumpkin_stem", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_7, 0, 7))); @@ -963,46 +964,46 @@ public final class Blocks { .enumState(ATTACH_FACE, "floor", "wall", "ceiling") .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block SKELETON_SKULL = register(new SkullBlock("skeleton_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block SKELETON_SKULL = register(new SkullBlock("skeleton_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block SKELETON_WALL_SKULL = register(new SkullBlock("skeleton_wall_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block SKELETON_WALL_SKULL = register(new WallSkullBlock("skeleton_wall_skull", SkullBlock.Type.SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block WITHER_SKELETON_SKULL = register(new SkullBlock("wither_skeleton_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block WITHER_SKELETON_SKULL = register(new SkullBlock("wither_skeleton_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block WITHER_SKELETON_WALL_SKULL = register(new SkullBlock("wither_skeleton_wall_skull", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block WITHER_SKELETON_WALL_SKULL = register(new WallSkullBlock("wither_skeleton_wall_skull", SkullBlock.Type.WITHER_SKELETON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block ZOMBIE_HEAD = register(new SkullBlock("zombie_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block ZOMBIE_HEAD = register(new SkullBlock("zombie_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block ZOMBIE_WALL_HEAD = register(new SkullBlock("zombie_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block ZOMBIE_WALL_HEAD = register(new WallSkullBlock("zombie_wall_head", SkullBlock.Type.ZOMBIE, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block PLAYER_HEAD = register(new SkullBlock("player_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block PLAYER_HEAD = register(new SkullBlock("player_head", SkullBlock.Type.PLAYER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block PLAYER_WALL_HEAD = register(new SkullBlock("player_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block PLAYER_WALL_HEAD = register(new WallSkullBlock("player_wall_head", SkullBlock.Type.PLAYER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block CREEPER_HEAD = register(new SkullBlock("creeper_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block CREEPER_HEAD = register(new SkullBlock("creeper_head", SkullBlock.Type.CREEPER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block CREEPER_WALL_HEAD = register(new SkullBlock("creeper_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block CREEPER_WALL_HEAD = register(new WallSkullBlock("creeper_wall_head", SkullBlock.Type.CREEPER, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block DRAGON_HEAD = register(new SkullBlock("dragon_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block DRAGON_HEAD = register(new SkullBlock("dragon_head", SkullBlock.Type.DRAGON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block DRAGON_WALL_HEAD = register(new SkullBlock("dragon_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block DRAGON_WALL_HEAD = register(new WallSkullBlock("dragon_wall_head", SkullBlock.Type.DRAGON, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); - public static final Block PIGLIN_HEAD = register(new SkullBlock("piglin_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block PIGLIN_HEAD = register(new SkullBlock("piglin_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .booleanState(POWERED) .intState(ROTATION_16, 0, 15))); - public static final Block PIGLIN_WALL_HEAD = register(new SkullBlock("piglin_wall_head", builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) + public static final Block PIGLIN_WALL_HEAD = register(new WallSkullBlock("piglin_wall_head", SkullBlock.Type.PIGLIN, builder().setBlockEntity().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .enumState(HORIZONTAL_FACING, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST) .booleanState(POWERED))); public static final Block ANVIL = register(new Block("anvil", builder().requiresCorrectToolForDrops().destroyTime(5.0f).pushReaction(PistonBehavior.BLOCK) @@ -1694,7 +1695,7 @@ public final class Blocks { public static final Block BLACK_CONCRETE_POWDER = register(new Block("black_concrete_powder", builder().destroyTime(0.5f))); public static final Block KELP = register(new Block("kelp", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_25, 0, 25))); - public static final Block KELP_PLANT = register(new Block("kelp_plant", builder().pushReaction(PistonBehavior.DESTROY))); + public static final Block KELP_PLANT = register(new Block("kelp_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.KELP))); public static final Block DRIED_KELP_BLOCK = register(new Block("dried_kelp_block", builder().destroyTime(0.5f))); public static final Block TURTLE_EGG = register(new Block("turtle_egg", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) .intState(EGGS, 1, 4) @@ -1787,7 +1788,7 @@ public final class Blocks { public static final Block BLUE_ICE = register(new Block("blue_ice", builder().destroyTime(2.8f))); public static final Block CONDUIT = register(new Block("conduit", builder().setBlockEntity().destroyTime(3.0f) .booleanState(WATERLOGGED))); - public static final Block BAMBOO_SAPLING = register(new Block("bamboo_sapling", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY))); + public static final Block BAMBOO_SAPLING = register(new Block("bamboo_sapling", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.BAMBOO))); public static final Block BAMBOO = register(new Block("bamboo", builder().destroyTime(1.0f).pushReaction(PistonBehavior.DESTROY) .intState(AGE_1, 0, 1) .enumState(BAMBOO_LEAVES, "none", "small", "large") @@ -2072,10 +2073,10 @@ public final class Blocks { public static final Block SHROOMLIGHT = register(new Block("shroomlight", builder().destroyTime(1.0f))); public static final Block WEEPING_VINES = register(new Block("weeping_vines", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_25, 0, 25))); - public static final Block WEEPING_VINES_PLANT = register(new Block("weeping_vines_plant", builder().pushReaction(PistonBehavior.DESTROY))); + public static final Block WEEPING_VINES_PLANT = register(new Block("weeping_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.WEEPING_VINES))); public static final Block TWISTING_VINES = register(new Block("twisting_vines", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_25, 0, 25))); - public static final Block TWISTING_VINES_PLANT = register(new Block("twisting_vines_plant", builder().pushReaction(PistonBehavior.DESTROY))); + public static final Block TWISTING_VINES_PLANT = register(new Block("twisting_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.TWISTING_VINES))); public static final Block CRIMSON_ROOTS = register(new Block("crimson_roots", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block CRIMSON_PLANKS = register(new Block("crimson_planks", builder().destroyTime(2.0f))); public static final Block WARPED_PLANKS = register(new Block("warped_planks", builder().destroyTime(2.0f))); @@ -2319,39 +2320,39 @@ public final class Blocks { .intState(CANDLES, 1, 4) .booleanState(LIT) .booleanState(WATERLOGGED))); - public static final Block CANDLE_CAKE = register(new Block("candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block CANDLE_CAKE = register(new Block("candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block WHITE_CANDLE_CAKE = register(new Block("white_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block WHITE_CANDLE_CAKE = register(new Block("white_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block ORANGE_CANDLE_CAKE = register(new Block("orange_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block ORANGE_CANDLE_CAKE = register(new Block("orange_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block MAGENTA_CANDLE_CAKE = register(new Block("magenta_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block MAGENTA_CANDLE_CAKE = register(new Block("magenta_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block LIGHT_BLUE_CANDLE_CAKE = register(new Block("light_blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block LIGHT_BLUE_CANDLE_CAKE = register(new Block("light_blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block YELLOW_CANDLE_CAKE = register(new Block("yellow_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block YELLOW_CANDLE_CAKE = register(new Block("yellow_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block LIME_CANDLE_CAKE = register(new Block("lime_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block LIME_CANDLE_CAKE = register(new Block("lime_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block PINK_CANDLE_CAKE = register(new Block("pink_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block PINK_CANDLE_CAKE = register(new Block("pink_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block GRAY_CANDLE_CAKE = register(new Block("gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block GRAY_CANDLE_CAKE = register(new Block("gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block LIGHT_GRAY_CANDLE_CAKE = register(new Block("light_gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block LIGHT_GRAY_CANDLE_CAKE = register(new Block("light_gray_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block CYAN_CANDLE_CAKE = register(new Block("cyan_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block CYAN_CANDLE_CAKE = register(new Block("cyan_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block PURPLE_CANDLE_CAKE = register(new Block("purple_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block PURPLE_CANDLE_CAKE = register(new Block("purple_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block BLUE_CANDLE_CAKE = register(new Block("blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block BLUE_CANDLE_CAKE = register(new Block("blue_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block BROWN_CANDLE_CAKE = register(new Block("brown_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block BROWN_CANDLE_CAKE = register(new Block("brown_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block GREEN_CANDLE_CAKE = register(new Block("green_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block GREEN_CANDLE_CAKE = register(new Block("green_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block RED_CANDLE_CAKE = register(new Block("red_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block RED_CANDLE_CAKE = register(new Block("red_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); - public static final Block BLACK_CANDLE_CAKE = register(new Block("black_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY) + public static final Block BLACK_CANDLE_CAKE = register(new Block("black_candle_cake", builder().destroyTime(0.5f).pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.CAKE) .booleanState(LIT))); public static final Block AMETHYST_BLOCK = register(new Block("amethyst_block", builder().requiresCorrectToolForDrops().destroyTime(1.5f))); public static final Block BUDDING_AMETHYST = register(new Block("budding_amethyst", builder().requiresCorrectToolForDrops().destroyTime(1.5f).pushReaction(PistonBehavior.DESTROY))); @@ -2682,7 +2683,7 @@ public final class Blocks { public static final Block CAVE_VINES = register(new Block("cave_vines", builder().pushReaction(PistonBehavior.DESTROY) .intState(AGE_25, 0, 25) .booleanState(BERRIES))); - public static final Block CAVE_VINES_PLANT = register(new Block("cave_vines_plant", builder().pushReaction(PistonBehavior.DESTROY) + public static final Block CAVE_VINES_PLANT = register(new Block("cave_vines_plant", builder().pushReaction(PistonBehavior.DESTROY).pickItem(() -> Items.GLOW_BERRIES) .booleanState(BERRIES))); public static final Block SPORE_BLOSSOM = register(new Block("spore_blossom", builder().pushReaction(PistonBehavior.DESTROY))); public static final Block AZALEA = register(new Block("azalea", builder().pushReaction(PistonBehavior.DESTROY))); @@ -2811,7 +2812,8 @@ public final class Blocks { .booleanState(WATERLOGGED))); private static T register(T block) { - BlockRegistries.JAVA_BLOCKS_TO_RENAME.get().add(block); + block.setJavaId(BlockRegistries.JAVA_BLOCKS.get().size()); + BlockRegistries.JAVA_BLOCKS.get().add(block); return block; } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/DoubleChestValue.java b/core/src/main/java/org/geysermc/geyser/level/block/DoubleChestValue.java deleted file mode 100644 index 97c861df7..000000000 --- a/core/src/main/java/org/geysermc/geyser/level/block/DoubleChestValue.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.level.block; - -/** - * This stores all values of double chests that are part of the Java block state. - * - * @param isFacingEast If true, then chest is facing east/west; if false, south/north - * @param isDirectionPositive If true, direction is positive (east/south); if false, direction is negative (west/north) - * @param isLeft If true, chest is the left of a pair; if false, chest is the right of a pair. - */ -public record DoubleChestValue( - boolean isFacingEast, - boolean isDirectionPositive, - boolean isLeft) { - -} diff --git a/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java b/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java index 491894a35..b31c9aeb5 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/Block.java @@ -37,24 +37,33 @@ import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.level.block.Blocks; -import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.level.block.property.Property; import org.geysermc.geyser.level.physics.PistonBehavior; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.intellij.lang.annotations.Subst; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Stream; public class Block { public static final int JAVA_AIR_ID = 0; private final Key javaIdentifier; + /** + * Can you harvest this with your hand. + */ private final boolean requiresCorrectToolForDrops; private final boolean hasBlockEntity; private final float destroyTime; private final @NonNull PistonBehavior pushReaction; + /** + * Used for classes we don't have implemented yet that override Mojmap getCloneItemStack with their own item. + * A supplier prevents any issues arising where the Items class finishes before the Blocks class. + */ + private final Supplier pickItem; protected Item item = null; private int javaId = -1; @@ -64,10 +73,13 @@ public class Block { this.hasBlockEntity = builder.hasBlockEntity; this.destroyTime = builder.destroyTime; this.pushReaction = builder.pushReaction; + this.pickItem = builder.pickItem; processStates(builder.build(this)); } public void updateBlock(GeyserSession session, BlockState state, Vector3i position) { + checkForEmptySkull(session, state, position); + BlockDefinition definition = session.getBlockMappings().getBedrockBlock(state); sendBlockUpdatePacket(session, state, definition, position); @@ -118,8 +130,7 @@ public class Block { UpdateBlockPacket waterPacket = new UpdateBlockPacket(); waterPacket.setDataLayer(1); waterPacket.setBlockPosition(position); - Boolean waterlogged = state.getValue(Properties.WATERLOGGED); - if (waterlogged == Boolean.TRUE) { + if (BlockRegistries.WATERLOGGED.get().get(state.javaId())) { waterPacket.setDefinition(session.getBlockMappings().getBedrockWater()); } else { waterPacket.setDefinition(session.getBlockMappings().getBedrockAir()); @@ -127,6 +138,13 @@ public class Block { session.sendUpstreamPacket(waterPacket); } + protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) { + if (!(state.block() instanceof SkullBlock)) { + // Skull is gone + session.getSkullCache().removeSkull(position); + } + } + protected void handleLecternBlockUpdate(GeyserSession session, BlockState state, Vector3i position) { // Block state is out of bounds of this map - lectern has been destroyed, if it existed if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)) { @@ -141,6 +159,13 @@ public class Block { return this.item; } + public ItemStack pickItem(BlockState state) { + if (this.pickItem != null) { + return new ItemStack(this.pickItem.get().javaId()); + } + return new ItemStack(this.asItem().javaId()); + } + /** * A list of BlockStates is created pertaining to this block. Do we need any of them? If so, override this method. */ @@ -198,6 +223,7 @@ public class Block { private boolean hasBlockEntity = false; private PistonBehavior pushReaction = PistonBehavior.NORMAL; private float destroyTime; + private Supplier pickItem; /** * For states that we're just tracking for mirroring Java states. @@ -248,6 +274,11 @@ public class Block { return this; } + public Builder pickItem(Supplier pickItem) { + this.pickItem = pickItem; + return this; + } + private List build(Block block) { if (states.isEmpty()) { BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size()); diff --git a/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java b/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java index cb375eaf9..4fed30e7a 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/BlockState.java @@ -37,7 +37,7 @@ public final class BlockState { private final int javaId; private final Reference2ObjectMap, Comparable> states; - BlockState(Block block, int javaId) { + public BlockState(Block block, int javaId) { this(block, javaId, Reference2ObjectMaps.emptyMap()); } @@ -52,6 +52,14 @@ public final class BlockState { return (T) states.get(property); } + public boolean getValue(Property property, boolean def) { + var value = states.get(property); + if (value == null) { + return def; + } + return (Boolean) value; + } + public Block block() { return block; } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/type/FlowerPotBlock.java b/core/src/main/java/org/geysermc/geyser/level/block/type/FlowerPotBlock.java index 53d0b1ec1..5107616af 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/type/FlowerPotBlock.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/FlowerPotBlock.java @@ -34,6 +34,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.level.block.entity.BedrockChunkWantsBlockEntityTag; import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; import org.geysermc.geyser.util.BlockEntityUtils; +import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntityTag { private final Block flower; @@ -68,11 +69,24 @@ public class FlowerPotBlock extends Block implements BedrockChunkWantsBlockEntit if (this.flower != Blocks.AIR) { // Get the Bedrock CompoundTag of the block. // This is where we need to store the *Java* name because Bedrock has six minecraft:sapling blocks with different block states. - NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower.javaIdentifier().asString()); + // TODO flattening might make this nicer in the future! + NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(this.flower); if (plant != null) { tagBuilder.putCompound("PlantBlock", plant.toBuilder().build()); } } return tagBuilder.build(); } + + @Override + public ItemStack pickItem(BlockState state) { + if (this.flower != Blocks.AIR) { + return new ItemStack(this.flower.asItem().javaId()); + } + return super.pickItem(state); + } + + public Block flower() { + return flower; + } } diff --git a/core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java b/core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java index 01a757eae..76b532919 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/SkullBlock.java @@ -27,28 +27,58 @@ package org.geysermc.geyser.level.block.type; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; -import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.SkullCache; public class SkullBlock extends Block { - public SkullBlock(String javaIdentifier, Builder builder) { + private final Type type; + + public SkullBlock(String javaIdentifier, Type type, Builder builder) { super(javaIdentifier, builder); + this.type = type; } @Override protected void sendBlockUpdatePacket(GeyserSession session, BlockState state, BlockDefinition definition, Vector3i position) { - int skullVariant = BlockStateValues.getSkullVariant(state.javaId()); // TODO - if (skullVariant == -1) { - // Skull is gone - session.getSkullCache().removeSkull(position); - } else if (skullVariant == 3) { + if (this.type == Type.PLAYER) { // The changed block was a player skull so check if a custom block was defined for this skull - SkullCache.Skull skull = session.getSkullCache().updateSkull(position, state.javaId()); + SkullCache.Skull skull = session.getSkullCache().updateSkull(position, state); if (skull != null && skull.getBlockDefinition() != null) { definition = skull.getBlockDefinition(); } } super.sendBlockUpdatePacket(session, state, definition, position); } + + @Override + protected void checkForEmptySkull(GeyserSession session, BlockState state, Vector3i position) { + // It's not an empty skull. + } + + public Type skullType() { + return type; + } + + /** + * Enum order matches Java. + */ + public enum Type { + SKELETON(0), + WITHER_SKELETON(1), + PLAYER(3), + ZOMBIE(2), + CREEPER(4), + PIGLIN(6), + DRAGON(5); + + private final int bedrockId; + + Type(int bedrockId) { + this.bedrockId = bedrockId; + } + + public int bedrockId() { + return bedrockId; + } + } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java b/core/src/main/java/org/geysermc/geyser/level/block/type/WallSkullBlock.java similarity index 58% rename from core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java rename to core/src/main/java/org/geysermc/geyser/level/block/type/WallSkullBlock.java index 0520924f8..e8fedcc76 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMapping.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/type/WallSkullBlock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2024 GeyserMC. http://geysermc.org * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,21 +23,27 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.registry.type; +package org.geysermc.geyser.level.block.type; -import lombok.Builder; -import lombok.Value; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.level.block.property.Properties; +import org.geysermc.geyser.level.physics.Direction; -@Builder -@Value -public class BlockMapping { - public static BlockMapping DEFAULT = BlockMapping.builder().javaIdentifier("minecraft:air").build(); +public class WallSkullBlock extends SkullBlock { + public WallSkullBlock(String javaIdentifier, Type type, Builder builder) { + super(javaIdentifier, type, builder); + } - String javaIdentifier; + public static int getDegrees(BlockState state) { + return getDegrees(state.getValue(Properties.HORIZONTAL_FACING)); + } - @Nullable String pickItem; - - boolean isBlockEntity; - boolean isNonVanilla; + public static int getDegrees(Direction direction) { + return switch (direction) { + case NORTH -> 180; + case WEST -> 90; + case EAST -> 270; + case SOUTH -> 0; + default -> throw new IllegalStateException(); + }; + } } diff --git a/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java b/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java index a18ffff7d..93ac5e457 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java +++ b/core/src/main/java/org/geysermc/geyser/registry/BlockRegistries.java @@ -42,7 +42,6 @@ import org.geysermc.geyser.registry.loader.RegistryLoaders; import org.geysermc.geyser.registry.populator.BlockRegistryPopulator; import org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator; import org.geysermc.geyser.registry.populator.CustomSkullRegistryPopulator; -import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.CustomSkull; import org.geysermc.geyser.translator.collision.BlockCollision; @@ -67,24 +66,22 @@ public class BlockRegistries { */ public static final ListRegistry BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::new)); - public static final ListRegistry JAVA_BLOCKS_TO_RENAME = ListRegistry.create(RegistryLoaders.empty(ArrayList::new)); - /** * A mapped registry which stores Java to Bedrock block identifiers. */ public static final SimpleMappedRegistry JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); - /** - * A registry which stores Java IDs to {@link BlockMapping}, containing miscellaneous information about - * blocks and their behavior in many cases. - */ - public static final ArrayRegistry JAVA_BLOCKS = ArrayRegistry.create(RegistryLoaders.uninitialized()); - /** * A mapped registry containing which holds block IDs to its {@link BlockCollision}. */ public static final ListRegistry COLLISIONS; + /** + * A registry which stores Java IDs to {@link Block}, containing miscellaneous information about + * blocks and their behavior in many cases. + */ + public static final ListRegistry JAVA_BLOCKS = ListRegistry.create(RegistryLoaders.empty(ArrayList::new)); + /** * A mapped registry containing the Java identifiers to IDs. */ diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java index 0180fc8b7..a0af4cfa0 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java @@ -32,13 +32,12 @@ import com.google.common.collect.Interner; import com.google.common.collect.Interners; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.*; import org.cloudburstmc.blockstateupdater.BlockStateUpdater; import org.cloudburstmc.blockstateupdater.util.tagupdater.CompoundTagUpdaterContext; -import org.cloudburstmc.nbt.NBTInputStream; -import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.nbt.NbtMapBuilder; -import org.cloudburstmc.nbt.NbtType; +import org.cloudburstmc.nbt.*; import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622; import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630; import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649; @@ -50,21 +49,26 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState; -import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.item.Items; import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.block.Blocks; +import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.level.block.type.FlowerPotBlock; import org.geysermc.geyser.level.physics.PistonBehavior; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; -import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.registry.type.BlockMappings; import org.geysermc.geyser.registry.type.GeyserBedrockBlock; import org.geysermc.geyser.util.BlockUtils; +import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import java.io.DataInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.stream.Stream; import java.util.zip.GZIPInputStream; /** @@ -102,7 +106,7 @@ public final class BlockRegistryPopulator { public static void populate(Stage stage) { switch (stage) { - case PRE_INIT, POST_INIT -> nullifyBlocksNode(); + case PRE_INIT, POST_INIT -> nullifyBlocksNbt(); case INIT_JAVA -> registerJavaBlocks(); case INIT_BEDROCK -> registerBedrockBlocks(); default -> throw new IllegalArgumentException("Unknown stage: " + stage); @@ -110,14 +114,14 @@ public final class BlockRegistryPopulator { } /** - * Stores the raw blocks JSON until it is no longer needed. + * Stores the raw blocks NBT until it is no longer needed. */ - private static JsonNode BLOCKS_JSON; + private static List BLOCKS_NBT; private static int MIN_CUSTOM_RUNTIME_ID = -1; private static int JAVA_BLOCKS_SIZE = -1; - private static void nullifyBlocksNode() { - BLOCKS_JSON = null; + private static void nullifyBlocksNbt() { + BLOCKS_NBT = null; } private static void registerBedrockBlocks() { @@ -218,19 +222,31 @@ public final class BlockRegistryPopulator { int javaRuntimeId = -1; + List javaBlockStates = BlockRegistries.BLOCK_STATES.get(); + GeyserBedrockBlock airDefinition = null; BlockDefinition commandBlockDefinition = null; BlockDefinition mobSpawnerBlockDefinition = null; BlockDefinition waterDefinition = null; BlockDefinition movingBlockDefinition = null; - Iterator> blocksIterator = BLOCKS_JSON.fields(); + Iterator blocksIterator = BLOCKS_NBT.iterator(); Remapper stateMapper = blockMappers.get(palette); GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE]; GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE]; - Map flowerPotBlocks = new Object2ObjectOpenHashMap<>(); + // Stream isn't ideal. + List javaPottable = BlockRegistries.JAVA_BLOCKS.get() + .parallelStream() + .flatMap(block -> { + if (block instanceof FlowerPotBlock flowerPot && flowerPot.flower() != Blocks.AIR) { + return Stream.of(flowerPot.flower()); + } + return null; + }) + .toList(); + Map flowerPotBlocks = new Object2ObjectOpenHashMap<>(); Map itemFrames = new Object2ObjectOpenHashMap<>(); Set jigsawDefinitions = new ObjectOpenHashSet<>(); @@ -239,10 +255,11 @@ public final class BlockRegistryPopulator { BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder(); while (blocksIterator.hasNext()) { javaRuntimeId++; - Map.Entry entry = blocksIterator.next(); - String javaId = entry.getKey(); + NbtMap entry = blocksIterator.next(); + BlockState blockState = javaBlockStates.get(javaRuntimeId); + String javaId = blockState.toString(); - NbtMap originalBedrockTag = buildBedrockState(entry.getValue()); + NbtMap originalBedrockTag = buildBedrockState(blockState, entry); NbtMap bedrockTag = stateMapper.remap(originalBedrockTag); GeyserBedrockBlock vanillaBedrockDefinition = blockStateOrderedMap.get(bedrockTag); @@ -274,35 +291,27 @@ public final class BlockRegistryPopulator { case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition; } - if (javaId.contains("jigsaw")) { + if (blockState.block() == Blocks.JIGSAW) { jigsawDefinitions.add(bedrockDefinition); } - if (javaId.contains("structure_block")) { - int modeIndex = javaId.indexOf("mode="); - if (modeIndex != -1) { - int startIndex = modeIndex + 5; // Length of "mode=" is 5 - int endIndex = javaId.indexOf("]", startIndex); - if (endIndex != -1) { - String modeValue = javaId.substring(startIndex, endIndex); - structureBlockDefinitions.put(modeValue.toUpperCase(), bedrockDefinition); - } - } + if (blockState.block() == Blocks.STRUCTURE_BLOCK) { + String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE); + structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition); } - boolean waterlogged = entry.getKey().contains("waterlogged=true") - || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); + boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false) + || blockState.block() == Blocks.BUBBLE_COLUMN || blockState.block() == Blocks.KELP || blockState.block() == Blocks.SEAGRASS; if (waterlogged) { int finalJavaRuntimeId = javaRuntimeId; BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId)); } - String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey()); - // Get the tag needed for non-empty flower pots - if (entry.getValue().get("pottable") != null) { - flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockDefinition.getRuntimeId())); + if (javaPottable.contains(blockState.block())) { + // Specifically NOT putIfAbsent - mangrove propagule breaks otherwise + flowerPotBlocks.put(blockState.block(), blockStates.get(bedrockDefinition.getRuntimeId())); } javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition; @@ -386,9 +395,10 @@ public final class BlockRegistryPopulator { } private static void registerJavaBlocks() { - JsonNode blocksJson; - try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.json")) { - blocksJson = GeyserImpl.JSON_MAPPER.readTree(stream); + List blocksNbt; + try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/blocks.nbt")) { + blocksNbt = ((NbtMap) NbtUtils.createGZIPReader(stream).readTag()) + .getList("bedrock_mappings", NbtType.COMPOUND); } catch (Exception e) { throw new AssertionError("Unable to load Java block mappings", e); } @@ -399,56 +409,29 @@ public final class BlockRegistryPopulator { MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId(); int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId(); - if (MIN_CUSTOM_RUNTIME_ID < blocksJson.size()) { + if (MIN_CUSTOM_RUNTIME_ID < blocksNbt.size()) { throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")"); } JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1 } - BlockRegistries.JAVA_BLOCKS.set(new BlockMapping[JAVA_BLOCKS_SIZE]); // Set array size to number of blockstates - - Deque cleanIdentifiers = new ArrayDeque<>(); - int javaRuntimeId = -1; int waterRuntimeId = -1; - Iterator> blocksIterator = blocksJson.fields(); - while (blocksIterator.hasNext()) { + for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) { javaRuntimeId++; - Map.Entry entry = blocksIterator.next(); - String javaId = entry.getKey(); + String javaId = javaBlockState.toString().intern(); - BlockMapping.BlockMappingBuilder builder = BlockMapping.builder(); + BlockStateValues.storeBlockStateValues(javaId, javaRuntimeId); - JsonNode pickItemNode = entry.getValue().get("pick_item"); - if (pickItemNode != null) { - builder.pickItem(pickItemNode.textValue().intern()); - } - - JsonNode hasBlockEntityNode = entry.getValue().get("has_block_entity"); - if (hasBlockEntityNode != null) { - builder.isBlockEntity(hasBlockEntityNode.booleanValue()); - } else { - builder.isBlockEntity(false); - } - - BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue()); - - String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(entry.getKey()); - String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); - - if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) { - cleanIdentifiers.add(cleanJavaIdentifier.intern()); - } - - builder.javaIdentifier(javaId); + //String cleanJavaIdentifier = javaBlockState.block().javaIdentifier().toString(); + //String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId); - BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build()); // Keeping this here since this is currently unchanged between versions // It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions - BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern()); + //BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern()); if ("minecraft:water[level=0]".equals(javaId)) { waterRuntimeId = javaRuntimeId; @@ -461,7 +444,7 @@ public final class BlockRegistryPopulator { BlockStateValues.JAVA_WATER_ID = waterRuntimeId; if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) { - Set usedNonVanillaRuntimeIDs = new HashSet<>(); + IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet(); for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) { if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) { @@ -473,11 +456,6 @@ public final class BlockRegistryPopulator { String javaId = javaBlockState.identifier(); int stateRuntimeId = javaBlockState.javaId(); String pistonBehavior = javaBlockState.pistonBehavior(); - BlockMapping blockMapping = BlockMapping.builder() - .pickItem(javaBlockState.pickItem()) - .isNonVanilla(true) - .javaIdentifier(javaId) - .build(); Block.Builder builder = Block.builder() .destroyTime(javaBlockState.blockHardness()) @@ -492,23 +470,25 @@ public final class BlockRegistryPopulator { String pickItem = javaBlockState.pickItem(); Block block = new Block(cleanJavaIdentifier, builder) { @Override - public Item asItem() { + public ItemStack pickItem(BlockState state) { if (this.item == null) { - return Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem); + this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem); + if (this.item == null) { + GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem + + " for getting the item for block " + javaBlockState.identifier()); + this.item = Items.AIR; + } } - return this.item; + return new ItemStack(this.item.javaId()); } }; + block.setJavaId(javaBlockState.stateGroupId()); String bedrockIdentifier = customBlockState.block().identifier(); - if (!cleanJavaIdentifier.equals(cleanIdentifiers.peekLast())) { - cleanIdentifiers.add(cleanJavaIdentifier.intern()); - } - - BlockRegistries.JAVA_BLOCKS_TO_RENAME.get().add(javaBlockState.stateGroupId(), block); //TODO don't allow duplicates, allow blanks + BlockRegistries.JAVA_BLOCKS.get().add(javaBlockState.stateGroupId(), block); //TODO don't allow duplicates, allow blanks BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId); - BlockRegistries.JAVA_BLOCKS.register(stateRuntimeId, blockMapping); + BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId)); // Keeping this here since this is currently unchanged between versions // It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions @@ -516,7 +496,7 @@ public final class BlockRegistryPopulator { } } - BLOCKS_JSON = blocksJson; + BLOCKS_NBT = blocksNbt; JsonNode blockInteractionsJson; try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) { @@ -537,29 +517,11 @@ public final class BlockRegistryPopulator { return blockStateSet; } - private static NbtMap buildBedrockState(JsonNode node) { + private static NbtMap buildBedrockState(BlockState state, NbtMap nbt) { NbtMapBuilder tagBuilder = NbtMap.builder(); - String bedrockIdentifier = node.get("bedrock_identifier").textValue(); + String bedrockIdentifier = "minecraft:" + nbt.getString("bedrock_identifier", state.block().javaIdentifier().value()); tagBuilder.putString("name", bedrockIdentifier); - - NbtMapBuilder statesBuilder = NbtMap.builder(); - - // check for states - JsonNode states = node.get("bedrock_states"); - if (states != null) { - Iterator> statesIterator = states.fields(); - - while (statesIterator.hasNext()) { - Map.Entry stateEntry = statesIterator.next(); - JsonNode stateValue = stateEntry.getValue(); - switch (stateValue.getNodeType()) { - case BOOLEAN -> statesBuilder.putBoolean(stateEntry.getKey(), stateValue.booleanValue()); - case STRING -> statesBuilder.putString(stateEntry.getKey(), stateValue.textValue()); - case NUMBER -> statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue()); - } - } - } - tagBuilder.put("states", statesBuilder.build()); + tagBuilder.put("states", nbt.getCompound("state")); return tagBuilder.build(); } diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java index 2fe808070..e4b7111b3 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java @@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.BlockPropertyData; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.common.DefinitionRegistry; import org.geysermc.geyser.api.block.custom.CustomBlockState; +import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.level.block.type.BlockState; import java.util.List; @@ -59,7 +60,7 @@ public class BlockMappings implements DefinitionRegistry { BlockDefinition mobSpawnerBlock; Map itemFrames; - Map flowerPotBlocks; + Map flowerPotBlocks; Set jigsawStates; Map structureBlockStates; diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java index 5b208a41f..a40a1156d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/SkullCache.java @@ -25,18 +25,20 @@ package org.geysermc.geyser.session.cache; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.math.vector.Vector3i; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.block.custom.CustomBlockState; import org.geysermc.geyser.entity.type.player.SkullPlayerEntity; -import org.geysermc.geyser.level.block.BlockStateValues; +import org.geysermc.geyser.level.block.property.Properties; +import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.level.block.type.WallSkullBlock; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.type.CustomSkull; import org.geysermc.geyser.session.GeyserSession; @@ -80,7 +82,7 @@ public class SkullCache { this.skullRenderDistanceSquared = distance * distance; } - public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, int blockState) { + public Skull putSkull(Vector3i position, UUID uuid, String texturesProperty, BlockState blockState) { Skull skull = skulls.computeIfAbsent(position, Skull::new); skull.uuid = uuid; if (!texturesProperty.equals(skull.texturesProperty)) { @@ -147,7 +149,7 @@ public class SkullCache { } } - public Skull updateSkull(Vector3i position, int blockState) { + public Skull updateSkull(Vector3i position, BlockState blockState) { Skull skull = skulls.get(position); if (skull != null) { putSkull(position, skull.uuid, skull.texturesProperty, blockState); @@ -248,17 +250,14 @@ public class SkullCache { lastPlayerPosition = null; } - private @Nullable BlockDefinition translateCustomSkull(String skinHash, int blockState) { + private @Nullable BlockDefinition translateCustomSkull(String skinHash, BlockState blockState) { CustomSkull customSkull = BlockRegistries.CUSTOM_SKULLS.get(skinHash); if (customSkull != null) { - byte floorRotation = BlockStateValues.getSkullRotation(blockState); CustomBlockState customBlockState; - if (floorRotation == -1) { - // Wall skull - int wallDirection = BlockStateValues.getSkullWallDirections().get(blockState); - customBlockState = customSkull.getWallBlockState(wallDirection); + if (blockState.block() instanceof WallSkullBlock) { + customBlockState = customSkull.getWallBlockState(WallSkullBlock.getDegrees(blockState)); } else { - customBlockState = customSkull.getFloorBlockState(floorRotation); + customBlockState = customSkull.getFloorBlockState(blockState.getValue(Properties.ROTATION_16)); } return session.getBlockMappings().getCustomBlockStateDefinitions().get(customBlockState); @@ -273,7 +272,7 @@ public class SkullCache { private String texturesProperty; private String skinHash; - private int blockState; + private BlockState blockState; private BlockDefinition blockDefinition; private SkullPlayerEntity entity; diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java index cb973fa18..4c49c8e5a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/chest/DoubleChestInventoryTranslator.java @@ -36,10 +36,13 @@ import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.inventory.Container; import org.geysermc.geyser.inventory.Inventory; +import org.geysermc.geyser.level.block.Blocks; +import org.geysermc.geyser.level.block.property.ChestType; +import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.registry.BlockRegistries; -import org.geysermc.geyser.registry.type.BlockMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator; import org.geysermc.geyser.translator.level.block.entity.DoubleChestBlockEntityTranslator; import org.geysermc.geyser.util.InventoryUtils; @@ -55,24 +58,17 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { public boolean prepareInventory(GeyserSession session, Inventory inventory) { // See BlockInventoryHolder - same concept there except we're also dealing with a specific block state if (session.getLastInteractionPlayerPosition().equals(session.getPlayerEntity().getPosition())) { - int javaBlockId = session.getGeyser().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition()); - if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(javaBlockId)) { - String[] javaBlockString = BlockRegistries.JAVA_BLOCKS.getOrDefault(javaBlockId, BlockMapping.DEFAULT).getJavaIdentifier().split("\\["); - if (javaBlockString.length > 1 && (javaBlockString[0].equals("minecraft:chest") || javaBlockString[0].equals("minecraft:trapped_chest")) - && !javaBlockString[1].contains("type=single")) { + BlockState state = session.getGeyser().getWorldManager().blockAt(session, session.getLastInteractionBlockPosition()); + if (!BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get().containsKey(state.javaId())) { + if (state.block() == Blocks.CHEST || state.block() == Blocks.TRAPPED_CHEST + && state.getValue(Properties.CHEST_TYPE) != ChestType.SINGLE) { inventory.setHolderPosition(session.getLastInteractionBlockPosition()); - ((Container) inventory).setUsingRealBlock(true, javaBlockString[0]); + ((Container) inventory).setUsingRealBlock(true, state.block()); - NbtMapBuilder tag = NbtMap.builder() - .putString("id", "Chest") - .putInt("x", session.getLastInteractionBlockPosition().getX()) - .putInt("y", session.getLastInteractionBlockPosition().getY()) - .putInt("z", session.getLastInteractionBlockPosition().getZ()) - .putString("CustomName", inventory.getTitle()) - .putString("id", "Chest"); + NbtMapBuilder tag = BlockEntityTranslator.getConstantBedrockTag("Chest", session.getLastInteractionBlockPosition()) + .putString("CustomName", inventory.getTitle()); - BlockState blockState = BlockState.of(javaBlockId); - DoubleChestBlockEntityTranslator.translateChestValue(tag, blockState, + DoubleChestBlockEntityTranslator.translateChestValue(tag, state, session.getLastInteractionBlockPosition().getX(), session.getLastInteractionBlockPosition().getZ()); BlockEntityDataPacket dataPacket = new BlockEntityDataPacket(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java index c03ae8ef0..cdbb20c44 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/SkullBlockEntityTranslator.java @@ -33,9 +33,9 @@ import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.GeyserImpl; -import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.property.Properties; import org.geysermc.geyser.level.block.type.BlockState; +import org.geysermc.geyser.level.block.type.SkullBlock; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.SkullCache; import org.geysermc.geyser.skin.SkinProvider; @@ -53,17 +53,12 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements @Override public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, NbtMap javaNbt, BlockState blockState) { - byte skullVariant = BlockStateValues.getSkullVariant(blockState.javaId()); // TODO - // Just in case... - if (skullVariant == -1) { - skullVariant = 0; - } Integer rotation = blockState.getValue(Properties.ROTATION_16); if (rotation != null) { - // Could be a wall skull block + // Could be a wall skull block otherwise, which has rotation in its Bedrock state bedrockNbt.putFloat("Rotation", rotation * 22.5f); } - bedrockNbt.putByte("SkullType", skullVariant); + bedrockNbt.putByte("SkullType", (byte) (blockState.block() instanceof SkullBlock skull ? skull.skullType().bedrockId() : 0)); if (blockState.getValue(Properties.POWERED)) { bedrockNbt.putBoolean("MouthMoving", true); } @@ -106,7 +101,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements return CompletableFuture.completedFuture(texture); } - public static @Nullable BlockDefinition translateSkull(GeyserSession session, NbtMap javaNbt, Vector3i blockPosition, int blockState) { + public static @Nullable BlockDefinition translateSkull(GeyserSession session, NbtMap javaNbt, Vector3i blockPosition, BlockState blockState) { NbtMap profile = javaNbt.getCompound("profile"); if (profile.isEmpty()) { session.getSkullCache().removeSkull(blockPosition); @@ -150,7 +145,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements return null; } - private static void putSkull(GeyserSession session, Vector3i blockPosition, UUID uuid, String texturesProperty, int blockState) { + private static void putSkull(GeyserSession session, Vector3i blockPosition, UUID uuid, String texturesProperty, BlockState blockState) { SkullCache.Skull skull = session.getSkullCache().putSkull(blockPosition, uuid, texturesProperty, blockState); if (skull.getBlockDefinition() != null) { UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java index be3af2061..0a721e4b0 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockBlockPickRequestTranslator.java @@ -32,7 +32,7 @@ import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.level.block.Blocks; import org.geysermc.geyser.level.block.type.BannerBlock; -import org.geysermc.geyser.level.block.type.Block; +import org.geysermc.geyser.level.block.type.BlockState; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -45,10 +45,10 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator session.ensureInEventLoop(() -> { if (components == null) { @@ -73,7 +73,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator { @@ -77,9 +78,9 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator { @@ -102,7 +100,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator> 4) - (bedrockDimension.minY() >> 4); int subChunkIndex = (y >> 4) + (bedrockDimension.minY() >> 4); diff --git a/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java b/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java index 7e1227f6c..72fcb4fa6 100644 --- a/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/StatisticsUtils.java @@ -94,7 +94,7 @@ public class StatisticsUtils { for (Object2IntMap.Entry entry : session.getStatistics().object2IntEntrySet()) { if (entry.getKey() instanceof BreakBlockStatistic statistic) { - Block block = BlockRegistries.JAVA_BLOCKS_TO_RENAME.get(statistic.getId()); + Block block = BlockRegistries.JAVA_BLOCKS.get(statistic.getId()); if (block != null) { String identifier = "block.minecraft." + block.javaIdentifier().value(); content.add(identifier + ": " + entry.getIntValue()); diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2BooleanMap.java b/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2BooleanMap.java deleted file mode 100644 index dc4545529..000000000 --- a/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2BooleanMap.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.util.collection; - -import it.unimi.dsi.fastutil.ints.AbstractInt2BooleanMap; -import it.unimi.dsi.fastutil.objects.ObjectSet; - -import java.io.Serial; - -public class FixedInt2BooleanMap extends AbstractInt2BooleanMap { - - @Serial - private static final long serialVersionUID = 1L; - - protected boolean[] value; - protected int start = -1; - - @Override - public int size() { - return value.length; - } - - @Override - public ObjectSet int2BooleanEntrySet() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean get(int i) { - return getOrDefault(i, defRetValue); - } - - @Override - public boolean getOrDefault(int key, boolean defaultValue) { - int offset = key - start; - if (offset < 0 || offset >= value.length) { - return defaultValue; - } - - return value[offset]; - } - - @Override - public boolean put(int key, boolean value) { - if (start == -1) { - start = key; - this.value = new boolean[] {value}; - } else { - int offset = key - start; - if (offset >= 0 && offset < this.value.length) { - boolean curr = this.value[offset]; - this.value[offset] = value; - return curr; - } else if (offset != this.value.length) { - throw new IndexOutOfBoundsException("Expected: " + (this.value.length + start) + ", got " + key); - } - - boolean[] newValue = new boolean[offset + 1]; - System.arraycopy(this.value, 0, newValue, 0, this.value.length); - this.value = newValue; - this.value[offset] = value; - } - - return this.defRetValue; - } - - @Override - public boolean containsKey(int k) { - int offset = k - start; - return offset >= 0 && offset < value.length; - } - - @Override - public boolean containsValue(boolean v) { - for (boolean b : value) { - if (b == v) { - return true; - } - } - - return false; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("{"); - int index = start; - for (boolean b : value) { - builder.append(index++).append("=>").append(b); - if (index < value.length + start) { - // Add commas while there are still more entries in the list - builder.append(", "); - } - } - return builder.append('}').toString(); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2ByteMap.java b/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2ByteMap.java deleted file mode 100644 index 46cb2a2ca..000000000 --- a/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2ByteMap.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.util.collection; - -import it.unimi.dsi.fastutil.ints.AbstractInt2ByteMap; -import it.unimi.dsi.fastutil.ints.Int2ByteMap; -import it.unimi.dsi.fastutil.objects.ObjectSet; - -import java.io.Serial; - -public class FixedInt2ByteMap extends AbstractInt2ByteMap { - - @Serial - private static final long serialVersionUID = 1L; - - protected byte[] value; - protected int start = -1; - - @Override - public int size() { - return value.length; - } - - @Override - public ObjectSet int2ByteEntrySet() { - throw new UnsupportedOperationException(); - } - - @Override - public byte get(int i) { - return getOrDefault(i, defRetValue); - } - - @Override - public byte getOrDefault(int key, byte defaultValue) { - int offset = key - start; - if (offset < 0 || offset >= value.length) { - return defaultValue; - } - - return value[offset]; - } - - @Override - public byte put(int key, byte value) { - if (start == -1) { - start = key; - this.value = new byte[] {value}; - } else { - int offset = key - start; - if (offset >= 0 && offset < this.value.length) { - byte curr = this.value[offset]; - this.value[offset] = value; - return curr; - } else if (offset != this.value.length) { - throw new IndexOutOfBoundsException("Expected: " + (this.value.length + start) + ", got " + key); - } - - byte[] newValue = new byte[offset + 1]; - System.arraycopy(this.value, 0, newValue, 0, this.value.length); - this.value = newValue; - this.value[offset] = value; - } - - return this.defRetValue; - } - - @Override - public boolean containsKey(int k) { - int offset = k - start; - return offset >= 0 && offset < value.length; - } - - @Override - public boolean containsValue(byte v) { - for (byte i : value) { - if (i == v) { - return true; - } - } - - return false; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("{"); - int index = start; - for (byte b : value) { - builder.append(index++).append("=>").append(b); - if (index < value.length + start) { - // Add commas while there are still more entries in the list - builder.append(", "); - } - } - return builder.append('}').toString(); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2IntMap.java b/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2IntMap.java deleted file mode 100644 index 4a27d51a9..000000000 --- a/core/src/main/java/org/geysermc/geyser/util/collection/FixedInt2IntMap.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.util.collection; - -import it.unimi.dsi.fastutil.ints.AbstractInt2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.objects.ObjectSet; - -import java.io.Serial; - -public class FixedInt2IntMap extends AbstractInt2IntMap { - protected int[] value; - protected int start = -1; - - @Serial - private static final long serialVersionUID = 1L; - - @Override - public int size() { - return value.length; - } - - @Override - public ObjectSet int2IntEntrySet() { - throw new UnsupportedOperationException(); - } - - @Override - public int get(int i) { - return getOrDefault(i, defRetValue); - } - - @Override - public int getOrDefault(int key, int defaultValue) { - int offset = key - start; - if (offset < 0 || offset >= value.length) { - return defaultValue; - } - - return value[offset]; - } - - @Override - public int put(int key, int value) { - if (start == -1) { - start = key; - this.value = new int[] {value}; - } else { - int offset = key - start; - if (offset >= 0 && offset < this.value.length) { - int curr = this.value[offset]; - this.value[offset] = value; - return curr; - } else if (offset != this.value.length) { - throw new IndexOutOfBoundsException("Expected: " + (this.value.length + start) + ", got " + key); - } - - int[] newValue = new int[offset + 1]; - System.arraycopy(this.value, 0, newValue, 0, this.value.length); - this.value = newValue; - this.value[offset] = value; - } - - return this.defRetValue; - } - - @Override - public boolean containsKey(int k) { - int offset = k - start; - return offset >= 0 && offset < value.length; - } - - @Override - public boolean containsValue(int v) { - for (int i : value) { - if (i == v) { - return true; - } - } - - return false; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("{"); - int index = start; - for (int i : value) { - builder.append(index++).append("=>").append(i); - if (index < value.length + start) { - // Add commas while there are still more entries in the list - builder.append(", "); - } - } - return builder.append('}').toString(); - } -} diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java b/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java deleted file mode 100644 index 42d0da803..000000000 --- a/core/src/main/java/org/geysermc/geyser/util/collection/LecternHasBookMap.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.util.collection; - -import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.nbt.NbtMap; -import org.geysermc.erosion.util.LecternUtils; -import org.geysermc.geyser.level.WorldManager; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.util.BlockEntityUtils; - -import java.io.Serial; - -/** - * Map that takes advantage of its internals for fast operations on block states to determine if they are lecterns. - */ -public class LecternHasBookMap extends FixedInt2BooleanMap { - - @Serial - private static final long serialVersionUID = 1L; - - /** - * Update a potential lectern within the world. This is a map method so it can use the internal fields to - * optimize lectern determining. - */ - public void handleBlockChange(GeyserSession session, int blockState, Vector3i position) { - WorldManager worldManager = session.getGeyser().getWorldManager(); - - int offset = blockState - this.start; - if (offset < 0 || offset >= this.value.length) { - // Block state is out of bounds of this map - lectern has been destroyed, if it existed - if (!worldManager.shouldExpectLecternHandled(session)) { - session.getLecternCache().remove(position); - } - return; - } - - boolean newLecternHasBook; - if (worldManager.shouldExpectLecternHandled(session)) { - worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ()); - } else if ((newLecternHasBook = this.value[offset]) != this.get(worldManager.getBlockAt(session, position))) { - // newLecternHasBook = the new lectern block state's "has book" toggle. - if (newLecternHasBook) { - worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ()); - } else { - session.getLecternCache().remove(position); - NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build(); - BlockEntityUtils.updateBlockEntity(session, newLecternTag, position); - } - } - } -} diff --git a/core/src/main/java/org/geysermc/geyser/util/collection/package-info.java b/core/src/main/java/org/geysermc/geyser/util/collection/package-info.java deleted file mode 100644 index 46fa5df11..000000000 --- a/core/src/main/java/org/geysermc/geyser/util/collection/package-info.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -/** - * Contains useful collections for use in Geyser. - *

- * Of note are the fixed int maps. Designed for use with block states that are positive and sequential, they do not allow keys to be - * added that are not greater by one versus the previous key. Because of this, speedy operations of {@link java.util.Map#get(java.lang.Object)} - * and {@link java.util.Map#containsKey(java.lang.Object)} can be performed by simply checking the bounds of the map - * size and its "start" integer. - */ -package org.geysermc.geyser.util.collection; \ No newline at end of file diff --git a/core/src/main/resources/mappings b/core/src/main/resources/mappings index 6b661f0d5..968a22bba 160000 --- a/core/src/main/resources/mappings +++ b/core/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 6b661f0d517d895aebc1f55a25d2c86f033beb1d +Subproject commit 968a22bbab02d7d003c5b451a40d8bb2439b0d97 diff --git a/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java b/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java deleted file mode 100644 index 639c79331..000000000 --- a/core/src/test/java/org/geysermc/geyser/util/collection/GeyserCollectionsTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/Geyser - */ - -package org.geysermc.geyser.util.collection; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class GeyserCollectionsTest { - private final byte[] bytes = new byte[] {(byte) 5, (byte) 4, (byte) 3, (byte) 2, (byte) 2, (byte) 1}; - private final boolean[] booleans = new boolean[] {true, false, false, true}; - private final int[] ints = new int[] {76, 3006, 999, 2323, 888, 0, 111, 999}; - - private final int[] startBlockRanges = new int[] {0, 70, 600, 450, 787, 1980}; - - @Test - public void testBytes() { - for (int startRange : startBlockRanges) { - testBytes(startRange, new FixedInt2ByteMap()); - } - } - - private void testBytes(final int start, final FixedInt2ByteMap map) { - int index = start; - for (byte b : bytes) { - map.put(index++, b); - } - - int lastKey = index; - - // Easy, understandable out-of-bounds checks - Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!"); - Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!"); - - // Ensure the first and last values do not throw an exception on get, and test getOrDefault - map.get(start - 1); - map.get(lastKey); - Assertions.assertEquals(map.getOrDefault(start - 1, Byte.MAX_VALUE), Byte.MAX_VALUE); - Assertions.assertEquals(map.getOrDefault(lastKey, Byte.MAX_VALUE), Byte.MAX_VALUE); - Assertions.assertEquals(map.getOrDefault(lastKey, Byte.MIN_VALUE), Byte.MIN_VALUE); - - Assertions.assertEquals(map.size(), bytes.length); - - for (int i = start; i < bytes.length; i++) { - Assertions.assertTrue(map.containsKey(i)); - Assertions.assertEquals(map.get(i), bytes[i - start]); - } - - for (int i = start - 1; i >= (start - 6); i--) { - // Lower than expected check - Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start); - } - - for (int i = bytes.length + start; i < bytes.length + 5 + start; i++) { - // Higher than expected check - Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + bytes.length)); - } - - for (byte b : bytes) { - Assertions.assertTrue(map.containsValue(b)); - } - } - - @Test - public void testBooleans() { - for (int startRange : startBlockRanges) { - testBooleans(startRange, new FixedInt2BooleanMap()); - } - } - - private void testBooleans(final int start, final FixedInt2BooleanMap map) { - int index = start; - for (boolean b : booleans) { - map.put(index++, b); - } - - int lastKey = index; - - // Easy, understandable out-of-bounds checks - Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!"); - Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!"); - - // Ensure the first and last values do not throw an exception on get - map.get(start - 1); - map.get(lastKey); - Assertions.assertTrue(map.getOrDefault(lastKey, true)); - - Assertions.assertEquals(map.size(), booleans.length); - - for (int i = start; i < booleans.length; i++) { - Assertions.assertTrue(map.containsKey(i)); - Assertions.assertEquals(map.get(i), booleans[i - start]); - } - - for (int i = start - 1; i >= (start - 6); i--) { - // Lower than expected check - Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start); - } - - for (int i = booleans.length + start; i < booleans.length + start + 5; i++) { - // Higher than expected check - Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + booleans.length)); - } - - for (boolean b : booleans) { - Assertions.assertTrue(map.containsValue(b)); - } - } - - @Test - public void testInts() { - for (int startRange : startBlockRanges) { - testInts(startRange, new FixedInt2IntMap()); - } - } - - private void testInts(final int start, final FixedInt2IntMap map) { - int index = start; - for (int i : ints) { - map.put(index++, i); - } - - int lastKey = index; - - // Easy, understandable out-of-bounds checks - Assertions.assertFalse(map.containsKey(lastKey), "Map contains key bigger by one!"); - Assertions.assertTrue(map.containsKey(lastKey - 1), "Map doesn't contain final key!"); - - // Ensure the first and last values do not throw an exception on get, and test getOrDefault - map.get(start - 1); - map.get(lastKey); - Assertions.assertEquals(map.getOrDefault(start - 1, Integer.MAX_VALUE), Integer.MAX_VALUE); - Assertions.assertEquals(map.getOrDefault(lastKey, Integer.MAX_VALUE), Integer.MAX_VALUE); - Assertions.assertEquals(map.getOrDefault(lastKey, Integer.MIN_VALUE), Integer.MIN_VALUE); - - Assertions.assertEquals(map.size(), ints.length); - - for (int i = start; i < ints.length; i++) { - Assertions.assertTrue(map.containsKey(i)); - Assertions.assertEquals(map.get(i), ints[i - start]); - } - - for (int i = start - 1; i >= (start - 6); i--) { - // Lower than expected check - Assertions.assertFalse(map.containsKey(i), i + " is in a map that starts with " + start); - } - - for (int i = ints.length + start; i < ints.length + 5 + start; i++) { - // Higher than expected check - Assertions.assertFalse(map.containsKey(i), i + " is in a map that ends with " + (start + ints.length)); - } - - for (int i : ints) { - Assertions.assertTrue(map.containsValue(i)); - } - } -}