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");
+ }
+}