All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.minestom.server.item.ItemStack Maven / Gradle / Ivy

There is a newer version: 7320437640
Show newest version
package net.minestom.server.item;

import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEventSource;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.MinestomAdventure;
import net.minestom.server.component.DataComponent;
import net.minestom.server.component.DataComponentMap;
import net.minestom.server.item.component.CustomData;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.tag.Tag;
import net.minestom.server.tag.TagReadable;
import net.minestom.server.utils.Unit;
import net.minestom.server.utils.nbt.BinaryTagSerializer;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;

import java.io.IOException;
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, DataComponent.Holder, HoverEventSource permits ItemStackImpl { @NotNull NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { @Override public void write(@NotNull NetworkBuffer buffer, ItemStack value) { if (value.isAir()) { buffer.write(NetworkBuffer.VAR_INT, 0); return; } buffer.write(NetworkBuffer.VAR_INT, value.amount()); buffer.write(NetworkBuffer.VAR_INT, value.material().id()); buffer.write(ItemComponent.PATCH_NETWORK_TYPE, ((ItemStackImpl) value).components()); } @Override public ItemStack read(@NotNull NetworkBuffer buffer) { int amount = buffer.read(NetworkBuffer.VAR_INT); if (amount <= 0) return ItemStack.AIR; Material material = Material.fromId(buffer.read(NetworkBuffer.VAR_INT)); DataComponentMap components = buffer.read(ItemComponent.PATCH_NETWORK_TYPE); return ItemStackImpl.create(material, amount, components); } }; @NotNull NetworkBuffer.Type STRICT_NETWORK_TYPE = NETWORK_TYPE.transform(itemStack -> { Check.argCondition(itemStack.amount() == 0 || itemStack.isAir(), "ItemStack cannot be empty"); return itemStack; }, itemStack -> { Check.argCondition(itemStack.amount() == 0 || itemStack.isAir(), "ItemStack cannot be empty"); return itemStack; }); @NotNull BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map(ItemStackImpl::fromCompound, ItemStackImpl::toCompound); /** * 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) { return of(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, @NotNull DataComponentMap components) { return ItemStackImpl.create(material, 1, components); } @Contract(value = "_ ,_, _ -> new", pure = true) static @NotNull ItemStack of(@NotNull Material material, int amount, @NotNull DataComponentMap components) { return ItemStackImpl.create(material, amount, components); } /** * Converts this item to an NBT tag containing the id (material), count (amount), and components. * * @param nbtCompound The nbt representation of the item */ static @NotNull ItemStack fromItemNBT(@NotNull CompoundBinaryTag nbtCompound) { BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), false); return NBT_TYPE.read(context, nbtCompound); } @Contract(pure = true) @NotNull Material material(); @Contract(pure = true) int amount(); @Contract(value = "_, -> new", pure = true) @NotNull ItemStack with(@NotNull Consumer<@NotNull Builder> consumer); @Contract(value = "_, -> new", pure = true) @NotNull ItemStack withMaterial(@NotNull Material material); @Contract(value = "_, -> new", pure = true) @NotNull ItemStack withAmount(int amount); @Contract(value = "_, -> new", pure = true) default @NotNull ItemStack withAmount(@NotNull IntUnaryOperator intUnaryOperator) { return withAmount(intUnaryOperator.applyAsInt(amount())); } /** *

Returns a new ItemStack with the given component set to the given value.

* *

Note: this should not be used to remove components, see {@link #without(DataComponent)}.

*/ @Contract(value = "_, _ -> new", pure = true) @NotNull ItemStack with(@NotNull DataComponent component, @NotNull T value); /** * Returns a new ItemStack with the given {@link Unit} component applied. * * @param component The unit component to apply * @return A new ItemStack with the given component applied */ @Contract(value = "_ -> new", pure = true) default @NotNull ItemStack with(@NotNull DataComponent component) { return with(component, Unit.INSTANCE); } /** * Applies a transformation to the value of a component, only if present. * * @param component The component type to modify * @param operator The transformation function * @return A new ItemStack if the component was transformed, otherwise this. * @param The component type */ default @NotNull ItemStack with(@NotNull DataComponent component, @NotNull UnaryOperator operator) { T value = get(component); if (value == null) return this; return with(component, operator.apply(value)); } /** *

Removes the given component from this item. This will explicitly remove the component from the item, as opposed * to reverting back to the default.

* *

For example, if {@link ItemComponent#FOOD} is applied to an apple, and then this method is called, * the resulting itemstack will not be a food item at all, as opposed to returning to the default apple * food type. Likewise, if this method is called on a default apple, it will no longer be a food item.

* * @param component The component to remove * @return A new ItemStack without the given component */ @Contract(value = "_, -> new", pure = true) @NotNull ItemStack without(@NotNull DataComponent component); @Contract(value = "_, -> new", pure = true) default @NotNull ItemStack withCustomName(@NotNull Component customName) { return with(ItemComponent.CUSTOM_NAME, customName); } @Contract(value = "_, -> new", pure = true) default @NotNull ItemStack withLore(@NotNull Component... lore) { return with(ItemComponent.LORE, List.of(lore)); } @Contract(value = "_, -> new", pure = true) default @NotNull ItemStack withLore(@NotNull List lore) { return with(ItemComponent.LORE, lore); } @Contract(value = "_ -> new", pure = true) default @NotNull ItemStack withCustomModelData(int customModelData) { return with(ItemComponent.CUSTOM_MODEL_DATA, customModelData); } @Contract(value = "_ -> new", pure = true) default @NotNull ItemStack withGlowing(boolean glowing) { return with(ItemComponent.ENCHANTMENT_GLINT_OVERRIDE, glowing); } @Contract(value = "-> new", pure = true) default @NotNull ItemStack withoutExtraTooltip() { return builder().hideExtraTooltip().build(); } @Contract(pure = true) default int maxStackSize() { return get(ItemComponent.MAX_STACK_SIZE, 64); } @Contract(value = "_ -> new", pure = true) default @NotNull ItemStack withMaxStackSize(int maxStackSize) { return with(ItemComponent.MAX_STACK_SIZE, maxStackSize); } @Contract(value = "_, _ -> new", pure = true) default @NotNull ItemStack withTag(@NotNull Tag tag, @Nullable T value) { return with(ItemComponent.CUSTOM_DATA, get(ItemComponent.CUSTOM_DATA, CustomData.EMPTY).withTag(tag, value)); } @Override @Contract(pure = true) default @UnknownNullability T getTag(@NotNull Tag tag) { return get(ItemComponent.CUSTOM_DATA, CustomData.EMPTY).getTag(tag); } @Contract(value = "_, -> new", pure = true) @NotNull ItemStack consume(int amount); @Contract(pure = true) default boolean isAir() { return material() == Material.AIR; } @Contract(pure = true) boolean isSimilar(@NotNull ItemStack itemStack); /** * Converts this itemstack back into a builder (starting from the current state). * @return this itemstack, as a builder. */ @NotNull ItemStack.Builder builder(); /** * Converts this item to an NBT tag containing the id (material), count (amount), and components (diff) * * @return The nbt representation of the item */ @NotNull CompoundBinaryTag toItemNBT(); @Override default @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { try { BinaryTagHolder tagHolder = BinaryTagHolder.encode((CompoundBinaryTag) NBT_TYPE.write(this), MinestomAdventure.NBT_CODEC); return HoverEvent.showItem(op.apply(HoverEvent.ShowItem.showItem(material(), amount(), tagHolder))); } catch (IOException e) { throw new RuntimeException("failed to encode itemstack nbt", e); } } sealed interface Builder permits ItemStackImpl.Builder { @Contract(value = "_ -> this") @NotNull Builder material(@NotNull Material material); @Contract(value = "_ -> this") @NotNull Builder amount(int amount); @Contract(value = "_, _ -> this") @NotNull Builder set(@NotNull DataComponent component, T value); @Contract(value = "_ -> this") default @NotNull Builder set(@NotNull DataComponent component) { return set(component, Unit.INSTANCE); } @Contract(value = "_ -> this") @NotNull Builder remove(@NotNull DataComponent component); default @NotNull Builder customName(@NotNull Component customName) { return set(ItemComponent.CUSTOM_NAME, customName); } default @NotNull Builder lore(@NotNull Component... lore) { return set(ItemComponent.LORE, List.of(lore)); } default @NotNull Builder lore(@NotNull List lore) { return set(ItemComponent.LORE, lore); } default @NotNull Builder customModelData(int customModelData) { return set(ItemComponent.CUSTOM_MODEL_DATA, customModelData); } default @NotNull Builder glowing() { return set(ItemComponent.ENCHANTMENT_GLINT_OVERRIDE, true); } default @NotNull Builder glowing(boolean glowing) { return set(ItemComponent.ENCHANTMENT_GLINT_OVERRIDE, glowing); } default @NotNull Builder maxStackSize(int maxStackSize) { return set(ItemComponent.MAX_STACK_SIZE, maxStackSize); } /** *

Sets all available tooltip hide flags. The result should be an item with only name and lore.

* *

One notable behavior here is that {@link ItemComponent#ATTRIBUTE_MODIFIERS} will be added if it is not * present. This is because armor flags in tooltips use attribute modifiers show flag to display or not, but * are not actually based on the attribute modifiers component.

*/ @NotNull Builder hideExtraTooltip(); @Contract(value = "_, _ -> this") @NotNull Builder set(@NotNull Tag tag, @Nullable T value); default void setTag(@NotNull Tag tag, @Nullable T value) { set(tag, value); } @Contract(value = "-> new", pure = true) @NotNull ItemStack build(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy