From c64d57439f25246d94ce7b64509c2905f6804734 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 5 Nov 2020 22:36:22 +0100 Subject: [PATCH] Block entity performance improvements (#1481) * BlockEntity performance improvements * Use chunk cache if possible for block caching * Get new block state from ViaVersion if block entity * Add Javadoc for FlowerPotBlockEntityTranslator.isFlowerBlock * Remove debug line * Don't add all RequiresBlockState instances if cache chunks is enabled * Double chest map get optimization * Last changes Co-authored-by: DoctorMacc --- .../world/GeyserSpigotWorldManager.java | 58 ++++++---- .../connector/entity/ItemFrameEntity.java | 13 +-- ...BedrockInventoryTransactionTranslator.java | 7 +- .../translators/item/ItemRegistry.java | 10 +- .../translators/item/ItemTranslator.java | 7 +- .../item/translators/BannerTranslator.java | 108 +++++++++--------- .../java/world/JavaChunkDataTranslator.java | 7 +- .../world/JavaUpdateTileEntityTranslator.java | 25 ++-- .../world/block/BlockStateValues.java | 41 +++---- .../entity/BannerBlockEntityTranslator.java | 18 +-- .../entity/BedBlockEntityTranslator.java | 16 +-- .../block/entity/BedrockOnlyBlockEntity.java | 3 +- .../block/entity/BlockEntityTranslator.java | 30 +++-- .../entity/CampfireBlockEntityTranslator.java | 10 +- .../CommandBlockBlockEntityTranslator.java | 33 +++--- .../DoubleChestBlockEntityTranslator.java | 59 ++++------ .../entity/EmptyBlockEntityTranslator.java | 9 +- .../EndGatewayBlockEntityTranslator.java | 15 +-- .../FlowerPotBlockEntityTranslator.java | 46 +++++--- .../JigsawBlockBlockEntityTranslator.java | 19 ++- .../NoteblockBlockEntityTranslator.java | 7 +- .../entity/PistonBlockEntityTranslator.java | 20 ++-- .../ShulkerBoxBlockEntityTranslator.java | 17 +-- .../entity/SignBlockEntityTranslator.java | 73 ++++++------ .../entity/SkullBlockEntityTranslator.java | 19 ++- .../entity/SpawnerBlockEntityTranslator.java | 57 +++++---- .../connector/utils/BlockEntityUtils.java | 15 ++- .../geysermc/connector/utils/ChunkUtils.java | 24 ++-- 28 files changed, 362 insertions(+), 404 deletions(-) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index ad3d1cf1..28b2da3a 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -43,16 +43,19 @@ import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.GameRule; import org.geysermc.connector.utils.LanguageUtils; import us.myles.ViaVersion.api.Pair; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.data.MappingData; +import us.myles.ViaVersion.api.minecraft.Position; import us.myles.ViaVersion.api.protocol.Protocol; import us.myles.ViaVersion.api.protocol.ProtocolRegistry; import us.myles.ViaVersion.api.protocol.ProtocolVersion; import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2; +import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockStorage; import java.io.InputStream; import java.util.List; public class GeyserSpigotWorldManager extends GeyserWorldManager { - /** * The current client protocol version for ViaVersion usage. */ @@ -99,8 +102,9 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { } // Only load in the biomes that are present in this version of Minecraft for (Biome enumBiome : Biome.values()) { - if (biomes.has(enumBiome.toString())) { - biomeToIdMap.put(enumBiome.ordinal(), biomes.get(enumBiome.toString()).intValue()); + JsonNode biome = biomes.get(enumBiome.toString()); + if (biome != null) { + biomeToIdMap.put(enumBiome.ordinal(), biome.intValue()); } else { GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() + ", defaulting to 0"); @@ -127,30 +131,38 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) { if (isViaVersion) { - return getLegacyBlock(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(), x, y, z, true); + Player bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername()); + // Get block entity storage + BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class); + return getLegacyBlock(storage, bukkitPlayer.getWorld(), x, y, z); } else { return BlockTranslator.AIR; } } @SuppressWarnings("deprecation") - public static int getLegacyBlock(World world, int x, int y, int z, boolean isViaVersion) { - if (isViaVersion) { - Block block = world.getBlockAt(x, y, z); - // Black magic that gets the old block state ID - int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); - // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 - blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId); - List> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, - ProtocolVersion.v1_13.getId()); - for (int i = protocolList.size() - 1; i >= 0; i--) { - if (protocolList.get(i).getValue().getMappingData() == null) continue; - blockId = protocolList.get(i).getValue().getMappingData().getNewBlockStateId(blockId); + public static int getLegacyBlock(BlockStorage storage, World world, int x, int y, int z) { + Block block = world.getBlockAt(x, y, z); + // Black magic that gets the old block state ID + int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); + // Convert block state from old version (1.12.2) -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 + blockId = ProtocolRegistry.getProtocol(Protocol1_13To1_12_2.class).getMappingData().getNewBlockId(blockId); + List> protocolList = ProtocolRegistry.getProtocolPath(CLIENT_PROTOCOL_VERSION, + ProtocolVersion.v1_13.getId()); + // Translate block entity differences - some information was stored in block tags and not block states + if (storage.isWelcome(blockId)) { // No getOrDefault method + BlockStorage.ReplacementData data = storage.get(new Position(x, (short) y, z)); + if (data != null && data.getReplacement() != -1) { + blockId = data.getReplacement(); } - return blockId; - } else { - return BlockTranslator.AIR; } + for (int i = protocolList.size() - 1; i >= 0; i--) { + MappingData mappingData = protocolList.get(i).getValue().getMappingData(); + if (mappingData != null) { + blockId = mappingData.getNewBlockStateId(blockId); + } + } + return blockId; } @Override @@ -162,11 +174,13 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { return; } World world = bukkitPlayer.getWorld(); - if (this.isLegacy) { + if (this.isLegacy) { + // Get block entity storage + BlockStorage storage = Via.getManager().getConnection(bukkitPlayer.getUniqueId()).get(BlockStorage.class); for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockX = 0; blockX < 16; blockX++) { - chunk.set(blockX, blockY, blockZ, getLegacyBlock(world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ, true)); + chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ)); } } } @@ -176,7 +190,7 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockX = 0; blockX < 16; blockX++) { Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); - int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0); + int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), BlockTranslator.AIR); chunk.set(blockX, blockY, blockZ, id); } } 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 f9d2ace4..501c7e46 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -141,6 +141,7 @@ public class ItemFrameEntity extends Entity { UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(bedrockPosition); + // TODO 1.16.100 set to BEDROCK_AIR updateBlockPacket.setRuntimeId(0); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); @@ -196,18 +197,6 @@ public class ItemFrameEntity extends Entity { return session.getItemFrameCache().getOrDefault(position, -1); } - /** - * Determines if the position contains an item frame. - * Does largely the same thing as getItemFrameEntityId, but for speed purposes is implemented separately, - * since every block destroy packet has to check for an item frame. - * @param position position of block. - * @param session GeyserSession. - * @return true if position contains item frame, false if not. - */ - public static boolean positionContainsItemFrame(GeyserSession session, Vector3i position) { - return session.getItemFrameCache().containsKey(position); - } - /** * Force-remove from the position-to-ID map so it doesn't cause conflicts. * @param session GeyserSession. diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index b2a70146..81a2cd50 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -194,10 +194,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator ITEM_ENTRIES.values() - .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null)); + 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 f95a0ccc..55db9a25 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 @@ -51,7 +51,6 @@ import java.util.*; import java.util.stream.Collectors; public abstract class ItemTranslator { - private static final Int2ObjectMap ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap<>(); private static final List NBT_TRANSLATORS; @@ -220,7 +219,7 @@ public abstract class ItemTranslator { public abstract List getAppliedItems(); public NbtMap translateNbtToBedrock(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag) { - Map javaValue = new HashMap<>(); + NbtMapBuilder builder = NbtMap.builder(); if (tag.getValue() != null && !tag.getValue().isEmpty()) { for (String str : tag.getValue().keySet()) { com.github.steveice10.opennbt.tag.builtin.Tag javaTag = tag.get(str); @@ -228,11 +227,9 @@ public abstract class ItemTranslator { if (translatedTag == null) continue; - javaValue.put(javaTag.getName(), translatedTag); + builder.put(javaTag.getName(), translatedTag); } } - NbtMapBuilder builder = NbtMap.builder(); - javaValue.forEach(builder::put); return builder.build(); } 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 f4bfdfb6..200271cf 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 @@ -26,20 +26,16 @@ 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.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.IntTag; -import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.github.steveice10.opennbt.tag.builtin.StringTag; -import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.github.steveice10.opennbt.tag.builtin.*; import com.nukkitx.nbt.NbtList; 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.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.ItemEntry; import java.util.ArrayList; import java.util.HashMap; @@ -49,54 +45,13 @@ import java.util.stream.Collectors; @ItemRemapper public class BannerTranslator extends ItemTranslator { - private final List appliedItems; public BannerTranslator() { - appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList()); - } - - @Override - public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { - if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, itemEntry); - - ItemData itemData = super.translateToBedrock(itemStack, itemEntry); - - CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); - if (blockEntityTag.contains("Patterns")) { - ListTag patterns = blockEntityTag.get("Patterns"); - - NbtMapBuilder builder = itemData.getTag().toBuilder(); - builder.put("Patterns", convertBannerPattern(patterns)); - - itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build()); - } - - return itemData; - } - - @Override - public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { - if (itemData.getTag() == null) return super.translateToJava(itemData, itemEntry); - - ItemStack itemStack = super.translateToJava(itemData, itemEntry); - - NbtMap nbtTag = itemData.getTag(); - if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { - List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); - - CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); - blockEntityTag.put(convertBannerPattern(patterns)); - - itemStack.getNbt().put(blockEntityTag); - } - - return itemStack; - } - - @Override - public List getAppliedItems() { - return appliedItems; + appliedItems = ItemRegistry.ITEM_ENTRIES.values() + .stream() + .filter(entry -> entry.getJavaIdentifier().endsWith("banner")) + .collect(Collectors.toList()); } /** @@ -133,7 +88,6 @@ public class BannerTranslator extends ItemTranslator { return NbtMap.builder() .putInt("Color", 15 - (int) pattern.get("Color").getValue()) - .putString("Pattern", (String) pattern.get("Pattern").getValue()) .putString("Pattern", patternName) .build(); } @@ -147,8 +101,7 @@ public class BannerTranslator extends ItemTranslator { public static ListTag convertBannerPattern(List patterns) { List tagsList = new ArrayList<>(); for (Object patternTag : patterns) { - CompoundTag newPatternTag = getJavaBannerPattern((NbtMap) patternTag); - tagsList.add(newPatternTag); + tagsList.add(getJavaBannerPattern((NbtMap) patternTag)); } return new ListTag("Patterns", tagsList); @@ -167,4 +120,51 @@ public class BannerTranslator extends ItemTranslator { return new CompoundTag("", tags); } + + @Override + public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { + if (itemStack.getNbt() == null) { + return super.translateToBedrock(itemStack, itemEntry); + } + + ItemData itemData = super.translateToBedrock(itemStack, itemEntry); + + CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag"); + if (blockEntityTag.contains("Patterns")) { + ListTag patterns = blockEntityTag.get("Patterns"); + + NbtMapBuilder builder = itemData.getTag().toBuilder(); + builder.put("Patterns", convertBannerPattern(patterns)); + + itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build()); + } + + return itemData; + } + + @Override + public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + if (itemData.getTag() == null) { + return super.translateToJava(itemData, itemEntry); + } + + ItemStack itemStack = super.translateToJava(itemData, itemEntry); + + NbtMap nbtTag = itemData.getTag(); + if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { + List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); + + CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); + blockEntityTag.put(convertBannerPattern(patterns)); + + itemStack.getNbt().put(blockEntityTag); + } + + return itemStack; + } + + @Override + public List getAppliedItems() { + return appliedItems; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index cd1a321c..741632bc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -45,14 +45,13 @@ import org.geysermc.connector.utils.ChunkUtils; @Translator(packet = ServerChunkDataPacket.class) public class JavaChunkDataTranslator extends PacketTranslator { - /** * Determines if we should process non-full chunks */ - private final boolean isCacheChunks; + private final boolean cacheChunks; public JavaChunkDataTranslator() { - isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); + cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); } @Override @@ -61,7 +60,7 @@ public class JavaChunkDataTranslator extends PacketTranslator { + private final boolean cacheChunks; + + public JavaUpdateTileEntityTranslator() { + cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); + } @Override public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { @@ -48,16 +54,17 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE && packet.getNbt().size() > 5) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java index 305118e6..bfd59cc7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java @@ -36,7 +36,6 @@ import java.util.Map; * Used for block entities if the Java block state contains Bedrock block information. */ public class BlockStateValues { - private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap(); private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap(); private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap(); @@ -52,7 +51,8 @@ public class BlockStateValues { /** * Determines if the block state contains Bedrock block information - * @param entry The String to JsonNode map used in BlockTranslator + * + * @param entry The String to JsonNode map used in BlockTranslator * @param javaBlockState the Java Block State of the block */ public static void storeBlockStateValues(Map.Entry entry, int javaBlockState) { @@ -101,7 +101,7 @@ public class BlockStateValues { } JsonNode skullVariation = entry.getValue().get("variation"); - if(skullVariation != null) { + if (skullVariation != null) { SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); } @@ -124,10 +124,7 @@ public class BlockStateValues { * @return Banner color integer or -1 if no color */ public static int getBannerColor(int state) { - if (BANNER_COLORS.containsKey(state)) { - return BANNER_COLORS.get(state); - } - return -1; + return BANNER_COLORS.getOrDefault(state, -1); } /** @@ -138,10 +135,7 @@ public class BlockStateValues { * @return Bed color byte or -1 if no color */ public static byte getBedColor(int state) { - if (BED_COLORS.containsKey(state)) { - return BED_COLORS.get(state); - } - return -1; + return BED_COLORS.getOrDefault(state, (byte) -1); } /** @@ -157,6 +151,7 @@ public class BlockStateValues { /** * All double chest values are part of the block state in Java and part of the block entity tag in Bedrock. * This gives the DoubleChestValue that can be calculated into the final tag. + * * @return The map of all DoubleChestValues. */ public static Int2ObjectMap getDoubleChestValues() { @@ -165,6 +160,7 @@ public class BlockStateValues { /** * Get the Int2ObjectMap of flower pot block states to containing plant + * * @return Int2ObjectMap of flower pot values */ public static Int2ObjectMap getFlowerPotValues() { @@ -173,6 +169,7 @@ public class BlockStateValues { /** * Get the map of contained flower pot plants to Bedrock CompoundTag + * * @return Map of flower pot blocks. */ public static Map getFlowerPotBlocks() { @@ -182,18 +179,17 @@ public class BlockStateValues { /** * The note that noteblocks output when hit is part of the block state in Java but sent as a BlockEventPacket in Bedrock. * This gives an integer pitch that Bedrock can use. + * * @param state BlockState of the block * @return note block note integer or -1 if not present */ public static int getNoteblockPitch(int state) { - if (NOTEBLOCK_PITCHES.containsKey(state)) { - return NOTEBLOCK_PITCHES.get(state); - } - return -1; + return NOTEBLOCK_PITCHES.getOrDefault(state, -1); } /** * Get the Int2BooleanMap showing if a piston block state is extended or not. + * * @return the Int2BooleanMap of piston extensions. */ public static Int2BooleanMap getPistonValues() { @@ -212,10 +208,7 @@ public class BlockStateValues { * @return Skull variant byte or -1 if no variant */ public static byte getSkullVariant(int state) { - if (SKULL_VARIANTS.containsKey(state)) { - return SKULL_VARIANTS.get(state); - } - return -1; + return SKULL_VARIANTS.getOrDefault(state, (byte) -1); } /** @@ -226,10 +219,7 @@ public class BlockStateValues { * @return Skull rotation value or -1 if no value */ public static byte getSkullRotation(int state) { - if (SKULL_ROTATIONS.containsKey(state)) { - return SKULL_ROTATIONS.get(state); - } - return -1; + return SKULL_ROTATIONS.getOrDefault(state, (byte) -1); } @@ -241,9 +231,6 @@ public class BlockStateValues { * @return Shulker direction value or -1 if no value */ public static byte getShulkerBoxDirection(int state) { - if (SHULKERBOX_DIRECTIONS.containsKey(state)) { - return SHULKERBOX_DIRECTIONS.get(state); - } - return -1; + return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java index 57393a6c..f5e1d594 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java @@ -27,39 +27,31 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.item.translators.BannerTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Banner", regex = "banner") public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getBannerColor(blockState) != -1; } @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { int bannerColor = BlockStateValues.getBannerColor(blockState); if (bannerColor != -1) { - tags.put("Base", 15 - bannerColor); + builder.put("Base", 15 - bannerColor); } if (tag.contains("Patterns")) { ListTag patterns = tag.get("Patterns"); - tags.put("Patterns", BannerTranslator.convertBannerPattern(patterns)); + builder.put("Patterns", BannerTranslator.convertBannerPattern(patterns)); } if (tag.contains("CustomName")) { - tags.put("CustomName", tag.get("CustomName").getValue()); + builder.put("CustomName", tag.get("CustomName").getValue()); } - - return tags; } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java index 080bdc3b..0067cc41 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java @@ -26,27 +26,23 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Bed", regex = "bed") public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getBedColor(blockState) != -1; } @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte bedcolor = BlockStateValues.getBedColor(blockState); // Just in case... - if (bedcolor == -1) bedcolor = 0; - tags.put("color", bedcolor); - return tags; + if (bedcolor == -1) { + bedcolor = 0; + } + builder.put("color", bedcolor); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java index d2e4537f..646929f3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedrockOnlyBlockEntity.java @@ -33,7 +33,6 @@ import org.geysermc.connector.network.session.GeyserSession; * Implemented only if a block is a block entity in Bedrock and not Java Edition. */ public interface BedrockOnlyBlockEntity { - /** * Update the block on Bedrock Edition. * @param session GeyserSession. @@ -49,7 +48,7 @@ public interface BedrockOnlyBlockEntity { * @return Bedrock tag, or null if not a Bedrock-only Block Entity */ static NbtMap getTag(Vector3i position, int blockState) { - if (new FlowerPotBlockEntityTranslator().isBlock(blockState)) { + if (FlowerPotBlockEntityTranslator.isFlowerBlock(blockState)) { return FlowerPotBlockEntityTranslator.getTag(blockState, position); } else if (PistonBlockEntityTranslator.isBlock(blockState)) { return PistonBlockEntityTranslator.getTag(blockState, position); 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 4df4fd95..67963652 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 @@ -41,10 +41,16 @@ 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<>(); - public static ObjectArrayList REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); + /** + * 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 REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>(); /** * Contains a list of irregular block entity name translations that can't be fit into the regex @@ -78,27 +84,33 @@ public abstract class BlockEntityTranslator { GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_entity.failed", clazz.getCanonicalName())); } } + boolean cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); for (Class clazz : ref.getSubTypesOf(RequiresBlockState.class)) { GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName()); try { - REQUIRES_BLOCK_STATE_LIST.add((RequiresBlockState) clazz.newInstance()); + RequiresBlockState requiresBlockState = (RequiresBlockState) clazz.newInstance(); + if (cacheChunks && !(requiresBlockState instanceof BedrockOnlyBlockEntity)) { + // Not needed to put this one in the map; cache chunks takes care of that for us + GeyserConnector.getInstance().getLogger().debug("Not adding because cache chunks is enabled."); + continue; + } + REQUIRES_BLOCK_STATE_LIST.add(requiresBlockState); } catch (InstantiationException | IllegalAccessException e) { GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName())); } } } - public abstract Map translateTag(CompoundTag tag, int blockState); + public abstract void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState); public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) { - int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue())); - int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue())); - int z = Integer.parseInt(String.valueOf(tag.getValue().get("z").getValue())); + int x = ((IntTag) tag.getValue().get("x")).getValue(); + int y = ((IntTag) tag.getValue().get("y")).getValue(); + int z = ((IntTag) tag.getValue().get("z")).getValue(); NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder(); - Map translatedTags = translateTag(tag, blockState); - translatedTags.forEach(tagBuilder::put); + translateTag(tagBuilder, tag, blockState); return tagBuilder.build(); } 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 d6ac0281..3e4f9fb9 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 @@ -32,22 +32,16 @@ import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Campfire", regex = "campfire") public class CampfireBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { ListTag items = tag.get("Items"); int i = 1; for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) { - tags.put("Item" + i, getItem((CompoundTag) itemTag)); + builder.put("Item" + i, getItem((CompoundTag) itemTag)); i++; } - return tags; } protected NbtMap getItem(CompoundTag tag) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java index 6bc940ad..2484dba7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java @@ -26,38 +26,33 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.*; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.utils.MessageUtils; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "CommandBlock", regex = "command_block") public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map map = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { if (tag.size() < 5) { - return map; // These values aren't here + return; // These values aren't here } // Java infers from the block state, but Bedrock needs it in the tag - map.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0)); + builder.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0)); // Java and Bedrock values - map.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue()); - map.put("auto", ((ByteTag) tag.get("auto")).getValue()); - map.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue())); - map.put("powered", ((ByteTag) tag.get("powered")).getValue()); - map.put("Command", ((StringTag) tag.get("Command")).getValue()); - map.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue()); - map.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue()); - map.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue()); + builder.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue()); + builder.put("auto", ((ByteTag) tag.get("auto")).getValue()); + builder.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue())); + builder.put("powered", ((ByteTag) tag.get("powered")).getValue()); + builder.put("Command", ((StringTag) tag.get("Command")).getValue()); + builder.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue()); + builder.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue()); + builder.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue()); if (tag.get("LastExecution") != null) { - map.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue()); + builder.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue()); } else { - map.put("LastExecution", (long) 0); + builder.put("LastExecution", (long) 0); } - return map; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java index 5b59420e..47bcf489 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java @@ -33,15 +33,11 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; import org.geysermc.connector.network.translators.world.block.DoubleChestValue; import org.geysermc.connector.utils.BlockEntityUtils; -import java.util.HashMap; -import java.util.Map; - /** * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity */ @BlockEntity(name = "Chest", regex = "chest") public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getDoubleChestValues().containsKey(blockState); @@ -51,44 +47,39 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl public void updateBlock(GeyserSession session, int blockState, Vector3i position) { CompoundTag javaTag = getConstantJavaTag("chest", position.getX(), position.getY(), position.getZ()); NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId("chest"), position.getX(), position.getY(), position.getZ()).toBuilder(); - translateTag(javaTag, blockState).forEach(tagBuilder::put); + translateTag(tagBuilder, javaTag, blockState); BlockEntityUtils.updateBlockEntity(session, tagBuilder.build(), position); } @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - if (BlockStateValues.getDoubleChestValues().containsKey(blockState)) { - DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().get(blockState); - if (chestValues != null) { - int x = (int) tag.getValue().get("x").getValue(); - int z = (int) tag.getValue().get("z").getValue(); - // Calculate the position of the other chest based on the Java block state - if (chestValues.isFacingEast) { - if (chestValues.isDirectionPositive) { - // East - z = z + (chestValues.isLeft ? 1 : -1); - } else { - // West - z = z + (chestValues.isLeft ? -1 : 1); - } + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + DoubleChestValue chestValues = BlockStateValues.getDoubleChestValues().getOrDefault(blockState, null); + if (chestValues != null) { + int x = (int) tag.getValue().get("x").getValue(); + int z = (int) tag.getValue().get("z").getValue(); + // Calculate the position of the other chest based on the Java block state + if (chestValues.isFacingEast) { + if (chestValues.isDirectionPositive) { + // East + z = z + (chestValues.isLeft ? 1 : -1); } else { - if (chestValues.isDirectionPositive) { - // South - x = x + (chestValues.isLeft ? -1 : 1); - } else { - // North - x = x + (chestValues.isLeft ? 1 : -1); - } + // West + z = z + (chestValues.isLeft ? -1 : 1); } - tags.put("pairx", x); - tags.put("pairz", z); - if (!chestValues.isLeft) { - tags.put("pairlead", (byte) 1); + } else { + if (chestValues.isDirectionPositive) { + // South + x = x + (chestValues.isLeft ? -1 : 1); + } else { + // North + x = x + (chestValues.isLeft ? 1 : -1); } } + builder.put("pairx", x); + builder.put("pairz", z); + if (!chestValues.isLeft) { + builder.put("pairlead", (byte) 1); + } } - return tags; } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java index e9715bd3..3926b866 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java @@ -26,16 +26,11 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; - -import java.util.HashMap; -import java.util.Map; +import com.nukkitx.nbt.NbtMapBuilder; @BlockEntity(name = "Empty", regex = "") public class EmptyBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - return new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java index af94c560..0bf58822 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java @@ -28,21 +28,18 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.nukkitx.nbt.NbtList; +import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtType; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.Map; @BlockEntity(name = "EndGateway", regex = "end_gateway") public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - tags.put("Age", (int) ((long) tag.get("Age").getValue())); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + builder.put("Age", (int) ((long) tag.get("Age").getValue())); // Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist // Linked coordinates IntList tagsList = new IntArrayList(); @@ -50,8 +47,7 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { tagsList.add(getExitPortalCoordinate(tag, "X")); tagsList.add(getExitPortalCoordinate(tag, "Y")); tagsList.add(getExitPortalCoordinate(tag, "Z")); - tags.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList)); - return tags; + builder.put("ExitPortal", new NbtList<>(NbtType.INT, tagsList)); } private int getExitPortalCoordinate(CompoundTag tag, String axis) { @@ -60,6 +56,7 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { LinkedHashMap compoundTag = (LinkedHashMap) tag.get("ExitPortal").getValue(); IntTag intTag = (IntTag) compoundTag.get(axis); return intTag.getValue(); - } return 0; + } + return 0; } } 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 7bfcc7ee..f64474ae 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 @@ -35,30 +35,19 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockEntityUtils; public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { - - @Override - public boolean isBlock(int blockState) { - return (BlockStateValues.getFlowerPotValues().containsKey(blockState)); - } - - @Override - public void updateBlock(GeyserSession session, int blockState, Vector3i position) { - BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); - UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); - updateBlockPacket.setDataLayer(0); - updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState)); - updateBlockPacket.setBlockPosition(position); - updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); - updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); - updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); - session.sendUpstreamPacket(updateBlockPacket); - BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + /** + * @param blockState the Java block state of a potential flower pot block + * @return true if the block is a flower pot + */ + public static boolean isFlowerBlock(int blockState) { + return BlockStateValues.getFlowerPotValues().containsKey(blockState); } /** * Get the Nukkit CompoundTag of the flower pot. + * * @param blockState Java block state of flower pot. - * @param position Bedrock position of flower pot. + * @param position Bedrock position of flower pot. * @return Bedrock tag of flower pot. */ public static NbtMap getTag(int blockState, Vector3i position) { @@ -80,4 +69,23 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, R } return tagBuilder.build(); } + + @Override + public boolean isBlock(int blockState) { + return isFlowerBlock(blockState); + } + + @Override + public void updateBlock(GeyserSession session, int blockState, Vector3i position) { + BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); + updateBlockPacket.setDataLayer(0); + updateBlockPacket.setRuntimeId(BlockTranslator.getBedrockBlockId(blockState)); + updateBlockPacket.setBlockPosition(position); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); + updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); + session.sendUpstreamPacket(updateBlockPacket); + BlockEntityUtils.updateBlockEntity(session, getTag(blockState, position), position); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java index 43ac1a96..4fcdfe54 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java @@ -27,21 +27,16 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; - -import java.util.HashMap; -import java.util.Map; +import com.nukkitx.nbt.NbtMapBuilder; @BlockEntity(name = "JigsawBlock", regex = "jigsaw") public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map map = new HashMap<>(); - map.put("joint", ((StringTag) tag.get("joint")).getValue()); - map.put("name", ((StringTag) tag.get("name")).getValue()); - map.put("target_pool", ((StringTag) tag.get("pool")).getValue()); - map.put("final_state", ((StringTag) tag.get("final_state")).getValue()); - map.put("target", ((StringTag) tag.get("target")).getValue()); - return map; + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + builder.put("joint", ((StringTag) tag.get("joint")).getValue()); + builder.put("name", ((StringTag) tag.get("name")).getValue()); + builder.put("target_pool", ((StringTag) tag.get("pool")).getValue()); + builder.put("final_state", ((StringTag) tag.get("final_state")).getValue()); + builder.put("target", ((StringTag) tag.get("target")).getValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java index f3e43009..fce0a056 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/NoteblockBlockEntityTranslator.java @@ -36,21 +36,20 @@ import org.geysermc.connector.utils.ChunkUtils; * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock */ public class NoteblockBlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getNoteblockPitch(blockState) != -1; } public static void translate(GeyserSession session, Position position) { - int blockState = ChunkUtils.CACHED_BLOCK_ENTITIES.getOrDefault(position, 0); + int blockState = session.getConnector().getConfig().isCacheChunks() ? + session.getConnector().getWorldManager().getBlockAt(session, position) : + ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(position); BlockEventPacket blockEventPacket = new BlockEventPacket(); blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); blockEventPacket.setEventType(0); blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState)); session.sendUpstreamPacket(blockEventPacket); - - ChunkUtils.CACHED_BLOCK_ENTITIES.remove(position); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java index 54feacbe..c8a6e868 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntityTranslator.java @@ -34,9 +34,9 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues; * Pistons are a special case where they are only a block entity on Bedrock. */ public class PistonBlockEntityTranslator { - /** * Used in ChunkUtils to determine if the block is a piston. + * * @param blockState Java BlockState of block. * @return if block is a piston or not. */ @@ -46,8 +46,9 @@ public class PistonBlockEntityTranslator { /** * Calculates the Nukkit CompoundTag to send to the client on chunk + * * @param blockState Java block state of block. - * @param position Bedrock position of piston. + * @param position Bedrock position of piston. * @return Bedrock tag of piston. */ public static NbtMap getTag(int blockState, Vector3i position) { @@ -57,14 +58,13 @@ public class PistonBlockEntityTranslator { .putInt("z", position.getZ()) .putByte("isMovable", (byte) 1) .putString("id", "PistonArm"); - if (BlockStateValues.getPistonValues().containsKey(blockState)) { - boolean extended = BlockStateValues.getPistonValues().get(blockState); - // 1f if extended, otherwise 0f - tagBuilder.putFloat("Progress", (extended) ? 1.0f : 0.0f); - // 1 if sticky, 0 if not - tagBuilder.putByte("Sticky", (byte)((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0)); - } + + boolean extended = BlockStateValues.getPistonValues().get(blockState); + // 1f if extended, otherwise 0f + tagBuilder.putFloat("Progress", (extended) ? 1.0f : 0.0f); + // 1 if sticky, 0 if not + tagBuilder.putByte("Sticky", (byte) ((BlockStateValues.isStickyPiston(blockState)) ? 1 : 0)); + return tagBuilder.build(); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java index 08e3abaa..69fa1084 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java @@ -26,23 +26,18 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "ShulkerBox", regex = "shulker_box") public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte direction = BlockStateValues.getShulkerBoxDirection(blockState); // Just in case... - if (direction == -1) direction = 1; - tags.put("facing", direction); - return tags; + if (direction == -1) { + direction = 1; + } + builder.put("facing", direction); } - } 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 b40ed42c..a4f800da 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 @@ -27,51 +27,12 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.SignUtils; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Sign", regex = "sign") public class SignBlockEntityTranslator extends BlockEntityTranslator { - - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); - - StringBuilder signText = new StringBuilder(); - for(int i = 0; i < 4; i++) { - int currentLine = i+1; - String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), ""); - signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine)); - - // Check the character width on the sign to ensure there is no overflow that is usually hidden - // to Java Edition clients but will appear to Bedrock clients - int signWidth = 0; - StringBuilder finalSignLine = new StringBuilder(); - for (char c : signLine.toCharArray()) { - signWidth += SignUtils.getCharacterWidth(c); - if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { - finalSignLine.append(c); - } else { - break; - } - } - - // Java Edition 1.14 added the ability to change the text color of the whole sign using dye - if (tag.contains("Color")) { - signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); - } - - signText.append(finalSignLine.toString()); - signText.append("\n"); - } - - tags.put("Text", MessageUtils.getBedrockMessage(MessageSerializer.fromString(signText.toString()))); - return tags; - } - /** * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. *
@@ -133,4 +94,36 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { return base; } + @Override + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + StringBuilder signText = new StringBuilder(); + for (int i = 0; i < 4; i++) { + int currentLine = i + 1; + String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), ""); + signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine)); + + // Check the character width on the sign to ensure there is no overflow that is usually hidden + // to Java Edition clients but will appear to Bedrock clients + int signWidth = 0; + StringBuilder finalSignLine = new StringBuilder(); + for (char c : signLine.toCharArray()) { + signWidth += SignUtils.getCharacterWidth(c); + if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { + finalSignLine.append(c); + } else { + break; + } + } + + // Java Edition 1.14 added the ability to change the text color of the whole sign using dye + if (tag.contains("Color")) { + signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); + } + + signText.append(finalSignLine.toString()); + signText.append("\n"); + } + + builder.put("Text", MessageUtils.getBedrockMessage(MessageSerializer.fromString(signText.toString()))); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java index 6d350c0c..c5f47994 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java @@ -25,29 +25,26 @@ package org.geysermc.connector.network.translators.world.block.entity; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "Skull", regex = "skull") public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - @Override public boolean isBlock(int blockState) { return BlockStateValues.getSkullVariant(blockState) != -1; } @Override - public Map translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { byte skullVariant = BlockStateValues.getSkullVariant(blockState); float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f; // Just in case... - if (skullVariant == -1) skullVariant = 0; - tags.put("Rotation", rotation); - tags.put("SkullType", skullVariant); - return tags; + if (skullVariant == -1) { + skullVariant = 0; + } + builder.put("Rotation", rotation); + builder.put("SkullType", skullVariant); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java index 2601e3de..38507f54 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java @@ -26,63 +26,58 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.entity.type.EntityType; -import java.util.HashMap; -import java.util.Map; - @BlockEntity(name = "MobSpawner", regex = "mob_spawner") public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { - @Override - public Map translateTag(CompoundTag tag, int blockState) { - Map tags = new HashMap<>(); + public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { + Tag current; - if (tag.get("MaxNearbyEntities") != null) { - tags.put("MaxNearbyEntities", (short) tag.get("MaxNearbyEntities").getValue()); + if ((current = tag.get("MaxNearbyEntities")) != null) { + builder.put("MaxNearbyEntities", current.getValue()); } - if (tag.get("RequiredPlayerRange") != null) { - tags.put("RequiredPlayerRange", (short) tag.get("RequiredPlayerRange").getValue()); + if ((current = tag.get("RequiredPlayerRange")) != null) { + builder.put("RequiredPlayerRange", current.getValue()); } - if (tag.get("SpawnCount") != null) { - tags.put("SpawnCount", (short) tag.get("SpawnCount").getValue()); + if ((current = tag.get("SpawnCount")) != null) { + builder.put("SpawnCount", current.getValue()); } - if (tag.get("MaxSpawnDelay") != null) { - tags.put("MaxSpawnDelay", (short) tag.get("MaxSpawnDelay").getValue()); + if ((current = tag.get("MaxSpawnDelay")) != null) { + builder.put("MaxSpawnDelay", current.getValue()); } - if (tag.get("Delay") != null) { - tags.put("Delay", (short) tag.get("Delay").getValue()); + if ((current = tag.get("Delay")) != null) { + builder.put("Delay", current.getValue()); } - if (tag.get("SpawnRange") != null) { - tags.put("SpawnRange", (short) tag.get("SpawnRange").getValue()); + if ((current = tag.get("SpawnRange")) != null) { + builder.put("SpawnRange", current.getValue()); } - if (tag.get("MinSpawnDelay") != null) { - tags.put("MinSpawnDelay", (short) tag.get("MinSpawnDelay").getValue()); + if ((current = tag.get("MinSpawnDelay")) != null) { + builder.put("MinSpawnDelay", current.getValue()); } - if (tag.get("SpawnData") != null) { - CompoundTag spawnData = tag.get("SpawnData"); + CompoundTag spawnData = tag.get("SpawnData"); + if (spawnData != null) { String entityID = (String) spawnData.get("id").getValue(); - tags.put("EntityIdentifier", entityID); + builder.put("EntityIdentifier", entityID); EntityType type = EntityType.getFromIdentifier(entityID); if (type != null) { - tags.put("DisplayEntityWidth", type.getWidth()); - tags.put("DisplayEntityHeight", type.getHeight()); - tags.put("DisplayEntityScale", 1.0f); + builder.put("DisplayEntityWidth", type.getWidth()); + builder.put("DisplayEntityHeight", type.getHeight()); + builder.put("DisplayEntityScale", 1.0f); } } - tags.put("id", "MobSpawner"); - tags.put("isMovable", (byte) 1); - - return tags; + builder.put("id", "MobSpawner"); + builder.put("isMovable", (byte) 1); } - } 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 0ae31b33..e8fd8291 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockEntityUtils.java @@ -33,17 +33,17 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; public class BlockEntityUtils { - private static final BlockEntityTranslator EMPTY_TRANSLATOR = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get("Empty"); public static String getBedrockBlockEntityId(String id) { // These are the only exceptions when it comes to block entity ids - if (BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.containsKey(id)) { - return BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); + String value = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATIONS.get(id); + if (value != null) { + return value; } id = id.replace("minecraft:", "") - .replace("_", " "); + .replace("_", " "); // Split at every space or capital letter - for the latter, some legacy Java block entity tags are the correct format already String[] words; if (!id.toUpperCase().equals(id)) { // Otherwise we get [S, K, U, L, L] @@ -60,11 +60,10 @@ public class BlockEntityUtils { public static BlockEntityTranslator getBlockEntityTranslator(String name) { BlockEntityTranslator blockEntityTranslator = BlockEntityTranslator.BLOCK_ENTITY_TRANSLATORS.get(name); - if (blockEntityTranslator == null) { - return EMPTY_TRANSLATOR; + if (blockEntityTranslator != null) { + return blockEntityTranslator; } - - return blockEntityTranslator; + return EMPTY_TRANSLATOR; } public static void updateBlockEntity(GeyserSession session, NbtMap blockEntity, Position position) { 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 a63eeb42..0769a4d1 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -72,9 +72,9 @@ import static org.geysermc.connector.network.translators.world.block.BlockTransl @UtilityClass public class ChunkUtils { - /** - * Temporarily stores positions of BlockState values that are needed for certain block entities actively + * Temporarily stores positions of BlockState values that are needed for certain block entities actively. + * Not used if cache chunks is enabled */ public static final Object2IntMap CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>(); @@ -300,11 +300,16 @@ public class ChunkUtils { public static void updateBlock(GeyserSession session, int blockState, Vector3i position) { // Checks for item frames so they aren't tripped up and removed - if (ItemFrameEntity.positionContainsItemFrame(session, position) && blockState == AIR) { - ((ItemFrameEntity) session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position))).updateBlock(session); - return; - } else if (ItemFrameEntity.positionContainsItemFrame(session, position)) { - Entity entity = session.getEntityCache().getEntityByJavaId(ItemFrameEntity.getItemFrameEntityId(session, position)); + long frameEntityId = ItemFrameEntity.getItemFrameEntityId(session, position); + if (frameEntityId != -1) { + // TODO: Very occasionally the item frame doesn't sync up when destroyed + Entity entity = session.getEntityCache().getEntityByJavaId(frameEntityId); + if (blockState == AIR && entity != null) { // Item frame is still present and no block overrides that; refresh it + ((ItemFrameEntity) entity).updateBlock(session); + return; + } + + // Otherwise the item frame is gone if (entity != null) { session.getEntityCache().removeEntity(entity, false); } else { @@ -342,7 +347,10 @@ public class ChunkUtils { ((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position); break; } - CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + if (!session.getConnector().getConfig().isCacheChunks()) { + // Blocks aren't saved to a chunk cache; resort to this smaller cache + CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + } break; //No block will be a part of two classes } }