Initial reusable meta builder

This commit is contained in:
TheMode 2021-06-27 17:36:37 +02:00
parent edaec0cb6d
commit e772aace71
11 changed files with 127 additions and 87 deletions

View File

@ -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();
}

View File

@ -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<ItemMetaBuilder, NBTCompound> 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<NBTString> 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<Enchantment, Short> 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<NBTCompound> 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<NBTString> 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<NBTString> 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 <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
tag.write(nbt, value);
mutateNbt(compound -> tag.write(compound, value));
}
public <T> @NotNull ItemMetaBuilder set(@NotNull Tag<T> 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<NBTCompound> 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<T extends ItemMetaBuilder> {
}
}

View File

@ -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;
}
}
}

View File

@ -45,18 +45,20 @@ public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider<Co
public Builder lodestoneTracked(boolean lodestoneTracked) {
this.lodestoneTracked = lodestoneTracked;
this.nbt.setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
mutateNbt(compound -> 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<Co
public Builder lodestonePosition(@Nullable Position lodestonePosition) {
this.lodestonePosition = lodestonePosition;
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());
this.nbt.set("LodestonePos", posCompound);
} else {
this.nbt.removeTag("LodestonePos");
}
mutateNbt(compound -> {
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;
}

View File

@ -97,7 +97,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<S
if (!projectile.isAir()) {
chargedProjectiles.add(getItemCompound(projectile));
}
this.nbt.set("ChargedProjectiles", chargedProjectiles);
mutateNbt(compound -> compound.set("ChargedProjectiles", chargedProjectiles));
return this;
}
@ -123,7 +123,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<S
chargedProjectiles.add(getItemCompound(projectile1));
chargedProjectiles.add(getItemCompound(projectile2));
chargedProjectiles.add(getItemCompound(projectile3));
this.nbt.set("ChargedProjectiles", chargedProjectiles);
mutateNbt(compound -> compound.set("ChargedProjectiles", chargedProjectiles));
return this;
}
@ -135,7 +135,7 @@ public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<S
*/
public Builder charged(boolean charged) {
this.charged = charged;
this.nbt.setByte("Charged", (byte) (charged ? 1 : 0));
mutateNbt(compound -> compound.setByte("Charged", (byte) (charged ? 1 : 0)));
return this;
}

View File

@ -35,13 +35,13 @@ public class EnchantedBookMeta extends ItemMeta implements ItemMetaBuilder.Provi
private Map<Enchantment, Short> enchantments = new HashMap<>();
public @NotNull Builder enchantments(Map<Enchantment, Short> enchantments) {
public @NotNull Builder enchantments(@NotNull Map<Enchantment, Short> 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;

View File

@ -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;
}

View File

@ -91,18 +91,18 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMet
public Builder mapId(int value) {
this.mapId = value;
this.nbt.setInt("map", mapId);
mutateNbt(compound -> 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<MapDecoration> value) {
this.decorations = value;
this.decorations = new ArrayList<>(value);
NBTList<NBTCompound> decorationsList = new NBTList<>(NBTTypes.TAG_Compound);
for (MapDecoration decoration : decorations) {
@ -115,7 +115,7 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMet
decorationsList.add(decorationCompound);
}
this.nbt.set("Decorations", decorationsList);
mutateNbt(compound -> compound.set("Decorations", decorationsList));
return this;
}
@ -123,14 +123,16 @@ public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMet
public Builder mapColor(Color value) {
this.mapColor = value;
NBTCompound displayCompound;
if (nbt.containsKey("display")) {
displayCompound = nbt.getCompound("display");
} else {
displayCompound = new NBTCompound();
this.nbt.set("display", displayCompound);
}
displayCompound.setInt("MapColor", mapColor.asRGB());
mutateNbt(nbt -> {
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;
}

View File

@ -52,7 +52,7 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<Pot
public Builder potionType(@NotNull PotionType potionType) {
this.potionType = potionType;
this.nbt.setString("Potion", potionType.getNamespaceID().asString());
mutateNbt(compound -> compound.setString("Potion", potionType.getNamespaceID().asString()));
return this;
}
@ -71,14 +71,14 @@ public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<Pot
potionList.add(potionCompound);
}
this.nbt.set("CustomPotionEffects", potionList);
mutateNbt(compound -> 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;
}

View File

@ -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<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
for (Component page : pages) {
list.add(new NBTString(GsonComponentSerializer.gson().serialize(page)));

View File

@ -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<NBTString> list = new NBTList<>(NBTTypes.TAG_String);
for (Component page : pages) {
list.add(new NBTString(GsonComponentSerializer.gson().serialize(page)));