From f7ef90278b57d48b3e3f12d9759872a9753182e4 Mon Sep 17 00:00:00 2001 From: Redned Date: Mon, 12 Jul 2021 21:19:40 -0400 Subject: [PATCH] Implement a new registry system (#2306) Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- README.md | 2 +- bootstrap/bungeecord/pom.xml | 4 +- bootstrap/pom.xml | 2 +- bootstrap/spigot/pom.xml | 4 +- .../GeyserSpigot1_11CraftingListener.java | 6 +- .../world/GeyserSpigotBlockPlaceListener.java | 7 +- .../GeyserSpigot1_12NativeWorldManager.java | 4 +- .../manager/GeyserSpigot1_12WorldManager.java | 6 +- .../GeyserSpigotFallbackWorldManager.java | 4 +- .../GeyserSpigotNativeWorldManager.java | 4 +- .../manager/GeyserSpigotWorldManager.java | 9 +- bootstrap/sponge/pom.xml | 4 +- bootstrap/standalone/pom.xml | 4 +- bootstrap/velocity/pom.xml | 4 +- common/pom.xml | 2 +- connector/pom.xml | 8 +- .../geysermc/connector/GeyserConnector.java | 27 +- .../entity/AreaEffectCloudEntity.java | 4 +- .../entity/CommandBlockMinecartEntity.java | 2 +- .../entity/DefaultBlockMinecartEntity.java | 4 +- .../connector/entity/FallingBlockEntity.java | 2 +- .../connector/entity/FishingHookEntity.java | 10 +- .../entity/FurnaceMinecartEntity.java | 4 +- .../connector/entity/ItemFrameEntity.java | 13 +- .../connector/entity/LivingEntity.java | 12 +- .../connector/entity/MinecartEntity.java | 2 +- .../entity/SpawnerMinecartEntity.java | 4 +- .../connector/entity/ThrownPotionEntity.java | 9 +- .../entity/living/animal/AnimalEntity.java | 4 +- .../entity/living/animal/AxolotlEntity.java | 4 +- .../entity/living/animal/BeeEntity.java | 6 +- .../entity/living/animal/ChickenEntity.java | 4 +- .../entity/living/animal/FoxEntity.java | 6 +- .../entity/living/animal/HoglinEntity.java | 4 +- .../entity/living/animal/OcelotEntity.java | 4 +- .../entity/living/animal/PandaEntity.java | 7 +- .../entity/living/animal/PigEntity.java | 4 +- .../entity/living/animal/PolarBearEntity.java | 4 +- .../entity/living/animal/RabbitEntity.java | 4 +- .../entity/living/animal/StriderEntity.java | 4 +- .../entity/living/animal/TurtleEntity.java | 4 +- .../animal/horse/AbstractHorseEntity.java | 7 +- .../living/animal/horse/LlamaEntity.java | 7 +- .../living/animal/tameable/CatEntity.java | 4 +- .../living/animal/tameable/ParrotEntity.java | 4 +- .../living/animal/tameable/WolfEntity.java | 4 +- .../living/merchant/VillagerEntity.java | 4 +- .../living/monster/EnderDragonEntity.java | 2 - .../entity/living/monster/EndermanEntity.java | 2 +- .../entity/living/monster/PiglinEntity.java | 3 +- .../living/monster/raid/PillagerEntity.java | 7 +- .../connector/inventory/GeyserItemStack.java | 7 +- .../connector/network/BedrockProtocol.java | 2 + .../network/UpstreamPacketHandler.java | 11 +- .../network/session/GeyserSession.java | 31 +- .../network/session/UpstreamSession.java | 9 + .../network/session/cache/BookEditCache.java | 3 +- .../network/session/cache/ChunkCache.java | 14 +- .../network/session/cache/TagCache.java | 14 +- .../BedrockBlockPickRequestTranslator.java | 7 +- .../BedrockEntityPickRequestTranslator.java | 7 +- ...BedrockInventoryTransactionTranslator.java | 42 +- .../BedrockMobEquipmentTranslator.java | 3 +- .../player/BedrockActionTranslator.java | 22 +- .../player/BedrockInteractTranslator.java | 3 +- .../collision/CollisionManager.java | 11 +- .../translators/effect/EffectRegistry.java | 182 - .../inventory/InventoryTranslator.java | 8 +- .../holder/BlockInventoryHolder.java | 10 +- .../CartographyInventoryTranslator.java | 4 +- .../translators/LoomInventoryTranslator.java | 2 +- .../PlayerInventoryTranslator.java | 13 +- .../ShulkerInventoryTranslator.java | 3 +- .../StonecutterInventoryTranslator.java | 2 +- .../chest/DoubleChestInventoryTranslator.java | 12 +- .../updater/ChestInventoryUpdater.java | 5 +- .../translators/item/ItemRegistry.java | 628 -- .../translators/item/ItemTranslator.java | 64 +- .../item/NbtItemStackTranslator.java | 15 +- .../translators/item/StoredItemMappings.java | 78 + .../item/translators/BannerTranslator.java | 26 +- .../item/translators/CompassTranslator.java | 33 +- .../item/translators/PotionTranslator.java | 29 +- .../translators/TippedArrowTranslator.java | 37 +- .../nbt/AxolotlBucketTranslator.java | 8 +- .../translators/nbt/BasicItemTranslator.java | 8 +- .../translators/nbt/BookPagesTranslator.java | 6 +- .../translators/nbt/CrossbowTranslator.java | 19 +- .../nbt/EnchantedBookTranslator.java | 10 +- .../nbt/EnchantmentTranslator.java | 6 +- .../nbt/FireworkRocketTranslator.java | 10 +- .../nbt/FireworkStarTranslator.java | 10 +- .../nbt/LeatherArmorTranslator.java | 20 +- .../translators/nbt/MapItemTranslator.java | 10 +- .../translators/nbt/PlayerHeadTranslator.java | 8 +- .../nbt/ShulkerBoxItemTranslator.java | 19 +- .../java/JavaAdvancementsTranslator.java | 2 + .../java/JavaDeclareCommandsTranslator.java | 24 +- .../java/JavaDeclareRecipesTranslator.java | 94 +- .../entity/JavaEntityStatusTranslator.java | 3 +- .../player/JavaPlayerActionAckTranslator.java | 4 +- .../java/title/JavaClearTitlesTranslator.java | 2 + .../title/JavaSetActionBarTextTranslator.java | 2 + .../title/JavaSetSubtitleTextTranslator.java | 2 + .../title/JavaSetTitleTextTranslator.java | 2 + .../JavaSetTitlesAnimationTranslator.java | 2 + .../world/JavaBlockBreakAnimTranslator.java | 6 +- .../java/world/JavaBlockChangeTranslator.java | 8 +- .../java/world/JavaBlockValueTranslator.java | 5 +- .../java/world/JavaChunkDataTranslator.java | 11 +- .../java/world/JavaExplosionTranslator.java | 4 +- .../world/JavaPlayBuiltinSoundTranslator.java | 16 +- .../java/world/JavaPlayEffectTranslator.java | 8 +- .../java/world/JavaPlaySoundTranslator.java | 5 +- .../world/JavaSpawnParticleTranslator.java | 10 +- .../java/world/JavaStopSoundTranslator.java | 5 +- .../java/world/JavaTradeListTranslator.java | 9 +- .../world/JavaUpdateTileEntityTranslator.java | 4 +- .../sound/BlockSoundInteractionHandler.java | 5 +- .../sound/EntitySoundInteractionHandler.java | 5 +- .../block/BucketSoundInteractionHandler.java | 2 +- .../block/GrassPathInteractionHandler.java | 4 +- .../sound/block/HoeInteractionHandler.java | 4 +- .../FeedBabySoundInteractionHandler.java | 4 +- .../MilkEntitySoundInteractionHandler.java | 2 +- .../translators/world/GeyserWorldManager.java | 4 +- .../world/block/BlockStateValues.java | 9 + .../world/block/BlockTranslator.java | 488 -- .../block/entity/BlockEntityTranslator.java | 54 - .../entity/CampfireBlockEntityTranslator.java | 12 +- .../FlowerPotBlockEntityTranslator.java | 4 +- .../entity/SignBlockEntityTranslator.java | 76 +- .../registry/AbstractMappedRegistry.java | 48 + .../connector/registry/BlockRegistries.java | 59 + .../connector/registry/MappedRegistry.java | 57 + .../connector/registry/Registries.java | 101 + .../geysermc/connector/registry/Registry.java | 46 + .../registry/SimpleMappedRegistry.java | 53 + .../connector/registry/SimpleRegistry.java | 52 + .../connector/registry/VersionedRegistry.java | 67 + .../loader/AnnotatedRegistryLoader.java} | 49 +- .../loader/BlockEntityRegistryLoader.java | 35 + .../loader/CollisionRegistryLoader.java} | 58 +- .../registry/loader/EffectRegistryLoader.java | 55 + .../loader/MultiResourceRegistryLoader.java | 31 + .../loader/NbtRegistryLoader.java} | 29 +- .../loader/ParticleTypesRegistryLoader.java | 62 + .../loader/PotionMixRegistryLoader.java} | 56 +- .../registry/loader/RegistryLoader.java | 37 + .../loader/RegistryLoaders.java} | 23 +- .../loader/SoundEffectsRegistryLoader.java | 94 + .../loader/SoundHandlerRegistryLoader.java | 37 + .../loader/SoundRegistryLoader.java} | 71 +- .../populator/BlockRegistryPopulator.java | 373 ++ .../populator/ItemRegistryPopulator.java | 519 ++ .../populator/RecipeRegistryPopulator.java} | 206 +- .../registry/type/BlockMappings.java | 88 + .../type/GeyserMappingItem.java} | 23 +- .../type/ItemMapping.java} | 72 +- .../connector/registry/type/ItemMappings.java | 142 + .../connector/registry/type/PaletteItem.java | 35 + .../registry/type/ParticleMapping.java | 39 + .../connector/registry/type/SoundMapping.java | 47 + .../BiomeUtils.java} | 158 +- .../connector/utils/BlockEntityUtils.java | 45 +- .../geysermc/connector/utils/BlockUtils.java | 38 +- .../geysermc/connector/utils/ChunkUtils.java | 32 +- .../connector/utils/CooldownUtils.java | 6 + .../geysermc/connector/utils/EffectUtils.java | 54 + .../utils/InteractiveTagManager.java | 10 +- .../connector/utils/InventoryUtils.java | 27 +- .../geysermc/connector/utils/ItemUtils.java | 6 +- .../SoundUtils.java} | 87 +- .../connector/utils/StatisticsUtils.java | 17 +- .../bedrock/block_palette.1_17_10.nbt | Bin 0 -> 41318 bytes ..._items.json => creative_items.1_17_0.json} | 0 .../bedrock/creative_items.1_17_10.json | 5203 +++++++++++++++++ ...s.json => runtime_item_states.1_17_0.json} | 0 .../bedrock/runtime_item_states.1_17_10.json | 4290 ++++++++++++++ connector/src/main/resources/mappings | 2 +- pom.xml | 2 +- 181 files changed, 12800 insertions(+), 2483 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/StoredItemMappings.java delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/AbstractMappedRegistry.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/BlockRegistries.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/MappedRegistry.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/Registries.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/Registry.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/SimpleMappedRegistry.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/SimpleRegistry.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/VersionedRegistry.java rename connector/src/main/java/org/geysermc/connector/{network/translators/sound/SoundHandlerRegistry.java => registry/loader/AnnotatedRegistryLoader.java} (57%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/BlockEntityRegistryLoader.java rename connector/src/main/java/org/geysermc/connector/{network/translators/collision/CollisionTranslator.java => registry/loader/CollisionRegistryLoader.java} (78%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/EffectRegistryLoader.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/MultiResourceRegistryLoader.java rename connector/src/main/java/org/geysermc/connector/{network/translators/EntityIdentifierRegistry.java => registry/loader/NbtRegistryLoader.java} (72%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/ParticleTypesRegistryLoader.java rename connector/src/main/java/org/geysermc/connector/{network/translators/item/PotionMixRegistry.java => registry/loader/PotionMixRegistryLoader.java} (70%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoader.java rename connector/src/main/java/org/geysermc/connector/{network/translators/world/block/BlockTranslator1_17_0.java => registry/loader/RegistryLoaders.java} (73%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/SoundEffectsRegistryLoader.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/loader/SoundHandlerRegistryLoader.java rename connector/src/main/java/org/geysermc/connector/{network/translators/sound/SoundRegistry.java => registry/loader/SoundRegistryLoader.java} (59%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/populator/BlockRegistryPopulator.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java rename connector/src/main/java/org/geysermc/connector/{network/translators/item/RecipeRegistry.java => registry/populator/RecipeRegistryPopulator.java} (51%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/type/BlockMappings.java rename connector/src/main/java/org/geysermc/connector/{network/translators/item/TranslatableItemEntry.java => registry/type/GeyserMappingItem.java} (65%) rename connector/src/main/java/org/geysermc/connector/{network/translators/item/ItemEntry.java => registry/type/ItemMapping.java} (52%) create mode 100644 connector/src/main/java/org/geysermc/connector/registry/type/ItemMappings.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/type/PaletteItem.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/type/ParticleMapping.java create mode 100644 connector/src/main/java/org/geysermc/connector/registry/type/SoundMapping.java rename connector/src/main/java/org/geysermc/connector/{network/translators/BiomeTranslator.java => utils/BiomeUtils.java} (70%) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java rename connector/src/main/java/org/geysermc/connector/{network/translators/item/ToolItemEntry.java => utils/SoundUtils.java} (65%) create mode 100644 connector/src/main/resources/bedrock/block_palette.1_17_10.nbt rename connector/src/main/resources/bedrock/{creative_items.json => creative_items.1_17_0.json} (100%) create mode 100644 connector/src/main/resources/bedrock/creative_items.1_17_10.json rename connector/src/main/resources/bedrock/{runtime_item_states.json => runtime_item_states.1_17_0.json} (100%) create mode 100644 connector/src/main/resources/bedrock/runtime_item_states.1_17_10.json diff --git a/README.md b/README.md index 11d7e835d..9da7caf5d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here! -### Currently supporting Minecraft Bedrock 1.17 - 1.17.2 and Minecraft Java 1.17.1. +### Currently supporting Minecraft Bedrock 1.17.0 - 1.17.10 and Minecraft Java 1.17.1. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki/Setup) for how to set up Geyser. diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 710c8176e..0f13390d5 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT bootstrap-bungeecord @@ -14,7 +14,7 @@ org.geysermc connector - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT compile diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index bf31f3fea..98a08ee31 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT bootstrap-parent pom diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 4d295b66d..133ea7779 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT bootstrap-spigot @@ -21,7 +21,7 @@ org.geysermc connector - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT compile diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java index eff79c7cc..4f24af19c 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java @@ -50,7 +50,7 @@ import org.bukkit.inventory.ShapelessRecipe; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.item.RecipeRegistry; +import org.geysermc.connector.utils.InventoryUtils; import java.util.*; @@ -94,7 +94,7 @@ public class GeyserSpigot1_11CraftingListener implements Listener { } public void sendServerRecipes(GeyserSession session) { - int netId = RecipeRegistry.LAST_RECIPE_NET_ID; + int netId = InventoryUtils.LAST_RECIPE_NET_ID; CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); @@ -106,7 +106,7 @@ public class GeyserSpigot1_11CraftingListener implements Listener { Pair outputs = translateToBedrock(session, recipe.getResult()); ItemStack javaOutput = outputs.getKey(); ItemData output = outputs.getValue(); - if (output.getId() == 0) continue; // If items make air we don't want that + if (output == null || output.getId() == 0) continue; // If items make air we don't want that boolean isNotAllAir = false; // Check for all-air recipes if (recipe instanceof ShapedRecipe) { diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java index 51e68a263..8111c7e56 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java @@ -35,7 +35,8 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager; @AllArgsConstructor @@ -53,11 +54,11 @@ public class GeyserSpigotBlockPlaceListener implements Listener { placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())); placeBlockSoundPacket.setBabySound(false); if (worldManager.isLegacy()) { - placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(worldManager.getBlockAt(session, + placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(worldManager.getBlockAt(session, event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()))); } else { String javaBlockId = event.getBlockPlaced().getBlockData().getAsString(); - placeBlockSoundPacket.setExtraData(session.getBlockTranslator().getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, BlockTranslator.JAVA_AIR_ID))); + placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIERS.get().getOrDefault(javaBlockId, BlockStateValues.JAVA_AIR_ID))); } placeBlockSoundPacket.setIdentifier(":"); session.sendUpstreamPacket(placeBlockSoundPacket); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java index c59cb0d77..1875d3451 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12NativeWorldManager.java @@ -31,7 +31,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; @@ -51,7 +51,7 @@ public class GeyserSpigot1_12NativeWorldManager extends GeyserSpigot1_12WorldMan public int getBlockAt(GeyserSession session, int x, int y, int z) { Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); if (player == null) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } // Get block entity storage BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java index b50aefee7..e32b52f24 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java @@ -37,7 +37,7 @@ import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; import java.util.List; @@ -70,11 +70,11 @@ public class GeyserSpigot1_12WorldManager extends GeyserSpigotWorldManager { public int getBlockAt(GeyserSession session, int x, int y, int z) { Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); if (player == null) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } if (!player.getWorld().isChunkLoaded(x >> 4, z >> 4)) { // Prevent nasty async errors if a player is loading in - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } // Get block entity storage BlockStorage storage = Via.getManager().getConnectionManager().getConnectedClient(player.getUniqueId()).get(BlockStorage.class); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java index 8bd2c6628..88721b8b1 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotFallbackWorldManager.java @@ -27,7 +27,7 @@ package org.geysermc.platform.spigot.world.manager; import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; /** * Should only be used when we know {@link GeyserSpigotWorldManager#getBlockAt(GeyserSession, int, int, int)} @@ -41,7 +41,7 @@ public class GeyserSpigotFallbackWorldManager extends GeyserSpigotWorldManager { @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java index 7e0b9267b..91a589da1 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotNativeWorldManager.java @@ -29,7 +29,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.geyser.adapters.spigot.SpigotAdapters; import org.geysermc.geyser.adapters.spigot.SpigotWorldAdapter; @@ -45,7 +45,7 @@ public class GeyserSpigotNativeWorldManager extends GeyserSpigotWorldManager { public int getBlockAt(GeyserSession session, int x, int y, int z) { Player player = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); if (player == null) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } return adapter.getBlockAt(player.getWorld(), x, y, z); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java index 6746a8d1a..be6342172 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigotWorldManager.java @@ -42,7 +42,8 @@ import org.bukkit.plugin.Plugin; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.network.translators.world.GeyserWorldManager; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.GameRule; @@ -68,15 +69,15 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { public int getBlockAt(GeyserSession session, int x, int y, int z) { Player bukkitPlayer; if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } World world = bukkitPlayer.getWorld(); if (!world.isChunkLoaded(x >> 4, z >> 4)) { // If the chunk isn't loaded, how could we even be here? - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } - return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockTranslator.JAVA_AIR_ID); + return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID); } @Override diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index 72733a311..e0f019152 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT bootstrap-sponge @@ -14,7 +14,7 @@ org.geysermc connector - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT compile diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml index 2d28a7535..64aa54745 100644 --- a/bootstrap/standalone/pom.xml +++ b/bootstrap/standalone/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT bootstrap-standalone @@ -14,7 +14,7 @@ org.geysermc connector - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT compile diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index e44d7b48f..c67f5a628 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT bootstrap-velocity @@ -14,7 +14,7 @@ org.geysermc connector - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT compile diff --git a/common/pom.xml b/common/pom.xml index 8ddcfd981..2dfa63a42 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT common diff --git a/connector/pom.xml b/connector/pom.xml index b05a969b9..4c8391766 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -6,7 +6,7 @@ org.geysermc geyser-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT connector @@ -21,7 +21,7 @@ org.geysermc common - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT compile @@ -57,8 +57,8 @@ com.github.CloudburstMC.Protocol - bedrock-v440 - 1656151 + bedrock-v448 + ddfa38b compile diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 9c3386e38..291f2d7e5 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -44,20 +44,11 @@ import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.network.ConnectorServerEventHandler; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.BiomeTranslator; -import org.geysermc.connector.network.translators.EntityIdentifierRegistry; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.Registries; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; -import org.geysermc.connector.network.translators.collision.CollisionTranslator; -import org.geysermc.connector.network.translators.effect.EffectRegistry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.item.PotionMixRegistry; -import org.geysermc.connector.network.translators.item.RecipeRegistry; -import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry; -import org.geysermc.connector.network.translators.sound.SoundRegistry; import org.geysermc.connector.network.translators.world.WorldManager; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; -import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator; import org.geysermc.connector.skin.FloodgateSkinUploader; import org.geysermc.connector.utils.*; @@ -158,19 +149,11 @@ public class GeyserConnector { PacketTranslatorRegistry.init(); /* Initialize translators and registries */ - BiomeTranslator.init(); - BlockTranslator.init(); - BlockEntityTranslator.init(); - EffectRegistry.init(); - EntityIdentifierRegistry.init(); - ItemRegistry.init(); + BlockRegistries.init(); + Registries.init(); + ItemTranslator.init(); - CollisionTranslator.init(); LocaleUtils.init(); - PotionMixRegistry.init(); - RecipeRegistry.init(); - SoundRegistry.init(); - SoundHandlerRegistry.init(); ResourcePack.loadPacks(); diff --git a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java index 5a33bc98c..218275ae8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java @@ -32,7 +32,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.effect.EffectRegistry; +import org.geysermc.connector.utils.EffectUtils; public class AreaEffectCloudEntity extends Entity { @@ -59,7 +59,7 @@ public class AreaEffectCloudEntity extends Entity { metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue()); } else if (entityMetadata.getId() == 11) { Particle particle = (Particle) entityMetadata.getValue(); - int particleId = EffectRegistry.getParticleId(session, particle.getType()); + int particleId = EffectUtils.getParticleId(session, particle.getType()); if (particleId != -1) { metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java index bda4e7972..c04e9f0b7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java @@ -60,7 +60,7 @@ public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { */ @Override public void updateDefaultBlockMetadata(GeyserSession session) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockRuntimeCommandBlockId()); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getCommandBlockRuntimeId()); metadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java index 24e5bb259..5df7ae440 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java @@ -60,7 +60,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { customBlock = (int) entityMetadata.getValue(); if (showCustomBlock) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock)); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock)); } } @@ -77,7 +77,7 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { if (entityMetadata.getId() == 13) { if ((boolean) entityMetadata.getValue()) { showCustomBlock = true; - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(customBlock)); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock)); metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); } else { showCustomBlock = false; diff --git a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java index bd0fe9b80..a82118c21 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java @@ -42,7 +42,7 @@ public class FallingBlockEntity extends Entity { @Override public void spawnEntity(GeyserSession session) { - this.metadata.put(EntityData.VARIANT, session.getBlockTranslator().getBedrockBlockId(javaId)); + this.metadata.put(EntityData.VARIANT, session.getBlockMappings().getBedrockBlockId(javaId)); super.spawnEntity(session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java index db44a0767..66cfe386d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java @@ -36,10 +36,10 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.collision.BoundingBox; import org.geysermc.connector.network.translators.collision.CollisionManager; -import org.geysermc.connector.network.translators.collision.CollisionTranslator; import org.geysermc.connector.network.translators.collision.translators.BlockCollision; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.utils.BlockUtils; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -97,14 +97,14 @@ public class FishingHookEntity extends ThrowableEntity { boolean collided = false; for (Vector3i blockPos : collidableBlocks) { int blockID = session.getConnector().getWorldManager().getBlockAt(session, blockPos); - BlockCollision blockCollision = CollisionTranslator.getCollision(blockID, blockPos.getX(), blockPos.getY(), blockPos.getZ()); + BlockCollision blockCollision = BlockUtils.getCollision(blockID, blockPos.getX(), blockPos.getY(), blockPos.getZ()); if (blockCollision != null && blockCollision.checkIntersection(boundingBox)) { // TODO Push bounding box out of collision to improve movement collided = true; } int waterLevel = BlockStateValues.getWaterLevel(blockID); - if (BlockTranslator.isWaterlogged(blockID)) { + if (BlockRegistries.WATERLOGGED.get().contains(blockID)) { waterLevel = 0; } if (waterLevel >= 0) { @@ -175,7 +175,7 @@ public class FishingHookEntity extends ThrowableEntity { */ protected boolean isInAir(GeyserSession session) { int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); - return block == BlockTranslator.JAVA_AIR_ID; + return block == BlockStateValues.JAVA_AIR_ID; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java index 2eb083883..fea1e62bd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { @@ -52,7 +52,7 @@ public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { @Override public void updateDefaultBlockMetadata(GeyserSession session) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(hasFuel ? BlockTranslator.JAVA_RUNTIME_FURNACE_LIT_ID : BlockTranslator.JAVA_RUNTIME_FURNACE_ID)); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(hasFuel ? BlockStateValues.JAVA_FURNACE_LIT_ID : BlockStateValues.JAVA_FURNACE_ID)); metadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java index f2f36e606..dfa989c1d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -38,9 +38,8 @@ import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import lombok.Getter; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.concurrent.TimeUnit; @@ -85,12 +84,12 @@ public class ItemFrameEntity extends Entity { public void spawnEntity(GeyserSession session) { NbtMapBuilder blockBuilder = NbtMap.builder() .putString("name", this.entityType == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame") - .putInt("version", session.getBlockTranslator().getBlockStateVersion()); + .putInt("version", session.getBlockMappings().getBlockStateVersion()); blockBuilder.put("states", NbtMap.builder() .putInt("facing_direction", direction.ordinal()) .putByte("item_frame_map_bit", (byte) 0) .build()); - bedrockRuntimeId = session.getBlockTranslator().getItemFrame(blockBuilder.build()); + bedrockRuntimeId = session.getBlockMappings().getItemFrame(blockBuilder.build()); bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); session.getItemFrameCache().put(bedrockPosition, this); @@ -108,7 +107,7 @@ public class ItemFrameEntity extends Entity { if (entityMetadata.getId() == 8 && entityMetadata.getValue() != null) { this.heldItem = (ItemStack) entityMetadata.getValue(); ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem); - ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue()); + ItemMapping mapping = session.getItemMappings().getMapping((ItemStack) entityMetadata.getValue()); NbtMapBuilder builder = NbtMap.builder(); builder.putByte("Count", (byte) itemData.getCount()); @@ -116,7 +115,7 @@ public class ItemFrameEntity extends Entity { builder.put("tag", itemData.getTag()); } builder.putShort("Damage", (short) itemData.getDamage()); - builder.putString("Name", itemEntry.getBedrockIdentifier()); + builder.putString("Name", mapping.getBedrockIdentifier()); NbtMapBuilder tag = getDefaultTag().toBuilder(); tag.put("Item", builder.build()); tag.putFloat("ItemDropChance", 1.0f); @@ -149,7 +148,7 @@ public class ItemFrameEntity extends Entity { UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(bedrockPosition); - updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId()); + updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockAirId()); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); diff --git a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java index e1c85fc08..5ba66f71a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java @@ -45,7 +45,7 @@ import lombok.Setter; import org.geysermc.connector.entity.attribute.GeyserAttributeType; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.ChunkUtils; @@ -89,8 +89,9 @@ public class LivingEntity extends Entity { //blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like //you're "mining" with ex. a shield. - boolean isUsingShield = (getHand().getId() == ItemRegistry.SHIELD.getBedrockId() || - getHand().equals(ItemData.AIR) && getOffHand().getId() == ItemRegistry.SHIELD.getBedrockId()); + ItemMapping shield = session.getItemMappings().getStoredItems().shield(); + boolean isUsingShield = (getHand().getId() == shield.getBedrockId() || + getHand().equals(ItemData.AIR) && getOffHand().getId() == shield.getBedrockId()); metadata.getFlags().setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield); metadata.getFlags().setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01); @@ -171,10 +172,11 @@ public class LivingEntity extends Entity { ItemData chestplate = this.chestplate; // If an entity has a banner on them, it will be in the helmet slot in Java but the chestplate spot in Bedrock // But don't overwrite the chestplate if it isn't empty - if (chestplate.getId() == ItemData.AIR.getId() && helmet.getId() == ItemRegistry.BANNER.getBedrockId()) { + ItemMapping banner = session.getItemMappings().getStoredItems().banner(); + if (chestplate.getId() == ItemData.AIR.getId() && helmet.getId() == banner.getBedrockId()) { chestplate = this.helmet; helmet = ItemData.AIR; - } else if (chestplate.getId() == ItemRegistry.BANNER.getBedrockId()) { + } else if (chestplate.getId() == banner.getBedrockId()) { // Prevent chestplate banners from showing erroneously chestplate = ItemData.AIR; } diff --git a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java index 18d1d92a1..6ab9a1b22 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java @@ -57,7 +57,7 @@ public class MinecartEntity extends Entity { if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class // Custom block if (entityMetadata.getId() == 11) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue())); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId((int) entityMetadata.getValue())); } // Custom block offset diff --git a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java index 2f7af73eb..c6d4c3aa5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java @@ -29,7 +29,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity { @@ -39,7 +39,7 @@ public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity { @Override public void updateDefaultBlockMetadata(GeyserSession session) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockTranslator().getBedrockBlockId(BlockTranslator.JAVA_RUNTIME_SPAWNER_ID)); + metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(BlockStateValues.JAVA_SPAWNER_ID)); metadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java index 9ce218a81..735bf274c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java @@ -36,9 +36,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.Potion; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.EnumSet; @@ -53,8 +52,8 @@ public class ThrownPotionEntity extends ThrowableEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 8 && entityMetadata.getType() == MetadataType.ITEM) { ItemStack itemStack = (ItemStack) entityMetadata.getValue(); - ItemEntry itemEntry = ItemRegistry.getItem(itemStack); - if (itemEntry.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) { + ItemMapping mapping = session.getItemMappings().getMapping(itemStack); + if (mapping.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) { Tag potionTag = itemStack.getNbt().get("Potion"); if (potionTag instanceof StringTag) { Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); @@ -67,7 +66,7 @@ public class ThrownPotionEntity extends ThrowableEntity { } } - boolean isLingering = itemEntry.getJavaIdentifier().equals("minecraft:lingering_potion"); + boolean isLingering = mapping.getJavaIdentifier().equals("minecraft:lingering_potion"); metadata.getFlags().setFlag(EntityFlag.LINGERING, isLingering); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java index efded1f6f..2495eab0a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java @@ -29,7 +29,7 @@ import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.living.AgeableEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class AnimalEntity extends AgeableEntity { @@ -42,7 +42,7 @@ public class AnimalEntity extends AgeableEntity { * wheat. * @return true if this is a valid item to breed with for this animal. */ - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { // This is what it defaults to. OK. return javaIdentifierStripped.equals("wheat"); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java index 7c6c81ba2..f3d634140 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java @@ -31,7 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class AxolotlEntity extends AnimalEntity { public AxolotlEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { @@ -59,7 +59,7 @@ public class AxolotlEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("tropical_fish_bucket"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java index 59035bb85..07136252a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java @@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class BeeEntity extends AnimalEntity { @@ -66,7 +66,7 @@ public class BeeEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { - return session.getTagCache().isFlower(itemEntry); + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { + return session.getTagCache().isFlower(mapping); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java index b5f0395d4..c7eb62c6e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java @@ -28,7 +28,7 @@ package org.geysermc.connector.entity.living.animal; import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class ChickenEntity extends AnimalEntity { @@ -37,7 +37,7 @@ public class ChickenEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.contains("seeds"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java index 2c1934139..26da89612 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java @@ -31,7 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class FoxEntity extends AnimalEntity { @@ -55,7 +55,7 @@ public class FoxEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { - return session.getTagCache().isFoxFood(itemEntry); + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { + return session.getTagCache().isFoxFood(mapping); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java index 492ff68a8..d782dc53e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.DimensionUtils; public class HoglinEntity extends AnimalEntity { @@ -57,7 +57,7 @@ public class HoglinEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("crimson_fungus"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java index 48a5e08bf..98e7140cd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class OcelotEntity extends AnimalEntity { @@ -47,7 +47,7 @@ public class OcelotEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java index 83f0ed8d9..39049d91a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java @@ -33,8 +33,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.registry.type.ItemMapping; public class PandaEntity extends AnimalEntity { @@ -55,7 +54,7 @@ public class PandaEntity extends AnimalEntity { EntityEventPacket packet = new EntityEventPacket(); packet.setRuntimeEntityId(geyserId); packet.setType(EntityEventType.EATING_ITEM); - packet.setData(ItemRegistry.BAMBOO.getBedrockId() << 16); + packet.setData(session.getItemMappings().getStoredItems().bamboo().getBedrockId() << 16); session.sendUpstreamPacket(packet); } } @@ -81,7 +80,7 @@ public class PandaEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("bamboo"); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java index bc07472ea..ba6bfd553 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class PigEntity extends AnimalEntity { @@ -47,7 +47,7 @@ public class PigEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java index 8f292411d..893def315 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class PolarBearEntity extends AnimalEntity { @@ -47,7 +47,7 @@ public class PolarBearEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return false; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java index 41579d6a6..0f0e1fb3e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java @@ -31,7 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class RabbitEntity extends AnimalEntity { @@ -65,7 +65,7 @@ public class RabbitEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot"); } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java index 437f60cb4..83c1e3674 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java @@ -31,7 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class StriderEntity extends AnimalEntity { @@ -93,7 +93,7 @@ public class StriderEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("warped_fungus"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java index 7e9e3260d..b26b21ee2 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class TurtleEntity extends AnimalEntity { @@ -49,7 +49,7 @@ public class TurtleEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("seagrass"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index d4aed4196..e255d2856 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -38,8 +38,7 @@ import org.geysermc.connector.entity.attribute.GeyserAttributeType; import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.Set; @@ -105,7 +104,7 @@ public class AbstractHorseEntity extends AnimalEntity { EntityEventPacket entityEventPacket = new EntityEventPacket(); entityEventPacket.setRuntimeEntityId(geyserId); entityEventPacket.setType(EntityEventType.EATING_ITEM); - entityEventPacket.setData(ItemRegistry.WHEAT.getBedrockId() << 16); + entityEventPacket.setData(session.getItemMappings().getStoredItems().wheat().getBedrockId() << 16); session.sendUpstreamPacket(entityEventPacket); } @@ -120,7 +119,7 @@ public class AbstractHorseEntity extends AnimalEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index 67e3304d3..14e625c81 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -32,8 +32,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.registry.type.ItemMapping; public class LlamaEntity extends ChestedHorseEntity { @@ -59,7 +58,7 @@ public class LlamaEntity extends ChestedHorseEntity { if (carpetIndex > -1 && carpetIndex <= 15) { // The damage value is the dye color that Java sends us, for pre-1.16.220 // The item is always going to be a carpet - equipmentPacket.setChestplate(ItemRegistry.CARPETS.get(carpetIndex)); + equipmentPacket.setChestplate(session.getItemMappings().getCarpets().get(carpetIndex)); } else { equipmentPacket.setChestplate(ItemData.AIR); } @@ -78,7 +77,7 @@ public class LlamaEntity extends ChestedHorseEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java index ac4142988..904f563f9 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java @@ -31,7 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class CatEntity extends TameableEntity { @@ -91,7 +91,7 @@ public class CatEntity extends TameableEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java index dcc9d6f78..1bb75931e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java @@ -30,7 +30,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; public class ParrotEntity extends TameableEntity { @@ -48,7 +48,7 @@ public class ParrotEntity extends TameableEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { return javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie"); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index 0a5d2a58c..183719dbb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -32,7 +32,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.Set; @@ -88,7 +88,7 @@ public class WolfEntity extends TameableEntity { } @Override - public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) { + public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { // Cannot be a baby to eat these foods return WOLF_FOODS.contains(javaIdentifierStripped) && !metadata.getFlags().getFlag(EntityFlag.BABY); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java index 1ba8b595b..3c94f9de2 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java @@ -36,7 +36,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.BlockRegistries; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -114,7 +114,7 @@ public class VillagerEntity extends AbstractMerchantEntity { if (bedPosition != null) { bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); } - String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId); + String bedRotationZ = BlockRegistries.JAVA_IDENTIFIERS.get().inverse().get(bedId); setRotation(rotation); setOnGround(isOnGround); this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java index 8e9dca997..d3d6e30d5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java @@ -27,7 +27,6 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; @@ -35,7 +34,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.*; import lombok.Data; import org.geysermc.connector.entity.Tickable; -import org.geysermc.connector.entity.attribute.GeyserAttributeType; import org.geysermc.connector.entity.living.InsentientEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java index f11e57a8f..e187d7f0b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java @@ -44,7 +44,7 @@ public class EndermanEntity extends MonsterEntity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { // Held block if (entityMetadata.getId() == 16) { - metadata.put(EntityData.CARRIED_BLOCK, session.getBlockTranslator().getBedrockBlockId((int) entityMetadata.getValue())); + metadata.put(EntityData.CARRIED_BLOCK, session.getBlockMappings().getBedrockBlockId((int) entityMetadata.getValue())); } // "Is screaming" - controls sound if (entityMetadata.getId() == 17) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java index 5b1ccd342..860f6dfd3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java @@ -31,7 +31,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemRegistry; public class PiglinEntity extends BasePiglinEntity { @@ -61,7 +60,7 @@ public class PiglinEntity extends BasePiglinEntity { @Override public void updateOffHand(GeyserSession session) { // Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates - boolean changed = metadata.getFlags().setFlag(EntityFlag.ADMIRING, session.getTagCache().shouldPiglinAdmire(ItemRegistry.getItem(this.offHand))); + boolean changed = metadata.getFlags().setFlag(EntityFlag.ADMIRING, session.getTagCache().shouldPiglinAdmire(session.getItemMappings().getMapping(this.offHand))); if (changed) { super.updateBedrockMetadata(session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java index 73794586f..325cac7ab 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java @@ -29,7 +29,7 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.registry.type.ItemMapping; public class PillagerEntity extends AbstractIllagerEntity { @@ -55,8 +55,9 @@ public class PillagerEntity extends AbstractIllagerEntity { * Check for a crossbow in either the mainhand or offhand. If one exists, indicate that the pillager should be posing */ protected void checkForCrossbow(GeyserSession session) { - boolean hasCrossbow = this.hand.getId() == ItemRegistry.CROSSBOW.getBedrockId() - || this.offHand.getId() == ItemRegistry.CROSSBOW.getBedrockId(); + ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow(); + boolean hasCrossbow = this.hand.getId() == crossbow.getBedrockId() + || this.offHand.getId() == crossbow.getBedrockId(); boolean usingItemChanged = metadata.getFlags().setFlag(EntityFlag.USING_ITEM, hasCrossbow); boolean chargedChanged = metadata.getFlags().setFlag(EntityFlag.CHARGED, hasCrossbow); diff --git a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java index 1cfd425d9..b80757fbd 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/GeyserItemStack.java @@ -30,9 +30,8 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import lombok.Data; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.registry.type.ItemMapping; @Data public class GeyserItemStack { @@ -105,8 +104,8 @@ public class GeyserItemStack { return itemData; } - public ItemEntry getItemEntry() { - return ItemRegistry.ITEM_ENTRIES.get(getJavaId()); + public ItemMapping getMapping(GeyserSession session) { + return session.getItemMappings().getMapping(this.javaId); } public boolean isEmpty() { diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java index 84fc449e9..0be6eefae 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v440.Bedrock_v440; +import com.nukkitx.protocol.bedrock.v448.Bedrock_v448; import java.util.ArrayList; import java.util.List; @@ -47,6 +48,7 @@ public class BedrockProtocol { static { SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v448.V448_CODEC); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index be34ca81b..159924541 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -35,8 +35,8 @@ import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.Registries; import org.geysermc.connector.utils.*; import java.io.FileInputStream; @@ -71,7 +71,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { session.getUpstream().getSession().setPacketCodec(packetCodec); // Set the block translation based off of version - session.setBlockTranslator(BlockTranslator1_17_0.INSTANCE); + session.setBlockMappings(BlockRegistries.BLOCKS.forVersion(loginPacket.getProtocolVersion())); + session.setItemMappings(Registries.ITEMS.forVersion(loginPacket.getProtocolVersion())); LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket); @@ -131,7 +132,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), "")); } - if (ItemRegistry.FURNACE_MINECART_DATA != null) { + if (session.getItemMappings().getFurnaceMinecartData() != null) { // Allow custom items to work stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true)); } @@ -194,6 +195,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { titlePacket.setFadeInTime(0); titlePacket.setFadeOutTime(1); titlePacket.setStayTime(2); + titlePacket.setXuid(""); + titlePacket.setPlatformOnlineId(""); session.sendUpstreamPacket(titlePacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 8c5455d1e..512f248ac 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -86,14 +86,13 @@ import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; import org.geysermc.connector.network.session.cache.*; -import org.geysermc.connector.network.translators.BiomeTranslator; -import org.geysermc.connector.network.translators.EntityIdentifierRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.network.translators.collision.CollisionManager; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.BlockMappings; +import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.connector.skin.FloodgateSkinUploader; import org.geysermc.connector.skin.SkinManager; import org.geysermc.connector.utils.*; @@ -175,10 +174,16 @@ public class GeyserSession implements CommandSender { private final CollisionManager collisionManager; /** - * Stores the block translations for this specific version. + * Stores the block mappings for this specific version. */ @Setter - private BlockTranslator blockTranslator; + private BlockMappings blockMappings; + + /** + * Stores the item translations for this specific version. + */ + @Setter + private ItemMappings itemMappings; private final Map skullCache = new ConcurrentHashMap<>(); private final Long2ObjectMap storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); @@ -487,26 +492,26 @@ public class GeyserSession implements CommandSender { this.remoteAuthType = connector.getDefaultAuthType(); // Set the hardcoded shield ID to the ID we just defined in StartGamePacket - upstream.getSession().getHardcodedBlockingId().set(ItemRegistry.SHIELD.getBedrockId()); + upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId()); - if (ItemRegistry.FURNACE_MINECART_DATA != null) { + if (this.itemMappings.getFurnaceMinecartData() != null) { ItemComponentPacket componentPacket = new ItemComponentPacket(); - componentPacket.getItems().add(ItemRegistry.FURNACE_MINECART_DATA); + componentPacket.getItems().add(this.itemMappings.getFurnaceMinecartData()); upstream.sendPacket(componentPacket); } ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false); BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); - biomeDefinitionListPacket.setDefinitions(BiomeTranslator.BIOMES); + biomeDefinitionListPacket.setDefinitions(Registries.BIOMES.get()); upstream.sendPacket(biomeDefinitionListPacket); AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); - entityPacket.setIdentifiers(EntityIdentifierRegistry.ENTITY_IDENTIFIERS); + entityPacket.setIdentifiers(Registries.ENTITY_IDENTIFIERS.get()); upstream.sendPacket(entityPacket); CreativeContentPacket creativePacket = new CreativeContentPacket(); - creativePacket.setContents(ItemRegistry.CREATIVE_ITEMS); + creativePacket.setContents(this.itemMappings.getCreativeItems()); upstream.sendPacket(creativePacket); PlayStatusPacket playStatusPacket = new PlayStatusPacket(); @@ -1054,7 +1059,7 @@ public class GeyserSession implements CommandSender { // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); - startGamePacket.setItemEntries(ItemRegistry.ITEMS); + startGamePacket.setItemEntries(this.itemMappings.getItemEntries()); startGamePacket.setVanillaVersion("*"); startGamePacket.setInventoriesServerAuthoritative(true); startGamePacket.setServerEngine(""); // Do we want to fill this in? diff --git a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java index f973574b0..11df3139c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java @@ -63,4 +63,13 @@ public class UpstreamSession { public InetSocketAddress getAddress() { return session.getRealAddress(); } + + /** + * Gets the session's protocol version. + * + * @return the session's protocol version. + */ + public int getProtocolVersion() { + return this.session.getPacketCodec().getProtocolVersion(); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/BookEditCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/BookEditCache.java index cb3737895..d2ee8f552 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/BookEditCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/BookEditCache.java @@ -29,7 +29,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientEditB import lombok.Setter; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemRegistry; /** * Manages updating the current writable book. @@ -64,7 +63,7 @@ public class BookEditCache { } // Don't send the update if the player isn't not holding a book, shouldn't happen if we catch all interactions GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand(); - if (itemStack == null || itemStack.getJavaId() != ItemRegistry.WRITABLE_BOOK.getJavaId()) { + if (itemStack == null || itemStack.getJavaId() != this.session.getItemMappings().getStoredItems().writableBook().getJavaId()) { packet = null; return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index 4f93b0f53..72d271a79 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -31,7 +31,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import lombok.Setter; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.chunk.GeyserColumn; import org.geysermc.connector.utils.MathUtils; @@ -81,11 +81,11 @@ public class ChunkCache { Chunk chunk = column.getChunks()[(y >> 4) - getChunkMinY()]; if (chunk == null) { - if (block != BlockTranslator.JAVA_AIR_ID) { + if (block != BlockStateValues.JAVA_AIR_ID) { // A previously empty chunk, which is no longer empty as a block has been added to it chunk = new Chunk(); // Fixes the chunk assuming that all blocks is the `block` variable we are updating. /shrug - chunk.getPalette().stateToId(BlockTranslator.JAVA_AIR_ID); + chunk.getPalette().stateToId(BlockStateValues.JAVA_AIR_ID); column.getChunks()[(y >> 4) - getChunkMinY()] = chunk; } else { // Nothing to update @@ -98,17 +98,17 @@ public class ChunkCache { public int getBlockAt(int x, int y, int z) { if (!cache) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } GeyserColumn column = this.getChunk(x >> 4, z >> 4); if (column == null) { - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } if (y < minY || (y >> 4) > column.getChunks().length - 1) { // Y likely goes above or below the height limit of this world - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } Chunk chunk = column.getChunks()[(y >> 4) - getChunkMinY()]; @@ -116,7 +116,7 @@ public class ChunkCache { return chunk.get(x & 0xF, y & 0xF, z & 0xF); } - return BlockTranslator.JAVA_AIR_ID; + return BlockStateValues.JAVA_AIR_ID; } public void removeChunk(int chunkX, int chunkZ) { diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java index b1427a864..e10e4dad5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/TagCache.java @@ -28,8 +28,8 @@ package org.geysermc.connector.network.session.cache; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareTagsPacket; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntLists; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.registry.type.BlockMapping; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.Map; @@ -99,16 +99,16 @@ public class TagCache { this.piglinLoved = IntLists.emptyList(); } - public boolean isFlower(ItemEntry itemEntry) { - return flowers.contains(itemEntry.getJavaId()); + public boolean isFlower(ItemMapping mapping) { + return flowers.contains(mapping.getJavaId()); } - public boolean isFoxFood(ItemEntry itemEntry) { - return foxFood.contains(itemEntry.getJavaId()); + public boolean isFoxFood(ItemMapping mapping) { + return foxFood.contains(mapping.getJavaId()); } - public boolean shouldPiglinAdmire(ItemEntry itemEntry) { - return piglinLoved.contains(itemEntry.getJavaId()); + public boolean shouldPiglinAdmire(ItemMapping mapping) { + return piglinLoved.contains(mapping.getJavaId()); } public boolean isAxeEffective(BlockMapping blockMapping) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java index 493789bbb..3a3a4bbed 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java @@ -32,7 +32,8 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.utils.InventoryUtils; @Translator(packet = BlockPickRequestPacket.class) @@ -44,7 +45,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE) { // Otherwise insufficient permissions - int blockState = session.getBlockTranslator().getJavaBlockState(packet.getBlockRuntimeId()); - String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, ""); + int blockState = session.getBlockMappings().getJavaBlockState(packet.getBlockRuntimeId()); + String blockName = BlockRegistries.JAVA_IDENTIFIERS.get().inverse().getOrDefault(blockState, ""); // In the future this can be used for structure blocks too, however not all elements // are available in each GUI if (blockName.contains("jigsaw")) { @@ -243,7 +246,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator switch (packet.getAction()) { case INTERACT: - if (session.getPlayerInventory().getItemInHand().getJavaId() == ItemRegistry.SHIELD.getJavaId()) { + if (session.getPlayerInventory().getItemInHand().getJavaId() == session.getItemMappings().getStoredItems().shield().getJavaId()) { break; } ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java index b6d0ae176..ceb528650 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java @@ -39,7 +39,8 @@ import org.geysermc.connector.entity.player.PlayerEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.collision.translators.BlockCollision; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.utils.BlockUtils; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -228,7 +229,7 @@ public class CollisionManager { // Used when correction code needs to be run before the main correction for (Vector3i blockPos : collidableBlocks) { - BlockCollision blockCollision = CollisionTranslator.getCollisionAt( + BlockCollision blockCollision = BlockUtils.getCollisionAt( session, blockPos.getX(), blockPos.getY(), blockPos.getZ() ); if (blockCollision != null) { @@ -238,7 +239,7 @@ public class CollisionManager { // Main correction code for (Vector3i blockPos : collidableBlocks) { - BlockCollision blockCollision = CollisionTranslator.getCollisionAt( + BlockCollision blockCollision = BlockUtils.getCollisionAt( session, blockPos.getX(), blockPos.getY(), blockPos.getZ() ); if (blockCollision != null) { @@ -259,7 +260,7 @@ public class CollisionManager { */ public boolean isUnderSlab() { Vector3i position = session.getPlayerEntity().getPosition().toInt(); - BlockCollision collision = CollisionTranslator.getCollisionAt(session, position.getX(), position.getY(), position.getZ()); + BlockCollision collision = BlockUtils.getCollisionAt(session, position.getX(), position.getY(), position.getZ()); if (collision != null) { // Determine, if the player's bounding box *were* at full height, if it would intersect with the block // at the current location. @@ -275,7 +276,7 @@ public class CollisionManager { * @return if the player is currently in a water block */ public boolean isPlayerInWater() { - return session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID; + return session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockStateValues.JAVA_WATER_ID; } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java deleted file mode 100644 index 91b21fb2a..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2019-2021 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.connector.network.translators.effect; - -import com.fasterxml.jackson.databind.JsonNode; -import com.github.steveice10.mc.protocol.data.game.world.effect.SoundEffect; -import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType; -import com.nukkitx.protocol.bedrock.data.LevelEventType; -import com.nukkitx.protocol.bedrock.data.SoundEvent; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import lombok.NonNull; -import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.utils.FileUtils; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * Registry for particles and effects. - */ -public class EffectRegistry { - - public static final Map SOUND_EFFECTS = new HashMap<>(); - public static final Int2ObjectMap RECORDS = new Int2ObjectOpenHashMap<>(); - - /** - * Java particle type to Bedrock level event - */ - private static final Map PARTICLE_TO_LEVEL_EVENT = new HashMap<>(); - /** - * Java particle type to Bedrock namespaced string ID - */ - private static final Map PARTICLE_TO_STRING = new HashMap<>(); - - public static void init() { - // no-op - } - - static { - /* Load particles */ - InputStream particleStream = FileUtils.getResource("mappings/particles.json"); - JsonNode particleEntries; - try { - particleEntries = GeyserConnector.JSON_MAPPER.readTree(particleStream); - } catch (Exception e) { - throw new AssertionError("Unable to load particle map", e); - } - - Iterator> particlesIterator = particleEntries.fields(); - try { - while (particlesIterator.hasNext()) { - Map.Entry entry = particlesIterator.next(); - JsonNode bedrockId = entry.getValue().get("bedrockId"); - JsonNode eventType = entry.getValue().get("eventType"); - if (bedrockId != null) { - PARTICLE_TO_STRING.put(ParticleType.valueOf(entry.getKey().toUpperCase()), bedrockId.asText()); - } - if (eventType != null) { - PARTICLE_TO_LEVEL_EVENT.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(eventType.asText().toUpperCase())); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - /* Load effects */ - InputStream effectsStream = FileUtils.getResource("mappings/effects.json"); - JsonNode effects; - try { - effects = GeyserConnector.JSON_MAPPER.readTree(effectsStream); - } catch (Exception e) { - throw new AssertionError("Unable to load effects mappings", e); - } - - Iterator> effectsIterator = effects.fields(); - while (effectsIterator.hasNext()) { - Map.Entry entry = effectsIterator.next(); - JsonNode node = entry.getValue(); - try { - String type = node.get("type").asText(); - SoundEffect javaEffect = null; - Effect effect = null; - switch (type) { - case "soundLevel": { - javaEffect = SoundEffect.valueOf(entry.getKey()); - LevelEventType levelEventType = LevelEventType.valueOf(node.get("name").asText()); - int data = node.has("data") ? node.get("data").intValue() : 0; - effect = new SoundLevelEffect(levelEventType, data); - break; - } - case "soundEvent": { - javaEffect = SoundEffect.valueOf(entry.getKey()); - SoundEvent soundEvent = SoundEvent.valueOf(node.get("name").asText()); - String identifier = node.has("identifier") ? node.get("identifier").asText() : ""; - int extraData = node.has("extraData") ? node.get("extraData").intValue() : -1; - effect = new SoundEventEffect(soundEvent, identifier, extraData); - break; - } - case "playSound": { - javaEffect = SoundEffect.valueOf(entry.getKey()); - String name = node.get("name").asText(); - float volume = node.has("volume") ? node.get("volume").floatValue() : 1.0f; - boolean pitchSub = node.has("pitch_sub") && node.get("pitch_sub").booleanValue(); - float pitchMul = node.has("pitch_mul") ? node.get("pitch_mul").floatValue() : 1.0f; - float pitchAdd = node.has("pitch_add") ? node.get("pitch_add").floatValue() : 0.0f; - boolean relative = node.has("relative") && node.get("relative").booleanValue(); - effect = new PlaySoundEffect(name, volume, pitchSub, pitchMul, pitchAdd, relative); - break; - } - case "record": { - // Special case handled in ItemRegistry - break; - } - } - if (javaEffect != null) { - SOUND_EFFECTS.put(javaEffect, effect); - } - } catch (Exception e) { - GeyserConnector.getInstance().getLogger().warning("Failed to map sound effect " + entry.getKey() + " : " + e.toString()); - } - } - } - - /** - * Used for area effect clouds. - * - * @param type the Java particle to search for - * @return the Bedrock integer ID of the particle, or -1 if it does not exist - */ - public static int getParticleId(GeyserSession session, @NonNull ParticleType type) { - LevelEventType levelEventType = getParticleLevelEventType(type); - if (levelEventType == null) { - return -1; - } - - // Remove the legacy bit applied to particles for LevelEventType serialization - return session.getUpstream().getSession().getPacketCodec().getHelper().getLevelEventId(levelEventType) & ~0x4000; - } - - /** - * @param type the Java particle to search for - * @return the level event equivalent Bedrock particle - */ - public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) { - return PARTICLE_TO_LEVEL_EVENT.getOrDefault(type, null); - } - - /** - * @param type the Java particle to search for - * @return the namespaced ID equivalent for Bedrock - */ - public static String getParticleString(@NonNull ParticleType type) { - return PARTICLE_TO_STRING.getOrDefault(type, null); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java index c8af7f38f..2ca80041b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java @@ -843,7 +843,7 @@ public abstract class InventoryTranslator { for (int slot : affectedSlots) { BedrockContainerSlot bedrockSlot = javaSlotToBedrockContainer(slot); List list = containerMap.computeIfAbsent(bedrockSlot.getContainer(), k -> new ArrayList<>()); - list.add(makeItemEntry(bedrockSlot.getSlot(), inventory.getItem(slot))); + list.add(makeItemEntry(session, bedrockSlot.getSlot(), inventory.getItem(slot))); } List containerEntries = new ArrayList<>(); @@ -851,13 +851,13 @@ public abstract class InventoryTranslator { containerEntries.add(new ItemStackResponsePacket.ContainerEntry(entry.getKey(), entry.getValue())); } - ItemStackResponsePacket.ItemEntry cursorEntry = makeItemEntry(0, session.getPlayerInventory().getCursor()); + ItemStackResponsePacket.ItemEntry cursorEntry = makeItemEntry(session, 0, session.getPlayerInventory().getCursor()); containerEntries.add(new ItemStackResponsePacket.ContainerEntry(ContainerSlotType.CURSOR, Collections.singletonList(cursorEntry))); return containerEntries; } - public static ItemStackResponsePacket.ItemEntry makeItemEntry(int bedrockSlot, GeyserItemStack itemStack) { + public static ItemStackResponsePacket.ItemEntry makeItemEntry(GeyserSession session, int bedrockSlot, GeyserItemStack itemStack) { ItemStackResponsePacket.ItemEntry itemEntry; if (!itemStack.isEmpty()) { // As of 1.16.210: Bedrock needs confirmation on what the current item durability is. @@ -866,7 +866,7 @@ public abstract class InventoryTranslator { if (itemStack.getNbt() != null) { Tag damage = itemStack.getNbt().get("Damage"); if (damage instanceof IntTag) { - durability = ItemUtils.getCorrectBedrockDurability(itemStack.getJavaId(), ((IntTag) damage).getValue()); + durability = ItemUtils.getCorrectBedrockDurability(session, itemStack.getJavaId(), ((IntTag) damage).getValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java index b7f67879b..bb633780a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java @@ -38,7 +38,7 @@ import org.geysermc.connector.inventory.Container; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.BlockRegistries; import java.util.Collections; import java.util.HashSet; @@ -57,7 +57,7 @@ public class BlockInventoryHolder extends InventoryHolder { private final Set validBlocks; public BlockInventoryHolder(String javaBlockIdentifier, ContainerType containerType, String... validBlocks) { - this.defaultJavaBlockState = BlockTranslator.getJavaBlockState(javaBlockIdentifier); + this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIERS.get(javaBlockIdentifier); this.containerType = containerType; if (validBlocks != null) { Set validBlocksTemp = new HashSet<>(validBlocks.length + 1); @@ -77,7 +77,7 @@ public class BlockInventoryHolder extends InventoryHolder { if (checkInteractionPosition(session)) { // Then, check to see if the interacted block is valid for this inventory by ensuring the block state identifier is valid int javaBlockId = session.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition()); - String[] javaBlockString = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\["); + String[] javaBlockString = BlockRegistries.JAVA_IDENTIFIERS.get().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\["); if (isValidBlock(javaBlockString)) { // We can safely use this block inventory.setHolderPosition(session.getLastInteractionBlockPosition()); @@ -93,7 +93,7 @@ public class BlockInventoryHolder extends InventoryHolder { UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); blockPacket.setBlockPosition(position); - blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(defaultJavaBlockState)); + blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState)); blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY); session.sendUpstreamPacket(blockPacket); inventory.setHolderPosition(position); @@ -158,7 +158,7 @@ public class BlockInventoryHolder extends InventoryHolder { UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); blockPacket.setBlockPosition(holderPos); - blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(realBlock)); + blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(realBlock)); blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY); session.sendUpstreamPacket(blockPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java index a3b50dace..76bf21bd3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/CartographyInventoryTranslator.java @@ -48,11 +48,11 @@ public class CartographyInventoryTranslator extends AbstractBlockInventoryTransl if (javaDestinationSlot == 0) { // Bedrock Edition can use paper or an empty map in slot 0 GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot); - return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:paper") || itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:map"); + return itemStack.getMapping(session).getJavaIdentifier().equals("minecraft:paper") || itemStack.getMapping(session).getJavaIdentifier().equals("minecraft:map"); } else if (javaDestinationSlot == 1) { // Bedrock Edition can use a compass to create locator maps, or use a filled map, in the ADDITIONAL slot GeyserItemStack itemStack = javaSourceSlot == -1 ? session.getPlayerInventory().getCursor() : inventory.getItem(javaSourceSlot); - return itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:compass") || itemStack.getItemEntry().getJavaIdentifier().equals("minecraft:filled_map"); + return itemStack.getMapping(session).getJavaIdentifier().equals("minecraft:compass") || itemStack.getMapping(session).getJavaIdentifier().equals("minecraft:filled_map"); } return false; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java index 17c93c15b..ec8f33f2e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/LoomInventoryTranslator.java @@ -112,7 +112,7 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator { } // Reject the item if Bedrock is attempting to put in a dye that is not a dye in Java Edition - return !itemStack.getItemEntry().getJavaIdentifier().endsWith("_dye"); + return !itemStack.getMapping(session).getJavaIdentifier().endsWith("_dye"); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/PlayerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/PlayerInventoryTranslator.java index e3dbec507..adde2b26f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/PlayerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/PlayerInventoryTranslator.java @@ -43,16 +43,16 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.inventory.SlotType; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.LanguageUtils; import java.util.Arrays; import java.util.Collections; +import java.util.function.IntFunction; public class PlayerInventoryTranslator extends InventoryTranslator { - private static final ItemData UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.creative")); + private static final IntFunction UNUSUABLE_CRAFTING_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.creative")); public PlayerInventoryTranslator() { super(46); @@ -106,7 +106,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { slotPacket.setSlot(i + 27); if (session.getGameMode() == GameMode.CREATIVE) { - slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK); + slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK.apply(session.getUpstream().getProtocolVersion())); } else { slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i).getItemStack())); } @@ -384,12 +384,13 @@ public class PlayerInventoryTranslator extends InventoryTranslator { craftState = CraftState.RECIPE_ID; int creativeId = creativeAction.getCreativeItemNetworkId() - 1; - if (creativeId < 0 || creativeId >= ItemRegistry.CREATIVE_ITEMS.length) { + ItemData[] creativeItems = session.getItemMappings().getCreativeItems(); + if (creativeId < 0 || creativeId >= creativeItems.length) { return rejectRequest(request); } // Reference the creative items list we send to the client to know what it's asking of us - ItemData creativeItem = ItemRegistry.CREATIVE_ITEMS[creativeId]; - javaCreativeItem = ItemTranslator.translateToJava(creativeItem); + ItemData creativeItem = creativeItems[creativeId]; + javaCreativeItem = ItemTranslator.translateToJava(creativeItem, session.getItemMappings()); break; } case CRAFT_RESULTS_DEPRECATED: { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java index 76d1cb1cf..4f28d0e4e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/ShulkerInventoryTranslator.java @@ -37,11 +37,12 @@ import org.geysermc.connector.network.translators.inventory.BedrockContainerSlot import org.geysermc.connector.network.translators.inventory.holder.BlockInventoryHolder; import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; +import org.geysermc.connector.registry.Registries; public class ShulkerInventoryTranslator extends AbstractBlockInventoryTranslator { public ShulkerInventoryTranslator() { super(27, new BlockInventoryHolder("minecraft:shulker_box[facing=north]", ContainerType.CONTAINER) { - private final BlockEntityTranslator shulkerBoxTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("ShulkerBox"); + private final BlockEntityTranslator shulkerBoxTranslator = Registries.BLOCK_ENTITIES.get("ShulkerBox"); @Override protected boolean isValidBlock(String[] javaBlockString) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java index 2acce3a9b..e4dea7265 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/StonecutterInventoryTranslator.java @@ -75,7 +75,7 @@ public class StonecutterInventoryTranslator extends AbstractBlockInventoryTransl return rejectRequest(request); } - ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0]); + ItemStack javaOutput = ItemTranslator.translateToJava(craftData.getResultItems()[0], session.getItemMappings()); int button = results.indexOf(javaOutput.getId()); // If we've already pressed the button with this item, no need to press it again! if (container.getStonecutterButton() != button) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/DoubleChestInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/DoubleChestInventoryTranslator.java index 78ac0b609..a20cca12e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/DoubleChestInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/chest/DoubleChestInventoryTranslator.java @@ -37,16 +37,16 @@ import org.geysermc.connector.inventory.Container; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.DoubleChestValue; import org.geysermc.connector.network.translators.world.block.entity.DoubleChestBlockEntityTranslator; +import org.geysermc.connector.registry.BlockRegistries; public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { private final int defaultJavaBlockState; public DoubleChestInventoryTranslator(int size) { super(size, 54); - this.defaultJavaBlockState = BlockTranslator.getJavaBlockState("minecraft:chest[facing=north,type=single,waterlogged=false]"); + this.defaultJavaBlockState = BlockRegistries.JAVA_IDENTIFIERS.get("minecraft:chest[facing=north,type=single,waterlogged=false]"); } @Override @@ -54,7 +54,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { // 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.getConnector().getWorldManager().getBlockAt(session, session.getLastInteractionBlockPosition()); - String[] javaBlockString = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\["); + String[] javaBlockString = BlockRegistries.JAVA_IDENTIFIERS.get().inverse().getOrDefault(javaBlockId, "minecraft:air").split("\\["); if (javaBlockString.length > 1 && (javaBlockString[0].equals("minecraft:chest") || javaBlockString[0].equals("minecraft:trapped_chest")) && !javaBlockString[1].contains("type=single")) { inventory.setHolderPosition(session.getLastInteractionBlockPosition()); @@ -82,7 +82,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP); Vector3i pairPosition = position.add(Vector3i.UNIT_X); - int bedrockBlockId = session.getBlockTranslator().getBedrockBlockId(defaultJavaBlockState); + int bedrockBlockId = session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState); UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); @@ -154,7 +154,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { UpdateBlockPacket blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); blockPacket.setBlockPosition(holderPos); - blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(realBlock)); + blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(realBlock)); session.sendUpstreamPacket(blockPacket); holderPos = holderPos.add(Vector3i.UNIT_X); @@ -162,7 +162,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator { blockPacket = new UpdateBlockPacket(); blockPacket.setDataLayer(0); blockPacket.setBlockPosition(holderPos); - blockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(realBlock)); + blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(realBlock)); session.sendUpstreamPacket(blockPacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java index f5029f749..b421044bf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/updater/ChestInventoryUpdater.java @@ -37,10 +37,11 @@ import org.geysermc.connector.utils.LanguageUtils; import java.util.ArrayList; import java.util.List; +import java.util.function.IntFunction; @AllArgsConstructor public class ChestInventoryUpdater extends InventoryUpdater { - private static final ItemData UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.slot")); + private static final IntFunction UNUSUABLE_SPACE_BLOCK = InventoryUtils.createUnusableSpaceBlock(LanguageUtils.getLocaleStringLog("geyser.inventory.unusable_item.slot")); private final int paddedSize; @@ -53,7 +54,7 @@ public class ChestInventoryUpdater extends InventoryUpdater { if (i < translator.size) { bedrockItems.add(inventory.getItem(i).getItemData(session)); } else { - bedrockItems.add(UNUSUABLE_SPACE_BLOCK); + bedrockItems.add(UNUSUABLE_SPACE_BLOCK.apply(session.getUpstream().getProtocolVersion())); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java deleted file mode 100644 index 4d8a50e85..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ /dev/null @@ -1,628 +0,0 @@ -/* - * Copyright (c) 2019-2021 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.connector.network.translators.item; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.google.common.collect.ImmutableSet; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtMapBuilder; -import com.nukkitx.nbt.NbtType; -import com.nukkitx.nbt.NbtUtils; -import com.nukkitx.protocol.bedrock.data.SoundEvent; -import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; -import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import it.unimi.dsi.fastutil.ints.*; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.network.translators.effect.EffectRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; -import org.geysermc.connector.utils.FileUtils; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; - -/** - * Registry for anything item related. - */ -public class ItemRegistry { - - private static final Map JAVA_IDENTIFIER_MAP = new HashMap<>(); - - /** - * A list of all identifiers that only exist on Java. Used to prevent creative items from becoming these unintentionally. - */ - private static final Set JAVA_ONLY_ITEMS; - - public static final ItemData[] CREATIVE_ITEMS; - - public static final List ITEMS = new ArrayList<>(); - public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); - - /** - * A list of all Java item names. - */ - public static final String[] ITEM_NAMES; - - /** - * Bamboo item entry, used in PandaEntity.java - */ - public static ItemEntry BAMBOO; - /** - * Banner item entry, used in LivingEntity.java - */ - public static ItemEntry BANNER; - /** - * Boat item entries, used in BedrockInventoryTransactionTranslator.java - */ - public static final IntSet BOATS = new IntArraySet(); - /** - * Bucket item entries (excluding the milk bucket), used in BedrockInventoryTransactionTranslator.java - */ - public static final IntSet BUCKETS = new IntArraySet(); - /** - * Carpet item data, used in LlamaEntity.java - */ - public static final List CARPETS = new ArrayList<>(16); - /** - * Crossbow item entry, used in PillagerEntity.java - */ - public static ItemEntry CROSSBOW; - /** - * Fishing rod item entry, used in ItemUtils.java - */ - public static ItemEntry FISHING_ROD; - /** - * Empty item bucket, used in BedrockInventoryTransactionTranslator.java - */ - public static ItemEntry MILK_BUCKET; - /** - * Egg item entry, used in JavaEntityStatusTranslator.java - */ - public static ItemEntry EGG; - /** - * Shield item entry, used in Entity.java and LivingEntity.java - */ - public static ItemEntry SHIELD; - /** - * A list of all spawn eggs by their Bedrock IDs. Used in BedrockInventoryTransactionTranslator.java - */ - public static final IntSet SPAWN_EGGS = new IntArraySet(); - /** - * Wheat item entry, used in AbstractHorseEntity.java - */ - public static ItemEntry WHEAT; - /** - * Writable book item entry, used in BedrockBookEditTranslator.java - */ - public static ItemEntry WRITABLE_BOOK; - - public static int BARRIER_INDEX = 0; - - /** - * Stores the properties and data of the "custom" furnace minecart item. - */ - public static final ComponentItemData FURNACE_MINECART_DATA; - - public static void init() { - // no-op - } - - static { - /* Load item palette */ - InputStream stream = FileUtils.getResource("bedrock/runtime_item_states.json"); - - TypeReference> itemEntriesType = new TypeReference>() { - }; - - // Used to get the Bedrock namespaced ID (in instances where there are small differences) - Int2ObjectMap bedrockIdToIdentifier = new Int2ObjectOpenHashMap<>(); - - List itemNames = new ArrayList<>(); - - List itemEntries; - try { - itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, itemEntriesType); - } catch (Exception e) { - throw new AssertionError("Unable to load Bedrock runtime item IDs", e); - } - - int lodestoneCompassId = 0; - - for (JsonNode entry : itemEntries) { - ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue())); - bedrockIdToIdentifier.put(entry.get("id").intValue(), entry.get("name").textValue()); - if (entry.get("name").textValue().equals("minecraft:lodestone_compass")) { - lodestoneCompassId = entry.get("id").intValue(); - } - } - - Object2IntMap bedrockBlockIdOverrides = new Object2IntOpenHashMap<>(); - Object2IntMap blacklistedIdentifiers = new Object2IntOpenHashMap<>(); - - // Load creative items - // We load this before item mappings to get overridden block runtime ID mappings - stream = FileUtils.getResource("bedrock/creative_items.json"); - - JsonNode creativeItemEntries; - try { - creativeItemEntries = GeyserConnector.JSON_MAPPER.readTree(stream).get("items"); - } catch (Exception e) { - throw new AssertionError("Unable to load creative items", e); - } - - int netId = 1; - List creativeItems = new ArrayList<>(); - for (JsonNode itemNode : creativeItemEntries) { - int count = 1; - int damage = 0; - int blockRuntimeId = 0; - NbtMap tag = null; - JsonNode damageNode = itemNode.get("damage"); - if (damageNode != null) { - damage = damageNode.asInt(); - } - JsonNode countNode = itemNode.get("count"); - if (countNode != null) { - count = countNode.asInt(); - } - JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId"); - if (blockRuntimeIdNode != null) { - blockRuntimeId = blockRuntimeIdNode.asInt(); - } - JsonNode nbtNode = itemNode.get("nbt_b64"); - if (nbtNode != null) { - byte[] bytes = Base64.getDecoder().decode(nbtNode.asText()); - ByteArrayInputStream bais = new ByteArrayInputStream(bytes); - try { - tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - String identifier = itemNode.get("id").textValue(); - int id = -1; - for (StartGamePacket.ItemEntry itemEntry : ITEMS) { - if (itemEntry.getIdentifier().equals(identifier)) { - id = itemEntry.getId(); - break; - } - } - if (id == -1) { - throw new RuntimeException("Unable to find matching Bedrock item for " + identifier); - } - - creativeItems.add(ItemData.builder() - .id(id) - .damage(damage) - .count(count) - .blockRuntimeId(blockRuntimeId) - .tag(tag) - .netId(netId++).build()); - - if (blockRuntimeId != 0) { - // Add override for item mapping, unless it already exists... then we know multiple states can exist - if (!blacklistedIdentifiers.containsKey(identifier)) { - if (bedrockBlockIdOverrides.containsKey(identifier)) { - bedrockBlockIdOverrides.remove(identifier); - // Save this as a blacklist, but also as knowledge of what the block state name should be - blacklistedIdentifiers.put(identifier, blockRuntimeId); - } else { - // Unless there's multiple possibilities for this one state, let this be - bedrockBlockIdOverrides.put(identifier, blockRuntimeId); - } - } - } - } - - // Load item mappings from Java Edition to Bedrock Edition - stream = FileUtils.getResource("mappings/items.json"); - - JsonNode items; - try { - items = GeyserConnector.JSON_MAPPER.readTree(stream); - } catch (Exception e) { - throw new AssertionError("Unable to load Java runtime item IDs", e); - } - - BlockTranslator blockTranslator = BlockTranslator1_17_0.INSTANCE; - - int itemIndex = 0; - int javaFurnaceMinecartId = 0; - boolean usingFurnaceMinecart = GeyserConnector.getInstance().getConfig().isAddNonBedrockItems(); - Iterator> iterator = items.fields(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (usingFurnaceMinecart && entry.getKey().equals("minecraft:furnace_minecart")) { - javaFurnaceMinecartId = itemIndex; - itemIndex++; - continue; - } - int bedrockId = entry.getValue().get("bedrock_id").intValue(); - String bedrockIdentifier = bedrockIdToIdentifier.get(bedrockId); - if (bedrockIdentifier == null) { - throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); - } - JsonNode stackSizeNode = entry.getValue().get("stack_size"); - int stackSize = stackSizeNode == null ? 64 : stackSizeNode.intValue(); - - int bedrockBlockId = -1; - JsonNode blockRuntimeIdNode = entry.getValue().get("blockRuntimeId"); - if (blockRuntimeIdNode != null) { - int blockIdOverride = bedrockBlockIdOverrides.getOrDefault(bedrockIdentifier, -1); - if (blockIdOverride != -1) { - // Straight from BDS is our best chance of getting an item that doesn't run into issues - bedrockBlockId = blockIdOverride; - } else { - // Try to get an example block runtime ID from the creative contents packet, for Bedrock identifier obtaining - int aValidBedrockBlockId = blacklistedIdentifiers.getOrDefault(bedrockIdentifier, -1); - if (aValidBedrockBlockId == -1) { - // Fallback - bedrockBlockId = blockTranslator.getBedrockBlockId(blockRuntimeIdNode.intValue()); - } else { - // As of 1.16.220, every item requires a block runtime ID attached to it. - // This is mostly for identifying different blocks with the same item ID - wool, slabs, some walls. - // However, in order for some visuals and crafting to work, we need to send the first matching block state - // as indexed by Bedrock's block palette - // There are exceptions! But, ideally, the block ID override should take care of those. - String javaBlockIdentifier = BlockTranslator.getBlockMapping(blockRuntimeIdNode.intValue()).getCleanJavaIdentifier(); - NbtMapBuilder requiredBlockStatesBuilder = NbtMap.builder(); - String correctBedrockIdentifier = blockTranslator.getAllBedrockBlockStates().get(aValidBedrockBlockId).getString("name"); - boolean firstPass = true; - for (Map.Entry blockEntry : BlockTranslator.getJavaIdBlockMap().entrySet()) { - if (blockEntry.getKey().split("\\[")[0].equals(javaBlockIdentifier)) { - int bedrockBlockRuntimeId = blockTranslator.getBedrockBlockId(blockEntry.getValue()); - NbtMap blockTag = blockTranslator.getAllBedrockBlockStates().get(bedrockBlockRuntimeId); - String bedrockName = blockTag.getString("name"); - if (!bedrockName.equals(correctBedrockIdentifier)) { - continue; - } - NbtMap states = blockTag.getCompound("states"); - - if (firstPass) { - firstPass = false; - if (states.size() == 0) { - // No need to iterate and find all block states - this is the one, as there can't be any others - bedrockBlockId = bedrockBlockRuntimeId; - break; - } - requiredBlockStatesBuilder.putAll(states); - continue; - } - for (Map.Entry nbtEntry : states.entrySet()) { - Object value = requiredBlockStatesBuilder.get(nbtEntry.getKey()); - if (value != null && !nbtEntry.getValue().equals(value)) { // Null means this value has already been removed/deemed as unneeded - // This state can change between different block states, and therefore is not required - // to build a successful block state of this - requiredBlockStatesBuilder.remove(nbtEntry.getKey()); - } - } - if (requiredBlockStatesBuilder.size() == 0) { - // There are no required block states - // E.G. there was only a direction property that is no longer in play - // (States that are important include color for glass) - break; - } - } - } - - NbtMap requiredBlockStates = requiredBlockStatesBuilder.build(); - if (bedrockBlockId == -1) { - int i = -1; - // We need to loop around again (we can't cache the block tags above) because Bedrock can include states that we don't have a pairing for - // in it's "preferred" block state - I.E. the first matching block state in the list - for (NbtMap blockTag : blockTranslator.getAllBedrockBlockStates()) { - i++; - if (blockTag.getString("name").equals(correctBedrockIdentifier)) { - NbtMap states = blockTag.getCompound("states"); - boolean valid = true; - for (Map.Entry nbtEntry : requiredBlockStates.entrySet()) { - if (!states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) { - // A required block state doesn't match - this one is not valid - valid = false; - break; - } - } - if (valid) { - bedrockBlockId = i; - break; - } - } - } - if (bedrockBlockId == -1) { - throw new RuntimeException("Could not find a block match for " + entry.getKey()); - } - } - - // Because we have replaced the Bedrock block ID, we also need to replace the creative contents block runtime ID - // That way, creative items work correctly for these blocks - for (int j = 0; j < creativeItems.size(); j++) { - ItemData itemData = creativeItems.get(j); - if (itemData.getId() == bedrockId) { - if (itemData.getDamage() != 0) { - break; - } - NbtMap states = blockTranslator.getAllBedrockBlockStates().get(itemData.getBlockRuntimeId()).getCompound("states"); - boolean valid = true; - for (Map.Entry nbtEntry : requiredBlockStates.entrySet()) { - if (!states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) { - // A required block state doesn't match - this one is not valid - valid = false; - break; - } - } - if (valid) { - creativeItems.set(j, itemData.toBuilder().blockRuntimeId(bedrockBlockId).build()); - break; - } - } - } - } - } - } - - ItemEntry itemEntry; - if (entry.getValue().has("tool_type")) { - if (entry.getValue().has("tool_tier")) { - itemEntry = new ToolItemEntry( - entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, - entry.getValue().get("bedrock_data").intValue(), - entry.getValue().get("tool_type").textValue(), - entry.getValue().get("tool_tier").textValue(), - bedrockBlockId, - stackSize); - } else { - itemEntry = new ToolItemEntry( - entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, - entry.getValue().get("bedrock_data").intValue(), - entry.getValue().get("tool_type").textValue(), - "", bedrockBlockId, - stackSize); - } - } else if (entry.getKey().equals("minecraft:spectral_arrow") || entry.getKey().equals("minecraft:knowledge_book") - // To remove later... hopefully - || entry.getKey().contains("candle") || entry.getKey().equals("minecraft:bundle") || entry.getKey().equals("minecraft:sculk_sensor")) { - // These items don't exist on Bedrock, so set up a container that indicates they should have custom names - itemEntry = new TranslatableItemEntry( - entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, - entry.getValue().get("bedrock_data").intValue(), - bedrockBlockId, - stackSize); - GeyserConnector.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated."); - } else { - itemEntry = new ItemEntry( - entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, - entry.getValue().get("bedrock_data").intValue(), - bedrockBlockId, - stackSize); - } - ITEM_ENTRIES.put(itemIndex, itemEntry); - - switch (entry.getKey()) { - case "minecraft:barrier": - BARRIER_INDEX = itemIndex; - break; - case "minecraft:bamboo": - BAMBOO = itemEntry; - break; - case "minecraft:crossbow": - CROSSBOW = itemEntry; - break; - case "minecraft:egg": - EGG = itemEntry; - break; - case "minecraft:fishing_rod": - FISHING_ROD = itemEntry; - break; - case "minecraft:shield": - SHIELD = itemEntry; - break; - case "minecraft:milk_bucket": - MILK_BUCKET = itemEntry; - break; - case "minecraft:wheat": - WHEAT = itemEntry; - break; - case "minecraft:white_banner": // As of 1.16.220, all banners share the same Bedrock ID and differ their colors through their damage value - BANNER = itemEntry; - break; - case "minecraft:writable_book": - WRITABLE_BOOK = itemEntry; - break; - default: - break; - } - - if (entry.getKey().contains("boat")) { - BOATS.add(itemEntry.getBedrockId()); - } else if (entry.getKey().contains("bucket") && !entry.getKey().contains("milk")) { - BUCKETS.add(itemEntry.getBedrockId()); - } else if (entry.getKey().contains("_carpet") && !entry.getKey().contains("moss")) { - // This should be the numerical order Java sends as an integer value for llamas - CARPETS.add(ItemData.builder() - .id(itemEntry.getBedrockId()) - .damage(itemEntry.getBedrockData()) - .count(1) - .blockRuntimeId(itemEntry.getBedrockBlockId()).build()); - } else if (entry.getKey().startsWith("minecraft:music_disc_")) { - // The Java record level event uses the item ID as the "key" to play the record - EffectRegistry.RECORDS.put(itemIndex, SoundEvent.valueOf("RECORD_" + - entry.getKey().replace("minecraft:music_disc_", "").toUpperCase(Locale.ENGLISH))); - } else if (entry.getKey().endsWith("_spawn_egg")) { - SPAWN_EGGS.add(itemEntry.getBedrockId()); - } - - itemNames.add(entry.getKey()); - - itemIndex++; - } - - itemNames.add("minecraft:furnace_minecart"); - - if (lodestoneCompassId == 0) { - throw new RuntimeException("Lodestone compass not found in item palette!"); - } - - // Add the loadstone compass since it doesn't exist on java but we need it for item conversion - ITEM_ENTRIES.put(itemIndex, new ItemEntry("minecraft:lodestone_compass", "minecraft:lodestone_compass", itemIndex, - lodestoneCompassId, 0, -1, 1)); - - if (usingFurnaceMinecart) { - // Add the furnace minecart as a custom item - int furnaceMinecartId = ITEMS.size() + 1; - - ITEMS.add(new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true)); - ITEM_ENTRIES.put(javaFurnaceMinecartId, new ItemEntry("minecraft:furnace_minecart", "geysermc:furnace_minecart", javaFurnaceMinecartId, - furnaceMinecartId, 0, -1, 1)); - creativeItems.add(ItemData.builder() - .netId(netId) - .id(furnaceMinecartId) - .count(1).build()); - - NbtMapBuilder builder = NbtMap.builder(); - builder.putString("name", "geysermc:furnace_minecart") - .putInt("id", furnaceMinecartId); - - NbtMapBuilder componentBuilder = NbtMap.builder(); - // Conveniently, as of 1.16.200, the furnace minecart has a texture AND translation string already. - componentBuilder.putCompound("minecraft:icon", NbtMap.builder().putString("texture", "minecart_furnace").build()); - componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", "item.minecartFurnace.name").build()); - - // Indicate that the arm animation should play on rails - List useOnTag = Collections.singletonList(NbtMap.builder().putString("tags", "q.any_tag('rail')").build()); - componentBuilder.putCompound("minecraft:entity_placer", NbtMap.builder() - .putList("dispense_on", NbtType.COMPOUND, useOnTag) - .putString("entity", "minecraft:minecart") - .putList("use_on", NbtType.COMPOUND, useOnTag) - .build()); - - NbtMapBuilder itemProperties = NbtMap.builder(); - // We always want to allow offhand usage when we can - matches Java Edition - itemProperties.putBoolean("allow_off_hand", true); - itemProperties.putBoolean("hand_equipped", false); - itemProperties.putInt("max_stack_size", 1); - itemProperties.putString("creative_group", "itemGroup.name.minecart"); - itemProperties.putInt("creative_category", 4); // 4 - "Items" - - componentBuilder.putCompound("item_properties", itemProperties.build()); - builder.putCompound("components", componentBuilder.build()); - FURNACE_MINECART_DATA = new ComponentItemData("geysermc:furnace_minecart", builder.build()); - } else { - FURNACE_MINECART_DATA = null; - } - - CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]); - - ITEM_NAMES = itemNames.toArray(new String[0]); - - Set javaOnlyItems = new ObjectOpenHashSet<>(); - Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick", - "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg", - // To be removed in Bedrock 1.17.10... right??? RIGHT??? - "minecraft:candle", "minecraft:white_candle", "minecraft:orange_candle", "minecraft:magenta_candle", - "minecraft:light_blue_candle", "minecraft:yellow_candle", "minecraft:lime_candle", "minecraft:pink_candle", - "minecraft:gray_candle", "minecraft:light_gray_candle", "minecraft:cyan_candle", "minecraft:purple_candle", - "minecraft:blue_candle", "minecraft:brown_candle", "minecraft:green_candle", "minecraft:red_candle", "minecraft:black_candle", - "minecraft:bundle", "minecraft:sculk_sensor"); - if (!usingFurnaceMinecart) { - javaOnlyItems.add("minecraft:furnace_minecart"); - } - JAVA_ONLY_ITEMS = ImmutableSet.copyOf(javaOnlyItems); - } - - /** - * Gets an {@link ItemEntry} from the given {@link ItemStack}. - * - * @param stack the item stack - * @return an item entry from the given item stack - */ - public static ItemEntry getItem(ItemStack stack) { - return ITEM_ENTRIES.get(stack.getId()); - } - - /** - * Gets an {@link ItemEntry} from the given {@link ItemData}. - * - * @param data the item data - * @return an item entry from the given item data - */ - public static ItemEntry getItem(ItemData data) { - boolean isBlock = data.getBlockRuntimeId() != 0; - boolean hasDamage = data.getDamage() != 0; - - for (ItemEntry itemEntry : ITEM_ENTRIES.values()) { - if (itemEntry.getBedrockId() == data.getId()) { - if (isBlock && !hasDamage) { // Pre-1.16.220 will not use block runtime IDs at all, so we shouldn't check either - if (data.getBlockRuntimeId() != itemEntry.getBedrockBlockId()) { - continue; - } - } else { - if (!(itemEntry.getBedrockData() == data.getDamage() || - // Make exceptions for potions and tipped arrows, whose damage values can vary - (itemEntry.getJavaIdentifier().endsWith("potion") || itemEntry.getJavaIdentifier().equals("minecraft:arrow")))) { - continue; - } - } - if (!JAVA_ONLY_ITEMS.contains(itemEntry.getJavaIdentifier())) { - // From a Bedrock item data, we aren't getting one of these items - return itemEntry; - } - } - } - - // This will hide the message when the player clicks with an empty hand - if (data.getId() != 0 && data.getDamage() != 0) { - GeyserConnector.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage()); - } - return ItemEntry.AIR; - } - - /** - * Gets an {@link ItemEntry} from the given Minecraft: Java Edition - * block state identifier. - * - * @param javaIdentifier the block state identifier - * @return an item entry from the given java edition identifier - */ - public static ItemEntry getItemEntry(String javaIdentifier) { - return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> { - for (ItemEntry entry : ITEM_ENTRIES.values()) { - if (entry.getJavaIdentifier().equals(key)) { - return entry; - } - } - return null; - }); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index c2bef9c6d..c4d098f79 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -38,6 +38,9 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.chat.MessageTranslator; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LocaleUtils; import org.reflections.Reflections; @@ -73,8 +76,8 @@ public abstract class ItemTranslator { continue; } ItemTranslator itemStackTranslator = (ItemTranslator) clazz.newInstance(); - List appliedItems = itemStackTranslator.getAppliedItems(); - for (ItemEntry item : appliedItems) { + List appliedItems = itemStackTranslator.getAppliedItems(); + for (ItemMapping item : appliedItems) { ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId()); if (registered != null) { GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated item translator " + @@ -92,18 +95,23 @@ public abstract class ItemTranslator { NBT_TRANSLATORS = loadedNbtItemTranslators.keySet().stream().sorted(Comparator.comparingInt(loadedNbtItemTranslators::get)).collect(Collectors.toList()); } - public static ItemStack translateToJava(ItemData data) { + /** + * @param mappings item mappings to use while translating. This can't just be a Geyser session as this method is used + * when loading recipes. + */ + public static ItemStack translateToJava(ItemData data, ItemMappings mappings) { if (data == null) { return new ItemStack(0); } - ItemEntry javaItem = ItemRegistry.getItem(data); + + ItemMapping javaItem = mappings.getMapping(data); ItemStack itemStack; ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(javaItem.getJavaId()); if (itemStackTranslator != null) { - itemStack = itemStackTranslator.translateToJava(data, javaItem); + itemStack = itemStackTranslator.translateToJava(data, javaItem, mappings); } else { - itemStack = DEFAULT_TRANSLATOR.translateToJava(data, javaItem); + itemStack = DEFAULT_TRANSLATOR.translateToJava(data, javaItem, mappings); } if (itemStack != null && itemStack.getNbt() != null) { @@ -125,9 +133,9 @@ public abstract class ItemTranslator { return ItemData.AIR; } - ItemEntry bedrockItem = ItemRegistry.getItem(stack); + ItemMapping bedrockItem = session.getItemMappings().getMapping(stack); if (bedrockItem == null) { - session.getConnector().getLogger().debug("No matching ItemEntry for " + stack); + session.getConnector().getLogger().debug("No matching ItemMapping for " + stack); return ItemData.AIR; } @@ -154,9 +162,9 @@ public abstract class ItemTranslator { ItemData.Builder builder; ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); if (itemStackTranslator != null) { - builder = itemStackTranslator.translateToBedrock(itemStack, bedrockItem); + builder = itemStackTranslator.translateToBedrock(itemStack, bedrockItem, session.getItemMappings()); } else { - builder = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem); + builder = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem, session.getItemMappings()); } if (bedrockItem.isBlock()) { builder.blockRuntimeId(bedrockItem.getBedrockBlockId()); @@ -168,8 +176,8 @@ public abstract class ItemTranslator { String[] canBreak = new String[0]; ListTag canPlaceOn = nbt.get("CanPlaceOn"); String[] canPlace = new String[0]; - canBreak = getCanModify(session, canDestroy, canBreak); - canPlace = getCanModify(session, canPlaceOn, canPlace); + canBreak = getCanModify(canDestroy, canBreak); + canPlace = getCanModify(canPlaceOn, canPlace); builder.canBreak(canBreak); builder.canPlace(canPlace); } @@ -185,7 +193,7 @@ public abstract class ItemTranslator { * @param canModifyBedrock the empty list of items in Bedrock * @return the new list of items in Bedrock */ - private static String[] getCanModify(GeyserSession session, ListTag canModifyJava, String[] canModifyBedrock) { + private static String[] getCanModify(ListTag canModifyJava, String[] canModifyBedrock) { if (canModifyJava != null && canModifyJava.size() > 0) { canModifyBedrock = new String[canModifyJava.size()]; for (int i = 0; i < canModifyBedrock.length; i++) { @@ -195,7 +203,7 @@ public abstract class ItemTranslator { if (!block.startsWith("minecraft:")) block = "minecraft:" + block; // Get the Bedrock identifier of the item and replace it. // This will unfortunately be limited - for example, beds and banners will be translated weirdly - canModifyBedrock[i] = session.getBlockTranslator().getBedrockBlockIdentifier(block).replace("minecraft:", ""); + canModifyBedrock[i] = BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get(block).replace("minecraft:", ""); } } return canModifyBedrock; @@ -203,19 +211,19 @@ public abstract class ItemTranslator { private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() { @Override - public List getAppliedItems() { + public List getAppliedItems() { return null; } }; - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { + public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (itemStack == null) { // Return, essentially, air return ItemData.builder(); } ItemData.Builder builder = ItemData.builder() - .id(itemEntry.getBedrockId()) - .damage(itemEntry.getBedrockData()) + .id(mapping.getBedrockId()) + .damage(mapping.getBedrockData()) .count(itemStack.getAmount()); if (itemStack.getNbt() != null) { builder.tag(this.translateNbtToBedrock(itemStack.getNbt())); @@ -223,15 +231,15 @@ public abstract class ItemTranslator { return builder; } - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { if (itemData == null) return null; if (itemData.getTag() == null) { - return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), new CompoundTag("")); + return new ItemStack(mapping.getJavaId(), itemData.getCount(), new CompoundTag("")); } - return new ItemStack(itemEntry.getJavaId(), itemData.getCount(), this.translateToJavaNBT("", itemData.getTag())); + return new ItemStack(mapping.getJavaId(), itemData.getCount(), this.translateToJavaNBT("", itemData.getTag())); } - public abstract List getAppliedItems(); + public abstract List getAppliedItems(); public NbtMap translateNbtToBedrock(CompoundTag tag) { NbtMapBuilder builder = NbtMap.builder(); @@ -395,19 +403,19 @@ public abstract class ItemTranslator { * Translates the display name of the item * @param session the Bedrock client's session * @param tag the tag to translate - * @param itemEntry the item entry, in case it requires translation + * @param mapping the item entry, in case it requires translation * * @return the new tag to use, should the current one be null */ - public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemEntry itemEntry) { - return translateDisplayProperties(session, tag, itemEntry, 'f'); + public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemMapping mapping) { + return translateDisplayProperties(session, tag, mapping, 'f'); } /** * @param translationColor if this item is not available on Java, the color that the new name should be. * Normally, this should just be white, but for shulker boxes this should be gray. */ - public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemEntry itemEntry, char translationColor) { + public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemMapping mapping, char translationColor) { boolean hasCustomName = false; if (tag != null) { CompoundTag display = tag.get("display"); @@ -427,7 +435,7 @@ public abstract class ItemTranslator { } } - if (!hasCustomName && itemEntry instanceof TranslatableItemEntry) { + if (!hasCustomName && mapping.hasTranslation()) { // No custom name, but we need to localize the item's name if (tag == null) { tag = new CompoundTag(""); @@ -439,7 +447,7 @@ public abstract class ItemTranslator { tag.put(display); } - String translationKey = ((TranslatableItemEntry) itemEntry).getTranslationString(); + String translationKey = mapping.getTranslationString(); // Reset formatting since Bedrock defaults to italics display.put(new StringTag("Name", "§r§" + translationColor + LocaleUtils.getLocaleString(translationKey, session.getLocale()))); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java index bfd1d777c..a7044c3b2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.item; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.registry.type.ItemMapping; public class NbtItemStackTranslator { @@ -34,26 +35,28 @@ public class NbtItemStackTranslator { * Translate the item NBT to Bedrock * @param session the client's current session * @param itemTag the item's CompoundTag - * @param itemEntry Geyser's item entry + * @param mapping Geyser's item mapping */ - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { } /** * Translate the item NBT to Java. * @param itemTag the item's CompoundTag - * @param itemEntry Geyser's item entry + * @param mapping Geyser's item mapping */ - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { } /** - * @param itemEntry Geyser's item entry + * Gets whether this nbt translator takes in this item. + * + * @param mapping Geyser's item mapping * @return if the item should be processed under this class */ - public boolean acceptItem(ItemEntry itemEntry) { + public boolean acceptItem(ItemMapping mapping) { return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/StoredItemMappings.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/StoredItemMappings.java new file mode 100644 index 000000000..80a50f831 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/StoredItemMappings.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019-2021 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.connector.network.translators.item; + +import lombok.Getter; +import lombok.experimental.Accessors; +import org.geysermc.connector.registry.type.ItemMapping; + +import javax.annotation.Nonnull; +import java.util.Map; + +/** + * A class to have easy access to specific item mappings per-version. + */ +@Getter +@Accessors(fluent = true) +public class StoredItemMappings { + private final ItemMapping bamboo; + private final ItemMapping banner; + private final ItemMapping barrier; + private final ItemMapping compass; + private final ItemMapping crossbow; + private final ItemMapping fishingRod; + private final ItemMapping lodestoneCompass; + private final ItemMapping milkBucket; + private final ItemMapping egg; + private final ItemMapping shield; + private final ItemMapping wheat; + private final ItemMapping writableBook; + + public StoredItemMappings(Map itemMappings) { + this.bamboo = load(itemMappings, "bamboo"); + this.banner = load(itemMappings, "white_banner"); // As of 1.17.10, all banners have the same Bedrock ID + this.barrier = load(itemMappings, "barrier"); + this.compass = load(itemMappings, "compass"); + this.crossbow = load(itemMappings, "crossbow"); + this.fishingRod = load(itemMappings, "fishing_rod"); + this.lodestoneCompass = load(itemMappings, "lodestone_compass"); + this.milkBucket = load(itemMappings, "milk_bucket"); + this.egg = load(itemMappings, "egg"); + this.shield = load(itemMappings, "shield"); + this.wheat = load(itemMappings, "wheat"); + this.writableBook = load(itemMappings, "writable_book"); + } + + @Nonnull + private ItemMapping load(Map itemMappings, String cleanIdentifier) { + ItemMapping mapping = itemMappings.get("minecraft:" + cleanIdentifier); + if (mapping == null) { + throw new RuntimeException("Could not find item " + cleanIdentifier); + } + + return mapping; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java index fb1256e40..116e703bc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java @@ -32,10 +32,12 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.registry.type.ItemMappings; import java.util.ArrayList; import java.util.HashMap; @@ -54,7 +56,7 @@ public class BannerTranslator extends ItemTranslator { */ public static final ListTag OMINOUS_BANNER_PATTERN; - private final List appliedItems; + private final List appliedItems; static { OMINOUS_BANNER_PATTERN = new ListTag("Patterns"); @@ -79,7 +81,9 @@ public class BannerTranslator extends ItemTranslator { } public BannerTranslator() { - appliedItems = ItemRegistry.ITEM_ENTRIES.values() + appliedItems = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() .stream() .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) .collect(Collectors.toList()); @@ -153,12 +157,12 @@ public class BannerTranslator extends ItemTranslator { } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { + public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { if (itemStack.getNbt() == null) { - return super.translateToBedrock(itemStack, itemEntry); + return super.translateToBedrock(itemStack, mapping, mappings); } - ItemData.Builder builder = super.translateToBedrock(itemStack, itemEntry); + ItemData.Builder builder = super.translateToBedrock(itemStack, mapping, mappings); CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); if (blockEntityTag != null && blockEntityTag.contains("Patterns")) { @@ -180,12 +184,12 @@ public class BannerTranslator extends ItemTranslator { } @Override - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { if (itemData.getTag() == null) { - return super.translateToJava(itemData, itemEntry); + return super.translateToJava(itemData, mapping, mappings); } - ItemStack itemStack = super.translateToJava(itemData, itemEntry); + ItemStack itemStack = super.translateToJava(itemData, mapping, mappings); NbtMap nbtTag = itemData.getTag(); if (nbtTag.containsKey("Type", NbtType.INT) && nbtTag.getInt("Type") == 1) { @@ -209,7 +213,7 @@ public class BannerTranslator extends ItemTranslator { } @Override - public List getAppliedItems() { + public List getAppliedItems() { return appliedItems; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java index 08c7426fe..e35e5c252 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java @@ -28,10 +28,12 @@ package org.geysermc.connector.network.translators.item.translators; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.connector.utils.LoadstoneTracker; import java.util.List; @@ -40,20 +42,25 @@ import java.util.stream.Collectors; @ItemRemapper public class CompassTranslator extends ItemTranslator { - private final List appliedItems; + private final List appliedItems; public CompassTranslator() { - appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("compass")).collect(Collectors.toList()); + appliedItems = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() + .stream() + .filter(entry -> entry.getJavaIdentifier().endsWith("compass")) + .collect(Collectors.toList()); } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { - if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry); + public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); Tag lodestoneTag = itemStack.getNbt().get("LodestoneTracked"); if (lodestoneTag instanceof ByteTag) { // Get the fake lodestonecompass entry - itemEntry = ItemRegistry.getItemEntry("minecraft:lodestone_compass"); + mapping = mappings.getStoredItems().lodestoneCompass(); // Get the loadstone pos CompoundTag loadstonePos = itemStack.getNbt().get("LodestonePos"); @@ -75,20 +82,20 @@ public class CompassTranslator extends ItemTranslator { } } - return super.translateToBedrock(itemStack, itemEntry); + return super.translateToBedrock(itemStack, mapping, mappings); } @Override - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { boolean isLoadstone = false; - if (itemEntry.getBedrockIdentifier().equals("minecraft:lodestone_compass")) { + if (mapping.getBedrockIdentifier().equals("minecraft:lodestone_compass")) { // Revert the entry back to the compass - itemEntry = ItemRegistry.getItemEntry("minecraft:compass"); + mapping = mappings.getStoredItems().compass(); isLoadstone = true; } - ItemStack itemStack = super.translateToJava(itemData, itemEntry); + ItemStack itemStack = super.translateToJava(itemData, mapping, mappings); if (isLoadstone) { // Get the tracking id @@ -113,7 +120,7 @@ public class CompassTranslator extends ItemTranslator { } @Override - public List getAppliedItems() { + public List getAppliedItems() { return appliedItems; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java index 9f41472fe..2908754aa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java @@ -30,11 +30,13 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.Potion; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.registry.type.ItemMappings; import java.util.List; import java.util.stream.Collectors; @@ -42,34 +44,39 @@ import java.util.stream.Collectors; @ItemRemapper public class PotionTranslator extends ItemTranslator { - private final List appliedItems; + private final List appliedItems; public PotionTranslator() { - appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList()); + appliedItems = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() + .stream() + .filter(entry -> entry.getJavaIdentifier().endsWith("potion")) + .collect(Collectors.toList()); } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { - if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry); + public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); Tag potionTag = itemStack.getNbt().get("Potion"); if (potionTag instanceof StringTag) { Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); if (potion != null) { return ItemData.builder() - .id(itemEntry.getBedrockId()) + .id(mapping.getBedrockId()) .damage(potion.getBedrockId()) .count(itemStack.getAmount()) .tag(translateNbtToBedrock(itemStack.getNbt())); } GeyserConnector.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue()); } - return super.translateToBedrock(itemStack, itemEntry); + return super.translateToBedrock(itemStack, mapping, mappings); } @Override - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { Potion potion = Potion.getByBedrockId(itemData.getDamage()); - ItemStack itemStack = super.translateToJava(itemData, itemEntry); + ItemStack itemStack = super.translateToJava(itemData, mapping, mappings); if (potion != null) { StringTag potionTag = new StringTag("Potion", potion.getJavaIdentifier()); itemStack.getNbt().put(potionTag); @@ -78,7 +85,7 @@ public class PotionTranslator extends ItemTranslator { } @Override - public List getAppliedItems() { + public List getAppliedItems() { return appliedItems; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java index 8b64732c4..ebc3936de 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java @@ -30,11 +30,13 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.TippedArrowPotion; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.registry.type.ItemMappings; import java.util.List; import java.util.stream.Collectors; @@ -42,40 +44,47 @@ import java.util.stream.Collectors; @ItemRemapper public class TippedArrowTranslator extends ItemTranslator { - private final List appliedItems; + private final List appliedItems; - private static final int TIPPED_ARROW_JAVA_ID = ItemRegistry.getItemEntry("minecraft:tipped_arrow").getJavaId(); + private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getMapping("minecraft:tipped_arrow") + .getJavaId(); public TippedArrowTranslator() { - appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> - entry.getJavaIdentifier().contains("arrow") && !entry.getJavaIdentifier().contains("spectral")).collect(Collectors.toList()); + appliedItems = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) + .getItems() + .values() + .stream() + .filter(entry -> entry.getJavaIdentifier().contains("arrow") + && !entry.getJavaIdentifier().contains("spectral")) + .collect(Collectors.toList()); } @Override - public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { - if (!itemEntry.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) { + public ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { + if (!mapping.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) { // We're only concerned about minecraft:arrow when translating Bedrock -> Java - return super.translateToBedrock(itemStack, itemEntry); + return super.translateToBedrock(itemStack, mapping, mappings); } Tag potionTag = itemStack.getNbt().get("Potion"); if (potionTag instanceof StringTag) { TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByJavaIdentifier(((StringTag) potionTag).getValue()); if (tippedArrowPotion != null) { return ItemData.builder() - .id(itemEntry.getBedrockId()) + .id(mapping.getBedrockId()) .damage(tippedArrowPotion.getBedrockId()) .count(itemStack.getAmount()) .tag(translateNbtToBedrock(itemStack.getNbt())); } GeyserConnector.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionTag.getValue()); } - return super.translateToBedrock(itemStack, itemEntry); + return super.translateToBedrock(itemStack, mapping, mappings); } @Override - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage()); - ItemStack itemStack = super.translateToJava(itemData, itemEntry); + ItemStack itemStack = super.translateToJava(itemData, mapping, mappings); if (tippedArrowPotion != null) { itemStack = new ItemStack(TIPPED_ARROW_JAVA_ID, itemStack.getAmount(), itemStack.getNbt()); StringTag potionTag = new StringTag("Potion", tippedArrowPotion.getJavaIdentifier()); @@ -85,7 +94,7 @@ public class TippedArrowTranslator extends ItemTranslator { } @Override - public List getAppliedItems() { + public List getAppliedItems() { return appliedItems; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/AxolotlBucketTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/AxolotlBucketTranslator.java index a0fe4910b..f62ac05ae 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/AxolotlBucketTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/AxolotlBucketTranslator.java @@ -30,15 +30,15 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.LocaleUtils; @ItemRemapper public class AxolotlBucketTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { // Bedrock Edition displays the properties of the axolotl. Java does not. // To work around this, set the custom name to the Axolotl translation and it's displayed correctly itemTag.put(new ByteTag("AppendCustomName", (byte) 1)); @@ -49,7 +49,7 @@ public class AxolotlBucketTranslator extends NbtItemStackTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return itemEntry.getJavaIdentifier().equals("minecraft:axolotl_bucket"); + public boolean acceptItem(ItemMapping mapping) { + return mapping.getJavaIdentifier().equals("minecraft:axolotl_bucket"); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java index c097dd544..58d72de0e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java @@ -29,8 +29,8 @@ import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.chat.MessageTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.ItemUtils; import java.util.ArrayList; @@ -40,11 +40,11 @@ import java.util.List; public class BasicItemTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { Tag damage = itemTag.get("Damage"); if (damage instanceof IntTag) { int originalDurability = ((IntTag) damage).getValue(); - int durability = ItemUtils.getCorrectBedrockDurability(itemEntry.getJavaId(), originalDurability); + int durability = ItemUtils.getCorrectBedrockDurability(session, mapping.getJavaId(), originalDurability); if (durability != originalDurability) { // Fix damage tag inconsistencies itemTag.put(new IntTag("Damage", durability)); @@ -68,7 +68,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { CompoundTag displayTag = itemTag.get("display"); if (displayTag == null) { return; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java index 90eef3bce..208db0987 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java @@ -32,8 +32,8 @@ import com.github.steveice10.opennbt.tag.builtin.Tag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.chat.MessageTranslator; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.ArrayList; import java.util.List; @@ -42,7 +42,7 @@ import java.util.List; public class BookPagesTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("pages")) { return; } @@ -65,7 +65,7 @@ public class BookPagesTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("pages")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java index ae52e4221..b0e55737f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java @@ -30,30 +30,29 @@ import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.registry.type.ItemMapping; @ItemRemapper public class CrossbowTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { if (itemTag.get("ChargedProjectiles") != null) { ListTag chargedProjectiles = itemTag.get("ChargedProjectiles"); if (!chargedProjectiles.getValue().isEmpty()) { CompoundTag projectile = (CompoundTag) chargedProjectiles.getValue().get(0); - ItemEntry projectileEntry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue()); - if (projectileEntry == null) return; + ItemMapping projectileMapping = session.getItemMappings().getMapping((String) projectile.get("id").getValue()); + if (projectileMapping == null) return; CompoundTag tag = projectile.get("tag"); - ItemStack itemStack = new ItemStack(itemEntry.getJavaId(), (byte) projectile.get("Count").getValue(), tag); + ItemStack itemStack = new ItemStack(mapping.getJavaId(), (byte) projectile.get("Count").getValue(), tag); ItemData itemData = ItemTranslator.translateToBedrock(session, itemStack); CompoundTag newProjectile = new CompoundTag("chargedItem"); newProjectile.put(new ByteTag("Count", (byte) itemData.getCount())); - newProjectile.put(new StringTag("Name", projectileEntry.getBedrockIdentifier())); + newProjectile.put(new StringTag("Name", projectileMapping.getBedrockIdentifier())); newProjectile.put(new ShortTag("Damage", (short) itemData.getDamage())); @@ -63,7 +62,7 @@ public class CrossbowTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { if (itemTag.get("chargedItem") != null) { CompoundTag chargedItem = itemTag.get("chargedItem"); @@ -79,7 +78,7 @@ public class CrossbowTranslator extends NbtItemStackTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return "minecraft:crossbow".equals(itemEntry.getJavaIdentifier()); + public boolean acceptItem(ItemMapping mapping) { + return "minecraft:crossbow".equals(mapping.getJavaIdentifier()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java index cc01feb4f..0bca7a8ba 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java @@ -31,13 +31,13 @@ import com.github.steveice10.opennbt.tag.builtin.Tag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; @ItemRemapper(priority = 1) public class EnchantedBookTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("StoredEnchantments")) { return; } @@ -50,7 +50,7 @@ public class EnchantedBookTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("Enchantments")) { return; } @@ -63,7 +63,7 @@ public class EnchantedBookTranslator extends NbtItemStackTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return "minecraft:enchanted_book".equals(itemEntry.getJavaIdentifier()); + public boolean acceptItem(ItemMapping mapping) { + return "minecraft:enchanted_book".equals(mapping.getJavaIdentifier()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java index 2e381d223..58caad3f1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java @@ -31,7 +31,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.Enchantment; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.ArrayList; import java.util.List; @@ -41,7 +41,7 @@ import java.util.Map; public class EnchantmentTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { List newTags = new ArrayList<>(); if (itemTag.contains("Enchantments")) { ListTag enchantmentTag = itemTag.get("Enchantments"); @@ -73,7 +73,7 @@ public class EnchantmentTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("ench")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java index f294315c8..0d37f0477 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkRocketTranslator.java @@ -31,14 +31,14 @@ import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.MathUtils; @ItemRemapper public class FireworkRocketTranslator extends FireworkBaseTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { CompoundTag fireworks = itemTag.get("Fireworks"); if (fireworks == null) { return; @@ -62,7 +62,7 @@ public class FireworkRocketTranslator extends FireworkBaseTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { CompoundTag fireworks = itemTag.get("Fireworks"); if (fireworks == null) { return; @@ -86,7 +86,7 @@ public class FireworkRocketTranslator extends FireworkBaseTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return "minecraft:firework_rocket".equals(itemEntry.getJavaIdentifier()); + public boolean acceptItem(ItemMapping mapping) { + return "minecraft:firework_rocket".equals(mapping.getJavaIdentifier()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java index 686887b45..38eca4856 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkStarTranslator.java @@ -31,13 +31,13 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; @ItemRemapper public class FireworkStarTranslator extends FireworkBaseTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { Tag explosion = itemTag.get("Explosion"); if (explosion instanceof CompoundTag) { CompoundTag newExplosion = translateExplosionToBedrock((CompoundTag) explosion, "FireworksItem"); @@ -78,7 +78,7 @@ public class FireworkStarTranslator extends FireworkBaseTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { Tag explosion = itemTag.get("FireworksItem"); if (explosion instanceof CompoundTag) { CompoundTag newExplosion = translateExplosionToJava((CompoundTag) explosion, "Explosion"); @@ -90,7 +90,7 @@ public class FireworkStarTranslator extends FireworkBaseTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return "minecraft:firework_star".equals(itemEntry.getJavaIdentifier()); + public boolean acceptItem(ItemMapping mapping) { + return "minecraft:firework_star".equals(mapping.getJavaIdentifier()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java index c2305738d..0fd6f1e7b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java @@ -30,16 +30,19 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; + +import java.util.Arrays; +import java.util.List; @ItemRemapper public class LeatherArmorTranslator extends NbtItemStackTranslator { - private static final String[] ITEMS = new String[]{"minecraft:leather_helmet", "minecraft:leather_chestplate", - "minecraft:leather_leggings", "minecraft:leather_boots", "minecraft:leather_horse_armor"}; + private static final List ITEMS = Arrays.asList("minecraft:leather_helmet", "minecraft:leather_chestplate", + "minecraft:leather_leggings", "minecraft:leather_boots", "minecraft:leather_horse_armor"); @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { CompoundTag displayTag = itemTag.get("display"); if (displayTag == null) { return; @@ -52,7 +55,7 @@ public class LeatherArmorTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { IntTag color = itemTag.get("customColor"); if (color == null) { return; @@ -66,10 +69,7 @@ public class LeatherArmorTranslator extends NbtItemStackTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - for (String item : ITEMS) { - if (itemEntry.getJavaIdentifier().equals(item)) return true; - } - return false; + public boolean acceptItem(ItemMapping mapping) { + return ITEMS.contains(mapping.getJavaIdentifier()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java index 79db364b1..14342a8b3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java @@ -28,14 +28,14 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.registry.type.ItemMapping; @ItemRemapper public class MapItemTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { // Can be either an IntTag or ShortTag Tag mapId = itemTag.get("map"); if (mapId == null) return; @@ -55,7 +55,7 @@ public class MapItemTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { IntTag tag = itemTag.get("map_name_index"); if (tag != null) { itemTag.put(new IntTag("map", tag.getValue())); @@ -65,7 +65,7 @@ public class MapItemTranslator extends NbtItemStackTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return itemEntry.getJavaIdentifier().equals("minecraft:filled_map"); + public boolean acceptItem(ItemMapping mapping) { + return mapping.getJavaIdentifier().equals("minecraft:filled_map"); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/PlayerHeadTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/PlayerHeadTranslator.java index 3824ff3c8..152c28daa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/PlayerHeadTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/PlayerHeadTranslator.java @@ -30,15 +30,15 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.LocaleUtils; @ItemRemapper public class PlayerHeadTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("display") || !((CompoundTag) itemTag.get("display")).contains("Name")) { if (itemTag.contains("SkullOwner")) { StringTag name; @@ -66,7 +66,7 @@ public class PlayerHeadTranslator extends NbtItemStackTranslator { } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return itemEntry.getJavaIdentifier().equals("minecraft:player_head"); + public boolean acceptItem(ItemMapping mapping) { + return mapping.getJavaIdentifier().equals("minecraft:player_head"); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java index 197e119fc..5f39d3a68 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -29,12 +29,13 @@ import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.*; +import org.geysermc.connector.registry.type.ItemMapping; @ItemRemapper public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { if (!itemTag.contains("BlockEntityTag")) return; // Empty shulker box CompoundTag blockEntityTag = itemTag.get("BlockEntityTag"); @@ -46,18 +47,18 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { boxItemTag.put(new ByteTag("Slot", ((ByteTag) itemData.get("Slot")).getValue())); boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ??? - ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue()); + ItemMapping boxMapping = session.getItemMappings().getMapping(((StringTag) itemData.get("id")).getValue()); - boxItemTag.put(new StringTag("Name", boxItemEntry.getBedrockIdentifier())); - boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData())); + boxItemTag.put(new StringTag("Name", boxMapping.getBedrockIdentifier())); + boxItemTag.put(new ShortTag("Damage", (short) boxMapping.getBedrockData())); boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue())); // Only the display name is what we have interest in, so just translate that if relevant CompoundTag displayTag = itemData.get("tag"); - if (displayTag == null && boxItemEntry instanceof TranslatableItemEntry) { + if (displayTag == null && boxMapping.hasTranslation()) { displayTag = new CompoundTag("tag"); } if (displayTag != null) { - boxItemTag.put(ItemTranslator.translateDisplayProperties(session, displayTag, boxItemEntry, '7')); + boxItemTag.put(ItemTranslator.translateDisplayProperties(session, displayTag, boxMapping, '7')); } itemsList.add(boxItemTag); @@ -69,14 +70,14 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { } @Override - public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToJava(CompoundTag itemTag, ItemMapping mapping) { if (itemTag.contains("Items")) { // Remove any extraneous Bedrock tag and don't touch the Java one itemTag.remove("Items"); } } @Override - public boolean acceptItem(ItemEntry itemEntry) { - return itemEntry.getJavaIdentifier().contains("shulker_box"); + public boolean acceptItem(ItemMapping mapping) { + return mapping.getJavaIdentifier().contains("shulker_box"); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTranslator.java index 714578e9a..2e1057e0e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaAdvancementsTranslator.java @@ -95,6 +95,8 @@ public class JavaAdvancementsTranslator extends PacketTranslator new HashSet<>()).add(node.getName().toLowerCase()); @@ -168,11 +167,12 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator= 1) { // Create the root param node and build all the children ParamInfo rootParam = new ParamInfo(commandNode, null); - rootParam.buildChildren(allNodes); + rootParam.buildChildren(session, allNodes); List treeData = rootParam.getTree(); @@ -195,10 +195,11 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator { + /** + * Required to use the specified cartography table recipes + */ + private static final List CARTOGRAPHY_RECIPES = Arrays.asList( + CraftingData.fromMulti(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), ++LAST_RECIPE_NET_ID), // Map extending + CraftingData.fromMulti(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), ++LAST_RECIPE_NET_ID), // Map cloning + CraftingData.fromMulti(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), ++LAST_RECIPE_NET_ID), // Map upgrading + CraftingData.fromMulti(UUID.fromString("602234e4-cac1-4353-8bb7-b1ebff70024b"), ++LAST_RECIPE_NET_ID) // Map locking + ); @Override public void translate(ServerDeclareRecipesPacket packet, GeyserSession session) { + Map> recipeTypes = Registries.CRAFTING_DATA.forVersion(session.getUpstream().getProtocolVersion()); // Get the last known network ID (first used for the pregenerated recipes) and increment from there. - int netId = RecipeRegistry.LAST_RECIPE_NET_ID + 1; - Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(RecipeRegistry.ALL_CRAFTING_RECIPES); + int netId = InventoryUtils.LAST_RECIPE_NET_ID + 1; + + Int2ObjectMap recipeMap = new Int2ObjectOpenHashMap<>(Registries.RECIPES.forVersion(session.getUpstream().getProtocolVersion())); Int2ObjectMap> unsortedStonecutterData = new Int2ObjectOpenHashMap<>(); CraftingDataPacket craftingDataPacket = new CraftingDataPacket(); craftingDataPacket.setCleanRecipes(true); @@ -94,55 +111,6 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator craftingData = recipeTypes.get(recipe.getType()); + if (craftingData != null) { + craftingDataPacket.getCraftingData().addAll(craftingData); + } + break; } } } - // Add all cartography table recipe UUIDs, so we can use the cartography table - craftingDataPacket.getCraftingData().addAll(RecipeRegistry.CARTOGRAPHY_RECIPE_DATA); - - craftingDataPacket.getPotionMixData().addAll(PotionMixRegistry.POTION_MIXES); + craftingDataPacket.getCraftingData().addAll(CARTOGRAPHY_RECIPES); + craftingDataPacket.getPotionMixData().addAll(Registries.POTION_MIXES.get()); Int2ObjectMap stonecutterRecipeMap = new Int2ObjectOpenHashMap<>(); for (Int2ObjectMap.Entry> data : unsortedStonecutterData.int2ObjectEntrySet()) { // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore // We can get the correct order for button pressing data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData -> - ItemRegistry.getItem(stoneCuttingRecipeData.getResult()).getJavaIdentifier()))); + session.getItemMappings().getItems().get(stoneCuttingRecipeData.getResult().getId()).getJavaIdentifier()))); // Now that it's sorted, let's translate these recipes for (StoneCuttingRecipeData stoneCuttingData : data.getValue()) { @@ -222,8 +196,8 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator new IntOpenHashSet()).add(i); } int totalCombinations = 1; - for (Set optionSet : squashedOptions.keySet()) { + for (Set optionSet : squashedOptions.keySet()) { totalCombinations *= optionSet.size(); } if (totalCombinations > 500) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java index 162028ef8..af1cfc45b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java @@ -38,7 +38,6 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.item.ItemRegistry; @Translator(packet = ServerEntityStatusPacket.class) public class JavaEntityStatusTranslator extends PacketTranslator { @@ -98,7 +97,7 @@ public class JavaEntityStatusTranslator extends PacketTranslator { @@ -77,7 +76,7 @@ public class JavaBlockValueTranslator extends PacketTranslator { try { + if (session.isClosed()) { + return; + } ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, column, yOffset); ChunkSection[] sections = chunkData.getSections(); @@ -74,7 +77,7 @@ public class JavaChunkDataTranslator extends PacketTranslator { @@ -44,7 +46,7 @@ public class JavaPlayBuiltinSoundTranslator extends PacketTranslator { @@ -50,7 +51,7 @@ public class JavaPlaySoundTranslator extends PacketTranslator createParticle(GeyserSession session, Particle particle) { switch (particle.getType()) { case BLOCK: { - int blockState = session.getBlockTranslator().getBedrockBlockId(((BlockParticleData) particle.getData()).getBlockState()); + int blockState = session.getBlockMappings().getBedrockBlockId(((BlockParticleData) particle.getData()).getBlockState()); return (position) -> { LevelEventPacket packet = new LevelEventPacket(); packet.setType(LevelEventType.PARTICLE_CRACK_BLOCK); @@ -91,7 +91,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator { LevelEventPacket packet = new LevelEventPacket(); // In fact, FallingDustParticle should have data like DustParticle, @@ -130,7 +130,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator { LevelEventPacket packet = new LevelEventPacket(); @@ -139,7 +139,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaStopSoundTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaStopSoundTranslator.java index d7d0f0738..ebcf2234e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaStopSoundTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaStopSoundTranslator.java @@ -32,7 +32,8 @@ import com.nukkitx.protocol.bedrock.packet.StopSoundPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.sound.SoundRegistry; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.SoundMapping; @Translator(packet = ServerStopSoundPacket.class) public class JavaStopSoundTranslator extends PacketTranslator { @@ -57,7 +58,7 @@ public class JavaStopSoundTranslator extends PacketTranslator " + soundMapping + (soundMapping == null ? "[not found]" : "") diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java index c4dcf50c9..cce48f5cc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaTradeListTranslator.java @@ -41,9 +41,8 @@ import org.geysermc.connector.inventory.MerchantContainer; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.ArrayList; import java.util.List; @@ -136,18 +135,18 @@ public class JavaTradeListTranslator extends PacketTranslator> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { + for (Map.Entry> interactionEntry : Registries.SOUND_HANDLERS.get().entrySet()) { if (!(interactionEntry.getValue() instanceof BlockSoundInteractionHandler)) { continue; } @@ -68,7 +69,7 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { + for (Map.Entry> interactionEntry : Registries.SOUND_HANDLERS.get().entrySet()) { if (!(interactionEntry.getValue() instanceof EntitySoundInteractionHandler)) { continue; } @@ -70,7 +71,7 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler bedrockBlockStates; - - /** - * Stores a list of differences in block identifiers. - * Items will not be added to this list if the key and value is the same. - */ - private static final Object2ObjectMap JAVA_TO_BEDROCK_IDENTIFIERS = new Object2ObjectOpenHashMap<>(); - private static final BiMap JAVA_ID_BLOCK_MAP = HashBiMap.create(); - private static final IntSet WATERLOGGED = new IntOpenHashSet(); - private final Object2IntMap itemFrames = new Object2IntOpenHashMap<>(); - private final Map flowerPotBlocks = new HashMap<>(); - - private static final Int2ObjectMap JAVA_RUNTIME_ID_TO_BLOCK_MAPPING = new Int2ObjectOpenHashMap<>(); - - /** - * Java numeric ID to java unique identifier, used for block names in the statistics screen - */ - public static final Int2ObjectMap JAVA_ID_TO_JAVA_IDENTIFIER_MAP = new Int2ObjectOpenHashMap<>(); - - /** - * Runtime command block ID, used for fixing command block minecart appearances - */ - @Getter - private final int bedrockRuntimeCommandBlockId; - - private final EmptyChunkProvider emptyChunkProvider; - - public static final int JAVA_COBWEB_BLOCK_ID; - public static final int JAVA_BELL_BLOCK_ID; - - public static final int JAVA_RUNTIME_FURNACE_ID; - public static final int JAVA_RUNTIME_FURNACE_LIT_ID; - - public static final int JAVA_RUNTIME_SPAWNER_ID; - - /** - * Contains a map of Java blocks to their respective Bedrock block tag, if the Java identifier is different from Bedrock. - * Required to fix villager trades with these blocks. - */ - private final Map javaIdentifierToBedrockTag; - - /** - * Stores the raw blocks JSON until it is no longer needed. - */ - public static JsonNode BLOCKS_JSON; - - static { - InputStream stream = FileUtils.getResource("mappings/blocks.json"); - try { - BLOCKS_JSON = GeyserConnector.JSON_MAPPER.readTree(stream); - } catch (Exception e) { - throw new AssertionError("Unable to load Java block mappings", e); - } - - int javaRuntimeId = -1; - int bellBlockId = -1; - int cobwebBlockId = -1; - int furnaceRuntimeId = -1; - int furnaceLitRuntimeId = -1; - int spawnerRuntimeId = -1; - int uniqueJavaId = -1; - int waterRuntimeId = -1; - Iterator> blocksIterator = BLOCKS_JSON.fields(); - while (blocksIterator.hasNext()) { - javaRuntimeId++; - Map.Entry entry = blocksIterator.next(); - String javaId = entry.getKey(); - - BlockMapping.BlockMappingBuilder builder = BlockMapping.builder(); - // TODO fix this, (no block should have a null hardness) - JsonNode hardnessNode = entry.getValue().get("block_hardness"); - if (hardnessNode != null) { - builder.hardness(hardnessNode.doubleValue()); - } - - JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand"); - if (canBreakWithHandNode != null) { - builder.canBreakWithHand(canBreakWithHandNode.booleanValue()); - } else { - builder.canBreakWithHand(false); - } - - JsonNode collisionIndexNode = entry.getValue().get("collision_index"); - if (hardnessNode != null) { - builder.collisionIndex(collisionIndexNode.intValue()); - } - - JsonNode pickItemNode = entry.getValue().get("pick_item"); - if (pickItemNode != null) { - builder.pickItem(pickItemNode.textValue()); - } - - boolean waterlogged = entry.getKey().contains("waterlogged=true") - || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); - - if (waterlogged) { - WATERLOGGED.add(javaRuntimeId); - } - - JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); - - BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue()); - - String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; - String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); - - if (!JAVA_ID_TO_JAVA_IDENTIFIER_MAP.containsValue(cleanJavaIdentifier)) { - uniqueJavaId++; - JAVA_ID_TO_JAVA_IDENTIFIER_MAP.put(uniqueJavaId, cleanJavaIdentifier); - } - - // Keeping this here since this is currently unchanged between versions - if (!cleanJavaIdentifier.equals(bedrockIdentifier)) { - JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier); - } - - builder.javaBlockId(uniqueJavaId); - - builder.javaIdentifier(javaId); - - JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.put(javaRuntimeId, builder.build()); - - if (javaId.startsWith("minecraft:bell[")) { - bellBlockId = uniqueJavaId; - - } else if (javaId.contains("cobweb")) { - cobwebBlockId = uniqueJavaId; - - } else if (javaId.startsWith("minecraft:furnace[facing=north")) { - if (javaId.contains("lit=true")) { - furnaceLitRuntimeId = javaRuntimeId; - } else { - furnaceRuntimeId = javaRuntimeId; - } - - } else if (javaId.startsWith("minecraft:spawner")) { - spawnerRuntimeId = javaRuntimeId; - - } else if ("minecraft:water[level=0]".equals(javaId)) { - waterRuntimeId = javaRuntimeId; - } - } - - if (bellBlockId == -1) { - throw new AssertionError("Unable to find bell in palette"); - } - JAVA_BELL_BLOCK_ID = bellBlockId; - - if (cobwebBlockId == -1) { - throw new AssertionError("Unable to find cobwebs in palette"); - } - JAVA_COBWEB_BLOCK_ID = cobwebBlockId; - - if (furnaceRuntimeId == -1) { - throw new AssertionError("Unable to find furnace in palette"); - } - JAVA_RUNTIME_FURNACE_ID = furnaceRuntimeId; - - if (furnaceLitRuntimeId == -1) { - throw new AssertionError("Unable to find lit furnace in palette"); - } - JAVA_RUNTIME_FURNACE_LIT_ID = furnaceLitRuntimeId; - - if (spawnerRuntimeId == -1) { - throw new AssertionError("Unable to find spawner in palette"); - } - JAVA_RUNTIME_SPAWNER_ID = spawnerRuntimeId; - - if (waterRuntimeId == -1) { - throw new AssertionError("Unable to find Java water in palette"); - } - JAVA_WATER_ID = waterRuntimeId; - - BlockMapping.AIR = JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.get(JAVA_AIR_ID); - - BlockTranslator1_17_0.init(); - BLOCKS_JSON = null; // We no longer require this so let it garbage collect away - } - - public BlockTranslator(String paletteFile) { - /* Load block palette */ - InputStream stream = FileUtils.getResource(paletteFile); - - NbtList blocksTag; - try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)))) { - NbtMap blockPalette = (NbtMap) nbtInputStream.readTag(); - blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND); - this.bedrockBlockStates = blocksTag; - } catch (Exception e) { - throw new AssertionError("Unable to get blocks from runtime block states", e); - } - - javaIdentifierToBedrockTag = new Object2ObjectOpenHashMap<>(); - - // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette, - // as we no longer send a block palette - Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size()); - - for (int i = 0; i < blocksTag.size(); i++) { - NbtMap tag = blocksTag.get(i); - if (blockStateOrderedMap.containsKey(tag)) { - throw new AssertionError("Duplicate block states in Bedrock palette: " + tag); - } - blockStateOrderedMap.put(tag, i); - } - - int airRuntimeId = -1; - int commandBlockRuntimeId = -1; - int javaRuntimeId = -1; - int waterRuntimeId = -1; - Iterator> blocksIterator = BLOCKS_JSON.fields(); - while (blocksIterator.hasNext()) { - javaRuntimeId++; - Map.Entry entry = blocksIterator.next(); - String javaId = entry.getKey(); - - NbtMap blockTag = buildBedrockState(entry.getValue()); - int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1); - if (bedrockRuntimeId == -1) { - throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID! Built compound tag: \n" + blockTag); - } - - switch (javaId) { - case "minecraft:air": - airRuntimeId = bedrockRuntimeId; - break; - case "minecraft:water[level=0]": - waterRuntimeId = bedrockRuntimeId; - break; - case "minecraft:command_block[conditional=false,facing=north]": - commandBlockRuntimeId = bedrockRuntimeId; - break; - } - - boolean waterlogged = entry.getKey().contains("waterlogged=true") - || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); - - if (waterlogged) { - bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId); - } else { - bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId, javaRuntimeId); - } - - String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; - - // Get the tag needed for non-empty flower pots - if (entry.getValue().get("pottable") != null) { - flowerPotBlocks.put(cleanJavaIdentifier, blockTag); - } - - if (!cleanJavaIdentifier.equals(entry.getValue().get("bedrock_identifier").asText())) { - javaIdentifierToBedrockTag.put(cleanJavaIdentifier, blockTag); - } - - javaToBedrockBlockMap.put(javaRuntimeId, bedrockRuntimeId); - } - - if (commandBlockRuntimeId == -1) { - throw new AssertionError("Unable to find command block in palette"); - } - bedrockRuntimeCommandBlockId = commandBlockRuntimeId; - - if (waterRuntimeId == -1) { - throw new AssertionError("Unable to find water in palette"); - } - bedrockWaterId = waterRuntimeId; - - if (airRuntimeId == -1) { - throw new AssertionError("Unable to find air in palette"); - } - bedrockAirId = airRuntimeId; - - // Loop around again to find all item frame runtime IDs - for (Object2IntMap.Entry entry : blockStateOrderedMap.object2IntEntrySet()) { - String name = entry.getKey().getString("name"); - if (name.equals("minecraft:frame") || name.equals("minecraft:glow_frame")) { - itemFrames.put(entry.getKey(), entry.getIntValue()); - } - } - - this.emptyChunkProvider = new EmptyChunkProvider(bedrockAirId); - } - - public static void init() { - // no-op - } - - private NbtMap buildBedrockState(JsonNode node) { - NbtMapBuilder tagBuilder = NbtMap.builder(); - String bedrockIdentifier = node.get("bedrock_identifier").textValue(); - tagBuilder.putString("name", bedrockIdentifier) - .putInt("version", getBlockStateVersion()); - - NbtMapBuilder statesBuilder = NbtMap.builder(); - - // check for states - if (node.has("bedrock_states")) { - Iterator> statesIterator = node.get("bedrock_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()); - continue; - case STRING: - statesBuilder.putString(stateEntry.getKey(), stateValue.textValue()); - continue; - case NUMBER: - statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue()); - } - } - } - tagBuilder.put("states", adjustBlockStateForVersion(bedrockIdentifier, statesBuilder).build()); - return tagBuilder.build(); - } - - /** - * @return an adjusted state list, if necessary, that converts Geyser's new mapping to Bedrock's older version - * of the mapping. - */ - protected NbtMapBuilder adjustBlockStateForVersion(String bedrockIdentifier, NbtMapBuilder statesBuilder) { - return statesBuilder; - } - - public int getBedrockBlockId(int state) { - return javaToBedrockBlockMap.get(state); - } - - public int getJavaBlockState(int bedrockId) { - return bedrockToJavaBlockMap.get(bedrockId); - } - - /** - * @param javaIdentifier the Java identifier of the block to search for - * @return the Bedrock identifier if different, or else the Java identifier - */ - public String getBedrockBlockIdentifier(String javaIdentifier) { - return JAVA_TO_BEDROCK_IDENTIFIERS.getOrDefault(javaIdentifier, javaIdentifier); - } - - public int getItemFrame(NbtMap tag) { - return itemFrames.getOrDefault(tag, -1); - } - - public boolean isItemFrame(int bedrockBlockRuntimeId) { - return itemFrames.values().contains(bedrockBlockRuntimeId); - } - - /** - * Get the map of contained flower pot plants to Bedrock CompoundTag - * - * @return Map of flower pot blocks. - */ - public Map getFlowerPotBlocks() { - return flowerPotBlocks; - } - - public int getBedrockAirId() { - return bedrockAirId; - } - - public int getBedrockWaterId() { - return bedrockWaterId; - } - - public NbtList getAllBedrockBlockStates() { - return this.bedrockBlockStates; - } - - /** - * @return the "block state version" generated in the Bedrock block palette that completes an NBT indication of a - * block state. - */ - public abstract int getBlockStateVersion(); - - public byte[] getEmptyChunkData() { - return emptyChunkProvider.getEmptyLevelChunkData(); - } - - public ChunkSection getEmptyChunkSection() { - return emptyChunkProvider.getEmptySection(); - } - - /** - * @param javaId the Java string identifier to search for - * @return the Java block state integer, or {@link #JAVA_AIR_ID} if there is no valid entry. - */ - public static int getJavaBlockState(String javaId) { - return JAVA_ID_BLOCK_MAP.getOrDefault(javaId, JAVA_AIR_ID); - } - - public static boolean isWaterlogged(int state) { - return WATERLOGGED.contains(state); - } - - public static BiMap getJavaIdBlockMap() { - return JAVA_ID_BLOCK_MAP; - } - - /** - * @param javaRuntimeId the Java runtime ID of the block to search for. - * @return the corresponding block mapping for this runtime ID. - */ - public static BlockMapping getBlockMapping(int javaRuntimeId) { - return JAVA_RUNTIME_ID_TO_BLOCK_MAPPING.getOrDefault(javaRuntimeId, BlockMapping.AIR); - } - - /** - * @return a list of all Java block identifiers. For use with command suggestions. - */ - public static String[] getAllBlockIdentifiers() { - return JAVA_ID_TO_JAVA_IDENTIFIER_MAP.values().toArray(new String[0]); - } - - /** - * @param cleanJavaIdentifier the clean Java identifier of the block to look up - * - * @return the block tag of the block name mapped from Java to Bedrock. - */ - public NbtMap getBedrockBlockNbt(String cleanJavaIdentifier) { - return javaIdentifierToBedrockTag.get(cleanJavaIdentifier); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java index 983a3d06d..41843e96a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java @@ -31,69 +31,15 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.BlockEntityUtils; -import org.geysermc.connector.utils.FileUtils; -import org.reflections.Reflections; - -import java.util.HashMap; -import java.util.Map; /** * The class that all block entities (on both Java and Bedrock) should translate with */ public abstract class BlockEntityTranslator { - public static final Map BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); - /** - * A list of all block entities that only exist on Bedrock - */ - public static final ObjectArrayList BEDROCK_ONLY_BLOCK_ENTITIES = new ObjectArrayList<>(); - - /** - * Contains a list of irregular block entity name translations that can't be fit into the regex - */ - public static final Map BLOCK_ENTITY_TRANSLATIONS = new HashMap() { - { - // Bedrock/Java differences - put("minecraft:enchanting_table", "EnchantTable"); - put("minecraft:jigsaw", "JigsawBlock"); - put("minecraft:piston_head", "PistonArm"); - put("minecraft:trapped_chest", "Chest"); - // There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly - } - }; - protected BlockEntityTranslator() { } - public static void init() { - // no-op - } - - static { - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); - for (Class clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) { - GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName()); - - try { - BLOCK_ENTITY_TRANSLATORS.put(clazz.getAnnotation(BlockEntity.class).name(), (BlockEntityTranslator) clazz.newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block entity" + clazz.getCanonicalName()); - } - } - for (Class clazz : ref.getSubTypesOf(BedrockOnlyBlockEntity.class)) { - GeyserConnector.getInstance().getLogger().debug("Found Bedrock-only block entity: " + clazz.getCanonicalName()); - - try { - BedrockOnlyBlockEntity bedrockOnlyBlockEntity = (BedrockOnlyBlockEntity) clazz.newInstance(); - BEDROCK_ONLY_BLOCK_ENTITIES.add(bedrockOnlyBlockEntity); - } catch (InstantiationException | IllegalAccessException e) { - GeyserConnector.getInstance().getLogger().error("Could not instantiate annotated block state " + clazz.getCanonicalName()); - } - } - } - public abstract void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState); public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java index 40f305ad6..88bb14203 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java @@ -29,8 +29,9 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.BedrockProtocol; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; @BlockEntity(name = "Campfire") public class CampfireBlockEntityTranslator extends BlockEntityTranslator { @@ -45,11 +46,12 @@ public class CampfireBlockEntityTranslator extends BlockEntityTranslator { } protected NbtMap getItem(CompoundTag tag) { - ItemEntry entry = ItemRegistry.getItemEntry((String) tag.get("id").getValue()); + // TODO: Version independent mappings + ItemMapping mapping = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping((String) tag.get("id").getValue()); NbtMapBuilder tagBuilder = NbtMap.builder() - .putString("Name", entry.getBedrockIdentifier()) + .putString("Name", mapping.getBedrockIdentifier()) .putByte("Count", (byte) tag.get("Count").getValue()) - .putShort("Damage", (short) entry.getBedrockData()); + .putShort("Damage", (short) mapping.getBedrockData()); tagBuilder.put("tag", NbtMap.builder().build()); return tagBuilder.build(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java index 1fa6cab02..833542543 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/FlowerPotBlockEntityTranslator.java @@ -61,7 +61,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity { if (name != null) { // 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.getBlockTranslator().getFlowerPotBlocks().get(name); + NbtMap plant = session.getBlockMappings().getFlowerPotBlocks().get(name); if (plant != null) { tagBuilder.put("PlantBlock", plant.toBuilder().build()); } @@ -80,7 +80,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity { BlockEntityUtils.updateBlockEntity(session, tag, position); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); - updateBlockPacket.setRuntimeId(session.getBlockTranslator().getBedrockBlockId(blockState)); + updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(blockState)); updateBlockPacket.setBlockPosition(position); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java index ad6c32d4f..0551386c5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java @@ -34,69 +34,72 @@ import org.geysermc.connector.utils.SignUtils; @BlockEntity(name = "Sign") public class SignBlockEntityTranslator extends BlockEntityTranslator { /** - * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. - *
- * The color names correspond to dye names, because of this we can't use a more global method. + * Maps a color stored in a sign's Color tag to its ARGB value. * * @param javaColor The dye color stored in the sign's Color tag. - * @return A Bedrock Edition formatting code for valid dye colors, otherwise an empty string. + * @return Java Edition's integer matching the color specified */ - private String getBedrockSignColor(String javaColor) { - String base = "\u00a7"; + private int getBedrockSignColor(String javaColor) { + //TODO create a DyeColor class and combine with FireworkColor??? + int dyeColor; switch (javaColor) { case "white": - base += 'f'; + dyeColor = 16383998; break; case "orange": - base += '6'; + dyeColor = 16351261; break; case "magenta": - case "purple": - base += '5'; + dyeColor = 13061821; break; case "light_blue": - base += 'b'; + dyeColor = 3847130; break; case "yellow": - base += 'e'; + dyeColor = 16701501; break; case "lime": - base += 'a'; + dyeColor = 8439583; break; case "pink": - base += 'd'; + dyeColor = 15961002; break; case "gray": - base += '8'; + dyeColor = 4673362; break; case "light_gray": - base += '7'; + dyeColor = 10329495; break; case "cyan": - base += '3'; + dyeColor = 1481884; + break; + case "purple": + dyeColor = 8991416; break; case "blue": - base += '9'; + dyeColor = 3949738; break; - case "brown": // Brown does not have a bedrock counterpart. - case "red": // In Java Edition light red (&c) can only be applied using commands. Red dye gives &4. - base += '4'; + case "brown": + dyeColor = 8606770; break; case "green": - base += '2'; + dyeColor = 6192150; + break; + case "red": + dyeColor = 11546150; break; case "black": - base += '0'; - break; default: - return ""; + // The proper Java color is 1908001, but this does not render well with glow text. + dyeColor = 0; + break; } - return base; + // Add the transparency of the color, too. + return dyeColor | (255 << 24); } @Override public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { - //TODO Bedrock 1.17.10 glow text StringBuilder signText = new StringBuilder(); for (int i = 0; i < 4; i++) { int currentLine = i + 1; @@ -127,16 +130,21 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { } } - // Java Edition 1.14 added the ability to change the text color of the whole sign using dye - Tag color = tag.get("Color"); - if (color != null) { - signText.append(getBedrockSignColor(color.getValue().toString())); - } - signText.append(finalSignLine); signText.append("\n"); } - builder.put("Text", signText.toString()); + builder.putString("Text", signText.toString()); + + // Java Edition 1.14 added the ability to change the text color of the whole sign using dye + Tag color = tag.get("Color"); + if (color != null) { + builder.putInt("SignTextColor", getBedrockSignColor(color.getValue().toString())); + } + + // Glowing text + boolean isGlowing = getOrDefault(tag.getValue().get("GlowingText"), (byte) 0) != (byte) 0; + builder.putBoolean("IgnoreLighting", isGlowing); + builder.putBoolean("TextIgnoreLegacyBugResolved", isGlowing); // ??? required } } diff --git a/connector/src/main/java/org/geysermc/connector/registry/AbstractMappedRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/AbstractMappedRegistry.java new file mode 100644 index 000000000..034eef706 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/AbstractMappedRegistry.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import org.geysermc.connector.registry.loader.RegistryLoader; + +import java.util.Map; + +public abstract class AbstractMappedRegistry> extends Registry { + protected AbstractMappedRegistry(I input, RegistryLoader registryLoader) { + super(input, registryLoader); + } + + public V get(K key) { + return this.mappings.get(key); + } + + public V getOrDefault(K key, V defaultValue) { + return this.mappings.getOrDefault(key, defaultValue); + } + + public V register(K key, V value) { + return this.mappings.put(key, value); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/BlockRegistries.java b/connector/src/main/java/org/geysermc/connector/registry/BlockRegistries.java new file mode 100644 index 000000000..bc92f4b93 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/BlockRegistries.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +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.Object2ObjectOpenHashMap; +import org.geysermc.connector.registry.loader.RegistryLoaders; +import org.geysermc.connector.registry.populator.BlockRegistryPopulator; +import org.geysermc.connector.registry.type.BlockMapping; +import org.geysermc.connector.registry.type.BlockMappings; + +public class BlockRegistries { + public static final VersionedRegistry BLOCKS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + + public static final SimpleMappedRegistry JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); + + public static final SimpleMappedRegistry JAVA_BLOCKS = SimpleMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + + public static final MappedRegistry> JAVA_IDENTIFIERS = MappedRegistry.create(RegistryLoaders.empty(HashBiMap::create)); + + public static final SimpleMappedRegistry JAVA_CLEAN_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + + public static final SimpleRegistry WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(IntOpenHashSet::new)); + + static { + BlockRegistryPopulator.populate(); + } + + public static void init() { + // no-op + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/MappedRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/MappedRegistry.java new file mode 100644 index 000000000..41d831f98 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/MappedRegistry.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import org.geysermc.connector.registry.loader.RegistryLoader; + +import java.util.Map; +import java.util.function.Supplier; + +public class MappedRegistry> extends AbstractMappedRegistry { + protected MappedRegistry(I input, RegistryLoader registryLoader) { + super(input, registryLoader); + } + + public static > MappedRegistry createEmpty() { + return new MappedRegistry<>(null, input -> null); + } + + public static > MappedRegistry create(RegistryLoader registryLoader) { + return new MappedRegistry<>(null, registryLoader); + } + + public static > MappedRegistry create(I input, RegistryLoader registryLoader) { + return new MappedRegistry<>(input, registryLoader); + } + + public static > MappedRegistry create(Supplier> registryLoader) { + return new MappedRegistry<>(null, registryLoader.get()); + } + + public static > MappedRegistry create(I input, Supplier> registryLoader) { + return new MappedRegistry<>(input, registryLoader.get()); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/Registries.java b/connector/src/main/java/org/geysermc/connector/registry/Registries.java new file mode 100644 index 000000000..cb23e9976 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/Registries.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; +import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; +import com.github.steveice10.mc.protocol.data.game.world.effect.SoundEffect; +import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.protocol.bedrock.data.SoundEvent; +import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; +import com.nukkitx.protocol.bedrock.data.inventory.PotionMixData; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import org.geysermc.connector.network.translators.collision.translators.BlockCollision; +import org.geysermc.connector.network.translators.effect.Effect; +import org.geysermc.connector.network.translators.sound.SoundHandler; +import org.geysermc.connector.network.translators.sound.SoundInteractionHandler; +import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; +import org.geysermc.connector.registry.loader.BlockEntityRegistryLoader; +import org.geysermc.connector.registry.loader.CollisionRegistryLoader; +import org.geysermc.connector.registry.loader.ParticleTypesRegistryLoader; +import org.geysermc.connector.registry.loader.PotionMixRegistryLoader; +import org.geysermc.connector.registry.loader.RegistryLoaders; +import org.geysermc.connector.registry.loader.SoundEffectsRegistryLoader; +import org.geysermc.connector.registry.loader.SoundHandlerRegistryLoader; +import org.geysermc.connector.registry.loader.SoundRegistryLoader; +import org.geysermc.connector.registry.populator.ItemRegistryPopulator; +import org.geysermc.connector.registry.populator.RecipeRegistryPopulator; +import org.geysermc.connector.registry.type.ItemMappings; +import org.geysermc.connector.registry.type.ParticleMapping; +import org.geysermc.connector.registry.type.SoundMapping; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Registries { + public static final SimpleRegistry BIOMES = SimpleRegistry.create("bedrock/biome_definitions.dat", RegistryLoaders.NBT); + + public static final SimpleMappedRegistry BLOCK_ENTITIES = SimpleMappedRegistry.create("org.geysermc.connector.network.translators.world.block.entity", BlockEntityRegistryLoader::new); + + public static final SimpleMappedRegistry COLLISIONS = SimpleMappedRegistry.create(Pair.of("org.geysermc.connector.network.translators.collision.translators", "mappings/collision.json"), CollisionRegistryLoader::new); + + public static final VersionedRegistry>> CRAFTING_DATA = VersionedRegistry.create(RegistryLoaders.empty(Object2ObjectLinkedOpenHashMap::new)); + + public static final SimpleRegistry ENTITY_IDENTIFIERS = SimpleRegistry.create("bedrock/entity_identifiers.dat", RegistryLoaders.NBT); + + public static final VersionedRegistry ITEMS = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + + public static final SimpleMappedRegistry PARTICLES = SimpleMappedRegistry.create("mappings/particles.json", ParticleTypesRegistryLoader::new); + + public static final SimpleRegistry> POTION_MIXES; + + public static final VersionedRegistry> RECIPES = VersionedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + + public static final SimpleMappedRegistry RECORDS = SimpleMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new)); + + public static final SimpleMappedRegistry SOUNDS = SimpleMappedRegistry.create("mappings/sounds.json", SoundRegistryLoader::new); + + public static final SimpleMappedRegistry SOUND_EFFECTS = SimpleMappedRegistry.create("mappings/effects.json", SoundEffectsRegistryLoader::new); + + public static final SimpleMappedRegistry> SOUND_HANDLERS = SimpleMappedRegistry.create("org.geysermc.connector.network.translators.sound", SoundHandlerRegistryLoader::new); + + public static void init() { + // no-op + } + + static { + ItemRegistryPopulator.populate(); + RecipeRegistryPopulator.populate(); + + // Create registries that require other registries to load first + POTION_MIXES = SimpleRegistry.create(PotionMixRegistryLoader::new); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/Registry.java b/connector/src/main/java/org/geysermc/connector/registry/Registry.java new file mode 100644 index 000000000..ca3d22373 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/Registry.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import org.geysermc.connector.registry.loader.RegistryLoader; + +import java.util.function.Consumer; + +public abstract class Registry { + protected final M mappings; + + protected Registry(I input, RegistryLoader registryLoader) { + this.mappings = registryLoader.load(input); + } + + public M get() { + return this.mappings; + } + + public void register(Consumer consumer) { + consumer.accept(this.mappings); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/SimpleMappedRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/SimpleMappedRegistry.java new file mode 100644 index 000000000..1188b3590 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/SimpleMappedRegistry.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import org.geysermc.connector.registry.loader.RegistryLoader; + +import java.util.Map; +import java.util.function.Supplier; + +public class SimpleMappedRegistry extends AbstractMappedRegistry> { + protected SimpleMappedRegistry(I input, RegistryLoader> registryLoader) { + super(input, registryLoader); + } + + public static SimpleMappedRegistry create(RegistryLoader> registryLoader) { + return new SimpleMappedRegistry<>(null, registryLoader); + } + + public static SimpleMappedRegistry create(I input, RegistryLoader> registryLoader) { + return new SimpleMappedRegistry<>(input, registryLoader); + } + + public static SimpleMappedRegistry create(Supplier>> registryLoader) { + return new SimpleMappedRegistry<>(null, registryLoader.get()); + } + + public static SimpleMappedRegistry create(I input, Supplier>> registryLoader) { + return new SimpleMappedRegistry<>(input, registryLoader.get()); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/SimpleRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/SimpleRegistry.java new file mode 100644 index 000000000..1bea97954 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/SimpleRegistry.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import org.geysermc.connector.registry.loader.RegistryLoader; + +import java.util.function.Supplier; + +public class SimpleRegistry extends Registry { + private SimpleRegistry(I input, RegistryLoader registryLoader) { + super(input, registryLoader); + } + + public static SimpleRegistry create(Supplier> registryLoader) { + return new SimpleRegistry<>(null, registryLoader.get()); + } + + public static SimpleRegistry create(I input, Supplier> registryLoader) { + return new SimpleRegistry<>(input, registryLoader.get()); + } + + public static SimpleRegistry create(RegistryLoader registryLoader) { + return new SimpleRegistry<>(null, registryLoader); + } + + public static SimpleRegistry create(I input, RegistryLoader registryLoader) { + return new SimpleRegistry<>(input, registryLoader); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/VersionedRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/VersionedRegistry.java new file mode 100644 index 000000000..d29bcddaa --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/VersionedRegistry.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry; + +import org.geysermc.connector.registry.loader.RegistryLoader; + +import java.util.Map; +import java.util.function.Supplier; + +public class VersionedRegistry extends AbstractMappedRegistry> { + protected VersionedRegistry(I input, RegistryLoader> registryLoader) { + super(input, registryLoader); + } + + public V forVersion(int version) { + V value = null; + for (Map.Entry entry : this.mappings.entrySet()) { + if (version < entry.getKey()) { + continue; + } + if (version == entry.getKey()) { + return entry.getValue(); + } + value = entry.getValue(); + } + return value; + } + + public static VersionedRegistry create(RegistryLoader> registryLoader) { + return new VersionedRegistry<>(null, registryLoader); + } + + public static VersionedRegistry create(I input, RegistryLoader> registryLoader) { + return new VersionedRegistry<>(input, registryLoader); + } + + public static VersionedRegistry< V> create(Supplier>> registryLoader) { + return new VersionedRegistry<>(null, registryLoader.get()); + } + + public static VersionedRegistry< V> create(I input, Supplier>> registryLoader) { + return new VersionedRegistry<>(input, registryLoader.get()); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java similarity index 57% rename from connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java rename to connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java index 6cfb97675..4f6354af5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/AnnotatedRegistryLoader.java @@ -23,48 +23,37 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.sound; +package org.geysermc.connector.registry.loader; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; import org.reflections.Reflections; -import java.util.HashMap; +import java.lang.annotation.Annotation; import java.util.Map; +import java.util.function.Function; -/** - * Registry that holds {@link SoundInteractionHandler}s. - */ -public class SoundHandlerRegistry { +public class AnnotatedRegistryLoader implements RegistryLoader> { + private final Class annotation; + private final Function mapper; - static final Map> INTERACTION_HANDLERS = new HashMap<>(); + public AnnotatedRegistryLoader(Class annotation, Function mapper) { + this.annotation = annotation; + this.mapper = mapper; + } - static { - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.sound") : new Reflections("org.geysermc.connector.network.translators.sound"); - for (Class clazz : ref.getTypesAnnotatedWith(SoundHandler.class)) { + @Override + public Map load(String input) { + Map entries = new Object2ObjectOpenHashMap<>(); + Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections(input) : new Reflections(input); + for (Class clazz : ref.getTypesAnnotatedWith(this.annotation)) { try { - SoundInteractionHandler interactionHandler = (SoundInteractionHandler) clazz.newInstance(); - SoundHandler annotation = clazz.getAnnotation(SoundHandler.class); - INTERACTION_HANDLERS.put(annotation, interactionHandler); + entries.put(this.mapper.apply(clazz.getAnnotation(this.annotation)), (V) clazz.newInstance()); } catch (InstantiationException | IllegalAccessException ex) { ex.printStackTrace(); } } + return entries; } - - private SoundHandlerRegistry() { - } - - public static void init() { - // no-op - } - - /** - * Returns a map of the interaction handlers - * - * @return a map of the interaction handlers - */ - public static Map> getInteractionHandlers() { - return INTERACTION_HANDLERS; - } -} +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/BlockEntityRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/BlockEntityRegistryLoader.java new file mode 100644 index 000000000..6824d611d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/BlockEntityRegistryLoader.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +import org.geysermc.connector.network.translators.world.block.entity.BlockEntity; +import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; + +public class BlockEntityRegistryLoader extends AnnotatedRegistryLoader { + public BlockEntityRegistryLoader() { + super(BlockEntity.class, BlockEntity::name); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java similarity index 78% rename from connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java rename to connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java index e5cf52bc8..a60dfdbca 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/CollisionRegistryLoader.java @@ -23,36 +23,41 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.collision; +package org.geysermc.connector.registry.loader; import com.fasterxml.jackson.databind.node.ArrayNode; import com.google.common.collect.BiMap; +import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.collision.BoundingBox; +import org.geysermc.connector.network.translators.collision.CollisionRemapper; import org.geysermc.connector.network.translators.collision.translators.BlockCollision; import org.geysermc.connector.network.translators.collision.translators.EmptyCollision; import org.geysermc.connector.network.translators.collision.translators.OtherCollision; import org.geysermc.connector.network.translators.collision.translators.SolidCollision; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.utils.FileUtils; import org.reflections.Reflections; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; -public class CollisionTranslator { - private static final Int2ObjectMap COLLISION_MAP = new Int2ObjectOpenHashMap<>(); +public class CollisionRegistryLoader extends MultiResourceRegistryLoader> { + + @Override + public Map load(Pair input) { + Int2ObjectMap collisions = new Int2ObjectOpenHashMap<>(); - public static void init() { List> collisionTypes = new ArrayList<>(); - Map, CollisionRemapper> annotationMap = new HashMap<>(); - - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.collision.translators") : new Reflections("org.geysermc.connector.network.translators.collision.translators"); + Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections(input.key()) : new Reflections(input.key()); for (Class clazz : ref.getTypesAnnotatedWith(CollisionRemapper.class)) { GeyserConnector.getInstance().getLogger().debug("Found annotated collision translator: " + clazz.getCanonicalName()); @@ -61,7 +66,7 @@ public class CollisionTranslator { } // Load collision mappings file - InputStream stream = FileUtils.getResource("mappings/collision.json"); + InputStream stream = FileUtils.getResource(input.value()); ArrayNode collisionList; try { @@ -70,28 +75,28 @@ public class CollisionTranslator { throw new AssertionError("Unable to load collision data", e); } - BiMap javaIdBlockMap = BlockTranslator.getJavaIdBlockMap(); + BiMap javaIdBlockMap = BlockRegistries.JAVA_IDENTIFIERS.get(); // Map of classes that don't change based on parameters that have already been created Map, BlockCollision> instantiatedCollision = new HashMap<>(); - for (Map.Entry entry : javaIdBlockMap.entrySet()) { BlockCollision newCollision = instantiateCollision(entry.getKey(), entry.getValue(), collisionTypes, annotationMap, instantiatedCollision, collisionList); if (newCollision != null) { instantiatedCollision.put(newCollision.getClass(), newCollision); } - COLLISION_MAP.put(entry.getValue().intValue(), newCollision); + collisions.put(entry.getValue().intValue(), newCollision); } + return collisions; } - private static BlockCollision instantiateCollision(String blockID, int numericBlockID, List> collisionTypes, Map, CollisionRemapper> annotationMap, Map, BlockCollision> instantiatedCollision, ArrayNode collisionList) { + private BlockCollision instantiateCollision(String blockID, int numericBlockID, List> collisionTypes, Map, CollisionRemapper> annotationMap, Map, BlockCollision> instantiatedCollision, ArrayNode collisionList) { String blockName = blockID.split("\\[")[0].replace("minecraft:", ""); String params = ""; if (blockID.contains("[")) { params = "[" + blockID.split("\\[")[1]; } - int collisionIndex = BlockTranslator.getBlockMapping(numericBlockID).getCollisionIndex(); + int collisionIndex = BlockRegistries.JAVA_BLOCKS.get(numericBlockID).getCollisionIndex(); for (Class type : collisionTypes) { CollisionRemapper annotation = annotationMap.get(type); @@ -163,25 +168,4 @@ public class CollisionTranslator { return collision; } - - // Note: these reuse classes, so don't try to store more than once instance or coordinates will get overwritten - - public static BlockCollision getCollision(int blockID, int x, int y, int z) { - BlockCollision collision = COLLISION_MAP.get(blockID); - if (collision != null) { - collision.setPosition(x, y, z); - } - return collision; - } - - - public static BlockCollision getCollisionAt(GeyserSession session, int x, int y, int z) { - try { - return getCollision(session.getConnector().getWorldManager().getBlockAt(session, x, y, z), x, y, z); - } catch (ArrayIndexOutOfBoundsException e) { - // Block out of world - return null; - } - } - } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/EffectRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/EffectRegistryLoader.java new file mode 100644 index 000000000..5e9e435f3 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/EffectRegistryLoader.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +import com.fasterxml.jackson.databind.JsonNode; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.utils.FileUtils; + +import java.io.InputStream; +import java.util.Map; +import java.util.WeakHashMap; + +public abstract class EffectRegistryLoader implements RegistryLoader { + private static final Map loadedFiles = new WeakHashMap<>(); + + public void loadFile(String input) { + if (!loadedFiles.containsKey(input)) { + InputStream effectsStream = FileUtils.getResource(input); + JsonNode effects; + try { + effects = GeyserConnector.JSON_MAPPER.readTree(effectsStream); + } catch (Exception e) { + throw new AssertionError("Unable to load registrations for " + input, e); + } + loadedFiles.put(input, effects); + } + } + + public JsonNode get(String input) { + return loadedFiles.get(input); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/MultiResourceRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/MultiResourceRegistryLoader.java new file mode 100644 index 000000000..a2bb23696 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/MultiResourceRegistryLoader.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +import it.unimi.dsi.fastutil.Pair; + +public abstract class MultiResourceRegistryLoader implements RegistryLoader, V> { +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/loader/NbtRegistryLoader.java similarity index 72% rename from connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java rename to connector/src/main/java/org/geysermc/connector/registry/loader/NbtRegistryLoader.java index 5bb029882..773ee1038 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/EntityIdentifierRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/NbtRegistryLoader.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators; +package org.geysermc.connector.registry.loader; import com.nukkitx.nbt.NBTInputStream; import com.nukkitx.nbt.NbtMap; @@ -32,28 +32,15 @@ import org.geysermc.connector.utils.FileUtils; import java.io.InputStream; -/** - * Registry for entity identifiers. - */ -public class EntityIdentifierRegistry { - - public static final NbtMap ENTITY_IDENTIFIERS; - - private EntityIdentifierRegistry() { - } - - public static void init() { - // no-op - } - - static { - /* Load entity identifiers */ - InputStream stream = FileUtils.getResource("bedrock/entity_identifiers.dat"); +public class NbtRegistryLoader implements RegistryLoader { + @Override + public NbtMap load(String input) { + InputStream stream = FileUtils.getResource(input); try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) { - ENTITY_IDENTIFIERS = (NbtMap) nbtInputStream.readTag(); + return (NbtMap) nbtInputStream.readTag(); } catch (Exception e) { - throw new AssertionError("Unable to get entities from entity identifiers", e); + throw new AssertionError("Failed to load registrations for " + input, e); } } -} +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/ParticleTypesRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/ParticleTypesRegistryLoader.java new file mode 100644 index 000000000..3c3188236 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/ParticleTypesRegistryLoader.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType; +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.connector.registry.type.ParticleMapping; + +import java.util.Iterator; +import java.util.Map; + +public class ParticleTypesRegistryLoader extends EffectRegistryLoader> { + + @Override + public Map load(String input) { + this.loadFile(input); + + Iterator> particlesIterator = this.get(input).fields(); + Map particles = new Object2ObjectOpenHashMap<>(); + try { + while (particlesIterator.hasNext()) { + Map.Entry entry = particlesIterator.next(); + JsonNode bedrockId = entry.getValue().get("bedrockId"); + JsonNode bedrockIdNumeric = entry.getValue().get("bedrockNumericId"); + JsonNode eventType = entry.getValue().get("eventType"); + particles.put(ParticleType.valueOf(entry.getKey().toUpperCase()), new ParticleMapping( + eventType == null ? null : LevelEventType.valueOf(eventType.asText().toUpperCase()), + bedrockId == null ? null : bedrockId.asText(), + bedrockIdNumeric == null ? -1 : bedrockIdNumeric.asInt()) + ); + } + } catch (Exception e) { + e.printStackTrace(); + } + return particles; + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/loader/PotionMixRegistryLoader.java similarity index 70% rename from connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java rename to connector/src/main/java/org/geysermc/connector/registry/loader/PotionMixRegistryLoader.java index 30be7da23..7c43a21fd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/PotionMixRegistryLoader.java @@ -23,14 +23,22 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.item; +package org.geysermc.connector.registry.loader; import com.nukkitx.protocol.bedrock.data.inventory.PotionMixData; +import org.geysermc.connector.network.BedrockProtocol; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.network.translators.item.Potion; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +//TODO this needs to be versioned, but the runtime item states between 1.17 and 1.17.10 are identical except for new blocks so this works for both /** - * Generates a {@link Collection} of {@link PotionMixData} that enables the + * Generates a collection of {@link PotionMixData} that enables the * Bedrock client to place brewing items into the brewing stand. * (Does not contain actual potion mixes.) * @@ -38,18 +46,11 @@ import java.util.*; * (Ex: Bedrock cannot normally place glass bottles or fully upgraded * potions into the brewing stand, but Java can.) */ -public class PotionMixRegistry { - public static final Collection POTION_MIXES; +public class PotionMixRegistryLoader implements RegistryLoader> { - private PotionMixRegistry() { - } - - public static void init() { - // no-op - } - - static { - List ingredients = new ArrayList<>(); + @Override + public Set load(Object input) { + List ingredients = new ArrayList<>(); ingredients.add(getNonNull("minecraft:nether_wart")); ingredients.add(getNonNull("minecraft:redstone")); ingredients.add(getNonNull("minecraft:glowstone_dust")); @@ -68,21 +69,21 @@ public class PotionMixRegistry { ingredients.add(getNonNull("minecraft:turtle_helmet")); ingredients.add(getNonNull("minecraft:phantom_membrane")); - List inputs = new ArrayList<>(); + List inputs = new ArrayList<>(); inputs.add(getNonNull("minecraft:potion")); inputs.add(getNonNull("minecraft:splash_potion")); inputs.add(getNonNull("minecraft:lingering_potion")); - ItemEntry glassBottle = getNonNull("minecraft:glass_bottle"); + ItemMapping glassBottle = getNonNull("minecraft:glass_bottle"); Set potionMixes = new HashSet<>(); // Add all types of potions as inputs - ItemEntry fillerIngredient = ingredients.get(0); - for (ItemEntry input : inputs) { - for (Potion potion : Potion.VALUES) { + ItemMapping fillerIngredient = ingredients.get(0); + for (ItemMapping entryInput : inputs) { + for (Potion potion : Potion.values()) { potionMixes.add(new PotionMixData( - input.getBedrockId(), potion.getBedrockId(), + entryInput.getBedrockId(), potion.getBedrockId(), fillerIngredient.getBedrockId(), fillerIngredient.getBedrockData(), glassBottle.getBedrockId(), glassBottle.getBedrockData()) ); @@ -91,22 +92,21 @@ public class PotionMixRegistry { // Add all brewing ingredients // Also adds glass bottle as input - for (ItemEntry ingredient : ingredients) { + for (ItemMapping ingredient : ingredients) { potionMixes.add(new PotionMixData( glassBottle.getBedrockId(), glassBottle.getBedrockData(), ingredient.getBedrockId(), ingredient.getBedrockData(), glassBottle.getBedrockId(), glassBottle.getBedrockData()) ); } - - POTION_MIXES = potionMixes; + return potionMixes; } - private static ItemEntry getNonNull(String javaIdentifier) { - ItemEntry itemEntry = ItemRegistry.getItemEntry(javaIdentifier); - if (itemEntry == null) + private static ItemMapping getNonNull(String javaIdentifier) { + ItemMapping itemMapping = Registries.ITEMS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getMapping(javaIdentifier); + if (itemMapping == null) throw new NullPointerException("No item entry exists for java identifier: " + javaIdentifier); - return itemEntry; + return itemMapping; } -} +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoader.java new file mode 100644 index 000000000..495895789 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoader.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +/** + * Represents a registry loader. + * + * @param the input to load the registry from + * @param the output of the registry + */ +@FunctionalInterface +public interface RegistryLoader { + O load(I input); +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_17_0.java b/connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoaders.java similarity index 73% rename from connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_17_0.java rename to connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoaders.java index d86d0e71f..9a9634157 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator1_17_0.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/RegistryLoaders.java @@ -23,21 +23,14 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.world.block; +package org.geysermc.connector.registry.loader; -public class BlockTranslator1_17_0 extends BlockTranslator { - public static final BlockTranslator1_17_0 INSTANCE = new BlockTranslator1_17_0(); +import java.util.function.Supplier; - public BlockTranslator1_17_0() { - super("bedrock/block_palette.1_17_0.nbt"); +public class RegistryLoaders { + public static NbtRegistryLoader NBT = new NbtRegistryLoader(); + + public static RegistryLoader empty(Supplier value) { + return input -> value.get(); } - - @Override - public int getBlockStateVersion() { - return 17879555; - } - - public static void init() { - // no-op - } -} +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/SoundEffectsRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/SoundEffectsRegistryLoader.java new file mode 100644 index 000000000..8103ece8b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/SoundEffectsRegistryLoader.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.steveice10.mc.protocol.data.game.world.effect.SoundEffect; +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import com.nukkitx.protocol.bedrock.data.SoundEvent; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.effect.Effect; +import org.geysermc.connector.network.translators.effect.PlaySoundEffect; +import org.geysermc.connector.network.translators.effect.SoundEventEffect; +import org.geysermc.connector.network.translators.effect.SoundLevelEffect; + +import java.util.Iterator; +import java.util.Map; + +public class SoundEffectsRegistryLoader extends EffectRegistryLoader> { + + @Override + public Map load(String input) { + this.loadFile(input); + + Iterator> effectsIterator = this.get(input).fields(); + Map soundEffects = new Object2ObjectOpenHashMap<>(); + while (effectsIterator.hasNext()) { + Map.Entry entry = effectsIterator.next(); + JsonNode node = entry.getValue(); + try { + String type = node.get("type").asText(); + SoundEffect javaEffect = null; + Effect effect = null; + switch (type) { + case "soundLevel": { + javaEffect = SoundEffect.valueOf(entry.getKey()); + LevelEventType levelEventType = LevelEventType.valueOf(node.get("name").asText()); + int data = node.has("data") ? node.get("data").intValue() : 0; + effect = new SoundLevelEffect(levelEventType, data); + break; + } + case "soundEvent": { + javaEffect = SoundEffect.valueOf(entry.getKey()); + SoundEvent soundEvent = SoundEvent.valueOf(node.get("name").asText()); + String identifier = node.has("identifier") ? node.get("identifier").asText() : ""; + int extraData = node.has("extraData") ? node.get("extraData").intValue() : -1; + effect = new SoundEventEffect(soundEvent, identifier, extraData); + break; + } + case "playSound": { + javaEffect = SoundEffect.valueOf(entry.getKey()); + String name = node.get("name").asText(); + float volume = node.has("volume") ? node.get("volume").floatValue() : 1.0f; + boolean pitchSub = node.has("pitch_sub") && node.get("pitch_sub").booleanValue(); + float pitchMul = node.has("pitch_mul") ? node.get("pitch_mul").floatValue() : 1.0f; + float pitchAdd = node.has("pitch_add") ? node.get("pitch_add").floatValue() : 0.0f; + boolean relative = !node.has("relative") || node.get("relative").booleanValue(); + effect = new PlaySoundEffect(name, volume, pitchSub, pitchMul, pitchAdd, relative); + break; + } + } + if (javaEffect != null) { + soundEffects.put(javaEffect, effect); + } + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().warning("Failed to map sound effect " + entry.getKey() + " : " + e.toString()); + } + } + return soundEffects; + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/loader/SoundHandlerRegistryLoader.java b/connector/src/main/java/org/geysermc/connector/registry/loader/SoundHandlerRegistryLoader.java new file mode 100644 index 000000000..42c10c2ce --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/SoundHandlerRegistryLoader.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.loader; + +import org.geysermc.connector.network.translators.sound.SoundHandler; +import org.geysermc.connector.network.translators.sound.SoundInteractionHandler; + +import java.util.function.Function; + +public class SoundHandlerRegistryLoader extends AnnotatedRegistryLoader> { + public SoundHandlerRegistryLoader() { + super(SoundHandler.class, Function.identity()); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/loader/SoundRegistryLoader.java similarity index 59% rename from connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundRegistry.java rename to connector/src/main/java/org/geysermc/connector/registry/loader/SoundRegistryLoader.java index 90599eb19..4dc1dab58 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/loader/SoundRegistryLoader.java @@ -23,33 +23,23 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.sound; +package org.geysermc.connector.registry.loader; import com.fasterxml.jackson.databind.JsonNode; -import com.nukkitx.protocol.bedrock.data.SoundEvent; -import lombok.Data; -import lombok.ToString; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.registry.type.SoundMapping; import org.geysermc.connector.utils.FileUtils; + import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -public class SoundRegistry { +public class SoundRegistryLoader implements RegistryLoader> { - private static final Map SOUNDS; - - private SoundRegistry() { - } - - public static void init() { - // no-op - } - - static { - /* Load sound mappings */ + @Override + public Map load(String input) { InputStream stream = FileUtils.getResource("mappings/sounds.json"); JsonNode soundsTree; try { @@ -63,7 +53,6 @@ public class SoundRegistry { while(soundsIterator.hasNext()) { Map.Entry next = soundsIterator.next(); JsonNode brMap = next.getValue(); - soundMappings.put(next.getKey(), new SoundMapping( next.getKey(), brMap.has("bedrock_mapping") && brMap.get("bedrock_mapping").isTextual() ? brMap.get("bedrock_mapping").asText() : null, @@ -74,50 +63,6 @@ public class SoundRegistry { ) ); } - SOUNDS = soundMappings; + return soundMappings; } - - /** - * Get's the sound mapping for a Java edition sound identifier - * @param java Java edition sound identifier - * @return SoundMapping object with information for bedrock, nukkit, java, etc. null if not found - */ - public static SoundMapping fromJava(String java) { - return SOUNDS.get(java); - } - - /** - * Maps a sound name to a sound event, null if one - * does not exist. - * - * @param sound the sound name - * @return a sound event from the given sound - */ - public static SoundEvent toSoundEvent(String sound) { - try { - return SoundEvent.valueOf(sound.toUpperCase().replaceAll("\\.", "_")); - } catch (Exception ex) { - return null; - } - } - - @Data - @ToString - public static class SoundMapping { - private final String java; - private final String bedrock; - private final String playsound; - private final int extraData; - private String identifier; - private boolean levelEvent; - - public SoundMapping(String java, String bedrock, String playsound, int extraData, String identifier, boolean levelEvent) { - this.java = java; - this.bedrock = bedrock == null || bedrock.equalsIgnoreCase("") ? null : bedrock; - this.playsound = playsound == null || playsound.equalsIgnoreCase("") ? null : playsound; - this.extraData = extraData; - this.identifier = identifier == null || identifier.equalsIgnoreCase("") ? ":" : identifier; - this.levelEvent = levelEvent; - } - } -} +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/populator/BlockRegistryPopulator.java b/connector/src/main/java/org/geysermc/connector/registry/populator/BlockRegistryPopulator.java new file mode 100644 index 000000000..7903e820b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/populator/BlockRegistryPopulator.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.populator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableMap; +import com.nukkitx.nbt.*; +import com.nukkitx.protocol.bedrock.v440.Bedrock_v440; +import com.nukkitx.protocol.bedrock.v448.Bedrock_v448; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.network.translators.world.chunk.BlockStorage; +import org.geysermc.connector.network.translators.world.chunk.ChunkSection; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.type.BlockMapping; +import org.geysermc.connector.registry.type.BlockMappings; +import org.geysermc.connector.utils.FileUtils; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.zip.GZIPInputStream; + +public class BlockRegistryPopulator { + private static final ImmutableMap> STATE_MAPPER = ImmutableMap.>builder() + .put("1_17_0", (bedrockIdentifier, statesBuilder) -> { + if (bedrockIdentifier.contains("candle")) { + // Replace candles with sea pickles or cake + if (bedrockIdentifier.contains("cake")) { + statesBuilder.remove("lit"); + statesBuilder.putInt("bite_counter", 0); + return "minecraft:cake"; + } else { + statesBuilder.put("cluster_count", statesBuilder.remove("candles")); + statesBuilder.putBoolean("dead_bit", ((byte) (statesBuilder.remove("lit"))) != 0); + return "minecraft:sea_pickle"; + } + } + return null; + }) + .put("1_17_10", (bedrockIdentifier, statesBuilder) -> null) + .build(); + + private static final Object2IntMap PALETTE_VERSIONS = new Object2IntOpenHashMap() { + { + put("1_17_0", Bedrock_v440.V440_CODEC.getProtocolVersion()); + put("1_17_10", Bedrock_v448.V448_CODEC.getProtocolVersion()); + } + }; + + /** + * Stores the raw blocks JSON until it is no longer needed. + */ + private static JsonNode BLOCKS_JSON; + + public static void populate() { + registerJavaBlocks(); + registerBedrockBlocks(); + + BLOCKS_JSON = null; + } + + private static void registerBedrockBlocks() { + for (Map.Entry> palette : STATE_MAPPER.entrySet()) { + InputStream stream = FileUtils.getResource(String.format("bedrock/block_palette.%s.nbt", palette.getKey())); + NbtList blocksTag; + try (NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)))) { + NbtMap blockPalette = (NbtMap) nbtInputStream.readTag(); + blocksTag = (NbtList) blockPalette.getList("blocks", NbtType.COMPOUND); + } catch (Exception e) { + throw new AssertionError("Unable to get blocks from runtime block states", e); + } + Map javaIdentifierToBedrockTag = new Object2ObjectOpenHashMap<>(blocksTag.size()); + // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette, + // as we no longer send a block palette + Object2IntMap blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size()); + + int stateVersion = -1; + for (int i = 0; i < blocksTag.size(); i++) { + NbtMap tag = blocksTag.get(i); + if (blockStateOrderedMap.containsKey(tag)) { + throw new AssertionError("Duplicate block states in Bedrock palette: " + tag); + } + blockStateOrderedMap.put(tag, i); + if (stateVersion == -1) { + stateVersion = tag.getInt("version"); + } + } + int airRuntimeId = -1; + int commandBlockRuntimeId = -1; + int javaRuntimeId = -1; + int waterRuntimeId = -1; + Iterator> blocksIterator = BLOCKS_JSON.fields(); + + BiFunction stateMapper = STATE_MAPPER.getOrDefault(palette.getKey(), (i, s) -> null); + + Int2IntMap javaToBedrockBlockMap = new Int2IntOpenHashMap(); + Int2IntMap bedrockToJavaBlockMap = new Int2IntOpenHashMap(); + + Map flowerPotBlocks = new Object2ObjectOpenHashMap<>(); + Object2IntMap itemFrames = new Object2IntOpenHashMap<>(); + + BlockMappings.BlockMappingsBuilder builder = BlockMappings.builder(); + while (blocksIterator.hasNext()) { + javaRuntimeId++; + Map.Entry entry = blocksIterator.next(); + String javaId = entry.getKey(); + + NbtMap blockTag = buildBedrockState(entry.getValue(), stateVersion, stateMapper); + int bedrockRuntimeId = blockStateOrderedMap.getOrDefault(blockTag, -1); + if (bedrockRuntimeId == -1) { + throw new RuntimeException("Unable to find " + javaId + " Bedrock runtime ID! Built compound tag: \n" + blockTag); + } + + switch (javaId) { + case "minecraft:air": + airRuntimeId = bedrockRuntimeId; + break; + case "minecraft:water[level=0]": + waterRuntimeId = bedrockRuntimeId; + break; + case "minecraft:command_block[conditional=false,facing=north]": + commandBlockRuntimeId = bedrockRuntimeId; + break; + } + + boolean waterlogged = entry.getKey().contains("waterlogged=true") + || javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass"); + + if (waterlogged) { + bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId | 1 << 31, javaRuntimeId); + int finalJavaRuntimeId = javaRuntimeId; + BlockRegistries.WATERLOGGED.register(set -> set.add(finalJavaRuntimeId)); + } else { + bedrockToJavaBlockMap.putIfAbsent(bedrockRuntimeId, javaRuntimeId); + } + + String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; + + // Get the tag needed for non-empty flower pots + if (entry.getValue().get("pottable") != null) { + flowerPotBlocks.put(cleanJavaIdentifier, blockTag); + } + + if (!cleanJavaIdentifier.equals(entry.getValue().get("bedrock_identifier").asText())) { + javaIdentifierToBedrockTag.put(cleanJavaIdentifier, blockTag); + } + + javaToBedrockBlockMap.put(javaRuntimeId, bedrockRuntimeId); + } + + if (commandBlockRuntimeId == -1) { + throw new AssertionError("Unable to find command block in palette"); + } + builder.commandBlockRuntimeId(commandBlockRuntimeId); + + if (waterRuntimeId == -1) { + throw new AssertionError("Unable to find water in palette"); + } + builder.bedrockWaterId(waterRuntimeId); + + if (airRuntimeId == -1) { + throw new AssertionError("Unable to find air in palette"); + } + builder.bedrockAirId(airRuntimeId); + + // Loop around again to find all item frame runtime IDs + for (Object2IntMap.Entry entry : blockStateOrderedMap.object2IntEntrySet()) { + String name = entry.getKey().getString("name"); + if (name.equals("minecraft:frame") || name.equals("minecraft:glow_frame")) { + itemFrames.put(entry.getKey(), entry.getIntValue()); + } + } + builder.bedrockBlockStates(blocksTag); + + BlockRegistries.BLOCKS.register(PALETTE_VERSIONS.getInt(palette.getKey()), builder.blockStateVersion(stateVersion) + .emptyChunkSection(new ChunkSection(new BlockStorage[]{new BlockStorage(airRuntimeId)})) + .javaToBedrockBlockMap(javaToBedrockBlockMap) + .bedrockToJavaBlockMap(bedrockToJavaBlockMap) + .javaIdentifierToBedrockTag(javaIdentifierToBedrockTag) + .itemFrames(itemFrames) + .flowerPotBlocks(flowerPotBlocks) + .build()); + } + } + + private static void registerJavaBlocks() { + InputStream stream = FileUtils.getResource("mappings/blocks.json"); + + JsonNode blocksJson; + try { + blocksJson = GeyserConnector.JSON_MAPPER.readTree(stream); + } catch (Exception e) { + throw new AssertionError("Unable to load Java block mappings", e); + } + + int javaRuntimeId = -1; + int bellBlockId = -1; + int cobwebBlockId = -1; + int furnaceRuntimeId = -1; + int furnaceLitRuntimeId = -1; + int spawnerRuntimeId = -1; + int uniqueJavaId = -1; + int waterRuntimeId = -1; + Iterator> blocksIterator = blocksJson.fields(); + while (blocksIterator.hasNext()) { + javaRuntimeId++; + Map.Entry entry = blocksIterator.next(); + String javaId = entry.getKey(); + + // TODO fix this, (no block should have a null hardness) + BlockMapping.BlockMappingBuilder builder = BlockMapping.builder(); + JsonNode hardnessNode = entry.getValue().get("block_hardness"); + if (hardnessNode != null) { + builder.hardness(hardnessNode.doubleValue()); + } + + JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand"); + if (canBreakWithHandNode != null) { + builder.canBreakWithHand(canBreakWithHandNode.booleanValue()); + } else { + builder.canBreakWithHand(false); + } + + JsonNode collisionIndexNode = entry.getValue().get("collision_index"); + if (hardnessNode != null) { + builder.collisionIndex(collisionIndexNode.intValue()); + } + + JsonNode pickItemNode = entry.getValue().get("pick_item"); + if (pickItemNode != null) { + builder.pickItem(pickItemNode.textValue()); + } + + builder.javaIdentifier(javaId); + builder.javaBlockId(uniqueJavaId); + + BlockRegistries.JAVA_IDENTIFIERS.register(javaId, javaRuntimeId); + BlockRegistries.JAVA_BLOCKS.register(javaRuntimeId, builder.build()); + + BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue()); + + String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; + String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); + + if (!BlockRegistries.JAVA_CLEAN_IDENTIFIERS.get().containsValue(cleanJavaIdentifier)) { + uniqueJavaId++; + BlockRegistries.JAVA_CLEAN_IDENTIFIERS.register(uniqueJavaId, cleanJavaIdentifier); + } + + // Keeping this here since this is currently unchanged between versions + if (!cleanJavaIdentifier.equals(bedrockIdentifier)) { + BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier, bedrockIdentifier); + } + + if (javaId.startsWith("minecraft:bell[")) { + bellBlockId = uniqueJavaId; + + } else if (javaId.contains("cobweb")) { + cobwebBlockId = uniqueJavaId; + + } else if (javaId.startsWith("minecraft:furnace[facing=north")) { + if (javaId.contains("lit=true")) { + furnaceLitRuntimeId = javaRuntimeId; + } else { + furnaceRuntimeId = javaRuntimeId; + } + + } else if (javaId.startsWith("minecraft:spawner")) { + spawnerRuntimeId = javaRuntimeId; + + } else if ("minecraft:water[level=0]".equals(javaId)) { + waterRuntimeId = javaRuntimeId; + } + } + if (bellBlockId == -1) { + throw new AssertionError("Unable to find bell in palette"); + } + BlockStateValues.JAVA_BELL_ID = bellBlockId; + + if (cobwebBlockId == -1) { + throw new AssertionError("Unable to find cobwebs in palette"); + } + BlockStateValues.JAVA_COBWEB_ID = cobwebBlockId; + + if (furnaceRuntimeId == -1) { + throw new AssertionError("Unable to find furnace in palette"); + } + BlockStateValues.JAVA_FURNACE_ID = furnaceRuntimeId; + + if (furnaceLitRuntimeId == -1) { + throw new AssertionError("Unable to find lit furnace in palette"); + } + BlockStateValues.JAVA_FURNACE_LIT_ID = furnaceLitRuntimeId; + + if (spawnerRuntimeId == -1) { + throw new AssertionError("Unable to find spawner in palette"); + } + BlockStateValues.JAVA_SPAWNER_ID = spawnerRuntimeId; + + if (waterRuntimeId == -1) { + throw new AssertionError("Unable to find Java water in palette"); + } + BlockStateValues.JAVA_WATER_ID = waterRuntimeId; + + BLOCKS_JSON = blocksJson; + } + + private static NbtMap buildBedrockState(JsonNode node, int blockStateVersion, BiFunction statesMapper) { + NbtMapBuilder tagBuilder = NbtMap.builder(); + String bedrockIdentifier = node.get("bedrock_identifier").textValue(); + tagBuilder.putString("name", bedrockIdentifier) + .putInt("version", blockStateVersion); + + NbtMapBuilder statesBuilder = NbtMap.builder(); + + // check for states + if (node.has("bedrock_states")) { + Iterator> statesIterator = node.get("bedrock_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()); + continue; + case STRING: + statesBuilder.putString(stateEntry.getKey(), stateValue.textValue()); + continue; + case NUMBER: + statesBuilder.putInt(stateEntry.getKey(), stateValue.intValue()); + } + } + } + String newIdentifier = statesMapper.apply(bedrockIdentifier, statesBuilder); + if (newIdentifier != null) { + tagBuilder.putString("name", newIdentifier); + } + tagBuilder.put("states", statesBuilder.build()); + return tagBuilder.build(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java b/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java new file mode 100644 index 000000000..222521241 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/populator/ItemRegistryPopulator.java @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.populator; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; +import com.nukkitx.nbt.NbtUtils; +import com.nukkitx.protocol.bedrock.data.SoundEvent; +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import com.nukkitx.protocol.bedrock.v440.Bedrock_v440; +import com.nukkitx.protocol.bedrock.v448.Bedrock_v448; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.item.StoredItemMappings; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.*; +import org.geysermc.connector.utils.FileUtils; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +public class ItemRegistryPopulator { + private static final Map PALETTE_VERSIONS = new Object2ObjectOpenHashMap(){ + { + put("1_17_0", new PaletteVersion(Bedrock_v440.V440_CODEC.getProtocolVersion(), new Object2ObjectOpenHashMap() { + { + put("minecraft:candle", "minecraft:sea_pickle"); + put("minecraft:white_candle", "minecraft:sea_pickle"); + put("minecraft:orange_candle", "minecraft:sea_pickle"); + put("minecraft:magenta_candle", "minecraft:sea_pickle"); + put("minecraft:light_blue_candle", "minecraft:sea_pickle"); + put("minecraft:yellow_candle", "minecraft:sea_pickle"); + put("minecraft:lime_candle", "minecraft:sea_pickle"); + put("minecraft:pink_candle", "minecraft:sea_pickle"); + put("minecraft:gray_candle", "minecraft:sea_pickle"); + put("minecraft:light_gray_candle", "minecraft:sea_pickle"); + put("minecraft:cyan_candle", "minecraft:sea_pickle"); + put("minecraft:purple_candle", "minecraft:sea_pickle"); + put("minecraft:blue_candle", "minecraft:sea_pickle"); + put("minecraft:brown_candle", "minecraft:sea_pickle"); + put("minecraft:green_candle", "minecraft:sea_pickle"); + put("minecraft:red_candle", "minecraft:sea_pickle"); + put("minecraft:black_candle", "minecraft:sea_pickle"); + } + })); + put("1_17_10", new PaletteVersion(Bedrock_v448.V448_CODEC.getProtocolVersion(), Collections.emptyMap())); + } + }; + + @Getter + @AllArgsConstructor + private static class PaletteVersion { + private final int protocolVersion; + /** + * Key - item not available in this version. Value - Java replacement item + */ + private final Map additionalTranslatedItems; + } + + public static void populate() { + // Load item mappings from Java Edition to Bedrock Edition + InputStream stream = FileUtils.getResource("mappings/items.json"); + + TypeReference> mappingItemsType = new TypeReference>() { }; + + Map items; + try { + items = GeyserConnector.JSON_MAPPER.readValue(stream, mappingItemsType); + } catch (Exception e) { + throw new AssertionError("Unable to load Java runtime item IDs", e); + } + + /* Load item palette */ + for (Map.Entry palette : PALETTE_VERSIONS.entrySet()) { + stream = FileUtils.getResource(String.format("bedrock/runtime_item_states.%s.json", palette.getKey())); + + TypeReference> paletteEntriesType = new TypeReference>() { }; + + // Used to get the Bedrock namespaced ID (in instances where there are small differences) + Object2IntMap bedrockIdentifierToId = new Object2IntOpenHashMap<>(); + + List itemNames = new ArrayList<>(); + + List itemEntries; + try { + itemEntries = GeyserConnector.JSON_MAPPER.readValue(stream, paletteEntriesType); + } catch (Exception e) { + throw new AssertionError("Unable to load Bedrock runtime item IDs", e); + } + + Map entries = new Object2ObjectOpenHashMap<>(); + + for (PaletteItem entry : itemEntries) { + entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) entry.getId())); + bedrockIdentifierToId.put(entry.getName(), entry.getId()); + } + + Object2IntMap bedrockBlockIdOverrides = new Object2IntOpenHashMap<>(); + Object2IntMap blacklistedIdentifiers = new Object2IntOpenHashMap<>(); + + // Load creative items + // We load this before item mappings to get overridden block runtime ID mappings + stream = FileUtils.getResource(String.format("bedrock/creative_items.%s.json", palette.getKey())); + + JsonNode creativeItemEntries; + try { + creativeItemEntries = GeyserConnector.JSON_MAPPER.readTree(stream).get("items"); + } catch (Exception e) { + throw new AssertionError("Unable to load creative items", e); + } + + IntList boats = new IntArrayList(); + IntList buckets = new IntArrayList(); + IntList spawnEggs = new IntArrayList(); + List carpets = new ObjectArrayList<>(); + + Int2ObjectMap mappings = new Int2ObjectOpenHashMap<>(); + // Temporary mapping to create stored items + Map identifierToMapping = new Object2ObjectOpenHashMap<>(); + + int netId = 1; + List creativeItems = new ArrayList<>(); + for (JsonNode itemNode : creativeItemEntries) { + int count = 1; + int damage = 0; + int blockRuntimeId = 0; + NbtMap tag = null; + JsonNode damageNode = itemNode.get("damage"); + if (damageNode != null) { + damage = damageNode.asInt(); + } + JsonNode countNode = itemNode.get("count"); + if (countNode != null) { + count = countNode.asInt(); + } + JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId"); + if (blockRuntimeIdNode != null) { + blockRuntimeId = blockRuntimeIdNode.asInt(); + } + JsonNode nbtNode = itemNode.get("nbt_b64"); + if (nbtNode != null) { + byte[] bytes = Base64.getDecoder().decode(nbtNode.asText()); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + try { + tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + String identifier = itemNode.get("id").textValue(); + StartGamePacket.ItemEntry entry = entries.get(identifier); + int id = -1; + if (entry != null) { + id = entry.getId(); + } + + if (id == -1) { + throw new RuntimeException("Unable to find matching Bedrock item for " + identifier); + } + + creativeItems.add(ItemData.builder() + .id(id) + .damage(damage) + .count(count) + .blockRuntimeId(blockRuntimeId) + .tag(tag) + .netId(netId++) + .build()); + + if (blockRuntimeId != 0) { + // Add override for item mapping, unless it already exists... then we know multiple states can exist + if (!blacklistedIdentifiers.containsKey(identifier)) { + if (bedrockBlockIdOverrides.containsKey(identifier)) { + bedrockBlockIdOverrides.removeInt(identifier); + // Save this as a blacklist, but also as knowledge of what the block state name should be + blacklistedIdentifiers.put(identifier, blockRuntimeId); + } else { + // Unless there's multiple possibilities for this one state, let this be + bedrockBlockIdOverrides.put(identifier, blockRuntimeId); + } + } + } + } + + BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.getValue().getProtocolVersion()); + + int itemIndex = 0; + int javaFurnaceMinecartId = 0; + boolean usingFurnaceMinecart = GeyserConnector.getInstance().getConfig().isAddNonBedrockItems(); + + Set javaOnlyItems = new ObjectOpenHashSet<>(); + Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick", + "minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg", + "minecraft:bundle", "minecraft:sculk_sensor"); + if (!usingFurnaceMinecart) { + javaOnlyItems.add("minecraft:furnace_minecart"); + } + // Java-only items for this version + javaOnlyItems.addAll(palette.getValue().getAdditionalTranslatedItems().keySet()); + + for (Map.Entry entry : items.entrySet()) { + GeyserMappingItem mappingItem; + String replacementItem = palette.getValue().getAdditionalTranslatedItems().get(entry.getKey()); + if (replacementItem != null) { + mappingItem = items.get(replacementItem); + } else { + // This items has a mapping specifically for this version of the game + mappingItem = entry.getValue(); + } + + if (usingFurnaceMinecart && entry.getKey().equals("minecraft:furnace_minecart")) { + javaFurnaceMinecartId = itemIndex; + itemIndex++; + continue; + } + String bedrockIdentifier = mappingItem.getBedrockIdentifier(); + int bedrockId = bedrockIdentifierToId.getInt(bedrockIdentifier); + if (bedrockIdentifier == null) { + throw new RuntimeException("Missing Bedrock ID in mappings!: " + bedrockId); + } + int stackSize = mappingItem.getStackSize() == null ? 64 : mappingItem.getStackSize(); + + int bedrockBlockId = -1; + Integer blockRuntimeIdNode = entry.getValue().getBlockRuntimeId(); + if (blockRuntimeIdNode != null) { + int blockIdOverride = bedrockBlockIdOverrides.getOrDefault(bedrockIdentifier, -1); + if (blockIdOverride != -1) { + // Straight from BDS is our best chance of getting an item that doesn't run into issues + bedrockBlockId = blockIdOverride; + } else { + // Try to get an example block runtime ID from the creative contents packet, for Bedrock identifier obtaining + int aValidBedrockBlockId = blacklistedIdentifiers.getOrDefault(bedrockIdentifier, -1); + if (aValidBedrockBlockId == -1) { + // Fallback + bedrockBlockId = blockMappings.getBedrockBlockId(blockRuntimeIdNode); + } else { + // As of 1.16.220, every item requires a block runtime ID attached to it. + // This is mostly for identifying different blocks with the same item ID - wool, slabs, some walls. + // However, in order for some visuals and crafting to work, we need to send the first matching block state + // as indexed by Bedrock's block palette + // There are exceptions! But, ideally, the block ID override should take care of those. + String javaBlockIdentifier = BlockRegistries.JAVA_BLOCKS.get(blockRuntimeIdNode).getCleanJavaIdentifier(); + NbtMapBuilder requiredBlockStatesBuilder = NbtMap.builder(); + String correctBedrockIdentifier = blockMappings.getBedrockBlockStates().get(aValidBedrockBlockId).getString("name"); + boolean firstPass = true; + for (Map.Entry blockEntry : BlockRegistries.JAVA_IDENTIFIERS.get().entrySet()) { + if (blockEntry.getKey().split("\\[")[0].equals(javaBlockIdentifier)) { + int bedrockBlockRuntimeId = blockMappings.getBedrockBlockId(blockEntry.getValue()); + NbtMap blockTag = blockMappings.getBedrockBlockStates().get(bedrockBlockRuntimeId); + String bedrockName = blockTag.getString("name"); + if (!bedrockName.equals(correctBedrockIdentifier)) { + continue; + } + NbtMap states = blockTag.getCompound("states"); + + if (firstPass) { + firstPass = false; + if (states.size() == 0) { + // No need to iterate and find all block states - this is the one, as there can't be any others + bedrockBlockId = bedrockBlockRuntimeId; + break; + } + requiredBlockStatesBuilder.putAll(states); + continue; + } + for (Map.Entry nbtEntry : states.entrySet()) { + Object value = requiredBlockStatesBuilder.get(nbtEntry.getKey()); + if (value != null && !nbtEntry.getValue().equals(value)) { // Null means this value has already been removed/deemed as unneeded + // This state can change between different block states, and therefore is not required + // to build a successful block state of this + requiredBlockStatesBuilder.remove(nbtEntry.getKey()); + } + } + if (requiredBlockStatesBuilder.size() == 0) { + // There are no required block states + // E.G. there was only a direction property that is no longer in play + // (States that are important include color for glass) + break; + } + } + } + + NbtMap requiredBlockStates = requiredBlockStatesBuilder.build(); + if (bedrockBlockId == -1) { + int i = -1; + // We need to loop around again (we can't cache the block tags above) because Bedrock can include states that we don't have a pairing for + // in it's "preferred" block state - I.E. the first matching block state in the list + for (NbtMap blockTag : blockMappings.getBedrockBlockStates()) { + i++; + if (blockTag.getString("name").equals(correctBedrockIdentifier)) { + NbtMap states = blockTag.getCompound("states"); + boolean valid = true; + for (Map.Entry nbtEntry : requiredBlockStates.entrySet()) { + if (!states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) { + // A required block state doesn't match - this one is not valid + valid = false; + break; + } + } + if (valid) { + bedrockBlockId = i; + break; + } + } + } + if (bedrockBlockId == -1) { + throw new RuntimeException("Could not find a block match for " + entry.getKey()); + } + } + + // Because we have replaced the Bedrock block ID, we also need to replace the creative contents block runtime ID + // That way, creative items work correctly for these blocks + for (int j = 0; j < creativeItems.size(); j++) { + ItemData itemData = creativeItems.get(j); + if (itemData.getId() == bedrockId) { + if (itemData.getDamage() != 0) { + break; + } + NbtMap states = blockMappings.getBedrockBlockStates().get(itemData.getBlockRuntimeId()).getCompound("states"); + boolean valid = true; + for (Map.Entry nbtEntry : requiredBlockStates.entrySet()) { + if (!states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) { + // A required block state doesn't match - this one is not valid + valid = false; + break; + } + } + if (valid) { + creativeItems.set(j, itemData.toBuilder().blockRuntimeId(bedrockBlockId).build()); + break; + } + } + } + } + } + } + + ItemMapping.ItemMappingBuilder mappingBuilder = ItemMapping.builder() + .javaIdentifier(entry.getKey()) + .javaId(itemIndex) + .bedrockIdentifier(bedrockIdentifier) + .bedrockId(bedrockId) + .bedrockData(mappingItem.getBedrockData()) + .bedrockBlockId(bedrockBlockId) + .stackSize(stackSize); + + if (mappingItem.getToolType() != null) { + if (mappingItem.getToolTier() != null) { + mappingBuilder = mappingBuilder.toolType(mappingItem.getToolType()) + .toolTier(mappingItem.getToolTier()); + } else { + mappingBuilder = mappingBuilder.toolType(mappingItem.getToolType()) + .toolTier(""); + } + } + if (javaOnlyItems.contains(entry.getKey())) { + // These items don't exist on Bedrock, so set up a variable that indicates they should have custom names + mappingBuilder = mappingBuilder.translationString((bedrockBlockId != -1 ? "block." : "item.") + entry.getKey().replace(":", ".")); + GeyserConnector.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated."); + } + + ItemMapping mapping = mappingBuilder.build(); + + if (entry.getKey().contains("boat")) { + boats.add(bedrockId); + } else if (entry.getKey().contains("bucket") && !entry.getKey().contains("milk")) { + buckets.add(bedrockId); + } else if (entry.getKey().contains("_carpet") && !entry.getKey().contains("moss")) { + // This should be the numerical order Java sends as an integer value for llamas + carpets.add(ItemData.builder() + .id(mapping.getBedrockId()) + .damage(mapping.getBedrockData()) + .count(1) + .blockRuntimeId(mapping.getBedrockBlockId()) + .build()); + } else if (entry.getKey().startsWith("minecraft:music_disc_")) { + // The Java record level event uses the item ID as the "key" to play the record + Registries.RECORDS.register(itemIndex, SoundEvent.valueOf("RECORD_" + + entry.getKey().replace("minecraft:music_disc_", "").toUpperCase(Locale.ENGLISH))); + } else if (entry.getKey().endsWith("_spawn_egg")) { + spawnEggs.add(mapping.getBedrockId()); + } + + mappings.put(itemIndex, mapping); + identifierToMapping.put(entry.getKey(), mapping); + + itemNames.add(entry.getKey()); + + itemIndex++; + } + + itemNames.add("minecraft:furnace_minecart"); + + int lodestoneCompassId = entries.get("minecraft:lodestone_compass").getId(); + if (lodestoneCompassId == 0) { + throw new RuntimeException("Lodestone compass not found in item palette!"); + } + + // Add the lodestone compass since it doesn't exist on java but we need it for item conversion + ItemMapping lodestoneEntry = ItemMapping.builder() + .javaIdentifier("minecraft:lodestone_compass") + .bedrockIdentifier("minecraft:lodestone_compass") + .javaId(itemIndex) + .bedrockId(lodestoneCompassId) + .bedrockData(0) + .bedrockBlockId(-1) + .stackSize(1) + .build(); + mappings.put(itemIndex, lodestoneEntry); + identifierToMapping.put(lodestoneEntry.getJavaIdentifier(), lodestoneEntry); + + ComponentItemData furnaceMinecartData = null; + if (usingFurnaceMinecart) { + // Add the furnace minecart as a custom item + int furnaceMinecartId = mappings.size() + 1; + + entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true)); + + mappings.put(javaFurnaceMinecartId, ItemMapping.builder() + .javaIdentifier("geysermc:furnace_minecart") + .bedrockIdentifier("geysermc:furnace_minecart") + .javaId(javaFurnaceMinecartId) + .bedrockId(furnaceMinecartId) + .bedrockData(0) + .bedrockBlockId(-1) + .stackSize(1) + .build()); + + creativeItems.add(ItemData.builder() + .netId(netId) + .id(furnaceMinecartId) + .count(1).build()); + + NbtMapBuilder builder = NbtMap.builder(); + builder.putString("name", "geysermc:furnace_minecart") + .putInt("id", furnaceMinecartId); + + NbtMapBuilder componentBuilder = NbtMap.builder(); + // Conveniently, as of 1.16.200, the furnace minecart has a texture AND translation string already. + componentBuilder.putCompound("minecraft:icon", NbtMap.builder().putString("texture", "minecart_furnace").build()); + componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", "item.minecartFurnace.name").build()); + + // Indicate that the arm animation should play on rails + List useOnTag = Collections.singletonList(NbtMap.builder().putString("tags", "q.any_tag('rail')").build()); + componentBuilder.putCompound("minecraft:entity_placer", NbtMap.builder() + .putList("dispense_on", NbtType.COMPOUND, useOnTag) + .putString("entity", "minecraft:minecart") + .putList("use_on", NbtType.COMPOUND, useOnTag) + .build()); + + NbtMapBuilder itemProperties = NbtMap.builder(); + // We always want to allow offhand usage when we can - matches Java Edition + itemProperties.putBoolean("allow_off_hand", true); + itemProperties.putBoolean("hand_equipped", false); + itemProperties.putInt("max_stack_size", 1); + itemProperties.putString("creative_group", "itemGroup.name.minecart"); + itemProperties.putInt("creative_category", 4); // 4 - "Items" + + componentBuilder.putCompound("item_properties", itemProperties.build()); + builder.putCompound("components", componentBuilder.build()); + furnaceMinecartData = new ComponentItemData("geysermc:furnace_minecart", builder.build()); + } + + ItemMappings itemMappings = ItemMappings.builder() + .items(mappings) + .creativeItems(creativeItems.toArray(new ItemData[0])) + .itemEntries(new ArrayList<>(entries.values())) + .itemNames(itemNames.toArray(new String[0])) + .storedItems(new StoredItemMappings(identifierToMapping)) + .javaOnlyItems(javaOnlyItems) + .bucketIds(buckets) + .boatIds(boats) + .spawnEggIds(spawnEggs) + .carpets(carpets) + .furnaceMinecartData(furnaceMinecartData) + .build(); + + Registries.ITEMS.register(palette.getValue().getProtocolVersion(), itemMappings); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java b/connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java similarity index 51% rename from connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java rename to connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java index 506317ab9..97eeccece 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/populator/RecipeRegistryPopulator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.item; +package org.geysermc.connector.registry.populator; import com.fasterxml.jackson.databind.JsonNode; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; @@ -40,144 +40,95 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; +import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; -/** - * Manages any recipe-related storing - */ -public class RecipeRegistry { +import static org.geysermc.connector.utils.InventoryUtils.LAST_RECIPE_NET_ID; - /** - * Stores the last used recipe network ID. Since 1.16.200 (and for server-authoritative inventories), - * each recipe needs a unique network ID (or else in .200 the client crashes). - */ - public static int LAST_RECIPE_NET_ID = 0; +public class RecipeRegistryPopulator { - /** - * A list of all the following crafting recipes, but in a format understood by Java servers. - * Used for console autocrafting. - */ - public static final Int2ObjectMap ALL_CRAFTING_RECIPES = new Int2ObjectOpenHashMap<>(); - - /** - * A list of all possible leather armor dyeing recipes. - * Created manually. - */ - public static final List LEATHER_DYEING_RECIPES = new ObjectArrayList<>(); - /** - * A list of all possible firework rocket recipes, including the base rocket. - * Obtained from a ProxyPass dump of protocol v407 - */ - public static final List FIREWORK_ROCKET_RECIPES = new ObjectArrayList<>(); - /** - * A list of all possible firework star recipes. - * Obtained from a ProxyPass dump of protocol v407 - */ - public static final List FIREWORK_STAR_RECIPES = new ObjectArrayList<>(); - /** - * A list of all possible shulker box dyeing options. - * Obtained from a ProxyPass dump of protocol v407 - */ - public static final List SHULKER_BOX_DYEING_RECIPES = new ObjectArrayList<>(); - /** - * A list of all possible suspicious stew recipes. - * Obtained from a ProxyPass dump of protocol v407 - */ - public static final List SUSPICIOUS_STEW_RECIPES = new ObjectArrayList<>(); - /** - * A list of all possible tipped arrow recipes. - * Obtained from a ProxyPass dump of protocol v407 - */ - public static final List TIPPED_ARROW_RECIPES = new ObjectArrayList<>(); - - /** - * Recipe data that, when sent to the client, enables cartography features. - * This does not have a Java equivalent. - */ - public static final List CARTOGRAPHY_RECIPE_DATA = new ObjectArrayList<>(); - - /** - * Recipe data that, when sent to the client, enables book cloning - */ - public static final CraftingData BOOK_CLONING_RECIPE_DATA; - /** - * Recipe data that, when sent to the client, enables tool repairing in a crafting table - */ - public static final CraftingData TOOL_REPAIRING_RECIPE_DATA; - /** - * Recipe data that, when sent to the client, enables map extending in a crafting table - */ - public static final CraftingData MAP_EXTENDING_RECIPE_DATA; - /** - * Recipe data that, when sent to the client, enables map cloning in a crafting table - */ - public static final CraftingData MAP_CLONING_RECIPE_DATA; - /** - * Recipe data that, when sent to the client, enables banner duplicating - */ - public static final CraftingData BANNER_DUPLICATING_RECIPE_DATA; - - - static { - BOOK_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), LAST_RECIPE_NET_ID++); - TOOL_REPAIRING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("00000000-0000-0000-0000-000000000001"), LAST_RECIPE_NET_ID++); - MAP_EXTENDING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), LAST_RECIPE_NET_ID++); - MAP_CLONING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), LAST_RECIPE_NET_ID++); - BANNER_DUPLICATING_RECIPE_DATA = CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), LAST_RECIPE_NET_ID++); - - CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("8b36268c-1829-483c-a0f1-993b7156a8f2"), LAST_RECIPE_NET_ID++)); // Map extending - CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("442d85ed-8272-4543-a6f1-418f90ded05d"), LAST_RECIPE_NET_ID++)); // Map cloning - CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("98c84b38-1085-46bd-b1ce-dd38c159e6cc"), LAST_RECIPE_NET_ID++)); // Map upgrading - CARTOGRAPHY_RECIPE_DATA.add(CraftingData.fromMulti(UUID.fromString("602234e4-cac1-4353-8bb7-b1ebff70024b"), LAST_RECIPE_NET_ID++)); // Map locking - // https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php - - // Get all recipes that are not directly sent from a Java server + public static void populate() { InputStream stream = FileUtils.getResource("mappings/recipes.json"); JsonNode items; try { items = GeyserConnector.JSON_MAPPER.readTree(stream); } catch (Exception e) { - throw new AssertionError("Unable to load Java runtime item IDs", e); + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); } - for (JsonNode entry : items.get("leather_armor")) { - // This won't be perfect, as we can't possibly send every leather input for every kind of color - // But it does display the correct output from a base leather armor, and besides visuals everything works fine - LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry)); - } - for (JsonNode entry : items.get("firework_rockets")) { - FIREWORK_ROCKET_RECIPES.add(getCraftingDataFromJsonNode(entry)); - } - for (JsonNode entry : items.get("firework_stars")) { - FIREWORK_STAR_RECIPES.add(getCraftingDataFromJsonNode(entry)); - } - for (JsonNode entry : items.get("shulker_boxes")) { - SHULKER_BOX_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry)); - } - for (JsonNode entry : items.get("suspicious_stew")) { - SUSPICIOUS_STEW_RECIPES.add(getCraftingDataFromJsonNode(entry)); - } - for (JsonNode entry : items.get("tipped_arrows")) { - TIPPED_ARROW_RECIPES.add(getCraftingDataFromJsonNode(entry)); + int currentRecipeId = LAST_RECIPE_NET_ID; + for (Map.Entry version : Registries.ITEMS.get().entrySet()) { + // Make a bit of an assumption here that the last recipe net ID will be equivalent between all versions + LAST_RECIPE_NET_ID = currentRecipeId; + Map> craftingData = new EnumMap<>(RecipeType.class); + Int2ObjectMap recipes = new Int2ObjectOpenHashMap<>(); + + craftingData.put(RecipeType.CRAFTING_SPECIAL_BOOKCLONING, + Collections.singletonList(CraftingData.fromMulti(UUID.fromString("d1ca6b84-338e-4f2f-9c6b-76cc8b4bd98d"), ++LAST_RECIPE_NET_ID))); + craftingData.put(RecipeType.CRAFTING_SPECIAL_REPAIRITEM, + Collections.singletonList(CraftingData.fromMulti(UUID.fromString("00000000-0000-0000-0000-000000000001"), ++LAST_RECIPE_NET_ID))); + craftingData.put(RecipeType.CRAFTING_SPECIAL_MAPEXTENDING, + Collections.singletonList(CraftingData.fromMulti(UUID.fromString("d392b075-4ba1-40ae-8789-af868d56f6ce"), ++LAST_RECIPE_NET_ID))); + craftingData.put(RecipeType.CRAFTING_SPECIAL_MAPCLONING, + Collections.singletonList(CraftingData.fromMulti(UUID.fromString("85939755-ba10-4d9d-a4cc-efb7a8e943c4"), ++LAST_RECIPE_NET_ID))); + craftingData.put(RecipeType.CRAFTING_SPECIAL_BANNERADDPATTERN, + Collections.singletonList(CraftingData.fromMulti(UUID.fromString("b5c5d105-75a2-4076-af2b-923ea2bf4bf0"), ++LAST_RECIPE_NET_ID))); + + // https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/inventory/MultiRecipe.php + + for (JsonNode entry : items.get("leather_armor")) { + // This won't be perfect, as we can't possibly send every leather input for every kind of color + // But it does display the correct output from a base leather armor, and besides visuals everything works fine + craftingData.computeIfAbsent(RecipeType.CRAFTING_SPECIAL_ARMORDYE, + c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); + } + for (JsonNode entry : items.get("firework_rockets")) { + craftingData.computeIfAbsent(RecipeType.CRAFTING_SPECIAL_FIREWORK_ROCKET, + c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); + } + for (JsonNode entry : items.get("firework_stars")) { + craftingData.computeIfAbsent(RecipeType.CRAFTING_SPECIAL_FIREWORK_STAR, + c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); + } + for (JsonNode entry : items.get("shulker_boxes")) { + craftingData.computeIfAbsent(RecipeType.CRAFTING_SPECIAL_SHULKERBOXCOLORING, + c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); + } + for (JsonNode entry : items.get("suspicious_stew")) { + craftingData.computeIfAbsent(RecipeType.CRAFTING_SPECIAL_SUSPICIOUSSTEW, + c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); + } + for (JsonNode entry : items.get("tipped_arrows")) { + craftingData.computeIfAbsent(RecipeType.CRAFTING_SPECIAL_TIPPEDARROW, + c -> new ObjectArrayList<>()).add(getCraftingDataFromJsonNode(entry, recipes, version.getValue())); + } + + Registries.CRAFTING_DATA.register(version.getKey(), craftingData); + Registries.RECIPES.register(version.getKey(), recipes); } } /** * Computes a Bedrock crafting recipe from the given JSON data. * @param node the JSON data to compute + * @param recipes a list of all the recipes * @return the {@link CraftingData} to send to the Bedrock client. */ - private static CraftingData getCraftingDataFromJsonNode(JsonNode node) { - int netId = LAST_RECIPE_NET_ID++; + private static CraftingData getCraftingDataFromJsonNode(JsonNode node, Int2ObjectMap recipes, ItemMappings mappings) { + int netId = ++LAST_RECIPE_NET_ID; int type = node.get("bedrockRecipeType").asInt(); JsonNode outputNode = node.get("output"); - ItemEntry outputEntry = ItemRegistry.getItemEntry(outputNode.get("identifier").asText()); + ItemMapping outputEntry = mappings.getMapping(outputNode.get("identifier").asText()); ItemData output = getBedrockItemFromIdentifierJson(outputEntry, outputNode); UUID uuid = UUID.randomUUID(); if (type == 1) { @@ -194,7 +145,7 @@ public class RecipeRegistry { while (iterator.hasNext()) { Map.Entry entry = iterator.next(); JsonNode inputNode = entry.getValue(); - ItemEntry inputEntry = ItemRegistry.getItemEntry(inputNode.get("identifier").asText()); + ItemMapping inputEntry = mappings.getMapping(inputNode.get("identifier").asText()); letterToRecipe.put(entry.getKey(), getBedrockItemFromIdentifierJson(inputEntry, inputNode)); } @@ -212,12 +163,12 @@ public class RecipeRegistry { /* Convert into a Java recipe class for autocrafting */ List ingredients = new ArrayList<>(); for (ItemData input : inputs) { - ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input)})); + ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input, mappings)})); } ShapedRecipeData data = new ShapedRecipeData(shape.get(0).length(), shape.size(), "crafting_table", - ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output)); + ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output, mappings)); Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPED, "", data); - ALL_CRAFTING_RECIPES.put(netId, recipe); + recipes.put(netId, recipe); /* Convert end */ return CraftingData.fromShaped(uuid.toString(), shape.get(0).length(), shape.size(), @@ -225,19 +176,19 @@ public class RecipeRegistry { } List inputs = new ObjectArrayList<>(); for (JsonNode entry : node.get("inputs")) { - ItemEntry inputEntry = ItemRegistry.getItemEntry(entry.get("identifier").asText()); + ItemMapping inputEntry = mappings.getMapping(entry.get("identifier").asText()); inputs.add(getBedrockItemFromIdentifierJson(inputEntry, entry)); } /* Convert into a Java Recipe class for autocrafting */ List ingredients = new ArrayList<>(); for (ItemData input : inputs) { - ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input)})); + ingredients.add(new Ingredient(new ItemStack[]{ItemTranslator.translateToJava(input, mappings)})); } ShapelessRecipeData data = new ShapelessRecipeData("crafting_table", - ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output)); + ingredients.toArray(new Ingredient[0]), ItemTranslator.translateToJava(output, mappings)); Recipe recipe = new Recipe(RecipeType.CRAFTING_SHAPELESS, "", data); - ALL_CRAFTING_RECIPES.put(netId, recipe); + recipes.put(netId, recipe); /* Convert end */ if (type == 5) { @@ -249,7 +200,7 @@ public class RecipeRegistry { inputs, Collections.singletonList(output), uuid, "crafting_table", 0, netId); } - private static ItemData getBedrockItemFromIdentifierJson(ItemEntry itemEntry, JsonNode itemNode) { + private static ItemData getBedrockItemFromIdentifierJson(ItemMapping mapping, JsonNode itemNode) { int count = 1; short damage = 0; NbtMap tag = null; @@ -272,14 +223,11 @@ public class RecipeRegistry { } } return ItemData.builder() - .id(itemEntry.getBedrockId()) + .id(mapping.getBedrockId()) .damage(damage) .count(count) - .blockRuntimeId(itemEntry.isBlock() ? itemEntry.getBedrockBlockId() : 0) - .tag(tag).build(); + .blockRuntimeId(mapping.isBlock() ? mapping.getBedrockBlockId() : 0) + .tag(tag) + .build(); } - - public static void init() { - // no-op - } -} +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/BlockMappings.java b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMappings.java new file mode 100644 index 000000000..0cadcf232 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/BlockMappings.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.type; + +import com.nukkitx.nbt.NbtList; +import com.nukkitx.nbt.NbtMap; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import lombok.Builder; +import lombok.Value; +import org.geysermc.connector.network.translators.world.chunk.ChunkSection; + +import java.util.Map; + +@Builder +@Value +public class BlockMappings { + int bedrockAirId; + int bedrockWaterId; + + int blockStateVersion; + + ChunkSection emptyChunkSection; + + Int2IntMap javaToBedrockBlockMap; + Int2IntMap bedrockToJavaBlockMap; + + NbtList bedrockBlockStates; + + /** + * Contains a map of Java blocks to their respective Bedrock block tag, if the Java identifier is different from Bedrock. + * Required to fix villager trades with these blocks. + */ + Map javaIdentifierToBedrockTag; + + int commandBlockRuntimeId; + + Object2IntMap itemFrames; + Map flowerPotBlocks; + + public int getBedrockBlockId(int state) { + return this.javaToBedrockBlockMap.get(state); + } + + public int getJavaBlockState(int bedrockId) { + return this.bedrockToJavaBlockMap.get(bedrockId); + } + + public int getItemFrame(NbtMap tag) { + return this.itemFrames.getOrDefault(tag, -1); + } + + public boolean isItemFrame(int bedrockBlockRuntimeId) { + return this.itemFrames.values().contains(bedrockBlockRuntimeId); + } + + /** + * @param cleanJavaIdentifier the clean Java identifier of the block to look up + * + * @return the block tag of the block name mapped from Java to Bedrock. + */ + public NbtMap getBedrockBlockNbt(String cleanJavaIdentifier) { + return this.javaIdentifierToBedrockTag.get(cleanJavaIdentifier); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java b/connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java similarity index 65% rename from connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java rename to connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java index 3967abb17..9219a8793 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/type/GeyserMappingItem.java @@ -23,19 +23,20 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.item; +package org.geysermc.connector.registry.type; -import lombok.Getter; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; /** - * Used when an item should have a custom name applied, if there already isn't one. + * Represents Geyser's own serialized item information before being processed per-version */ -public class TranslatableItemEntry extends ItemEntry { - @Getter - private final String translationString; - - public TranslatableItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, int bedrockBlockId, int stackSize) { - super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, bedrockBlockId, stackSize); - this.translationString = (isBlock() ? "block." : "item.") + javaIdentifier.replace(":", "."); - } +@Data +public class GeyserMappingItem { + @JsonProperty("bedrock_identifier") String bedrockIdentifier; + @JsonProperty("bedrock_data") int bedrockData; + Integer blockRuntimeId; + @JsonProperty("stack_size") Integer stackSize; + @JsonProperty("tool_type") String toolType; + @JsonProperty("tool_tier") String toolTier; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/registry/type/ItemMapping.java similarity index 52% rename from connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java rename to connector/src/main/java/org/geysermc/connector/registry/type/ItemMapping.java index 74ce0951f..fff00950b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/registry/type/ItemMapping.java @@ -23,38 +23,64 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.item; +package org.geysermc.connector.registry.type; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.ToString; -import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.geysermc.connector.network.BedrockProtocol; +import org.geysermc.connector.registry.BlockRegistries; -@Getter -@AllArgsConstructor -@ToString -public class ItemEntry { - public static final ItemEntry AIR = new ItemEntry("minecraft:air", "minecraft:air", 0, 0, 0, - BlockTranslator1_17_0.INSTANCE.getBedrockAirId(), 64); +@Value +@Builder +@EqualsAndHashCode +public class ItemMapping { + public static final ItemMapping AIR = new ItemMapping("minecraft:air", "minecraft:air", 0, 0, 0, + BlockRegistries.BLOCKS.forVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getBedrockAirId(), + 64, null, null, null); + + String javaIdentifier; + String bedrockIdentifier; + int javaId; + int bedrockId; + int bedrockData; - private final String javaIdentifier; - private final String bedrockIdentifier; - private final int javaId; - private final int bedrockId; - private final int bedrockData; /** * The Bedrock block runtime ID to render this item with. The specific state *does* matter in how this item is rendered and used as a crafting ingredient. * Required since 1.16.220. */ - private final int bedrockBlockId; - private final int stackSize; + int bedrockBlockId; + int stackSize; + String toolType; + String toolTier; + + String translationString; + + /** + * Gets if this item is a block. + * + * @return if this item is a block + */ public boolean isBlock() { - return bedrockBlockId != -1; + return this.bedrockBlockId != -1; } - @Override - public boolean equals(Object obj) { - return obj == this || (obj instanceof ItemEntry && ((ItemEntry) obj).getBedrockId() == this.getBedrockId() && ((ItemEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier())); + /** + * Gets if this item has a translation string present. + * + * @return if this item has a translation string present + */ + public boolean hasTranslation() { + return this.translationString != null; } -} + + /** + * Gets if this item is a tool. + * + * @return if this item is a tool + */ + public boolean isTool() { + return this.toolType != null; + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/ItemMappings.java b/connector/src/main/java/org/geysermc/connector/registry/type/ItemMappings.java new file mode 100644 index 000000000..767b6b2e0 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/ItemMappings.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.type; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.IntList; +import lombok.Builder; +import lombok.Value; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.item.StoredItemMappings; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +@Builder +@Value +public class ItemMappings { + + Map cachedJavaMappings = new WeakHashMap<>(); + + Int2ObjectMap items; + + ItemData[] creativeItems; + List itemEntries; + + StoredItemMappings storedItems; + String[] itemNames; + Set javaOnlyItems; + + IntList bucketIds; + IntList boatIds; + IntList spawnEggIds; + List carpets; + + @Nullable ComponentItemData furnaceMinecartData; + + /** + * Gets an {@link ItemMapping} from the given {@link ItemStack}. + * + * @param itemStack the itemstack + * @return an item entry from the given java edition identifier + */ + public ItemMapping getMapping(ItemStack itemStack) { + return this.getMapping(itemStack.getId()); + } + + /** + * Gets an {@link ItemMapping} from the given Minecraft: Java + * Edition id. + * + * @param javaId the id + * @return an item entry from the given java edition identifier + */ + public ItemMapping getMapping(int javaId) { + return this.items.get(javaId); + } + + /** + * Gets an {@link ItemMapping} from the given Minecraft: Java Edition + * block state identifier. + * + * @param javaIdentifier the block state identifier + * @return an item entry from the given java edition identifier + */ + public ItemMapping getMapping(String javaIdentifier) { + return this.cachedJavaMappings.computeIfAbsent(javaIdentifier, key -> { + for (ItemMapping mapping : this.items.values()) { + if (mapping.getJavaIdentifier().equals(key)) { + return mapping; + } + } + return null; + }); + } + + /** + * Gets an {@link ItemMapping} from the given {@link ItemData}. + * + * @param data the item data + * @return an item entry from the given item data + */ + public ItemMapping getMapping(ItemData data) { + boolean isBlock = data.getBlockRuntimeId() != 0; + boolean hasDamage = data.getDamage() != 0; + + for (ItemMapping mapping : this.items.values()) { + if (mapping.getBedrockId() == data.getId()) { + if (isBlock && !hasDamage) { // Pre-1.16.220 will not use block runtime IDs at all, so we shouldn't check either + if (data.getBlockRuntimeId() != mapping.getBedrockBlockId()) { + continue; + } + } else { + if (!(mapping.getBedrockData() == data.getDamage() || + // Make exceptions for potions and tipped arrows, whose damage values can vary + (mapping.getJavaIdentifier().endsWith("potion") || mapping.getJavaIdentifier().equals("minecraft:arrow")))) { + continue; + } + } + if (!this.javaOnlyItems.contains(mapping.getJavaIdentifier())) { + // From a Bedrock item data, we aren't getting one of these items + return mapping; + } + } + } + + // This will hide the message when the player clicks with an empty hand + if (data.getId() != 0 && data.getDamage() != 0) { + GeyserConnector.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage()); + } + return ItemMapping.AIR; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/PaletteItem.java b/connector/src/main/java/org/geysermc/connector/registry/type/PaletteItem.java new file mode 100644 index 000000000..4582d1e80 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/PaletteItem.java @@ -0,0 +1,35 @@ + +/* + * Copyright (c) 2019-2021 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.connector.registry.type; + +import lombok.Data; + +@Data +public class PaletteItem { + String name; + int id; +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/ParticleMapping.java b/connector/src/main/java/org/geysermc/connector/registry/type/ParticleMapping.java new file mode 100644 index 000000000..2ed21ddb3 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/ParticleMapping.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.type; + +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import lombok.Value; + +import javax.annotation.ParametersAreNullableByDefault; + +@Value +@ParametersAreNullableByDefault +public class ParticleMapping { + LevelEventType levelEventType; + String identifier; + int id; +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/registry/type/SoundMapping.java b/connector/src/main/java/org/geysermc/connector/registry/type/SoundMapping.java new file mode 100644 index 000000000..ba0bcd862 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/registry/type/SoundMapping.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2021 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.connector.registry.type; + +import lombok.Value; + +@Value +public class SoundMapping { + String java; + String bedrock; + String playsound; + int extraData; + String identifier; + boolean levelEvent; + + public SoundMapping(String java, String bedrock, String playsound, int extraData, String identifier, boolean levelEvent) { + this.java = java; + this.bedrock = bedrock == null || bedrock.equalsIgnoreCase("") ? null : bedrock; + this.playsound = playsound == null || playsound.equalsIgnoreCase("") ? null : playsound; + this.extraData = extraData; + this.identifier = identifier == null || identifier.equalsIgnoreCase("") ? ":" : identifier; + this.levelEvent = levelEvent; + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java b/connector/src/main/java/org/geysermc/connector/utils/BiomeUtils.java similarity index 70% rename from connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java rename to connector/src/main/java/org/geysermc/connector/utils/BiomeUtils.java index d797381ce..650367a4a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/BiomeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BiomeUtils.java @@ -1,95 +1,63 @@ -/* - * Copyright (c) 2019-2021 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.connector.network.translators; - -import com.nukkitx.nbt.NBTInputStream; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtUtils; -import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.utils.FileUtils; - -import java.io.InputStream; -import java.util.Arrays; - -// Based off of ProtocolSupport's LegacyBiomeData.java: -// https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java -// Array index formula by https://wiki.vg/Chunk_Format -public class BiomeTranslator { - - public static final NbtMap BIOMES; - - private BiomeTranslator() { - } - - public static void init() { - // no-op - } - - static { - /* Load biomes */ - InputStream stream = FileUtils.getResource("bedrock/biome_definitions.dat"); - - NbtMap biomesTag; - - try (NBTInputStream biomenbtInputStream = NbtUtils.createNetworkReader(stream)) { - biomesTag = (NbtMap) biomenbtInputStream.readTag(); - BIOMES = biomesTag; - } catch (Exception ex) { - GeyserConnector.getInstance().getLogger().warning("Failed to get biomes from biome definitions, is there something wrong with the file?"); - throw new AssertionError(ex); - } - } - - public static byte[] toBedrockBiome(int[] biomeData) { - byte[] bedrockData = new byte[256]; - if (biomeData == null) { - return bedrockData; - } - - for (int y = 0; y < 16; y += 4) { - for (int z = 0; z < 16; z += 4) { - for (int x = 0; x < 16; x += 4) { - byte biomeId = biomeID(biomeData, x, y, z); - int offset = ((z + (y / 4)) << 4) | x; - Arrays.fill(bedrockData, offset, offset + 4, biomeId); - } - } - } - return bedrockData; - } - - private static byte biomeID(int[] biomeData, int x, int y, int z) { - int biomeId = biomeData[((y >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3)]; - if (biomeId == 0) { - biomeId = 42; // Ocean - } else if (biomeId >= 40 && biomeId <= 43) { // Java has multiple End dimensions that Bedrock doesn't recognize - biomeId = 9; - } else if (biomeId >= 170) { // Nether biomes. Dunno why it's like this :microjang: - biomeId += 8; - } - return (byte) biomeId; - } -} +/* + * Copyright (c) 2019-2021 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.connector.utils; + +import java.util.Arrays; + +// Based off of ProtocolSupport's LegacyBiomeData.java: +// https://github.com/ProtocolSupport/ProtocolSupport/blob/b2cad35977f3fcb65bee57b9e14fc9c975f71d32/src/protocolsupport/protocol/typeremapper/legacy/LegacyBiomeData.java +// Array index formula by https://wiki.vg/Chunk_Format +public class BiomeUtils { + public static byte[] toBedrockBiome(int[] biomeData) { + byte[] bedrockData = new byte[256]; + if (biomeData == null) { + return bedrockData; + } + + for (int y = 0; y < 16; y += 4) { + for (int z = 0; z < 16; z += 4) { + for (int x = 0; x < 16; x += 4) { + byte biomeId = biomeID(biomeData, x, y, z); + int offset = ((z + (y / 4)) << 4) | x; + Arrays.fill(bedrockData, offset, offset + 4, biomeId); + } + } + } + return bedrockData; + } + + private static byte biomeID(int[] biomeData, int x, int y, int z) { + int biomeId = biomeData[((y >> 2) & 63) << 4 | ((z >> 2) & 3) << 2 | ((x >> 2) & 3)]; + if (biomeId == 0) { + biomeId = 42; // Ocean + } else if (biomeId >= 40 && biomeId <= 43) { // Java has multiple End dimensions that Bedrock doesn't recognize + biomeId = 9; + } else if (biomeId >= 170) { // Nether biomes. Dunno why it's like this :microjang: + biomeId += 8; + } + return (byte) biomeId; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java index d932c7d24..97a5e90db 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java @@ -29,17 +29,56 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; +import org.geysermc.connector.registry.Registries; + +import java.util.HashMap; +import java.util.Map; import javax.annotation.Nonnull; public class BlockEntityUtils { - private static final BlockEntityTranslator EMPTY_TRANSLATOR = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("Empty"); + /** + * A list of all block entities that require the Java block state in order to fill out their block entity information. + * This list will be smaller with cache chunks on as we don't need to double-cache data + */ + public static final ObjectArrayList BEDROCK_ONLY_BLOCK_ENTITIES = new ObjectArrayList<>(); + + /** + * Contains a list of irregular block entity name translations that can't be fit into the regex + */ + public static final Map BLOCK_ENTITY_TRANSLATIONS = new HashMap() { + { + // Bedrock/Java differences + put("minecraft:enchanting_table", "EnchantTable"); + put("minecraft:jigsaw", "JigsawBlock"); + put("minecraft:piston_head", "PistonArm"); + put("minecraft:trapped_chest", "Chest"); + // There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly + } + }; + + private static final BlockEntityTranslator EMPTY_TRANSLATOR = Registries.BLOCK_ENTITIES.get("Empty"); + + static { + for (BlockEntityTranslator translator : Registries.BLOCK_ENTITIES.get().values()) { + if (!(translator instanceof BedrockOnlyBlockEntity)) { + continue; + } + + GeyserConnector.getInstance().getLogger().debug("Found Bedrock-only block entity: " + translator.getClass().getCanonicalName()); + BedrockOnlyBlockEntity bedrockOnlyBlockEntity = (BedrockOnlyBlockEntity) translator; + BEDROCK_ONLY_BLOCK_ENTITIES.add(bedrockOnlyBlockEntity); + } + } public static String getBedrockBlockEntityId(String id) { // These are the only exceptions when it comes to block entity ids - String value = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); + String value = BLOCK_ENTITY_TRANSLATIONS.get(id); if (value != null) { return value; } @@ -61,7 +100,7 @@ public class BlockEntityUtils { } public static BlockEntityTranslator getBlockEntityTranslator(String name) { - BlockEntityTranslator blockEntityTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get(name); + BlockEntityTranslator blockEntityTranslator = Registries.BLOCK_ENTITIES.get(name); if (blockEntityTranslator != null) { return blockEntityTranslator; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java index 0ad3f0148..ea613ef6b 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java @@ -32,10 +32,11 @@ import com.nukkitx.math.vector.Vector3i; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ToolItemEntry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.network.translators.collision.translators.BlockCollision; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.registry.Registries; import org.geysermc.connector.registry.type.BlockMapping; +import org.geysermc.connector.registry.type.ItemMapping; public class BlockUtils { /** @@ -56,7 +57,7 @@ public class BlockUtils { case "shovel": return session.getTagCache().isShovelEffective(blockMapping); case "sword": - return blockMapping.getJavaBlockId() == BlockTranslator.JAVA_COBWEB_BLOCK_ID; + return blockMapping.getJavaBlockId() == BlockStateValues.JAVA_COBWEB_ID; default: session.getConnector().getLogger().warning("Unknown tool type: " + itemToolType); return false; @@ -146,17 +147,16 @@ public class BlockUtils { return 1.0 / speed; } - public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemEntry item, CompoundTag nbtData, boolean isSessionPlayer) { + public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemMapping item, CompoundTag nbtData, boolean isSessionPlayer) { boolean isShearsEffective = session.getTagCache().isShearsEffective(blockMapping); //TODO called twice boolean canHarvestWithHand = blockMapping.isCanBreakWithHand(); String toolType = ""; String toolTier = ""; boolean correctTool = false; boolean toolCanBreak = false; - if (item instanceof ToolItemEntry) { - ToolItemEntry toolItem = (ToolItemEntry) item; - toolType = toolItem.getToolType(); - toolTier = toolItem.getToolTier(); + if (item.isTool()) { + toolType = item.getToolType(); + toolTier = item.getToolTier(); correctTool = correctTool(session, blockMapping, toolType); toolCanBreak = canToolTierBreakBlock(session, blockMapping, toolTier); } @@ -189,16 +189,16 @@ public class BlockUtils { public static double getSessionBreakTime(GeyserSession session, BlockMapping blockMapping) { PlayerInventory inventory = session.getPlayerInventory(); GeyserItemStack item = inventory.getItemInHand(); - ItemEntry itemEntry; + ItemMapping mapping; CompoundTag nbtData; if (item != null) { - itemEntry = item.getItemEntry(); + mapping = item.getMapping(session); nbtData = item.getNbt(); } else { - itemEntry = null; + mapping = ItemMapping.AIR; nbtData = new CompoundTag(""); } - return getBreakTime(session, blockMapping, itemEntry, nbtData, true); + return getBreakTime(session, blockMapping, mapping, nbtData, true); } /** @@ -225,4 +225,16 @@ public class BlockUtils { return blockPos; } + // Note: these reuse classes, so don't try to store more than once instance or coordinates will get overwritten + public static BlockCollision getCollision(int blockId, int x, int y, int z) { + BlockCollision collision = Registries.COLLISIONS.get(blockId); + if (collision != null) { + collision.setPosition(x, y, z); + } + return collision; + } + + public static BlockCollision getCollisionAt(GeyserSession session, int x, int y, int z) { + return getCollision(session.getConnector().getWorldManager().getBlockAt(session, x, y, z), x, y, z); + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 343f5c53e..5681e1d4f 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -51,7 +51,6 @@ import org.geysermc.connector.entity.player.SkullPlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator; @@ -59,12 +58,13 @@ import org.geysermc.connector.network.translators.world.chunk.BlockStorage; import org.geysermc.connector.network.translators.world.chunk.ChunkSection; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion; +import org.geysermc.connector.registry.BlockRegistries; import java.util.ArrayList; import java.util.BitSet; import java.util.List; -import static org.geysermc.connector.network.translators.world.block.BlockTranslator.JAVA_AIR_ID; +import static org.geysermc.connector.network.translators.world.block.BlockStateValues.JAVA_AIR_ID; @UtilityClass public class ChunkUtils { @@ -109,15 +109,15 @@ public class ChunkUtils { if (javaPalette instanceof GlobalPalette) { // As this is the global palette, simply iterate through the whole chunk section once - ChunkSection section = new ChunkSection(session.getBlockTranslator().getBedrockAirId()); + ChunkSection section = new ChunkSection(session.getBlockMappings().getBedrockAirId()); for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { int javaId = javaData.get(yzx); - int bedrockId = session.getBlockTranslator().getBedrockBlockId(javaId); + int bedrockId = session.getBlockMappings().getBedrockBlockId(javaId); int xzy = indexYZXtoXZY(yzx); section.getBlockStorageArray()[0].setFullBlock(xzy, bedrockId); - if (BlockTranslator.isWaterlogged(javaId)) { - section.getBlockStorageArray()[1].setFullBlock(xzy, session.getBlockTranslator().getBedrockWaterId()); + if (BlockRegistries.WATERLOGGED.get().contains(javaId)) { + section.getBlockStorageArray()[1].setFullBlock(xzy, session.getBlockMappings().getBedrockWaterId()); } // Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock @@ -139,9 +139,9 @@ public class ChunkUtils { // Iterate through palette and convert state IDs to Bedrock, doing some additional checks as we go for (int i = 0; i < javaPalette.size(); i++) { int javaId = javaPalette.idToState(i); - bedrockPalette.add(session.getBlockTranslator().getBedrockBlockId(javaId)); + bedrockPalette.add(session.getBlockMappings().getBedrockBlockId(javaId)); - if (BlockTranslator.isWaterlogged(javaId)) { + if (BlockRegistries.WATERLOGGED.get().contains(javaId)) { waterloggedPaletteIds.set(i); } @@ -195,8 +195,8 @@ public class ChunkUtils { // V1 palette IntList layer1Palette = new IntArrayList(2); - layer1Palette.add(session.getBlockTranslator().getBedrockAirId()); // Air - see BlockStorage's constructor for more information - layer1Palette.add(session.getBlockTranslator().getBedrockWaterId()); + layer1Palette.add(session.getBlockMappings().getBedrockAirId()); // Air - see BlockStorage's constructor for more information + layer1Palette.add(session.getBlockMappings().getBedrockWaterId()); layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) }; } @@ -317,7 +317,7 @@ public class ChunkUtils { skull.despawnEntity(session, position); } - int blockId = session.getBlockTranslator().getBedrockBlockId(blockState); + int blockId = session.getBlockMappings().getBedrockBlockId(blockState); UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); @@ -330,10 +330,10 @@ public class ChunkUtils { UpdateBlockPacket waterPacket = new UpdateBlockPacket(); waterPacket.setDataLayer(1); waterPacket.setBlockPosition(position); - if (BlockTranslator.isWaterlogged(blockState)) { - waterPacket.setRuntimeId(session.getBlockTranslator().getBedrockWaterId()); + if (BlockRegistries.WATERLOGGED.get().contains(blockState)) { + waterPacket.setRuntimeId(session.getBlockMappings().getBedrockWaterId()); } else { - waterPacket.setRuntimeId(session.getBlockTranslator().getBedrockAirId()); + waterPacket.setRuntimeId(session.getBlockMappings().getBedrockAirId()); } session.sendUpstreamPacket(waterPacket); @@ -364,7 +364,7 @@ public class ChunkUtils { // Iterates through all Bedrock-only block entity translators and determines if a manual block entity packet // needs to be sent - for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityTranslator.BEDROCK_ONLY_BLOCK_ENTITIES) { + for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityUtils.BEDROCK_ONLY_BLOCK_ENTITIES) { if (bedrockOnlyBlockEntity.isBlock(blockState)) { // Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks bedrockOnlyBlockEntity.updateBlock(session, blockState, position); @@ -383,7 +383,7 @@ public class ChunkUtils { data.setChunkX(chunkX + x); data.setChunkZ(chunkZ + z); data.setSubChunksLength(0); - data.setData(session.getBlockTranslator().getEmptyChunkData()); + data.setData(new byte[0]); data.setCachingEnabled(false); session.sendUpstreamPacket(data); diff --git a/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java b/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java index 43675ebfd..583c2ddae 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/CooldownUtils.java @@ -61,6 +61,8 @@ public class CooldownUtils { SetTitlePacket titlePacket = new SetTitlePacket(); titlePacket.setType(SetTitlePacket.Type.TITLE); titlePacket.setText(" "); + titlePacket.setXuid(""); + titlePacket.setPlatformOnlineId(""); session.sendUpstreamPacket(titlePacket); session.setLastHitTime(System.currentTimeMillis()); long lastHitTime = session.getLastHitTime(); // Used later to prevent multiple scheduled cooldown threads @@ -86,6 +88,8 @@ public class CooldownUtils { titlePacket.setFadeInTime(0); titlePacket.setFadeOutTime(5); titlePacket.setStayTime(2); + titlePacket.setXuid(""); + titlePacket.setPlatformOnlineId(""); session.sendUpstreamPacket(titlePacket); if (hasCooldown(session)) { session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 @@ -97,6 +101,8 @@ public class CooldownUtils { removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE); } removeTitlePacket.setText(" "); + removeTitlePacket.setXuid(""); + removeTitlePacket.setPlatformOnlineId(""); session.sendUpstreamPacket(removeTitlePacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java b/connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java new file mode 100644 index 000000000..8d69fa691 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/EffectUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019-2021 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.connector.utils; + +import com.github.steveice10.mc.protocol.data.game.world.particle.ParticleType; +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import lombok.NonNull; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.registry.Registries; + +/** + * Util for particles and effects. + */ +public class EffectUtils { + + /** + * Used for area effect clouds. + * + * @param type the Java particle to search for + * @return the Bedrock integer ID of the particle, or -1 if it does not exist + */ + public static int getParticleId(GeyserSession session, @NonNull ParticleType type) { + LevelEventType levelEventType = Registries.PARTICLES.get(type).getLevelEventType(); + if (levelEventType == null) { + return -1; + } + + // Remove the legacy bit applied to particles for LevelEventType serialization + return session.getUpstream().getSession().getPacketCodec().getHelper().getLevelEventId(levelEventType) & ~0x4000; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java index ce72c5d93..228a8d448 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java @@ -34,7 +34,7 @@ import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.living.animal.horse.HorseEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.EnumSet; import java.util.Set; @@ -61,8 +61,8 @@ public class InteractiveTagManager { */ public static void updateTag(GeyserSession session, Entity interactEntity) { EntityDataMap entityMetadata = interactEntity.getMetadata(); - ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); - String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); + ItemMapping mapping = session.getPlayerInventory().getItemInHand().getMapping(session); + String javaIdentifierStripped = mapping.getJavaIdentifier().replace("minecraft:", ""); InteractiveTag interactiveTag = InteractiveTag.NONE; @@ -83,7 +83,7 @@ public class InteractiveTagManager { // Holding a leash and the mob is leashable for sure // (Plugins can change this behavior so that's something to look into in the far far future) interactiveTag = InteractiveTag.LEASH; - } else if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(session, javaIdentifierStripped, itemEntry)) { + } else if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(session, javaIdentifierStripped, mapping)) { // This animal can be fed interactiveTag = InteractiveTag.FEED; } else { @@ -148,7 +148,7 @@ public class InteractiveTagManager { // Can't ride a baby if (tamed) { interactiveTag = InteractiveTag.RIDE_HORSE; - } else if (itemEntry.getJavaId() == 0) { + } else if (mapping.getJavaId() == 0) { // Can't hide an untamed entity without having your hand empty interactiveTag = InteractiveTag.MOUNT; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java index d9480d466..c9ddd8ed9 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -47,14 +47,21 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.network.translators.inventory.translators.LecternInventoryTranslator; import org.geysermc.connector.network.translators.inventory.translators.chest.DoubleChestInventoryTranslator; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.registry.Registries; +import org.geysermc.connector.registry.type.ItemMapping; import java.util.Collections; import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.function.IntFunction; public class InventoryUtils { + /** + * Stores the last used recipe network ID. Since 1.16.200 (and for server-authoritative inventories), + * each recipe needs a unique network ID (or else in .200 the client crashes). + */ + public static int LAST_RECIPE_NET_ID; + public static final ItemStack REFRESH_ITEM = new ItemStack(1, 127, new CompoundTag("")); public static void openInventory(GeyserSession session, Inventory inventory) { @@ -155,7 +162,7 @@ public class InventoryUtils { * @param description the description * @return the unusable space block */ - public static ItemData createUnusableSpaceBlock(String description) { + public static IntFunction createUnusableSpaceBlock(String description) { NbtMapBuilder root = NbtMap.builder(); NbtMapBuilder display = NbtMap.builder(); @@ -164,8 +171,8 @@ public class InventoryUtils { display.putList("Lore", NbtType.STRING, Collections.singletonList(ChatColor.RESET + ChatColor.DARK_PURPLE + description)); root.put("display", display.build()); - return ItemData.builder() - .id(ItemRegistry.ITEM_ENTRIES.get(ItemRegistry.BARRIER_INDEX).getBedrockId()) + return protocolVersion -> ItemData.builder() + .id(Registries.ITEMS.forVersion(protocolVersion).getStoredItems().barrier().getBedrockId()) .count(1) .tag(root.build()).build(); } @@ -250,7 +257,7 @@ public class InventoryUtils { continue; } // If this isn't the item we're looking for - if (!geyserItem.getItemEntry().getJavaIdentifier().equals(itemName)) { + if (!geyserItem.getMapping(session).getJavaIdentifier().equals(itemName)) { continue; } @@ -266,7 +273,7 @@ public class InventoryUtils { continue; } // If this isn't the item we're looking for - if (!geyserItem.getItemEntry().getJavaIdentifier().equals(itemName)) { + if (!geyserItem.getMapping(session).getJavaIdentifier().equals(itemName)) { continue; } @@ -279,10 +286,10 @@ public class InventoryUtils { if (session.getGameMode() == GameMode.CREATIVE) { int slot = findEmptyHotbarSlot(inventory); - ItemEntry entry = ItemRegistry.getItemEntry(itemName); - if (entry != null) { + ItemMapping mapping = session.getItemMappings().getMapping(itemName); + if (mapping != null) { ClientCreativeInventoryActionPacket actionPacket = new ClientCreativeInventoryActionPacket(slot, - new ItemStack(entry.getJavaId())); + new ItemStack(mapping.getJavaId())); if ((slot - 36) != inventory.getHeldItemSlot()) { setHotbarItem(session, slot); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java index dd4543f61..db4e9e2e1 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java @@ -26,7 +26,7 @@ package org.geysermc.connector.utils; import com.github.steveice10.opennbt.tag.builtin.*; -import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.session.GeyserSession; public class ItemUtils { @@ -49,8 +49,8 @@ public class ItemUtils { /** * @return the correct Bedrock durability for this item. */ - public static int getCorrectBedrockDurability(int javaId, int original) { - if (javaId == ItemRegistry.FISHING_ROD.getJavaId()) { + public static int getCorrectBedrockDurability(GeyserSession session, int javaId, int original) { + if (javaId == session.getItemMappings().getStoredItems().fishingRod().getJavaId()) { // Java durability: 64 // Bedrock durability : 384 // 384 / 64 = 6 diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java b/connector/src/main/java/org/geysermc/connector/utils/SoundUtils.java similarity index 65% rename from connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java rename to connector/src/main/java/org/geysermc/connector/utils/SoundUtils.java index 2e93811c0..5abd9ff57 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SoundUtils.java @@ -1,41 +1,46 @@ -/* - * Copyright (c) 2019-2021 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.connector.network.translators.item; - -import lombok.Getter; - -@Getter -public class ToolItemEntry extends ItemEntry { - private final String toolType; - private final String toolTier; - - public ToolItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, - String toolType, String toolTier, int bedrockBlockId, int stackSize) { - super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, bedrockBlockId, stackSize); - this.toolType = toolType; - this.toolTier = toolTier; - } -} +/* + * Copyright (c) 2019-2021 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.connector.utils; + +import com.nukkitx.protocol.bedrock.data.SoundEvent; + +public class SoundUtils { + + /** + * Maps a sound name to a sound event, null if one + * does not exist. + * + * @param sound the sound name + * @return a sound event from the given sound + */ + public static SoundEvent toSoundEvent(String sound) { + try { + return SoundEvent.valueOf(sound.toUpperCase().replaceAll("\\.", "_")); + } catch (Exception ex) { + return null; + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java index e59807d75..eca4f7453 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/StatisticsUtils.java @@ -29,8 +29,8 @@ import com.github.steveice10.mc.protocol.data.MagicValues; import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.github.steveice10.mc.protocol.data.game.statistic.*; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.cumulus.SimpleForm; import org.geysermc.cumulus.response.SimpleFormResponse; import org.geysermc.cumulus.util.FormImage; @@ -76,6 +76,7 @@ public class StatisticsUtils { StringBuilder content = new StringBuilder(); + ItemMappings mappings = session.getItemMappings(); switch (response.getClickedButtonId()) { case 0: builder.title("stat.generalButton"); @@ -92,7 +93,7 @@ public class StatisticsUtils { for (Map.Entry entry : session.getStatistics().entrySet()) { if (entry.getKey() instanceof BreakBlockStatistic) { - String block = BlockTranslator.JAVA_ID_TO_JAVA_IDENTIFIER_MAP.get(((BreakBlockStatistic) entry.getKey()).getId()); + String block = BlockRegistries.JAVA_BLOCKS.get(((BreakBlockStatistic) entry.getKey()).getId()).getJavaIdentifier(); block = block.replace("minecraft:", "block.minecraft."); content.append(block).append(": ").append(entry.getValue()).append("\n"); } @@ -103,7 +104,7 @@ public class StatisticsUtils { for (Map.Entry entry : session.getStatistics().entrySet()) { if (entry.getKey() instanceof BreakItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((BreakItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + String item = mappings.getMapping(((BreakItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); } } @@ -113,7 +114,7 @@ public class StatisticsUtils { for (Map.Entry entry : session.getStatistics().entrySet()) { if (entry.getKey() instanceof CraftItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((CraftItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + String item = mappings.getMapping(((CraftItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); } } @@ -123,7 +124,7 @@ public class StatisticsUtils { for (Map.Entry entry : session.getStatistics().entrySet()) { if (entry.getKey() instanceof UseItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((UseItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + String item = mappings.getMapping(((UseItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); } } @@ -133,7 +134,7 @@ public class StatisticsUtils { for (Map.Entry entry : session.getStatistics().entrySet()) { if (entry.getKey() instanceof PickupItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((PickupItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + String item = mappings.getMapping(((PickupItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); } } @@ -143,7 +144,7 @@ public class StatisticsUtils { for (Map.Entry entry : session.getStatistics().entrySet()) { if (entry.getKey() instanceof DropItemStatistic) { - String item = ItemRegistry.ITEM_ENTRIES.get(((DropItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); + String item = mappings.getMapping(((DropItemStatistic) entry.getKey()).getId()).getJavaIdentifier(); content.append(getItemTranslateKey(item, language)).append(": ").append(entry.getValue()).append("\n"); } } diff --git a/connector/src/main/resources/bedrock/block_palette.1_17_10.nbt b/connector/src/main/resources/bedrock/block_palette.1_17_10.nbt new file mode 100644 index 0000000000000000000000000000000000000000..a32c0736ea54406f5d8eb9a12729aa58feb5d9ea GIT binary patch literal 41318 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.0.0 org.geysermc geyser-parent - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT pom Geyser Allows for players from Minecraft Bedrock Edition to join Minecraft Java Edition servers.