diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index 953f6861c..38dfb21e0 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -60,7 +60,7 @@ public class DynamicChunk extends Chunk { public void setBlock(int x, int y, int z, @NotNull Block block) { final short blockStateId = block.getStateId(); final BlockHandler handler = block.getHandler(); - final NBTCompound nbt = null; // TODO + final NBTCompound nbt = block.getNbt(); // TODO final boolean updatable = false; // TODO { // Update pathfinder diff --git a/src/main/java/net/minestom/server/instance/block/Block.java b/src/main/java/net/minestom/server/instance/block/Block.java index 048ea51e5..a10dcc363 100644 --- a/src/main/java/net/minestom/server/instance/block/Block.java +++ b/src/main/java/net/minestom/server/instance/block/Block.java @@ -36,6 +36,8 @@ public interface Block extends ProtocolObject, TagReadable, BlockConstants { @NotNull String getProperty(@NotNull String property); + @Nullable NBTCompound getNbt(); + @Nullable BlockHandler getHandler(); @NotNull Block getDefaultBlock(); diff --git a/src/main/java/net/minestom/server/instance/block/BlockHandler.java b/src/main/java/net/minestom/server/instance/block/BlockHandler.java index 1536f9ed9..247d9caf9 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockHandler.java +++ b/src/main/java/net/minestom/server/instance/block/BlockHandler.java @@ -3,10 +3,14 @@ package net.minestom.server.instance.block; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; import net.minestom.server.instance.Instance; +import net.minestom.server.tag.Tag; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; + /** * Interface used to provide block behavior. Set with {@link Block#withHandler(BlockHandler)}. *

@@ -51,6 +55,14 @@ public interface BlockHandler { default void handleContact(@NotNull Instance instance, @NotNull BlockPosition position, @NotNull Entity touching) { } + default @NotNull Collection> getBlockEntityTags() { + return Collections.emptyList(); + } + + default byte getBlockEntityAction() { + return -1; + } + /** * Gets the id of this handler. *

diff --git a/src/main/java/net/minestom/server/instance/block/BlockImpl.java b/src/main/java/net/minestom/server/instance/block/BlockImpl.java index 2bd28fc28..534643e19 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockImpl.java +++ b/src/main/java/net/minestom/server/instance/block/BlockImpl.java @@ -132,6 +132,11 @@ class BlockImpl implements Block { return null; } + @Override + public @Nullable NBTCompound getNbt() { + return compound != null ? compound.deepClone() : null; + } + @Override public @NotNull Block getDefaultBlock() { return original; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java index d68689311..689899c6b 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java @@ -9,6 +9,7 @@ import net.minestom.server.instance.Section; import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.tag.Tag; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Utils; import net.minestom.server.utils.binary.BinaryReader; @@ -23,10 +24,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTException; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; public class ChunkDataPacket implements ServerPacket, CacheablePacket { @@ -130,25 +128,35 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket { if (handlerMap == null || handlerMap.isEmpty()) { writer.writeVarInt(0); } else { - writer.writeVarInt(handlerMap.size()); - + List compounds = new ArrayList<>(); for (var entry : handlerMap.entrySet()) { final int index = entry.getKey(); final BlockHandler handler = entry.getValue(); - final BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ); + final var blockEntityTags = handler.getBlockEntityTags(); + if (blockEntityTags.isEmpty()) + continue; + final var blockNbt = Objects.requireNonNullElseGet(nbtMap.get(index), NBTCompound::new); + final var resultNbt = new NBTCompound(); - NBTCompound nbt; - if (nbtMap != null) { - nbt = Objects.requireNonNullElseGet(nbtMap.get(index), NBTCompound::new); - } else { - nbt = new NBTCompound(); + for (Tag tag : blockEntityTags) { + final var value = tag.read(blockNbt); + if (value != null) { + // Tag is present and valid + tag.writeUnsafe(resultNbt, value); + } + } + + if (resultNbt.getSize() > 0) { + final BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ); + resultNbt.setString("id", handler.getNamespaceId().asString()) + .setInt("x", blockPosition.getX()) + .setInt("y", blockPosition.getY()) + .setInt("z", blockPosition.getZ()); + compounds.add(resultNbt); } - nbt.setString("id", handler.getNamespaceId().asString()) - .setInt("x", blockPosition.getX()) - .setInt("y", blockPosition.getY()) - .setInt("z", blockPosition.getZ()); - writer.writeNBT("", nbt); } + writer.writeVarInt(compounds.size()); + compounds.forEach(nbtCompound -> writer.writeNBT("", nbtCompound)); } } diff --git a/src/main/java/net/minestom/server/tag/Tag.java b/src/main/java/net/minestom/server/tag/Tag.java index 63e054446..b89aaed6f 100644 --- a/src/main/java/net/minestom/server/tag/Tag.java +++ b/src/main/java/net/minestom/server/tag/Tag.java @@ -101,6 +101,10 @@ public class Tag { } } + public void writeUnsafe(@NotNull NBTCompound nbtCompound, @Nullable Object value) { + write(nbtCompound, (T) value); + } + public static @NotNull Tag Byte(@NotNull String key) { return new Tag<>(key, nbtCompound -> nbtCompound.getByte(key), @@ -178,20 +182,7 @@ public class Tag { public static @NotNull Tag Custom(@NotNull String key, @NotNull TagSerializer serializer) { return new Tag<>(key, - nbtCompound -> { - final var compound = nbtCompound.getCompound(key); - if (compound == null) { - return null; - } - return serializer.read(TagReadable.fromCompound(compound)); - }, - (nbtCompound, value) -> { - var compound = nbtCompound.getCompound(key); - if (compound == null) { - compound = new NBTCompound(); - nbtCompound.set(key, compound); - } - serializer.write(TagWritable.fromCompound(compound), value); - }); + nbtCompound -> serializer.read(TagReadable.fromCompound(nbtCompound)), + (nbtCompound, value) -> serializer.write(TagWritable.fromCompound(nbtCompound), value)); } } diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 655f0fa7c..d5dc3acbf 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -1,5 +1,6 @@ package demo; +import demo.block.CampfireHandler; import demo.generator.ChunkGeneratorDemo; import demo.generator.NoiseTestGenerator; import net.kyori.adventure.text.Component; @@ -37,10 +38,7 @@ import net.minestom.server.utils.Vector; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.world.DimensionType; -import java.util.Collection; -import java.util.Collections; -import java.util.Random; -import java.util.Set; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; @@ -58,6 +56,10 @@ public class PlayerInit { instanceContainer.enableAutoChunkLoad(true); instanceContainer.setChunkGenerator(chunkGeneratorDemo); + instanceContainer.setBlock(0, 45, 3, Block.CAMPFIRE + .withHandler(new CampfireHandler()) + .withTag(CampfireHandler.ITEMS, List.of(ItemStack.of(Material.DIAMOND, 1)))); + inventory = new Inventory(InventoryType.CHEST_1_ROW, Component.text("Test inventory")); /*inventory.addInventoryCondition((p, slot, clickType, inventoryConditionResult) -> { p.sendMessage("click type inventory: " + clickType); @@ -204,13 +206,6 @@ public class PlayerInit { int x = Math.abs(ThreadLocalRandom.current().nextInt()) % 500 - 250; int z = Math.abs(ThreadLocalRandom.current().nextInt()) % 500 - 250; player.setRespawnPoint(new Position(0, 42f, 0)); - - player.getInventory().addInventoryCondition((p, slot, clickType, inventoryConditionResult) -> { - if (slot == -999) - return; - //ItemStack itemStack = p.getInventory().getItemStack(slot); - //System.out.println("test " + itemStack.getIdentifier() + " " + itemStack.getData()); - }); }); globalEventHandler.addEventCallback(PlayerSpawnEvent.class, event -> { diff --git a/src/test/java/demo/block/CampfireHandler.java b/src/test/java/demo/block/CampfireHandler.java new file mode 100644 index 000000000..d0ac02a1b --- /dev/null +++ b/src/test/java/demo/block/CampfireHandler.java @@ -0,0 +1,84 @@ +package demo.block; + +import net.minestom.server.entity.Player; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.BlockHandler; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.registry.Registries; +import net.minestom.server.tag.Tag; +import net.minestom.server.tag.TagReadable; +import net.minestom.server.tag.TagSerializer; +import net.minestom.server.tag.TagWritable; +import net.minestom.server.utils.BlockPosition; +import net.minestom.server.utils.NamespaceID; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import org.jglrxavpok.hephaistos.nbt.NBTList; +import org.jglrxavpok.hephaistos.nbt.NBTTypes; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class CampfireHandler implements BlockHandler { + + public static final Tag> ITEMS = Tag.Custom("Items", new TagSerializer<>() { + + private final Tag internal = Tag.NBT("Items"); + + @Override + public @Nullable List read(@NotNull TagReadable reader) { + NBTList item = (NBTList) reader.getTag(internal); + List result = new ArrayList<>(); + assert item != null; + item.forEach(nbtCompound -> { + int amount = nbtCompound.getAsByte("Count"); + String id = nbtCompound.getString("id"); + Material material = Registries.getMaterial(id); + result.add(ItemStack.of(material, amount)); + }); + return result; + } + + @Override + public void write(@NotNull TagWritable writer, @NotNull List value) { + NBTList items = new NBTList<>(NBTTypes.TAG_Compound); + for (var item : value) { + NBTCompound compound = new NBTCompound() + .setByte("Count", (byte) item.getAmount()) + .setByte("Slot", (byte) 1) + .setString("id", item.getMaterial().getNamespaceID().asString()); + items.add(compound); + } + writer.setTag(internal, items); + } + }); + + @Override + public void onPlace(@NotNull Instance instance, @NotNull BlockPosition blockPosition) { + + } + + @Override + public void onDestroy(@NotNull Instance instance, @NotNull BlockPosition blockPosition) { + + } + + @Override + public boolean onInteract(@NotNull Player player, Player.@NotNull Hand hand, @NotNull BlockPosition blockPosition) { + return false; + } + + @Override + public @NotNull Collection> getBlockEntityTags() { + return List.of(ITEMS); + } + + @Override + public @NotNull NamespaceID getNamespaceId() { + return NamespaceID.from("minestom:test"); + } +}