diff --git a/connector/pom.xml b/connector/pom.xml index 6d56fbcad..0258e9a30 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -48,6 +48,12 @@ 8.1.1 compile + + com.nukkitx.fastutil + fastutil-object-byte-maps + 8.3.1 + compile + com.github.steveice10 opennbt 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 18ac3e6dc..908b88a8e 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 @@ -35,6 +35,7 @@ import com.nukkitx.nbt.tag.ListTag; import gnu.trove.map.TObjectIntMap; import gnu.trove.map.hash.TObjectIntHashMap; import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.*; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.Toolbox; @@ -50,6 +51,7 @@ public class BlockTranslator { private static final Int2ObjectMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>(); private static final Map JAVA_ID_BLOCK_MAP = new HashMap<>(); private static final IntSet WATERLOGGED = new IntOpenHashSet(); + private static final Object2ByteOpenHashMap BED_COLORS = new Object2ByteOpenHashMap<>(); private static final Map JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>(); @@ -101,6 +103,14 @@ public class BlockTranslator { JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaBlockState, javaId); } + // 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()); + } + if ("minecraft:water[level=0]".equals(javaId)) { waterRuntimeId = bedrockRuntimeId; } @@ -197,6 +207,13 @@ 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 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/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java new file mode 100644 index 000000000..42865918b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/entity/BedBlockEntityTranslator.java @@ -0,0 +1,41 @@ +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 org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.block.BlockTranslator; +import org.geysermc.connector.utils.BlockEntityUtils; + +import java.util.concurrent.TimeUnit; + +public class BedBlockEntityTranslator { + + 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 + ); + } + } + + 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); + 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 b9fca124a..029a6f6f3 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 @@ -105,7 +105,16 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry: chunkData.beds.int2ObjectEntrySet()) { + int x = blockEntityEntry.getValue().getInt("x"); + int y = blockEntityEntry.getValue().getInt("y"); + int z = blockEntityEntry.getValue().getInt("z"); + + ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntKey()), new Position(x, y, z)); + } chunkData.signs.clear(); + chunkData.beds.clear(); } else { final int xOffset = packet.getColumn().getX() << 4; final int zOffset = packet.getColumn().getZ() << 4; 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 8da00fa32..340edd01a 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -44,6 +44,7 @@ import org.geysermc.connector.network.translators.block.entity.BlockEntityTransl import org.geysermc.connector.world.chunk.ChunkPosition; import org.geysermc.connector.network.translators.block.BlockTranslator; import org.geysermc.connector.world.chunk.ChunkSection; +import org.geysermc.connector.network.translators.block.entity.BedBlockEntityTranslator; import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID; @@ -72,6 +73,9 @@ public class ChunkUtils { if (BlockTranslator.getBlockEntityString(blockState) != null && BlockTranslator.getBlockEntityString(blockState).contains("sign[")) { Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); chunkData.signs.put(blockState.getId(), TranslatorsInit.getBlockEntityTranslators().get("Sign").getDefaultBedrockTag("Sign", pos.getX(), pos.getY(), pos.getZ())); + } else if (BlockTranslator.getBedColor(blockState) > -1) { + Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); + chunkData.beds.put(blockState.getId(), BedBlockEntityTranslator.getBedTag(BlockTranslator.getBedColor(blockState), pos)); } else { section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id); } @@ -128,6 +132,10 @@ public class ChunkUtils { waterPacket.setRuntimeId(0); } session.getUpstream().sendPacket(waterPacket); + + // Since Java stores bed colors 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); } public static void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) { @@ -160,5 +168,6 @@ public class ChunkUtils { public com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0]; public Int2ObjectMap signs = new Int2ObjectOpenHashMap<>(); + public Int2ObjectMap beds = new Int2ObjectOpenHashMap<>(); } } diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 278c73449..efc9db6b7 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a +Subproject commit efc9db6b7d51bdf145230933ac23b321ac1c132d