diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java index f0098f3c..cffb85f1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/Translators.java @@ -91,15 +91,15 @@ public class Translators { if (Packet.class.isAssignableFrom(packet)) { Class targetPacket = (Class) packet; PacketTranslator translator = (PacketTranslator) clazz.newInstance(); - + Registry.registerJava(targetPacket, translator); - + } else if (BedrockPacket.class.isAssignableFrom(packet)) { Class targetPacket = (Class) packet; PacketTranslator translator = (PacketTranslator) clazz.newInstance(); - + Registry.registerBedrock(targetPacket, translator); - + } else { GeyserConnector.getInstance().getLogger().error("Class " + clazz.getCanonicalName() + " is annotated as a translator but has an invalid target packet."); } @@ -116,11 +116,18 @@ public class Translators { } private static void registerBlockEntityTranslators() { - blockEntityTranslators.put("Empty", new EmptyBlockEntityTranslator()); - blockEntityTranslators.put("Sign", new SignBlockEntityTranslator()); - blockEntityTranslators.put("Campfire", new CampfireBlockEntityTranslator()); - blockEntityTranslators.put("Banner", new BannerBlockEntityTranslator()); - blockEntityTranslators.put("EndGateway", new EndGatewayBlockEntityTranslator()); + Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity"); + + for (Class clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) { + + GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName()); + + try { + blockEntityTranslators.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() + "."); + } + } } private static void registerInventoryTranslators() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java new file mode 100644 index 00000000..19581c7e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockStateValues.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019-2020 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.block; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; +import it.unimi.dsi.fastutil.objects.Object2ByteMap; +import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + +import java.util.Map; + +/** + * Used for block entities if the Java block state contains Bedrock block information. + */ +public class BlockStateValues { + + private static final Object2IntMap BANNER_COLORS = new Object2IntOpenHashMap<>(); + private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>(); + private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>(); + private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>(); + + /** + * Determines if the block state contains Bedrock block information + * @param entry The String -> JsonNode map used in BlockTranslator + * @param javaBlockState the Java Block State of the block + */ + public static void storeBlockStateValues(Map.Entry entry, BlockState javaBlockState) { + JsonNode bannerColor = entry.getValue().get("banner_color"); + if (bannerColor != null) { + BlockStateValues.BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); + return; // There will never be a banner color and a skull variant + } + + JsonNode bedColor = entry.getValue().get("bed_color"); + if (bedColor != null) { + BlockStateValues.BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); + return; + } + + JsonNode skullVariation = entry.getValue().get("variation"); + if(skullVariation != null) { + BlockStateValues.SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); + } + + JsonNode skullRotation = entry.getValue().get("skull_rotation"); + if (skullRotation != null) { + BlockStateValues.SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); + } + } + + /** + * Banner colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. + * This gives an integer color that Bedrock can use. + * @param state BlockState of the block + * @return banner color integer or -1 if no color + */ + public static int getBannerColor(BlockState state) { + if (BANNER_COLORS.containsKey(state)) { + return BANNER_COLORS.getInt(state); + } + return -1; + } + + /** + * Bed colors are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. + * This gives a byte color that Bedrock can use - Bedrock needs a byte in the final tag. + * @param state BlockState of the block + * @return bed color byte or -1 if no color + */ + public static byte getBedColor(BlockState state) { + if (BED_COLORS.containsKey(state)) { + return BED_COLORS.getByte(state); + } + return -1; + } + + /** + * Skull variations are part of the namespaced ID in Java Edition, but part of the block entity tag in Bedrock. + * This gives a byte variant ID that Bedrock can use. + * @param state BlockState of the block + * @return skull variant byte or -1 if no variant + */ + public static byte getSkullVariant(BlockState state) { + if (SKULL_VARIANTS.containsKey(state)) { + return SKULL_VARIANTS.getByte(state); + } + return -1; + } + + /** + * + * @param state BlockState of the block + * @return skull rotation value or -1 if no value + */ + public static byte getSkullRotation(BlockState state) { + if (SKULL_ROTATIONS.containsKey(state)) { + return SKULL_ROTATIONS.getByte(state); + } + return -1; + } + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java index d48b9a30..5b5d5c9c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java @@ -42,12 +42,12 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.Object2ByteMap; -import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.block.entity.BlockEntity; import org.geysermc.connector.utils.Toolbox; +import org.reflections.Reflections; import java.io.InputStream; import java.util.*; @@ -66,9 +66,6 @@ public class BlockTranslator { public static final int CARPET = 171; private static final Map JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>(); - private static final Object2ByteMap BED_COLORS = new Object2ByteOpenHashMap<>(); - private static final Object2ByteMap SKULL_VARIANTS = new Object2ByteOpenHashMap<>(); - private static final Object2ByteMap SKULL_ROTATIONS = new Object2ByteOpenHashMap<>(); public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap(); public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap(); @@ -110,6 +107,9 @@ public class BlockTranslator { addedStatesMap.defaultReturnValue(-1); List paletteList = new ArrayList<>(); + Reflections ref = new Reflections("org.geysermc.connector.network.translators.block.entity"); + ref.getTypesAnnotatedWith(BlockEntity.class); + int waterRuntimeId = -1; int javaRuntimeId = -1; int bedrockRuntimeId = 0; @@ -145,28 +145,19 @@ public class BlockTranslator { JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState); - if (javaId.contains("sign[")) { - JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaBlockState, javaId); + // Used for adding all "special" Java block states to block state map + String identifier; + String bedrock_identifer = entry.getValue().get("bedrock_identifier").asText(); + for (Class clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) { + identifier = clazz.getAnnotation(BlockEntity.class).regex(); + // Endswith, or else the block bedrock gets picked up for bed + if (bedrock_identifer.endsWith(identifier) && !identifier.equals("")) { + JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaBlockState, clazz.getAnnotation(BlockEntity.class).name()); + break; + } } - - JsonNode skullVariation = entry.getValue().get("variation"); - if(skullVariation != null) { - SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); - } - - JsonNode skullRotation = entry.getValue().get("skull_rotation"); - if (skullRotation != null) { - SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); - } - - // If the Java ID is bed, signal that it needs a tag to show color - // The color is in the namespace ID in Java Edition but it's a tag in Bedrock. - JsonNode bedColor = entry.getValue().get("bed_color"); - if (bedColor != null) { - // Converting to byte because the final tag value is a byte. bedColor.binaryValue() returns an array - BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); - } + BlockStateValues.storeBlockStateValues(entry, javaBlockState); if ("minecraft:water[level=0]".equals(javaId)) { waterRuntimeId = bedrockRuntimeId; @@ -186,7 +177,7 @@ public class BlockTranslator { addedStatesMap.put(blockTag, bedrockRuntimeId); paletteList.add(runtimeTag); } else { - int duplicateRuntimeId = addedStatesMap.get(blockTag); + int duplicateRuntimeId = addedStatesMap.getOrDefault(blockTag, -1); if (duplicateRuntimeId == -1) { GeyserConnector.getInstance().getLogger().debug("Mapping " + javaId + " was not found for bedrock edition!"); } else { @@ -274,27 +265,6 @@ public class BlockTranslator { return WATERLOGGED.contains(state.getId()); } - public static byte getBedColor(BlockState state) { - if (BED_COLORS.containsKey(state)) { - return BED_COLORS.getByte(state); - } - return -1; - } - - public static byte getSkullVariant(BlockState state) { - if (SKULL_VARIANTS.containsKey(state)) { - return SKULL_VARIANTS.getByte(state); - } - return -1; - } - - public static byte getSkullRotation(BlockState state) { - if (SKULL_ROTATIONS.containsKey(state)) { - return SKULL_ROTATIONS.getByte(state); - } - return -1; - } - public static BlockState getJavaWaterloggedState(int bedrockId) { return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java index cf868fa3..22d26cb4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BannerBlockEntityTranslator.java @@ -25,20 +25,33 @@ package org.geysermc.connector.network.translators.block.entity; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.IntTag; import com.nukkitx.nbt.tag.StringTag; import com.nukkitx.nbt.tag.Tag; +import org.geysermc.connector.network.translators.block.BlockStateValues; import java.util.ArrayList; import java.util.List; -public class BannerBlockEntityTranslator extends BlockEntityTranslator { +@BlockEntity(name = "Banner", delay = false, regex = "banner") +public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { @Override - public List> translateTag(CompoundTag tag) { + public boolean isBlock(BlockState blockState) { + return BlockStateValues.getBannerColor(blockState) != -1; + } + + @Override + public List> translateTag(CompoundTag tag, BlockState blockState) { List> tags = new ArrayList<>(); + int bannerColor = BlockStateValues.getBannerColor(blockState); + if (bannerColor != -1) { + tags.add(new IntTag("Base", 15 - bannerColor)); + } ListTag patterns = tag.get("Patterns"); List tagsList = new ArrayList<>(); if (tag.contains("Patterns")) { @@ -71,7 +84,7 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator { protected com.nukkitx.nbt.tag.CompoundTag getPattern(CompoundTag pattern) { return CompoundTagBuilder.builder() - .intTag("Color", (int) pattern.get("Color").getValue()) + .intTag("Color", 15 - (int) pattern.get("Color").getValue()) .stringTag("Pattern", (String) pattern.get("Pattern").getValue()) .buildRootTag(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java index 7522506a..a8dae253 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java @@ -25,42 +25,43 @@ package org.geysermc.connector.network.translators.block.entity; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; -import com.nukkitx.math.vector.Vector3i; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.CompoundTagBuilder; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.block.BlockTranslator; -import org.geysermc.connector.utils.BlockEntityUtils; +import com.nukkitx.nbt.tag.ByteTag; +import com.nukkitx.nbt.tag.Tag; +import org.geysermc.connector.network.translators.block.BlockStateValues; -import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.List; -public class BedBlockEntityTranslator { +@BlockEntity(name = "Bed", delay = false, regex = "bed") +public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - public static void checkForBedColor(GeyserSession session, BlockState blockState, Vector3i position) { - byte bedcolor = BlockTranslator.getBedColor(blockState); - // If Bed Color is not -1 then it is indeed a bed with a color. - if (bedcolor > -1) { - Position pos = new Position(position.getX(), position.getY(), position.getZ()); - com.nukkitx.nbt.tag.CompoundTag finalbedTag = getBedTag(bedcolor, pos); - // Delay needed, otherwise newly placed beds will not get their color - // Delay is not needed for beds already placed on login - session.getConnector().getGeneralThreadPool().schedule(() -> - BlockEntityUtils.updateBlockEntity(session, finalbedTag, pos), - 500, - TimeUnit.MILLISECONDS - ); - } + @Override + public boolean isBlock(BlockState blockState) { + return BlockStateValues.getBedColor(blockState) != -1; } - public static com.nukkitx.nbt.tag.CompoundTag getBedTag(byte bedcolor, Position pos) { - CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder() - .intTag("x", pos.getX()) - .intTag("y", pos.getY()) - .intTag("z", pos.getZ()) - .stringTag("id", "Bed"); - tagBuilder.byteTag("color", bedcolor); + @Override + public List> translateTag(CompoundTag tag, BlockState blockState) { + List> tags = new ArrayList<>(); + byte bedcolor = BlockStateValues.getBedColor(blockState); + // Just in case... + if (bedcolor == -1) bedcolor = 0; + tags.add(new ByteTag("color", bedcolor)); + return tags; + } + + @Override + public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { + return null; + } + + @Override + public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) { + CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder(); + tagBuilder.byteTag("color", (byte) 0); return tagBuilder.buildRootTag(); } - } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java new file mode 100644 index 00000000..0321f2c1 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntity.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 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.block.entity; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(value = RetentionPolicy.RUNTIME) +public @interface BlockEntity { + /** + * Whether to delay the sending of the block entity + */ + boolean delay(); + /** + * The block entity name + */ + String name(); + /** + * The search term used in BlockTranslator + */ + String regex(); +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java index d7683c4a..f2825789 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BlockEntityTranslator.java @@ -25,32 +25,32 @@ package org.geysermc.connector.network.translators.block.entity; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.nbt.CompoundTagBuilder; import com.nukkitx.nbt.tag.Tag; -import lombok.AllArgsConstructor; import org.geysermc.connector.utils.BlockEntityUtils; import java.util.List; public abstract class BlockEntityTranslator { - public abstract List> translateTag(CompoundTag tag); + public abstract List> translateTag(CompoundTag tag, BlockState blockState); public abstract CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z); public abstract com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z); - public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(String id, CompoundTag tag) { + public com.nukkitx.nbt.tag.CompoundTag getBlockEntityTag(String id, CompoundTag tag, BlockState 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())); CompoundTagBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(id), x, y, z).toBuilder(); - translateTag(tag).forEach(tagBuilder::tag); + translateTag(tag, blockState).forEach(tagBuilder::tag); return tagBuilder.buildRootTag(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java index ead43b06..11b7e864 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/CampfireBlockEntityTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.connector.network.translators.block.entity; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.nukkitx.nbt.CompoundTagBuilder; @@ -37,10 +38,11 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +@BlockEntity(name = "Campfire", delay = false, regex = "campfire") public class CampfireBlockEntityTranslator extends BlockEntityTranslator { @Override - public List> translateTag(CompoundTag tag) { + public List> translateTag(CompoundTag tag, BlockState blockState) { List> tags = new ArrayList<>(); ListTag items = tag.get("Items"); int i = 1; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/ContainerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/ContainerBlockEntityTranslator.java deleted file mode 100644 index 7eb3b1a7..00000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/ContainerBlockEntityTranslator.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2019-2020 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.block.entity; - -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.nukkitx.nbt.CompoundTagBuilder; -import com.nukkitx.nbt.tag.Tag; - -import org.geysermc.connector.network.translators.Translators; -import org.geysermc.connector.network.translators.item.ItemEntry; - -import java.util.ArrayList; -import java.util.List; - -public class ContainerBlockEntityTranslator extends BlockEntityTranslator { - - @Override - public List> translateTag(CompoundTag tag) { - List> tags = new ArrayList<>(); - ListTag items = tag.get("Items"); - List tagsList = new ArrayList<>(); - for (com.github.steveice10.opennbt.tag.builtin.Tag itemTag : items.getValue()) { - tagsList.add(getItem((CompoundTag) itemTag)); - } - - com.nukkitx.nbt.tag.ListTag bedrockItems = - new com.nukkitx.nbt.tag.ListTag<>("Items", com.nukkitx.nbt.tag.CompoundTag.class, tagsList); - tags.add(bedrockItems); - return tags; - } - - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - CompoundTag tag = getConstantJavaTag(javaId, x, y, z); - tag.put(new ListTag("Items")); - return tag; - } - - @Override - public com.nukkitx.nbt.tag.CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder(); - tagBuilder.listTag("Items", com.nukkitx.nbt.tag.CompoundTag.class, new ArrayList<>()); - return tagBuilder.buildRootTag(); - } - - protected com.nukkitx.nbt.tag.CompoundTag getItem(CompoundTag tag) { - ItemEntry entry = Translators.getItemTranslator().getItemEntry((String) tag.get("id").getValue()); - CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder() - .shortTag("id", (short) entry.getBedrockId()) - .byteTag("Count", (byte) tag.get("Count").getValue()) - .shortTag("Damage", (short) entry.getBedrockData()) - .byteTag("Slot", (byte) tag.get("Slot").getValue()) - .tag(CompoundTagBuilder.builder().build("tag")); - return tagBuilder.buildRootTag(); - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java index a523eab2..e4601400 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EmptyBlockEntityTranslator.java @@ -25,16 +25,18 @@ package org.geysermc.connector.network.translators.block.entity; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.tag.Tag; import java.util.ArrayList; import java.util.List; +@BlockEntity(name = "Empty", delay = false, regex = "") public class EmptyBlockEntityTranslator extends BlockEntityTranslator { @Override - public List> translateTag(CompoundTag tag) { + public List> translateTag(CompoundTag tag, BlockState blockState) { return new ArrayList<>(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java index e66a0bcd..de5868a4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/EndGatewayBlockEntityTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.connector.network.translators.block.entity; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.LongTag; import com.nukkitx.nbt.CompoundTagBuilder; @@ -35,9 +36,11 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +@BlockEntity(name = "EndGateway", delay = true, regex = "end_gateway") public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { + @Override - public List> translateTag(CompoundTag tag) { + public List> translateTag(CompoundTag tag, BlockState blockState) { List> tags = new ArrayList<>(); tags.add(new IntTag("Age", (int) (long) tag.get("Age").getValue())); // Java sometimes does not provide this tag, but Bedrock crashes if it doesn't exist diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java new file mode 100644 index 00000000..ed8e6ede --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/RequiresBlockState.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2020 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.block.entity; + +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; + +/** + * Implemented in block entities if their Java block state is required for additional values in Bedrock + */ +public interface RequiresBlockState { + + /** + * Determines if block is part of class + * @param blockState BlockState to be compared + * @return true if part of the class + */ + boolean isBlock(BlockState blockState); + +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java index 658f665b..74dcdd13 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SignBlockEntityTranslator.java @@ -25,21 +25,22 @@ package org.geysermc.connector.network.translators.block.entity; +import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; import com.github.steveice10.mc.protocol.data.message.Message; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.CompoundTagBuilder; import com.nukkitx.nbt.tag.StringTag; import com.nukkitx.nbt.tag.Tag; - import org.geysermc.connector.utils.MessageUtils; import java.util.ArrayList; import java.util.List; +@BlockEntity(name = "Sign", delay = true, regex = "sign") public class SignBlockEntityTranslator extends BlockEntityTranslator { @Override - public List> translateTag(CompoundTag tag) { + public List> translateTag(CompoundTag tag, BlockState blockState) { List> tags = new ArrayList<>(); String line1 = getOrDefault(tag.getValue().get("Text1"), ""); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java index 12526d8e..2380663b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/SkullBlockEntityTranslator.java @@ -25,43 +25,47 @@ package org.geysermc.connector.network.translators.block.entity; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.world.block.BlockState; -import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.CompoundTagBuilder; +import com.nukkitx.nbt.tag.ByteTag; import com.nukkitx.nbt.tag.CompoundTag; -import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.block.BlockTranslator; -import org.geysermc.connector.utils.BlockEntityUtils; +import com.nukkitx.nbt.tag.FloatTag; +import com.nukkitx.nbt.tag.Tag; +import org.geysermc.connector.network.translators.block.BlockStateValues; -import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.List; -public class SkullBlockEntityTranslator { +@BlockEntity(name = "Skull", delay = false, regex = "skull") +public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { - public static void checkForSkullVariant(GeyserSession session, BlockState blockState, Vector3i position) { - byte skullVariant = BlockTranslator.getSkullVariant(blockState); - byte rotation = BlockTranslator.getSkullRotation(blockState); - if (skullVariant > -1) { - Position pos = new Position(position.getX(), position.getY(), position.getZ()); - CompoundTag finalSkullTag = getSkullTag(skullVariant, pos, rotation); - // Delay needed, otherwise newly placed skulls will not appear - // Delay is not needed for skulls already placed on login - session.getConnector().getGeneralThreadPool().schedule(() -> - BlockEntityUtils.updateBlockEntity(session, finalSkullTag, pos), - 500, - TimeUnit.MILLISECONDS - ); - } + @Override + public boolean isBlock(BlockState blockState) { + return BlockStateValues.getSkullVariant(blockState) != -1; } - public static CompoundTag getSkullTag(byte skullvariant, Position pos, byte rotation) { - CompoundTagBuilder tagBuilder = CompoundTagBuilder.builder() - .intTag("x", pos.getX()) - .intTag("y", pos.getY()) - .intTag("z", pos.getZ()) - .stringTag("id", "Skull") - .floatTag("Rotation", rotation * 22.5f); - tagBuilder.byteTag("SkullType", skullvariant); + @Override + public List> translateTag(com.github.steveice10.opennbt.tag.builtin.CompoundTag tag, BlockState blockState) { + List> tags = new ArrayList<>(); + byte skullVariant = BlockStateValues.getSkullVariant(blockState); + float rotation = BlockStateValues.getSkullRotation(blockState) * 22.5f; + // Just in case... + if (skullVariant == -1) skullVariant = 0; + tags.add(new FloatTag("Rotation", rotation)); + tags.add(new ByteTag("SkullType", skullVariant)); + return tags; + } + + @Override + public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { + return null; + } + + @Override + public CompoundTag getDefaultBedrockTag(String bedrockId, int x, int y, int z) { + CompoundTagBuilder tagBuilder = getConstantBedrockTag(bedrockId, x, y, z).toBuilder(); + tagBuilder.floatTag("Rotation", 0); + tagBuilder.byteTag("SkullType", (byte) 0); return tagBuilder.buildRootTag(); } } 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 9309ca75..e72038c5 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 @@ -85,7 +85,7 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry : chunkData.signs.object2IntEntrySet()) { + // Some block entities need to be loaded in later or else text doesn't show (signs) or they crash the game (end gateway blocks) + for (Object2IntMap.Entry blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) { int x = blockEntityEntry.getKey().getInt("x"); int y = blockEntityEntry.getKey().getInt("y"); int z = blockEntityEntry.getKey().getInt("z"); ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntValue()), new Position(x, y, z)); } + chunkData.getLoadBlockEntitiesLater().clear(); - for (Object2IntMap.Entry blockEntityEntry : chunkData.gateways.object2IntEntrySet()) { - int x = blockEntityEntry.getKey().getInt("x"); - int y = blockEntityEntry.getKey().getInt("y"); - int z = blockEntityEntry.getKey().getInt("z"); - ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntValue()), new Position(x, y, z)); - } - - for (Map.Entry blockEntityEntry: chunkData.beds.entrySet()) { - ChunkUtils.updateBlock(session, blockEntityEntry.getValue(), blockEntityEntry.getKey()); - } - for (Map.Entry blockEntityEntry: chunkData.skulls.entrySet()) { - ChunkUtils.updateBlock(session, blockEntityEntry.getValue(), blockEntityEntry.getKey()); - } - chunkData.signs.clear(); - chunkData.gateways.clear(); - chunkData.beds.clear(); - chunkData.skulls.clear(); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java index e6b5a335..b1158ef1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTileEntityTranslator.java @@ -30,27 +30,35 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdate 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.block.entity.BlockEntity; import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator; import org.geysermc.connector.utils.BlockEntityUtils; +import org.geysermc.connector.utils.ChunkUtils; import java.util.concurrent.TimeUnit; @Translator(packet = ServerUpdateTileEntityPacket.class) public class JavaUpdateTileEntityTranslator extends PacketTranslator { + // This should be modified if sign text is not showing up + private static final int DELAY = 500; + @Override public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name()); BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id); - if (id.equalsIgnoreCase("Sign")) { + // If not null then the BlockState is used in BlockEntityTranslator.translateTag() + if (ChunkUtils.CACHED_BLOCK_ENTITIES.get(packet.getPosition()) != null) { + BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), + ChunkUtils.CACHED_BLOCK_ENTITIES.get(packet.getPosition())), packet.getPosition()); + ChunkUtils.CACHED_BLOCK_ENTITIES.remove(packet.getPosition()); + } else if (translator.getClass().getAnnotation(BlockEntity.class).delay()) { // Delay so chunks can finish sending session.getConnector().getGeneralThreadPool().schedule(() -> - BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag("Sign", packet.getNbt()), packet.getPosition()), - 5, - TimeUnit.SECONDS - ); + BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), null), packet.getPosition()), + DELAY, TimeUnit.MILLISECONDS); } else { - BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt()), packet.getPosition()); + BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), null), packet.getPosition()); } } } 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 c75664b8..d496215a 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -39,14 +39,13 @@ import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import lombok.Getter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.block.entity.BlockEntityTranslator; -import org.geysermc.connector.network.translators.block.entity.SkullBlockEntityTranslator; -import org.geysermc.connector.world.chunk.ChunkPosition; +import org.geysermc.connector.network.translators.block.entity.*; import org.geysermc.connector.network.translators.Translators; import org.geysermc.connector.network.translators.block.BlockTranslator; -import org.geysermc.connector.network.translators.block.entity.BedBlockEntityTranslator; +import org.geysermc.connector.world.chunk.ChunkPosition; import org.geysermc.connector.world.chunk.ChunkSection; import java.util.HashMap; @@ -56,12 +55,20 @@ import static org.geysermc.connector.network.translators.block.BlockTranslator.B public class ChunkUtils { + /** + * Temporarily stores positions of BlockState values that are needed for certain block entities actively + */ + public static final Map CACHED_BLOCK_ENTITIES = new HashMap<>(); + public static ChunkData translateToBedrock(Column column) { ChunkData chunkData = new ChunkData(); Chunk[] chunks = column.getChunks(); chunkData.sections = new ChunkSection[chunks.length]; CompoundTag[] blockEntities = column.getTileEntities(); + // Temporarily stores positions of BlockState values per chunk load + Map blockEntityPositions = new HashMap<>(); + for (int chunkY = 0; chunkY < chunks.length; chunkY++) { chunkData.sections[chunkY] = new ChunkSection(); Chunk chunk = chunks[chunkY]; @@ -76,21 +83,19 @@ public class ChunkUtils { BlockState blockState = chunk.get(x, y, z); int id = BlockTranslator.getBedrockBlockId(blockState); - if (BlockTranslator.getBlockEntityString(blockState) != null && BlockTranslator.getBlockEntityString(blockState).contains("sign[")) { + // Check to see if the name is in BlockTranslator.getBlockEntityString, and therefore must be handled differently + if (BlockTranslator.getBlockEntityString(blockState) != null) { + // Get the block entity translator + BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(BlockTranslator.getBlockEntityString(blockState)); Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); - chunkData.signs.put(Translators.getBlockEntityTranslators().get("Sign").getDefaultBedrockTag("Sign", pos.getX(), pos.getY(), pos.getZ()), blockState.getId()); - } else if (BlockTranslator.getBlockEntityString(blockState) != null && BlockTranslator.getBlockEntityString(blockState).contains("end_gateway")) { - Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); - chunkData.gateways.put(Translators.getBlockEntityTranslators().get("EndGateway").getDefaultBedrockTag("EndGateway", pos.getX(), pos.getY(), pos.getZ()), blockState.getId()); - } else if (BlockTranslator.getBedColor(blockState) > -1) { - Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); - // Beds need to be updated separately to add the bed color tag - // Previously this was done by matching block state but this resulted in only one bed per color+orientation showing - chunkData.beds.put(pos, blockState); - } else if (BlockTranslator.getSkullVariant(blockState) > 0) { - Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); - //Doing the same stuff as beds - chunkData.skulls.put(pos, blockState); + blockEntityPositions.put(pos, blockState); + // If there is a delay required for the block, allow it. + if (blockEntityTranslator.getClass().getAnnotation(BlockEntity.class).delay()) { + chunkData.loadBlockEntitiesLater.put(blockEntityTranslator.getDefaultBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(BlockTranslator.getBlockEntityString(blockState)), + pos.getX(), pos.getY(), pos.getZ()), blockState.getId()); + } else { + section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id); + } } else { section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id); } @@ -101,6 +106,7 @@ public class ChunkUtils { } } } + } com.nukkitx.nbt.tag.CompoundTag[] bedrockBlockEntities = new com.nukkitx.nbt.tag.CompoundTag[blockEntities.length]; @@ -116,7 +122,8 @@ public class ChunkUtils { String id = BlockEntityUtils.getBedrockBlockEntityId(tagName); BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id); - bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag); + BlockState blockState = blockEntityPositions.get(new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue())); + bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag, blockState); } chunkData.blockEntities = bedrockBlockEntities; @@ -162,10 +169,18 @@ public class ChunkUtils { } session.getUpstream().sendPacket(waterPacket); - // Since Java stores bed colors as part of the namespaced ID and Bedrock stores it as a tag + // Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag // This is the only place I could find that interacts with the Java block state and block updates - BedBlockEntityTranslator.checkForBedColor(session, blockState, position); - SkullBlockEntityTranslator.checkForSkullVariant(session, blockState, position); + // Iterates through all block entity translators and determines if the block state needs to be saved + for (Map.Entry entry : Translators.getBlockEntityTranslators().entrySet()) { + if (entry.getValue() instanceof RequiresBlockState) { + RequiresBlockState requiresBlockState = (RequiresBlockState) entry.getValue(); + if (requiresBlockState.isBlock(blockState)) { + CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState); + break; //No block will be a part of two classes + } + } + } } public static void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) { @@ -196,10 +211,9 @@ public class ChunkUtils { public static final class ChunkData { public ChunkSection[] sections; - public com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0]; - public Object2IntMap signs = new Object2IntOpenHashMap<>(); - public Object2IntMap gateways = new Object2IntOpenHashMap<>(); - public Map beds = new HashMap<>(); - public Map skulls = new HashMap<>(); + @Getter + private com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0]; + @Getter + private Object2IntMap loadBlockEntitiesLater = new Object2IntOpenHashMap<>(); } }