mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-27 06:50:12 +01:00
dae475b636
* Switch Impl types to Holderable * Fix compile issues * more improvements * compile fixes * remove unneeded unwrapAndConvertHolder call --------- Co-authored-by: Bjarne Koll <git@lynxplay.dev>
4926 lines
203 KiB
Diff
4926 lines
203 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
|
|
Date: Sun, 28 Apr 2024 19:53:01 -0400
|
|
Subject: [PATCH] DataComponent API
|
|
|
|
Exposes the data component logic used by vanilla ItemStack to API
|
|
consumers as a version-specific API.
|
|
The types and methods introduced by this patch do not follow the general
|
|
API deprecation contracts and will be adapted to each new minecraft
|
|
release without backwards compatibility measures.
|
|
|
|
== AT ==
|
|
public net/minecraft/world/item/component/ItemContainerContents MAX_SIZE
|
|
public net/minecraft/world/item/component/ItemContainerContents items
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java b/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..957fdf1e32d109b8131359a159ea6817885968d1
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapter.java
|
|
@@ -0,0 +1,36 @@
|
|
+package io.papermc.paper.datacomponent;
|
|
+
|
|
+import java.util.function.Function;
|
|
+import net.minecraft.core.component.DataComponentType;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.util.NullOps;
|
|
+import net.minecraft.util.Unit;
|
|
+import org.bukkit.craftbukkit.CraftRegistry;
|
|
+
|
|
+public record DataComponentAdapter<NMS, API>(
|
|
+ DataComponentType<NMS> type,
|
|
+ Function<API, NMS> apiToVanilla,
|
|
+ Function<NMS, API> vanillaToApi,
|
|
+ boolean codecValidation
|
|
+) {
|
|
+ static final Function<Void, Unit> API_TO_UNIT_CONVERTER = $ -> Unit.INSTANCE;
|
|
+
|
|
+ public boolean isValued() {
|
|
+ return this.apiToVanilla != API_TO_UNIT_CONVERTER;
|
|
+ }
|
|
+
|
|
+ public NMS toVanilla(final API value) {
|
|
+ final NMS nms = this.apiToVanilla.apply(value);
|
|
+ if (this.codecValidation) {
|
|
+ this.type.codecOrThrow().encodeStart(CraftRegistry.getMinecraftRegistry().createSerializationContext(NullOps.INSTANCE), nms).ifError(error -> {
|
|
+ throw new IllegalArgumentException("Failed to encode data component %s (%s)".formatted(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(this.type), error.message()));
|
|
+ });
|
|
+ }
|
|
+
|
|
+ return nms;
|
|
+ }
|
|
+
|
|
+ public API fromVanilla(final NMS value) {
|
|
+ return this.vanillaToApi.apply(value);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java b/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7675588202b20af182cc44253f4c036d37000a8c
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/DataComponentAdapters.java
|
|
@@ -0,0 +1,170 @@
|
|
+package io.papermc.paper.datacomponent;
|
|
+
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.datacomponent.item.PaperBannerPatternLayers;
|
|
+import io.papermc.paper.datacomponent.item.PaperBlockItemDataProperties;
|
|
+import io.papermc.paper.datacomponent.item.PaperBundleContents;
|
|
+import io.papermc.paper.datacomponent.item.PaperChargedProjectiles;
|
|
+import io.papermc.paper.datacomponent.item.PaperConsumable;
|
|
+import io.papermc.paper.datacomponent.item.PaperCustomModelData;
|
|
+import io.papermc.paper.datacomponent.item.PaperDamageResistant;
|
|
+import io.papermc.paper.datacomponent.item.PaperDeathProtection;
|
|
+import io.papermc.paper.datacomponent.item.PaperDyedItemColor;
|
|
+import io.papermc.paper.datacomponent.item.PaperEnchantable;
|
|
+import io.papermc.paper.datacomponent.item.PaperEquippable;
|
|
+import io.papermc.paper.datacomponent.item.PaperFireworks;
|
|
+import io.papermc.paper.datacomponent.item.PaperFoodProperties;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemAdventurePredicate;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemArmorTrim;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemAttributeModifiers;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemContainerContents;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemEnchantments;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemLore;
|
|
+import io.papermc.paper.datacomponent.item.PaperItemTool;
|
|
+import io.papermc.paper.datacomponent.item.PaperJukeboxPlayable;
|
|
+import io.papermc.paper.datacomponent.item.PaperLodestoneTracker;
|
|
+import io.papermc.paper.datacomponent.item.PaperMapDecorations;
|
|
+import io.papermc.paper.datacomponent.item.PaperMapId;
|
|
+import io.papermc.paper.datacomponent.item.PaperMapItemColor;
|
|
+import io.papermc.paper.datacomponent.item.PaperOminousBottleAmplifier;
|
|
+import io.papermc.paper.datacomponent.item.PaperPotDecorations;
|
|
+import io.papermc.paper.datacomponent.item.PaperPotionContents;
|
|
+import io.papermc.paper.datacomponent.item.PaperRepairable;
|
|
+import io.papermc.paper.datacomponent.item.PaperResolvableProfile;
|
|
+import io.papermc.paper.datacomponent.item.PaperSeededContainerLoot;
|
|
+import io.papermc.paper.datacomponent.item.PaperSuspiciousStewEffects;
|
|
+import io.papermc.paper.datacomponent.item.PaperUnbreakable;
|
|
+import io.papermc.paper.datacomponent.item.PaperUseCooldown;
|
|
+import io.papermc.paper.datacomponent.item.PaperUseRemainder;
|
|
+import io.papermc.paper.datacomponent.item.PaperWritableBookContent;
|
|
+import io.papermc.paper.datacomponent.item.PaperWrittenBookContent;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import java.util.function.Function;
|
|
+import net.minecraft.core.component.DataComponentType;
|
|
+import net.minecraft.core.component.DataComponents;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.resources.ResourceKey;
|
|
+import net.minecraft.util.Unit;
|
|
+import net.minecraft.world.item.Rarity;
|
|
+import net.minecraft.world.item.component.MapPostProcessing;
|
|
+import org.bukkit.DyeColor;
|
|
+import org.bukkit.craftbukkit.CraftMusicInstrument;
|
|
+import org.bukkit.craftbukkit.inventory.CraftMetaFirework;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemRarity;
|
|
+
|
|
+import static io.papermc.paper.util.MCUtil.transformUnmodifiable;
|
|
+
|
|
+public final class DataComponentAdapters {
|
|
+
|
|
+ static final Function<Unit, Void> UNIT_TO_API_CONVERTER = $ -> {
|
|
+ throw new UnsupportedOperationException("Cannot convert the Unit type to an API value");
|
|
+ };
|
|
+
|
|
+ static final Map<ResourceKey<DataComponentType<?>>, DataComponentAdapter<?, ?>> ADAPTERS = new HashMap<>();
|
|
+
|
|
+ public static void bootstrap() {
|
|
+ registerIdentity(DataComponents.MAX_STACK_SIZE);
|
|
+ registerIdentity(DataComponents.MAX_DAMAGE);
|
|
+ registerIdentity(DataComponents.DAMAGE);
|
|
+ register(DataComponents.UNBREAKABLE, PaperUnbreakable::new);
|
|
+ register(DataComponents.CUSTOM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
|
|
+ register(DataComponents.ITEM_NAME, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
|
|
+ register(DataComponents.ITEM_MODEL, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
|
|
+ register(DataComponents.LORE, PaperItemLore::new);
|
|
+ register(DataComponents.RARITY, nms -> ItemRarity.valueOf(nms.name()), api -> Rarity.valueOf(api.name()));
|
|
+ register(DataComponents.ENCHANTMENTS, PaperItemEnchantments::new);
|
|
+ register(DataComponents.CAN_PLACE_ON, PaperItemAdventurePredicate::new);
|
|
+ register(DataComponents.CAN_BREAK, PaperItemAdventurePredicate::new);
|
|
+ register(DataComponents.ATTRIBUTE_MODIFIERS, PaperItemAttributeModifiers::new);
|
|
+ register(DataComponents.CUSTOM_MODEL_DATA, PaperCustomModelData::new);
|
|
+ registerUntyped(DataComponents.HIDE_ADDITIONAL_TOOLTIP);
|
|
+ registerUntyped(DataComponents.HIDE_TOOLTIP);
|
|
+ registerIdentity(DataComponents.REPAIR_COST);
|
|
+ // registerUntyped(DataComponents.CREATIVE_SLOT_LOCK);
|
|
+ registerIdentity(DataComponents.ENCHANTMENT_GLINT_OVERRIDE);
|
|
+ registerUntyped(DataComponents.INTANGIBLE_PROJECTILE);
|
|
+ register(DataComponents.FOOD, PaperFoodProperties::new);
|
|
+ register(DataComponents.CONSUMABLE, PaperConsumable::new);
|
|
+ register(DataComponents.USE_REMAINDER, PaperUseRemainder::new);
|
|
+ register(DataComponents.USE_COOLDOWN, PaperUseCooldown::new);
|
|
+ register(DataComponents.DAMAGE_RESISTANT, PaperDamageResistant::new);
|
|
+ register(DataComponents.TOOL, PaperItemTool::new);
|
|
+ register(DataComponents.ENCHANTABLE, PaperEnchantable::new);
|
|
+ register(DataComponents.EQUIPPABLE, PaperEquippable::new);
|
|
+ register(DataComponents.REPAIRABLE, PaperRepairable::new);
|
|
+ registerUntyped(DataComponents.GLIDER);
|
|
+ register(DataComponents.TOOLTIP_STYLE, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
|
|
+ register(DataComponents.DEATH_PROTECTION, PaperDeathProtection::new);
|
|
+ register(DataComponents.STORED_ENCHANTMENTS, PaperItemEnchantments::new);
|
|
+ register(DataComponents.DYED_COLOR, PaperDyedItemColor::new);
|
|
+ register(DataComponents.MAP_COLOR, PaperMapItemColor::new);
|
|
+ register(DataComponents.MAP_ID, PaperMapId::new);
|
|
+ register(DataComponents.MAP_DECORATIONS, PaperMapDecorations::new);
|
|
+ register(DataComponents.MAP_POST_PROCESSING, nms -> io.papermc.paper.item.MapPostProcessing.valueOf(nms.name()), api -> MapPostProcessing.valueOf(api.name()));
|
|
+ register(DataComponents.CHARGED_PROJECTILES, PaperChargedProjectiles::new);
|
|
+ register(DataComponents.BUNDLE_CONTENTS, PaperBundleContents::new);
|
|
+ register(DataComponents.POTION_CONTENTS, PaperPotionContents::new);
|
|
+ register(DataComponents.SUSPICIOUS_STEW_EFFECTS, PaperSuspiciousStewEffects::new);
|
|
+ register(DataComponents.WRITTEN_BOOK_CONTENT, PaperWrittenBookContent::new);
|
|
+ register(DataComponents.WRITABLE_BOOK_CONTENT, PaperWritableBookContent::new);
|
|
+ register(DataComponents.TRIM, PaperItemArmorTrim::new);
|
|
+ // debug stick state
|
|
+ // entity data
|
|
+ // bucket entity data
|
|
+ // block entity data
|
|
+ register(DataComponents.INSTRUMENT, CraftMusicInstrument::minecraftHolderToBukkit, CraftMusicInstrument::bukkitToMinecraftHolder);
|
|
+ register(DataComponents.OMINOUS_BOTTLE_AMPLIFIER, PaperOminousBottleAmplifier::new);
|
|
+ register(DataComponents.JUKEBOX_PLAYABLE, PaperJukeboxPlayable::new);
|
|
+ register(DataComponents.RECIPES,
|
|
+ nms -> transformUnmodifiable(nms, PaperAdventure::asAdventureKey),
|
|
+ api -> transformUnmodifiable(api, key -> PaperAdventure.asVanilla(Registries.RECIPE, key))
|
|
+ );
|
|
+ register(DataComponents.LODESTONE_TRACKER, PaperLodestoneTracker::new);
|
|
+ register(DataComponents.FIREWORK_EXPLOSION, CraftMetaFirework::getEffect, CraftMetaFirework::getExplosion);
|
|
+ register(DataComponents.FIREWORKS, PaperFireworks::new);
|
|
+ register(DataComponents.PROFILE, PaperResolvableProfile::new);
|
|
+ register(DataComponents.NOTE_BLOCK_SOUND, PaperAdventure::asAdventure, PaperAdventure::asVanilla);
|
|
+ register(DataComponents.BANNER_PATTERNS, PaperBannerPatternLayers::new);
|
|
+ register(DataComponents.BASE_COLOR, nms -> DyeColor.getByWoolData((byte) nms.getId()), api -> net.minecraft.world.item.DyeColor.byId(api.getWoolData()));
|
|
+ register(DataComponents.POT_DECORATIONS, PaperPotDecorations::new);
|
|
+ register(DataComponents.CONTAINER, PaperItemContainerContents::new);
|
|
+ register(DataComponents.BLOCK_STATE, PaperBlockItemDataProperties::new);
|
|
+ // bees
|
|
+ // register(DataComponents.LOCK, PaperLockCode::new);
|
|
+ register(DataComponents.CONTAINER_LOOT, PaperSeededContainerLoot::new);
|
|
+
|
|
+ // TODO: REMOVE THIS... we want to build the PR... so lets just make things UNTYPED!
|
|
+ for (final Map.Entry<ResourceKey<DataComponentType<?>>, DataComponentType<?>> componentType : BuiltInRegistries.DATA_COMPONENT_TYPE.entrySet()) {
|
|
+ if (!ADAPTERS.containsKey(componentType.getKey())) {
|
|
+ registerUntyped((DataComponentType<Unit>) componentType.getValue());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void registerUntyped(final DataComponentType<Unit> type) {
|
|
+ registerInternal(type, UNIT_TO_API_CONVERTER, DataComponentAdapter.API_TO_UNIT_CONVERTER, false);
|
|
+ }
|
|
+
|
|
+ private static <COMMON> void registerIdentity(final DataComponentType<COMMON> type) {
|
|
+ registerInternal(type, Function.identity(), Function.identity(), true);
|
|
+ }
|
|
+
|
|
+ private static <NMS, API extends Handleable<NMS>> void register(final DataComponentType<NMS> type, final Function<NMS, API> vanillaToApi) {
|
|
+ registerInternal(type, vanillaToApi, Handleable::getHandle, false);
|
|
+ }
|
|
+
|
|
+ private static <NMS, API> void register(final DataComponentType<NMS> type, final Function<NMS, API> vanillaToApi, final Function<API, NMS> apiToVanilla) {
|
|
+ registerInternal(type, vanillaToApi, apiToVanilla, false);
|
|
+ }
|
|
+
|
|
+ private static <NMS, API> void registerInternal(final DataComponentType<NMS> type, final Function<NMS, API> vanillaToApi, final Function<API, NMS> apiToVanilla, final boolean codecValidation) {
|
|
+ final ResourceKey<DataComponentType<?>> key = BuiltInRegistries.DATA_COMPONENT_TYPE.getResourceKey(type).orElseThrow();
|
|
+ if (ADAPTERS.containsKey(key)) {
|
|
+ throw new IllegalStateException("Duplicate adapter registration for " + key);
|
|
+ }
|
|
+ ADAPTERS.put(key, new DataComponentAdapter<>(type, apiToVanilla, vanillaToApi, codecValidation && !type.isTransient()));
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java b/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e2fcf870b2256e3df90372c3208f3ed27469b16e
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/PaperDataComponentType.java
|
|
@@ -0,0 +1,109 @@
|
|
+package io.papermc.paper.datacomponent;
|
|
+
|
|
+import java.util.Collections;
|
|
+import java.util.HashSet;
|
|
+import java.util.Set;
|
|
+import net.minecraft.core.component.DataComponentMap;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import org.bukkit.NamespacedKey;
|
|
+import org.bukkit.Registry;
|
|
+import org.bukkit.craftbukkit.CraftRegistry;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jspecify.annotations.Nullable;
|
|
+
|
|
+public abstract class PaperDataComponentType<T, NMS> implements DataComponentType, Handleable<net.minecraft.core.component.DataComponentType<NMS>> {
|
|
+
|
|
+ static {
|
|
+ DataComponentAdapters.bootstrap();
|
|
+ }
|
|
+
|
|
+ public static <T> net.minecraft.core.component.DataComponentType<T> bukkitToMinecraft(final DataComponentType type) {
|
|
+ return CraftRegistry.bukkitToMinecraft(type);
|
|
+ }
|
|
+
|
|
+ public static DataComponentType minecraftToBukkit(final net.minecraft.core.component.DataComponentType<?> type) {
|
|
+ return CraftRegistry.minecraftToBukkit(type, Registries.DATA_COMPONENT_TYPE, Registry.DATA_COMPONENT_TYPE);
|
|
+ }
|
|
+
|
|
+ public static Set<DataComponentType> minecraftToBukkit(final Set<net.minecraft.core.component.DataComponentType<?>> nmsTypes) {
|
|
+ final Set<DataComponentType> types = new HashSet<>(nmsTypes.size());
|
|
+ for (final net.minecraft.core.component.DataComponentType<?> nmsType : nmsTypes) {
|
|
+ types.add(PaperDataComponentType.minecraftToBukkit(nmsType));
|
|
+ }
|
|
+ return Collections.unmodifiableSet(types);
|
|
+ }
|
|
+
|
|
+ public static <B, M> @Nullable B convertDataComponentValue(final DataComponentMap map, final PaperDataComponentType.ValuedImpl<B, M> type) {
|
|
+ final net.minecraft.core.component.DataComponentType<M> nms = bukkitToMinecraft(type);
|
|
+ final M nmsValue = map.get(nms);
|
|
+ if (nmsValue == null) {
|
|
+ return null;
|
|
+ }
|
|
+ return type.getAdapter().fromVanilla(nmsValue);
|
|
+ }
|
|
+
|
|
+ private final NamespacedKey key;
|
|
+ private final net.minecraft.core.component.DataComponentType<NMS> type;
|
|
+ private final DataComponentAdapter<NMS, T> adapter;
|
|
+
|
|
+ public PaperDataComponentType(final NamespacedKey key, final net.minecraft.core.component.DataComponentType<NMS> type, final DataComponentAdapter<NMS, T> adapter) {
|
|
+ this.key = key;
|
|
+ this.type = type;
|
|
+ this.adapter = adapter;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public NamespacedKey getKey() {
|
|
+ return this.key;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isPersistent() {
|
|
+ return !this.type.isTransient();
|
|
+ }
|
|
+
|
|
+ public DataComponentAdapter<NMS, T> getAdapter() {
|
|
+ return this.adapter;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.core.component.DataComponentType<NMS> getHandle() {
|
|
+ return this.type;
|
|
+ }
|
|
+
|
|
+ @SuppressWarnings("unchecked")
|
|
+ public static <NMS> DataComponentType of(final NamespacedKey key, final net.minecraft.core.component.DataComponentType<NMS> type) {
|
|
+ final DataComponentAdapter<NMS, ?> adapter = (DataComponentAdapter<NMS, ?>) DataComponentAdapters.ADAPTERS.get(BuiltInRegistries.DATA_COMPONENT_TYPE.getResourceKey(type).orElseThrow());
|
|
+ if (adapter == null) {
|
|
+ throw new IllegalArgumentException("No adapter found for " + key);
|
|
+ }
|
|
+ if (adapter.isValued()) {
|
|
+ return new ValuedImpl<>(key, type, adapter);
|
|
+ } else {
|
|
+ return new NonValuedImpl<>(key, type, adapter);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static final class NonValuedImpl<T, NMS> extends PaperDataComponentType<T, NMS> implements NonValued {
|
|
+
|
|
+ NonValuedImpl(
|
|
+ final NamespacedKey key,
|
|
+ final net.minecraft.core.component.DataComponentType<NMS> type,
|
|
+ final DataComponentAdapter<NMS, T> adapter
|
|
+ ) {
|
|
+ super(key, type, adapter);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static final class ValuedImpl<T, NMS> extends PaperDataComponentType<T, NMS> implements Valued<T> {
|
|
+
|
|
+ ValuedImpl(
|
|
+ final NamespacedKey key,
|
|
+ final net.minecraft.core.component.DataComponentType<NMS> type,
|
|
+ final DataComponentAdapter<NMS, T> adapter
|
|
+ ) {
|
|
+ super(key, type, adapter);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..15c66b0186ffede98a196f63e0e616b125bac35a
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/ItemComponentTypesBridgesImpl.java
|
|
@@ -0,0 +1,239 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.destroystokyo.paper.profile.PlayerProfile;
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.registry.PaperRegistries;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.registry.set.RegistryKeySet;
|
|
+import io.papermc.paper.registry.tag.TagKey;
|
|
+import io.papermc.paper.text.Filtered;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.kyori.adventure.util.TriState;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.world.item.component.OminousBottleAmplifier;
|
|
+import org.bukkit.JukeboxSong;
|
|
+import org.bukkit.block.BlockType;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.damage.DamageType;
|
|
+import org.bukkit.inventory.EquipmentSlot;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+import org.bukkit.inventory.ItemType;
|
|
+import org.bukkit.inventory.meta.trim.ArmorTrim;
|
|
+import org.bukkit.map.MapCursor;
|
|
+import org.jspecify.annotations.Nullable;
|
|
+
|
|
+public final class ItemComponentTypesBridgesImpl implements ItemComponentTypesBridge {
|
|
+
|
|
+ @Override
|
|
+ public ChargedProjectiles.Builder chargedProjectiles() {
|
|
+ return new PaperChargedProjectiles.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotDecorations.Builder potDecorations() {
|
|
+ return new PaperPotDecorations.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Unbreakable.Builder unbreakable() {
|
|
+ return new PaperUnbreakable.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemLore.Builder lore() {
|
|
+ return new PaperItemLore.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemEnchantments.Builder enchantments() {
|
|
+ return new PaperItemEnchantments.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAttributeModifiers.Builder modifiers() {
|
|
+ return new PaperItemAttributeModifiers.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public FoodProperties.Builder food() {
|
|
+ return new PaperFoodProperties.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DyedItemColor.Builder dyedItemColor() {
|
|
+ return new PaperDyedItemColor.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotionContents.Builder potionContents() {
|
|
+ return new PaperPotionContents.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BundleContents.Builder bundleContents() {
|
|
+ return new PaperBundleContents.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SuspiciousStewEffects.Builder suspiciousStewEffects() {
|
|
+ return new PaperSuspiciousStewEffects.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapItemColor.Builder mapItemColor() {
|
|
+ return new PaperMapItemColor.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapDecorations.Builder mapDecorations() {
|
|
+ return new PaperMapDecorations.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapDecorations.DecorationEntry decorationEntry(final MapCursor.Type type, final double x, final double z, final float rotation) {
|
|
+ return PaperMapDecorations.PaperDecorationEntry.toApi(type, x, z, rotation);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SeededContainerLoot.Builder seededContainerLoot(final Key lootTableKey) {
|
|
+ return new PaperSeededContainerLoot.BuilderImpl(lootTableKey);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemContainerContents.Builder itemContainerContents() {
|
|
+ return new PaperItemContainerContents.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public JukeboxPlayable.Builder jukeboxPlayable(final JukeboxSong song) {
|
|
+ return new PaperJukeboxPlayable.BuilderImpl(song);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Tool.Builder tool() {
|
|
+ return new PaperItemTool.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Tool.Rule rule(final RegistryKeySet<BlockType> blocks, final @Nullable Float speed, final TriState correctForDrops) {
|
|
+ return PaperItemTool.PaperRule.fromUnsafe(blocks, speed, correctForDrops);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAdventurePredicate.Builder itemAdventurePredicate() {
|
|
+ return new PaperItemAdventurePredicate.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder writtenBookContent(final Filtered<String> title, final String author) {
|
|
+ return new PaperWrittenBookContent.BuilderImpl(title, author);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WritableBookContent.Builder writeableBookContent() {
|
|
+ return new PaperWritableBookContent.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemArmorTrim.Builder itemArmorTrim(final ArmorTrim armorTrim) {
|
|
+ return new PaperItemArmorTrim.BuilderImpl(armorTrim);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public LodestoneTracker.Builder lodestoneTracker() {
|
|
+ return new PaperLodestoneTracker.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Fireworks.Builder fireworks() {
|
|
+ return new PaperFireworks.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile.Builder resolvableProfile() {
|
|
+ return new PaperResolvableProfile.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile resolvableProfile(final PlayerProfile profile) {
|
|
+ return PaperResolvableProfile.toApi(profile);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BannerPatternLayers.Builder bannerPatternLayers() {
|
|
+ return new PaperBannerPatternLayers.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BlockItemDataProperties.Builder blockItemStateProperties() {
|
|
+ return new PaperBlockItemDataProperties.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapId mapId(final int id) {
|
|
+ return new PaperMapId(new net.minecraft.world.level.saveddata.maps.MapId(id));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public UseRemainder useRemainder(final ItemStack itemStack) {
|
|
+ Preconditions.checkArgument(itemStack != null, "Item cannot be null");
|
|
+ Preconditions.checkArgument(!itemStack.isEmpty(), "Remaining item cannot be empty!");
|
|
+ return new PaperUseRemainder(
|
|
+ new net.minecraft.world.item.component.UseRemainder(CraftItemStack.asNMSCopy(itemStack))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Consumable.Builder consumable() {
|
|
+ return new PaperConsumable.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public UseCooldown.Builder useCooldown(final float seconds) {
|
|
+ Preconditions.checkArgument(seconds > 0, "seconds must be positive, was %s", seconds);
|
|
+ return new PaperUseCooldown.BuilderImpl(seconds);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DamageResistant damageResistant(final TagKey<DamageType> types) {
|
|
+ return new PaperDamageResistant(new net.minecraft.world.item.component.DamageResistant(PaperRegistries.toNms(types)));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Enchantable enchantable(final int level) {
|
|
+ return new PaperEnchantable(new net.minecraft.world.item.enchantment.Enchantable(level));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Repairable repairable(final RegistryKeySet<ItemType> types) {
|
|
+ return new PaperRepairable(new net.minecraft.world.item.enchantment.Repairable(
|
|
+ PaperRegistrySets.convertToNms(Registries.ITEM, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), types)
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Equippable.Builder equippable(EquipmentSlot slot) {
|
|
+ return new PaperEquippable.BuilderImpl(slot);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DeathProtection.Builder deathProtection() {
|
|
+ return new PaperDeathProtection.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CustomModelData.Builder customModelData() {
|
|
+ return new PaperCustomModelData.BuilderImpl();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PaperOminousBottleAmplifier ominousBottleAmplifier(final int amplifier) {
|
|
+ Preconditions.checkArgument(OminousBottleAmplifier.MIN_AMPLIFIER <= amplifier && amplifier <= OminousBottleAmplifier.MAX_AMPLIFIER,
|
|
+ "amplifier must be between %s-%s, was %s", OminousBottleAmplifier.MIN_AMPLIFIER, OminousBottleAmplifier.MAX_AMPLIFIER, amplifier
|
|
+ );
|
|
+ return new PaperOminousBottleAmplifier(
|
|
+ new OminousBottleAmplifier(amplifier)
|
|
+ );
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperBannerPatternLayers.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperBannerPatternLayers.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ca49c2d2e1edcf6c4f7a5ca6c9ba96920aa385f4
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperBannerPatternLayers.java
|
|
@@ -0,0 +1,62 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.registry.RegistryAccess;
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import java.util.List;
|
|
+import java.util.Objects;
|
|
+import java.util.Optional;
|
|
+import org.bukkit.DyeColor;
|
|
+import org.bukkit.block.banner.Pattern;
|
|
+import org.bukkit.block.banner.PatternType;
|
|
+import org.bukkit.craftbukkit.CraftRegistry;
|
|
+import org.bukkit.craftbukkit.block.banner.CraftPatternType;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperBannerPatternLayers(
|
|
+ net.minecraft.world.level.block.entity.BannerPatternLayers impl
|
|
+) implements BannerPatternLayers, Handleable<net.minecraft.world.level.block.entity.BannerPatternLayers> {
|
|
+
|
|
+ private static List<Pattern> convert(final net.minecraft.world.level.block.entity.BannerPatternLayers nmsPatterns) {
|
|
+ return MCUtil.transformUnmodifiable(nmsPatterns.layers(), input -> {
|
|
+ final Optional<PatternType> type = CraftRegistry.unwrapAndConvertHolder(RegistryAccess.registryAccess().getRegistry(RegistryKey.BANNER_PATTERN), input.pattern());
|
|
+ return new Pattern(Objects.requireNonNull(DyeColor.getByWoolData((byte) input.color().getId())), type.orElseThrow(() -> new IllegalStateException("Inlined banner patterns are not supported yet in the API!")));
|
|
+ });
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.level.block.entity.BannerPatternLayers getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Pattern> patterns() {
|
|
+ return convert(impl);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements BannerPatternLayers.Builder {
|
|
+
|
|
+ private final net.minecraft.world.level.block.entity.BannerPatternLayers.Builder builder = new net.minecraft.world.level.block.entity.BannerPatternLayers.Builder();
|
|
+
|
|
+ @Override
|
|
+ public BannerPatternLayers.Builder add(final Pattern pattern) {
|
|
+ this.builder.add(
|
|
+ CraftPatternType.bukkitToMinecraftHolder(pattern.getPattern()),
|
|
+ net.minecraft.world.item.DyeColor.byId(pattern.getColor().getWoolData())
|
|
+ );
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BannerPatternLayers.Builder addAll(final List<Pattern> patterns) {
|
|
+ patterns.forEach(this::add);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BannerPatternLayers build() {
|
|
+ return new PaperBannerPatternLayers(this.builder.build());
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..5757e16c5948a6897bc61005ea7260940a49abfe
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperBlockItemDataProperties.java
|
|
@@ -0,0 +1,50 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
+import java.util.Map;
|
|
+import net.minecraft.world.item.component.BlockItemStateProperties;
|
|
+import net.minecraft.world.level.block.Block;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import org.bukkit.block.BlockType;
|
|
+import org.bukkit.block.data.BlockData;
|
|
+import org.bukkit.craftbukkit.block.CraftBlockType;
|
|
+import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperBlockItemDataProperties(
|
|
+ BlockItemStateProperties impl
|
|
+) implements BlockItemDataProperties, Handleable<BlockItemStateProperties> {
|
|
+
|
|
+ @Override
|
|
+ public BlockData createBlockData(final BlockType blockType) {
|
|
+ final Block block = CraftBlockType.bukkitToMinecraftNew(blockType);
|
|
+ final BlockState defaultState = block.defaultBlockState();
|
|
+ return this.impl.apply(defaultState).createCraftBlockData();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BlockData applyTo(final BlockData blockData) {
|
|
+ final BlockState state = ((CraftBlockData) blockData).getState();
|
|
+ return this.impl.apply(state).createCraftBlockData();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BlockItemStateProperties getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements BlockItemDataProperties.Builder {
|
|
+
|
|
+ private final Map<String, String> properties = new Object2ObjectOpenHashMap<>();
|
|
+
|
|
+ // TODO when BlockProperty API is merged
|
|
+
|
|
+ @Override
|
|
+ public BlockItemDataProperties build() {
|
|
+ if (this.properties.isEmpty()) {
|
|
+ return new PaperBlockItemDataProperties(BlockItemStateProperties.EMPTY);
|
|
+ }
|
|
+ return new PaperBlockItemDataProperties(new BlockItemStateProperties(new Object2ObjectOpenHashMap<>(this.properties)));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ba95ce77dbddb90fd2616c9112fd74051dddc3ee
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperBundleContents.java
|
|
@@ -0,0 +1,51 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+
|
|
+public record PaperBundleContents(
|
|
+ net.minecraft.world.item.component.BundleContents impl
|
|
+) implements BundleContents, Handleable<net.minecraft.world.item.component.BundleContents> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.BundleContents getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<ItemStack> contents() {
|
|
+ return MCUtil.transformUnmodifiable((List<net.minecraft.world.item.ItemStack>) this.impl.items(), CraftItemStack::asBukkitCopy);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements BundleContents.Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.ItemStack> items = new ObjectArrayList<>();
|
|
+
|
|
+ @Override
|
|
+ public BundleContents.Builder add(final ItemStack stack) {
|
|
+ Preconditions.checkArgument(stack != null, "stack cannot be null");
|
|
+ Preconditions.checkArgument(!stack.isEmpty(), "stack cannot be empty");
|
|
+ this.items.add(CraftItemStack.asNMSCopy(stack));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BundleContents.Builder addAll(final List<ItemStack> stacks) {
|
|
+ stacks.forEach(this::add);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public BundleContents build() {
|
|
+ if (this.items.isEmpty()) {
|
|
+ return new PaperBundleContents(net.minecraft.world.item.component.BundleContents.EMPTY);
|
|
+ }
|
|
+ return new PaperBundleContents(new net.minecraft.world.item.component.BundleContents(new ObjectArrayList<>(this.items)));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2129dd67fd02a13f6e6fbdfb07505dc64307a3f0
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperChargedProjectiles.java
|
|
@@ -0,0 +1,51 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+
|
|
+public record PaperChargedProjectiles(
|
|
+ net.minecraft.world.item.component.ChargedProjectiles impl
|
|
+) implements ChargedProjectiles, Handleable<net.minecraft.world.item.component.ChargedProjectiles> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.ChargedProjectiles getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<ItemStack> projectiles() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.getItems() /*makes copies internally*/, CraftItemStack::asCraftMirror);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ChargedProjectiles.Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.ItemStack> items = new ArrayList<>();
|
|
+
|
|
+ @Override
|
|
+ public ChargedProjectiles.Builder add(final ItemStack stack) {
|
|
+ Preconditions.checkArgument(stack != null, "stack cannot be null");
|
|
+ Preconditions.checkArgument(!stack.isEmpty(), "stack cannot be empty");
|
|
+ this.items.add(CraftItemStack.asNMSCopy(stack));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ChargedProjectiles.Builder addAll(final List<ItemStack> stacks) {
|
|
+ stacks.forEach(this::add);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ChargedProjectiles build() {
|
|
+ if (this.items.isEmpty()) {
|
|
+ return new PaperChargedProjectiles(net.minecraft.world.item.component.ChargedProjectiles.EMPTY);
|
|
+ }
|
|
+ return new PaperChargedProjectiles(net.minecraft.world.item.component.ChargedProjectiles.of(this.items));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperConsumable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperConsumable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0bc2bad71d6945ca24f37008effc903a84466004
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperConsumable.java
|
|
@@ -0,0 +1,126 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
|
|
+import io.papermc.paper.datacomponent.item.consumable.ItemUseAnimation;
|
|
+import io.papermc.paper.datacomponent.item.consumable.PaperConsumableEffects;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.minecraft.core.Holder;
|
|
+import net.minecraft.sounds.SoundEvent;
|
|
+import net.minecraft.sounds.SoundEvents;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.checkerframework.checker.index.qual.NonNegative;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperConsumable(
|
|
+ net.minecraft.world.item.component.Consumable impl
|
|
+) implements Consumable, Handleable<net.minecraft.world.item.component.Consumable> {
|
|
+
|
|
+ private static final ItemUseAnimation[] VALUES = ItemUseAnimation.values();
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.Consumable getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @NonNegative float consumeSeconds() {
|
|
+ return this.impl.consumeSeconds();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemUseAnimation animation() {
|
|
+ return VALUES[this.impl.animation().ordinal()];
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Key sound() {
|
|
+ return PaperAdventure.asAdventure(this.impl.sound().value().location());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasConsumeParticles() {
|
|
+ return this.impl.hasConsumeParticles();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<ConsumeEffect> consumeEffects() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.onConsumeEffects(), PaperConsumableEffects::fromNms);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Consumable.Builder toBuilder() {
|
|
+ return new BuilderImpl()
|
|
+ .consumeSeconds(this.consumeSeconds())
|
|
+ .animation(this.animation())
|
|
+ .sound(this.sound())
|
|
+ .addEffects(this.consumeEffects());
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private static final net.minecraft.world.item.ItemUseAnimation[] VALUES = net.minecraft.world.item.ItemUseAnimation.values();
|
|
+
|
|
+ private float consumeSeconds = net.minecraft.world.item.component.Consumable.DEFAULT_CONSUME_SECONDS;
|
|
+ private net.minecraft.world.item.ItemUseAnimation consumeAnimation = net.minecraft.world.item.ItemUseAnimation.EAT;
|
|
+ private Holder<SoundEvent> eatSound = SoundEvents.GENERIC_EAT;
|
|
+ private boolean hasConsumeParticles = true;
|
|
+ private final List<net.minecraft.world.item.consume_effects.ConsumeEffect> effects = new ObjectArrayList<>();
|
|
+
|
|
+ @Override
|
|
+ public Builder consumeSeconds(final @NonNegative float consumeSeconds) {
|
|
+ Preconditions.checkArgument(consumeSeconds >= 0, "consumeSeconds must be non-negative, was %s", consumeSeconds);
|
|
+ this.consumeSeconds = consumeSeconds;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder animation(final ItemUseAnimation animation) {
|
|
+ this.consumeAnimation = VALUES[animation.ordinal()];
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder sound(final Key sound) {
|
|
+ this.eatSound = PaperAdventure.resolveSound(sound);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder hasConsumeParticles(final boolean hasConsumeParticles) {
|
|
+ this.hasConsumeParticles = hasConsumeParticles;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addEffect(final ConsumeEffect effect) {
|
|
+ this.effects.add(PaperConsumableEffects.toNms(effect));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addEffects(final List<ConsumeEffect> effects) {
|
|
+ for (final ConsumeEffect effect : effects) {
|
|
+ this.effects.add(PaperConsumableEffects.toNms(effect));
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Consumable build() {
|
|
+ return new PaperConsumable(
|
|
+ new net.minecraft.world.item.component.Consumable(
|
|
+ this.consumeSeconds,
|
|
+ this.consumeAnimation,
|
|
+ this.eatSound,
|
|
+ this.hasConsumeParticles,
|
|
+ new ObjectArrayList<>(this.effects)
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperCustomModelData.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperCustomModelData.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..33a93c8acf79d02f8a19e66c4f52dfdd4471680e
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperCustomModelData.java
|
|
@@ -0,0 +1,121 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
|
+import it.unimi.dsi.fastutil.booleans.BooleanList;
|
|
+import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
|
+import it.unimi.dsi.fastutil.floats.FloatList;
|
|
+import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
+import it.unimi.dsi.fastutil.ints.IntList;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.Collections;
|
|
+import java.util.List;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import org.bukkit.Color;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperCustomModelData(
|
|
+ net.minecraft.world.item.component.CustomModelData impl
|
|
+) implements CustomModelData, Handleable<net.minecraft.world.item.component.CustomModelData> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.CustomModelData getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<Float> floats() {
|
|
+ return Collections.unmodifiableList(this.impl.floats());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<Boolean> flags() {
|
|
+ return Collections.unmodifiableList(this.impl.flags());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<String> strings() {
|
|
+ return Collections.unmodifiableList(this.impl.strings());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<Color> colors() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.colors(), Color::fromRGB);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements CustomModelData.Builder {
|
|
+
|
|
+ private final FloatList floats = new FloatArrayList();
|
|
+ private final BooleanList flags = new BooleanArrayList();
|
|
+ private final List<String> strings = new ObjectArrayList<>();
|
|
+ private final IntList colors = new IntArrayList();
|
|
+
|
|
+ @Override
|
|
+ public Builder addFloat(final float f) {
|
|
+ this.floats.add(f);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addFloats(final List<Float> floats) {
|
|
+ for (Float f : floats) {
|
|
+ Preconditions.checkArgument(f != null, "Float cannot be null");
|
|
+ }
|
|
+ this.floats.addAll(floats);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addFlag(final boolean flag) {
|
|
+ this.flags.add(flag);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addFlags(final List<Boolean> flags) {
|
|
+ for (Boolean flag : flags) {
|
|
+ Preconditions.checkArgument(flag != null, "Flag cannot be null");
|
|
+ }
|
|
+ this.flags.addAll(flags);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addString(final String string) {
|
|
+ Preconditions.checkArgument(string != null, "String cannot be null");
|
|
+ this.strings.add(string);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addStrings(final List<String> strings) {
|
|
+ strings.forEach(this::addString);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addColor(final Color color) {
|
|
+ Preconditions.checkArgument(color != null, "Color cannot be null");
|
|
+ this.colors.add(color.asRGB());
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addColors(final List<Color> colors) {
|
|
+ colors.forEach(this::addColor);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CustomModelData build() {
|
|
+ return new PaperCustomModelData(
|
|
+ new net.minecraft.world.item.component.CustomModelData(
|
|
+ new FloatArrayList(this.floats),
|
|
+ new BooleanArrayList(this.flags),
|
|
+ new ObjectArrayList<>(this.strings),
|
|
+ new IntArrayList(this.colors)
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..adc986c8b3d65e3fb91a8951048194bbe4052b74
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperDamageResistant.java
|
|
@@ -0,0 +1,21 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.registry.PaperRegistries;
|
|
+import io.papermc.paper.registry.tag.TagKey;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.damage.DamageType;
|
|
+
|
|
+public record PaperDamageResistant(
|
|
+ net.minecraft.world.item.component.DamageResistant impl
|
|
+) implements DamageResistant, Handleable<net.minecraft.world.item.component.DamageResistant> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.DamageResistant getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public TagKey<DamageType> types() {
|
|
+ return PaperRegistries.fromNms(this.impl.types());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperDeathProtection.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperDeathProtection.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..798e45d3b3e895f8b3abb9db1c9d58348bcd22d3
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperDeathProtection.java
|
|
@@ -0,0 +1,50 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.datacomponent.item.consumable.ConsumeEffect;
|
|
+import io.papermc.paper.datacomponent.item.consumable.PaperConsumableEffects;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperDeathProtection(
|
|
+ net.minecraft.world.item.component.DeathProtection impl
|
|
+) implements DeathProtection, Handleable<net.minecraft.world.item.component.DeathProtection> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.DeathProtection getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<ConsumeEffect> deathEffects() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.deathEffects(), PaperConsumableEffects::fromNms);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.consume_effects.ConsumeEffect> effects = new ArrayList<>();
|
|
+
|
|
+ @Override
|
|
+ public Builder addEffect(final ConsumeEffect effect) {
|
|
+ this.effects.add(PaperConsumableEffects.toNms(effect));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addEffects(final List<ConsumeEffect> effects) {
|
|
+ for (final ConsumeEffect effect : effects) {
|
|
+ this.effects.add(PaperConsumableEffects.toNms(effect));
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DeathProtection build() {
|
|
+ return new PaperDeathProtection(
|
|
+ new net.minecraft.world.item.component.DeathProtection(this.effects)
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperDyedItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperDyedItemColor.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2407d79e2e77e8be6de8e65769efc4d79e3be9db
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperDyedItemColor.java
|
|
@@ -0,0 +1,52 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.Color;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperDyedItemColor(
|
|
+ net.minecraft.world.item.component.DyedItemColor impl
|
|
+) implements DyedItemColor, Handleable<net.minecraft.world.item.component.DyedItemColor> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.DyedItemColor getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Color color() {
|
|
+ return Color.fromRGB(this.impl.rgb() & 0x00FFFFFF); // skip alpha channel
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DyedItemColor showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperDyedItemColor(this.impl.withTooltip(showInTooltip));
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements DyedItemColor.Builder {
|
|
+
|
|
+ private Color color = Color.WHITE;
|
|
+ private boolean showInToolTip = true;
|
|
+
|
|
+ @Override
|
|
+ public DyedItemColor.Builder color(final Color color) {
|
|
+ this.color = color;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DyedItemColor.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInToolTip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public DyedItemColor build() {
|
|
+ return new PaperDyedItemColor(new net.minecraft.world.item.component.DyedItemColor(this.color.asRGB(), this.showInToolTip));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperEnchantable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperEnchantable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..422e1a4d606481f0dc68843fbbc8126ccfda1cc3
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperEnchantable.java
|
|
@@ -0,0 +1,18 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperEnchantable(
|
|
+ net.minecraft.world.item.enchantment.Enchantable impl
|
|
+) implements Enchantable, Handleable<net.minecraft.world.item.enchantment.Enchantable> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.enchantment.Enchantable getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int value() {
|
|
+ return this.impl.value();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..6d427d2c62cce557fa824dd347fd5d0c2ae7411e
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperEquippable.java
|
|
@@ -0,0 +1,174 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.registry.PaperRegistries;
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.registry.set.RegistryKeySet;
|
|
+import java.util.Optional;
|
|
+import java.util.function.Function;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.minecraft.core.Holder;
|
|
+import net.minecraft.core.HolderSet;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.resources.ResourceKey;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import net.minecraft.sounds.SoundEvent;
|
|
+import net.minecraft.sounds.SoundEvents;
|
|
+import net.minecraft.util.datafix.fixes.EquippableAssetRenameFix;
|
|
+import net.minecraft.world.item.equipment.EquipmentAsset;
|
|
+import net.minecraft.world.item.equipment.EquipmentAssets;
|
|
+import org.bukkit.craftbukkit.CraftEquipmentSlot;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.entity.EntityType;
|
|
+import org.bukkit.inventory.EquipmentSlot;
|
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
|
+
|
|
+public record PaperEquippable(
|
|
+ net.minecraft.world.item.equipment.Equippable impl
|
|
+) implements Equippable, Handleable<net.minecraft.world.item.equipment.Equippable> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.equipment.Equippable getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public EquipmentSlot slot() {
|
|
+ return CraftEquipmentSlot.getSlot(this.impl.slot());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Key equipSound() {
|
|
+ return PaperAdventure.asAdventure(this.impl.equipSound().value().location());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Key assetId() {
|
|
+ return this.impl.assetId()
|
|
+ .map(PaperAdventure::asAdventureKey)
|
|
+ .orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Key cameraOverlay() {
|
|
+ return this.impl.cameraOverlay()
|
|
+ .map(PaperAdventure::asAdventure)
|
|
+ .orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable RegistryKeySet<EntityType> allowedEntities() {
|
|
+ return this.impl.allowedEntities()
|
|
+ .map((set) -> PaperRegistrySets.convertToApi(RegistryKey.ENTITY_TYPE, set))
|
|
+ .orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean dispensable() {
|
|
+ return this.impl.dispensable();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean swappable() {
|
|
+ return this.impl.swappable();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean damageOnHurt() {
|
|
+ return this.impl.damageOnHurt();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder toBuilder() {
|
|
+ return new BuilderImpl(this.slot())
|
|
+ .equipSound(this.equipSound())
|
|
+ .assetId(this.assetId())
|
|
+ .cameraOverlay(this.cameraOverlay())
|
|
+ .allowedEntities(this.allowedEntities())
|
|
+ .dispensable(this.dispensable())
|
|
+ .swappable(this.swappable())
|
|
+ .damageOnHurt(this.damageOnHurt());
|
|
+ }
|
|
+
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private final net.minecraft.world.entity.EquipmentSlot equipmentSlot;
|
|
+ private Holder<SoundEvent> equipSound = SoundEvents.ARMOR_EQUIP_GENERIC;
|
|
+ private Optional<ResourceKey<EquipmentAsset>> assetId = Optional.empty();
|
|
+ private Optional<ResourceLocation> cameraOverlay = Optional.empty();
|
|
+ private Optional<HolderSet<net.minecraft.world.entity.EntityType<?>>> allowedEntities = Optional.empty();
|
|
+ private boolean dispensable = true;
|
|
+ private boolean swappable = true;
|
|
+ private boolean damageOnHurt = true;
|
|
+
|
|
+ BuilderImpl(final EquipmentSlot equipmentSlot) {
|
|
+ this.equipmentSlot = CraftEquipmentSlot.getNMS(equipmentSlot);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder equipSound(final Key sound) {
|
|
+ this.equipSound = PaperAdventure.resolveSound(sound);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder assetId(final @Nullable Key assetId) {
|
|
+ this.assetId = Optional.ofNullable(assetId)
|
|
+ .map(key -> PaperAdventure.asVanilla(EquipmentAssets.ROOT_ID, key));
|
|
+
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder cameraOverlay(@Nullable final Key cameraOverlay) {
|
|
+ this.cameraOverlay = Optional.ofNullable(cameraOverlay)
|
|
+ .map(PaperAdventure::asVanilla);
|
|
+
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder allowedEntities(final @Nullable RegistryKeySet<EntityType> allowedEntities) {
|
|
+ this.allowedEntities = Optional.ofNullable(allowedEntities)
|
|
+ .map((set) -> PaperRegistrySets.convertToNms(Registries.ENTITY_TYPE, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), set));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder dispensable(final boolean dispensable) {
|
|
+ this.dispensable = dispensable;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder swappable(final boolean swappable) {
|
|
+ this.swappable = swappable;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder damageOnHurt(final boolean damageOnHurt) {
|
|
+ this.damageOnHurt = damageOnHurt;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Equippable build() {
|
|
+ return new PaperEquippable(
|
|
+ new net.minecraft.world.item.equipment.Equippable(
|
|
+ this.equipmentSlot,
|
|
+ this.equipSound,
|
|
+ this.assetId,
|
|
+ this.cameraOverlay,
|
|
+ this.allowedEntities,
|
|
+ this.dispensable,
|
|
+ this.swappable,
|
|
+ this.damageOnHurt
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperFireworks.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperFireworks.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..80189eb5054a044a76f19200eb0e5f316c30de92
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperFireworks.java
|
|
@@ -0,0 +1,73 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import net.minecraft.world.item.component.FireworkExplosion;
|
|
+import org.bukkit.FireworkEffect;
|
|
+import org.bukkit.craftbukkit.inventory.CraftMetaFirework;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperFireworks(
|
|
+ net.minecraft.world.item.component.Fireworks impl
|
|
+) implements Fireworks, Handleable<net.minecraft.world.item.component.Fireworks> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.Fireworks getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<FireworkEffect> effects() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.explosions(), CraftMetaFirework::getEffect);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int flightDuration() {
|
|
+ return this.impl.flightDuration();
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Fireworks.Builder {
|
|
+
|
|
+ private final List<FireworkExplosion> effects = new ObjectArrayList<>();
|
|
+ private int duration = 0; // default set from nms Fireworks component
|
|
+
|
|
+ @Override
|
|
+ public Fireworks.Builder flightDuration(final int duration) {
|
|
+ Preconditions.checkArgument(duration >= 0 && duration <= 0xFF, "duration must be an unsigned byte ([%s, %s]), was %s", 0, 0xFF, duration);
|
|
+ this.duration = duration;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Fireworks.Builder addEffect(final FireworkEffect effect) {
|
|
+ Preconditions.checkArgument(
|
|
+ this.effects.size() + 1 <= net.minecraft.world.item.component.Fireworks.MAX_EXPLOSIONS,
|
|
+ "Cannot have more than %s effects, had %s",
|
|
+ net.minecraft.world.item.component.Fireworks.MAX_EXPLOSIONS,
|
|
+ this.effects.size() + 1
|
|
+ );
|
|
+ this.effects.add(CraftMetaFirework.getExplosion(effect));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Fireworks.Builder addEffects(final List<FireworkEffect> effects) {
|
|
+ Preconditions.checkArgument(
|
|
+ this.effects.size() + effects.size() <= net.minecraft.world.item.component.Fireworks.MAX_EXPLOSIONS,
|
|
+ "Cannot have more than %s effects, had %s",
|
|
+ net.minecraft.world.item.component.Fireworks.MAX_EXPLOSIONS,
|
|
+ this.effects.size() + effects.size()
|
|
+ );
|
|
+ MCUtil.addAndConvert(this.effects, effects, CraftMetaFirework::getExplosion);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Fireworks build() {
|
|
+ return new PaperFireworks(new net.minecraft.world.item.component.Fireworks(this.duration, new ObjectArrayList<>(this.effects)));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperFoodProperties.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperFoodProperties.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2a043bb9001048f66d3a6aa8cb896b35bd2df606
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperFoodProperties.java
|
|
@@ -0,0 +1,72 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperFoodProperties(
|
|
+ net.minecraft.world.food.FoodProperties impl
|
|
+) implements FoodProperties, Handleable<net.minecraft.world.food.FoodProperties> {
|
|
+
|
|
+ @Override
|
|
+ public int nutrition() {
|
|
+ return this.impl.nutrition();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float saturation() {
|
|
+ return this.impl.saturation();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean canAlwaysEat() {
|
|
+ return this.impl.canAlwaysEat();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public FoodProperties.Builder toBuilder() {
|
|
+ return new BuilderImpl()
|
|
+ .nutrition(this.nutrition())
|
|
+ .saturation(this.saturation())
|
|
+ .canAlwaysEat(this.canAlwaysEat());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.food.FoodProperties getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements FoodProperties.Builder {
|
|
+
|
|
+ private boolean canAlwaysEat = false;
|
|
+ private float saturation = 0;
|
|
+ private int nutrition = 0;
|
|
+
|
|
+ @Override
|
|
+ public FoodProperties.Builder canAlwaysEat(final boolean canAlwaysEat) {
|
|
+ this.canAlwaysEat = canAlwaysEat;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public FoodProperties.Builder saturation(final float saturation) {
|
|
+ this.saturation = saturation;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public FoodProperties.Builder nutrition(final int nutrition) {
|
|
+ Preconditions.checkArgument(nutrition >= 0, "nutrition must be non-negative, was %s", nutrition);
|
|
+ this.nutrition = nutrition;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public FoodProperties build() {
|
|
+ return new PaperFoodProperties(new net.minecraft.world.food.FoodProperties(
|
|
+ this.nutrition,
|
|
+ this.saturation,
|
|
+ this.canAlwaysEat
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..e6315cd0ebd46f874284c32da9cc03eb77f0677f
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAdventurePredicate.java
|
|
@@ -0,0 +1,75 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.block.BlockPredicate;
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import java.util.Optional;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperItemAdventurePredicate(
|
|
+ net.minecraft.world.item.AdventureModePredicate impl
|
|
+) implements ItemAdventurePredicate, Handleable<net.minecraft.world.item.AdventureModePredicate> {
|
|
+
|
|
+ private static List<BlockPredicate> convert(final net.minecraft.world.item.AdventureModePredicate nmsModifiers) {
|
|
+ return MCUtil.transformUnmodifiable(nmsModifiers.predicates, nms -> BlockPredicate.predicate()
|
|
+ .blocks(nms.blocks().map(blocks -> PaperRegistrySets.convertToApi(RegistryKey.BLOCK, blocks)).orElse(null)).build());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.AdventureModePredicate getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PaperItemAdventurePredicate showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperItemAdventurePredicate(this.impl.withTooltip(showInTooltip));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<BlockPredicate> predicates() {
|
|
+ return convert(this.impl);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ItemAdventurePredicate.Builder {
|
|
+
|
|
+ private final List<net.minecraft.advancements.critereon.BlockPredicate> predicates = new ObjectArrayList<>();
|
|
+ private boolean showInTooltip = true;
|
|
+
|
|
+ @Override
|
|
+ public ItemAdventurePredicate.Builder addPredicate(final BlockPredicate predicate) {
|
|
+ this.predicates.add(new net.minecraft.advancements.critereon.BlockPredicate(Optional.ofNullable(predicate.blocks()).map(
|
|
+ blocks -> PaperRegistrySets.convertToNms(Registries.BLOCK, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), blocks)
|
|
+ ), Optional.empty(), Optional.empty()));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addPredicates(final List<BlockPredicate> predicates) {
|
|
+ for (final BlockPredicate predicate : predicates) {
|
|
+ this.addPredicate(predicate);
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAdventurePredicate.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInTooltip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAdventurePredicate build() {
|
|
+ return new PaperItemAdventurePredicate(new net.minecraft.world.item.AdventureModePredicate(new ObjectArrayList<>(this.predicates), this.showInTooltip));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemArmorTrim.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemArmorTrim.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..5d060c907f4b1bc2bae063ca1e3baf35140215b6
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemArmorTrim.java
|
|
@@ -0,0 +1,62 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial;
|
|
+import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.meta.trim.ArmorTrim;
|
|
+
|
|
+public record PaperItemArmorTrim(
|
|
+ net.minecraft.world.item.equipment.trim.ArmorTrim impl
|
|
+) implements ItemArmorTrim, Handleable<net.minecraft.world.item.equipment.trim.ArmorTrim> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.equipment.trim.ArmorTrim getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemArmorTrim showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperItemArmorTrim(this.impl.withTooltip(showInTooltip));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ArmorTrim armorTrim() {
|
|
+ return new ArmorTrim(CraftTrimMaterial.minecraftHolderToBukkit(this.impl.material()), CraftTrimPattern.minecraftHolderToBukkit(this.impl.pattern()));
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ItemArmorTrim.Builder {
|
|
+
|
|
+ private ArmorTrim armorTrim;
|
|
+ private boolean showInTooltip = true;
|
|
+
|
|
+ BuilderImpl(final ArmorTrim armorTrim) {
|
|
+ this.armorTrim = armorTrim;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemArmorTrim.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInTooltip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemArmorTrim.Builder armorTrim(final ArmorTrim armorTrim) {
|
|
+ this.armorTrim = armorTrim;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemArmorTrim build() {
|
|
+ return new PaperItemArmorTrim(new net.minecraft.world.item.equipment.trim.ArmorTrim(
|
|
+ CraftTrimMaterial.bukkitToMinecraftHolder(this.armorTrim.getMaterial()),
|
|
+ CraftTrimPattern.bukkitToMinecraftHolder(this.armorTrim.getPattern()),
|
|
+ this.showInTooltip
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAttributeModifiers.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAttributeModifiers.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..47ca2b8eb1c1483b6049cf18c7d8a40dd20e7cab
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemAttributeModifiers.java
|
|
@@ -0,0 +1,97 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import org.bukkit.attribute.Attribute;
|
|
+import org.bukkit.attribute.AttributeModifier;
|
|
+import org.bukkit.craftbukkit.CraftEquipmentSlot;
|
|
+import org.bukkit.craftbukkit.attribute.CraftAttribute;
|
|
+import org.bukkit.craftbukkit.attribute.CraftAttributeInstance;
|
|
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.EquipmentSlotGroup;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperItemAttributeModifiers(
|
|
+ net.minecraft.world.item.component.ItemAttributeModifiers impl
|
|
+) implements ItemAttributeModifiers, Handleable<net.minecraft.world.item.component.ItemAttributeModifiers> {
|
|
+
|
|
+ private static List<Entry> convert(final net.minecraft.world.item.component.ItemAttributeModifiers nmsModifiers) {
|
|
+ return MCUtil.transformUnmodifiable(nmsModifiers.modifiers(), nms -> new PaperEntry(
|
|
+ CraftAttribute.minecraftHolderToBukkit(nms.attribute()),
|
|
+ CraftAttributeInstance.convert(nms.modifier(), nms.slot())
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.ItemAttributeModifiers getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAttributeModifiers showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperItemAttributeModifiers(this.impl.withTooltip(showInTooltip));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Entry> modifiers() {
|
|
+ return convert(this.impl);
|
|
+ }
|
|
+
|
|
+ public record PaperEntry(Attribute attribute, AttributeModifier modifier) implements ItemAttributeModifiers.Entry {
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ItemAttributeModifiers.Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.component.ItemAttributeModifiers.Entry> entries = new ObjectArrayList<>();
|
|
+ private boolean showInTooltip = net.minecraft.world.item.component.ItemAttributeModifiers.EMPTY.showInTooltip();
|
|
+
|
|
+ @Override
|
|
+ public Builder addModifier(final Attribute attribute, final AttributeModifier modifier) {
|
|
+ return this.addModifier(attribute, modifier, modifier.getSlotGroup());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAttributeModifiers.Builder addModifier(final Attribute attribute, final AttributeModifier modifier, final EquipmentSlotGroup equipmentSlotGroup) {
|
|
+ Preconditions.checkArgument(
|
|
+ this.entries.stream().noneMatch(e ->
|
|
+ e.modifier().id().equals(CraftNamespacedKey.toMinecraft(modifier.getKey())) && e.attribute().is(CraftNamespacedKey.toMinecraft(attribute.getKey()))
|
|
+ ),
|
|
+ "Cannot add 2 modifiers with identical keys on the same attribute (modifier %s for attribute %s)",
|
|
+ modifier.getKey(), attribute.getKey()
|
|
+ );
|
|
+
|
|
+ this.entries.add(new net.minecraft.world.item.component.ItemAttributeModifiers.Entry(
|
|
+ CraftAttribute.bukkitToMinecraftHolder(attribute),
|
|
+ CraftAttributeInstance.convert(modifier),
|
|
+ CraftEquipmentSlot.getNMSGroup(equipmentSlotGroup)
|
|
+ ));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAttributeModifiers.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInTooltip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemAttributeModifiers build() {
|
|
+ if (this.entries.isEmpty()) {
|
|
+ return new PaperItemAttributeModifiers(net.minecraft.world.item.component.ItemAttributeModifiers.EMPTY.withTooltip(this.showInTooltip));
|
|
+ }
|
|
+
|
|
+ return new PaperItemAttributeModifiers(new net.minecraft.world.item.component.ItemAttributeModifiers(
|
|
+ new ObjectArrayList<>(this.entries),
|
|
+ this.showInTooltip
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2c4ecc2d5fc925f245c691facde9c96f3b5eef85
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemContainerContents.java
|
|
@@ -0,0 +1,65 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+
|
|
+public record PaperItemContainerContents(
|
|
+ net.minecraft.world.item.component.ItemContainerContents impl
|
|
+) implements ItemContainerContents, Handleable<net.minecraft.world.item.component.ItemContainerContents> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.ItemContainerContents getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public List<ItemStack> contents() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.items, CraftItemStack::asBukkitCopy);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ItemContainerContents.Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.ItemStack> items = new ObjectArrayList<>();
|
|
+
|
|
+ @Override
|
|
+ public ItemContainerContents.Builder add(final ItemStack stack) {
|
|
+ Preconditions.checkArgument(stack != null, "Item cannot be null");
|
|
+ Preconditions.checkArgument(
|
|
+ this.items.size() + 1 <= net.minecraft.world.item.component.ItemContainerContents.MAX_SIZE,
|
|
+ "Cannot have more than %s items, had %s",
|
|
+ net.minecraft.world.item.component.ItemContainerContents.MAX_SIZE,
|
|
+ this.items.size() + 1
|
|
+ );
|
|
+ this.items.add(CraftItemStack.asNMSCopy(stack));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemContainerContents.Builder addAll(final List<ItemStack> stacks) {
|
|
+ Preconditions.checkArgument(
|
|
+ this.items.size() + stacks.size() <= net.minecraft.world.item.component.ItemContainerContents.MAX_SIZE,
|
|
+ "Cannot have more than %s items, had %s",
|
|
+ net.minecraft.world.item.component.ItemContainerContents.MAX_SIZE,
|
|
+ this.items.size() + stacks.size()
|
|
+ );
|
|
+ MCUtil.addAndConvert(this.items, stacks, stack -> {
|
|
+ Preconditions.checkArgument(stack != null, "Cannot pass null itemstacks!");
|
|
+ return CraftItemStack.asNMSCopy(stack);
|
|
+ });
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemContainerContents build() {
|
|
+ if (this.items.isEmpty()) {
|
|
+ return new PaperItemContainerContents(net.minecraft.world.item.component.ItemContainerContents.EMPTY);
|
|
+ }
|
|
+ return new PaperItemContainerContents(net.minecraft.world.item.component.ItemContainerContents.fromItems(this.items));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemEnchantments.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemEnchantments.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3cfb18f6a4868ff32e2b118c5833b1b9864e967c
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemEnchantments.java
|
|
@@ -0,0 +1,92 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
+import java.util.Collections;
|
|
+import java.util.HashMap;
|
|
+import java.util.Map;
|
|
+import net.minecraft.core.Holder;
|
|
+import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.enchantments.Enchantment;
|
|
+
|
|
+public record PaperItemEnchantments(
|
|
+ net.minecraft.world.item.enchantment.ItemEnchantments impl,
|
|
+ Map<Enchantment, Integer> enchantments // API values are stored externally as the concept of a lazy key transformer map does not make much sense
|
|
+) implements ItemEnchantments, Handleable<net.minecraft.world.item.enchantment.ItemEnchantments> {
|
|
+
|
|
+ public PaperItemEnchantments(final net.minecraft.world.item.enchantment.ItemEnchantments itemEnchantments) {
|
|
+ this(itemEnchantments, convert(itemEnchantments));
|
|
+ }
|
|
+
|
|
+ private static Map<Enchantment, Integer> convert(final net.minecraft.world.item.enchantment.ItemEnchantments itemEnchantments) {
|
|
+ if (itemEnchantments.isEmpty()) {
|
|
+ return Collections.emptyMap();
|
|
+ }
|
|
+ final Map<Enchantment, Integer> map = new HashMap<>(itemEnchantments.size());
|
|
+ for (final Object2IntMap.Entry<Holder<net.minecraft.world.item.enchantment.Enchantment>> entry : itemEnchantments.entrySet()) {
|
|
+ map.put(CraftEnchantment.minecraftHolderToBukkit(entry.getKey()), entry.getIntValue());
|
|
+ }
|
|
+ return Collections.unmodifiableMap(map); // TODO look into making a "transforming" map maybe?
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemEnchantments showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperItemEnchantments(this.impl.withTooltip(showInTooltip), this.enchantments);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.enchantment.ItemEnchantments getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ItemEnchantments.Builder {
|
|
+
|
|
+ private final Map<Enchantment, Integer> enchantments = new Object2ObjectOpenHashMap<>();
|
|
+ private boolean showInTooltip = true;
|
|
+
|
|
+ @Override
|
|
+ public ItemEnchantments.Builder add(final Enchantment enchantment, final int level) {
|
|
+ Preconditions.checkArgument(
|
|
+ level >= 1 && level <= net.minecraft.world.item.enchantment.Enchantment.MAX_LEVEL,
|
|
+ "level must be between %s and %s, was %s",
|
|
+ 1, net.minecraft.world.item.enchantment.Enchantment.MAX_LEVEL,
|
|
+ level
|
|
+ );
|
|
+ this.enchantments.put(enchantment, level);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemEnchantments.Builder addAll(final Map<Enchantment, Integer> enchantments) {
|
|
+ enchantments.forEach(this::add);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemEnchantments.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInTooltip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemEnchantments build() {
|
|
+ final net.minecraft.world.item.enchantment.ItemEnchantments initialEnchantments = net.minecraft.world.item.enchantment.ItemEnchantments.EMPTY.withTooltip(this.showInTooltip);
|
|
+ if (this.enchantments.isEmpty()) {
|
|
+ return new PaperItemEnchantments(initialEnchantments);
|
|
+ }
|
|
+
|
|
+ final net.minecraft.world.item.enchantment.ItemEnchantments.Mutable mutable = new net.minecraft.world.item.enchantment.ItemEnchantments.Mutable(initialEnchantments);
|
|
+ this.enchantments.forEach((enchantment, level) ->
|
|
+ mutable.set(CraftEnchantment.bukkitToMinecraftHolder(enchantment), level)
|
|
+ );
|
|
+ return new PaperItemEnchantments(mutable.toImmutable());
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemLore.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemLore.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3bb0c1aebb03c8dfd6a76ab60c26cbb104586975
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemLore.java
|
|
@@ -0,0 +1,77 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.ComponentLike;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperItemLore(
|
|
+ net.minecraft.world.item.component.ItemLore impl
|
|
+) implements ItemLore, Handleable<net.minecraft.world.item.component.ItemLore> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.ItemLore getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Component> lines() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.lines(), PaperAdventure::asAdventure);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Component> styledLines() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.styledLines(), PaperAdventure::asAdventure);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ItemLore.Builder {
|
|
+
|
|
+ private List<Component> lines = new ObjectArrayList<>();
|
|
+
|
|
+ private static void validateLineCount(final int current, final int add) {
|
|
+ final int newSize = current + add;
|
|
+ Preconditions.checkArgument(
|
|
+ newSize <= net.minecraft.world.item.component.ItemLore.MAX_LINES,
|
|
+ "Cannot have more than %s lines, had %s",
|
|
+ net.minecraft.world.item.component.ItemLore.MAX_LINES,
|
|
+ newSize
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemLore.Builder lines(final List<? extends ComponentLike> lines) {
|
|
+ validateLineCount(0, lines.size());
|
|
+ this.lines = new ArrayList<>(ComponentLike.asComponents(lines));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemLore.Builder addLine(final ComponentLike line) {
|
|
+ validateLineCount(this.lines.size(), 1);
|
|
+ this.lines.add(line.asComponent());
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemLore.Builder addLines(final List<? extends ComponentLike> lines) {
|
|
+ validateLineCount(this.lines.size(), lines.size());
|
|
+ this.lines.addAll(ComponentLike.asComponents(lines));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemLore build() {
|
|
+ if (this.lines.isEmpty()) {
|
|
+ return new PaperItemLore(net.minecraft.world.item.component.ItemLore.EMPTY);
|
|
+ }
|
|
+
|
|
+ return new PaperItemLore(new net.minecraft.world.item.component.ItemLore(PaperAdventure.asVanilla(this.lines))); // asVanilla does a list clone
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..538a61eaa02c029b4d92f938e0ffde8aa6cf027c
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperItemTool.java
|
|
@@ -0,0 +1,100 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.registry.set.RegistryKeySet;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.Collection;
|
|
+import java.util.List;
|
|
+import java.util.Optional;
|
|
+import net.kyori.adventure.util.TriState;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import org.bukkit.block.BlockType;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperItemTool(
|
|
+ net.minecraft.world.item.component.Tool impl
|
|
+) implements Tool, Handleable<net.minecraft.world.item.component.Tool> {
|
|
+
|
|
+ private static List<Tool.Rule> convert(final List<net.minecraft.world.item.component.Tool.Rule> tool) {
|
|
+ return MCUtil.transformUnmodifiable(tool, nms -> new PaperRule(
|
|
+ PaperRegistrySets.convertToApi(RegistryKey.BLOCK, nms.blocks()),
|
|
+ nms.speed().orElse(null),
|
|
+ TriState.byBoolean(nms.correctForDrops().orElse(null))
|
|
+ ));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.Tool getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Rule> rules() {
|
|
+ return convert(this.impl.rules());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float defaultMiningSpeed() {
|
|
+ return this.impl.defaultMiningSpeed();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int damagePerBlock() {
|
|
+ return this.impl.damagePerBlock();
|
|
+ }
|
|
+
|
|
+ record PaperRule(RegistryKeySet<BlockType> blocks, @Nullable Float speed, TriState correctForDrops) implements Rule {
|
|
+
|
|
+ public static PaperRule fromUnsafe(final RegistryKeySet<BlockType> blocks, final @Nullable Float speed, final TriState correctForDrops) {
|
|
+ Preconditions.checkArgument(speed == null || speed > 0, "speed must be positive");
|
|
+ return new PaperRule(blocks, speed, correctForDrops);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.component.Tool.Rule> rules = new ObjectArrayList<>();
|
|
+ private int damage = 1;
|
|
+ private float miningSpeed = 1.0F;
|
|
+
|
|
+ @Override
|
|
+ public Builder damagePerBlock(final int damage) {
|
|
+ Preconditions.checkArgument(damage >= 0, "damage must be non-negative, was %s", damage);
|
|
+ this.damage = damage;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder defaultMiningSpeed(final float miningSpeed) {
|
|
+ this.miningSpeed = miningSpeed;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addRule(final Rule rule) {
|
|
+ this.rules.add(new net.minecraft.world.item.component.Tool.Rule(
|
|
+ PaperRegistrySets.convertToNms(Registries.BLOCK, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), rule.blocks()),
|
|
+ Optional.ofNullable(rule.speed()),
|
|
+ Optional.ofNullable(rule.correctForDrops().toBoolean())
|
|
+ ));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addRules(final Collection<Rule> rules) {
|
|
+ rules.forEach(this::addRule);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Tool build() {
|
|
+ return new PaperItemTool(new net.minecraft.world.item.component.Tool(new ObjectArrayList<>(this.rules), this.miningSpeed, this.damage));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c43ccf7ccc6157389fce9f9746d5297f0eab1b6e
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperJukeboxPlayable.java
|
|
@@ -0,0 +1,62 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import net.minecraft.world.item.EitherHolder;
|
|
+import org.bukkit.JukeboxSong;
|
|
+import org.bukkit.craftbukkit.CraftJukeboxSong;
|
|
+import org.bukkit.craftbukkit.CraftRegistry;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperJukeboxPlayable(
|
|
+ net.minecraft.world.item.JukeboxPlayable impl
|
|
+) implements JukeboxPlayable, Handleable<net.minecraft.world.item.JukeboxPlayable> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.JukeboxPlayable getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PaperJukeboxPlayable showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperJukeboxPlayable(this.impl.withTooltip(showInTooltip));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public JukeboxSong jukeboxSong() {
|
|
+ return this.impl.song()
|
|
+ .unwrap(CraftRegistry.getMinecraftRegistry())
|
|
+ .map(CraftJukeboxSong::minecraftHolderToBukkit)
|
|
+ .orElseThrow();
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements JukeboxPlayable.Builder {
|
|
+
|
|
+ private JukeboxSong song;
|
|
+ private boolean showInTooltip = true;
|
|
+
|
|
+ BuilderImpl(final JukeboxSong song) {
|
|
+ this.song = song;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public JukeboxPlayable.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInTooltip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public JukeboxPlayable.Builder jukeboxSong(final JukeboxSong song) {
|
|
+ this.song = song;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public JukeboxPlayable build() {
|
|
+ return new PaperJukeboxPlayable(new net.minecraft.world.item.JukeboxPlayable(new EitherHolder<>(CraftJukeboxSong.bukkitToMinecraftHolder(this.song)), this.showInTooltip));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperLodestoneTracker.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperLodestoneTracker.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..5b97249f6ae90bc1a10c2089e39f064068d7cd2c
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperLodestoneTracker.java
|
|
@@ -0,0 +1,53 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import java.util.Optional;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.craftbukkit.util.CraftLocation;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jspecify.annotations.Nullable;
|
|
+
|
|
+public record PaperLodestoneTracker(
|
|
+ net.minecraft.world.item.component.LodestoneTracker impl
|
|
+) implements LodestoneTracker, Handleable<net.minecraft.world.item.component.LodestoneTracker> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.LodestoneTracker getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Location location() {
|
|
+ return this.impl.target().map(CraftLocation::fromGlobalPos).orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean tracked() {
|
|
+ return this.impl.tracked();
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements LodestoneTracker.Builder {
|
|
+
|
|
+ private @Nullable Location location;
|
|
+ private boolean tracked = true;
|
|
+
|
|
+ @Override
|
|
+ public LodestoneTracker.Builder location(final @Nullable Location location) {
|
|
+ this.location = location;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public LodestoneTracker.Builder tracked(final boolean tracked) {
|
|
+ this.tracked = tracked;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public LodestoneTracker build() {
|
|
+ return new PaperLodestoneTracker(new net.minecraft.world.item.component.LodestoneTracker(
|
|
+ Optional.ofNullable(this.location).map(CraftLocation::toGlobalPos),
|
|
+ this.tracked
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperMapDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperMapDecorations.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..322a1285b0c5127abb67ccab478f1b16b44d0be4
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperMapDecorations.java
|
|
@@ -0,0 +1,97 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
+import java.util.Collections;
|
|
+import java.util.Map;
|
|
+import java.util.Set;
|
|
+import org.bukkit.craftbukkit.map.CraftMapCursor;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.map.MapCursor;
|
|
+import org.jspecify.annotations.Nullable;
|
|
+
|
|
+public record PaperMapDecorations(
|
|
+ net.minecraft.world.item.component.MapDecorations impl
|
|
+) implements MapDecorations, Handleable<net.minecraft.world.item.component.MapDecorations> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.MapDecorations getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable DecorationEntry decoration(final String id) {
|
|
+ final net.minecraft.world.item.component.MapDecorations.Entry decoration = this.impl.decorations().get(id);
|
|
+ if (decoration == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ return new PaperDecorationEntry(decoration);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Map<String, DecorationEntry> decorations() {
|
|
+ if (this.impl.decorations().isEmpty()) {
|
|
+ return Collections.emptyMap();
|
|
+ }
|
|
+
|
|
+ final Set<Map.Entry<String, net.minecraft.world.item.component.MapDecorations.Entry>> entries = this.impl.decorations().entrySet();
|
|
+ final Map<String, DecorationEntry> decorations = new Object2ObjectOpenHashMap<>(entries.size());
|
|
+ for (final Map.Entry<String, net.minecraft.world.item.component.MapDecorations.Entry> entry : entries) {
|
|
+ decorations.put(entry.getKey(), new PaperDecorationEntry(entry.getValue()));
|
|
+ }
|
|
+
|
|
+ return Collections.unmodifiableMap(decorations);
|
|
+ }
|
|
+
|
|
+ public record PaperDecorationEntry(net.minecraft.world.item.component.MapDecorations.Entry entry) implements DecorationEntry {
|
|
+
|
|
+ public static DecorationEntry toApi(final MapCursor.Type type, final double x, final double z, final float rotation) {
|
|
+ return new PaperDecorationEntry(new net.minecraft.world.item.component.MapDecorations.Entry(CraftMapCursor.CraftType.bukkitToMinecraftHolder(type), x, z, rotation));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapCursor.Type type() {
|
|
+ return CraftMapCursor.CraftType.minecraftHolderToBukkit(this.entry.type());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double x() {
|
|
+ return this.entry.x();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double z() {
|
|
+ return this.entry.z();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float rotation() {
|
|
+ return this.entry.rotation();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private final Map<String, net.minecraft.world.item.component.MapDecorations.Entry> entries = new Object2ObjectOpenHashMap<>();
|
|
+
|
|
+ @Override
|
|
+ public MapDecorations.Builder put(final String id, final DecorationEntry entry) {
|
|
+ this.entries.put(id, new net.minecraft.world.item.component.MapDecorations.Entry(CraftMapCursor.CraftType.bukkitToMinecraftHolder(entry.type()), entry.x(), entry.z(), entry.rotation()));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder putAll(final Map<String, DecorationEntry> entries) {
|
|
+ entries.forEach(this::put);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapDecorations build() {
|
|
+ if (this.entries.isEmpty()) {
|
|
+ return new PaperMapDecorations(net.minecraft.world.item.component.MapDecorations.EMPTY);
|
|
+ }
|
|
+ return new PaperMapDecorations(new net.minecraft.world.item.component.MapDecorations(new Object2ObjectOpenHashMap<>(this.entries)));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperMapId.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperMapId.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..a2b4cc372bb154bbc741ad1bf47cba210f292c5c
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperMapId.java
|
|
@@ -0,0 +1,19 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperMapId(
|
|
+ net.minecraft.world.level.saveddata.maps.MapId impl
|
|
+) implements MapId, Handleable<net.minecraft.world.level.saveddata.maps.MapId> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.level.saveddata.maps.MapId getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int id() {
|
|
+ return this.impl.id();
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperMapItemColor.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperMapItemColor.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..9b6fdfc9c1248bac426ce24d7b66610a6eff3b8f
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperMapItemColor.java
|
|
@@ -0,0 +1,35 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.Color;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperMapItemColor(
|
|
+ net.minecraft.world.item.component.MapItemColor impl
|
|
+) implements MapItemColor, Handleable<net.minecraft.world.item.component.MapItemColor> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.MapItemColor getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Color color() {
|
|
+ return Color.fromRGB(this.impl.rgb() & 0x00FFFFFF); // skip alpha channel
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private Color color = Color.fromRGB(net.minecraft.world.item.component.MapItemColor.DEFAULT.rgb());
|
|
+
|
|
+ @Override
|
|
+ public Builder color(final Color color) {
|
|
+ this.color = color;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public MapItemColor build() {
|
|
+ return new PaperMapItemColor(new net.minecraft.world.item.component.MapItemColor(this.color.asRGB()));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperOminousBottleAmplifier.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperOminousBottleAmplifier.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..a7ed2aa21d0384384a4c5830ead544cb064b15b6
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperOminousBottleAmplifier.java
|
|
@@ -0,0 +1,18 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperOminousBottleAmplifier(
|
|
+ net.minecraft.world.item.component.OminousBottleAmplifier impl
|
|
+) implements OminousBottleAmplifier, Handleable<net.minecraft.world.item.component.OminousBottleAmplifier> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.OminousBottleAmplifier getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int amplifier() {
|
|
+ return this.impl.value();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperPotDecorations.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotDecorations.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..bde757b51d0ae6a36870c789d416ec0e05c4cadf
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotDecorations.java
|
|
@@ -0,0 +1,83 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import java.util.Optional;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemType;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemType;
|
|
+import org.jspecify.annotations.Nullable;
|
|
+
|
|
+public record PaperPotDecorations(
|
|
+ net.minecraft.world.level.block.entity.PotDecorations impl
|
|
+) implements PotDecorations, Handleable<net.minecraft.world.level.block.entity.PotDecorations> {
|
|
+
|
|
+ @Override
|
|
+ public @Nullable ItemType back() {
|
|
+ return this.impl.back().map(CraftItemType::minecraftToBukkitNew).orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable ItemType left() {
|
|
+ return this.impl.left().map(CraftItemType::minecraftToBukkitNew).orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable ItemType right() {
|
|
+ return this.impl.right().map(CraftItemType::minecraftToBukkitNew).orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable ItemType front() {
|
|
+ return this.impl.front().map(CraftItemType::minecraftToBukkitNew).orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.level.block.entity.PotDecorations getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements PotDecorations.Builder {
|
|
+
|
|
+ private @Nullable ItemType back;
|
|
+ private @Nullable ItemType left;
|
|
+ private @Nullable ItemType right;
|
|
+ private @Nullable ItemType front;
|
|
+
|
|
+ @Override
|
|
+ public PotDecorations.Builder back(final @Nullable ItemType back) {
|
|
+ this.back = back;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotDecorations.Builder left(final @Nullable ItemType left) {
|
|
+ this.left = left;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotDecorations.Builder right(final @Nullable ItemType right) {
|
|
+ this.right = right;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotDecorations.Builder front(final @Nullable ItemType front) {
|
|
+ this.front = front;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotDecorations build() {
|
|
+ if (this.back == null && this.left == null && this.right == null && this.front == null) {
|
|
+ return new PaperPotDecorations(net.minecraft.world.level.block.entity.PotDecorations.EMPTY);
|
|
+ }
|
|
+
|
|
+ return new PaperPotDecorations(new net.minecraft.world.level.block.entity.PotDecorations(
|
|
+ Optional.ofNullable(this.back).map(CraftItemType::bukkitToMinecraftNew),
|
|
+ Optional.ofNullable(this.left).map(CraftItemType::bukkitToMinecraftNew),
|
|
+ Optional.ofNullable(this.right).map(CraftItemType::bukkitToMinecraftNew),
|
|
+ Optional.ofNullable(this.front).map(CraftItemType::bukkitToMinecraftNew)
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4712f8bbaa9f00ede895651472d7975ffa30c88d
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperPotionContents.java
|
|
@@ -0,0 +1,103 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import java.util.Optional;
|
|
+import net.minecraft.world.effect.MobEffectInstance;
|
|
+import org.bukkit.Color;
|
|
+import org.bukkit.craftbukkit.potion.CraftPotionType;
|
|
+import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.potion.PotionEffect;
|
|
+import org.bukkit.potion.PotionType;
|
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperPotionContents(
|
|
+ net.minecraft.world.item.alchemy.PotionContents impl
|
|
+) implements PotionContents, Handleable<net.minecraft.world.item.alchemy.PotionContents> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.alchemy.PotionContents getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<PotionEffect> customEffects() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.customEffects(), CraftPotionUtil::toBukkit);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable PotionType potion() {
|
|
+ return this.impl.potion()
|
|
+ .map(CraftPotionType::minecraftHolderToBukkit)
|
|
+ .orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Color customColor() {
|
|
+ return this.impl.customColor()
|
|
+ .map(Color::fromARGB) // alpha channel is supported for tipped arrows, so let's just leave it in
|
|
+ .orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable String customName() {
|
|
+ return this.impl.customName().orElse(null);
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements PotionContents.Builder {
|
|
+
|
|
+ private final List<MobEffectInstance> customEffects = new ObjectArrayList<>();
|
|
+ private @Nullable PotionType type;
|
|
+ private @Nullable Color color;
|
|
+ private @Nullable String customName;
|
|
+
|
|
+ @Override
|
|
+ public PotionContents.Builder potion(final @Nullable PotionType type) {
|
|
+ this.type = type;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotionContents.Builder customColor(final @Nullable Color color) {
|
|
+ this.color = color;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder customName(final @Nullable String name) {
|
|
+ Preconditions.checkArgument(name == null || name.length() <= Short.MAX_VALUE, "Custom name is longer than %s characters", Short.MAX_VALUE);
|
|
+ this.customName = name;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotionContents.Builder addCustomEffect(final PotionEffect effect) {
|
|
+ this.customEffects.add(CraftPotionUtil.fromBukkit(effect));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotionContents.Builder addCustomEffects(final List<PotionEffect> effects) {
|
|
+ effects.forEach(this::addCustomEffect);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PotionContents build() {
|
|
+ if (this.type == null && this.color == null && this.customEffects.isEmpty() && this.customName == null) {
|
|
+ return new PaperPotionContents(net.minecraft.world.item.alchemy.PotionContents.EMPTY);
|
|
+ }
|
|
+
|
|
+ return new PaperPotionContents(new net.minecraft.world.item.alchemy.PotionContents(
|
|
+ Optional.ofNullable(this.type).map(CraftPotionType::bukkitToMinecraftHolder),
|
|
+ Optional.ofNullable(this.color).map(Color::asARGB),
|
|
+ new ObjectArrayList<>(this.customEffects),
|
|
+ Optional.ofNullable(this.customName)
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperRepairable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperRepairable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..96345e051c4aa77820e857a02768b684d52d7096
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperRepairable.java
|
|
@@ -0,0 +1,22 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.registry.set.RegistryKeySet;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemType;
|
|
+
|
|
+public record PaperRepairable(
|
|
+ net.minecraft.world.item.enchantment.Repairable impl
|
|
+) implements Repairable, Handleable<net.minecraft.world.item.enchantment.Repairable> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.enchantment.Repairable getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public RegistryKeySet<ItemType> types() {
|
|
+ return PaperRegistrySets.convertToApi(RegistryKey.ITEM, this.impl.items());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7583a7efb4bfdb0157ee27a1b7cfb661eeccb02a
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperResolvableProfile.java
|
|
@@ -0,0 +1,105 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
|
+import com.destroystokyo.paper.profile.PlayerProfile;
|
|
+import com.destroystokyo.paper.profile.ProfileProperty;
|
|
+import com.google.common.base.Preconditions;
|
|
+import com.mojang.authlib.properties.Property;
|
|
+import com.mojang.authlib.properties.PropertyMap;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import java.util.Collection;
|
|
+import java.util.Optional;
|
|
+import java.util.UUID;
|
|
+import java.util.concurrent.CompletableFuture;
|
|
+import net.minecraft.util.StringUtil;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperResolvableProfile(
|
|
+ net.minecraft.world.item.component.ResolvableProfile impl
|
|
+) implements ResolvableProfile, Handleable<net.minecraft.world.item.component.ResolvableProfile> {
|
|
+
|
|
+ static PaperResolvableProfile toApi(final PlayerProfile profile) {
|
|
+ return new PaperResolvableProfile(new net.minecraft.world.item.component.ResolvableProfile(CraftPlayerProfile.asAuthlibCopy(profile)));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.ResolvableProfile getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable UUID uuid() {
|
|
+ return this.impl.id().orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable String name() {
|
|
+ return this.impl.name().orElse(null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable Collection<ProfileProperty> properties() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.properties().values(), input -> new ProfileProperty(input.name(), input.value(), input.signature()));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CompletableFuture<PlayerProfile> resolve() {
|
|
+ return this.impl.resolve().thenApply(resolvableProfile -> CraftPlayerProfile.asBukkitCopy(resolvableProfile.gameProfile()));
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements ResolvableProfile.Builder {
|
|
+
|
|
+ private final PropertyMap propertyMap = new PropertyMap();
|
|
+ private @Nullable String name;
|
|
+ private @Nullable UUID uuid;
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile.Builder name(final @Nullable String name) {
|
|
+ if (name != null) {
|
|
+ Preconditions.checkArgument(name.length() <= 16, "name cannot be more than 16 characters, was %s", name.length());
|
|
+ Preconditions.checkArgument(StringUtil.isValidPlayerName(name), "name cannot include invalid characters, was %s", name);
|
|
+ }
|
|
+ this.name = name;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile.Builder uuid(final @Nullable UUID uuid) {
|
|
+ this.uuid = uuid;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile.Builder addProperty(final ProfileProperty property) {
|
|
+ // ProfileProperty constructor already has specific validations
|
|
+ final Property newProperty = new Property(property.getName(), property.getValue(), property.getSignature());
|
|
+ if (!this.propertyMap.containsEntry(property.getName(), newProperty)) { // underlying map is a multimap that doesn't allow duplicate key-value pair
|
|
+ final int newSize = this.propertyMap.size() + 1;
|
|
+ Preconditions.checkArgument(newSize <= 16, "Cannot have more than 16 properties, was %s", newSize);
|
|
+ }
|
|
+
|
|
+ this.propertyMap.put(property.getName(), newProperty);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile.Builder addProperties(final Collection<ProfileProperty> properties) {
|
|
+ properties.forEach(this::addProperty);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ResolvableProfile build() {
|
|
+ final PropertyMap shallowCopy = new PropertyMap();
|
|
+ shallowCopy.putAll(this.propertyMap);
|
|
+
|
|
+ return new PaperResolvableProfile(new net.minecraft.world.item.component.ResolvableProfile(
|
|
+ Optional.ofNullable(this.name),
|
|
+ Optional.ofNullable(this.uuid),
|
|
+ shallowCopy
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperSeededContainerLoot.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperSeededContainerLoot.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1ee469b3b690a877e066dbca79706678cd915fa8
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperSeededContainerLoot.java
|
|
@@ -0,0 +1,59 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.resources.ResourceKey;
|
|
+import net.minecraft.world.level.storage.loot.LootTable;
|
|
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperSeededContainerLoot(
|
|
+ net.minecraft.world.item.component.SeededContainerLoot impl
|
|
+) implements SeededContainerLoot, Handleable<net.minecraft.world.item.component.SeededContainerLoot> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.SeededContainerLoot getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Key lootTable() {
|
|
+ return CraftNamespacedKey.fromMinecraft(this.impl.lootTable().location());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long seed() {
|
|
+ return this.impl.seed();
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements SeededContainerLoot.Builder {
|
|
+
|
|
+ private long seed = LootTable.RANDOMIZE_SEED;
|
|
+ private Key key;
|
|
+
|
|
+ BuilderImpl(final Key key) {
|
|
+ this.key = key;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SeededContainerLoot.Builder lootTable(final Key key) {
|
|
+ this.key = key;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SeededContainerLoot.Builder seed(final long seed) {
|
|
+ this.seed = seed;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SeededContainerLoot build() {
|
|
+ return new PaperSeededContainerLoot(new net.minecraft.world.item.component.SeededContainerLoot(
|
|
+ ResourceKey.create(Registries.LOOT_TABLE, PaperAdventure.asVanilla(this.key)),
|
|
+ this.seed
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperSuspiciousStewEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperSuspiciousStewEffects.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..41df23c7e7e713e88eef757fda347381e151b0fc
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperSuspiciousStewEffects.java
|
|
@@ -0,0 +1,58 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.potion.SuspiciousEffectEntry;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.Collection;
|
|
+import java.util.List;
|
|
+import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+import static io.papermc.paper.potion.SuspiciousEffectEntry.create;
|
|
+
|
|
+public record PaperSuspiciousStewEffects(
|
|
+ net.minecraft.world.item.component.SuspiciousStewEffects impl
|
|
+) implements SuspiciousStewEffects, Handleable<net.minecraft.world.item.component.SuspiciousStewEffects> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.SuspiciousStewEffects getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<SuspiciousEffectEntry> effects() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.effects(), entry -> create(CraftPotionEffectType.minecraftHolderToBukkit(entry.effect()), entry.duration()));
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private final List<net.minecraft.world.item.component.SuspiciousStewEffects.Entry> effects = new ObjectArrayList<>();
|
|
+
|
|
+ @Override
|
|
+ public Builder add(final SuspiciousEffectEntry entry) {
|
|
+ this.effects.add(new net.minecraft.world.item.component.SuspiciousStewEffects.Entry(
|
|
+ org.bukkit.craftbukkit.potion.CraftPotionEffectType.bukkitToMinecraftHolder(entry.effect()),
|
|
+ entry.duration()
|
|
+ ));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder addAll(final Collection<SuspiciousEffectEntry> entries) {
|
|
+ entries.forEach(this::add);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public SuspiciousStewEffects build() {
|
|
+ if (this.effects.isEmpty()) {
|
|
+ return new PaperSuspiciousStewEffects(net.minecraft.world.item.component.SuspiciousStewEffects.EMPTY);
|
|
+ }
|
|
+
|
|
+ return new PaperSuspiciousStewEffects(
|
|
+ new net.minecraft.world.item.component.SuspiciousStewEffects(new ObjectArrayList<>(this.effects))
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperUnbreakable.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperUnbreakable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..edeb3308af4c359d1930fdbc5417727451b6f0eb
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperUnbreakable.java
|
|
@@ -0,0 +1,39 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public record PaperUnbreakable(
|
|
+ net.minecraft.world.item.component.Unbreakable impl
|
|
+) implements Unbreakable, Handleable<net.minecraft.world.item.component.Unbreakable> {
|
|
+
|
|
+ @Override
|
|
+ public boolean showInTooltip() {
|
|
+ return this.impl.showInTooltip();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Unbreakable showInTooltip(final boolean showInTooltip) {
|
|
+ return new PaperUnbreakable(this.impl.withTooltip(showInTooltip));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.Unbreakable getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements Unbreakable.Builder {
|
|
+
|
|
+ private boolean showInTooltip = true;
|
|
+
|
|
+ @Override
|
|
+ public Unbreakable.Builder showInTooltip(final boolean showInTooltip) {
|
|
+ this.showInTooltip = showInTooltip;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Unbreakable build() {
|
|
+ return new PaperUnbreakable(new net.minecraft.world.item.component.Unbreakable(this.showInTooltip));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperUseCooldown.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseCooldown.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1aeab920faaf5653ddb8e77372060fb8d3226641
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseCooldown.java
|
|
@@ -0,0 +1,56 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import java.util.Optional;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jspecify.annotations.Nullable;
|
|
+
|
|
+public record PaperUseCooldown(
|
|
+ net.minecraft.world.item.component.UseCooldown impl
|
|
+) implements UseCooldown, Handleable<net.minecraft.world.item.component.UseCooldown> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.UseCooldown getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float seconds() {
|
|
+ return this.impl.seconds();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Nullable Key cooldownGroup() {
|
|
+ return this.impl.cooldownGroup()
|
|
+ .map(PaperAdventure::asAdventure)
|
|
+ .orElse(null);
|
|
+ }
|
|
+
|
|
+
|
|
+ static final class BuilderImpl implements Builder {
|
|
+
|
|
+ private final float seconds;
|
|
+ private Optional<ResourceLocation> cooldownGroup = Optional.empty();
|
|
+
|
|
+ BuilderImpl(final float seconds) {
|
|
+ this.seconds = seconds;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Builder cooldownGroup(@Nullable final Key key) {
|
|
+ this.cooldownGroup = Optional.ofNullable(key)
|
|
+ .map(PaperAdventure::asVanilla);
|
|
+
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public UseCooldown build() {
|
|
+ return new PaperUseCooldown(
|
|
+ new net.minecraft.world.item.component.UseCooldown(this.seconds, this.cooldownGroup)
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperUseRemainder.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseRemainder.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c2c04506940704c2ec9a5e6bb469c4771e2d49c2
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperUseRemainder.java
|
|
@@ -0,0 +1,20 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+
|
|
+public record PaperUseRemainder(
|
|
+ net.minecraft.world.item.component.UseRemainder impl
|
|
+) implements UseRemainder, Handleable<net.minecraft.world.item.component.UseRemainder> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.UseRemainder getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ItemStack transformInto() {
|
|
+ return CraftItemStack.asBukkitCopy(this.impl.convertInto());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperWritableBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperWritableBookContent.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..559343a33bada475cc5bbcd431cd88b537c8cef7
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperWritableBookContent.java
|
|
@@ -0,0 +1,103 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.text.Filtered;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import java.util.Optional;
|
|
+import net.minecraft.server.network.Filterable;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+public record PaperWritableBookContent(
|
|
+ net.minecraft.world.item.component.WritableBookContent impl
|
|
+) implements WritableBookContent, Handleable<net.minecraft.world.item.component.WritableBookContent> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.WritableBookContent getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Filtered<String>> pages() {
|
|
+ return MCUtil.transformUnmodifiable(this.impl.pages(), input -> Filtered.of(input.raw(), input.filtered().orElse(null)));
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements WritableBookContent.Builder {
|
|
+
|
|
+ private final List<Filterable<String>> pages = new ObjectArrayList<>();
|
|
+
|
|
+ private static void validatePageLength(final String page) {
|
|
+ Preconditions.checkArgument(
|
|
+ page.length() <= net.minecraft.world.item.component.WritableBookContent.PAGE_EDIT_LENGTH,
|
|
+ "Cannot have page length more than %s, had %s",
|
|
+ net.minecraft.world.item.component.WritableBookContent.PAGE_EDIT_LENGTH,
|
|
+ page.length()
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static void validatePageCount(final int current, final int add) {
|
|
+ final int newSize = current + add;
|
|
+ Preconditions.checkArgument(
|
|
+ newSize <= net.minecraft.world.item.component.WritableBookContent.MAX_PAGES,
|
|
+ "Cannot have more than %s pages, had %s",
|
|
+ net.minecraft.world.item.component.WritableBookContent.MAX_PAGES,
|
|
+ newSize
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WritableBookContent.Builder addPage(final String page) {
|
|
+ validatePageLength(page);
|
|
+ validatePageCount(this.pages.size(), 1);
|
|
+ this.pages.add(Filterable.passThrough(page));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WritableBookContent.Builder addPages(final List<String> pages) {
|
|
+ validatePageCount(this.pages.size(), pages.size());
|
|
+ for (final String page : pages) {
|
|
+ validatePageLength(page);
|
|
+ this.pages.add(Filterable.passThrough(page));
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WritableBookContent.Builder addFilteredPage(final Filtered<String> page) {
|
|
+ validatePageLength(page.raw());
|
|
+ if (page.filtered() != null) {
|
|
+ validatePageLength(page.filtered());
|
|
+ }
|
|
+ validatePageCount(this.pages.size(), 1);
|
|
+ this.pages.add(new Filterable<>(page.raw(), Optional.ofNullable(page.filtered())));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WritableBookContent.Builder addFilteredPages(final List<Filtered<String>> pages) {
|
|
+ validatePageCount(this.pages.size(), pages.size());
|
|
+ for (final Filtered<String> page : pages) {
|
|
+ validatePageLength(page.raw());
|
|
+ if (page.filtered() != null) {
|
|
+ validatePageLength(page.filtered());
|
|
+ }
|
|
+ this.pages.add(new Filterable<>(page.raw(), Optional.ofNullable(page.filtered())));
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WritableBookContent build() {
|
|
+ if (this.pages.isEmpty()) {
|
|
+ return new PaperWritableBookContent(net.minecraft.world.item.component.WritableBookContent.EMPTY);
|
|
+ }
|
|
+
|
|
+ return new PaperWritableBookContent(
|
|
+ new net.minecraft.world.item.component.WritableBookContent(new ObjectArrayList<>(this.pages))
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/PaperWrittenBookContent.java b/src/main/java/io/papermc/paper/datacomponent/item/PaperWrittenBookContent.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..037a6695bdb8ee6e3c119fa79000c4ea28e1bef8
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/PaperWrittenBookContent.java
|
|
@@ -0,0 +1,183 @@
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.text.Filtered;
|
|
+import io.papermc.paper.util.MCUtil;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
+import java.util.List;
|
|
+import java.util.Optional;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.ComponentLike;
|
|
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|
+import net.minecraft.server.network.Filterable;
|
|
+import net.minecraft.util.GsonHelper;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+import org.jetbrains.annotations.Unmodifiable;
|
|
+
|
|
+import static io.papermc.paper.adventure.PaperAdventure.asAdventure;
|
|
+import static io.papermc.paper.adventure.PaperAdventure.asVanilla;
|
|
+
|
|
+public record PaperWrittenBookContent(
|
|
+ net.minecraft.world.item.component.WrittenBookContent impl
|
|
+) implements WrittenBookContent, Handleable<net.minecraft.world.item.component.WrittenBookContent> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.component.WrittenBookContent getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Filtered<String> title() {
|
|
+ return Filtered.of(this.impl.title().raw(), this.impl.title().filtered().orElse(null));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String author() {
|
|
+ return this.impl.author();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int generation() {
|
|
+ return this.impl.generation();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public @Unmodifiable List<Filtered<Component>> pages() {
|
|
+ return MCUtil.transformUnmodifiable(
|
|
+ this.impl.pages(),
|
|
+ page -> Filtered.of(asAdventure(page.raw()), page.filtered().map(PaperAdventure::asAdventure).orElse(null))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean resolved() {
|
|
+ return this.impl.resolved();
|
|
+ }
|
|
+
|
|
+ static final class BuilderImpl implements WrittenBookContent.Builder {
|
|
+
|
|
+ private final List<Filterable<net.minecraft.network.chat.Component>> pages = new ObjectArrayList<>();
|
|
+ private Filterable<String> title;
|
|
+ private String author;
|
|
+ private int generation = 0;
|
|
+ private boolean resolved = false;
|
|
+
|
|
+ BuilderImpl(final Filtered<String> title, final String author) {
|
|
+ validateTitle(title.raw());
|
|
+ if (title.filtered() != null) {
|
|
+ validateTitle(title.filtered());
|
|
+ }
|
|
+ this.title = new Filterable<>(title.raw(), Optional.ofNullable(title.filtered()));
|
|
+ this.author = author;
|
|
+ }
|
|
+
|
|
+ private static void validateTitle(final String title) {
|
|
+ Preconditions.checkArgument(
|
|
+ title.length() <= net.minecraft.world.item.component.WrittenBookContent.TITLE_MAX_LENGTH,
|
|
+ "Title cannot be longer than %s, was %s",
|
|
+ net.minecraft.world.item.component.WrittenBookContent.TITLE_MAX_LENGTH,
|
|
+ title.length()
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static void validatePageLength(final Component page) {
|
|
+ final String flagPage = GsonHelper.toStableString(GsonComponentSerializer.gson().serializeToTree(page));
|
|
+ Preconditions.checkArgument(
|
|
+ flagPage.length() <= net.minecraft.world.item.component.WrittenBookContent.PAGE_LENGTH,
|
|
+ "Cannot have page length more than %s, had %s",
|
|
+ net.minecraft.world.item.component.WrittenBookContent.PAGE_LENGTH,
|
|
+ flagPage.length()
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder title(final String title) {
|
|
+ validateTitle(title);
|
|
+ this.title = Filterable.passThrough(title);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder filteredTitle(final Filtered<String> title) {
|
|
+ validateTitle(title.raw());
|
|
+ if (title.filtered() != null) {
|
|
+ validateTitle(title.filtered());
|
|
+ }
|
|
+ this.title = new Filterable<>(title.raw(), Optional.ofNullable(title.filtered()));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder author(final String author) {
|
|
+ this.author = author;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder generation(final int generation) {
|
|
+ Preconditions.checkArgument(
|
|
+ generation >= 0 && generation <= net.minecraft.world.item.component.WrittenBookContent.MAX_GENERATION,
|
|
+ "generation must be between %s and %s, was %s",
|
|
+ 0, net.minecraft.world.item.component.WrittenBookContent.MAX_GENERATION,
|
|
+ generation
|
|
+ );
|
|
+ this.generation = generation;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder resolved(final boolean resolved) {
|
|
+ this.resolved = resolved;
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder addPage(final ComponentLike page) {
|
|
+ final Component component = page.asComponent();
|
|
+ validatePageLength(component);
|
|
+ this.pages.add(Filterable.passThrough(asVanilla(component)));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder addPages(final List<? extends ComponentLike> pages) {
|
|
+ for (final ComponentLike page : pages) {
|
|
+ final Component component = page.asComponent();
|
|
+ validatePageLength(component);
|
|
+ this.pages.add(Filterable.passThrough(asVanilla(component)));
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder addFilteredPage(final Filtered<? extends ComponentLike> page) {
|
|
+ final Component raw = page.raw().asComponent();
|
|
+ validatePageLength(raw);
|
|
+ Component filtered = null;
|
|
+ if (page.filtered() != null) {
|
|
+ filtered = page.filtered().asComponent();
|
|
+ validatePageLength(filtered);
|
|
+ }
|
|
+ this.pages.add(new Filterable<>(asVanilla(raw), Optional.ofNullable(filtered).map(PaperAdventure::asVanilla)));
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent.Builder addFilteredPages(final List<Filtered<? extends ComponentLike>> pages) {
|
|
+ pages.forEach(this::addFilteredPage);
|
|
+ return this;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public WrittenBookContent build() {
|
|
+ return new PaperWrittenBookContent(new net.minecraft.world.item.component.WrittenBookContent(
|
|
+ this.title,
|
|
+ this.author,
|
|
+ this.generation,
|
|
+ new ObjectArrayList<>(this.pages),
|
|
+ this.resolved
|
|
+ ));
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..eab1883d691e0d0034b7959c8130a6240c3f529c
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/ConsumableTypesBridgeImpl.java
|
|
@@ -0,0 +1,64 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import com.google.common.base.Preconditions;
|
|
+import com.google.common.collect.Lists;
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.registry.set.RegistryKeySet;
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.minecraft.core.registries.BuiltInRegistries;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
|
+import org.bukkit.potion.PotionEffect;
|
|
+import org.bukkit.potion.PotionEffectType;
|
|
+import org.jetbrains.annotations.ApiStatus;
|
|
+import org.jspecify.annotations.NullMarked;
|
|
+
|
|
+@ApiStatus.Internal
|
|
+@NullMarked
|
|
+public class ConsumableTypesBridgeImpl implements ConsumableTypesBridge {
|
|
+
|
|
+ @Override
|
|
+ public ConsumeEffect.ApplyStatusEffects applyStatusEffects(final List<PotionEffect> effectList, final float probability) {
|
|
+ Preconditions.checkArgument(0 <= probability && probability <= 1, "probability must be between 0-1, was %s", probability);
|
|
+ return new PaperApplyStatusEffects(
|
|
+ new net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect(
|
|
+ new ArrayList<>(Lists.transform(effectList, CraftPotionUtil::fromBukkit)),
|
|
+ probability
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ConsumeEffect.RemoveStatusEffects removeStatusEffects(final RegistryKeySet<PotionEffectType> effectTypes) {
|
|
+ return new PaperRemoveStatusEffects(
|
|
+ new net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect(
|
|
+ PaperRegistrySets.convertToNms(Registries.MOB_EFFECT, BuiltInRegistries.BUILT_IN_CONVERSIONS.lookup(), effectTypes)
|
|
+ )
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ConsumeEffect.ClearAllStatusEffects clearAllStatusEffects() {
|
|
+ return new PaperClearAllStatusEffects(
|
|
+ new net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect()
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ConsumeEffect.PlaySound playSoundEffect(final Key sound) {
|
|
+ return new PaperPlaySound(
|
|
+ new net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect(PaperAdventure.resolveSound(sound))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ConsumeEffect.TeleportRandomly teleportRandomlyEffect(final float diameter) {
|
|
+ Preconditions.checkArgument(diameter > 0, "diameter must be positive, was %s", diameter);
|
|
+ return new PaperTeleportRandomly(
|
|
+ new net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect(diameter)
|
|
+ );
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperApplyStatusEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperApplyStatusEffects.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0d2a4ba560f5a34139517ac2e17667c94a3c56f6
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperApplyStatusEffects.java
|
|
@@ -0,0 +1,28 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import java.util.List;
|
|
+import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;
|
|
+import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
|
+import org.bukkit.potion.PotionEffect;
|
|
+
|
|
+import static io.papermc.paper.util.MCUtil.transformUnmodifiable;
|
|
+
|
|
+public record PaperApplyStatusEffects(
|
|
+ ApplyStatusEffectsConsumeEffect impl
|
|
+) implements ConsumeEffect.ApplyStatusEffects, PaperConsumableEffectImpl<ApplyStatusEffectsConsumeEffect> {
|
|
+
|
|
+ @Override
|
|
+ public List<PotionEffect> effects() {
|
|
+ return transformUnmodifiable(this.impl().effects(), CraftPotionUtil::toBukkit);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float probability() {
|
|
+ return this.impl.probability();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ApplyStatusEffectsConsumeEffect getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperClearAllStatusEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperClearAllStatusEffects.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..2afcbbbeb486783737fd606113b6f938d0a18cb5
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperClearAllStatusEffects.java
|
|
@@ -0,0 +1,11 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+public record PaperClearAllStatusEffects(
|
|
+ net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect impl
|
|
+) implements ConsumeEffect.ClearAllStatusEffects, PaperConsumableEffectImpl<net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect> {
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffectImpl.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffectImpl.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..05ede1d3f5b0b5ea3a5004cb4a7a153ed7714a55
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffectImpl.java
|
|
@@ -0,0 +1,7 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import net.minecraft.world.item.consume_effects.ConsumeEffect;
|
|
+import org.bukkit.craftbukkit.util.Handleable;
|
|
+
|
|
+public interface PaperConsumableEffectImpl<T extends ConsumeEffect> extends Handleable<T> {
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffects.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..ff07939ef0730a11c712c09c360da8a21a777618
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperConsumableEffects.java
|
|
@@ -0,0 +1,32 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect;
|
|
+import net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect;
|
|
+import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect;
|
|
+import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect;
|
|
+import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect;
|
|
+
|
|
+public final class PaperConsumableEffects {
|
|
+
|
|
+ private PaperConsumableEffects() {
|
|
+ }
|
|
+
|
|
+ public static ConsumeEffect fromNms(net.minecraft.world.item.consume_effects.ConsumeEffect consumable) {
|
|
+ return switch (consumable) {
|
|
+ case ApplyStatusEffectsConsumeEffect effect -> new PaperApplyStatusEffects(effect);
|
|
+ case ClearAllStatusEffectsConsumeEffect effect -> new PaperClearAllStatusEffects(effect);
|
|
+ case PlaySoundConsumeEffect effect -> new PaperPlaySound(effect);
|
|
+ case RemoveStatusEffectsConsumeEffect effect -> new PaperRemoveStatusEffects(effect);
|
|
+ case TeleportRandomlyConsumeEffect effect -> new PaperTeleportRandomly(effect);
|
|
+ default -> throw new UnsupportedOperationException("Don't know how to convert " + consumable.getClass());
|
|
+ };
|
|
+ }
|
|
+
|
|
+ public static net.minecraft.world.item.consume_effects.ConsumeEffect toNms(ConsumeEffect effect) {
|
|
+ if (effect instanceof PaperConsumableEffectImpl<?> consumableEffect) {
|
|
+ return consumableEffect.getHandle();
|
|
+ } else {
|
|
+ throw new UnsupportedOperationException("Must implement handleable!");
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperPlaySound.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperPlaySound.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..26a8ee292b45e57462e6e6629b328fbf9d6b47e7
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperPlaySound.java
|
|
@@ -0,0 +1,20 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import io.papermc.paper.adventure.PaperAdventure;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect;
|
|
+
|
|
+public record PaperPlaySound(
|
|
+ PlaySoundConsumeEffect impl
|
|
+) implements ConsumeEffect.PlaySound, PaperConsumableEffectImpl<PlaySoundConsumeEffect> {
|
|
+
|
|
+ @Override
|
|
+ public Key sound() {
|
|
+ return PaperAdventure.asAdventure(this.impl.sound().value().location());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PlaySoundConsumeEffect getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperRemoveStatusEffects.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperRemoveStatusEffects.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..20e09c6ebab91b1ec103aa149d0f57a2a5502644
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperRemoveStatusEffects.java
|
|
@@ -0,0 +1,21 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.registry.set.PaperRegistrySets;
|
|
+import io.papermc.paper.registry.set.RegistryKeySet;
|
|
+import org.bukkit.potion.PotionEffectType;
|
|
+
|
|
+public record PaperRemoveStatusEffects(
|
|
+ net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect impl
|
|
+) implements ConsumeEffect.RemoveStatusEffects, PaperConsumableEffectImpl<net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect> {
|
|
+
|
|
+ @Override
|
|
+ public RegistryKeySet<PotionEffectType> removeEffects() {
|
|
+ return PaperRegistrySets.convertToApi(RegistryKey.MOB_EFFECT, this.impl.effects());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperTeleportRandomly.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperTeleportRandomly.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..c21889e9984f7c36d9f19771c2e23b6efba5197d
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/PaperTeleportRandomly.java
|
|
@@ -0,0 +1,15 @@
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+public record PaperTeleportRandomly(
|
|
+ net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect impl
|
|
+) implements ConsumeEffect.TeleportRandomly, PaperConsumableEffectImpl<net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect> {
|
|
+ @Override
|
|
+ public float diameter() {
|
|
+ return this.impl.diameter();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect getHandle() {
|
|
+ return this.impl;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/consumable/package-info.java b/src/main/java/io/papermc/paper/datacomponent/item/consumable/package-info.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..af6720a49a9d336a345e2bc91d6714f6b2c39886
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/consumable/package-info.java
|
|
@@ -0,0 +1,7 @@
|
|
+/**
|
|
+ * Relating to consumable effects for components.
|
|
+ */
|
|
+@NullMarked
|
|
+package io.papermc.paper.datacomponent.item.consumable;
|
|
+
|
|
+import org.jspecify.annotations.NullMarked;
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/item/package-info.java b/src/main/java/io/papermc/paper/datacomponent/item/package-info.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..02a69025662d6a887f5449fd5eaf7d1083973bf3
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/item/package-info.java
|
|
@@ -0,0 +1,4 @@
|
|
+@NullMarked
|
|
+package io.papermc.paper.datacomponent.item;
|
|
+
|
|
+import org.jspecify.annotations.NullMarked;
|
|
diff --git a/src/main/java/io/papermc/paper/datacomponent/package-info.java b/src/main/java/io/papermc/paper/datacomponent/package-info.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..62aa1061c35d5358e6dec16a52574b427cc4b732
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/datacomponent/package-info.java
|
|
@@ -0,0 +1,4 @@
|
|
+@NullMarked
|
|
+package io.papermc.paper.datacomponent;
|
|
+
|
|
+import org.jspecify.annotations.NullMarked;
|
|
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
|
|
index dcca8bbba5c54001c3cf4fdcd8eeb08ffb1ba574..21f5c8af86c866ff7e0f7af3b3b810824b087d26 100644
|
|
--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
|
|
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
|
|
@@ -2,6 +2,8 @@ package io.papermc.paper.registry;
|
|
|
|
import com.google.common.base.Preconditions;
|
|
import io.papermc.paper.adventure.PaperAdventure;
|
|
+import io.papermc.paper.datacomponent.DataComponentTypes;
|
|
+import io.papermc.paper.datacomponent.PaperDataComponentType;
|
|
import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
|
|
import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
|
|
import io.papermc.paper.registry.data.PaperPaintingVariantRegistryEntry;
|
|
@@ -93,6 +95,7 @@ public final class PaperRegistries {
|
|
start(Registries.ATTRIBUTE, RegistryKey.ATTRIBUTE).craft(Attribute.class, CraftAttribute::new).build(),
|
|
start(Registries.FLUID, RegistryKey.FLUID).craft(Fluid.class, CraftFluid::new).build(),
|
|
start(Registries.SOUND_EVENT, RegistryKey.SOUND_EVENT).craft(Sound.class, CraftSound::new).build(),
|
|
+ start(Registries.DATA_COMPONENT_TYPE, RegistryKey.DATA_COMPONENT_TYPE).craft(DataComponentTypes.class, PaperDataComponentType::of).build(),
|
|
|
|
// data-drivens
|
|
start(Registries.BIOME, RegistryKey.BIOME).craft(Biome.class, CraftBiome::new).build().delayed(),
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
index 756c73a401437566258813946fa10c7caa8f2469..78975412da0f0c2b802bfce6d30d56b26d8023e2 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
|
|
@@ -20,13 +20,11 @@ import net.minecraft.world.item.enchantment.ItemEnchantments;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.configuration.serialization.DelegateDeserialization;
|
|
import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
|
|
-import org.bukkit.craftbukkit.util.CraftLegacy;
|
|
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
|
import org.bukkit.enchantments.Enchantment;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
import org.bukkit.material.MaterialData;
|
|
-import org.jetbrains.annotations.ApiStatus;
|
|
|
|
@DelegateDeserialization(ItemStack.class)
|
|
public final class CraftItemStack extends ItemStack {
|
|
@@ -206,7 +204,7 @@ public final class CraftItemStack extends ItemStack {
|
|
this.adjustTagForItemMeta(oldType); // Paper
|
|
}
|
|
}
|
|
- this.setData(null);
|
|
+ this.setData((MaterialData) null); // Paper
|
|
}
|
|
|
|
@Override
|
|
@@ -245,7 +243,7 @@ public final class CraftItemStack extends ItemStack {
|
|
|
|
@Override
|
|
public int getMaxStackSize() {
|
|
- return (this.handle == null) ? Material.AIR.getMaxStackSize() : this.handle.getMaxStackSize();
|
|
+ return (this.handle == null) ? Item.DEFAULT_MAX_STACK_SIZE : this.handle.getMaxStackSize(); // Paper - air stacks to 64
|
|
}
|
|
|
|
// Paper start
|
|
@@ -267,12 +265,14 @@ public final class CraftItemStack extends ItemStack {
|
|
public void addUnsafeEnchantment(Enchantment ench, int level) {
|
|
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
|
|
|
|
- // Paper start - Replace whole method
|
|
- final ItemMeta itemMeta = this.getItemMeta();
|
|
- if (itemMeta != null) {
|
|
- itemMeta.addEnchant(ench, level, true);
|
|
- this.setItemMeta(itemMeta);
|
|
+ // Paper start
|
|
+ if (this.handle == null) {
|
|
+ return;
|
|
}
|
|
+
|
|
+ EnchantmentHelper.updateEnchantments(this.handle, mutable -> { // data component api doesn't really support mutable things once already set yet
|
|
+ mutable.set(CraftEnchantment.bukkitToMinecraftHolder(ench), level);
|
|
+ });
|
|
// Paper end
|
|
}
|
|
|
|
@@ -302,17 +302,28 @@ public final class CraftItemStack extends ItemStack {
|
|
public int removeEnchantment(Enchantment ench) {
|
|
Preconditions.checkArgument(ench != null, "Enchantment cannot be null");
|
|
|
|
- // Paper start - replace entire method
|
|
- int level = getEnchantmentLevel(ench);
|
|
- if (level > 0) {
|
|
- final ItemMeta itemMeta = this.getItemMeta();
|
|
- if (itemMeta == null) return 0;
|
|
- itemMeta.removeEnchant(ench);
|
|
- this.setItemMeta(itemMeta);
|
|
+ // Paper start
|
|
+ if (this.handle == null) {
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ItemEnchantments itemEnchantments = this.handle.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
|
+ if (itemEnchantments.isEmpty()) {
|
|
+ return 0;
|
|
}
|
|
- // Paper end
|
|
|
|
- return level;
|
|
+ Holder<net.minecraft.world.item.enchantment.Enchantment> removedEnchantment = CraftEnchantment.bukkitToMinecraftHolder(ench);
|
|
+ if (itemEnchantments.keySet().contains(removedEnchantment)) {
|
|
+ int previousLevel = itemEnchantments.getLevel(removedEnchantment);
|
|
+
|
|
+ ItemEnchantments.Mutable mutable = new ItemEnchantments.Mutable(itemEnchantments); // data component api doesn't really support mutable things once already set yet
|
|
+ mutable.removeIf(enchantment -> enchantment.equals(removedEnchantment));
|
|
+ this.handle.set(DataComponents.ENCHANTMENTS, mutable.toImmutable());
|
|
+ return previousLevel;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ // Paper end
|
|
}
|
|
|
|
@Override
|
|
@@ -324,7 +335,13 @@ public final class CraftItemStack extends ItemStack {
|
|
|
|
@Override
|
|
public Map<Enchantment, Integer> getEnchantments() {
|
|
- return this.hasItemMeta() ? this.getItemMeta().getEnchants() : ImmutableMap.<Enchantment, Integer>of(); // Paper - use Item Meta
|
|
+ // Paper start
|
|
+ io.papermc.paper.datacomponent.item.ItemEnchantments itemEnchantments = this.getData(io.papermc.paper.datacomponent.DataComponentTypes.ENCHANTMENTS); // empty constant might be useful here
|
|
+ if (itemEnchantments == null) {
|
|
+ return java.util.Collections.emptyMap();
|
|
+ }
|
|
+ return itemEnchantments.enchantments();
|
|
+ // Paper end
|
|
}
|
|
|
|
static Map<Enchantment, Integer> getEnchantments(net.minecraft.world.item.ItemStack item) {
|
|
@@ -526,4 +543,119 @@ public final class CraftItemStack extends ItemStack {
|
|
return this.pdcView;
|
|
}
|
|
// Paper end - pdc
|
|
+ // Paper start - data component API
|
|
+ @Override
|
|
+ public <T> T getData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type) {
|
|
+ if (this.isEmpty()) {
|
|
+ return null;
|
|
+ }
|
|
+ return io.papermc.paper.datacomponent.PaperDataComponentType.convertDataComponentValue(this.handle.getComponents(), (io.papermc.paper.datacomponent.PaperDataComponentType.ValuedImpl<T, ?>) type);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasData(final io.papermc.paper.datacomponent.DataComponentType type) {
|
|
+ if (this.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ return this.handle.has(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Set<io.papermc.paper.datacomponent.DataComponentType> getDataTypes() {
|
|
+ if (this.isEmpty()) {
|
|
+ return java.util.Collections.emptySet();
|
|
+ }
|
|
+ return io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(this.handle.getComponents().keySet());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public <T> void setData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type, final T value) {
|
|
+ Preconditions.checkArgument(value != null, "value cannot be null");
|
|
+ if (this.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ this.setDataInternal((io.papermc.paper.datacomponent.PaperDataComponentType.ValuedImpl<T, ?>) type, value);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setData(final io.papermc.paper.datacomponent.DataComponentType.NonValued type) {
|
|
+ if (this.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ this.setDataInternal((io.papermc.paper.datacomponent.PaperDataComponentType.NonValuedImpl<?, ?>) type, null);
|
|
+ }
|
|
+
|
|
+ private <A, V> void setDataInternal(final io.papermc.paper.datacomponent.PaperDataComponentType<A, V> type, final A value) {
|
|
+ this.handle.set(type.getHandle(), type.getAdapter().toVanilla(value));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void unsetData(final io.papermc.paper.datacomponent.DataComponentType type) {
|
|
+ if (this.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ this.handle.remove(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void resetData(final io.papermc.paper.datacomponent.DataComponentType type) {
|
|
+ if (this.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ this.resetData((io.papermc.paper.datacomponent.PaperDataComponentType<?, ?>) type);
|
|
+ }
|
|
+
|
|
+ private <M> void resetData(final io.papermc.paper.datacomponent.PaperDataComponentType<?, M> type) {
|
|
+ final net.minecraft.core.component.DataComponentType<M> nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type);
|
|
+ final M nmsValue = this.handle.getItem().components().get(nms);
|
|
+ // if nmsValue is null, it will clear any set patch
|
|
+ // if nmsValue is not null, it will still clear any set patch because it will equal the default value
|
|
+ this.handle.set(nms, nmsValue);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isDataOverridden(final io.papermc.paper.datacomponent.DataComponentType type) {
|
|
+ if (this.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+ final net.minecraft.core.component.DataComponentType<?> nms = io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type);
|
|
+ // maybe a more efficient way is to expose the "patch" map in PatchedDataComponentMap and just check if the type exists as a key
|
|
+ return !java.util.Objects.equals(this.handle.get(nms), this.handle.getPrototype().get(nms));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean matchesWithoutData(final ItemStack item, final java.util.Set<io.papermc.paper.datacomponent.DataComponentType> exclude, final boolean ignoreCount) {
|
|
+ // Extracted from base equals
|
|
+ final CraftItemStack craftStack = getCraftStack(item);
|
|
+ if (this.handle == craftStack.handle) return true;
|
|
+ if (this.handle == null || craftStack.handle == null) return false;
|
|
+ if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
|
|
+
|
|
+ net.minecraft.world.item.ItemStack left = this.handle;
|
|
+ net.minecraft.world.item.ItemStack right = craftStack.handle;
|
|
+ if (!ignoreCount && left.getCount() != right.getCount()) {
|
|
+ return false;
|
|
+ }
|
|
+ if (!left.is(right.getItem())) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // It can be assumed that the prototype is equal since the type is the same. This way all we need to check is the patch
|
|
+
|
|
+ // Fast path when excluded types is empty
|
|
+ if (exclude.isEmpty()) {
|
|
+ return left.getComponentsPatch().equals(right.getComponentsPatch());
|
|
+ }
|
|
+
|
|
+ // Collect all the NMS types into a set
|
|
+ java.util.Set<net.minecraft.core.component.DataComponentType<?>> skippingTypes = new java.util.HashSet<>(exclude.size());
|
|
+ for (io.papermc.paper.datacomponent.DataComponentType api : exclude) {
|
|
+ skippingTypes.add(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(api));
|
|
+ }
|
|
+
|
|
+ // Check the patch by first stripping excluded types and then compare the trimmed patches
|
|
+ return left.getComponentsPatch().forget(skippingTypes::contains).equals(right.getComponentsPatch().forget(skippingTypes::contains));
|
|
+ }
|
|
+
|
|
+ // Paper end - data component API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
|
|
index 1b57649d0d3db24ed32c78cf3d5ce1d9fb1353e0..b0da057ce5124cb60b6249e13d7a5771601af937 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
|
|
@@ -150,7 +150,7 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
|
|
public int getMaxStackSize() {
|
|
// Based of the material enum air is only 0, in PerMaterialTest it is also set as special case
|
|
// the item info itself would return 64
|
|
- if (this == AIR) {
|
|
+ if (false && this == AIR) { // Paper - air stacks to 64
|
|
return 0;
|
|
}
|
|
return this.item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 64);
|
|
@@ -270,4 +270,20 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
|
|
return rarity == null ? null : org.bukkit.inventory.ItemRarity.valueOf(rarity.name());
|
|
}
|
|
// Paper end - expand ItemRarity API
|
|
+ // Paper start - data component API
|
|
+ @Override
|
|
+ public <T> T getDefaultData(final io.papermc.paper.datacomponent.DataComponentType.Valued<T> type) {
|
|
+ return io.papermc.paper.datacomponent.PaperDataComponentType.convertDataComponentValue(this.item.components(), ((io.papermc.paper.datacomponent.PaperDataComponentType.ValuedImpl<T, ?>) type));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean hasDefaultData(final io.papermc.paper.datacomponent.DataComponentType type) {
|
|
+ return this.item.components().has(io.papermc.paper.datacomponent.PaperDataComponentType.bukkitToMinecraft(type));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public java.util.Set<io.papermc.paper.datacomponent.DataComponentType> getDefaultDataTypes() {
|
|
+ return io.papermc.paper.datacomponent.PaperDataComponentType.minecraftToBukkit(this.item.components().keySet());
|
|
+ }
|
|
+ // Paper end - data component API
|
|
}
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
|
|
index a944803771d514572f94b4e98a6d4435a009c078..82cb8cd1635c279326cec8454f1906ce35021dec 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
|
|
@@ -91,7 +91,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
|
|
this.safelyAddEffects(effects, false); // Paper - limit firework effects
|
|
}
|
|
|
|
- static FireworkEffect getEffect(FireworkExplosion explosion) {
|
|
+ public static FireworkEffect getEffect(FireworkExplosion explosion) { // Paper
|
|
FireworkEffect.Builder effect = FireworkEffect.builder()
|
|
.flicker(explosion.hasTwinkle())
|
|
.trail(explosion.hasTrail())
|
|
@@ -111,7 +111,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
|
|
return effect.build();
|
|
}
|
|
|
|
- static FireworkExplosion getExplosion(FireworkEffect effect) {
|
|
+ public static FireworkExplosion getExplosion(FireworkEffect effect) { // Paper
|
|
IntList colors = CraftMetaFirework.addColors(effect.getColors());
|
|
IntList fadeColors = CraftMetaFirework.addColors(effect.getFadeColors());
|
|
|
|
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ItemComponentTypesBridge b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ItemComponentTypesBridge
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..0fd276c2fdbba784c1cd95105553996b4ba2460e
|
|
--- /dev/null
|
|
+++ b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.ItemComponentTypesBridge
|
|
@@ -0,0 +1 @@
|
|
+io.papermc.paper.datacomponent.item.ItemComponentTypesBridgesImpl
|
|
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridge b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridge
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..852ab097181491735fb9ee5ee4f70e4ceeb32e6d
|
|
--- /dev/null
|
|
+++ b/src/main/resources/META-INF/services/io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridge
|
|
@@ -0,0 +1 @@
|
|
+io.papermc.paper.datacomponent.item.consumable.ConsumableTypesBridgeImpl
|
|
diff --git a/src/test/java/io/papermc/paper/datacomponent/DataComponentTypesTest.java b/src/test/java/io/papermc/paper/datacomponent/DataComponentTypesTest.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..1d707114f53e80bf278dc640c55b515d85f03120
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/datacomponent/DataComponentTypesTest.java
|
|
@@ -0,0 +1,58 @@
|
|
+package io.papermc.paper.datacomponent;
|
|
+
|
|
+import com.google.common.collect.Collections2;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.resources.ResourceLocation;
|
|
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
|
+import org.bukkit.support.RegistryHelper;
|
|
+import org.bukkit.support.environment.AllFeatures;
|
|
+import org.junit.jupiter.api.Assertions;
|
|
+import org.junit.jupiter.api.Test;
|
|
+import java.lang.reflect.Field;
|
|
+import java.util.Set;
|
|
+import java.util.function.Predicate;
|
|
+import java.util.stream.Collectors;
|
|
+
|
|
+@AllFeatures
|
|
+public class DataComponentTypesTest {
|
|
+
|
|
+ private static final Set<ResourceLocation> NOT_IN_API = Set.of(
|
|
+ ResourceLocation.parse("custom_data"),
|
|
+ ResourceLocation.parse("entity_data"),
|
|
+ ResourceLocation.parse("bees"),
|
|
+ ResourceLocation.parse("debug_stick_state"),
|
|
+ ResourceLocation.parse("block_entity_data"),
|
|
+ ResourceLocation.parse("bucket_entity_data"),
|
|
+ ResourceLocation.parse("lock"),
|
|
+ ResourceLocation.parse("creative_slot_lock")
|
|
+ );
|
|
+
|
|
+ @Test
|
|
+ public void testAllDataComponentsAreMapped() throws IllegalAccessException {
|
|
+ final Set<ResourceLocation> vanillaDataComponentTypes = new ObjectOpenHashSet<>(
|
|
+ RegistryHelper.getRegistry()
|
|
+ .lookupOrThrow(Registries.DATA_COMPONENT_TYPE)
|
|
+ .keySet()
|
|
+ );
|
|
+
|
|
+ for (final Field declaredField : DataComponentTypes.class.getDeclaredFields()) {
|
|
+ if (!DataComponentType.class.isAssignableFrom(declaredField.getType())) continue;
|
|
+
|
|
+ final DataComponentType dataComponentType = (DataComponentType) declaredField.get(null);
|
|
+ if (!vanillaDataComponentTypes.remove(CraftNamespacedKey.toMinecraft(dataComponentType.getKey()))) {
|
|
+ Assertions.fail("API defined component type " + dataComponentType.key().asMinimalString() + " is unknown to vanilla registry");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!vanillaDataComponentTypes.containsAll(NOT_IN_API)) {
|
|
+ Assertions.fail("API defined data components that were marked as not-yet-implemented: " + NOT_IN_API.stream().filter(Predicate.not(vanillaDataComponentTypes::contains)).map(ResourceLocation::toString).collect(Collectors.joining(", ")));
|
|
+ }
|
|
+
|
|
+ vanillaDataComponentTypes.removeAll(NOT_IN_API);
|
|
+ if (!vanillaDataComponentTypes.isEmpty()) {
|
|
+ Assertions.fail("API did not define following vanilla data component types: " + String.join(", ", Collections2.transform(vanillaDataComponentTypes, ResourceLocation::toString)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/test/java/io/papermc/paper/item/ItemStackDataComponentEqualsTest.java b/src/test/java/io/papermc/paper/item/ItemStackDataComponentEqualsTest.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4ee0491763341232844a99aa528310a3b3dca1d5
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/item/ItemStackDataComponentEqualsTest.java
|
|
@@ -0,0 +1,92 @@
|
|
+package io.papermc.paper.item;
|
|
+
|
|
+import io.papermc.paper.datacomponent.DataComponentTypes;
|
|
+import java.util.Set;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import org.bukkit.Material;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+import org.bukkit.support.environment.AllFeatures;
|
|
+import org.junit.jupiter.api.Assertions;
|
|
+import org.junit.jupiter.api.Test;
|
|
+
|
|
+@AllFeatures
|
|
+class ItemStackDataComponentEqualsTest {
|
|
+
|
|
+ @Test
|
|
+ public void testEqual() {
|
|
+ ItemStack item1 = ItemStack.of(Material.STONE, 1);
|
|
+ item1.setData(DataComponentTypes.MAX_STACK_SIZE, 32);
|
|
+ item1.setData(DataComponentTypes.ITEM_NAME, Component.text("HI"));
|
|
+
|
|
+ ItemStack item2 = ItemStack.of(Material.STONE, 1);
|
|
+ item2.setData(DataComponentTypes.MAX_STACK_SIZE, 32);
|
|
+ item2.setData(DataComponentTypes.ITEM_NAME, Component.text("HI"));
|
|
+
|
|
+ Assertions.assertTrue(item1.matchesWithoutData(item2, Set.of()));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testEqualIgnoreComponent() {
|
|
+ ItemStack item1 = ItemStack.of(Material.STONE, 2);
|
|
+ item1.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
+
|
|
+ ItemStack item2 = ItemStack.of(Material.STONE, 1);
|
|
+ item2.setData(DataComponentTypes.MAX_STACK_SIZE, 2);
|
|
+
|
|
+ Assertions.assertFalse(item1.matchesWithoutData(item2, Set.of(DataComponentTypes.MAX_STACK_SIZE)));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testEqualIgnoreComponentAndSize() {
|
|
+ ItemStack item1 = ItemStack.of(Material.STONE, 2);
|
|
+ item1.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
+
|
|
+ ItemStack item2 = ItemStack.of(Material.STONE, 1);
|
|
+ item2.setData(DataComponentTypes.MAX_STACK_SIZE, 2);
|
|
+
|
|
+ Assertions.assertTrue(item1.matchesWithoutData(item2, Set.of(DataComponentTypes.MAX_STACK_SIZE), true));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testEqualWithoutComponent() {
|
|
+ ItemStack item1 = ItemStack.of(Material.STONE, 1);
|
|
+
|
|
+ ItemStack item2 = ItemStack.of(Material.STONE, 1);
|
|
+ item2.setData(DataComponentTypes.MAX_STACK_SIZE, 2);
|
|
+
|
|
+ Assertions.assertFalse(item1.matchesWithoutData(item2, Set.of(DataComponentTypes.WRITTEN_BOOK_CONTENT)));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testEqualRemoveComponent() {
|
|
+ ItemStack item1 = ItemStack.of(Material.STONE, 1);
|
|
+ item1.unsetData(DataComponentTypes.MAX_STACK_SIZE);
|
|
+
|
|
+ ItemStack item2 = ItemStack.of(Material.STONE, 1);
|
|
+ item2.unsetData(DataComponentTypes.MAX_STACK_SIZE);
|
|
+
|
|
+ Assertions.assertTrue(item1.matchesWithoutData(item2, Set.of()));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testEqualIncludeComponentIgnoreSize() {
|
|
+ ItemStack item1 = ItemStack.of(Material.STONE, 2);
|
|
+ item1.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
+
|
|
+ ItemStack item2 = ItemStack.of(Material.STONE, 1);
|
|
+ item2.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
+
|
|
+ Assertions.assertTrue(item1.matchesWithoutData(item2, Set.of(), true));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testAdvancedExample() {
|
|
+ ItemStack oakLeaves = ItemStack.of(Material.OAK_LEAVES, 1);
|
|
+ oakLeaves.setData(DataComponentTypes.HIDE_TOOLTIP);
|
|
+ oakLeaves.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
+
|
|
+ ItemStack otherOakLeavesItem = ItemStack.of(Material.OAK_LEAVES, 2);
|
|
+
|
|
+ Assertions.assertTrue(oakLeaves.matchesWithoutData(otherOakLeavesItem, Set.of(DataComponentTypes.HIDE_TOOLTIP, DataComponentTypes.MAX_STACK_SIZE), true));
|
|
+ }
|
|
+}
|
|
diff --git a/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java b/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..00133403f878d238fba84743b2d76a16d7a5e32f
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/item/ItemStackDataComponentTest.java
|
|
@@ -0,0 +1,416 @@
|
|
+package io.papermc.paper.item;
|
|
+
|
|
+import io.papermc.paper.datacomponent.DataComponentType;
|
|
+import io.papermc.paper.datacomponent.DataComponentTypes;
|
|
+import io.papermc.paper.datacomponent.item.ChargedProjectiles;
|
|
+import io.papermc.paper.datacomponent.item.CustomModelData;
|
|
+import io.papermc.paper.datacomponent.item.DyedItemColor;
|
|
+import io.papermc.paper.datacomponent.item.Fireworks;
|
|
+import io.papermc.paper.datacomponent.item.FoodProperties;
|
|
+import io.papermc.paper.datacomponent.item.ItemArmorTrim;
|
|
+import io.papermc.paper.datacomponent.item.ItemAttributeModifiers;
|
|
+import io.papermc.paper.datacomponent.item.ItemEnchantments;
|
|
+import io.papermc.paper.datacomponent.item.ItemLore;
|
|
+import io.papermc.paper.datacomponent.item.JukeboxPlayable;
|
|
+import io.papermc.paper.datacomponent.item.MapId;
|
|
+import io.papermc.paper.datacomponent.item.MapItemColor;
|
|
+import io.papermc.paper.datacomponent.item.PotDecorations;
|
|
+import io.papermc.paper.datacomponent.item.Tool;
|
|
+import io.papermc.paper.datacomponent.item.Unbreakable;
|
|
+import io.papermc.paper.registry.RegistryAccess;
|
|
+import io.papermc.paper.registry.RegistryKey;
|
|
+import io.papermc.paper.registry.set.RegistrySet;
|
|
+import io.papermc.paper.registry.tag.TagKey;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import java.util.function.BiConsumer;
|
|
+import java.util.function.Function;
|
|
+import net.kyori.adventure.key.Key;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.util.TriState;
|
|
+import net.minecraft.core.component.DataComponents;
|
|
+import net.minecraft.core.registries.Registries;
|
|
+import net.minecraft.world.item.EitherHolder;
|
|
+import net.minecraft.world.item.Items;
|
|
+import net.minecraft.world.item.JukeboxSongs;
|
|
+import org.bukkit.Color;
|
|
+import org.bukkit.FireworkEffect;
|
|
+import org.bukkit.JukeboxSong;
|
|
+import org.bukkit.Material;
|
|
+import org.bukkit.NamespacedKey;
|
|
+import org.bukkit.Registry;
|
|
+import org.bukkit.attribute.Attribute;
|
|
+import org.bukkit.attribute.AttributeModifier;
|
|
+import org.bukkit.block.BlockState;
|
|
+import org.bukkit.block.BlockType;
|
|
+import org.bukkit.block.DecoratedPot;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.enchantments.Enchantment;
|
|
+import org.bukkit.inventory.EquipmentSlotGroup;
|
|
+import org.bukkit.inventory.ItemFlag;
|
|
+import org.bukkit.inventory.ItemRarity;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+import org.bukkit.inventory.ItemType;
|
|
+import org.bukkit.inventory.meta.ArmorMeta;
|
|
+import org.bukkit.inventory.meta.BlockStateMeta;
|
|
+import org.bukkit.inventory.meta.CrossbowMeta;
|
|
+import org.bukkit.inventory.meta.Damageable;
|
|
+import org.bukkit.inventory.meta.FireworkMeta;
|
|
+import org.bukkit.inventory.meta.ItemMeta;
|
|
+import org.bukkit.inventory.meta.KnowledgeBookMeta;
|
|
+import org.bukkit.inventory.meta.LeatherArmorMeta;
|
|
+import org.bukkit.inventory.meta.MapMeta;
|
|
+import org.bukkit.inventory.meta.Repairable;
|
|
+import org.bukkit.inventory.meta.components.FoodComponent;
|
|
+import org.bukkit.inventory.meta.components.JukeboxPlayableComponent;
|
|
+import org.bukkit.inventory.meta.components.ToolComponent;
|
|
+import org.bukkit.inventory.meta.trim.ArmorTrim;
|
|
+import org.bukkit.inventory.meta.trim.TrimMaterial;
|
|
+import org.bukkit.inventory.meta.trim.TrimPattern;
|
|
+import org.bukkit.support.RegistryHelper;
|
|
+import org.bukkit.support.environment.AllFeatures;
|
|
+import org.junit.jupiter.api.Assertions;
|
|
+import org.junit.jupiter.api.Test;
|
|
+
|
|
+@AllFeatures
|
|
+class ItemStackDataComponentTest {
|
|
+
|
|
+ @Test
|
|
+ void testMaxStackSize() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.MAX_STACK_SIZE, 32, ItemMeta.class, ItemMeta::getMaxStackSize, ItemMeta::setMaxStackSize);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testMaxDamage() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.MAX_DAMAGE, 120, Damageable.class, Damageable::getMaxDamage, Damageable::setMaxDamage);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testDamage() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.DAMAGE, 120, Damageable.class, Damageable::getDamage, Damageable::setDamage);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testUnbreakable() {
|
|
+ final ItemStack stack = new ItemStack(Material.STONE);
|
|
+ stack.setData(DataComponentTypes.UNBREAKABLE, Unbreakable.unbreakable().showInTooltip(false).build());
|
|
+
|
|
+ Assertions.assertTrue(stack.getItemMeta().isUnbreakable());
|
|
+ Assertions.assertTrue(stack.getItemMeta().getItemFlags().contains(ItemFlag.HIDE_UNBREAKABLE));
|
|
+ stack.unsetData(DataComponentTypes.UNBREAKABLE);
|
|
+ Assertions.assertFalse(stack.getItemMeta().isUnbreakable());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testHideAdditionalTooltip() {
|
|
+ final ItemStack stack = new ItemStack(Material.STONE);
|
|
+ stack.setData(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP);
|
|
+
|
|
+ Assertions.assertTrue(stack.getItemMeta().getItemFlags().contains(ItemFlag.HIDE_ADDITIONAL_TOOLTIP));
|
|
+ stack.unsetData(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP);
|
|
+ Assertions.assertFalse(stack.getItemMeta().getItemFlags().contains(ItemFlag.HIDE_ADDITIONAL_TOOLTIP));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testHideTooltip() {
|
|
+ ItemStack stack = new ItemStack(Material.STONE);
|
|
+ stack.setData(DataComponentTypes.HIDE_TOOLTIP);
|
|
+
|
|
+ Assertions.assertEquals(stack.getItemMeta().isHideTooltip(), stack.hasData(DataComponentTypes.HIDE_TOOLTIP));
|
|
+ Assertions.assertTrue(stack.getItemMeta().isHideTooltip());
|
|
+ stack.unsetData(DataComponentTypes.HIDE_TOOLTIP);
|
|
+ Assertions.assertFalse(stack.getItemMeta().isHideTooltip());
|
|
+ stack = new ItemStack(Material.STONE);
|
|
+
|
|
+ stack.unsetData(DataComponentTypes.HIDE_TOOLTIP);
|
|
+ Assertions.assertFalse(stack.getItemMeta().isHideTooltip());
|
|
+ Assertions.assertEquals(stack.getItemMeta().isHideTooltip(), stack.hasData(DataComponentTypes.HIDE_TOOLTIP));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testRepairCost() {
|
|
+ final ItemStack stack = new ItemStack(Material.STONE);
|
|
+ testWithMeta(stack, DataComponentTypes.REPAIR_COST, 120, Repairable.class, Repairable::getRepairCost, Repairable::setRepairCost);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testCustomName() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.CUSTOM_NAME, Component.text("HELLO!!!!!!"), ItemMeta.class, ItemMeta::displayName, ItemMeta::displayName);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testItemName() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.ITEM_NAME, Component.text("HELLO!!!!!! ITEM NAME"), ItemMeta.class, ItemMeta::itemName, ItemMeta::itemName);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testItemLore() {
|
|
+ List<Component> list = List.of(Component.text("1"), Component.text("2"));
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.LORE, ItemLore.lore().lines(list).build(), ItemLore::lines, ItemMeta.class, ItemMeta::lore, ItemMeta::lore);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testItemRarity() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.RARITY, ItemRarity.RARE, ItemMeta.class, ItemMeta::getRarity, ItemMeta::setRarity);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testItemEnchantments() {
|
|
+ final ItemStack stack = new ItemStack(Material.STONE);
|
|
+ Map<Enchantment, Integer> enchantmentIntegerMap = Map.of(Enchantment.SOUL_SPEED, 1);
|
|
+ stack.setData(DataComponentTypes.ENCHANTMENTS, ItemEnchantments.itemEnchantments(enchantmentIntegerMap, false));
|
|
+
|
|
+ Assertions.assertTrue(stack.getItemMeta().hasItemFlag(ItemFlag.HIDE_ENCHANTS));
|
|
+ Assertions.assertEquals(1, stack.getItemMeta().getEnchantLevel(Enchantment.SOUL_SPEED));
|
|
+ Assertions.assertEquals(stack.getItemMeta().getEnchants(), enchantmentIntegerMap);
|
|
+ stack.unsetData(DataComponentTypes.ENCHANTMENTS);
|
|
+ Assertions.assertTrue(stack.getItemMeta().getEnchants().isEmpty());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testItemAttributes() {
|
|
+ final ItemStack stack = new ItemStack(Material.STONE);
|
|
+ AttributeModifier modifier = new AttributeModifier(NamespacedKey.minecraft("test"), 5, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.ANY);
|
|
+ stack.setData(DataComponentTypes.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.itemAttributes().showInTooltip(false).addModifier(Attribute.ATTACK_DAMAGE, modifier).build());
|
|
+
|
|
+ Assertions.assertTrue(stack.getItemMeta().hasItemFlag(ItemFlag.HIDE_ATTRIBUTES));
|
|
+ Assertions.assertEquals(modifier, ((List<AttributeModifier>) stack.getItemMeta().getAttributeModifiers(Attribute.ATTACK_DAMAGE)).getFirst());
|
|
+ stack.unsetData(DataComponentTypes.ATTRIBUTE_MODIFIERS);
|
|
+ Assertions.assertNull(stack.getItemMeta().getAttributeModifiers());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testLegacyCustomModelData() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData().addFloat(1).build(), customModelData -> customModelData.floats().get(0).intValue(), ItemMeta.class, ItemMeta::getCustomModelData, ItemMeta::setCustomModelData);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testEnchantmentGlintOverride() {
|
|
+ testWithMeta(new ItemStack(Material.STONE), DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true, ItemMeta.class, ItemMeta::getEnchantmentGlintOverride, ItemMeta::setEnchantmentGlintOverride);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testFood() {
|
|
+ FoodProperties properties = FoodProperties.food()
|
|
+ .canAlwaysEat(true)
|
|
+ .saturation(1.3F)
|
|
+ .nutrition(1)
|
|
+ .build();
|
|
+
|
|
+ final ItemStack stack = new ItemStack(Material.CROSSBOW);
|
|
+ stack.setData(DataComponentTypes.FOOD, properties);
|
|
+
|
|
+ ItemMeta meta = stack.getItemMeta();
|
|
+ FoodComponent component = meta.getFood();
|
|
+ Assertions.assertEquals(properties.canAlwaysEat(), component.canAlwaysEat());
|
|
+ Assertions.assertEquals(properties.saturation(), component.getSaturation());
|
|
+ Assertions.assertEquals(properties.nutrition(), component.getNutrition());
|
|
+
|
|
+ stack.unsetData(DataComponentTypes.FOOD);
|
|
+ meta = stack.getItemMeta();
|
|
+ Assertions.assertFalse(meta.hasFood());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testTool() {
|
|
+ Tool properties = Tool.tool()
|
|
+ .damagePerBlock(1)
|
|
+ .defaultMiningSpeed(2F)
|
|
+ .addRules(List.of(
|
|
+ Tool.rule(
|
|
+ RegistrySet.keySetFromValues(RegistryKey.BLOCK, List.of(BlockType.STONE, BlockType.GRAVEL)),
|
|
+ 2F,
|
|
+ TriState.TRUE
|
|
+ ),
|
|
+ Tool.rule(
|
|
+ RegistryAccess.registryAccess().getRegistry(RegistryKey.BLOCK).getTag(TagKey.create(RegistryKey.BLOCK, NamespacedKey.minecraft("bamboo_blocks"))),
|
|
+ 2F,
|
|
+ TriState.TRUE
|
|
+ )
|
|
+ ))
|
|
+ .build();
|
|
+
|
|
+ final ItemStack stack = new ItemStack(Material.CROSSBOW);
|
|
+ stack.setData(DataComponentTypes.TOOL, properties);
|
|
+
|
|
+ ItemMeta meta = stack.getItemMeta();
|
|
+ ToolComponent component = meta.getTool();
|
|
+ Assertions.assertEquals(properties.damagePerBlock(), component.getDamagePerBlock());
|
|
+ Assertions.assertEquals(properties.defaultMiningSpeed(), component.getDefaultMiningSpeed());
|
|
+
|
|
+ int idx = 0;
|
|
+ for (ToolComponent.ToolRule effect : component.getRules()) {
|
|
+ Assertions.assertEquals(properties.rules().get(idx).speed(), effect.getSpeed());
|
|
+ Assertions.assertEquals(properties.rules().get(idx).correctForDrops().toBoolean(), effect.isCorrectForDrops());
|
|
+ Assertions.assertEquals(properties.rules().get(idx).blocks().resolve(Registry.BLOCK), effect.getBlocks().stream().map(Material::asBlockType).toList());
|
|
+ idx++;
|
|
+ }
|
|
+
|
|
+ stack.unsetData(DataComponentTypes.TOOL);
|
|
+ meta = stack.getItemMeta();
|
|
+ Assertions.assertFalse(meta.hasTool());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testJukeboxPlayable() {
|
|
+ JukeboxPlayable properties = JukeboxPlayable.jukeboxPlayable(JukeboxSong.MALL).build();
|
|
+
|
|
+ final ItemStack stack = new ItemStack(Material.BEEF);
|
|
+ stack.setData(DataComponentTypes.JUKEBOX_PLAYABLE, properties);
|
|
+
|
|
+ ItemMeta meta = stack.getItemMeta();
|
|
+ JukeboxPlayableComponent component = meta.getJukeboxPlayable();
|
|
+ Assertions.assertEquals(properties.jukeboxSong(), component.getSong());
|
|
+
|
|
+ stack.unsetData(DataComponentTypes.JUKEBOX_PLAYABLE);
|
|
+ meta = stack.getItemMeta();
|
|
+ Assertions.assertFalse(meta.hasJukeboxPlayable());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testDyedColor() {
|
|
+ final ItemStack stack = new ItemStack(Material.LEATHER_CHESTPLATE);
|
|
+ Color color = Color.BLUE;
|
|
+ stack.setData(DataComponentTypes.DYED_COLOR, DyedItemColor.dyedItemColor(color, false));
|
|
+
|
|
+ Assertions.assertTrue(stack.getItemMeta().hasItemFlag(ItemFlag.HIDE_DYE));
|
|
+ Assertions.assertEquals(color, ((LeatherArmorMeta) stack.getItemMeta()).getColor());
|
|
+ stack.unsetData(DataComponentTypes.DYED_COLOR);
|
|
+ Assertions.assertFalse(((LeatherArmorMeta) stack.getItemMeta()).isDyed());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testMapColor() {
|
|
+ testWithMeta(new ItemStack(Material.FILLED_MAP), DataComponentTypes.MAP_COLOR, MapItemColor.mapItemColor().color(Color.BLUE).build(), MapItemColor::color, MapMeta.class, MapMeta::getColor, MapMeta::setColor);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testMapId() {
|
|
+ testWithMeta(new ItemStack(Material.FILLED_MAP), DataComponentTypes.MAP_ID, MapId.mapId(1), MapId::id, MapMeta.class, MapMeta::getMapId, MapMeta::setMapId);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testFireworks() {
|
|
+ testWithMeta(new ItemStack(Material.FIREWORK_ROCKET), DataComponentTypes.FIREWORKS, Fireworks.fireworks(List.of(FireworkEffect.builder().build()), 1), Fireworks::effects, FireworkMeta.class, FireworkMeta::getEffects, (fireworkMeta, effects) -> {
|
|
+ fireworkMeta.clearEffects();
|
|
+ fireworkMeta.addEffects(effects);
|
|
+ });
|
|
+
|
|
+ testWithMeta(new ItemStack(Material.FIREWORK_ROCKET), DataComponentTypes.FIREWORKS, Fireworks.fireworks(List.of(FireworkEffect.builder().build()), 1), Fireworks::flightDuration, FireworkMeta.class, FireworkMeta::getPower, FireworkMeta::setPower);
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testTrim() {
|
|
+ final ItemStack stack = new ItemStack(Material.LEATHER_CHESTPLATE);
|
|
+ ItemArmorTrim armorTrim = ItemArmorTrim.itemArmorTrim(new ArmorTrim(TrimMaterial.AMETHYST, TrimPattern.BOLT), false);
|
|
+ stack.setData(DataComponentTypes.TRIM, armorTrim);
|
|
+
|
|
+ Assertions.assertTrue(stack.getItemMeta().hasItemFlag(ItemFlag.HIDE_ARMOR_TRIM));
|
|
+ Assertions.assertEquals(armorTrim.armorTrim(), ((ArmorMeta) stack.getItemMeta()).getTrim());
|
|
+ stack.unsetData(DataComponentTypes.TRIM);
|
|
+ Assertions.assertFalse(stack.getItemMeta().hasItemFlag(ItemFlag.HIDE_ARMOR_TRIM));
|
|
+ Assertions.assertFalse(((ArmorMeta) stack.getItemMeta()).hasTrim());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testChargedProjectiles() {
|
|
+ final ItemStack stack = new ItemStack(Material.CROSSBOW);
|
|
+ ItemStack projectile = new ItemStack(Material.FIREWORK_ROCKET);
|
|
+ stack.setData(DataComponentTypes.CHARGED_PROJECTILES, ChargedProjectiles.chargedProjectiles().add(projectile).build());
|
|
+
|
|
+ CrossbowMeta meta = (CrossbowMeta) stack.getItemMeta();
|
|
+ Assertions.assertEquals(meta.getChargedProjectiles().getFirst(), projectile);
|
|
+
|
|
+ stack.unsetData(DataComponentTypes.CHARGED_PROJECTILES);
|
|
+ meta = (CrossbowMeta) stack.getItemMeta();
|
|
+ Assertions.assertTrue(meta.getChargedProjectiles().isEmpty());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testPot() {
|
|
+ final ItemStack stack = new ItemStack(Material.DECORATED_POT);
|
|
+ stack.setData(DataComponentTypes.POT_DECORATIONS, PotDecorations.potDecorations().back(ItemType.DANGER_POTTERY_SHERD).build());
|
|
+
|
|
+ BlockState state = ((BlockStateMeta) stack.getItemMeta()).getBlockState();
|
|
+ DecoratedPot decoratedPot = (DecoratedPot) state;
|
|
+
|
|
+ Assertions.assertEquals(decoratedPot.getSherd(DecoratedPot.Side.BACK), Material.DANGER_POTTERY_SHERD);
|
|
+ stack.unsetData(DataComponentTypes.POT_DECORATIONS);
|
|
+ decoratedPot = (DecoratedPot) ((BlockStateMeta) stack.getItemMeta()).getBlockState();
|
|
+ Assertions.assertTrue(decoratedPot.getSherds().values().stream().allMatch((m) -> m.asItemType() == ItemType.BRICK));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testRecipes() {
|
|
+ final ItemStack stack = new ItemStack(Material.KNOWLEDGE_BOOK);
|
|
+ stack.setData(DataComponentTypes.RECIPES, List.of(Key.key("paper:fun_recipe")));
|
|
+
|
|
+ final ItemMeta itemMeta = stack.getItemMeta();
|
|
+ Assertions.assertInstanceOf(KnowledgeBookMeta.class, itemMeta);
|
|
+
|
|
+ final List<NamespacedKey> recipes = ((KnowledgeBookMeta) itemMeta).getRecipes();
|
|
+ Assertions.assertEquals(recipes, List.of(new NamespacedKey("paper", "fun_recipe")));
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testJukeboxWithEitherKey() {
|
|
+ final ItemStack apiStack = CraftItemStack.asBukkitCopy(new net.minecraft.world.item.ItemStack(Items.MUSIC_DISC_5));
|
|
+ final JukeboxPlayable data = apiStack.getData(DataComponentTypes.JUKEBOX_PLAYABLE);
|
|
+
|
|
+ Assertions.assertNotNull(data);
|
|
+ Assertions.assertEquals(JukeboxSong.FIVE, data.jukeboxSong());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ void testJukeboxWithEitherHolder() {
|
|
+ final net.minecraft.world.item.ItemStack internalStack = new net.minecraft.world.item.ItemStack(Items.STONE);
|
|
+ internalStack.set(DataComponents.JUKEBOX_PLAYABLE, new net.minecraft.world.item.JukeboxPlayable(
|
|
+ new EitherHolder<>(RegistryHelper.getRegistry().lookupOrThrow(Registries.JUKEBOX_SONG).getOrThrow(JukeboxSongs.FIVE)),
|
|
+ true
|
|
+ ));
|
|
+
|
|
+ final ItemStack apiStack = CraftItemStack.asBukkitCopy(internalStack);
|
|
+ final JukeboxPlayable data = apiStack.getData(DataComponentTypes.JUKEBOX_PLAYABLE);
|
|
+
|
|
+ Assertions.assertNotNull(data);
|
|
+ Assertions.assertEquals(JukeboxSong.FIVE, data.jukeboxSong());
|
|
+ }
|
|
+
|
|
+ private static <T, M extends ItemMeta> void testWithMeta(final ItemStack stack, final DataComponentType.Valued<T> type, final T value, final Class<M> metaType, final Function<M, T> metaGetter, final BiConsumer<M, T> metaSetter) {
|
|
+ testWithMeta(stack, type, value, Function.identity(), metaType, metaGetter, metaSetter);
|
|
+ }
|
|
+
|
|
+ private static <T, M extends ItemMeta, R> void testWithMeta(final ItemStack stack, final DataComponentType.Valued<T> type, final T value, Function<T, R> mapper, final Class<M> metaType, final Function<M, R> metaGetter, final BiConsumer<M, R> metaSetter) {
|
|
+ ItemStack original = stack.clone();
|
|
+ stack.setData(type, value);
|
|
+
|
|
+ Assertions.assertEquals(value, stack.getData(type));
|
|
+
|
|
+ final ItemMeta meta = stack.getItemMeta();
|
|
+ final M typedMeta = Assertions.assertInstanceOf(metaType, meta);
|
|
+
|
|
+ Assertions.assertEquals(metaGetter.apply(typedMeta), mapper.apply(value));
|
|
+
|
|
+ // SETTING
|
|
+ metaSetter.accept(typedMeta, mapper.apply(value));
|
|
+ original.setItemMeta(typedMeta);
|
|
+ Assertions.assertEquals(value, original.getData(type));
|
|
+ }
|
|
+
|
|
+ private static <M extends ItemMeta> void testWithMeta(final ItemStack stack, final DataComponentType.NonValued type, final boolean value, final Class<M> metaType, final Function<M, Boolean> metaGetter, final BiConsumer<M, Boolean> metaSetter) {
|
|
+ ItemStack original = stack.clone();
|
|
+ stack.setData(type);
|
|
+
|
|
+ Assertions.assertEquals(value, stack.hasData(type));
|
|
+
|
|
+ final ItemMeta meta = stack.getItemMeta();
|
|
+ final M typedMeta = Assertions.assertInstanceOf(metaType, meta);
|
|
+
|
|
+ Assertions.assertEquals(metaGetter.apply(typedMeta), value);
|
|
+
|
|
+ // SETTING
|
|
+ metaSetter.accept(typedMeta, value);
|
|
+ original.setItemMeta(typedMeta);
|
|
+ Assertions.assertEquals(value, original.hasData(type));
|
|
+ }
|
|
+}
|
|
diff --git a/src/test/java/io/papermc/paper/item/MetaComparisonTest.java b/src/test/java/io/papermc/paper/item/MetaComparisonTest.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7cda79980729770695451adcd03b1886f60d86e3
|
|
--- /dev/null
|
|
+++ b/src/test/java/io/papermc/paper/item/MetaComparisonTest.java
|
|
@@ -0,0 +1,281 @@
|
|
+package io.papermc.paper.item;
|
|
+
|
|
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
|
+import com.destroystokyo.paper.profile.PlayerProfile;
|
|
+import java.util.UUID;
|
|
+import java.util.function.Consumer;
|
|
+import net.kyori.adventure.text.Component;
|
|
+import net.kyori.adventure.text.event.HoverEvent;
|
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.ChatColor;
|
|
+import org.bukkit.Color;
|
|
+import org.bukkit.Material;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemFactory;
|
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
+import org.bukkit.enchantments.Enchantment;
|
|
+import org.bukkit.inventory.ItemFactory;
|
|
+import org.bukkit.inventory.ItemStack;
|
|
+import org.bukkit.inventory.meta.BookMeta;
|
|
+import org.bukkit.inventory.meta.ItemMeta;
|
|
+import org.bukkit.inventory.meta.PotionMeta;
|
|
+import org.bukkit.inventory.meta.SkullMeta;
|
|
+import org.bukkit.potion.PotionEffect;
|
|
+import org.bukkit.potion.PotionEffectType;
|
|
+import org.bukkit.support.environment.AllFeatures;
|
|
+import org.junit.jupiter.api.Assertions;
|
|
+import org.junit.jupiter.api.Disabled;
|
|
+import org.junit.jupiter.api.Test;
|
|
+
|
|
+// TODO: This should technically be used to compare legacy meta vs the newly implemented
|
|
+@AllFeatures
|
|
+public class MetaComparisonTest {
|
|
+
|
|
+ private static final ItemFactory FACTORY = CraftItemFactory.instance();
|
|
+
|
|
+ @Test
|
|
+ public void testMetaApplication() {
|
|
+ ItemStack itemStack = new ItemStack(Material.STONE);
|
|
+
|
|
+ ItemMeta meta = itemStack.getItemMeta();
|
|
+ meta.setCustomModelData(1);
|
|
+
|
|
+ ItemMeta converted = FACTORY.asMetaFor(meta, Material.GOLD_INGOT);
|
|
+ Assertions.assertEquals(converted.getCustomModelData(), meta.getCustomModelData());
|
|
+
|
|
+ ItemMeta convertedAdvanced = FACTORY.asMetaFor(meta, Material.PLAYER_HEAD);
|
|
+ Assertions.assertEquals(convertedAdvanced.getCustomModelData(), meta.getCustomModelData());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testMetaApplicationDowngrading() {
|
|
+ ItemStack itemStack = new ItemStack(Material.PLAYER_HEAD);
|
|
+ PlayerProfile profile = Bukkit.createProfile("Owen1212055");
|
|
+
|
|
+ SkullMeta meta = (SkullMeta) itemStack.getItemMeta();
|
|
+ meta.setPlayerProfile(profile);
|
|
+
|
|
+ SkullMeta converted = (SkullMeta) FACTORY.asMetaFor(meta, Material.PLAYER_HEAD);
|
|
+ Assertions.assertEquals(converted.getPlayerProfile(), meta.getPlayerProfile());
|
|
+
|
|
+ SkullMeta downgraded = (SkullMeta) FACTORY.asMetaFor(FACTORY.asMetaFor(meta, Material.STONE), Material.PLAYER_HEAD);
|
|
+ Assertions.assertNull(downgraded.getPlayerProfile());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testMetaApplicationDowngradingPotion() {
|
|
+ ItemStack itemStack = new ItemStack(Material.POTION);
|
|
+ Color color = Color.BLUE;
|
|
+
|
|
+ PotionMeta meta = (PotionMeta) itemStack.getItemMeta();
|
|
+ meta.setColor(color);
|
|
+
|
|
+ PotionMeta converted = (PotionMeta) FACTORY.asMetaFor(meta, Material.POTION);
|
|
+ Assertions.assertEquals(converted.getColor(), color);
|
|
+
|
|
+ PotionMeta downgraded = (PotionMeta) FACTORY.asMetaFor(FACTORY.asMetaFor(meta, Material.STONE), Material.POTION);
|
|
+ Assertions.assertNull(downgraded.getColor());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testNullMeta() {
|
|
+ ItemStack itemStack = new ItemStack(Material.AIR);
|
|
+
|
|
+ Assertions.assertFalse(itemStack.hasItemMeta());
|
|
+ Assertions.assertNull(itemStack.getItemMeta());
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testPotionMeta() {
|
|
+ PotionEffect potionEffect = new PotionEffect(PotionEffectType.SPEED, 10, 10, false);
|
|
+ ItemStack nmsItemStack = new ItemStack(Material.POTION, 1);
|
|
+
|
|
+ testSetAndGet(nmsItemStack,
|
|
+ (meta) -> ((PotionMeta) meta).addCustomEffect(potionEffect, true),
|
|
+ (meta) -> Assertions.assertEquals(potionEffect, ((PotionMeta) meta).getCustomEffects().getFirst())
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testEnchantment() {
|
|
+ ItemStack stack = new ItemStack(Material.STICK, 1);
|
|
+
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> Assertions.assertTrue(meta.addEnchant(Enchantment.SHARPNESS, 1, true)),
|
|
+ (meta) -> Assertions.assertEquals(1, meta.getEnchantLevel(Enchantment.SHARPNESS))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ @Disabled
|
|
+ public void testPlayerHead() {
|
|
+ PlayerProfile profile = new CraftPlayerProfile(UUID.randomUUID(), "Owen1212055");
|
|
+ ItemStack stack = new ItemStack(Material.PLAYER_HEAD, 1);
|
|
+
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((SkullMeta) meta).setPlayerProfile(profile),
|
|
+ (meta) -> {
|
|
+ Assertions.assertTrue(((SkullMeta) meta).hasOwner());
|
|
+ Assertions.assertEquals(profile, ((SkullMeta) meta).getPlayerProfile());
|
|
+ }
|
|
+ );
|
|
+
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((SkullMeta) meta).setOwner("Owen1212055"),
|
|
+ (meta) -> {
|
|
+ Assertions.assertTrue(((SkullMeta) meta).hasOwner());
|
|
+ Assertions.assertEquals("Owen1212055", ((SkullMeta) meta).getOwner());
|
|
+ }
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testBookMetaAuthor() {
|
|
+ ItemStack stack = new ItemStack(Material.WRITTEN_BOOK, 1);
|
|
+
|
|
+ // Legacy string
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).setAuthor("Owen1212055"),
|
|
+ (meta) -> Assertions.assertEquals("Owen1212055", ((BookMeta) meta).getAuthor())
|
|
+ );
|
|
+
|
|
+ // Component Colored
|
|
+ Component coloredName = Component.text("Owen1212055", NamedTextColor.DARK_GRAY);
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).author(coloredName),
|
|
+ (meta) -> Assertions.assertEquals(coloredName, ((BookMeta) meta).author())
|
|
+ );
|
|
+
|
|
+ // Simple text
|
|
+ Component name = Component.text("Owen1212055");
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).author(name),
|
|
+ (meta) -> Assertions.assertEquals(name, ((BookMeta) meta).author())
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testBookMetaTitle() {
|
|
+ ItemStack stack = new ItemStack(Material.WRITTEN_BOOK, 1);
|
|
+
|
|
+ // Legacy string
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).setTitle("Owen1212055"),
|
|
+ (meta) -> Assertions.assertEquals("Owen1212055", ((BookMeta) meta).getTitle())
|
|
+ );
|
|
+
|
|
+ // Component Colored
|
|
+ Component coloredName = Component.text("Owen1212055", NamedTextColor.DARK_GRAY);
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).title(coloredName),
|
|
+ (meta) -> Assertions.assertEquals(coloredName, ((BookMeta) meta).title())
|
|
+ );
|
|
+
|
|
+ // Simple text
|
|
+ Component name = Component.text("Owen1212055");
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).title(name),
|
|
+ (meta) -> Assertions.assertEquals(name, ((BookMeta) meta).title())
|
|
+ );
|
|
+ }
|
|
+
|
|
+
|
|
+ @Test
|
|
+ public void testWriteableBookPages() {
|
|
+ ItemStack stack = new ItemStack(Material.WRITABLE_BOOK, 1);
|
|
+
|
|
+ // Writeable books are serialized as plain text, but has weird legacy color support.
|
|
+ // So, we need to test to make sure that all works here.
|
|
+
|
|
+ // Legacy string
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPage("Owen1212055"),
|
|
+ (meta) -> Assertions.assertEquals("Owen1212055", ((BookMeta) meta).getPage(1))
|
|
+ );
|
|
+
|
|
+ // Legacy string colored
|
|
+ String translatedLegacy = ChatColor.translateAlternateColorCodes('&', "&7Owen1212055");
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPage(translatedLegacy),
|
|
+ (meta) -> Assertions.assertEquals(translatedLegacy, ((BookMeta) meta).getPage(1))
|
|
+ );
|
|
+
|
|
+ // Component Colored
|
|
+ Component coloredName = Component.text("Owen1212055", NamedTextColor.DARK_GRAY);
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPages(coloredName),
|
|
+ (meta) -> Assertions.assertEquals(coloredName, ((BookMeta) meta).page(1))
|
|
+ );
|
|
+
|
|
+ // Simple text
|
|
+ Component name = Component.text("Owen1212055");
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPages(name),
|
|
+ (meta) -> Assertions.assertEquals(name, ((BookMeta) meta).page(1))
|
|
+ );
|
|
+
|
|
+ // Simple text + hover... should NOT be saved
|
|
+ // As this is plain text
|
|
+ Component nameWithHover = Component.text("Owen1212055")
|
|
+ .hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text("Hover")));
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPages(nameWithHover),
|
|
+ (meta) -> Assertions.assertEquals(name, ((BookMeta) meta).page(1))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ @Test
|
|
+ public void testWrittenBookPages() {
|
|
+ ItemStack stack = new ItemStack(Material.WRITTEN_BOOK, 1);
|
|
+
|
|
+ // Writeable books are serialized as plain text, but has weird legacy color support.
|
|
+ // So, we need to test to make sure that all works here.
|
|
+
|
|
+ // Legacy string
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPage("Owen1212055"),
|
|
+ (meta) -> Assertions.assertEquals("Owen1212055", ((BookMeta) meta).getPage(1))
|
|
+ );
|
|
+
|
|
+ // Legacy string colored
|
|
+ String translatedLegacy = ChatColor.translateAlternateColorCodes('&', "&7Owen1212055");
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPage(translatedLegacy),
|
|
+ (meta) -> Assertions.assertEquals(translatedLegacy, ((BookMeta) meta).getPage(1))
|
|
+ );
|
|
+
|
|
+ // Component Colored
|
|
+ Component coloredName = Component.text("Owen1212055", NamedTextColor.DARK_GRAY);
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPages(coloredName),
|
|
+ (meta) -> Assertions.assertEquals(coloredName, ((BookMeta) meta).page(1))
|
|
+ );
|
|
+
|
|
+ // Simple text
|
|
+ Component name = Component.text("Owen1212055");
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPages(name),
|
|
+ (meta) -> Assertions.assertEquals(name, ((BookMeta) meta).page(1))
|
|
+ );
|
|
+
|
|
+ // Simple text + hover... should be saved
|
|
+ Component nameWithHover = Component.text("Owen1212055")
|
|
+ .hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text("Hover")));
|
|
+ testSetAndGet(stack,
|
|
+ (meta) -> ((BookMeta) meta).addPages(nameWithHover),
|
|
+ (meta) -> Assertions.assertEquals(nameWithHover, ((BookMeta) meta).page(1))
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private void testSetAndGet(ItemStack itemStack, Consumer<ItemMeta> set, Consumer<ItemMeta> get) {
|
|
+ ItemMeta craftMeta = CraftItemStack.getItemMeta(CraftItemStack.asNMSCopy(itemStack)); // TODO: This should be converted to use the old meta when this is added.
|
|
+ ItemMeta paperMeta = CraftItemStack.getItemMeta(CraftItemStack.asNMSCopy(itemStack));
|
|
+ // Test craft meta
|
|
+ set.accept(craftMeta);
|
|
+ get.accept(craftMeta);
|
|
+
|
|
+ // Test paper meta
|
|
+ set.accept(paperMeta);
|
|
+ get.accept(paperMeta);
|
|
+ }
|
|
+}
|
|
diff --git a/src/test/java/org/bukkit/PerMaterialTest.java b/src/test/java/org/bukkit/PerMaterialTest.java
|
|
index 629fccec144b5d66addc0e8258cde90e81904e1c..6961730365da9083e8963200ecc5f85dbc654f35 100644
|
|
--- a/src/test/java/org/bukkit/PerMaterialTest.java
|
|
+++ b/src/test/java/org/bukkit/PerMaterialTest.java
|
|
@@ -101,17 +101,13 @@ public class PerMaterialTest {
|
|
|
|
final ItemStack bukkit = new ItemStack(material);
|
|
final CraftItemStack craft = CraftItemStack.asCraftCopy(bukkit);
|
|
- if (material == Material.AIR) {
|
|
- final int MAX_AIR_STACK = 0 /* Why can't I hold all of these AIR? */;
|
|
- assertThat(material.getMaxStackSize(), is(MAX_AIR_STACK));
|
|
- assertThat(bukkit.getMaxStackSize(), is(MAX_AIR_STACK));
|
|
- assertThat(craft.getMaxStackSize(), is(MAX_AIR_STACK));
|
|
- } else {
|
|
+
|
|
+ // Paper - remove air exception
|
|
int max = CraftMagicNumbers.getItem(material).components().getOrDefault(DataComponents.MAX_STACK_SIZE, 64);
|
|
assertThat(material.getMaxStackSize(), is(max));
|
|
assertThat(bukkit.getMaxStackSize(), is(max));
|
|
assertThat(craft.getMaxStackSize(), is(max));
|
|
- }
|
|
+ // Paper - remove air exception
|
|
}
|
|
|
|
@ParameterizedTest
|
|
diff --git a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
|
|
index b717a5ffa567781b0687bbe238b62844214db284..dc5fadb3d98b443df54b554168d60fe0b0226664 100644
|
|
--- a/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
|
|
+++ b/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
|
|
@@ -100,6 +100,7 @@ public class RegistriesArgumentProvider implements ArgumentsProvider {
|
|
register(RegistryKey.MAP_DECORATION_TYPE, MapCursor.Type.class, Registries.MAP_DECORATION_TYPE, CraftMapCursor.CraftType.class, MapDecorationType.class);
|
|
register(RegistryKey.BANNER_PATTERN, PatternType.class, Registries.BANNER_PATTERN, CraftPatternType.class, BannerPattern.class);
|
|
register(RegistryKey.MENU, MenuType.class, Registries.MENU, CraftMenuType.class, net.minecraft.world.inventory.MenuType.class);
|
|
+ register(RegistryKey.DATA_COMPONENT_TYPE, io.papermc.paper.datacomponent.DataComponentType.class, Registries.DATA_COMPONENT_TYPE, io.papermc.paper.datacomponent.PaperDataComponentType.class, net.minecraft.core.component.DataComponentType.class);
|
|
}
|
|
|
|
private static void register(RegistryKey registryKey, Class bukkit, ResourceKey registry, Class craft, Class minecraft) { // Paper
|