diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java index abf2f51a7..33659de8c 100644 --- a/src/main/java/net/minestom/server/utils/Utils.java +++ b/src/main/java/net/minestom/server/utils/Utils.java @@ -11,6 +11,8 @@ import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.potion.PotionType; import net.minestom.server.utils.buffer.BufferWrapper; import net.minestom.server.utils.item.NbtReaderUtils; +import net.minestom.server.utils.nbt.NBT; +import net.minestom.server.utils.nbt.NbtWriter; import java.util.*; @@ -104,163 +106,128 @@ public class Utils { return; } - packet.writeByte((byte) 0x0A); // Compound - packet.writeShort((short) 0); // Empty compound name + NbtWriter mainWriter = new NbtWriter(packet); - // Unbreakable - if (itemStack.isUnbreakable()) { - packet.writeByte((byte) 0x03); // Integer - packet.writeShortSizedString("Unbreakable"); - packet.writeInt(1); - } - - // Start damage - { - packet.writeByte((byte) 0x02); - packet.writeShortSizedString("Damage"); - packet.writeShort(itemStack.getDamage()); - } - // End damage - - // Display - boolean hasDisplayName = itemStack.hasDisplayName(); - boolean hasLore = itemStack.hasLore(); - - if (hasDisplayName || hasLore) { - packet.writeByte((byte) 0x0A); // Start display compound - packet.writeShortSizedString("display"); - - if (hasDisplayName) { - packet.writeByte((byte) 0x08); - packet.writeShortSizedString("Name"); - packet.writeShortSizedString(Chat.toJsonString(Chat.fromLegacyText(itemStack.getDisplayName()))); + mainWriter.writeCompound(null, writer -> { + // Unbreakable + if (itemStack.isUnbreakable()) { + writer.writeInt("Unbreakable", 1); } - if (hasLore) { - ArrayList lore = itemStack.getLore(); + // Start damage + { + writer.writeShort("Damage", itemStack.getDamage()); + } + // End damage - packet.writeByte((byte) 0x09); - packet.writeShortSizedString("Lore"); - packet.writeByte((byte) 0x08); - packet.writeInt(lore.size()); - for (String line : lore) { - packet.writeShortSizedString(Chat.toJsonString(Chat.fromLegacyText(line))); + // Display + boolean hasDisplayName = itemStack.hasDisplayName(); + boolean hasLore = itemStack.hasLore(); + + if (hasDisplayName || hasLore) { + writer.writeCompound("display", displayWriter -> { + if (hasDisplayName) { + final String name = Chat.toJsonString(Chat.fromLegacyText(itemStack.getDisplayName())); + displayWriter.writeString("Name", name); + } + + if (hasLore) { + final ArrayList lore = itemStack.getLore(); + + displayWriter.writeList("Lore", NBT.NBT_STRING, lore.size(), () -> { + for (String line : lore) { + line = Chat.toJsonString(Chat.fromLegacyText(line)); + packet.writeShortSizedString(line); + } + }); + + } + }); + } + // End display + + // Start enchantment + { + Map enchantmentMap = itemStack.getEnchantmentMap(); + if (!enchantmentMap.isEmpty()) { + writeEnchant(writer, "Enchantments", enchantmentMap); + } + + Map storedEnchantmentMap = itemStack.getStoredEnchantmentMap(); + if (!storedEnchantmentMap.isEmpty()) { + writeEnchant(writer, "StoredEnchantments", storedEnchantmentMap); } } + // End enchantment - packet.writeByte((byte) 0); // End display compound - } - // End display + // Start attribute + { + List itemAttributes = itemStack.getAttributes(); + if (!itemAttributes.isEmpty()) { + packet.writeByte((byte) 0x09); // Type id (list) + packet.writeShortSizedString("AttributeModifiers"); - // Start enchantment - { - Map enchantmentMap = itemStack.getEnchantmentMap(); - if (!enchantmentMap.isEmpty()) { - writeEnchant(packet, "Enchantments", enchantmentMap); - } + packet.writeByte((byte) 0x0A); // Compound + packet.writeInt(itemAttributes.size()); - Map storedEnchantmentMap = itemStack.getStoredEnchantmentMap(); - if (!storedEnchantmentMap.isEmpty()) { - writeEnchant(packet, "StoredEnchantments", storedEnchantmentMap); - } - } - // End enchantment + for (ItemAttribute itemAttribute : itemAttributes) { + UUID uuid = itemAttribute.getUuid(); - // Start attribute - { - List itemAttributes = itemStack.getAttributes(); - if (!itemAttributes.isEmpty()) { - packet.writeByte((byte) 0x09); // Type id (list) - packet.writeShortSizedString("AttributeModifiers"); + writer.writeLong("UUIDMost", uuid.getMostSignificantBits()); - packet.writeByte((byte) 0x0A); // Compound - packet.writeInt(itemAttributes.size()); + writer.writeLong("UUIDLeast", uuid.getLeastSignificantBits()); - for (ItemAttribute itemAttribute : itemAttributes) { - UUID uuid = itemAttribute.getUuid(); + writer.writeDouble("Amount", itemAttribute.getValue()); - packet.writeByte((byte) 0x04); // Type id (long) - packet.writeShortSizedString("UUIDMost"); - packet.writeLong(uuid.getMostSignificantBits()); + writer.writeString("Slot", itemAttribute.getSlot().name().toLowerCase()); - packet.writeByte((byte) 0x04); // Type id (long) - packet.writeShortSizedString("UUIDLeast"); - packet.writeLong(uuid.getLeastSignificantBits()); + writer.writeString("itemAttribute", itemAttribute.getAttribute().getKey()); - packet.writeByte((byte) 0x06); // Type id (double) - packet.writeShortSizedString("Amount"); - packet.writeDouble(itemAttribute.getValue()); + writer.writeInt("Operation", itemAttribute.getOperation().getId()); - packet.writeByte((byte) 0x08); // Type id (string) - packet.writeShortSizedString("Slot"); - packet.writeShortSizedString(itemAttribute.getSlot().name().toLowerCase()); - - packet.writeByte((byte) 0x08); // Type id (string) - packet.writeShortSizedString("AttributeName"); - packet.writeShortSizedString(itemAttribute.getAttribute().getKey()); - - packet.writeByte((byte) 0x03); // Type id (int) - packet.writeShortSizedString("Operation"); - packet.writeInt(itemAttribute.getOperation().getId()); - - packet.writeByte((byte) 0x08); // Type id (string) - packet.writeShortSizedString("Name"); - packet.writeShortSizedString(itemAttribute.getInternalName()); - } - packet.writeByte((byte) 0x00); // End compound - } - } - // End attribute - - // Start potion - { - Set potionTypes = itemStack.getPotionTypes(); - if (!potionTypes.isEmpty()) { - for (PotionType potionType : potionTypes) { - packet.writeByte((byte) 0x08); // type id (string) - packet.writeShortSizedString("Potion"); - packet.writeShortSizedString("minecraft:" + potionType.name().toLowerCase()); + writer.writeString("Name", itemAttribute.getInternalName()); + } + packet.writeByte((byte) 0x00); // End compound } } - } - // End potion + // End attribute - // Start hide flags - { - int hideFlag = itemStack.getHideFlag(); - if (hideFlag != 0) { - packet.writeByte((byte) 3); // Type id (int) - packet.writeShortSizedString("HideFlags"); - packet.writeInt(hideFlag); + // Start potion + { + Set potionTypes = itemStack.getPotionTypes(); + if (!potionTypes.isEmpty()) { + for (PotionType potionType : potionTypes) { + packet.writeByte((byte) 0x08); // type id (string) + packet.writeShortSizedString("Potion"); + packet.writeShortSizedString("minecraft:" + potionType.name().toLowerCase()); + } + } } - } + // End potion - packet.writeByte((byte) 0); // End nbt + // Start hide flags + { + int hideFlag = itemStack.getHideFlag(); + if (hideFlag != 0) { + writer.writeInt("HideFlags", hideFlag); + } + } + }); } } - private static void writeEnchant(PacketWriter packet, String listName, Map enchantmentMap) { - packet.writeByte((byte) 0x09); // Type id (list) - packet.writeShortSizedString(listName); + private static void writeEnchant(NbtWriter writer, String listName, Map enchantmentMap) { + writer.writeList(listName, NBT.NBT_COMPOUND, enchantmentMap.size(), () -> { + for (Map.Entry entry : enchantmentMap.entrySet()) { + Enchantment enchantment = entry.getKey(); + short level = entry.getValue(); - packet.writeByte((byte) 0x0A); // Compound - packet.writeInt(enchantmentMap.size()); // Map size + writer.writeShort("lvl", level); - for (Map.Entry entry : enchantmentMap.entrySet()) { - Enchantment enchantment = entry.getKey(); - short level = entry.getValue(); + writer.writeString("id", "minecraft:" + enchantment.name().toLowerCase()); - packet.writeByte((byte) 0x02); // Type id (short) - packet.writeShortSizedString("lvl"); - packet.writeShort(level); - - packet.writeByte((byte) 0x08); // Type id (string) - packet.writeShortSizedString("id"); - packet.writeShortSizedString("minecraft:" + enchantment.name().toLowerCase()); - - } - - packet.writeByte((byte) 0); // End enchantment compound + } + }); } public static ItemStack readItemStack(PacketReader reader) { diff --git a/src/main/java/net/minestom/server/utils/nbt/NBT.java b/src/main/java/net/minestom/server/utils/nbt/NBT.java new file mode 100644 index 000000000..a5b1b5f91 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/NBT.java @@ -0,0 +1,18 @@ +package net.minestom.server.utils.nbt; + +public class NBT { + + public static final byte NBT_BYTE = 0x01; + public static final byte NBT_SHORT = 0x02; + public static final byte NBT_INT = 0x03; + public static final byte NBT_LONG = 0x04; + public static final byte NBT_FLOAT = 0x05; + public static final byte NBT_DOUBLE = 0x06; + public static final byte NBT_BYTE_ARRAY = 0x07; + public static final byte NBT_STRING = 0x08; + public static final byte NBT_LIST = 0x09; + public static final byte NBT_COMPOUND = 0x0A; + public static final byte NBT_INT_ARRAY = 0x0B; + public static final byte NBT_LONG_ARRAY = 0x0C; + +} diff --git a/src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java b/src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java new file mode 100644 index 000000000..008eaacd0 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/NbtConsumer.java @@ -0,0 +1,6 @@ +package net.minestom.server.utils.nbt; + +@FunctionalInterface +public interface NbtConsumer { + void accept(NbtWriter writer); +} diff --git a/src/main/java/net/minestom/server/utils/nbt/NbtWriter.java b/src/main/java/net/minestom/server/utils/nbt/NbtWriter.java new file mode 100644 index 000000000..9645fe293 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/nbt/NbtWriter.java @@ -0,0 +1,84 @@ +package net.minestom.server.utils.nbt; + +import net.minestom.server.network.packet.PacketWriter; + +import static net.minestom.server.utils.nbt.NBT.*; + +public class NbtWriter { + + private PacketWriter packet; + + public NbtWriter(PacketWriter packet) { + this.packet = packet; + } + + public void writeByte(String name, byte value) { + packet.writeByte(NBT_BYTE); + packet.writeShortSizedString(name); + packet.writeByte(value); + } + + public void writeShort(String name, short value) { + packet.writeByte(NBT_SHORT); + packet.writeShortSizedString(name); + packet.writeShort(value); + } + + public void writeInt(String name, int value) { + packet.writeByte(NBT_INT); + packet.writeShortSizedString(name); + packet.writeInt(value); + } + + public void writeLong(String name, long value) { + packet.writeByte(NBT_LONG); + packet.writeShortSizedString(name); + packet.writeLong(value); + } + + public void writeFloat(String name, float value) { + packet.writeByte(NBT_FLOAT); + packet.writeShortSizedString(name); + packet.writeFloat(value); + } + + public void writeDouble(String name, double value) { + packet.writeByte(NBT_DOUBLE); + packet.writeShortSizedString(name); + packet.writeDouble(value); + } + + // FIXME: not sure + public void writeByteArray(String name, byte[] value) { + packet.writeByte(NBT_BYTE_ARRAY); + packet.writeShortSizedString(name); + packet.writeInt(value.length); + for (byte val : value) { + packet.writeByte(val); + } + } + + public void writeString(String name, String value) { + packet.writeByte(NBT_STRING); + packet.writeShortSizedString(name); + packet.writeShortSizedString(value); + } + + public void writeList(String name, byte type, int size, Runnable callback) { + packet.writeByte(NBT_LIST); + packet.writeShortSizedString(name == null ? "" : name); + packet.writeByte(type); + packet.writeInt(size); + callback.run(); + if (type == NBT_COMPOUND) + packet.writeByte((byte) 0x00); // End compount + } + + public void writeCompound(String name, NbtConsumer consumer) { + packet.writeByte(NBT_COMPOUND); + packet.writeShortSizedString(name == null ? "" : name); + consumer.accept(this); + packet.writeByte((byte) 0x00); // End compound + } + +}