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.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.NBTCompound;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;
import java.util.function.UnaryOperator;
/**
* Represents an immutable item to be placed inside {@link net.minestom.server.inventory.PlayerInventory},
* {@link net.minestom.server.inventory.Inventory} or even on the ground {@link net.minestom.server.entity.ItemEntity}.
*
* An item stack cannot be null, {@link ItemStack#AIR} should be used instead.
*/
public sealed interface ItemStack extends TagReadable, HoverEventSource
permits ItemStackImpl {
/**
* Constant AIR item. Should be used instead of 'null'.
*/
@NotNull ItemStack AIR = ItemStack.of(Material.AIR);
@Contract(value = "_ -> new", pure = true)
static @NotNull Builder builder(@NotNull Material material) {
return new ItemStackImpl.Builder(material, 1);
}
@Contract(value = "_ ,_ -> new", pure = true)
static @NotNull ItemStack of(@NotNull Material material, int amount) {
return ItemStackImpl.create(material, amount);
}
@Contract(value = "_ -> new", pure = true)
static @NotNull ItemStack of(@NotNull Material material) {
return of(material, 1);
}
@Contract(value = "_, _, _ -> new", pure = true)
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)
static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound) {
return fromNBT(material, nbtCompound, 1);
}
/**
* Converts this item to an NBT tag containing the id (material), count (amount), and tag (meta).
*
* @param nbtCompound The nbt representation of the item
*/
@ApiStatus.Experimental
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);
Check.notNull(material, "Unknown material: {0}", id);
Byte amount = nbtCompound.getByte("Count");
if (amount == null) amount = 1;
final NBTCompound tag = nbtCompound.getCompound("tag");
return tag != null ? fromNBT(material, tag, amount) : of(material, amount);
}
@Contract(pure = true)
@NotNull Material material();
@Contract(pure = true)
int amount();
@Contract(pure = true)
@NotNull ItemMeta meta();
@Contract(pure = true)
@ApiStatus.Experimental
> @NotNull T meta(@NotNull Class metaClass);
@Contract(value = "_, -> new", pure = true)
@NotNull ItemStack with(@NotNull Consumer<@NotNull Builder> builderConsumer);
@Contract(value = "_, _ -> new", pure = true)
@ApiStatus.Experimental
> @NotNull ItemStack withMeta(@NotNull Class metaType,
@NotNull Consumer metaConsumer);
@Contract(value = "_ -> new", pure = true)
@NotNull ItemStack withMeta(@NotNull UnaryOperator 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)
default @NotNull ItemStack withMeta(@NotNull ItemMeta meta) {
return new ItemStackImpl(material(), amount(), meta);
}
@Contract(value = "_, -> new", pure = true)
default @NotNull ItemStack withDisplayName(@Nullable Component displayName) {
return withMeta(builder -> builder.displayName(displayName));
}
@Contract(value = "_, -> new", pure = true)
default @NotNull ItemStack withDisplayName(@NotNull UnaryOperator<@Nullable Component> componentUnaryOperator) {
return withDisplayName(componentUnaryOperator.apply(getDisplayName()));
}
@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)
default @NotNull ItemStack withLore(@NotNull UnaryOperator<@NotNull List<@NotNull Component>> loreUnaryOperator) {
return withLore(loreUnaryOperator.apply(getLore()));
}
@Contract(pure = true)
default boolean isAir() {
return material() == Material.AIR;
}
@Contract(pure = true)
boolean isSimilar(@NotNull ItemStack itemStack);
@Contract(value = "_, _ -> new", pure = true)
default @NotNull ItemStack withTag(@NotNull Tag tag, @Nullable T value) {
return withMeta(builder -> builder.set(tag, value));
}
@Override
default @UnknownNullability T getTag(@NotNull Tag tag) {
return meta().getTag(tag);
}
@Override
default @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) {
return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.of(material(), amount(),
NBTUtils.asBinaryTagHolder(meta().toNBT()))));
}
/**
* Converts this item to an NBT tag containing the id (material), count (amount), and tag (meta)
*
* @return The nbt representation of the item
*/
@ApiStatus.Experimental
@NotNull NBTCompound toItemNBT();
@Deprecated
@Contract(pure = true)
default @NotNull Material getMaterial() {
return material();
}
@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 consumer);
@Contract(value = "_, _ -> this")
> @NotNull Builder meta(@NotNull Class 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));
}
}
}