forked from GeyserMC/Geyser
Implement bed colors
Java Edition includes the bed color in the namespaced ID; in Bedrock edition it's one of the tag values as a block entity. This code involves creating a table between block states and bed color numbers and looking that up on chunk load or block update.
This commit is contained in:
parent
1bcee8d36f
commit
5301c8c3f6
3 changed files with 95 additions and 0 deletions
|
@ -50,6 +50,7 @@ public class BlockTranslator {
|
||||||
private static final Int2ObjectMap<BlockState> BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<BlockState> BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Map<String, BlockState> JAVA_ID_BLOCK_MAP = new HashMap<>();
|
private static final Map<String, BlockState> JAVA_ID_BLOCK_MAP = new HashMap<>();
|
||||||
private static final IntSet WATERLOGGED = new IntOpenHashSet();
|
private static final IntSet WATERLOGGED = new IntOpenHashSet();
|
||||||
|
private static final Map<BlockState, Byte> BEDCOLORS = new HashMap<>();
|
||||||
|
|
||||||
private static final Map<BlockState, String> JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
|
private static final Map<BlockState, String> JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
|
||||||
|
|
||||||
|
@ -101,6 +102,50 @@ public class BlockTranslator {
|
||||||
JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaBlockState, javaId);
|
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.
|
||||||
|
byte bedcolor = -1;
|
||||||
|
if (javaId.contains("_bed[")) {
|
||||||
|
if (javaId.contains("minecraft:white")) {
|
||||||
|
bedcolor = 0;
|
||||||
|
} else if (javaId.contains("minecraft:orange")) {
|
||||||
|
bedcolor = 1;
|
||||||
|
} else if (javaId.contains("minecraft:magenta")) {
|
||||||
|
bedcolor = 2;
|
||||||
|
} else if (javaId.contains("minecraft:light_blue")) {
|
||||||
|
bedcolor = 3;
|
||||||
|
} else if (javaId.contains("minecraft:yellow")) {
|
||||||
|
bedcolor = 4;
|
||||||
|
} else if (javaId.contains("minecraft:lime")) {
|
||||||
|
bedcolor = 5;
|
||||||
|
} else if (javaId.contains("minecraft:pink")) {
|
||||||
|
bedcolor = 6;
|
||||||
|
} else if (javaId.contains("minecraft:gray")) {
|
||||||
|
bedcolor = 7;
|
||||||
|
} else if (javaId.contains("minecraft:light_gray")) {
|
||||||
|
bedcolor = 8;
|
||||||
|
} else if (javaId.contains("minecraft:cyan")) {
|
||||||
|
bedcolor = 9;
|
||||||
|
} else if (javaId.contains("minecraft:purple")) {
|
||||||
|
bedcolor = 10;
|
||||||
|
} else if (javaId.contains("minecraft:blue")) {
|
||||||
|
bedcolor = 11;
|
||||||
|
} else if (javaId.contains("minecraft:brown")) {
|
||||||
|
bedcolor = 12;
|
||||||
|
} else if (javaId.contains("minecraft:green")) {
|
||||||
|
bedcolor = 13;
|
||||||
|
} else if (javaId.contains("minecraft:red")) {
|
||||||
|
bedcolor = 14;
|
||||||
|
} else if (javaId.contains("minecraft:black")) {
|
||||||
|
bedcolor = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// -1 is used throughout the code to indicate no bed color.
|
||||||
|
if (bedcolor > -1) {
|
||||||
|
BEDCOLORS.put(javaBlockState, bedcolor);
|
||||||
|
}
|
||||||
|
|
||||||
if ("minecraft:water[level=0]".equals(javaId)) {
|
if ("minecraft:water[level=0]".equals(javaId)) {
|
||||||
waterRuntimeId = bedrockRuntimeId;
|
waterRuntimeId = bedrockRuntimeId;
|
||||||
}
|
}
|
||||||
|
@ -197,6 +242,13 @@ public class BlockTranslator {
|
||||||
return WATERLOGGED.contains(state.getId());
|
return WATERLOGGED.contains(state.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte getBedColor(BlockState state) {
|
||||||
|
if (BEDCOLORS.containsKey(state)) {
|
||||||
|
return BEDCOLORS.get(state);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public static BlockState getJavaWaterloggedState(int bedrockId) {
|
public static BlockState getJavaWaterloggedState(int bedrockId) {
|
||||||
return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId);
|
return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,16 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
||||||
|
|
||||||
ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntKey()), new Position(x, y, z));
|
ChunkUtils.updateBlock(session, new BlockState(blockEntityEntry.getIntKey()), new Position(x, y, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (Int2ObjectMap.Entry<CompoundTag> 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.signs.clear();
|
||||||
|
chunkData.beds.clear();
|
||||||
} else {
|
} else {
|
||||||
final int xOffset = packet.getColumn().getX() << 4;
|
final int xOffset = packet.getColumn().getX() << 4;
|
||||||
final int zOffset = packet.getColumn().getZ() << 4;
|
final int zOffset = packet.getColumn().getZ() << 4;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
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.CompoundTag;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
|
import com.nukkitx.nbt.CompoundTagBuilder;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
|
|
||||||
|
@ -45,6 +46,8 @@ import org.geysermc.connector.world.chunk.ChunkPosition;
|
||||||
import org.geysermc.connector.network.translators.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.block.BlockTranslator;
|
||||||
import org.geysermc.connector.world.chunk.ChunkSection;
|
import org.geysermc.connector.world.chunk.ChunkSection;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
|
import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
|
||||||
|
|
||||||
public class ChunkUtils {
|
public class ChunkUtils {
|
||||||
|
@ -72,6 +75,9 @@ public class ChunkUtils {
|
||||||
if (BlockTranslator.getBlockEntityString(blockState) != null && BlockTranslator.getBlockEntityString(blockState).contains("sign[")) {
|
if (BlockTranslator.getBlockEntityString(blockState) != null && BlockTranslator.getBlockEntityString(blockState).contains("sign[")) {
|
||||||
Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z);
|
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()));
|
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(), getBedTag(BlockTranslator.getBedColor(blockState), pos));
|
||||||
} else {
|
} else {
|
||||||
section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
|
section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
|
||||||
}
|
}
|
||||||
|
@ -79,6 +85,7 @@ public class ChunkUtils {
|
||||||
if (BlockTranslator.isWaterlogged(blockState)) {
|
if (BlockTranslator.isWaterlogged(blockState)) {
|
||||||
section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID);
|
section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,6 +135,23 @@ public class ChunkUtils {
|
||||||
waterPacket.setRuntimeId(0);
|
waterPacket.setRuntimeId(0);
|
||||||
}
|
}
|
||||||
session.getUpstream().sendPacket(waterPacket);
|
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
|
||||||
|
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 void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) {
|
public static void sendEmptyChunks(GeyserSession session, Vector3i position, int radius, boolean forceUpdate) {
|
||||||
|
@ -160,5 +184,15 @@ public class ChunkUtils {
|
||||||
|
|
||||||
public com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0];
|
public com.nukkitx.nbt.tag.CompoundTag[] blockEntities = new com.nukkitx.nbt.tag.CompoundTag[0];
|
||||||
public Int2ObjectMap<com.nukkitx.nbt.tag.CompoundTag> signs = new Int2ObjectOpenHashMap<>();
|
public Int2ObjectMap<com.nukkitx.nbt.tag.CompoundTag> signs = new Int2ObjectOpenHashMap<>();
|
||||||
|
public Int2ObjectMap<com.nukkitx.nbt.tag.CompoundTag> beds = new Int2ObjectOpenHashMap<>();
|
||||||
|
}
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue