From b277d7cb1a80c5b63a3de74ac39f0e39ac63c613 Mon Sep 17 00:00:00 2001 From: jglrxavpok Date: Mon, 6 Jul 2020 23:43:56 +0200 Subject: [PATCH] Moved NBTUtils from VanillaReimpl to core --- .../minestom/server/inventory/Inventory.java | 7 + .../server/network/packet/PacketReader.java | 3 +- .../server/network/packet/PacketWriter.java | 3 +- .../net/minestom/server/utils/NBTUtils.java | 304 ++++++++++++++++++ .../java/net/minestom/server/utils/Utils.java | 256 --------------- 5 files changed, 315 insertions(+), 258 deletions(-) create mode 100644 src/main/java/net/minestom/server/utils/NBTUtils.java diff --git a/src/main/java/net/minestom/server/inventory/Inventory.java b/src/main/java/net/minestom/server/inventory/Inventory.java index 8647e2d27..1af780f78 100644 --- a/src/main/java/net/minestom/server/inventory/Inventory.java +++ b/src/main/java/net/minestom/server/inventory/Inventory.java @@ -515,4 +515,11 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View update(player); } } + + public void clear() { + // TODO: optimize by sending whole inventory at once? (will need to change to setItemStackInternal) + for (int i = 0; i < getSize(); i++) { + setItemStack(i, ItemStack.getAirItem()); + } + } } diff --git a/src/main/java/net/minestom/server/network/packet/PacketReader.java b/src/main/java/net/minestom/server/network/packet/PacketReader.java index 17fc1f0c5..452c1bc66 100644 --- a/src/main/java/net/minestom/server/network/packet/PacketReader.java +++ b/src/main/java/net/minestom/server/network/packet/PacketReader.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minestom.server.item.ItemStack; import net.minestom.server.utils.BlockPosition; +import net.minestom.server.utils.NBTUtils; import net.minestom.server.utils.SerializerUtils; import net.minestom.server.utils.Utils; import org.jglrxavpok.hephaistos.nbt.NBT; @@ -105,7 +106,7 @@ public class PacketReader extends InputStream { } public ItemStack readSlot() { - return Utils.readItemStack(this); + return NBTUtils.readItemStack(this); } public ByteBuf getBuffer() { diff --git a/src/main/java/net/minestom/server/network/packet/PacketWriter.java b/src/main/java/net/minestom/server/network/packet/PacketWriter.java index 992172026..5eb7ec5f1 100644 --- a/src/main/java/net/minestom/server/network/packet/PacketWriter.java +++ b/src/main/java/net/minestom/server/network/packet/PacketWriter.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minestom.server.item.ItemStack; import net.minestom.server.utils.BlockPosition; +import net.minestom.server.utils.NBTUtils; import net.minestom.server.utils.SerializerUtils; import net.minestom.server.utils.Utils; import net.minestom.server.utils.buffer.BufferWrapper; @@ -130,7 +131,7 @@ public class PacketWriter extends OutputStream { } public void writeItemStack(ItemStack itemStack) { - Utils.writeItemStack(this, itemStack); + NBTUtils.writeItemStack(this, itemStack); } public void writeNBT(String name, NBT tag) { diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java new file mode 100644 index 000000000..1f6db2ec2 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -0,0 +1,304 @@ +package net.minestom.server.utils; + +import net.minestom.server.attribute.Attribute; +import net.minestom.server.attribute.AttributeOperation; +import net.minestom.server.chat.ColoredText; +import net.minestom.server.inventory.Inventory; +import net.minestom.server.item.Enchantment; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.item.attribute.AttributeSlot; +import net.minestom.server.item.attribute.ItemAttribute; +import net.minestom.server.network.packet.PacketReader; +import net.minestom.server.network.packet.PacketWriter; +import net.minestom.server.potion.PotionType; +import net.minestom.server.registry.Registries; +import org.jglrxavpok.hephaistos.nbt.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.*; + +// for lack of a better name +public class NBTUtils { + + private final static Logger LOGGER = LoggerFactory.getLogger(Utils.class); + + /** + * Loads all the items from the 'items' list into the given inventory + * @param items + * @param destination + */ + public static void loadAllItems(NBTList items, Inventory destination) { + destination.clear(); + for(NBTCompound tag : items) { + Material item = Registries.getMaterial(tag.getString("id")); + if(item == Material.AIR) { + item = Material.STONE; + } + ItemStack stack = new ItemStack(item, tag.getByte("Count")); + loadDataIntoItem(stack, tag); + destination.setItemStack(tag.getByte("Slot"), stack); + } + } + + public static void saveAllItems(NBTList list, Inventory inventory) { + for (int i = 0; i < inventory.getSize(); i++) { + ItemStack stack = inventory.getItemStack(i); + NBTCompound nbt = new NBTCompound(); + + saveDataIntoNBT(stack, nbt); + nbt.setByte("Slot", (byte) i); + nbt.setByte("Count", stack.getAmount()); + nbt.setString("id", stack.getMaterial().getName()); + + list.add(nbt); + } + } + + private static void writeEnchant(NBTCompound nbt, String listName, Map enchantmentMap) { + NBTList enchantList = new NBTList<>(NBTTypes.TAG_Compound); + for (Map.Entry entry : enchantmentMap.entrySet()) { + Enchantment enchantment = entry.getKey(); + short level = entry.getValue(); + + enchantList.add(new NBTCompound() + .setShort("lvl", level) + .setString("id", "minecraft:" + enchantment.name().toLowerCase()) + ); + } + nbt.set(listName, enchantList); + } + + public static ItemStack readItemStack(PacketReader reader) { + boolean present = reader.readBoolean(); + + if (!present) { + return ItemStack.getAirItem(); + } + + int id = reader.readVarInt(); + if (id == -1) { + // Drop mode + return ItemStack.getAirItem(); + } + + byte count = reader.readByte(); + + ItemStack item = new ItemStack((short) id, count); + + try { + NBT itemNBT = reader.readTag(); + if(itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data + NBTCompound nbt = (NBTCompound) itemNBT; + loadDataIntoItem(item, nbt); + } + } catch (IOException | NBTException e) { + e.printStackTrace(); + } + + return item; + } + + private static void loadDataIntoItem(ItemStack item, NBTCompound nbt) { + if(nbt.containsKey("Damage")) item.setDamage(nbt.getInt("Damage")); + if(nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getInt("Unbreakable") == 1); + if(nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags")); + if(nbt.containsKey("Potion")) item.addPotionType(Registries.getPotionType(nbt.getString("Potion"))); + if(nbt.containsKey("display")) { + NBTCompound display = nbt.getCompound("display"); + if(display.containsKey("Name")) item.setDisplayName(ColoredText.of(display.getString("Name"))); + if(display.containsKey("Lore")) { + NBTList loreList = display.getList("Lore"); + ArrayList lore = new ArrayList<>(); + for(NBTString s : loreList) { + lore.add(ColoredText.of(s.getValue())); + } + item.setLore(lore); + } + } + + if(nbt.containsKey("Enchantments")) { + loadEnchantments(nbt.getList("Enchantments"), item::setEnchantment); + } + if(nbt.containsKey("StoredEnchantments")) { + loadEnchantments(nbt.getList("StoredEnchantments"), item::setStoredEnchantment); + } + if(nbt.containsKey("AttributeModifiers")) { + NBTList attributes = nbt.getList("AttributeModifiers"); + for (NBTCompound attributeNBT : attributes) { + // TODO: 1.16 changed how UUIDs are stored, is this part affected? + long uuidMost = attributeNBT.getLong("UUIDMost"); + long uuidLeast = attributeNBT.getLong("UUIDLeast"); + UUID uuid = new UUID(uuidMost, uuidLeast); + double value = attributeNBT.getDouble("Amount"); + String slot = attributeNBT.getString("Slot"); + String attributeName = attributeNBT.getString("AttributeName"); + int operation = attributeNBT.getInt("Operation"); + String name = attributeNBT.getString("Name"); + + final Attribute attribute = Attribute.fromKey(attributeName); + // Wrong attribute name, stop here + if (attribute == null) + break; + final AttributeOperation attributeOperation = AttributeOperation.byId(operation); + // Wrong attribute operation, stop here + if (attributeOperation == null) + break; + final AttributeSlot attributeSlot = AttributeSlot.valueOf(slot.toUpperCase()); + // Wrong attribute slot, stop here + if (attributeSlot == null) + break; + + // Add attribute + final ItemAttribute itemAttribute = + new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot); + item.addAttribute(itemAttribute); + } + } + } + + private static void loadEnchantments(NBTList enchantments, EnchantmentSetter setter) { + for(NBTCompound enchantment : enchantments) { + short level = enchantment.getShort("lvl"); + String id = enchantment.getString("id"); + Enchantment enchant = Registries.getEnchantment(id); + if(enchant != null) { + setter.applyEnchantment(enchant, level); + } else { + LOGGER.warn("Unknown enchantment type: "+id); + } + } + } + + public static void writeItemStack(PacketWriter packet, ItemStack itemStack) { + if (itemStack == null || itemStack.isAir()) { + packet.writeBoolean(false); + } else { + packet.writeBoolean(true); + packet.writeVarInt(itemStack.getMaterialId()); + packet.writeByte(itemStack.getAmount()); + + if (!itemStack.hasNbtTag()) { + packet.writeByte((byte) NBTTypes.TAG_End); // No nbt + return; + } + + NBTCompound itemNBT = new NBTCompound(); + saveDataIntoNBT(itemStack, itemNBT); + // End custom model data + packet.writeNBT("", itemNBT); + } + } + + private static void saveDataIntoNBT(ItemStack itemStack, NBTCompound itemNBT) { + // Unbreakable + if (itemStack.isUnbreakable()) { + itemNBT.setInt("Unbreakable", 1); + } + + // Start damage + { + itemNBT.setInt("Damage", itemStack.getDamage()); + } + // End damage + + // Display + boolean hasDisplayName = itemStack.hasDisplayName(); + boolean hasLore = itemStack.hasLore(); + + if (hasDisplayName || hasLore) { + NBTCompound displayNBT = new NBTCompound(); + if (hasDisplayName) { + final String name = itemStack.getDisplayName().toString(); + displayNBT.setString("Name", name); + } + + if (hasLore) { + final ArrayList lore = itemStack.getLore(); + + final NBTList loreNBT = new NBTList<>(NBTTypes.TAG_String); + for (ColoredText line : lore) { + loreNBT.add(new NBTString(line.toString())); + } + displayNBT.set("Lore", loreNBT); + } + + itemNBT.set("display", displayNBT); + } + // End display + + // Start enchantment + { + Map enchantmentMap = itemStack.getEnchantmentMap(); + if (!enchantmentMap.isEmpty()) { + writeEnchant(itemNBT, "Enchantments", enchantmentMap); + } + + Map storedEnchantmentMap = itemStack.getStoredEnchantmentMap(); + if (!storedEnchantmentMap.isEmpty()) { + writeEnchant(itemNBT, "StoredEnchantments", storedEnchantmentMap); + } + } + // End enchantment + + // Start attribute + { + List itemAttributes = itemStack.getAttributes(); + if (!itemAttributes.isEmpty()) { + NBTList attributesNBT = new NBTList<>(NBTTypes.TAG_Compound); + + for (ItemAttribute itemAttribute : itemAttributes) { + UUID uuid = itemAttribute.getUuid(); + + attributesNBT.add( + new NBTCompound() + .setLong("UUIDMost", uuid.getMostSignificantBits()) + .setLong("UUIDLeast", uuid.getLeastSignificantBits()) + .setDouble("Amount", itemAttribute.getValue()) + .setString("Slot", itemAttribute.getSlot().name().toLowerCase()) + .setString("itemAttribute", itemAttribute.getAttribute().getKey()) + .setInt("Operation", itemAttribute.getOperation().getId()) + .setString("Name", itemAttribute.getInternalName()) + ); + } + itemNBT.set("AttributeModifiers", attributesNBT); + } + } + // End attribute + + // Start potion + { + Set potionTypes = itemStack.getPotionTypes(); + if (!potionTypes.isEmpty()) { + for (PotionType potionType : potionTypes) { + itemNBT.setString("Potion", potionType.getNamespaceID()); + } + } + } + // End potion + + // Start hide flags + { + int hideFlag = itemStack.getHideFlag(); + if (hideFlag != 0) { + itemNBT.setInt("HideFlags", hideFlag); + } + } + // End hide flags + + // Start custom model data + { + int customModelData = itemStack.getCustomModelData(); + if (customModelData != 0) { + itemNBT.setInt("CustomModelData", customModelData); + } + } + } + + @FunctionalInterface + private interface EnchantmentSetter { + void applyEnchantment(Enchantment name, short level); + } +} diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java index 2c568d2f0..14e51a04e 100644 --- a/src/main/java/net/minestom/server/utils/Utils.java +++ b/src/main/java/net/minestom/server/utils/Utils.java @@ -1,30 +1,12 @@ package net.minestom.server.utils; import io.netty.buffer.ByteBuf; -import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.AttributeOperation; -import net.minestom.server.chat.ColoredText; import net.minestom.server.instance.Chunk; -import net.minestom.server.item.Enchantment; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.attribute.AttributeSlot; -import net.minestom.server.item.attribute.ItemAttribute; -import net.minestom.server.network.packet.PacketReader; import net.minestom.server.network.packet.PacketWriter; -import net.minestom.server.potion.PotionType; -import net.minestom.server.registry.Registries; import net.minestom.server.utils.buffer.BufferWrapper; -import org.jglrxavpok.hephaistos.nbt.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.*; public class Utils { - private final static Logger LOGGER = LoggerFactory.getLogger(Utils.class); - public static int getVarIntSize(int input) { return (input & 0xFFFFFF80) == 0 ? 1 : (input & 0xFFFFC000) == 0 @@ -117,122 +99,6 @@ public class Utils { 70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE, 0, 5}; - private static void writeEnchant(NBTCompound nbt, String listName, Map enchantmentMap) { - NBTList enchantList = new NBTList<>(NBTTypes.TAG_Compound); - for (Map.Entry entry : enchantmentMap.entrySet()) { - Enchantment enchantment = entry.getKey(); - short level = entry.getValue(); - - enchantList.add(new NBTCompound() - .setShort("lvl", level) - .setString("id", "minecraft:" + enchantment.name().toLowerCase()) - ); - } - nbt.set(listName, enchantList); - } - - public static ItemStack readItemStack(PacketReader reader) { - boolean present = reader.readBoolean(); - - if (!present) { - return ItemStack.getAirItem(); - } - - int id = reader.readVarInt(); - if (id == -1) { - // Drop mode - return ItemStack.getAirItem(); - } - - byte count = reader.readByte(); - - ItemStack item = new ItemStack((short) id, count); - - try { - NBT itemNBT = reader.readTag(); - if(itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data - NBTCompound nbt = (NBTCompound) itemNBT; - if(nbt.containsKey("Damage")) item.setDamage(nbt.getInt("Damage")); - if(nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getInt("Unbreakable") == 1); - if(nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags")); - if(nbt.containsKey("Potion")) item.addPotionType(Registries.getPotionType(nbt.getString("Potion"))); - if(nbt.containsKey("display")) { - NBTCompound display = nbt.getCompound("display"); - if(display.containsKey("Name")) item.setDisplayName(ColoredText.of(display.getString("Name"))); - if(display.containsKey("Lore")) { - NBTList loreList = display.getList("Lore"); - ArrayList lore = new ArrayList<>(); - for(NBTString s : loreList) { - lore.add(ColoredText.of(s.getValue())); - } - item.setLore(lore); - } - } - - if(nbt.containsKey("Enchantments")) { - loadEnchantments(nbt.getList("Enchantments"), item::setEnchantment); - } - if(nbt.containsKey("StoredEnchantments")) { - loadEnchantments(nbt.getList("StoredEnchantments"), item::setStoredEnchantment); - } - if(nbt.containsKey("AttributeModifiers")) { - NBTList attributes = nbt.getList("AttributeModifiers"); - for (NBTCompound attributeNBT : attributes) { - // TODO: 1.16 changed how UUIDs are stored, is this part affected? - long uuidMost = attributeNBT.getLong("UUIDMost"); - long uuidLeast = attributeNBT.getLong("UUIDLeast"); - UUID uuid = new UUID(uuidMost, uuidLeast); - double value = attributeNBT.getDouble("Amount"); - String slot = attributeNBT.getString("Slot"); - String attributeName = attributeNBT.getString("AttributeName"); - int operation = attributeNBT.getInt("Operation"); - String name = attributeNBT.getString("Name"); - - final Attribute attribute = Attribute.fromKey(attributeName); - // Wrong attribute name, stop here - if (attribute == null) - break; - final AttributeOperation attributeOperation = AttributeOperation.byId(operation); - // Wrong attribute operation, stop here - if (attributeOperation == null) - break; - final AttributeSlot attributeSlot = AttributeSlot.valueOf(slot.toUpperCase()); - // Wrong attribute slot, stop here - if (attributeSlot == null) - break; - - // Add attribute - final ItemAttribute itemAttribute = - new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot); - item.addAttribute(itemAttribute); - } - } - } - } catch (IOException | NBTException e) { - e.printStackTrace(); - } - - return item; - } - - @FunctionalInterface - private interface EnchantmentSetter { - void applyEnchantment(Enchantment name, short level); - } - - private static void loadEnchantments(NBTList enchantments, EnchantmentSetter setter) { - for(NBTCompound enchantment : enchantments) { - short level = enchantment.getShort("lvl"); - String id = enchantment.getString("id"); - Enchantment enchant = Registries.getEnchantment(id); - if(enchant != null) { - setter.applyEnchantment(enchant, level); - } else { - LOGGER.warn("Unknown enchantment type: "+id); - } - } - } - public static void writeBlocks(BufferWrapper buffer, short[] blocksId, int bitsPerEntry) { short count = 0; for (short id : blocksId) @@ -258,128 +124,6 @@ public class Utils { } } - public static void writeItemStack(PacketWriter packet, ItemStack itemStack) { - if (itemStack == null || itemStack.isAir()) { - packet.writeBoolean(false); - } else { - packet.writeBoolean(true); - packet.writeVarInt(itemStack.getMaterialId()); - packet.writeByte(itemStack.getAmount()); - - if (!itemStack.hasNbtTag()) { - packet.writeByte((byte) NBTTypes.TAG_End); // No nbt - return; - } - - NBTCompound itemNBT = new NBTCompound(); - - // Unbreakable - if (itemStack.isUnbreakable()) { - itemNBT.setInt("Unbreakable", 1); - } - - // Start damage - { - itemNBT.setInt("Damage", itemStack.getDamage()); - } - // End damage - - // Display - boolean hasDisplayName = itemStack.hasDisplayName(); - boolean hasLore = itemStack.hasLore(); - - if (hasDisplayName || hasLore) { - NBTCompound displayNBT = new NBTCompound(); - if (hasDisplayName) { - final String name = itemStack.getDisplayName().toString(); - displayNBT.setString("Name", name); - } - - if (hasLore) { - final ArrayList lore = itemStack.getLore(); - - final NBTList loreNBT = new NBTList<>(NBTTypes.TAG_String); - for (ColoredText line : lore) { - loreNBT.add(new NBTString(line.toString())); - } - displayNBT.set("Lore", loreNBT); - } - - itemNBT.set("display", displayNBT); - } - // End display - - // Start enchantment - { - Map enchantmentMap = itemStack.getEnchantmentMap(); - if (!enchantmentMap.isEmpty()) { - writeEnchant(itemNBT, "Enchantments", enchantmentMap); - } - - Map storedEnchantmentMap = itemStack.getStoredEnchantmentMap(); - if (!storedEnchantmentMap.isEmpty()) { - writeEnchant(itemNBT, "StoredEnchantments", storedEnchantmentMap); - } - } - // End enchantment - - // Start attribute - { - List itemAttributes = itemStack.getAttributes(); - if (!itemAttributes.isEmpty()) { - NBTList attributesNBT = new NBTList<>(NBTTypes.TAG_Compound); - - for (ItemAttribute itemAttribute : itemAttributes) { - UUID uuid = itemAttribute.getUuid(); - - attributesNBT.add( - new NBTCompound() - .setLong("UUIDMost", uuid.getMostSignificantBits()) - .setLong("UUIDLeast", uuid.getLeastSignificantBits()) - .setDouble("Amount", itemAttribute.getValue()) - .setString("Slot", itemAttribute.getSlot().name().toLowerCase()) - .setString("itemAttribute", itemAttribute.getAttribute().getKey()) - .setInt("Operation", itemAttribute.getOperation().getId()) - .setString("Name", itemAttribute.getInternalName()) - ); - } - itemNBT.set("AttributeModifiers", attributesNBT); - } - } - // End attribute - - // Start potion - { - Set potionTypes = itemStack.getPotionTypes(); - if (!potionTypes.isEmpty()) { - for (PotionType potionType : potionTypes) { - itemNBT.setString("Potion", potionType.getNamespaceID()); - } - } - } - // End potion - - // Start hide flags - { - int hideFlag = itemStack.getHideFlag(); - if (hideFlag != 0) { - itemNBT.setInt("HideFlags", hideFlag); - } - } - // End hide flags - - // Start custom model data - { - int customModelData = itemStack.getCustomModelData(); - if (customModelData != 0) { - itemNBT.setInt("CustomModelData", customModelData); - } - } - // End custom model data - packet.writeNBT("", itemNBT); - } - } - public static long[] encodeBlocks(int[] blocks, int bitsPerEntry) { long maxEntryValue = (1L << bitsPerEntry) - 1; char valuesPerLong = (char) (64 / bitsPerEntry);