diff --git a/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java b/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java index 4bbccb467..0fa09f902 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/data/ChunkData.java @@ -2,19 +2,16 @@ package net.minestom.server.network.packet.server.play.data; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.block.Block; -import net.minestom.server.instance.block.BlockHandler; -import net.minestom.server.tag.Tag; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.Writeable; +import net.minestom.server.utils.block.BlockUtils; import net.minestom.server.utils.chunk.ChunkUtils; import org.jetbrains.annotations.NotNull; -import org.jglrxavpok.hephaistos.nbt.NBT; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data, @@ -47,33 +44,13 @@ public record ChunkData(@NotNull NBTCompound heightmaps, byte @NotNull [] data, final var registry = block.registry(); final Point point = ChunkUtils.getBlockPosition(index, 0, 0); - writer.writeByte((byte) ((point.blockX() & 15) << 4 | point.blockZ() & 15)); // xz writer.writeShort((short) point.blockY()); // y writer.writeVarInt(registry.blockEntityId()); - - NBTCompound resultNbt; - // Append handler tags - final BlockHandler handler = block.handler(); - if (handler != null) { - resultNbt = NBT.Compound(nbt -> { - final NBTCompound blockNbt = Objects.requireNonNullElseGet(block.nbt(), NBTCompound::new); - for (Tag tag : handler.getBlockEntityTags()) { - final var value = tag.read(blockNbt); - if (value != null) { - // Tag is present and valid - tag.writeUnsafe(nbt, value); - } - } - }); - } else { - // Complete nbt shall be sent if the block has no handler - // Necessary to support all vanilla blocks - final NBTCompound blockNbt = block.nbt(); - resultNbt = blockNbt == null ? new NBTCompound() : blockNbt; - } - writer.writeNBT("", resultNbt); // block nbt + final NBTCompound nbt = BlockUtils.extractClientNbt(block); + assert nbt != null; + writer.writeNBT("", nbt); // block nbt } } diff --git a/src/main/java/net/minestom/server/utils/block/BlockUtils.java b/src/main/java/net/minestom/server/utils/block/BlockUtils.java index 641debce1..e3640f8fd 100644 --- a/src/main/java/net/minestom/server/utils/block/BlockUtils.java +++ b/src/main/java/net/minestom/server/utils/block/BlockUtils.java @@ -3,10 +3,17 @@ package net.minestom.server.utils.block; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.Instance; import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.BlockHandler; +import net.minestom.server.tag.Tag; import net.minestom.server.utils.ArrayUtils; import net.minestom.server.utils.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.Map; +import java.util.Objects; public class BlockUtils { @@ -83,4 +90,26 @@ public class BlockUtils { } return ArrayUtils.toMap(keys, values, entryCount); } + + public static @Nullable NBTCompound extractClientNbt(@NotNull Block block) { + if (!block.registry().isBlockEntity()) return null; + // Append handler tags + final BlockHandler handler = block.handler(); + final NBTCompound blockNbt = Objects.requireNonNullElseGet(block.nbt(), NBTCompound::new); + if (handler != null) { + // Extract explicitly defined tags and keep the rest server-side + return NBT.Compound(nbt -> { + for (Tag tag : handler.getBlockEntityTags()) { + final var value = tag.read(blockNbt); + if (value != null) { + // Tag is present and valid + tag.writeUnsafe(nbt, value); + } + } + }); + } + // Complete nbt shall be sent if the block has no handler + // Necessary to support all vanilla blocks + return blockNbt; + } } diff --git a/src/test/java/net/minestom/server/instance/BlockClientNbtTest.java b/src/test/java/net/minestom/server/instance/BlockClientNbtTest.java new file mode 100644 index 000000000..4a7d56a62 --- /dev/null +++ b/src/test/java/net/minestom/server/instance/BlockClientNbtTest.java @@ -0,0 +1,50 @@ +package net.minestom.server.instance; + +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.block.BlockHandler; +import net.minestom.server.tag.Tag; +import net.minestom.server.utils.NamespaceID; +import net.minestom.server.utils.block.BlockUtils; +import org.jetbrains.annotations.NotNull; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import org.junit.jupiter.api.Test; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class BlockClientNbtTest { + + @Test + public void basic() { + assertNull(BlockUtils.extractClientNbt(Block.STONE)); + assertNull(BlockUtils.extractClientNbt(Block.GRASS)); + assertEquals(NBTCompound.EMPTY, BlockUtils.extractClientNbt(Block.CHEST)); + + var nbt = NBT.Compound(Map.of("test", NBT.String("test"))); + assertEquals(nbt, BlockUtils.extractClientNbt(Block.CHEST.withNbt(nbt))); + } + + @Test + public void handler() { + var handler = new BlockHandler() { + @Override + public @NotNull Collection> getBlockEntityTags() { + return List.of(Tag.String("test")); + } + + @Override + public @NotNull NamespaceID getNamespaceId() { + return NamespaceID.from("minestom:test"); + } + }; + + var nbt = NBT.Compound(Map.of("test", NBT.String("test"))); + assertNull(BlockUtils.extractClientNbt(Block.STONE.withNbt(nbt).withHandler(handler))); + assertEquals(nbt, BlockUtils.extractClientNbt(Block.CHEST.withNbt(nbt).withHandler(handler))); + } +}