mirror of
https://github.com/Minestom/Minestom.git
synced 2024-09-28 14:37:31 +02:00
Item API refactoring (#904)
This commit is contained in:
parent
0875becdc7
commit
63b40deb34
@ -49,9 +49,9 @@ public class CampfireHandler implements BlockHandler {
|
||||
NBTType.TAG_Compound,
|
||||
value.stream()
|
||||
.map(item -> NBT.Compound(nbt -> {
|
||||
nbt.setByte("Count", (byte) item.getAmount());
|
||||
nbt.setByte("Count", (byte) item.amount());
|
||||
nbt.setByte("Slot", (byte) 1);
|
||||
nbt.setString("id", item.getMaterial().name());
|
||||
nbt.setString("id", item.material().name());
|
||||
}))
|
||||
.toList()
|
||||
));
|
||||
|
@ -211,7 +211,7 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
||||
PickupItemEvent pickupItemEvent = new PickupItemEvent(this, itemEntity);
|
||||
EventDispatcher.callCancellable(pickupItemEvent, () -> {
|
||||
final ItemStack item = itemEntity.getItemStack();
|
||||
sendPacketToViewersAndSelf(new CollectItemPacket(itemEntity.getEntityId(), getEntityId(), item.getAmount()));
|
||||
sendPacketToViewersAndSelf(new CollectItemPacket(itemEntity.getEntityId(), getEntityId(), item.amount()));
|
||||
itemEntity.remove();
|
||||
});
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
for (var player : MinecraftServer.getConnectionManager().getOnlinePlayers()) {
|
||||
if (player != this) sendPacket(player.getAddPlayerToList());
|
||||
}
|
||||
|
||||
|
||||
//Teams
|
||||
for (Team team : MinecraftServer.getTeamManager().getTeams()) {
|
||||
sendPacket(team.createTeamsCreationPacket());
|
||||
@ -355,7 +355,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
refreshActiveHand(false, isOffHand, false);
|
||||
|
||||
final ItemStack foodItem = itemUpdateStateEvent.getItemStack();
|
||||
final boolean isFood = foodItem.getMaterial().isFood();
|
||||
final boolean isFood = foodItem.material().isFood();
|
||||
|
||||
if (isFood) {
|
||||
PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, foodItem, eatingHand);
|
||||
@ -747,7 +747,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
@Override
|
||||
public void openBook(@NotNull Book book) {
|
||||
final ItemStack writtenBook = ItemStack.builder(Material.WRITTEN_BOOK)
|
||||
.meta(WrittenBookMeta.fromAdventure(book, this))
|
||||
.meta(WrittenBookMeta.class, builder -> builder.resolved(false)
|
||||
.generation(WrittenBookMeta.WrittenBookGeneration.ORIGINAL)
|
||||
.author(book.author())
|
||||
.title(book.title())
|
||||
.pages(book.pages()))
|
||||
.build();
|
||||
// Set book in offhand
|
||||
playerConnection.sendPacket(new SetSlotPacket((byte) 0, 0, (short) PlayerInventoryUtils.OFFHAND_SLOT, writtenBook));
|
||||
@ -1814,7 +1818,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
return null;
|
||||
|
||||
final ItemStack updatedItem = getItemInHand(hand);
|
||||
final boolean isFood = updatedItem.getMaterial().isFood();
|
||||
final boolean isFood = updatedItem.material().isFood();
|
||||
|
||||
if (isFood && !allowFood)
|
||||
return null;
|
||||
@ -2098,9 +2102,13 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
return mainHand;
|
||||
}
|
||||
|
||||
public boolean enableTextFiltering() { return enableTextFiltering; }
|
||||
public boolean enableTextFiltering() {
|
||||
return enableTextFiltering;
|
||||
}
|
||||
|
||||
public boolean allowServerListings() { return allowServerListings; }
|
||||
public boolean allowServerListings() {
|
||||
return allowServerListings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the player settings internally.
|
||||
|
@ -126,7 +126,7 @@ public final class InventoryClickProcessor {
|
||||
|
||||
// Handle armor equip
|
||||
if (inventory instanceof PlayerInventory && targetInventory instanceof PlayerInventory) {
|
||||
final Material material = clicked.getMaterial();
|
||||
final Material material = clicked.material();
|
||||
final EquipmentSlot equipmentSlot = material.registry().equipmentSlot();
|
||||
if (equipmentSlot != null) {
|
||||
// Shift-click equip
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
/**
|
||||
* Represents a hide flag which can be applied to an {@link ItemStack} using {@link ItemMetaBuilder#hideFlag(int)}.
|
||||
* Represents a hide flag which can be applied to an {@link ItemStack} using {@link ItemMeta.Builder#hideFlag(int)}.
|
||||
*/
|
||||
public enum ItemHideFlag {
|
||||
HIDE_ENCHANTS,
|
||||
|
@ -5,7 +5,7 @@ import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import net.minestom.server.utils.binary.Writeable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -13,162 +13,161 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ItemMeta implements TagReadable, Writeable {
|
||||
|
||||
private final int damage;
|
||||
private final boolean unbreakable;
|
||||
private final int hideFlag;
|
||||
private final Component displayName;
|
||||
private final List<Component> lore;
|
||||
|
||||
private final Map<Enchantment, Short> enchantmentMap;
|
||||
private final List<ItemAttribute> attributes;
|
||||
|
||||
private final int customModelData;
|
||||
|
||||
private final Set<Block> canDestroy;
|
||||
private final Set<Block> canPlaceOn;
|
||||
|
||||
private final ItemMetaBuilder metaBuilder;
|
||||
private final NBTCompound nbt;
|
||||
|
||||
private String cachedSNBT;
|
||||
private ByteBuffer cachedBuffer;
|
||||
|
||||
protected ItemMeta(@NotNull ItemMetaBuilder metaBuilder) {
|
||||
this.damage = metaBuilder.damage;
|
||||
this.unbreakable = metaBuilder.unbreakable;
|
||||
this.hideFlag = metaBuilder.hideFlag;
|
||||
this.displayName = metaBuilder.displayName;
|
||||
this.lore = List.copyOf(metaBuilder.lore);
|
||||
this.enchantmentMap = Map.copyOf(metaBuilder.enchantmentMap);
|
||||
this.attributes = List.copyOf(metaBuilder.attributes);
|
||||
this.customModelData = metaBuilder.customModelData;
|
||||
this.canDestroy = Set.copyOf(metaBuilder.canDestroy);
|
||||
this.canPlaceOn = Set.copyOf(metaBuilder.canPlaceOn);
|
||||
|
||||
this.metaBuilder = metaBuilder;
|
||||
this.nbt = metaBuilder.nbt.toCompound();
|
||||
}
|
||||
public sealed interface ItemMeta extends TagReadable, Writeable
|
||||
permits ItemMetaImpl {
|
||||
@Override
|
||||
<T> @UnknownNullability T getTag(@NotNull Tag<T> tag);
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemMeta with(@NotNull Consumer<@NotNull ItemMetaBuilder> builderConsumer) {
|
||||
var builder = builder();
|
||||
builderConsumer.accept(builder);
|
||||
return builder.build();
|
||||
@NotNull ItemMeta with(@NotNull Consumer<@NotNull Builder> builderConsumer);
|
||||
|
||||
@NotNull NBTCompound toNBT();
|
||||
|
||||
@NotNull String toSNBT();
|
||||
|
||||
@Contract(pure = true)
|
||||
default int getDamage() {
|
||||
return getTag(ItemTags.DAMAGE);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getDamage() {
|
||||
return damage;
|
||||
default boolean isUnbreakable() {
|
||||
return getTag(ItemTags.UNBREAKABLE);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isUnbreakable() {
|
||||
return unbreakable;
|
||||
default int getHideFlag() {
|
||||
return getTag(ItemTags.HIDE_FLAGS);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getHideFlag() {
|
||||
return hideFlag;
|
||||
default @Nullable Component getDisplayName() {
|
||||
return getTag(ItemTags.NAME);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @Nullable Component getDisplayName() {
|
||||
return displayName;
|
||||
default @NotNull List<@NotNull Component> getLore() {
|
||||
return getTag(ItemTags.LORE);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull List<@NotNull Component> getLore() {
|
||||
return lore;
|
||||
default @NotNull Map<Enchantment, Short> getEnchantmentMap() {
|
||||
return getTag(ItemTags.ENCHANTMENTS);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Map<Enchantment, Short> getEnchantmentMap() {
|
||||
return enchantmentMap;
|
||||
default @NotNull List<@NotNull ItemAttribute> getAttributes() {
|
||||
return getTag(ItemTags.ATTRIBUTES);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull List<@NotNull ItemAttribute> getAttributes() {
|
||||
return attributes;
|
||||
default int getCustomModelData() {
|
||||
return getTag(ItemTags.CUSTOM_MODEL_DATA);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getCustomModelData() {
|
||||
return customModelData;
|
||||
default @NotNull Set<@NotNull Block> getCanDestroy() {
|
||||
return Set.copyOf(getTag(ItemTags.CAN_DESTROY));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Set<@NotNull Block> getCanDestroy() {
|
||||
return canDestroy;
|
||||
default @NotNull Set<@NotNull Block> getCanPlaceOn() {
|
||||
return Set.copyOf(getTag(ItemTags.CAN_PLACE_ON));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Set<@NotNull Block> getCanPlaceOn() {
|
||||
return canPlaceOn;
|
||||
}
|
||||
sealed interface Builder extends Taggable permits ItemMetaImpl.Builder {
|
||||
@NotNull ItemMeta build();
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(nbt);
|
||||
}
|
||||
|
||||
public @NotNull NBTCompound toNBT() {
|
||||
return nbt;
|
||||
}
|
||||
|
||||
public @NotNull String toSNBT() {
|
||||
if (cachedSNBT == null) {
|
||||
this.cachedSNBT = nbt.toSNBT();
|
||||
default <T> @NotNull Builder set(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
setTag(tag, value);
|
||||
return this;
|
||||
}
|
||||
return cachedSNBT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ItemMeta itemMeta = (ItemMeta) o;
|
||||
return nbt.equals(itemMeta.nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nbt.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "{" +
|
||||
"nbt=" + nbt +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
protected @NotNull ItemMetaBuilder builder() {
|
||||
ItemMetaBuilder result = metaBuilder.createEmpty();
|
||||
ItemMetaBuilder.resetMeta(result, nbt);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(@NotNull BinaryWriter writer) {
|
||||
if (nbt.isEmpty()) {
|
||||
writer.writeByte((byte) 0);
|
||||
return;
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder damage(int damage) {
|
||||
return set(ItemTags.DAMAGE, damage);
|
||||
}
|
||||
if (cachedBuffer == null) {
|
||||
BinaryWriter w = new BinaryWriter();
|
||||
w.writeNBT("", nbt);
|
||||
this.cachedBuffer = w.getBuffer();
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder unbreakable(boolean unbreakable) {
|
||||
return set(ItemTags.UNBREAKABLE, unbreakable);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder hideFlag(int hideFlag) {
|
||||
return set(ItemTags.HIDE_FLAGS, hideFlag);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder hideFlag(@NotNull ItemHideFlag... hideFlags) {
|
||||
int result = 0;
|
||||
for (ItemHideFlag hideFlag : hideFlags) result |= hideFlag.getBitFieldPart();
|
||||
return hideFlag(result);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder displayName(@Nullable Component displayName) {
|
||||
return set(ItemTags.NAME, displayName);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder lore(@NotNull List<? extends Component> lore) {
|
||||
return set(ItemTags.LORE, lore.isEmpty() ? null : List.class.cast(lore));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder lore(Component... lore) {
|
||||
return lore(Arrays.asList(lore));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder enchantments(@NotNull Map<Enchantment, Short> enchantments) {
|
||||
return set(ItemTags.ENCHANTMENTS, Map.copyOf(enchantments));
|
||||
}
|
||||
|
||||
@Contract("_, _ -> this")
|
||||
default @NotNull Builder enchantment(@NotNull Enchantment enchantment, short level) {
|
||||
var enchantments = new HashMap<>(getTag(ItemTags.ENCHANTMENTS));
|
||||
enchantments.put(enchantment, level);
|
||||
return enchantments(enchantments);
|
||||
}
|
||||
|
||||
@Contract("-> this")
|
||||
default @NotNull Builder clearEnchantment() {
|
||||
return enchantments(Map.of());
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder attributes(@NotNull List<@NotNull ItemAttribute> attributes) {
|
||||
return set(ItemTags.ATTRIBUTES, attributes.isEmpty() ? null : attributes);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder customModelData(int customModelData) {
|
||||
return set(ItemTags.CUSTOM_MODEL_DATA, customModelData);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder canPlaceOn(@NotNull Set<@NotNull Block> blocks) {
|
||||
return set(ItemTags.CAN_PLACE_ON, List.copyOf(blocks));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder canPlaceOn(@NotNull Block... blocks) {
|
||||
return canPlaceOn(Set.of(blocks));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder canDestroy(@NotNull Set<@NotNull Block> blocks) {
|
||||
return set(ItemTags.CAN_DESTROY, List.copyOf(blocks));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
default @NotNull Builder canDestroy(@NotNull Block... blocks) {
|
||||
return canDestroy(Set.of(blocks));
|
||||
}
|
||||
writer.write(cachedBuffer.flip());
|
||||
}
|
||||
}
|
||||
|
@ -1,353 +0,0 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minestom.server.attribute.Attribute;
|
||||
import net.minestom.server.attribute.AttributeOperation;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.AttributeSlot;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagWritable;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class ItemMetaBuilder implements TagWritable {
|
||||
MutableNBTCompound nbt = new MutableNBTCompound();
|
||||
|
||||
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 Set<Block> canDestroy = new HashSet<>();
|
||||
protected Set<Block> canPlaceOn = new HashSet<>();
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder damage(int damage) {
|
||||
this.damage = damage;
|
||||
this.nbt.setInt("Damage", damage);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder unbreakable(boolean unbreakable) {
|
||||
this.unbreakable = unbreakable;
|
||||
this.nbt.set("Unbreakable", NBT.Boolean(unbreakable));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder hideFlag(int hideFlag) {
|
||||
this.hideFlag = hideFlag;
|
||||
this.nbt.setInt("HideFlags", hideFlag);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder hideFlag(@NotNull ItemHideFlag... hideFlags) {
|
||||
int result = 0;
|
||||
for (ItemHideFlag hideFlag : hideFlags) result |= hideFlag.getBitFieldPart();
|
||||
return hideFlag(result);
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder displayName(@Nullable Component displayName) {
|
||||
this.displayName = displayName;
|
||||
handleCompound("display", nbtCompound -> {
|
||||
if (displayName != null) {
|
||||
nbtCompound.setString("Name", GsonComponentSerializer.gson().serialize(displayName));
|
||||
} else {
|
||||
nbtCompound.remove("Name");
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder lore(@NotNull List<? extends Component> lore) {
|
||||
this.lore = new ArrayList<>(lore);
|
||||
handleCompound("display", nbtCompound -> {
|
||||
final NBTList<NBTString> loreNBT = NBT.List(NBTType.TAG_String,
|
||||
lore.stream()
|
||||
.map(line -> new NBTString(GsonComponentSerializer.gson().serialize(line)))
|
||||
.toList()
|
||||
);
|
||||
if (loreNBT.isEmpty()) {
|
||||
nbtCompound.remove("Lore");
|
||||
} else {
|
||||
nbtCompound.set("Lore", loreNBT);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder lore(Component... lore) {
|
||||
lore(Arrays.asList(lore));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder enchantments(@NotNull Map<Enchantment, Short> enchantments) {
|
||||
this.enchantmentMap = new HashMap<>(enchantments);
|
||||
handleMap(enchantmentMap, "Enchantments",
|
||||
(nbt) -> NBTUtils.writeEnchant(nbt, "Enchantments", enchantmentMap));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_, _ -> this")
|
||||
public @NotNull ItemMetaBuilder enchantment(@NotNull Enchantment enchantment, short level) {
|
||||
this.enchantmentMap.put(enchantment, level);
|
||||
enchantments(enchantmentMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("-> this")
|
||||
public @NotNull ItemMetaBuilder clearEnchantment() {
|
||||
this.enchantmentMap = new HashMap<>();
|
||||
this.nbt.remove("Enchantments");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder attributes(@NotNull List<@NotNull ItemAttribute> attributes) {
|
||||
this.attributes = new ArrayList<>(attributes);
|
||||
handleCollection(attributes, "AttributeModifiers", () -> NBT.List(NBTType.TAG_Compound,
|
||||
attributes.stream()
|
||||
.map(itemAttribute -> NBT.Compound(Map.of(
|
||||
"UUID", NBT.IntArray(Utils.uuidToIntArray(itemAttribute.uuid())),
|
||||
"Amount", NBT.Double(itemAttribute.amount()),
|
||||
"Slot", NBT.String(itemAttribute.slot().name().toLowerCase()),
|
||||
"AttributeName", NBT.String(itemAttribute.attribute().key()),
|
||||
"Operation", NBT.Int(itemAttribute.operation().getId()),
|
||||
"Name", NBT.String(itemAttribute.name()))))
|
||||
.toList()
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder customModelData(int customModelData) {
|
||||
this.customModelData = customModelData;
|
||||
this.nbt.setInt("CustomModelData", customModelData);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canPlaceOn(@NotNull Set<@NotNull Block> blocks) {
|
||||
this.canPlaceOn = new HashSet<>(blocks);
|
||||
handleCollection(canPlaceOn, "CanPlaceOn", () -> NBT.List(
|
||||
NBTType.TAG_String,
|
||||
canPlaceOn.stream()
|
||||
.map(block -> new NBTString(block.name()))
|
||||
.toList()
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canPlaceOn(@NotNull Block... blocks) {
|
||||
return canPlaceOn(Set.of(blocks));
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canDestroy(@NotNull Set<@NotNull Block> blocks) {
|
||||
this.canDestroy = new HashSet<>(blocks);
|
||||
handleCollection(canDestroy, "CanDestroy", () -> NBT.List(
|
||||
NBTType.TAG_String,
|
||||
canDestroy.stream()
|
||||
.map(block -> new NBTString(block.name()))
|
||||
.toList()
|
||||
));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("_ -> this")
|
||||
public @NotNull ItemMetaBuilder canDestroy(@NotNull Block... blocks) {
|
||||
return canDestroy(Set.of(blocks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(nbt, value);
|
||||
}
|
||||
|
||||
public <T> @NotNull ItemMetaBuilder set(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
setTag(tag, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract("-> new")
|
||||
public abstract @NotNull ItemMeta build();
|
||||
|
||||
public abstract void read(@NotNull NBTCompound nbtCompound);
|
||||
|
||||
protected @NotNull ItemMetaBuilder createEmpty() {
|
||||
try {
|
||||
var constructor = getClass().getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected MutableNBTCompound mutableNbt() {
|
||||
return nbt;
|
||||
}
|
||||
|
||||
protected void mutateNbt(Consumer<MutableNBTCompound> consumer) {
|
||||
consumer.accept(nbt);
|
||||
}
|
||||
|
||||
protected void handleCompound(@NotNull String key,
|
||||
@NotNull Consumer<@NotNull MutableNBTCompound> consumer) {
|
||||
MutableNBTCompound compoundToModify = nbt.get(key) instanceof NBTCompound compound ?
|
||||
compound.toMutableCompound() : new MutableNBTCompound();
|
||||
consumer.accept(compoundToModify);
|
||||
if (compoundToModify.isEmpty()) {
|
||||
this.nbt.remove(key);
|
||||
} else {
|
||||
this.nbt.set(key, compoundToModify.toCompound());
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleNullable(@Nullable Object value,
|
||||
@NotNull String key,
|
||||
@NotNull Supplier<@NotNull NBT> supplier) {
|
||||
if (value != null) {
|
||||
this.nbt.set(key, supplier.get());
|
||||
} else {
|
||||
this.nbt.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleCollection(@NotNull Collection<?> objects,
|
||||
@NotNull String key,
|
||||
@NotNull Supplier<@NotNull NBT> supplier) {
|
||||
if (!objects.isEmpty()) {
|
||||
this.nbt.set(key, supplier.get());
|
||||
} else {
|
||||
this.nbt.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleMap(@NotNull Map<?, ?> objects,
|
||||
@NotNull String key,
|
||||
@NotNull Consumer<MutableNBTCompound> consumer) {
|
||||
if (!objects.isEmpty()) {
|
||||
consumer.accept(nbt);
|
||||
} else {
|
||||
nbt.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void resetMeta(@NotNull ItemMetaBuilder src, @NotNull NBTCompound nbtCompound) {
|
||||
src.nbt.copyFrom(nbtCompound);
|
||||
appendMeta(src, nbtCompound);
|
||||
}
|
||||
|
||||
private static void appendMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||
@NotNull NBTCompound nbt) {
|
||||
if (nbt.get("Damage") instanceof NBTInt damage) metaBuilder.damage = damage.getValue();
|
||||
if (nbt.get("Unbreakable") instanceof NBTByte unbreakable) metaBuilder.unbreakable = unbreakable.asBoolean();
|
||||
if (nbt.get("HideFlags") instanceof NBTInt hideFlags) metaBuilder.hideFlag = hideFlags.getValue();
|
||||
if (nbt.get("display") instanceof NBTCompound display) {
|
||||
if (display.get("Name") instanceof NBTString rawName) {
|
||||
metaBuilder.displayName = GsonComponentSerializer.gson().deserialize(rawName.getValue());
|
||||
}
|
||||
if (display.get("Lore") instanceof NBTList<?> loreList &&
|
||||
loreList.getSubtagType() == NBTType.TAG_String) {
|
||||
for (NBTString rawLore : loreList.<NBTString>asListOf()) {
|
||||
metaBuilder.lore.add(GsonComponentSerializer.gson().deserialize(rawLore.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Enchantments
|
||||
if (nbt.get("Enchantments") instanceof NBTList<?> nbtEnchants &&
|
||||
nbtEnchants.getSubtagType() == NBTType.TAG_Compound) {
|
||||
NBTUtils.loadEnchantments(nbtEnchants.asListOf(),
|
||||
(enchantment, level) -> metaBuilder.enchantmentMap.put(enchantment, level));
|
||||
}
|
||||
// Attributes
|
||||
if (nbt.get("AttributeModifiers") instanceof NBTList<?> nbtAttributes &&
|
||||
nbtAttributes.getSubtagType() == NBTType.TAG_Compound) {
|
||||
for (NBTCompound attributeNBT : nbtAttributes.<NBTCompound>asListOf()) {
|
||||
final UUID uuid;
|
||||
{
|
||||
final int[] uuidArray = attributeNBT.getIntArray("UUID").copyArray();
|
||||
uuid = Utils.intArrayToUuid(uuidArray);
|
||||
}
|
||||
|
||||
final double value = attributeNBT.getAsDouble("Amount");
|
||||
final String slot = attributeNBT.containsKey("Slot") ? attributeNBT.getString("Slot") : "MAINHAND";
|
||||
final String attributeName = attributeNBT.getString("AttributeName");
|
||||
final int operation = attributeNBT.getAsInt("Operation");
|
||||
final String name = attributeNBT.getString("Name");
|
||||
|
||||
final Attribute attribute = Attribute.fromKey(attributeName);
|
||||
// Wrong attribute name, stop here
|
||||
if (attribute == null)
|
||||
break;
|
||||
final AttributeOperation attributeOperation = AttributeOperation.fromId(operation);
|
||||
// Wrong attribute operation, stop here
|
||||
if (attributeOperation == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Find slot, default to the main hand if the nbt tag is invalid
|
||||
AttributeSlot attributeSlot;
|
||||
try {
|
||||
attributeSlot = AttributeSlot.valueOf(slot.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
attributeSlot = AttributeSlot.MAINHAND;
|
||||
}
|
||||
|
||||
// Add attribute
|
||||
final ItemAttribute itemAttribute =
|
||||
new ItemAttribute(uuid, name, attribute, attributeOperation, value, attributeSlot);
|
||||
metaBuilder.attributes.add(itemAttribute);
|
||||
}
|
||||
}
|
||||
// Custom model data
|
||||
if (nbt.get("CustomModelData") instanceof NBTInt customModelData) {
|
||||
metaBuilder.customModelData = customModelData.getValue();
|
||||
}
|
||||
// CanPlaceOn
|
||||
if (nbt.get("CanPlaceOn") instanceof NBTList<?> canPlaceOn &&
|
||||
canPlaceOn.getSubtagType() == NBTType.TAG_String) {
|
||||
for (NBTString blockNamespace : canPlaceOn.<NBTString>asListOf()) {
|
||||
Block block = Block.fromNamespaceId(blockNamespace.getValue());
|
||||
metaBuilder.canPlaceOn.add(block);
|
||||
}
|
||||
}
|
||||
// CanDestroy
|
||||
if (nbt.get("CanDestroy") instanceof NBTList<?> canDestroy &&
|
||||
canDestroy.getSubtagType() == NBTType.TAG_String) {
|
||||
for (NBTString blockNamespace : canDestroy.<NBTString>asListOf()) {
|
||||
Block block = Block.fromNamespaceId(blockNamespace.getValue());
|
||||
metaBuilder.canDestroy.add(block);
|
||||
}
|
||||
}
|
||||
// Meta specific fields
|
||||
metaBuilder.read(nbt);
|
||||
}
|
||||
|
||||
public interface Provider<T extends ItemMetaBuilder> {
|
||||
}
|
||||
}
|
69
src/main/java/net/minestom/server/item/ItemMetaImpl.java
Normal file
69
src/main/java/net/minestom/server/item/ItemMetaImpl.java
Normal file
@ -0,0 +1,69 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
record ItemMetaImpl(TagHandler tagHandler) implements ItemMeta {
|
||||
static final ItemMeta EMPTY = new ItemMetaImpl(TagHandler.newHandler());
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tagHandler.getTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemMeta with(@NotNull Consumer<ItemMeta.@NotNull Builder> builderConsumer) {
|
||||
Builder builder = new Builder(tagHandler.copy());
|
||||
builderConsumer.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NBTCompound toNBT() {
|
||||
return tagHandler.asCompound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String toSNBT() {
|
||||
return toNBT().toSNBT();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull BinaryWriter writer) {
|
||||
final NBTCompound nbt = toNBT();
|
||||
if (nbt.isEmpty()) {
|
||||
writer.writeByte((byte) 0);
|
||||
return;
|
||||
}
|
||||
BinaryWriter w = new BinaryWriter();
|
||||
w.writeNBT("", nbt);
|
||||
var cachedBuffer = w.getBuffer();
|
||||
writer.write(cachedBuffer.flip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ItemMetaImpl itemMeta)) return false;
|
||||
return toNBT().equals(itemMeta.toNBT());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(toNBT());
|
||||
}
|
||||
|
||||
record Builder(TagHandler tagHandler) implements ItemMeta.Builder {
|
||||
@Override
|
||||
public @NotNull ItemMeta build() {
|
||||
return new ItemMetaImpl(tagHandler.copy());
|
||||
}
|
||||
}
|
||||
}
|
13
src/main/java/net/minestom/server/item/ItemMetaView.java
Normal file
13
src/main/java/net/minestom/server/item/ItemMetaView.java
Normal file
@ -0,0 +1,13 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
@ApiStatus.Experimental
|
||||
public interface ItemMetaView<T extends ItemMetaView.Builder> extends TagReadable {
|
||||
@ApiStatus.Experimental
|
||||
interface Builder extends Taggable {
|
||||
}
|
||||
}
|
37
src/main/java/net/minestom/server/item/ItemMetaViewImpl.java
Normal file
37
src/main/java/net/minestom/server/item/ItemMetaViewImpl.java
Normal file
@ -0,0 +1,37 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
final class ItemMetaViewImpl {
|
||||
static <V extends ItemMetaView.Builder, T extends ItemMetaView<V>> Class<V> viewType(Class<T> metaClass) {
|
||||
final Type type = metaClass.getGenericInterfaces()[0];
|
||||
return (Class<V>) ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
static <T extends ItemMetaView<?>> T construct(Class<T> metaClass, TagReadable tagReadable) {
|
||||
try {
|
||||
final Constructor<T> cons = metaClass.getDeclaredConstructor(TagReadable.class);
|
||||
return cons.newInstance(tagReadable);
|
||||
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
|
||||
IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static <V extends ItemMetaView.Builder, T extends ItemMetaView<V>> V constructBuilder(Class<T> metaClass, TagHandler tagHandler) {
|
||||
final Class<V> clazz = viewType(metaClass);
|
||||
try {
|
||||
final Constructor<V> cons = clazz.getDeclaredConstructor(TagHandler.class);
|
||||
return cons.newInstance(tagHandler);
|
||||
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
|
||||
IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
86
src/main/java/net/minestom/server/item/ItemSerializers.java
Normal file
86
src/main/java/net/minestom/server/item/ItemSerializers.java
Normal file
@ -0,0 +1,86 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.attribute.Attribute;
|
||||
import net.minestom.server.attribute.AttributeOperation;
|
||||
import net.minestom.server.item.attribute.AttributeSlot;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.tag.TagSerializer;
|
||||
import net.minestom.server.tag.TagWritable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ItemSerializers {
|
||||
public static final TagSerializer<EnchantmentEntry> ENCHANTMENT_SERIALIZER = new TagSerializer<>() {
|
||||
static final Tag<Short> LEVEL = Tag.Short("Level");
|
||||
static final Tag<String> ID = Tag.String("Id");
|
||||
|
||||
@Override
|
||||
public @Nullable EnchantmentEntry read(@NotNull TagReadable reader) {
|
||||
final String id = reader.getTag(ID);
|
||||
final Short level = reader.getTag(LEVEL);
|
||||
if (id == null || level == null) return null;
|
||||
final Enchantment enchantment = Enchantment.fromNamespaceId(id);
|
||||
return new EnchantmentEntry(enchantment, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull EnchantmentEntry value) {
|
||||
writer.setTag(ID, value.enchantment.name());
|
||||
writer.setTag(LEVEL, value.level);
|
||||
}
|
||||
};
|
||||
|
||||
public record EnchantmentEntry(Enchantment enchantment, short level) {
|
||||
}
|
||||
|
||||
static final TagSerializer<ItemAttribute> ATTRIBUTE_SERIALIZER = new TagSerializer<>() {
|
||||
static final Tag<UUID> ID = Tag.UUID("UUID");
|
||||
static final Tag<Double> AMOUNT = Tag.Double("Amount");
|
||||
static final Tag<String> SLOT = Tag.String("Slot").defaultValue("MAINHAND");
|
||||
static final Tag<String> ATTRIBUTE_NAME = Tag.String("AttributeName");
|
||||
static final Tag<Integer> OPERATION = Tag.Integer("Operation");
|
||||
static final Tag<String> NAME = Tag.String("Name");
|
||||
|
||||
@Override
|
||||
public @Nullable ItemAttribute read(@NotNull TagReadable reader) {
|
||||
final UUID uuid = reader.getTag(ID);
|
||||
final double amount = reader.getTag(AMOUNT);
|
||||
final String slot = reader.getTag(SLOT);
|
||||
final String attributeName = reader.getTag(ATTRIBUTE_NAME);
|
||||
final int operation = reader.getTag(OPERATION);
|
||||
final String name = reader.getTag(NAME);
|
||||
|
||||
final Attribute attribute = Attribute.fromKey(attributeName);
|
||||
// Wrong attribute name, stop here
|
||||
if (attribute == null) return null;
|
||||
final AttributeOperation attributeOperation = AttributeOperation.fromId(operation);
|
||||
// Wrong attribute operation, stop here
|
||||
if (attributeOperation == null) return null;
|
||||
|
||||
// Find slot, default to the main hand if the nbt tag is invalid
|
||||
AttributeSlot attributeSlot;
|
||||
try {
|
||||
attributeSlot = AttributeSlot.valueOf(slot.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
attributeSlot = AttributeSlot.MAINHAND;
|
||||
}
|
||||
return new ItemAttribute(uuid, name, attribute, attributeOperation, amount, attributeSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull ItemAttribute value) {
|
||||
writer.setTag(ID, value.uuid());
|
||||
writer.setTag(AMOUNT, value.amount());
|
||||
writer.setTag(SLOT, value.slot().name());
|
||||
writer.setTag(ATTRIBUTE_NAME, value.attribute().key());
|
||||
writer.setTag(OPERATION, value.operation().getId());
|
||||
writer.setTag(NAME, value.name());
|
||||
}
|
||||
};
|
||||
}
|
@ -3,20 +3,15 @@ package net.minestom.server.item;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.event.HoverEventSource;
|
||||
import net.minestom.server.item.rule.VanillaStackingRule;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.*;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTByte;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTString;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.function.UnaryOperator;
|
||||
@ -27,50 +22,36 @@ import java.util.function.UnaryOperator;
|
||||
* <p>
|
||||
* An item stack cannot be null, {@link ItemStack#AIR} should be used instead.
|
||||
*/
|
||||
public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent.ShowItem> {
|
||||
|
||||
static final @NotNull VanillaStackingRule DEFAULT_STACKING_RULE = new VanillaStackingRule();
|
||||
|
||||
public sealed interface ItemStack extends TagReadable, HoverEventSource<HoverEvent.ShowItem>
|
||||
permits ItemStackImpl {
|
||||
/**
|
||||
* Constant AIR item. Should be used instead of 'null'.
|
||||
*/
|
||||
public static final @NotNull ItemStack AIR = ItemStack.of(Material.AIR);
|
||||
|
||||
private final Material material;
|
||||
private final int amount;
|
||||
private final ItemMeta meta;
|
||||
|
||||
ItemStack(@NotNull Material material, int amount,
|
||||
@NotNull ItemMeta meta) {
|
||||
this.material = material;
|
||||
this.amount = amount;
|
||||
this.meta = meta;
|
||||
}
|
||||
@NotNull ItemStack AIR = ItemStack.of(Material.AIR);
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public static @NotNull ItemStackBuilder builder(@NotNull Material material) {
|
||||
return new ItemStackBuilder(material);
|
||||
static @NotNull Builder builder(@NotNull Material material) {
|
||||
return new ItemStackImpl.Builder(material, 1);
|
||||
}
|
||||
|
||||
@Contract(value = "_ ,_ -> new", pure = true)
|
||||
public static @NotNull ItemStack of(@NotNull Material material, int amount) {
|
||||
return builder(material).amount(amount).build();
|
||||
static @NotNull ItemStack of(@NotNull Material material, int amount) {
|
||||
return ItemStackImpl.create(material, amount);
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public static @NotNull ItemStack of(@NotNull Material material) {
|
||||
static @NotNull ItemStack of(@NotNull Material material) {
|
||||
return of(material, 1);
|
||||
}
|
||||
|
||||
@Contract(value = "_, _, _ -> new", pure = true)
|
||||
public static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound, int amount) {
|
||||
ItemMetaBuilder builder = ItemStackBuilder.getMetaBuilder(material);
|
||||
if (nbtCompound != null) ItemMetaBuilder.resetMeta(builder, nbtCompound);
|
||||
return new ItemStack(material, amount, builder.build());
|
||||
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound, int amount) {
|
||||
if (nbtCompound == null) return of(material, amount);
|
||||
return builder(material).amount(amount).meta(nbtCompound).build();
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound) {
|
||||
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound) {
|
||||
return fromNBT(material, nbtCompound, 1);
|
||||
}
|
||||
|
||||
@ -80,7 +61,7 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
|
||||
* @param nbtCompound The nbt representation of the item
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static @NotNull ItemStack fromItemNBT(@NotNull NBTCompound nbtCompound) {
|
||||
static @NotNull ItemStack fromItemNBT(@NotNull NBTCompound nbtCompound) {
|
||||
String id = nbtCompound.getString("id");
|
||||
Check.notNull(id, "Item NBT must contain an id field.");
|
||||
Material material = Material.fromNamespaceId(id);
|
||||
@ -93,137 +74,106 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack with(@NotNull Consumer<@NotNull ItemStackBuilder> builderConsumer) {
|
||||
var builder = builder();
|
||||
builderConsumer.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
@NotNull Material material();
|
||||
|
||||
@Contract(pure = true)
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
int amount();
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack withAmount(int amount) {
|
||||
if (amount < 1) return AIR;
|
||||
return new ItemStack(material, amount, meta);
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack withAmount(@NotNull IntUnaryOperator intUnaryOperator) {
|
||||
return withAmount(intUnaryOperator.applyAsInt(amount));
|
||||
}
|
||||
@Contract(pure = true)
|
||||
@NotNull ItemMeta meta();
|
||||
|
||||
@Contract(pure = true)
|
||||
@ApiStatus.Experimental
|
||||
<T extends ItemMetaView<?>> @NotNull T meta(@NotNull Class<T> metaClass);
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack consume(int amount) {
|
||||
return DEFAULT_STACKING_RULE.apply(this, currentAmount -> currentAmount - amount);
|
||||
}
|
||||
@NotNull ItemStack with(@NotNull Consumer<@NotNull Builder> builderConsumer);
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public <T extends ItemMetaBuilder, U extends ItemMetaBuilder.Provider<T>> @NotNull ItemStack withMeta(Class<U> metaType, Consumer<T> metaConsumer) {
|
||||
return builder().meta(metaType, metaConsumer).build();
|
||||
}
|
||||
@ApiStatus.Experimental
|
||||
<V extends ItemMetaView.Builder, T extends ItemMetaView<V>> @NotNull ItemStack withMeta(@NotNull Class<T> metaType,
|
||||
@NotNull Consumer<V> metaConsumer);
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public <T extends ItemMetaBuilder> @NotNull ItemStack withMeta(@NotNull UnaryOperator<@NotNull T> metaOperator) {
|
||||
return builder().meta(metaOperator).build();
|
||||
@NotNull ItemStack withMeta(@NotNull UnaryOperator<ItemMeta.@NotNull Builder> metaOperator);
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
default @NotNull ItemStack withMaterial(@NotNull Material material) {
|
||||
return ItemStackImpl.create(material, amount(), meta());
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
default @NotNull ItemStack withAmount(int amount) {
|
||||
return ItemStackImpl.create(material(), amount, meta());
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
default @NotNull ItemStack withAmount(@NotNull IntUnaryOperator intUnaryOperator) {
|
||||
return withAmount(intUnaryOperator.applyAsInt(amount()));
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
@NotNull ItemStack consume(int amount);
|
||||
|
||||
@Contract(pure = true)
|
||||
default @Nullable Component getDisplayName() {
|
||||
return meta().getDisplayName();
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
default @NotNull List<@NotNull Component> getLore() {
|
||||
return meta().getLore();
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public @NotNull ItemStack withMeta(@NotNull ItemMeta meta) {
|
||||
return new ItemStack(material, amount, meta);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @Nullable Component getDisplayName() {
|
||||
return meta.getDisplayName();
|
||||
default @NotNull ItemStack withMeta(@NotNull ItemMeta meta) {
|
||||
return new ItemStackImpl(material(), amount(), meta);
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack withDisplayName(@Nullable Component displayName) {
|
||||
return builder().displayName(displayName).build();
|
||||
default @NotNull ItemStack withDisplayName(@Nullable Component displayName) {
|
||||
return withMeta(builder -> builder.displayName(displayName));
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack withDisplayName(@NotNull UnaryOperator<@Nullable Component> componentUnaryOperator) {
|
||||
default @NotNull ItemStack withDisplayName(@NotNull UnaryOperator<@Nullable Component> componentUnaryOperator) {
|
||||
return withDisplayName(componentUnaryOperator.apply(getDisplayName()));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull List<@NotNull Component> getLore() {
|
||||
return meta.getLore();
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
default @NotNull ItemStack withLore(@NotNull List<? extends Component> lore) {
|
||||
return withMeta(builder -> builder.lore(lore));
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack withLore(@NotNull List<? extends Component> lore) {
|
||||
return builder().lore(lore).build();
|
||||
}
|
||||
|
||||
@Contract(value = "_, -> new", pure = true)
|
||||
public @NotNull ItemStack withLore(@NotNull UnaryOperator<@NotNull List<@NotNull Component>> loreUnaryOperator) {
|
||||
default @NotNull ItemStack withLore(@NotNull UnaryOperator<@NotNull List<@NotNull Component>> loreUnaryOperator) {
|
||||
return withLore(loreUnaryOperator.apply(getLore()));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public @NotNull ItemMeta getMeta() {
|
||||
return meta;
|
||||
default boolean isAir() {
|
||||
return material() == Material.AIR;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isAir() {
|
||||
return material == Material.AIR;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public boolean isSimilar(@NotNull ItemStack itemStack) {
|
||||
return material == itemStack.material &&
|
||||
meta.equals(itemStack.meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ItemStack itemStack)) return false;
|
||||
return amount == itemStack.amount && material.equals(itemStack.material) && meta.equals(itemStack.meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(material, amount, meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ItemStack{" +
|
||||
"material=" + material +
|
||||
", amount=" + amount +
|
||||
", meta=" + meta +
|
||||
'}';
|
||||
}
|
||||
boolean isSimilar(@NotNull ItemStack itemStack);
|
||||
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public <T> @NotNull ItemStack withTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
return builder().meta(metaBuilder -> metaBuilder.set(tag, value)).build();
|
||||
default <T> @NotNull ItemStack withTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
return withMeta(builder -> builder.set(tag, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return meta.getTag(tag);
|
||||
default <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return meta().getTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
|
||||
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(this.material,
|
||||
this.amount,
|
||||
NBTUtils.asBinaryTagHolder(this.meta.toNBT()))));
|
||||
default @NotNull HoverEvent<HoverEvent.ShowItem> asHoverEvent(@NotNull UnaryOperator<HoverEvent.ShowItem> op) {
|
||||
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(material(), amount(),
|
||||
NBTUtils.asBinaryTagHolder(meta().toNBT()))));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,16 +182,62 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
|
||||
* @return The nbt representation of the item
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public @NotNull NBTCompound toItemNBT() {
|
||||
final NBTString material = NBT.String(getMaterial().name());
|
||||
final NBTByte amount = NBT.Byte(getAmount());
|
||||
final NBTCompound nbt = getMeta().toNBT();
|
||||
if (nbt.isEmpty()) return NBT.Compound(Map.of("id", material, "Count", amount));
|
||||
return NBT.Compound(Map.of("id", material, "Count", amount, "tag", nbt));
|
||||
@NotNull NBTCompound toItemNBT();
|
||||
|
||||
|
||||
@Deprecated
|
||||
@Contract(pure = true)
|
||||
default @NotNull Material getMaterial() {
|
||||
return material();
|
||||
}
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
private @NotNull ItemStackBuilder builder() {
|
||||
return new ItemStackBuilder(material, meta.builder()).amount(amount);
|
||||
@Deprecated
|
||||
@Contract(pure = true)
|
||||
default int getAmount() {
|
||||
return amount();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Contract(pure = true)
|
||||
default @NotNull ItemMeta getMeta() {
|
||||
return meta();
|
||||
}
|
||||
|
||||
sealed interface Builder permits ItemStackImpl.Builder {
|
||||
@Contract(value = "_ -> this")
|
||||
@NotNull Builder amount(int amount);
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
@NotNull Builder meta(@NotNull TagHandler tagHandler);
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
@NotNull Builder meta(@NotNull NBTCompound compound);
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
@NotNull Builder meta(@NotNull ItemMeta itemMeta);
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
@NotNull Builder meta(@NotNull UnaryOperator<ItemMeta.@NotNull Builder> consumer);
|
||||
|
||||
@Contract(value = "_, _ -> this")
|
||||
<V extends ItemMetaView.Builder, T extends ItemMetaView<V>> @NotNull Builder meta(@NotNull Class<T> metaType, @NotNull Consumer<@NotNull V> itemMetaConsumer);
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
@NotNull ItemStack build();
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
default @NotNull Builder displayName(@Nullable Component displayName) {
|
||||
return meta(builder -> builder.displayName(displayName));
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
default @NotNull Builder lore(@NotNull List<? extends Component> lore) {
|
||||
return meta(builder -> builder.lore(lore));
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
default @NotNull Builder lore(Component... lore) {
|
||||
return meta(builder -> builder.lore(lore));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,123 +0,0 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.item.metadata.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public final class ItemStackBuilder {
|
||||
private final Material material;
|
||||
private int amount;
|
||||
private ItemMetaBuilder metaBuilder;
|
||||
|
||||
ItemStackBuilder(@NotNull Material material, @NotNull ItemMetaBuilder metaBuilder) {
|
||||
this.material = material;
|
||||
this.amount = 1;
|
||||
this.metaBuilder = metaBuilder;
|
||||
}
|
||||
|
||||
private static final Map<Material, Supplier<ItemMetaBuilder>> MATERIAL_SUPPLIER_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.POTION, PotionMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LINGERING_POTION, PotionMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.SPLASH_POTION, PotionMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.TIPPED_ARROW, PotionMeta.Builder::new);
|
||||
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.FILLED_MAP, MapMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.COMPASS, CompassMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.ENCHANTED_BOOK, EnchantedBookMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.CROSSBOW, CrossbowMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.WRITABLE_BOOK, WritableBookMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.WRITTEN_BOOK, WrittenBookMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.FIREWORK_STAR, FireworkEffectMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.FIREWORK_ROCKET, FireworkMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.PLAYER_HEAD, PlayerHeadMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.BUNDLE, BundleMeta.Builder::new);
|
||||
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_HELMET, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_CHESTPLATE, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_LEGGINGS, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_BOOTS, LeatherArmorMeta.Builder::new);
|
||||
MATERIAL_SUPPLIER_MAP.put(Material.LEATHER_HORSE_ARMOR, LeatherArmorMeta.Builder::new);
|
||||
}
|
||||
|
||||
static ItemMetaBuilder getMetaBuilder(Material material) {
|
||||
Supplier<ItemMetaBuilder> supplier = MATERIAL_SUPPLIER_MAP.get(material);
|
||||
return supplier != null ? supplier.get() : new DefaultMeta();
|
||||
}
|
||||
|
||||
ItemStackBuilder(@NotNull Material material) {
|
||||
this(material, getMetaBuilder(material));
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder amount(int amount) {
|
||||
this.amount = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder meta(@NotNull ItemMeta itemMeta) {
|
||||
this.metaBuilder = itemMeta.builder();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public <T extends ItemMetaBuilder> @NotNull ItemStackBuilder meta(@NotNull UnaryOperator<@NotNull T> itemMetaConsumer) {
|
||||
//noinspection unchecked
|
||||
this.metaBuilder = itemMetaConsumer.apply((T) metaBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_, _ -> this")
|
||||
public <T extends ItemMetaBuilder, U extends ItemMetaBuilder.Provider<T>> @NotNull ItemStackBuilder meta(@NotNull Class<U> metaType, @NotNull Consumer<@NotNull T> itemMetaConsumer) {
|
||||
itemMetaConsumer.accept((T) metaBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder displayName(@Nullable Component displayName) {
|
||||
this.metaBuilder.displayName(displayName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder lore(@NotNull List<? extends Component> lore) {
|
||||
this.metaBuilder.lore(lore);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> this")
|
||||
public @NotNull ItemStackBuilder lore(Component... lore) {
|
||||
this.metaBuilder.lore(lore);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
public @NotNull ItemStack build() {
|
||||
if (amount < 1) return ItemStack.AIR;
|
||||
return new ItemStack(material, amount, metaBuilder.build());
|
||||
}
|
||||
|
||||
private static final class DefaultMeta extends ItemMetaBuilder {
|
||||
@Override
|
||||
public @NotNull ItemMeta build() {
|
||||
return new ItemMeta(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
// Empty
|
||||
}
|
||||
}
|
||||
}
|
136
src/main/java/net/minestom/server/item/ItemStackImpl.java
Normal file
136
src/main/java/net/minestom/server/item/ItemStackImpl.java
Normal file
@ -0,0 +1,136 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.item.rule.VanillaStackingRule;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTByte;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTString;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
record ItemStackImpl(Material material, int amount, ItemMeta meta) implements ItemStack {
|
||||
static final @NotNull VanillaStackingRule DEFAULT_STACKING_RULE = new VanillaStackingRule();
|
||||
|
||||
static ItemStack create(Material material, int amount, ItemMeta meta) {
|
||||
if (amount <= 0) return AIR;
|
||||
return new ItemStackImpl(material, amount, meta);
|
||||
}
|
||||
|
||||
static ItemStack create(Material material, int amount) {
|
||||
return create(material, amount, ItemMetaImpl.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ItemMetaView<?>> @NotNull T meta(@NotNull Class<T> metaClass) {
|
||||
return ItemMetaViewImpl.construct(metaClass, meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack with(@NotNull Consumer<ItemStack.@NotNull Builder> builderConsumer) {
|
||||
ItemStack.Builder builder = builder();
|
||||
builderConsumer.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <V extends ItemMetaView.Builder, T extends ItemMetaView<V>> ItemStack withMeta(@NotNull Class<T> metaType,
|
||||
@NotNull Consumer<V> metaConsumer) {
|
||||
return builder().meta(metaType, metaConsumer).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack withMeta(@NotNull UnaryOperator<ItemMeta.@NotNull Builder> metaOperator) {
|
||||
return builder().meta(metaOperator).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack consume(int amount) {
|
||||
return DEFAULT_STACKING_RULE.apply(this, currentAmount -> currentAmount - amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimilar(@NotNull ItemStack itemStack) {
|
||||
return material == itemStack.material() && meta.equals(itemStack.meta());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NBTCompound toItemNBT() {
|
||||
final NBTString material = NBT.String(material().name());
|
||||
final NBTByte amount = NBT.Byte(amount());
|
||||
final NBTCompound nbt = meta().toNBT();
|
||||
if (nbt.isEmpty()) return NBT.Compound(Map.of("id", material, "Count", amount));
|
||||
return NBT.Compound(Map.of("id", material, "Count", amount, "tag", nbt));
|
||||
}
|
||||
|
||||
@Contract(value = "-> new", pure = true)
|
||||
private @NotNull ItemStack.Builder builder() {
|
||||
return new Builder(material, amount, new ItemMetaImpl.Builder(TagHandler.fromCompound(meta.toNBT())));
|
||||
}
|
||||
|
||||
static final class Builder implements ItemStack.Builder {
|
||||
final Material material;
|
||||
int amount;
|
||||
ItemMeta.Builder metaBuilder;
|
||||
|
||||
Builder(Material material, int amount, ItemMeta.Builder metaBuilder) {
|
||||
this.material = material;
|
||||
this.amount = amount;
|
||||
this.metaBuilder = metaBuilder;
|
||||
}
|
||||
|
||||
Builder(Material material, int amount) {
|
||||
this(material, amount, new ItemMetaImpl.Builder(TagHandler.newHandler()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack.@NotNull Builder amount(int amount) {
|
||||
this.amount = amount;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack.@NotNull Builder meta(@NotNull TagHandler tagHandler) {
|
||||
return metaBuilder(new ItemMetaImpl.Builder(tagHandler.copy()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack.@NotNull Builder meta(@NotNull NBTCompound compound) {
|
||||
return metaBuilder(new ItemMetaImpl.Builder(TagHandler.fromCompound(compound)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack.@NotNull Builder meta(@NotNull ItemMeta itemMeta) {
|
||||
final TagHandler tagHandler = ((ItemMetaImpl) itemMeta).tagHandler();
|
||||
return metaBuilder(new ItemMetaImpl.Builder(tagHandler.copy()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack.@NotNull Builder meta(@NotNull UnaryOperator<ItemMeta.Builder> consumer) {
|
||||
this.metaBuilder = consumer.apply(metaBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V extends ItemMetaView.Builder, T extends ItemMetaView<V>> ItemStack.@NotNull Builder meta(@NotNull Class<T> metaType,
|
||||
@NotNull Consumer<@NotNull V> itemMetaConsumer) {
|
||||
V view = ItemMetaViewImpl.constructBuilder(metaType, metaBuilder.tagHandler());
|
||||
itemMetaConsumer.accept(view);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack build() {
|
||||
return ItemStackImpl.create(material, amount, metaBuilder.build());
|
||||
}
|
||||
|
||||
private ItemStack.@NotNull Builder metaBuilder(@NotNull ItemMeta.Builder builder) {
|
||||
this.metaBuilder = builder;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
35
src/main/java/net/minestom/server/item/ItemTags.java
Normal file
35
src/main/java/net/minestom/server/item/ItemTags.java
Normal file
@ -0,0 +1,35 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.attribute.ItemAttribute;
|
||||
import net.minestom.server.registry.ProtocolObject;
|
||||
import net.minestom.server.tag.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.minestom.server.item.ItemSerializers.*;
|
||||
|
||||
final class ItemTags {
|
||||
static final Tag<Integer> DAMAGE = Tag.Integer("Damage").defaultValue(0);
|
||||
static final Tag<Boolean> UNBREAKABLE = Tag.Boolean("Unbreakable").defaultValue(false);
|
||||
static final Tag<Integer> HIDE_FLAGS = Tag.Integer("HideFlags").defaultValue(0);
|
||||
static final Tag<Integer> CUSTOM_MODEL_DATA = Tag.Integer("CustomModelData").defaultValue(0);
|
||||
static final Tag<Component> NAME = Tag.Component("Name").path("display");
|
||||
static final Tag<List<Component>> LORE = Tag.Component("Lore").path("display").list().defaultValue(List.of());
|
||||
static final Tag<Map<Enchantment, Short>> ENCHANTMENTS = Tag.Structure("Enchantments", ENCHANTMENT_SERIALIZER).list().map(enchantmentEntry -> {
|
||||
Map<Enchantment, Short> map = new HashMap<>();
|
||||
for (var entry : enchantmentEntry) map.put(entry.enchantment(), entry.level());
|
||||
return Map.copyOf(map);
|
||||
}, o -> {
|
||||
List<EnchantmentEntry> entries = new ArrayList<>();
|
||||
for (var entry : o.entrySet()) entries.add(new EnchantmentEntry(entry.getKey(), entry.getValue()));
|
||||
return List.copyOf(entries);
|
||||
}).defaultValue(Map.of());
|
||||
static final Tag<List<ItemAttribute>> ATTRIBUTES = Tag.Structure("AttributeModifiers", ATTRIBUTE_SERIALIZER).list().defaultValue(List.of());
|
||||
static final Tag<List<Block>> CAN_PLACE_ON = Tag.String("CanPlaceOn").map(Block::fromNamespaceId, ProtocolObject::name).list().defaultValue(List.of());
|
||||
static final Tag<List<Block>> CAN_DESTROY = Tag.String("CanDestroy").map(Block::fromNamespaceId, ProtocolObject::name).list().defaultValue(List.of());
|
||||
}
|
@ -13,7 +13,7 @@ import java.util.function.IntUnaryOperator;
|
||||
public interface StackingRule {
|
||||
|
||||
static @NotNull StackingRule get() {
|
||||
return ItemStack.DEFAULT_STACKING_RULE;
|
||||
return ItemStackImpl.DEFAULT_STACKING_RULE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,73 +1,48 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTType;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public class BundleMeta extends ItemMeta implements ItemMetaBuilder.Provider<BundleMeta.Builder> {
|
||||
|
||||
private final List<ItemStack> items;
|
||||
|
||||
protected BundleMeta(ItemMetaBuilder metaBuilder,
|
||||
@NotNull List<ItemStack> items) {
|
||||
super(metaBuilder);
|
||||
this.items = List.copyOf(items);
|
||||
}
|
||||
public record BundleMeta(TagReadable readable) implements ItemMetaView<BundleMeta.Builder> {
|
||||
private static final Tag<List<ItemStack>> ITEMS = Tag.ItemStack("Items").list().defaultValue(List.of());
|
||||
|
||||
public @NotNull List<ItemStack> getItems() {
|
||||
return items;
|
||||
return getTag(ITEMS);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
private List<ItemStack> items = new ArrayList<>();
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder items(@NotNull List<ItemStack> items) {
|
||||
this.items = new ArrayList<>(items); // defensive copy
|
||||
updateItems();
|
||||
setTag(ITEMS, items);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public Builder addItem(@NotNull ItemStack item) {
|
||||
items.add(item);
|
||||
updateItems();
|
||||
return this;
|
||||
var newList = new ArrayList<>(getTag(ITEMS));
|
||||
newList.add(item);
|
||||
return items(newList);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public Builder removeItem(@NotNull ItemStack item) {
|
||||
items.remove(item);
|
||||
updateItems();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BundleMeta build() {
|
||||
return new BundleMeta(this, items);
|
||||
}
|
||||
|
||||
private void updateItems() {
|
||||
mutableNbt().set("Items", NBT.List(NBTType.TAG_Compound, items.size(), i -> items.get(i).toItemNBT()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("Items") instanceof NBTList<?> list &&
|
||||
list.getSubtagType() == NBTType.TAG_Compound) {
|
||||
for (NBTCompound item : list.<NBTCompound>asListOf()) {
|
||||
this.items.add(ItemStack.fromItemNBT(item));
|
||||
}
|
||||
}
|
||||
var newList = new ArrayList<>(getTag(ITEMS));
|
||||
newList.remove(item);
|
||||
return items(newList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,99 +2,64 @@ package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.tag.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTByte;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTString;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.Map;
|
||||
public record CompassMeta(TagReadable readable) implements ItemMetaView<CompassMeta.Builder> {
|
||||
private static final Tag<Boolean> LODESTONE_TRACKED = Tag.Boolean("LodestoneTracked").defaultValue(false);
|
||||
private static final Tag<String> LODESTONE_DIMENSION = Tag.String("LodestoneDimension");
|
||||
private static final Tag<Point> LODESTONE_POSITION = Tag.Structure("LodestonePos", new TagSerializer<>() {
|
||||
@Override
|
||||
public @Nullable Point read(@NotNull TagReadable reader) {
|
||||
final Integer x = reader.getTag(Tag.Integer("X"));
|
||||
final Integer y = reader.getTag(Tag.Integer("Y"));
|
||||
final Integer z = reader.getTag(Tag.Integer("Z"));
|
||||
if (x == null || y == null || z == null) return null;
|
||||
return new Vec(x, y, z);
|
||||
}
|
||||
|
||||
public class CompassMeta extends ItemMeta implements ItemMetaBuilder.Provider<CompassMeta.Builder> {
|
||||
|
||||
private final boolean lodestoneTracked;
|
||||
private final String lodestoneDimension;
|
||||
private final Point lodestonePosition;
|
||||
|
||||
protected CompassMeta(ItemMetaBuilder metaBuilder,
|
||||
boolean lodestoneTracked,
|
||||
@Nullable String lodestoneDimension,
|
||||
@Nullable Point lodestonePosition) {
|
||||
super(metaBuilder);
|
||||
this.lodestoneTracked = lodestoneTracked;
|
||||
this.lodestoneDimension = lodestoneDimension;
|
||||
this.lodestonePosition = lodestonePosition;
|
||||
}
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull Point value) {
|
||||
writer.setTag(Tag.Integer("X"), value.blockX());
|
||||
writer.setTag(Tag.Integer("Y"), value.blockY());
|
||||
writer.setTag(Tag.Integer("Z"), value.blockZ());
|
||||
}
|
||||
});
|
||||
|
||||
public boolean isLodestoneTracked() {
|
||||
return lodestoneTracked;
|
||||
return getTag(LODESTONE_TRACKED);
|
||||
}
|
||||
|
||||
public @Nullable String getLodestoneDimension() {
|
||||
return lodestoneDimension;
|
||||
return getTag(LODESTONE_DIMENSION);
|
||||
}
|
||||
|
||||
public @Nullable Point getLodestonePosition() {
|
||||
return lodestonePosition;
|
||||
return getTag(LODESTONE_POSITION);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private boolean lodestoneTracked;
|
||||
private String lodestoneDimension;
|
||||
private Point lodestonePosition;
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder lodestoneTracked(boolean lodestoneTracked) {
|
||||
this.lodestoneTracked = lodestoneTracked;
|
||||
mutableNbt().setByte("LodestoneTracked", (byte) (lodestoneTracked ? 1 : 0));
|
||||
setTag(LODESTONE_TRACKED, lodestoneTracked);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder lodestoneDimension(@Nullable String lodestoneDimension) {
|
||||
this.lodestoneDimension = lodestoneDimension;
|
||||
if (lodestoneDimension != null) {
|
||||
mutableNbt().setString("LodestoneDimension", lodestoneDimension);
|
||||
} else {
|
||||
mutableNbt().remove("LodestoneDimension");
|
||||
}
|
||||
setTag(LODESTONE_DIMENSION, lodestoneDimension);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder lodestonePosition(@Nullable Point lodestonePosition) {
|
||||
this.lodestonePosition = lodestonePosition;
|
||||
if (lodestonePosition != null) {
|
||||
mutableNbt().set("LodestonePos", NBT.Compound(Map.of(
|
||||
"X", NBT.Int(lodestonePosition.blockX()),
|
||||
"Y", NBT.Int(lodestonePosition.blockY()),
|
||||
"Z", NBT.Int(lodestonePosition.blockZ()))));
|
||||
} else {
|
||||
mutableNbt().remove("LodestonePos");
|
||||
}
|
||||
setTag(LODESTONE_POSITION, lodestonePosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompassMeta build() {
|
||||
return new CompassMeta(this, lodestoneTracked, lodestoneDimension, lodestonePosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("LodestoneTracked") instanceof NBTByte tracked) {
|
||||
this.lodestoneTracked = tracked.asBoolean();
|
||||
}
|
||||
if (nbtCompound.get("LodestoneDimension") instanceof NBTString dimension) {
|
||||
this.lodestoneDimension = dimension.getValue();
|
||||
}
|
||||
if (nbtCompound.get("LodestonePos") instanceof NBTCompound posCompound) {
|
||||
final int x = posCompound.getInt("X");
|
||||
final int y = posCompound.getInt("Y");
|
||||
final int z = posCompound.getInt("Z");
|
||||
this.lodestonePosition = new Vec(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,174 +1,46 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CrossbowMeta extends ItemMeta implements ItemMetaBuilder.Provider<SpawnEggMeta.Builder> {
|
||||
public record CrossbowMeta(TagReadable readable) implements ItemMetaView<CrossbowMeta.Builder> {
|
||||
private static final Tag<List<ItemStack>> PROJECTILES = Tag.ItemStack("ChargedProjectiles").list();
|
||||
private static final Tag<Boolean> CHARGED = Tag.Boolean("Charged");
|
||||
|
||||
private final boolean triple;
|
||||
private final ItemStack projectile1, projectile2, projectile3;
|
||||
private final boolean charged;
|
||||
|
||||
protected CrossbowMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||
boolean triple,
|
||||
ItemStack projectile1, ItemStack projectile2, ItemStack projectile3,
|
||||
boolean charged) {
|
||||
super(metaBuilder);
|
||||
this.triple = triple;
|
||||
this.projectile1 = projectile1;
|
||||
this.projectile2 = projectile2;
|
||||
this.projectile3 = projectile3;
|
||||
this.charged = charged;
|
||||
public @NotNull List<ItemStack> getProjectiles() {
|
||||
return getTag(PROJECTILES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this crossbow is charged with 3 projectiles.
|
||||
*
|
||||
* @return true if this crossbow is charged with 3 projectiles, false otherwise
|
||||
*/
|
||||
public boolean isTriple() {
|
||||
return triple;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first projectile.
|
||||
*
|
||||
* @return the first projectile
|
||||
*/
|
||||
public @NotNull ItemStack getProjectile1() {
|
||||
return projectile1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the second projectile.
|
||||
*
|
||||
* @return the second projectile
|
||||
*/
|
||||
public @NotNull ItemStack getProjectile2() {
|
||||
return projectile2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the third projectile.
|
||||
*
|
||||
* @return the third projectile
|
||||
*/
|
||||
public @NotNull ItemStack getProjectile3() {
|
||||
return projectile3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the crossbow is currently charged.
|
||||
*
|
||||
* @return true if the crossbow is charged, false otherwise
|
||||
*/
|
||||
public boolean isCharged() {
|
||||
return charged;
|
||||
return getTag(CHARGED);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
private boolean triple;
|
||||
private ItemStack projectile1 = ItemStack.AIR;
|
||||
private ItemStack projectile2 = ItemStack.AIR;
|
||||
private ItemStack projectile3 = ItemStack.AIR;
|
||||
private boolean charged;
|
||||
|
||||
/**
|
||||
* Sets the projectile of this crossbow.
|
||||
*
|
||||
* @param projectile the projectile of the crossbow, air to remove
|
||||
*/
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder projectile(@NotNull ItemStack projectile) {
|
||||
this.projectile1 = projectile;
|
||||
this.triple = false;
|
||||
mutableNbt().set("ChargedProjectiles", NBT.List(NBTType.TAG_Compound,
|
||||
projectile.isAir() ? List.of() : List.of(getItemCompound(projectile))));
|
||||
setTag(PROJECTILES, List.of(projectile));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the triple projectiles of this crossbow.
|
||||
*
|
||||
* @param projectile1 the projectile 1
|
||||
* @param projectile2 the projectile 2
|
||||
* @param projectile3 the projectile 3
|
||||
*/
|
||||
public Builder projectiles(@NotNull ItemStack projectile1, @NotNull ItemStack projectile2, @NotNull ItemStack projectile3) {
|
||||
Check.argCondition(projectile1.isAir(), "the projectile1 of your crossbow isn't visible");
|
||||
Check.argCondition(projectile2.isAir(), "the projectile2 of your crossbow isn't visible");
|
||||
Check.argCondition(projectile3.isAir(), "the projectile3 of your crossbow isn't visible");
|
||||
this.projectile1 = projectile1;
|
||||
this.projectile2 = projectile2;
|
||||
this.projectile3 = projectile3;
|
||||
this.triple = true;
|
||||
List<NBTCompound> chargedProjectiles =
|
||||
List.of(getItemCompound(projectile1), getItemCompound(projectile2), getItemCompound(projectile3));
|
||||
mutableNbt().set("ChargedProjectiles", NBT.List(NBTType.TAG_Compound, chargedProjectiles));
|
||||
setTag(PROJECTILES, List.of(projectile1, projectile2, projectile3));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the bow charged or uncharged.
|
||||
*
|
||||
* @param charged true to make the crossbow charged, false otherwise
|
||||
*/
|
||||
public Builder charged(boolean charged) {
|
||||
this.charged = charged;
|
||||
mutableNbt().set("Charged", NBT.Boolean(charged));
|
||||
setTag(CHARGED, charged);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CrossbowMeta build() {
|
||||
return new CrossbowMeta(this, triple, projectile1, projectile2, projectile3, charged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.containsKey("ChargedProjectiles")) {
|
||||
final NBTList<NBTCompound> projectilesList = nbtCompound.getList("ChargedProjectiles");
|
||||
List<ItemStack> projectiles = new ArrayList<>();
|
||||
for (NBTCompound projectileCompound : projectilesList) {
|
||||
final byte count = projectileCompound.getByte("Count");
|
||||
final String id = projectileCompound.getString("id");
|
||||
final Material material = Material.fromNamespaceId(id);
|
||||
|
||||
final NBTCompound tagsCompound = projectileCompound.getCompound("tag");
|
||||
ItemStack itemStack = ItemStack.fromNBT(material, tagsCompound, count);
|
||||
|
||||
projectiles.add(itemStack);
|
||||
}
|
||||
|
||||
if (projectiles.size() == 1) {
|
||||
this.projectile1 = projectiles.get(0);
|
||||
} else if (projectiles.size() == 3) {
|
||||
this.projectile1 = projectiles.get(0);
|
||||
this.projectile2 = projectiles.get(1);
|
||||
this.projectile3 = projectiles.get(2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (nbtCompound.get("Charged") instanceof NBTByte charged) {
|
||||
this.charged = charged.asBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull NBTCompound getItemCompound(@NotNull ItemStack itemStack) {
|
||||
NBTCompound compound = itemStack.getMeta().toNBT();
|
||||
return compound.modify(n -> {
|
||||
n.setByte("Count", (byte) itemStack.getAmount());
|
||||
n.setString("id", itemStack.getMaterial().name());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,63 +1,52 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.item.ItemSerializers;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTType;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class EnchantedBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<EnchantedBookMeta.Builder> {
|
||||
import static net.minestom.server.item.ItemSerializers.ENCHANTMENT_SERIALIZER;
|
||||
|
||||
private final Map<Enchantment, Short> storedEnchantmentMap;
|
||||
public record EnchantedBookMeta(TagReadable readable) implements ItemMetaView<EnchantedBookMeta.Builder> {
|
||||
static final Tag<Map<Enchantment, Short>> ENCHANTMENTS = Tag.Structure("StoredEnchantments", ENCHANTMENT_SERIALIZER).list().map(enchantmentEntry -> {
|
||||
Map<Enchantment, Short> map = new HashMap<>();
|
||||
for (var entry : enchantmentEntry) map.put(entry.enchantment(), entry.level());
|
||||
return Map.copyOf(map);
|
||||
}, o -> {
|
||||
List<ItemSerializers.EnchantmentEntry> entries = new ArrayList<>();
|
||||
for (var entry : o.entrySet())
|
||||
entries.add(new ItemSerializers.EnchantmentEntry(entry.getKey(), entry.getValue()));
|
||||
return List.copyOf(entries);
|
||||
}).defaultValue(Map.of());
|
||||
|
||||
protected EnchantedBookMeta(@NotNull ItemMetaBuilder metaBuilder, Map<Enchantment, Short> storedEnchantmentMap) {
|
||||
super(metaBuilder);
|
||||
this.storedEnchantmentMap = Map.copyOf(storedEnchantmentMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stored enchantment map.
|
||||
* Stored enchantments are used on enchanted book.
|
||||
*
|
||||
* @return an unmodifiable map containing the item stored enchantments
|
||||
*/
|
||||
public @NotNull Map<Enchantment, Short> getStoredEnchantmentMap() {
|
||||
return storedEnchantmentMap;
|
||||
return getTag(ENCHANTMENTS);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private Map<Enchantment, Short> enchantments = new HashMap<>();
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public @NotNull Builder enchantments(@NotNull Map<Enchantment, Short> enchantments) {
|
||||
this.enchantments = enchantments;
|
||||
NBTUtils.writeEnchant(mutableNbt(), "StoredEnchantments", enchantments);
|
||||
setTag(ENCHANTMENTS, Map.copyOf(enchantments));
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull Builder enchantment(@NotNull Enchantment enchantment, short level) {
|
||||
this.enchantments.put(enchantment, level);
|
||||
enchantments(enchantments);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull EnchantedBookMeta build() {
|
||||
return new EnchantedBookMeta(this, enchantments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("StoredEnchantments") instanceof NBTList<?> list &&
|
||||
list.getSubtagType() == NBTType.TAG_Compound) {
|
||||
NBTUtils.loadEnchantments(list.asListOf(), this::enchantment);
|
||||
}
|
||||
var enchantments = new HashMap<>(getTag(ENCHANTMENTS));
|
||||
enchantments.put(enchantment, level);
|
||||
return enchantments(enchantments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,32 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.item.firework.FireworkEffect;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.Objects;
|
||||
public record FireworkEffectMeta(TagReadable readable) implements ItemMetaView<FireworkEffectMeta.Builder> {
|
||||
private static final Tag<FireworkEffect> FIREWORK_EFFECT = Tag.NBT("Explosion")
|
||||
.map(nbt -> FireworkEffect.fromCompound((NBTCompound) nbt), FireworkEffect::asCompound);
|
||||
|
||||
public class FireworkEffectMeta extends ItemMeta implements ItemMetaBuilder.Provider<FireworkEffectMeta.Builder> {
|
||||
|
||||
private final FireworkEffect fireworkEffect;
|
||||
|
||||
protected FireworkEffectMeta(@NotNull ItemMetaBuilder metaBuilder, FireworkEffect fireworkEffect) {
|
||||
super(metaBuilder);
|
||||
this.fireworkEffect = fireworkEffect;
|
||||
public @Nullable FireworkEffect getFireworkEffect() {
|
||||
return getTag(FIREWORK_EFFECT);
|
||||
}
|
||||
|
||||
public FireworkEffect getFireworkEffect() {
|
||||
return fireworkEffect;
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private FireworkEffect fireworkEffect;
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder effect(@Nullable FireworkEffect fireworkEffect) {
|
||||
this.fireworkEffect = fireworkEffect;
|
||||
handleNullable(fireworkEffect, "Explosion", () -> Objects.requireNonNull(fireworkEffect).asCompound());
|
||||
setTag(FIREWORK_EFFECT, fireworkEffect);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FireworkEffectMeta build() {
|
||||
return new FireworkEffectMeta(this, fireworkEffect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("Explosion") instanceof NBTCompound explosionCompound) {
|
||||
this.fireworkEffect = FireworkEffect.fromCompound(explosionCompound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +1,44 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.item.firework.FireworkEffect;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class FireworkMeta extends ItemMeta implements ItemMetaBuilder.Provider<FireworkMeta.Builder> {
|
||||
|
||||
private final List<FireworkEffect> effects;
|
||||
private final byte flightDuration;
|
||||
|
||||
protected FireworkMeta(@NotNull ItemMetaBuilder metaBuilder, List<FireworkEffect> effects,
|
||||
byte flightDuration) {
|
||||
super(metaBuilder);
|
||||
this.effects = List.copyOf(effects);
|
||||
this.flightDuration = flightDuration;
|
||||
}
|
||||
public record FireworkMeta(TagReadable readable) implements ItemMetaView<FireworkMeta.Builder> {
|
||||
private static final Tag<List<FireworkEffect>> EFFECTS = Tag.NBT("Explosions").path("Fireworks")
|
||||
.map(nbt -> FireworkEffect.fromCompound((NBTCompound) nbt), FireworkEffect::asCompound)
|
||||
.list().defaultValue(List.of());
|
||||
private static final Tag<Byte> FLIGHT_DURATION = Tag.Byte("Flight").path("Fireworks");
|
||||
|
||||
public List<FireworkEffect> getEffects() {
|
||||
return effects;
|
||||
return getTag(EFFECTS);
|
||||
}
|
||||
|
||||
public byte getFlightDuration() {
|
||||
return flightDuration;
|
||||
return getTag(FLIGHT_DURATION);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private List<FireworkEffect> effects = new CopyOnWriteArrayList<>();
|
||||
private byte flightDuration;
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder effects(List<FireworkEffect> effects) {
|
||||
this.effects = effects;
|
||||
handleCompound("Fireworks", nbtCompound -> {
|
||||
nbtCompound.set("Explosions", NBT.List(
|
||||
NBTType.TAG_Compound,
|
||||
effects.stream()
|
||||
.map(FireworkEffect::asCompound)
|
||||
.toList()
|
||||
));
|
||||
});
|
||||
setTag(EFFECTS, effects);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder flightDuration(byte flightDuration) {
|
||||
this.flightDuration = flightDuration;
|
||||
handleCompound("Fireworks", nbtCompound ->
|
||||
nbtCompound.setByte("Flight", this.flightDuration));
|
||||
setTag(FLIGHT_DURATION, flightDuration);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FireworkMeta build() {
|
||||
return new FireworkMeta(this, effects, flightDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("Fireworks") instanceof NBTCompound fireworksCompound) {
|
||||
if (fireworksCompound.get("Flight") instanceof NBTByte flight) {
|
||||
this.flightDuration = flight.getValue();
|
||||
}
|
||||
|
||||
if (fireworksCompound.get("Explosions") instanceof NBTList<?> list &&
|
||||
list.getSubtagType() == NBTType.TAG_Compound) {
|
||||
for (NBTCompound explosion : list.<NBTCompound>asListOf()) {
|
||||
this.effects.add(FireworkEffect.fromCompound(explosion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,30 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTInt;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
public class LeatherArmorMeta extends ItemMeta implements ItemMetaBuilder.Provider<LeatherArmorMeta.Builder> {
|
||||
|
||||
private final Color color;
|
||||
|
||||
protected LeatherArmorMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable Color color) {
|
||||
super(metaBuilder);
|
||||
this.color = color;
|
||||
}
|
||||
public record LeatherArmorMeta(TagReadable readable) implements ItemMetaView<LeatherArmorMeta.Builder> {
|
||||
private static final Tag<Color> COLOR = Tag.Integer("color").path("display").map(Color::new, Color::asRGB);
|
||||
|
||||
public @Nullable Color getColor() {
|
||||
return color;
|
||||
return getTag(COLOR);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private Color color;
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder color(@Nullable Color color) {
|
||||
this.color = color;
|
||||
handleCompound("display", nbtCompound -> {
|
||||
if (color != null) {
|
||||
nbtCompound.setInt("color", color.asRGB());
|
||||
} else {
|
||||
nbtCompound.remove("color");
|
||||
}
|
||||
});
|
||||
setTag(COLOR, color);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull LeatherArmorMeta build() {
|
||||
return new LeatherArmorMeta(this, color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("display") instanceof NBTCompound displayCompound) {
|
||||
if (displayCompound.get("color") instanceof NBTInt colorInt) {
|
||||
this.color = new Color(colorInt.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,158 +1,81 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.tag.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class MapMeta extends ItemMeta implements ItemMetaBuilder.Provider<MapMeta.Builder> {
|
||||
public record MapMeta(TagReadable readable) implements ItemMetaView<MapMeta.Builder> {
|
||||
private static final Tag<Integer> MAP_ID = Tag.Integer("map").defaultValue(0);
|
||||
private static final Tag<Integer> MAP_SCALE_DIRECTION = Tag.Integer("map_scale_direction").defaultValue(0);
|
||||
private static final Tag<List<Decoration>> DECORATIONS = Tag.Structure("Decorations", new TagSerializer<Decoration>() {
|
||||
@Override
|
||||
public @Nullable Decoration read(@NotNull TagReadable reader) {
|
||||
final String id = reader.getTag(Tag.String("id"));
|
||||
final Byte type = reader.getTag(Tag.Byte("type"));
|
||||
final Byte x = reader.getTag(Tag.Byte("x"));
|
||||
final Byte z = reader.getTag(Tag.Byte("z"));
|
||||
final Double rot = reader.getTag(Tag.Double("rot"));
|
||||
if (id == null || type == null || x == null || z == null || rot == null) return null;
|
||||
return new Decoration(id, type, x, z, rot);
|
||||
}
|
||||
|
||||
private final int mapId;
|
||||
private final int mapScaleDirection;
|
||||
private final List<Decoration> decorations;
|
||||
private final Color mapColor;
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull Decoration value) {
|
||||
writer.setTag(Tag.String("id"), value.id);
|
||||
writer.setTag(Tag.Byte("type"), value.type);
|
||||
writer.setTag(Tag.Byte("x"), value.x);
|
||||
writer.setTag(Tag.Byte("z"), value.z);
|
||||
writer.setTag(Tag.Double("rot"), value.rotation);
|
||||
}
|
||||
}).list().defaultValue(List.of());
|
||||
private static final Tag<Color> MAP_COLOR = Tag.Integer("MapColor").path("display").map(Color::new, Color::asRGB);
|
||||
|
||||
protected MapMeta(ItemMetaBuilder metaBuilder,
|
||||
int mapId,
|
||||
int mapScaleDirection,
|
||||
@NotNull List<Decoration> decorations,
|
||||
@NotNull Color mapColor) {
|
||||
super(metaBuilder);
|
||||
this.mapId = mapId;
|
||||
this.mapScaleDirection = mapScaleDirection;
|
||||
this.decorations = List.copyOf(decorations);
|
||||
this.mapColor = mapColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map id.
|
||||
*
|
||||
* @return the map id
|
||||
*/
|
||||
public int getMapId() {
|
||||
return mapId;
|
||||
return getTag(MAP_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map scale direction.
|
||||
*
|
||||
* @return the map scale direction
|
||||
*/
|
||||
public int getMapScaleDirection() {
|
||||
return mapScaleDirection;
|
||||
return getTag(MAP_SCALE_DIRECTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map decorations.
|
||||
*
|
||||
* @return a modifiable list containing all the map decorations
|
||||
*/
|
||||
public List<Decoration> getDecorations() {
|
||||
return decorations;
|
||||
return getTag(DECORATIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map color.
|
||||
*
|
||||
* @return the map color
|
||||
*/
|
||||
public @NotNull Color getMapColor() {
|
||||
return this.mapColor;
|
||||
return getTag(MAP_COLOR);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private int mapId;
|
||||
private int mapScaleDirection = 1;
|
||||
private List<Decoration> decorations = new CopyOnWriteArrayList<>();
|
||||
private Color mapColor = new Color(0, 0, 0);
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder mapId(int value) {
|
||||
this.mapId = value;
|
||||
mutableNbt().setInt("map", mapId);
|
||||
setTag(MAP_ID, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mapScaleDirection(int value) {
|
||||
this.mapScaleDirection = value;
|
||||
mutableNbt().setInt("map_scale_direction", value);
|
||||
setTag(MAP_SCALE_DIRECTION, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder decorations(List<Decoration> value) {
|
||||
this.decorations = new ArrayList<>(value);
|
||||
mutableNbt().set("Decorations", NBT.List(NBTType.TAG_Compound,
|
||||
decorations.stream()
|
||||
.map(decoration -> NBT.Compound(Map.of(
|
||||
"id", NBT.String(decoration.id()),
|
||||
"type", NBT.Byte(decoration.type()),
|
||||
"x", NBT.Byte(decoration.x()),
|
||||
"z", NBT.Byte(decoration.z()),
|
||||
"rot", NBT.Double(decoration.rotation()))))
|
||||
.toList()
|
||||
));
|
||||
setTag(DECORATIONS, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder mapColor(Color value) {
|
||||
this.mapColor = value;
|
||||
handleCompound("display", displayCompound -> displayCompound.setInt("MapColor", mapColor.asRGB()));
|
||||
setTag(MAP_COLOR, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemMeta build() {
|
||||
return new MapMeta(this, mapId, mapScaleDirection, decorations, mapColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound compound) {
|
||||
if (compound.get("map") instanceof NBTInt mapInt) {
|
||||
this.mapId = mapInt.getValue();
|
||||
}
|
||||
if (compound.get("map_scale_direction") instanceof NBTInt mapScaleDirection) {
|
||||
this.mapScaleDirection = mapScaleDirection.getValue();
|
||||
}
|
||||
|
||||
if (compound.get("Decorations") instanceof NBTList<?> decorationsList &&
|
||||
decorationsList.getSubtagType() == NBTType.TAG_Compound) {
|
||||
List<Decoration> decorations = new ArrayList<>();
|
||||
for (NBTCompound decorationCompound : decorationsList.<NBTCompound>asListOf()) {
|
||||
final String id = decorationCompound.getString("id");
|
||||
final byte type = decorationCompound.getAsByte("type");
|
||||
byte x = 0;
|
||||
|
||||
if (decorationCompound.get("x") instanceof NBTByte xByte) {
|
||||
x = xByte.getValue();
|
||||
}
|
||||
|
||||
byte z = 0;
|
||||
if (decorationCompound.get("z") instanceof NBTByte zByte) {
|
||||
z = zByte.getValue();
|
||||
}
|
||||
|
||||
double rotation = 0.0;
|
||||
if (decorationCompound.get("rot") instanceof NBTDouble rotDouble) {
|
||||
rotation = rotDouble.getValue();
|
||||
}
|
||||
|
||||
decorations.add(new Decoration(id, type, x, z, rotation));
|
||||
}
|
||||
this.decorations = decorations;
|
||||
}
|
||||
|
||||
if (compound.get("display") instanceof NBTCompound displayCompound) {
|
||||
if (displayCompound.get("MapColor") instanceof NBTInt mapColor) {
|
||||
this.mapColor = new Color(mapColor.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record Decoration(String id, byte type, byte x, byte z, double rotation) {
|
||||
|
@ -1,89 +1,54 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.entity.PlayerSkin;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.tag.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerHeadMeta extends ItemMeta implements ItemMetaBuilder.Provider<PlayerHeadMeta.Builder> {
|
||||
public record PlayerHeadMeta(TagReadable readable) implements ItemMetaView<PlayerHeadMeta.Builder> {
|
||||
private static final Tag<UUID> SKULL_OWNER = Tag.UUID("Id").path("SkullOwner");
|
||||
private static final Tag<PlayerSkin> SKIN = Tag.Structure("Properties", new TagSerializer<PlayerSkin>() {
|
||||
@Override
|
||||
public @Nullable PlayerSkin read(@NotNull TagReadable reader) {
|
||||
final String value = reader.getTag(Tag.String("Value"));
|
||||
final String signature = reader.getTag(Tag.String("Signature"));
|
||||
if (value == null || signature == null) return null;
|
||||
return new PlayerSkin(value, signature);
|
||||
}
|
||||
|
||||
private final UUID skullOwner;
|
||||
private final PlayerSkin playerSkin;
|
||||
|
||||
protected PlayerHeadMeta(@NotNull ItemMetaBuilder metaBuilder, UUID skullOwner,
|
||||
@Nullable PlayerSkin playerSkin) {
|
||||
super(metaBuilder);
|
||||
this.skullOwner = skullOwner;
|
||||
this.playerSkin = playerSkin;
|
||||
}
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull PlayerSkin value) {
|
||||
writer.setTag(Tag.String("Value"), value.textures());
|
||||
writer.setTag(Tag.String("Signature"), value.signature());
|
||||
}
|
||||
}).path("SkullOwner");
|
||||
|
||||
public UUID getSkullOwner() {
|
||||
return skullOwner;
|
||||
return getTag(SKULL_OWNER);
|
||||
}
|
||||
|
||||
public @Nullable PlayerSkin getPlayerSkin() {
|
||||
return playerSkin;
|
||||
return getTag(SKIN);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private UUID skullOwner;
|
||||
private PlayerSkin playerSkin;
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder skullOwner(@Nullable UUID skullOwner) {
|
||||
this.skullOwner = skullOwner;
|
||||
handleCompound("SkullOwner", nbtCompound ->
|
||||
nbtCompound.setIntArray("Id", Utils.uuidToIntArray(this.skullOwner)));
|
||||
setTag(SKULL_OWNER, skullOwner);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder playerSkin(@Nullable PlayerSkin playerSkin) {
|
||||
this.playerSkin = playerSkin;
|
||||
handleCompound("SkullOwner", nbtCompound -> {
|
||||
if (playerSkin == null) {
|
||||
nbtCompound.remove("Properties");
|
||||
return;
|
||||
}
|
||||
|
||||
final String value = Objects.requireNonNullElse(this.playerSkin.textures(), "");
|
||||
final String signature = Objects.requireNonNullElse(this.playerSkin.signature(), "");
|
||||
NBTList<NBTCompound> textures = new NBTList<>(NBTType.TAG_Compound,
|
||||
List.of(NBT.Compound(Map.of(
|
||||
"Value", NBT.String(value),
|
||||
"Signature", NBT.String(signature)))));
|
||||
nbtCompound.set("Properties", NBT.Compound(Map.of("textures", textures)));
|
||||
});
|
||||
setTag(SKIN, playerSkin);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerHeadMeta build() {
|
||||
return new PlayerHeadMeta(this, skullOwner, playerSkin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("SkullOwner") instanceof NBTCompound skullOwnerCompound) {
|
||||
if (skullOwnerCompound.get("Id") instanceof NBTIntArray id) {
|
||||
this.skullOwner = Utils.intArrayToUuid(id.getValue().copyArray());
|
||||
}
|
||||
|
||||
if (skullOwnerCompound.get("Properties") instanceof NBTCompound propertyCompound) {
|
||||
if (propertyCompound.get("textures") instanceof NBTList<?> textures &&
|
||||
textures.getSubtagType() == NBTType.TAG_Compound) {
|
||||
NBTCompound nbt = (NBTCompound) textures.get(0);
|
||||
this.playerSkin = new PlayerSkin(nbt.getString("Value"), nbt.getString("Signature"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,108 +1,77 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.color.Color;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.potion.CustomPotionEffect;
|
||||
import net.minestom.server.potion.PotionType;
|
||||
import net.minestom.server.registry.ProtocolObject;
|
||||
import net.minestom.server.tag.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PotionMeta extends ItemMeta implements ItemMetaBuilder.Provider<PotionMeta.Builder> {
|
||||
public record PotionMeta(TagReadable readable) implements ItemMetaView<PotionMeta.Builder> {
|
||||
private static final Tag<PotionType> POTION_TYPE = Tag.String("Potion").map(PotionType::fromNamespaceId, ProtocolObject::name);
|
||||
private static final Tag<List<CustomPotionEffect>> CUSTOM_POTION_EFFECTS = Tag.Structure("CustomPotionEffects", new TagSerializer<CustomPotionEffect>() {
|
||||
@Override
|
||||
public @Nullable CustomPotionEffect read(@NotNull TagReadable reader) {
|
||||
final Byte id = reader.getTag(Tag.Byte("Id"));
|
||||
final Byte amplifier = reader.getTag(Tag.Byte("Amplifier"));
|
||||
final Integer duration = reader.getTag(Tag.Integer("Duration"));
|
||||
final Boolean ambient = reader.getTag(Tag.Boolean("Ambient"));
|
||||
final Boolean showParticles = reader.getTag(Tag.Boolean("ShowParticles"));
|
||||
final Boolean showIcon = reader.getTag(Tag.Boolean("ShowIcon"));
|
||||
if (id == null || amplifier == null || duration == null || ambient == null || showParticles == null || showIcon == null) {
|
||||
return null;
|
||||
}
|
||||
return new CustomPotionEffect(id, amplifier, duration, ambient, showParticles, showIcon);
|
||||
}
|
||||
|
||||
private final PotionType potionType;
|
||||
private final List<CustomPotionEffect> customPotionEffects;
|
||||
private final Color color;
|
||||
|
||||
protected PotionMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable PotionType potionType,
|
||||
List<CustomPotionEffect> customPotionEffects,
|
||||
Color color) {
|
||||
super(metaBuilder);
|
||||
this.potionType = potionType;
|
||||
this.customPotionEffects = List.copyOf(customPotionEffects);
|
||||
this.color = color;
|
||||
}
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull CustomPotionEffect value) {
|
||||
writer.setTag(Tag.Byte("Id"), value.id());
|
||||
writer.setTag(Tag.Byte("Amplifier"), value.amplifier());
|
||||
writer.setTag(Tag.Integer("Duration"), value.duration());
|
||||
writer.setTag(Tag.Boolean("Ambient"), value.isAmbient());
|
||||
writer.setTag(Tag.Boolean("ShowParticles"), value.showParticles());
|
||||
writer.setTag(Tag.Boolean("ShowIcon"), value.showIcon());
|
||||
}
|
||||
}).list().defaultValue(List.of());
|
||||
private static final Tag<Color> CUSTOM_POTION_COLOR = Tag.Integer("CustomPotionColor").path("display").map(Color::new, Color::asRGB);
|
||||
|
||||
public PotionType getPotionType() {
|
||||
return potionType;
|
||||
return getTag(POTION_TYPE);
|
||||
}
|
||||
|
||||
public List<CustomPotionEffect> getCustomPotionEffects() {
|
||||
return customPotionEffects;
|
||||
return getTag(CUSTOM_POTION_EFFECTS);
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return color;
|
||||
return getTag(CUSTOM_POTION_COLOR);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private PotionType potionType;
|
||||
private List<CustomPotionEffect> customPotionEffects = new ArrayList<>();
|
||||
private Color color;
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder potionType(@NotNull PotionType potionType) {
|
||||
this.potionType = potionType;
|
||||
mutableNbt().setString("Potion", potionType.name());
|
||||
setTag(POTION_TYPE, potionType);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder effects(@NotNull List<CustomPotionEffect> customPotionEffects) {
|
||||
this.customPotionEffects = customPotionEffects;
|
||||
mutableNbt().set("CustomPotionEffects", NBT.List(NBTType.TAG_Compound,
|
||||
customPotionEffects.stream()
|
||||
.map(customPotionEffect -> NBT.Compound(Map.of(
|
||||
"Id", NBT.Byte(customPotionEffect.id()),
|
||||
"Amplifier", NBT.Byte(customPotionEffect.amplifier()),
|
||||
"Duration", NBT.Int(customPotionEffect.duration()),
|
||||
"Ambient", NBT.Boolean(customPotionEffect.isAmbient()),
|
||||
"ShowParticles", NBT.Boolean(customPotionEffect.showParticles()),
|
||||
"ShowIcon", NBT.Boolean(customPotionEffect.showIcon()))))
|
||||
.toList()
|
||||
));
|
||||
setTag(CUSTOM_POTION_EFFECTS, customPotionEffects);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder color(@NotNull Color color) {
|
||||
this.color = color;
|
||||
mutableNbt().setInt("CustomPotionColor", color.asRGB());
|
||||
setTag(CUSTOM_POTION_COLOR, color);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PotionMeta build() {
|
||||
return new PotionMeta(this, potionType, customPotionEffects, color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("Potion") instanceof NBTString potion) {
|
||||
this.potionType = PotionType.fromNamespaceId(potion.getValue());
|
||||
}
|
||||
|
||||
if (nbtCompound.get("CustomPotionEffects") instanceof NBTList<?> customEffectList &&
|
||||
customEffectList.getSubtagType() == NBTType.TAG_Compound) {
|
||||
for (NBTCompound potionCompound : customEffectList.<NBTCompound>asListOf()) {
|
||||
final byte id = potionCompound.getAsByte("Id");
|
||||
final byte amplifier = potionCompound.getAsByte("Amplifier");
|
||||
final int duration = potionCompound.containsKey("Duration") ? potionCompound.getNumber("Duration").intValue() : (int) Duration.ofSeconds(30).toMillis();
|
||||
final boolean ambient = potionCompound.containsKey("Ambient") ? potionCompound.getAsByte("Ambient") == 1 : false;
|
||||
final boolean showParticles = potionCompound.containsKey("ShowParticles") ? potionCompound.getAsByte("ShowParticles") == 1 : true;
|
||||
final boolean showIcon = potionCompound.containsKey("ShowIcon") ? potionCompound.getAsByte("ShowIcon") == 1 : true;
|
||||
|
||||
this.customPotionEffects.add(new CustomPotionEffect(id, amplifier, duration, ambient, showParticles, showIcon));
|
||||
}
|
||||
}
|
||||
|
||||
if (nbtCompound.get("CustomPotionColor") instanceof NBTInt color) {
|
||||
this.color = new Color(color.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +0,0 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
|
||||
public class SpawnEggMeta extends ItemMeta implements ItemMetaBuilder.Provider<SpawnEggMeta.Builder> {
|
||||
|
||||
private final EntityType entityType;
|
||||
|
||||
protected SpawnEggMeta(@NotNull ItemMetaBuilder metaBuilder, @Nullable EntityType entityType) {
|
||||
super(metaBuilder);
|
||||
this.entityType = entityType;
|
||||
}
|
||||
|
||||
public @Nullable EntityType getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private EntityType entityType;
|
||||
|
||||
public Builder entityType(@Nullable EntityType entityType) {
|
||||
this.entityType = entityType;
|
||||
// TODO nbt
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SpawnEggMeta build() {
|
||||
return new SpawnEggMeta(this, entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
@ -2,94 +2,34 @@ package net.minestom.server.item.metadata;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class WritableBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<WritableBookMeta.Builder> {
|
||||
|
||||
private final String author;
|
||||
private final String title;
|
||||
private final List<Component> pages;
|
||||
|
||||
protected WritableBookMeta(@NotNull ItemMetaBuilder metaBuilder,
|
||||
@Nullable String author, @Nullable String title,
|
||||
@NotNull List<@NotNull Component> pages) {
|
||||
super(metaBuilder);
|
||||
this.author = author;
|
||||
this.title = title;
|
||||
this.pages = List.copyOf(pages);
|
||||
}
|
||||
|
||||
public @Nullable String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public @Nullable String getTitle() {
|
||||
return title;
|
||||
}
|
||||
public record WritableBookMeta(TagReadable readable) implements ItemMetaView<WritableBookMeta.Builder> {
|
||||
private static final Tag<List<Component>> PAGES = Tag.String("pages")
|
||||
.<Component>map(s -> LegacyComponentSerializer.legacySection().deserialize(s),
|
||||
textComponent -> LegacyComponentSerializer.legacySection().serialize(textComponent))
|
||||
.list().defaultValue(List.of());
|
||||
|
||||
public @NotNull List<@NotNull Component> getPages() {
|
||||
return pages;
|
||||
return getTag(PAGES);
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private String author;
|
||||
private String title;
|
||||
private List<Component> pages = new ArrayList<>();
|
||||
|
||||
public Builder author(@Nullable String author) {
|
||||
this.author = author;
|
||||
handleNullable(author, "author",
|
||||
() -> new NBTString(Objects.requireNonNull(author)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder title(@Nullable String title) {
|
||||
this.title = title;
|
||||
handleNullable(title, "title", () -> NBT.String(Objects.requireNonNull(title)));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder pages(@NotNull List<@NotNull Component> pages) {
|
||||
this.pages = new ArrayList<>(pages);
|
||||
|
||||
handleCollection(pages, "pages", () -> NBT.List(
|
||||
NBTType.TAG_String,
|
||||
pages.stream()
|
||||
.map(page -> new NBTString(LegacyComponentSerializer.legacySection().serialize(page)))
|
||||
.toList()
|
||||
));
|
||||
|
||||
setTag(PAGES, pages);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull WritableBookMeta build() {
|
||||
return new WritableBookMeta(this, author, title, pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("author") instanceof NBTString author) {
|
||||
this.author = author.getValue();
|
||||
}
|
||||
if (nbtCompound.get("title") instanceof NBTString title) {
|
||||
this.title = title.getValue();
|
||||
}
|
||||
if (nbtCompound.get("pages") instanceof NBTList<?> list &&
|
||||
list.getSubtagType() == NBTType.TAG_String) {
|
||||
for (NBTString page : list.<NBTString>asListOf()) {
|
||||
this.pages.add(LegacyComponentSerializer.legacySection().deserialize(page.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,161 +1,93 @@
|
||||
package net.minestom.server.item.metadata;
|
||||
|
||||
import net.kyori.adventure.inventory.Book;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import net.minestom.server.adventure.Localizable;
|
||||
import net.minestom.server.adventure.MinestomAdventure;
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minestom.server.item.ItemMetaView;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class WrittenBookMeta extends ItemMeta implements ItemMetaBuilder.Provider<WrittenBookMeta.Builder> {
|
||||
|
||||
private final boolean resolved;
|
||||
private final WrittenBookGeneration generation;
|
||||
private final String author;
|
||||
private final String title;
|
||||
private final List<Component> pages;
|
||||
|
||||
protected WrittenBookMeta(@NotNull ItemMetaBuilder metaBuilder, boolean resolved,
|
||||
@Nullable WrittenBookGeneration generation,
|
||||
@Nullable String author, @Nullable String title,
|
||||
@NotNull List<@NotNull Component> pages) {
|
||||
super(metaBuilder);
|
||||
this.resolved = resolved;
|
||||
this.generation = generation;
|
||||
this.author = author;
|
||||
this.title = title;
|
||||
this.pages = List.copyOf(pages);
|
||||
}
|
||||
public record WrittenBookMeta(TagReadable readable) implements ItemMetaView<WrittenBookMeta.Builder> {
|
||||
private static final Tag<Boolean> RESOLVED = Tag.Boolean("resolved");
|
||||
private static final Tag<WrittenBookGeneration> GENERATION = Tag.Integer("resolved").map(integer -> WrittenBookGeneration.values()[integer], Enum::ordinal);
|
||||
private static final Tag<String> AUTHOR = Tag.String("author");
|
||||
private static final Tag<String> TITLE = Tag.String("title");
|
||||
private static final Tag<List<Component>> PAGES = Tag.String("pages")
|
||||
.<Component>map(s -> LegacyComponentSerializer.legacySection().deserialize(s),
|
||||
textComponent -> LegacyComponentSerializer.legacySection().serialize(textComponent))
|
||||
.list().defaultValue(List.of());
|
||||
|
||||
public boolean isResolved() {
|
||||
return resolved;
|
||||
return getTag(RESOLVED);
|
||||
}
|
||||
|
||||
public @Nullable WrittenBookGeneration getGeneration() {
|
||||
return generation;
|
||||
return getTag(GENERATION);
|
||||
}
|
||||
|
||||
public @Nullable String getAuthor() {
|
||||
return author;
|
||||
return getTag(AUTHOR);
|
||||
}
|
||||
|
||||
public @Nullable String getTitle() {
|
||||
return title;
|
||||
return getTag(TITLE);
|
||||
}
|
||||
|
||||
public @NotNull List<@NotNull Component> getPages() {
|
||||
return pages;
|
||||
return getTag(PAGES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return readable.getTag(tag);
|
||||
}
|
||||
|
||||
public enum WrittenBookGeneration {
|
||||
ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a written book meta from an Adventure book. This meta will not be
|
||||
* resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}.
|
||||
*
|
||||
* @param book the book
|
||||
* @param localizable who the book is for
|
||||
* @return the meta
|
||||
*/
|
||||
public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book, @NotNull Localizable localizable) {
|
||||
return new Builder()
|
||||
.resolved(false)
|
||||
.generation(WrittenBookGeneration.ORIGINAL)
|
||||
.author(GsonComponentSerializer.gson().serialize(GlobalTranslator.render(book.author(), Objects.requireNonNullElse(localizable.getLocale(), MinestomAdventure.getDefaultLocale()))))
|
||||
.title(GsonComponentSerializer.gson().serialize(GlobalTranslator.render(book.title(), Objects.requireNonNullElse(localizable.getLocale(), MinestomAdventure.getDefaultLocale()))))
|
||||
.pages(book.pages())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class Builder extends ItemMetaBuilder {
|
||||
|
||||
private boolean resolved;
|
||||
private WrittenBookGeneration generation;
|
||||
private String author;
|
||||
private String title;
|
||||
private List<Component> pages = new ArrayList<>();
|
||||
|
||||
public record Builder(TagHandler tagHandler) implements ItemMetaView.Builder {
|
||||
public Builder resolved(boolean resolved) {
|
||||
this.resolved = resolved;
|
||||
mutableNbt().set("resolved", NBT.Boolean(resolved));
|
||||
setTag(RESOLVED, resolved);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder generation(@Nullable WrittenBookGeneration generation) {
|
||||
this.generation = generation;
|
||||
handleNullable(generation, "generation",
|
||||
() -> new NBTInt(Objects.requireNonNull(generation).ordinal()));
|
||||
setTag(GENERATION, generation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder author(@Nullable String author) {
|
||||
this.author = author;
|
||||
handleNullable(author, "author",
|
||||
() -> new NBTString(Objects.requireNonNull(author)));
|
||||
setTag(AUTHOR, author);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder author(@Nullable Component author) {
|
||||
return author(author != null ? LegacyComponentSerializer.legacySection().serialize(author) : null);
|
||||
}
|
||||
|
||||
public Builder title(@Nullable String title) {
|
||||
this.title = title;
|
||||
handleNullable(title, "title",
|
||||
() -> new NBTString(Objects.requireNonNull(title)));
|
||||
setTag(TITLE, title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder title(@Nullable Component title) {
|
||||
return title(title != null ? LegacyComponentSerializer.legacySection().serialize(title) : null);
|
||||
}
|
||||
|
||||
public Builder pages(@NotNull List<@NotNull Component> pages) {
|
||||
this.pages = new ArrayList<>(pages);
|
||||
|
||||
handleCollection(pages, "pages", () -> NBT.List(
|
||||
NBTType.TAG_String,
|
||||
pages.stream()
|
||||
.map(page -> new NBTString(GsonComponentSerializer.gson().serialize(page)))
|
||||
.toList()
|
||||
));
|
||||
|
||||
setTag(PAGES, pages);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pages(Component... pages) {
|
||||
return pages(Arrays.asList(pages));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull WrittenBookMeta build() {
|
||||
return new WrittenBookMeta(this, resolved, generation, author, title, pages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
if (nbtCompound.get("resolved") instanceof NBTByte resolved) {
|
||||
this.resolved = resolved.asBoolean();
|
||||
}
|
||||
if (nbtCompound.get("generation") instanceof NBTInt generation) {
|
||||
this.generation = WrittenBookGeneration.values()[generation.getValue()];
|
||||
}
|
||||
if (nbtCompound.get("author") instanceof NBTString author) {
|
||||
this.author = author.getValue();
|
||||
}
|
||||
if (nbtCompound.get("title") instanceof NBTString title) {
|
||||
this.title = title.getValue();
|
||||
}
|
||||
if (nbtCompound.get("pages") instanceof NBTList<?> list &&
|
||||
list.getSubtagType() == NBTType.TAG_String) {
|
||||
for (NBTString page : list.<NBTString>asListOf()) {
|
||||
this.pages.add(GsonComponentSerializer.gson().deserialize(page.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ public final class VanillaStackingRule implements StackingRule {
|
||||
|
||||
@Override
|
||||
public int getAmount(@NotNull ItemStack itemStack) {
|
||||
return itemStack.getAmount();
|
||||
return itemStack.amount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSize(@NotNull ItemStack itemStack) {
|
||||
return itemStack.getMaterial().maxStackSize();
|
||||
return itemStack.material().maxStackSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +63,7 @@ public class BlockPlacementListener {
|
||||
return;
|
||||
}
|
||||
|
||||
final Material useMaterial = usedItem.getMaterial();
|
||||
final Material useMaterial = usedItem.material();
|
||||
if (!useMaterial.isBlock()) {
|
||||
// Player didn't try to place a block but interacted with one
|
||||
PlayerUseItemOnBlockEvent event = new PlayerUseItemOnBlockEvent(player, hand, usedItem, blockPosition, blockFace);
|
||||
@ -78,7 +78,7 @@ public class BlockPlacementListener {
|
||||
canPlaceBlock = false; // Spectators can't place blocks
|
||||
} else if (player.getGameMode() == GameMode.ADVENTURE) {
|
||||
//Check if the block can be placed on the block
|
||||
canPlaceBlock = usedItem.getMeta().getCanPlaceOn().contains(interactedBlock);
|
||||
canPlaceBlock = usedItem.meta().getCanPlaceOn().contains(interactedBlock);
|
||||
}
|
||||
|
||||
// Get the newly placed block position
|
||||
|
@ -60,7 +60,7 @@ public final class PlayerDiggingListener {
|
||||
} else if (gameMode == GameMode.ADVENTURE) {
|
||||
// Check if the item can break the block with the current item
|
||||
final ItemStack itemInMainHand = player.getItemInMainHand();
|
||||
if (!itemInMainHand.getMeta().getCanDestroy().contains(block)) {
|
||||
if (!itemInMainHand.meta().getCanDestroy().contains(block)) {
|
||||
return new DiggingResult(block, false);
|
||||
}
|
||||
} else if (gameMode == GameMode.CREATIVE) {
|
||||
|
@ -28,7 +28,7 @@ public class UseItemListener {
|
||||
}
|
||||
|
||||
itemStack = useItemEvent.getItemStack();
|
||||
final Material material = itemStack.getMaterial();
|
||||
final Material material = itemStack.material();
|
||||
|
||||
// Equip armor with right click
|
||||
final EquipmentSlot equipmentSlot = material.registry().equipmentSlot();
|
||||
|
@ -8,20 +8,19 @@ import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
// for lack of a better name
|
||||
@ApiStatus.Internal
|
||||
public final class NBTUtils {
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(NBTUtils.class);
|
||||
|
||||
/**
|
||||
* An Adventure codec to convert between NBT and SNBT.
|
||||
@ -32,7 +31,6 @@ public final class NBTUtils {
|
||||
public static final Codec<NBT, String, NBTException, RuntimeException> SNBT_CODEC = MinestomAdventure.NBT_CODEC;
|
||||
|
||||
private NBTUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,15 +74,13 @@ public final class NBTUtils {
|
||||
public static void saveAllItems(@NotNull List<NBTCompound> list, @NotNull Inventory inventory) {
|
||||
for (int i = 0; i < inventory.getSize(); i++) {
|
||||
final ItemStack stack = inventory.getItemStack(i);
|
||||
|
||||
NBTCompound tag = stack.getMeta().toNBT();
|
||||
|
||||
final NBTCompound tag = stack.meta().toNBT();
|
||||
final int slotIndex = i;
|
||||
list.add(NBT.Compound(nbt -> {
|
||||
nbt.set("tag", tag);
|
||||
nbt.setByte("Slot", (byte) slotIndex);
|
||||
nbt.setByte("Count", (byte) stack.getAmount());
|
||||
nbt.setString("id", stack.getMaterial().name());
|
||||
nbt.setByte("Count", (byte) stack.amount());
|
||||
nbt.setString("id", stack.material().name());
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -117,22 +113,4 @@ public final class NBTUtils {
|
||||
compound : null;
|
||||
return ItemStack.fromNBT(material, nbtCompound, count);
|
||||
}
|
||||
|
||||
public static void loadEnchantments(NBTList<NBTCompound> enchantments, EnchantmentSetter setter) {
|
||||
for (NBTCompound enchantment : enchantments) {
|
||||
final short level = enchantment.getAsShort("lvl");
|
||||
final String id = enchantment.getString("id");
|
||||
final Enchantment enchant = Enchantment.fromNamespaceId(id);
|
||||
if (enchant != null) {
|
||||
setter.applyEnchantment(enchant, level);
|
||||
} else {
|
||||
LOGGER.warn("Unknown enchantment type: {}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EnchantmentSetter {
|
||||
void applyEnchantment(Enchantment name, short level);
|
||||
}
|
||||
}
|
||||
|
@ -307,9 +307,9 @@ public class BinaryWriter extends OutputStream {
|
||||
writeBoolean(false);
|
||||
} else {
|
||||
writeBoolean(true);
|
||||
writeVarInt(itemStack.getMaterial().id());
|
||||
writeByte((byte) itemStack.getAmount());
|
||||
write(itemStack.getMeta());
|
||||
writeVarInt(itemStack.material().id());
|
||||
writeByte((byte) itemStack.amount());
|
||||
write(itemStack.meta());
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,7 +324,7 @@ public class BinaryWriter extends OutputStream {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the given writeable object into this writer.
|
||||
*
|
||||
|
@ -58,7 +58,7 @@ public record BiomeParticle(float probability, Option option) {
|
||||
@Override
|
||||
public NBTCompound toNbt() {
|
||||
//todo test count might be wrong type
|
||||
NBTCompound nbtCompound = item.getMeta().toNBT();
|
||||
NBTCompound nbtCompound = item.meta().toNBT();
|
||||
return nbtCompound.modify(n -> {
|
||||
n.setString("type", type);
|
||||
});
|
||||
|
@ -181,7 +181,7 @@ public class EventNodeTest {
|
||||
AtomicBoolean childResult = new AtomicBoolean(false);
|
||||
|
||||
var node = EventNode.type("item_node", EventFilter.ITEM,
|
||||
(event, item) -> item.getMaterial() == Material.DIAMOND);
|
||||
(event, item) -> item.material() == Material.DIAMOND);
|
||||
var child = EventNode.type("item_node2", EventFilter.ITEM)
|
||||
.addListener(ItemTestEvent.class, event -> childResult.set(true));
|
||||
node.addChild(child);
|
||||
@ -204,7 +204,7 @@ public class EventNodeTest {
|
||||
var node = EventNode.all("main");
|
||||
|
||||
AtomicBoolean result = new AtomicBoolean(false);
|
||||
var binding = EventBinding.filtered(EventFilter.ITEM, itemStack -> itemStack.getMaterial() == Material.DIAMOND)
|
||||
var binding = EventBinding.filtered(EventFilter.ITEM, itemStack -> itemStack.material() == Material.DIAMOND)
|
||||
.map(ItemTestEvent.class, (itemStack, itemTestEvent) -> result.set(true))
|
||||
.build();
|
||||
node.register(binding);
|
||||
|
21
src/test/java/net/minestom/server/item/ItemAirTest.java
Normal file
21
src/test/java/net/minestom/server/item/ItemAirTest.java
Normal file
@ -0,0 +1,21 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ItemAirTest {
|
||||
@Test
|
||||
public void testAir() {
|
||||
var item = ItemStack.of(Material.DIAMOND_SWORD);
|
||||
assertFalse(item.isAir());
|
||||
assertTrue(ItemStack.AIR.isAir());
|
||||
var emptyItem = item.withAmount(0);
|
||||
assertTrue(emptyItem.isAir());
|
||||
assertEquals(emptyItem, ItemStack.AIR, "AIR item can be compared to empty item");
|
||||
assertSame(emptyItem, ItemStack.AIR, "AIR item identity can be compared to empty item");
|
||||
|
||||
assertSame(ItemStack.AIR, ItemStack.fromNBT(Material.DIAMOND, null, 0));
|
||||
assertSame(ItemStack.AIR, ItemStack.builder(Material.DIAMOND).amount(0).build());
|
||||
}
|
||||
}
|
36
src/test/java/net/minestom/server/item/ItemMetaTest.java
Normal file
36
src/test/java/net/minestom/server/item/ItemMetaTest.java
Normal file
@ -0,0 +1,36 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.item.metadata.BundleMeta;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class ItemMetaTest {
|
||||
@Test
|
||||
public void defaultMeta() {
|
||||
var item = ItemStack.builder(Material.BUNDLE).build();
|
||||
assertNotNull(item.meta());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNBT() {
|
||||
var compound = NBT.Compound(Map.of("value", NBT.Int(5)));
|
||||
var item = ItemStack.builder(Material.BUNDLE).meta(compound).build();
|
||||
assertEquals(compound, item.meta().toNBT());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bundle() {
|
||||
var item = ItemStack.builder(Material.BUNDLE)
|
||||
.meta(BundleMeta.class, bundleMetaBuilder -> {
|
||||
bundleMetaBuilder.addItem(ItemStack.of(Material.DIAMOND, 5));
|
||||
bundleMetaBuilder.addItem(ItemStack.of(Material.RABBIT_FOOT, 5));
|
||||
})
|
||||
.build();
|
||||
assertEquals(2, item.meta(BundleMeta.class).getItems().size());
|
||||
}
|
||||
}
|
20
src/test/java/net/minestom/server/item/ItemMetaViewTest.java
Normal file
20
src/test/java/net/minestom/server/item/ItemMetaViewTest.java
Normal file
@ -0,0 +1,20 @@
|
||||
package net.minestom.server.item;
|
||||
|
||||
import net.minestom.server.item.metadata.BundleMeta;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||
|
||||
public class ItemMetaViewTest {
|
||||
@Test
|
||||
public void viewType() {
|
||||
assertEquals(BundleMeta.Builder.class, ItemMetaViewImpl.viewType(BundleMeta.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void construct() {
|
||||
assertInstanceOf(BundleMeta.Builder.class, ItemMetaViewImpl.constructBuilder(BundleMeta.class, TagHandler.newHandler()));
|
||||
}
|
||||
}
|
@ -13,27 +13,35 @@ import java.util.stream.Stream;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ItemTest {
|
||||
private static final ItemStack ITEM = ItemStack.builder(Material.STONE)
|
||||
.displayName(Component.text("Display name!", NamedTextColor.GREEN))
|
||||
.lore(Component.text("Line 1"), Component.text("Line 2"))
|
||||
.meta(metaBuilder ->
|
||||
metaBuilder.enchantment(Enchantment.EFFICIENCY, (short) 10)
|
||||
.hideFlag(ItemHideFlag.HIDE_ENCHANTS))
|
||||
.build();
|
||||
|
||||
@Test
|
||||
public void testFields() {
|
||||
var item = ItemStack.of(Material.DIAMOND_SWORD);
|
||||
assertEquals(item.getMaterial(), Material.DIAMOND_SWORD, "Material must be the same");
|
||||
assertEquals(item.getAmount(), 1, "Default item amount must be 1");
|
||||
assertEquals(item.material(), Material.DIAMOND_SWORD, "Material must be the same");
|
||||
assertEquals(item.amount(), 1, "Default item amount must be 1");
|
||||
assertNull(item.getDisplayName(), "Default item display name must be null");
|
||||
assertTrue(item.getLore().isEmpty(), "Default item lore must be empty");
|
||||
ItemStack finalItem = item;
|
||||
assertThrows(Exception.class, () -> finalItem.getLore().add(Component.text("Hey!")), "Lore list cannot be modified directly");
|
||||
|
||||
item = item.withAmount(5);
|
||||
assertEquals(item.getAmount(), 5, "Items with different amount should not be equals");
|
||||
assertEquals(item.withAmount(amount -> amount * 2).getAmount(), 10, "Amount must be multiplied by 2");
|
||||
assertEquals(item.amount(), 5, "Items with different amount should not be equals");
|
||||
assertEquals(item.withAmount(amount -> amount * 2).amount(), 10, "Amount must be multiplied by 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultBuilder() {
|
||||
var item = ItemStack.builder(Material.DIAMOND_SWORD).build();
|
||||
assertEquals(item.material(), Material.DIAMOND_SWORD, "Material must be the same");
|
||||
assertEquals(item.amount(), 1, "Default item amount must be 1");
|
||||
assertNull(item.getDisplayName(), "Default item display name must be null");
|
||||
assertTrue(item.getLore().isEmpty(), "Default item lore must be empty");
|
||||
ItemStack finalItem = item;
|
||||
assertThrows(Exception.class, () -> finalItem.getLore().add(Component.text("Hey!")), "Lore list cannot be modified directly");
|
||||
|
||||
item = item.withAmount(5);
|
||||
assertEquals(item.amount(), 5, "Items with different amount should not be equals");
|
||||
assertEquals(item.withAmount(amount -> amount * 2).amount(), 10, "Amount must be multiplied by 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -48,67 +56,56 @@ public class ItemTest {
|
||||
assertFalse(item1.isSimilar(item2.withDisplayName(Component.text("Hey!"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAir() {
|
||||
var item = ItemStack.of(Material.DIAMOND_SWORD);
|
||||
assertFalse(item.isAir());
|
||||
assertTrue(ItemStack.AIR.isAir());
|
||||
var emptyItem = item.withAmount(0);
|
||||
assertTrue(emptyItem.isAir());
|
||||
assertEquals(emptyItem, ItemStack.AIR, "AIR item can be compared to empty item");
|
||||
assertSame(emptyItem, ItemStack.AIR, "AIR item identity can be compared to empty item");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemNbt() {
|
||||
var itemNbt = ITEM.toItemNBT();
|
||||
assertEquals(itemNbt.getString("id"), ITEM.getMaterial().name(), "id string should be the material name");
|
||||
assertEquals(itemNbt.getByte("Count"), (byte) ITEM.getAmount(), "Count byte should be the item amount");
|
||||
var itemNbt = createItem().toItemNBT();
|
||||
assertEquals(itemNbt.getString("id"), createItem().material().name(), "id string should be the material name");
|
||||
assertEquals(itemNbt.getByte("Count"), (byte) createItem().amount(), "Count byte should be the item amount");
|
||||
var metaNbt = itemNbt.getCompound("tag");
|
||||
var metaNbt2 = ITEM.getMeta().toNBT();
|
||||
var metaNbt2 = createItem().meta().toNBT();
|
||||
assertEquals(metaNbt, metaNbt2, "tag compound should be equal to the meta nbt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromNbt() {
|
||||
var itemNbt = ITEM.toItemNBT();
|
||||
var itemNbt = createItem().toItemNBT();
|
||||
var item = ItemStack.fromItemNBT(itemNbt);
|
||||
assertEquals(ITEM, item, "Items must be equal if created from the same item nbt");
|
||||
assertEquals(createItem(), item, "Items must be equal if created from the same item nbt");
|
||||
assertEquals(itemNbt, item.toItemNBT(), "Item nbt must be equal back");
|
||||
|
||||
var metaNbt = ITEM.getMeta().toNBT();
|
||||
item = ItemStack.fromNBT(ITEM.getMaterial(), metaNbt, ITEM.getAmount());
|
||||
assertEquals(ITEM, item, "Items must be equal if created from the same meta nbt");
|
||||
var metaNbt = createItem().meta().toNBT();
|
||||
item = ItemStack.fromNBT(createItem().material(), metaNbt, createItem().amount());
|
||||
assertEquals(createItem(), item, "Items must be equal if created from the same meta nbt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnchant() {
|
||||
var item = ItemStack.of(Material.DIAMOND_SWORD);
|
||||
var enchantments = item.getMeta().getEnchantmentMap();
|
||||
var enchantments = item.meta().getEnchantmentMap();
|
||||
assertTrue(enchantments.isEmpty(), "items do not have enchantments by default");
|
||||
|
||||
item = item.withMeta(meta -> meta.enchantment(Enchantment.EFFICIENCY, (short) 10));
|
||||
enchantments = item.getMeta().getEnchantmentMap();
|
||||
enchantments = item.meta().getEnchantmentMap();
|
||||
assertEquals(enchantments.size(), 1);
|
||||
assertEquals(enchantments.get(Enchantment.EFFICIENCY), (short) 10);
|
||||
|
||||
item = item.withMeta(meta -> meta.enchantment(Enchantment.INFINITY, (short) 5));
|
||||
enchantments = item.getMeta().getEnchantmentMap();
|
||||
enchantments = item.meta().getEnchantmentMap();
|
||||
assertEquals(enchantments.size(), 2);
|
||||
assertEquals(enchantments.get(Enchantment.EFFICIENCY), (short) 10);
|
||||
assertEquals(enchantments.get(Enchantment.INFINITY), (short) 5);
|
||||
|
||||
item = item.withMeta(meta -> meta.enchantments(Map.of()));
|
||||
enchantments = item.getMeta().getEnchantmentMap();
|
||||
enchantments = item.meta().getEnchantmentMap();
|
||||
assertTrue(enchantments.isEmpty());
|
||||
|
||||
// Ensure that enchantments can still be modified after being emptied
|
||||
item = item.withMeta(meta -> meta.enchantment(Enchantment.EFFICIENCY, (short) 10));
|
||||
enchantments = item.getMeta().getEnchantmentMap();
|
||||
enchantments = item.meta().getEnchantmentMap();
|
||||
assertEquals(enchantments.get(Enchantment.EFFICIENCY), (short) 10);
|
||||
|
||||
item = item.withMeta(ItemMetaBuilder::clearEnchantment);
|
||||
enchantments = item.getMeta().getEnchantmentMap();
|
||||
item = item.withMeta(ItemMeta.Builder::clearEnchantment);
|
||||
enchantments = item.meta().getEnchantmentMap();
|
||||
assertTrue(enchantments.isEmpty());
|
||||
}
|
||||
|
||||
@ -116,13 +113,13 @@ public class ItemTest {
|
||||
public void testLore() {
|
||||
var item = ItemStack.of(Material.DIAMOND_SWORD);
|
||||
assertEquals(List.of(), item.getLore());
|
||||
assertNull(item.getMeta().toNBT().get("display"));
|
||||
assertNull(item.meta().toNBT().get("display"));
|
||||
|
||||
{
|
||||
var lore = List.of(Component.text("Hello"));
|
||||
item = item.withLore(lore);
|
||||
assertEquals(lore, item.getLore());
|
||||
var loreNbt = item.getMeta().toNBT().getCompound("display").<NBTString>getList("Lore");
|
||||
var loreNbt = item.meta().toNBT().getCompound("display").<NBTString>getList("Lore");
|
||||
assertNotNull(loreNbt);
|
||||
assertEquals(loreNbt.getSize(), 1);
|
||||
assertEquals(lore, loreNbt.asListView().stream().map(line -> GsonComponentSerializer.gson().deserialize(line.getValue())).toList());
|
||||
@ -132,7 +129,7 @@ public class ItemTest {
|
||||
var lore = List.of(Component.text("Hello"), Component.text("World"));
|
||||
item = item.withLore(lore);
|
||||
assertEquals(lore, item.getLore());
|
||||
var loreNbt = item.getMeta().toNBT().getCompound("display").<NBTString>getList("Lore");
|
||||
var loreNbt = item.meta().toNBT().getCompound("display").<NBTString>getList("Lore");
|
||||
assertNotNull(loreNbt);
|
||||
assertEquals(loreNbt.getSize(), 2);
|
||||
assertEquals(lore, loreNbt.asListView().stream().map(line -> GsonComponentSerializer.gson().deserialize(line.getValue())).toList());
|
||||
@ -142,7 +139,7 @@ public class ItemTest {
|
||||
var lore = Stream.of("string test").map(Component::text).toList();
|
||||
item = item.withLore(lore);
|
||||
assertEquals(lore, item.getLore());
|
||||
var loreNbt = item.getMeta().toNBT().getCompound("display").<NBTString>getList("Lore");
|
||||
var loreNbt = item.meta().toNBT().getCompound("display").<NBTString>getList("Lore");
|
||||
assertNotNull(loreNbt);
|
||||
assertEquals(loreNbt.getSize(), 1);
|
||||
assertEquals(lore, loreNbt.asListView().stream().map(line -> GsonComponentSerializer.gson().deserialize(line.getValue())).toList());
|
||||
@ -150,7 +147,7 @@ public class ItemTest {
|
||||
|
||||
// Ensure that lore can be properly removed without residual (display compound)
|
||||
item = item.withLore(List.of());
|
||||
assertNull(item.getMeta().toNBT().get("display"));
|
||||
assertNull(item.meta().toNBT().get("display"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -162,4 +159,14 @@ public class ItemTest {
|
||||
assertNotNull(item2.getDisplayName());
|
||||
assertNotEquals(item1, item2, "Item builder should be reusable");
|
||||
}
|
||||
|
||||
static ItemStack createItem() {
|
||||
return ItemStack.builder(Material.STONE)
|
||||
.displayName(Component.text("Display name!", NamedTextColor.GREEN))
|
||||
.lore(Component.text("Line 1"), Component.text("Line 2"))
|
||||
.meta(metaBuilder ->
|
||||
metaBuilder.enchantment(Enchantment.EFFICIENCY, (short) 10)
|
||||
.hideFlag(ItemHideFlag.HIDE_ENCHANTS))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
package regressions;
|
||||
|
||||
import net.minestom.server.item.ItemMeta;
|
||||
import net.minestom.server.item.ItemMetaBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTString;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class ItemMetaBuilderRegressions {
|
||||
|
||||
private static class BasicMetaBuilder extends ItemMetaBuilder {
|
||||
@Override
|
||||
public @NotNull ItemMeta build() {
|
||||
return new ItemMeta(this) {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(@NotNull NBTCompound nbtCompound) {
|
||||
// don't care
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCompound(@NotNull String key, @NotNull Consumer<@NotNull MutableNBTCompound> consumer) {
|
||||
super.handleCompound(key, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleCompoundShouldUsePreviousValue() {
|
||||
BasicMetaBuilder builder = new BasicMetaBuilder();
|
||||
|
||||
builder.handleCompound("test", nbt -> {
|
||||
nbt.setString("my_key", "AAA");
|
||||
});
|
||||
|
||||
NBTCompound nbt = builder.build().toNBT().getCompound("test");
|
||||
|
||||
assertTrue(nbt.contains("my_key"));
|
||||
assertEquals(1, nbt.getSize());
|
||||
assertTrue(nbt.get("my_key") instanceof NBTString);
|
||||
assertEquals("AAA", nbt.getString("my_key"));
|
||||
|
||||
builder.handleCompound("test", n -> {
|
||||
n.setString("my_other_key", "BBB");
|
||||
});
|
||||
|
||||
nbt = builder.build().toNBT().getCompound("test");
|
||||
|
||||
assertTrue(nbt.contains("my_key"));
|
||||
assertTrue(nbt.contains("my_other_key"));
|
||||
assertEquals(2, nbt.getSize());
|
||||
assertTrue(nbt.get("my_key") instanceof NBTString);
|
||||
assertTrue(nbt.get("my_other_key") instanceof NBTString);
|
||||
assertEquals("AAA", nbt.getString("my_key"));
|
||||
assertEquals("BBB", nbt.getString("my_other_key"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearingShouldRemoveData() {
|
||||
BasicMetaBuilder builder = new BasicMetaBuilder();
|
||||
|
||||
builder.handleCompound("test", nbt -> {
|
||||
nbt.setString("my_key", "AAA");
|
||||
});
|
||||
|
||||
NBTCompound nbt = builder.build().toNBT().getCompound("test");
|
||||
assertTrue(nbt.contains("my_key"));
|
||||
assertEquals(1, nbt.getSize());
|
||||
assertTrue(nbt.get("my_key") instanceof NBTString);
|
||||
assertEquals("AAA", nbt.getString("my_key"));
|
||||
|
||||
builder.handleCompound("test", n -> {
|
||||
n.clear();
|
||||
});
|
||||
|
||||
NBTCompound rootNBT = builder.build().toNBT();
|
||||
|
||||
assertFalse(rootNBT.contains("test"));
|
||||
assertEquals(0, rootNBT.getSize());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user