Use original NBT when possible

This commit is contained in:
themode 2021-04-02 22:14:48 +02:00
parent 2b0c525ca2
commit 8d8a22f209
8 changed files with 165 additions and 53 deletions

View File

@ -47,8 +47,6 @@ public class ArgumentItemStack extends Argument<ItemStack> {
final String materialName = input.substring(0, nbtIndex);
final Material material = Registries.getMaterial(materialName);
ItemStack itemStack = ItemStack.of(material);
final String sNBT = input.substring(nbtIndex).replace("\\\"", "\"");
NBTCompound compound;
@ -58,9 +56,7 @@ public class ArgumentItemStack extends Argument<ItemStack> {
throw new ArgumentSyntaxException("Item NBT is invalid", input, INVALID_NBT);
}
NBTUtils.loadDataIntoItem(itemStack, compound);
return itemStack;
return NBTUtils.loadItem(material, 1, compound);
}
}

View File

@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
public class ItemBuilder {
@ -38,8 +39,14 @@ public class ItemBuilder {
return this;
}
@Contract(value = "_ -> this")
public @NotNull ItemBuilder meta(@NotNull UnaryOperator<@NotNull ItemMetaBuilder> itemMetaConsumer) {
itemMetaConsumer.apply(metaBuilder);
return this;
}
@Contract(value = "_, _ -> this")
public <T extends ItemMetaBuilder, U extends ItemMetaBuilder.Provider<T>> @NotNull ItemBuilder meta(Class<U> metaType, Consumer<T> itemMetaConsumer) {
public <T extends ItemMetaBuilder, U extends ItemMetaBuilder.Provider<T>> @NotNull ItemBuilder meta(@NotNull Class<U> metaType, @NotNull Consumer<@NotNull T> itemMetaConsumer) {
itemMetaConsumer.accept((T) metaBuilder);
return this;
}

View File

@ -30,6 +30,7 @@ public class ItemMeta implements Cloneable {
private final int customModelData;
private final @Nullable NBTCompound originalNbt;
private SoftReference<NBTCompound> cache;
protected ItemMeta(@NotNull ItemMetaBuilder metaBuilder) {
@ -42,6 +43,9 @@ public class ItemMeta implements Cloneable {
this.enchantmentMap = Collections.unmodifiableMap(metaBuilder.enchantmentMap);
this.attributes = new ArrayList<>();
this.customModelData = 0;
// Can be null
this.originalNbt = metaBuilder.originalNBT;
}
@Contract(value = "_, -> new", pure = true)
@ -69,28 +73,38 @@ public class ItemMeta implements Cloneable {
}
@Contract(pure = true)
public @Nullable List<@NotNull Component> getLore() {
public @NotNull List<@NotNull Component> getLore() {
return lore;
}
public Map<Enchantment, Short> getEnchantmentMap() {
@Contract(pure = true)
public @NotNull Map<Enchantment, Short> getEnchantmentMap() {
return enchantmentMap;
}
public List<ItemAttribute> getAttributes() {
@Contract(pure = true)
public @NotNull List<ItemAttribute> getAttributes() {
return attributes;
}
@Contract(pure = true)
public int getCustomModelData() {
return customModelData;
}
public @NotNull NBTCompound toNBT() {
if (originalNbt != null) {
// Return the nbt this meta has been created with
return originalNbt;
}
var nbt = cache != null ? cache.get() : null;
if (nbt == null) {
nbt = NBTUtils.metaToNBT(this);
cache = new SoftReference<>(nbt);
this.builder.write(nbt);
this.cache = new SoftReference<>(nbt);
}
return nbt;
}
@ -108,6 +122,7 @@ public class ItemMeta implements Cloneable {
return toNBT().hashCode();
}
@Contract(value = "-> new", pure = true)
protected @NotNull ItemMetaBuilder builder() {
return builder.clone();
}

View File

@ -1,20 +1,45 @@
package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.utils.NBTUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.*;
public abstract class ItemMetaBuilder implements Cloneable {
protected int damage;
protected boolean unbreakable;
protected int hideFlag;
protected Component displayName;
protected List<Component> lore = new ArrayList<>();
protected Map<Enchantment, Short> enchantmentMap = new HashMap<>();
protected List<ItemAttribute> attributes = new ArrayList<>();
protected int customModelData;
protected NBTCompound originalNBT;
protected ItemMetaBuilder() {
}
public @NotNull ItemMetaBuilder damage(int damage) {
this.damage = damage;
return this;
}
public @NotNull ItemMetaBuilder unbreakable(boolean unbreakable) {
this.unbreakable = unbreakable;
return this;
}
public @NotNull ItemMetaBuilder hideFlag(int hideFlag) {
this.hideFlag = hideFlag;
return this;
}
public @NotNull ItemMetaBuilder displayName(@Nullable Component displayName) {
this.displayName = displayName;
return this;
@ -45,10 +70,30 @@ public abstract class ItemMetaBuilder implements Cloneable {
return this;
}
public @NotNull ItemMetaBuilder attributes(List<ItemAttribute> attributes) {
this.attributes = attributes;
return this;
}
public @NotNull ItemMetaBuilder customModelData(int customModelData) {
this.customModelData = customModelData;
return this;
}
public abstract @NotNull ItemMeta build();
public abstract void read(@NotNull NBTCompound nbtCompound);
public abstract void write(@NotNull NBTCompound nbtCompound);
protected abstract void deepClone(@NotNull ItemMetaBuilder metaBuilder);
public static @NotNull ItemMetaBuilder fromNBT(@NotNull ItemMetaBuilder metaBuilder, @NotNull NBTCompound nbtCompound) {
NBTUtils.loadDataIntoMeta(metaBuilder, nbtCompound);
metaBuilder.originalNBT = nbtCompound;
return metaBuilder;
}
@Override
protected ItemMetaBuilder clone() {
try {

View File

@ -81,6 +81,11 @@ public class ItemStack {
return builder().meta(metaType, metaConsumer).build();
}
@Contract(value = "_ -> new", pure = true)
public @NotNull ItemStack withMeta(@NotNull UnaryOperator<@NotNull ItemMetaBuilder> metaOperator) {
return builder().meta(metaOperator).build();
}
@Contract(pure = true)
public @Nullable Component getDisplayName() {
return meta.getDisplayName();

View File

@ -7,6 +7,7 @@ import net.minestom.server.item.ItemMetaBuilder;
import net.minestom.server.utils.Position;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.util.List;
import java.util.Map;
@ -101,6 +102,40 @@ public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider<Co
return new CompassMeta(this, lodestoneTracked, lodestoneDimension, lodestonePosition);
}
@Override
public void read(@NotNull NBTCompound nbtCompound) {
if (nbtCompound.containsKey("LodestoneTracked")) {
this.lodestoneTracked = nbtCompound.getByte("LodestoneTracked") == 1;
}
if (nbtCompound.containsKey("LodestoneDimension")) {
this.lodestoneDimension = nbtCompound.getString("LodestoneDimension");
}
if (nbtCompound.containsKey("LodestonePos")) {
final NBTCompound posCompound = nbtCompound.getCompound("LodestonePos");
final int x = posCompound.getInt("X");
final int y = posCompound.getInt("Y");
final int z = posCompound.getInt("Z");
this.lodestonePosition = new Position(x, y, z);
}
}
@Override
public void write(@NotNull NBTCompound nbtCompound) {
nbtCompound.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
if (lodestoneDimension != null) {
nbtCompound.setString("LodestoneDimension", lodestoneDimension);
}
if (lodestonePosition != null) {
NBTCompound posCompound = new NBTCompound();
posCompound.setInt("X", (int) lodestonePosition.getX());
posCompound.setInt("Y", (int) lodestonePosition.getY());
posCompound.setInt("Z", (int) lodestonePosition.getZ());
nbtCompound.set("LodestonePos", posCompound);
}
}
@Override
protected void deepClone(@NotNull ItemMetaBuilder metaBuilder) {
var compassBuilder = (CompassMeta.Builder) metaBuilder;

View File

@ -136,8 +136,7 @@ public class CrossbowMeta extends ItemMeta {
final NBTCompound tagsCompound = projectileCompound.getCompound("tag");
ItemStack itemStack = ItemStack.of(material, count);
NBTUtils.loadDataIntoItem(itemStack, tagsCompound);
ItemStack itemStack = NBTUtils.loadItem(material, count, tagsCompound);
index++;

View File

@ -6,11 +6,11 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.util.Codec;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.AdventureSerializer;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.attribute.AttributeOperation;
import net.minestom.server.inventory.Inventory;
import net.minestom.server.item.Enchantment;
import net.minestom.server.item.ItemMeta;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.item.*;
import net.minestom.server.item.attribute.AttributeSlot;
import net.minestom.server.item.attribute.ItemAttribute;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.binary.BinaryReader;
@ -24,6 +24,8 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -65,15 +67,17 @@ public final class NBTUtils {
public static void loadAllItems(@NotNull NBTList<NBTCompound> items, @NotNull Inventory destination) {
destination.clear();
for (NBTCompound tag : items) {
Material item = Registries.getMaterial(tag.getString("id"));
if (item == Material.AIR) {
item = Material.STONE;
Material material = Registries.getMaterial(tag.getString("id"));
if (material == Material.AIR) {
material = Material.STONE;
}
ItemStack stack = ItemStack.of(item, tag.getByte("Count"));
byte count = tag.getByte("Count");
NBTCompound nbtCompound = null;
if (tag.containsKey("tag")) {
loadDataIntoItem(stack, tag.getCompound("tag"));
nbtCompound = tag.getCompound("tag");
}
destination.setItemStack(tag.getByte("Slot"), stack);
ItemStack itemStack = loadItem(material, count, nbtCompound);
destination.setItemStack(tag.getByte("Slot"), itemStack);
}
}
@ -124,32 +128,44 @@ public final class NBTUtils {
final Material material = Material.fromId((short) id);
final byte count = reader.readByte();
ItemStack item = ItemStack.of(material, count);
NBTCompound nbtCompound = null;
try {
final NBT itemNBT = reader.readTag();
if (itemNBT instanceof NBTCompound) { // can also be a TAG_End if no data
NBTCompound nbt = (NBTCompound) itemNBT;
loadDataIntoItem(item, nbt);
nbtCompound = (NBTCompound) itemNBT;
}
} catch (IOException | NBTException e) {
MinecraftServer.getExceptionManager().handleException(e);
}
return item;
return loadItem(material, count, nbtCompound);
}
public static @NotNull ItemStack loadItem(@NotNull Material material, int count, NBTCompound nbtCompound) {
return ItemStack.builder(material)
.amount(count)
.meta(metaBuilder -> {
if (nbtCompound != null) {
return ItemMetaBuilder.fromNBT(metaBuilder, nbtCompound);
} else {
return metaBuilder;
}
})
.build();
}
@SuppressWarnings("ConstantConditions")
public static void loadDataIntoItem(@NotNull ItemStack item, @NotNull NBTCompound nbt) {
/*if (nbt.containsKey("Damage")) item.setDamage(nbt.getInt("Damage"));
if (nbt.containsKey("Unbreakable")) item.setUnbreakable(nbt.getAsByte("Unbreakable") == 1);
if (nbt.containsKey("HideFlags")) item.setHideFlag(nbt.getInt("HideFlags"));
public static void loadDataIntoMeta(@NotNull ItemMetaBuilder metaBuilder, @NotNull NBTCompound nbt) {
if (nbt.containsKey("Damage")) metaBuilder.damage(nbt.getInt("Damage"));
if (nbt.containsKey("Unbreakable")) metaBuilder.unbreakable(nbt.getAsByte("Unbreakable") == 1);
if (nbt.containsKey("HideFlags")) metaBuilder.hideFlag(nbt.getInt("HideFlags"));
if (nbt.containsKey("display")) {
final NBTCompound display = nbt.getCompound("display");
if (display.containsKey("Name")) {
final String rawName = display.getString("Name");
final Component displayName = GsonComponentSerializer.gson().deserialize(rawName);
item.setDisplayName(displayName);
metaBuilder.displayName(displayName);
}
if (display.containsKey("Lore")) {
NBTList<NBTString> loreList = display.getList("Lore");
@ -157,19 +173,20 @@ public final class NBTUtils {
for (NBTString s : loreList) {
lore.add(GsonComponentSerializer.gson().deserialize(s.getValue()));
}
item.setLore(lore);
metaBuilder.lore(lore);
}
}
// Enchantments
if (nbt.containsKey("Enchantments")) {
loadEnchantments(nbt.getList("Enchantments"), item::setEnchantment);
loadEnchantments(nbt.getList("Enchantments"), metaBuilder::enchantment);
}
// Attributes
if (nbt.containsKey("AttributeModifiers")) {
NBTList<NBTCompound> attributes = nbt.getList("AttributeModifiers");
for (NBTCompound attributeNBT : attributes) {
List<ItemAttribute> attributes = new ArrayList<>();
NBTList<NBTCompound> nbtAttributes = nbt.getList("AttributeModifiers");
for (NBTCompound attributeNBT : nbtAttributes) {
final UUID uuid;
{
final int[] uuidArray = attributeNBT.getIntArray("UUID");
@ -203,44 +220,37 @@ public final class NBTUtils {
// Add attribute
final ItemAttribute itemAttribute =
new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot);
item.addAttribute(itemAttribute);
}
}
// Hide flags
{
if (nbt.containsKey("HideFlags")) {
item.setHideFlag(nbt.getInt("HideFlags"));
attributes.add(itemAttribute);
}
metaBuilder.attributes(attributes);
}
// Custom model data
{
if (nbt.containsKey("CustomModelData")) {
item.setCustomModelData(nbt.getInt("CustomModelData"));
metaBuilder.customModelData(nbt.getInt("CustomModelData"));
}
}
// Meta specific field
final ItemMeta itemMeta = item.getItemMeta();
if (itemMeta != null) {
itemMeta.read(nbt);
}
// Meta specific fields
metaBuilder.read(nbt);
// Ownership
{
if (nbt.containsKey(ItemStack.OWNERSHIP_DATA_KEY)) {
// FIXME: custom data
/*if (nbt.containsKey(ItemStack.OWNERSHIP_DATA_KEY)) {
final String identifierString = nbt.getString(ItemStack.OWNERSHIP_DATA_KEY);
final UUID identifier = UUID.fromString(identifierString);
final Data data = ItemStack.DATA_OWNERSHIP.getOwnObject(identifier);
if (data != null) {
item.setData(data);
}
}
}*/
}
//CanPlaceOn
{
// FIXME: PlaceOn/CanDestroy
/*{
if (nbt.containsKey("CanPlaceOn")) {
NBTList<NBTString> canPlaceOn = nbt.getList("CanPlaceOn");
canPlaceOn.forEach(x -> item.getCanPlaceOn().add(x.getValue()));
@ -268,7 +278,7 @@ public final class NBTUtils {
}
}
public static void writeItemStack(BinaryWriter packet, @NotNull ItemStack itemStack) {
public static void writeItemStack(@NotNull BinaryWriter packet, @NotNull ItemStack itemStack) {
if (itemStack.isAir()) {
packet.writeBoolean(false);
} else {