diff --git a/connector/pom.xml b/connector/pom.xml
index 112e104b..a14fcfc4 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -66,6 +66,12 @@
8.3.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 5132e90d..137edaff 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<>();
public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap();
@@ -130,6 +132,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;
}
@@ -231,6 +241,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 00000000..42865918
--- /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 b9fca124..029a6f6f 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 8da00fa3..340edd01 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 278c7344..efc9db6b 160000
--- a/connector/src/main/resources/mappings
+++ b/connector/src/main/resources/mappings
@@ -1 +1 @@
-Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a
+Subproject commit efc9db6b7d51bdf145230933ac23b321ac1c132d