From e772aace7149e15a49f6873573fc1a7294846865 Mon Sep 17 00:00:00 2001 From: TheMode Date: Sun, 27 Jun 2021 17:36:37 +0200 Subject: [PATCH] Initial reusable meta builder --- .../net/minestom/server/item/ItemMeta.java | 2 +- .../minestom/server/item/ItemMetaBuilder.java | 104 ++++++++++++------ .../server/item/ItemStackBuilder.java | 7 +- .../server/item/metadata/CompassMeta.java | 34 +++--- .../server/item/metadata/CrossbowMeta.java | 6 +- .../item/metadata/EnchantedBookMeta.java | 6 +- .../item/metadata/FireworkEffectMeta.java | 2 +- .../server/item/metadata/MapMeta.java | 26 +++-- .../server/item/metadata/PotionMeta.java | 6 +- .../item/metadata/WritableBookMeta.java | 9 +- .../server/item/metadata/WrittenBookMeta.java | 12 +- 11 files changed, 127 insertions(+), 87 deletions(-) diff --git a/src/main/java/net/minestom/server/item/ItemMeta.java b/src/main/java/net/minestom/server/item/ItemMeta.java index 443e15bb3..a634d73ca 100644 --- a/src/main/java/net/minestom/server/item/ItemMeta.java +++ b/src/main/java/net/minestom/server/item/ItemMeta.java @@ -51,7 +51,7 @@ public class ItemMeta implements TagReadable, Writeable { this.canDestroy = new HashSet<>(metaBuilder.canDestroy); this.canPlaceOn = new HashSet<>(metaBuilder.canPlaceOn); - this.nbt = metaBuilder.nbt; + this.nbt = metaBuilder.nbt(); this.emptyBuilder = metaBuilder.getSupplier().get(); } diff --git a/src/main/java/net/minestom/server/item/ItemMetaBuilder.java b/src/main/java/net/minestom/server/item/ItemMetaBuilder.java index b6858ff8c..6274e4905 100644 --- a/src/main/java/net/minestom/server/item/ItemMetaBuilder.java +++ b/src/main/java/net/minestom/server/item/ItemMetaBuilder.java @@ -2,7 +2,6 @@ package net.minestom.server.item; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.minestom.server.adventure.AdventureSerializer; import net.minestom.server.instance.block.Block; import net.minestom.server.item.attribute.ItemAttribute; import net.minestom.server.tag.Tag; @@ -15,12 +14,17 @@ import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.*; import java.util.*; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Consumer; import java.util.function.Supplier; public abstract class ItemMetaBuilder implements TagWritable { - protected NBTCompound nbt = new NBTCompound(); + private static final AtomicReferenceFieldUpdater NBT_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(ItemMetaBuilder.class, NBTCompound.class, "nbt"); + + protected volatile boolean built = false; + private volatile NBTCompound nbt = new NBTCompound(); protected int damage; protected boolean unbreakable; @@ -36,21 +40,21 @@ public abstract class ItemMetaBuilder implements TagWritable { @Contract("_ -> this") public @NotNull ItemMetaBuilder damage(int damage) { this.damage = damage; - this.nbt.setInt("Damage", damage); + mutateNbt(compound -> compound.setInt("Damage", damage)); return this; } @Contract("_ -> this") public @NotNull ItemMetaBuilder unbreakable(boolean unbreakable) { this.unbreakable = unbreakable; - this.nbt.setByte("Unbreakable", (byte) (unbreakable ? 1 : 0)); + mutateNbt(compound -> compound.setByte("Unbreakable", (byte) (unbreakable ? 1 : 0))); return this; } @Contract("_ -> this") public @NotNull ItemMetaBuilder hideFlag(int hideFlag) { this.hideFlag = hideFlag; - this.nbt.setInt("HideFlags", hideFlag); + mutateNbt(compound -> compound.setInt("HideFlags", hideFlag)); return this; } @@ -79,7 +83,7 @@ public abstract class ItemMetaBuilder implements TagWritable { @Contract("_ -> this") public @NotNull ItemMetaBuilder lore(@NotNull List<@NotNull Component> lore) { - this.lore = lore; + this.lore = new ArrayList<>(lore); handleCompound("display", nbtCompound -> { final NBTList loreNBT = new NBTList<>(NBTTypes.TAG_String); for (Component line : lore) { @@ -98,7 +102,7 @@ public abstract class ItemMetaBuilder implements TagWritable { @Contract("_ -> this") public @NotNull ItemMetaBuilder enchantments(@NotNull Map enchantments) { - this.enchantmentMap = enchantments; + this.enchantmentMap = new HashMap<>(enchantments); handleMap(enchantmentMap, "Enchantments", nbt, () -> { NBTUtils.writeEnchant(nbt, "Enchantments", enchantmentMap); return nbt.get("Enchantments"); @@ -108,21 +112,21 @@ public abstract class ItemMetaBuilder implements TagWritable { @Contract("_, _ -> this") public @NotNull ItemMetaBuilder enchantment(@NotNull Enchantment enchantment, short level) { - this.enchantmentMap.put(enchantment, level); + this.enchantmentMap = Map.of(enchantment, level); enchantments(enchantmentMap); return this; } @Contract("-> this") public @NotNull ItemMetaBuilder clearEnchantment() { - this.enchantmentMap.clear(); + this.enchantmentMap = Collections.emptyMap(); enchantments(enchantmentMap); return this; } @Contract("_ -> this") public @NotNull ItemMetaBuilder attributes(@NotNull List<@NotNull ItemAttribute> attributes) { - this.attributes = attributes; + this.attributes = new ArrayList<>(attributes); handleCollection(attributes, "AttributeModifiers", nbt, () -> { NBTList attributesNBT = new NBTList<>(NBTTypes.TAG_Compound); @@ -153,7 +157,7 @@ public abstract class ItemMetaBuilder implements TagWritable { @Contract("_ -> this") public @NotNull ItemMetaBuilder canPlaceOn(@NotNull Set<@NotNull Block> blocks) { - this.canPlaceOn = blocks; + this.canPlaceOn = new HashSet<>(blocks); handleCollection(canPlaceOn, "CanPlaceOn", nbt, () -> { NBTList list = new NBTList<>(NBTTypes.TAG_String); canPlaceOn.forEach(block -> list.add(new NBTString(block.getName()))); @@ -170,7 +174,7 @@ public abstract class ItemMetaBuilder implements TagWritable { @Contract("_ -> this") public @NotNull ItemMetaBuilder canDestroy(@NotNull Set<@NotNull Block> blocks) { - this.canDestroy = blocks; + this.canDestroy = new HashSet<>(blocks); handleCollection(canDestroy, "CanDestroy", nbt, () -> { NBTList list = new NBTList<>(NBTTypes.TAG_String); canDestroy.forEach(block -> list.add(new NBTString(block.getName()))); @@ -187,7 +191,7 @@ public abstract class ItemMetaBuilder implements TagWritable { @Override public void setTag(@NotNull Tag tag, @Nullable T value) { - tag.write(nbt, value); + mutateNbt(compound -> tag.write(compound, value)); } public @NotNull ItemMetaBuilder set(@NotNull Tag tag, @Nullable T value) { @@ -202,30 +206,51 @@ public abstract class ItemMetaBuilder implements TagWritable { protected abstract @NotNull Supplier<@NotNull ItemMetaBuilder> getSupplier(); + protected void mutateNbt(Consumer consumer) { + if (built) { + built = false; + final var currentNbt = nbt; + NBT_UPDATER.compareAndSet(this, currentNbt, currentNbt.deepClone()); + } + synchronized (this) { + consumer.accept(nbt); + } + } + + protected synchronized NBTCompound nbt() { + return nbt; + } + + protected @NotNull ItemMeta generate() { + this.built = true; + return build(); + } + protected void handleCompound(@NotNull String key, @NotNull Consumer<@NotNull NBTCompound> consumer) { - NBTCompound compound = null; - boolean newNbt = false; - if (nbt.containsKey(key)) { - NBT dNbt = nbt.get(key); - if (dNbt instanceof NBTCompound) { - compound = (NBTCompound) dNbt; - } - } else { - compound = new NBTCompound(); - newNbt = true; - } - - if (compound != null) { - consumer.accept(compound); - - if (newNbt && compound.getSize() > 0) { - this.nbt.set(key, compound); - } else if (!newNbt && compound.getSize() == 0) { - this.nbt.removeTag(key); + mutateNbt(nbt -> { + NBTCompound compound = null; + boolean newNbt = false; + if (nbt.containsKey(key)) { + NBT dNbt = nbt.get(key); + if (dNbt instanceof NBTCompound) { + compound = (NBTCompound) dNbt; + } + } else { + compound = new NBTCompound(); + newNbt = true; } - } + if (compound != null) { + consumer.accept(compound); + + if (newNbt && compound.getSize() > 0) { + this.nbt.set(key, compound); + } else if (!newNbt && compound.getSize() == 0) { + this.nbt.removeTag(key); + } + } + }); } protected void handleNullable(@Nullable Object value, @@ -239,6 +264,12 @@ public abstract class ItemMetaBuilder implements TagWritable { } } + protected void handleNullable(@Nullable Object value, + @NotNull String key, + @NotNull Supplier<@NotNull NBT> supplier) { + mutateNbt(compound -> handleNullable(value, key, compound, supplier)); + } + protected void handleCollection(@NotNull Collection objects, @NotNull String key, @NotNull NBTCompound nbtCompound, @@ -250,6 +281,12 @@ public abstract class ItemMetaBuilder implements TagWritable { } } + protected void handleCollection(@NotNull Collection objects, + @NotNull String key, + @NotNull Supplier<@NotNull NBT> supplier) { + mutateNbt(compound -> handleCollection(objects, key, compound, supplier)); + } + protected void handleMap(@NotNull Map objects, @NotNull String key, @NotNull NBTCompound nbtCompound, @@ -271,5 +308,4 @@ public abstract class ItemMetaBuilder implements TagWritable { public interface Provider { } - } diff --git a/src/main/java/net/minestom/server/item/ItemStackBuilder.java b/src/main/java/net/minestom/server/item/ItemStackBuilder.java index 75a2b42f7..168f8d43a 100644 --- a/src/main/java/net/minestom/server/item/ItemStackBuilder.java +++ b/src/main/java/net/minestom/server/item/ItemStackBuilder.java @@ -109,9 +109,9 @@ public class ItemStackBuilder { @Contract(value = "-> new", pure = true) public @NotNull ItemStack build() { - if (amount > 0) - return new ItemStack(material, amount, metaBuilder.build(), stackingRule); - return ItemStack.AIR; + if (amount < 1) + return ItemStack.AIR; + return new ItemStack(material, amount, metaBuilder.generate(), stackingRule); } private static final class DefaultMeta extends ItemMetaBuilder { @@ -130,5 +130,4 @@ public class ItemStackBuilder { return DefaultMeta::new; } } - } diff --git a/src/main/java/net/minestom/server/item/metadata/CompassMeta.java b/src/main/java/net/minestom/server/item/metadata/CompassMeta.java index a046f0597..2393fbe90 100644 --- a/src/main/java/net/minestom/server/item/metadata/CompassMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/CompassMeta.java @@ -45,18 +45,20 @@ public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0))); return this; } public Builder lodestoneDimension(@Nullable String lodestoneDimension) { this.lodestoneDimension = lodestoneDimension; - if (lodestoneDimension != null) { - this.nbt.setString("LodestoneDimension", lodestoneDimension); - } else { - this.nbt.removeTag("LodestoneDimension"); - } + mutateNbt(compound -> { + if (lodestoneDimension != null) { + compound.setString("LodestoneDimension", lodestoneDimension); + } else { + compound.removeTag("LodestoneDimension"); + } + }); return this; } @@ -64,15 +66,17 @@ public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider { + 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()); + compound.set("LodestonePos", posCompound); + } else { + compound.removeTag("LodestonePos"); + } + }); return this; } diff --git a/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java b/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java index c08236ecf..5aa7d45f3 100644 --- a/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/CrossbowMeta.java @@ -97,7 +97,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.set("ChargedProjectiles", chargedProjectiles)); return this; } @@ -123,7 +123,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.set("ChargedProjectiles", chargedProjectiles)); return this; } @@ -135,7 +135,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.setByte("Charged", (byte) (charged ? 1 : 0))); return this; } diff --git a/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java b/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java index 3f5c218ab..bed14c215 100644 --- a/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/EnchantedBookMeta.java @@ -35,13 +35,13 @@ public class EnchantedBookMeta extends ItemMeta implements ItemMetaBuilder.Provi private Map enchantments = new HashMap<>(); - public @NotNull Builder enchantments(Map enchantments) { + public @NotNull Builder enchantments(@NotNull Map enchantments) { this.enchantments = enchantments; - NBTUtils.writeEnchant(nbt, "StoredEnchantments", enchantments); + mutateNbt(compound -> NBTUtils.writeEnchant(compound, "StoredEnchantments", enchantments)); return this; } - public @NotNull Builder enchantment(Enchantment enchantment, short level) { + public @NotNull Builder enchantment(@NotNull Enchantment enchantment, short level) { this.enchantments.put(enchantment, level); enchantments(enchantments); return this; diff --git a/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java b/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java index 1af345b36..373dfcb3d 100644 --- a/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/FireworkEffectMeta.java @@ -28,7 +28,7 @@ public class FireworkEffectMeta extends ItemMeta implements ItemMetaBuilder.Prov public Builder effect(@Nullable FireworkEffect fireworkEffect) { this.fireworkEffect = fireworkEffect; - this.nbt.set("Explosion", this.fireworkEffect.asCompound()); + mutateNbt(compound -> compound.set("Explosion", this.fireworkEffect.asCompound())); return this; } diff --git a/src/main/java/net/minestom/server/item/metadata/MapMeta.java b/src/main/java/net/minestom/server/item/metadata/MapMeta.java index d720714a0..a3e86d19d 100644 --- a/src/main/java/net/minestom/server/item/metadata/MapMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/MapMeta.java @@ -91,18 +91,18 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.setInt("map", mapId)); return this; } public Builder mapScaleDirection(int value) { this.mapScaleDirection = value; - this.nbt.setInt("map_scale_direction", value); + mutateNbt(compound -> compound.setInt("map_scale_direction", value)); return this; } public Builder decorations(List value) { - this.decorations = value; + this.decorations = new ArrayList<>(value); NBTList decorationsList = new NBTList<>(NBTTypes.TAG_Compound); for (MapDecoration decoration : decorations) { @@ -115,7 +115,7 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.set("Decorations", decorationsList)); return this; } @@ -123,14 +123,16 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider { + NBTCompound displayCompound; + if (nbt.containsKey("display")) { + displayCompound = nbt.getCompound("display"); + } else { + displayCompound = new NBTCompound(); + nbt.set("display", displayCompound); + } + displayCompound.setInt("MapColor", mapColor.asRGB()); + }); return this; } diff --git a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java index a764b7cfa..e7ee9ad7d 100644 --- a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java @@ -52,7 +52,7 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.setString("Potion", potionType.getNamespaceID().asString())); return this; } @@ -71,14 +71,14 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider compound.set("CustomPotionEffects", potionList)); return this; } public Builder color(@NotNull Color color) { this.color = color; - this.nbt.setInt("CustomPotionColor", color.asRGB()); + mutateNbt(compound -> compound.setInt("CustomPotionColor", color.asRGB())); return this; } diff --git a/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java index f108845ec..761d5261a 100644 --- a/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WritableBookMeta.java @@ -2,7 +2,6 @@ package net.minestom.server.item.metadata; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.minestom.server.adventure.AdventureSerializer; import net.minestom.server.item.ItemMeta; import net.minestom.server.item.ItemMetaBuilder; import org.jetbrains.annotations.NotNull; @@ -53,22 +52,22 @@ public class WritableBookMeta extends ItemMeta implements ItemMetaBuilder.Provid public Builder author(@Nullable String author) { this.author = author; - handleNullable(author, "author", nbt, + handleNullable(author, "author", () -> new NBTString(Objects.requireNonNull(author))); return this; } public Builder title(@Nullable String title) { this.title = title; - handleNullable(title, "title", nbt, + handleNullable(title, "title", () -> new NBTString(Objects.requireNonNull(title))); return this; } public Builder pages(@NotNull List<@NotNull Component> pages) { - this.pages = pages; + this.pages = new ArrayList<>(pages); - handleCollection(pages, "pages", nbt, () -> { + handleCollection(pages, "pages", () -> { NBTList list = new NBTList<>(NBTTypes.TAG_String); for (Component page : pages) { list.add(new NBTString(GsonComponentSerializer.gson().serialize(page))); diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index 3ce191ae4..86e79a7f5 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -86,35 +86,35 @@ public class WrittenBookMeta extends ItemMeta implements ItemMetaBuilder.Provide public Builder resolved(boolean resolved) { this.resolved = resolved; - this.nbt.setByte("resolved", (byte) (resolved ? 1 : 0)); + mutateNbt(compound -> compound.setByte("resolved", (byte) (resolved ? 1 : 0))); return this; } public Builder generation(@Nullable WrittenBookGeneration generation) { this.generation = generation; - handleNullable(generation, "generation", nbt, + handleNullable(generation, "generation", () -> new NBTInt(Objects.requireNonNull(generation).ordinal())); return this; } public Builder author(@Nullable String author) { this.author = author; - handleNullable(author, "author", nbt, + handleNullable(author, "author", () -> new NBTString(Objects.requireNonNull(author))); return this; } public Builder title(@Nullable String title) { this.title = title; - handleNullable(title, "title", nbt, + handleNullable(title, "title", () -> new NBTString(Objects.requireNonNull(title))); return this; } public Builder pages(@NotNull List<@NotNull Component> pages) { - this.pages = pages; + this.pages = new ArrayList<>(pages); - handleCollection(pages, "pages", nbt, () -> { + handleCollection(pages, "pages", () -> { NBTList list = new NBTList<>(NBTTypes.TAG_String); for (Component page : pages) { list.add(new NBTString(GsonComponentSerializer.gson().serialize(page)));