From ade46cc79fefdfec66fde294ebfdde687c501b04 Mon Sep 17 00:00:00 2001 From: Doc Date: Sun, 16 Feb 2025 09:02:19 +1100 Subject: [PATCH] SPIGOT-7942: Add Consumable Component --- .../inventory/CraftMetaFirework.java | 4 +- .../craftbukkit/inventory/CraftMetaItem.java | 82 +++++++---- .../craftbukkit/inventory/ItemMetaKey.java | 31 ++++ .../consumable/CraftConsumableComponent.java | 137 ++++++++++++++++++ .../effects/CraftConsumableApplyEffects.java | 85 +++++++++++ .../effects/CraftConsumableClearEffects.java | 21 +++ .../effects/CraftConsumableEffect.java | 49 +++++++ .../effects/CraftConsumablePlaySound.java | 41 ++++++ .../effects/CraftConsumableRemoveEffect.java | 76 ++++++++++ .../CraftConsumableTeleportRandomly.java | 49 +++++++ 10 files changed, 541 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/ItemMetaKey.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/CraftConsumableComponent.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableApplyEffects.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableClearEffects.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableEffect.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumablePlaySound.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableRemoveEffect.java create mode 100644 src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableTeleportRandomly.java diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java index 681aed86b..ee0ab6fff 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java @@ -17,8 +17,8 @@ import org.bukkit.Color; import org.bukkit.FireworkEffect; import org.bukkit.FireworkEffect.Type; import org.bukkit.configuration.serialization.DelegateDeserialization; -import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; -import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific.To; +import org.bukkit.craftbukkit.inventory.ItemMetaKey.Specific; +import org.bukkit.craftbukkit.inventory.ItemMetaKey.Specific.To; import org.bukkit.inventory.meta.FireworkMeta; @DelegateDeserialization(SerializableMeta.class) diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java index 8bc8cb093..ce3880486 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -14,10 +14,6 @@ import com.mojang.serialization.DynamicOps; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -57,6 +53,7 @@ import net.minecraft.nbt.SnbtPrinterTagVisitor; import net.minecraft.network.chat.IChatBaseComponent; import net.minecraft.resources.MinecraftKey; import net.minecraft.server.MinecraftServer; +import net.minecraft.sounds.SoundEffects; import net.minecraft.tags.TagKey; import net.minecraft.util.Unit; import net.minecraft.world.entity.EnumItemSlot; @@ -65,9 +62,11 @@ import net.minecraft.world.entity.ai.attributes.AttributeBase; import net.minecraft.world.food.FoodInfo; import net.minecraft.world.item.EitherHolder; import net.minecraft.world.item.EnumItemRarity; +import net.minecraft.world.item.ItemUseAnimation; import net.minecraft.world.item.JukeboxPlayable; import net.minecraft.world.item.JukeboxSongs; import net.minecraft.world.item.component.BlockItemStateProperties; +import net.minecraft.world.item.component.Consumable; import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomModelData; import net.minecraft.world.item.component.DamageResistant; @@ -97,13 +96,14 @@ import org.bukkit.craftbukkit.attribute.CraftAttributeInstance; import org.bukkit.craftbukkit.block.CraftBlockType; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.enchantments.CraftEnchantment; -import org.bukkit.craftbukkit.inventory.CraftMetaItem.ItemMetaKey.Specific; +import org.bukkit.craftbukkit.inventory.ItemMetaKey.Specific; import org.bukkit.craftbukkit.inventory.components.CraftCustomModelDataComponent; import org.bukkit.craftbukkit.inventory.components.CraftEquippableComponent; import org.bukkit.craftbukkit.inventory.components.CraftFoodComponent; import org.bukkit.craftbukkit.inventory.components.CraftJukeboxComponent; import org.bukkit.craftbukkit.inventory.components.CraftToolComponent; import org.bukkit.craftbukkit.inventory.components.CraftUseCooldownComponent; +import org.bukkit.craftbukkit.inventory.components.consumable.CraftConsumableComponent; import org.bukkit.craftbukkit.inventory.tags.DeprecatedCustomTagContainer; import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; @@ -128,6 +128,7 @@ 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.components.UseCooldownComponent; +import org.bukkit.inventory.meta.components.consumable.ConsumableComponent; import org.bukkit.inventory.meta.tags.CustomItemTagContainer; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.tag.DamageTypeTags; @@ -158,32 +159,6 @@ import org.bukkit.tag.DamageTypeTags; // Important: ItemMeta needs to be the first interface see #applicableTo(Material) class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { - static class ItemMetaKey { - - @Retention(RetentionPolicy.SOURCE) - @Target(ElementType.FIELD) - @interface Specific { - enum To { - BUKKIT, - NBT, - ; - } - To value(); - } - - final String BUKKIT; - final String NBT; - - ItemMetaKey(final String both) { - this(both, both); - } - - ItemMetaKey(final String nbt, final String bukkit) { - this.NBT = nbt; - this.BUKKIT = bukkit; - } - } - static final class ItemMetaKeyType extends ItemMetaKey { final DataComponentType TYPE; @@ -262,6 +237,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { @Specific(Specific.To.NBT) static final ItemMetaKeyType FOOD = new ItemMetaKeyType<>(DataComponents.FOOD, "food"); @Specific(Specific.To.NBT) + static final ItemMetaKeyType CONSUMABLE = new ItemMetaKeyType<>(DataComponents.CONSUMABLE, "consumable"); + @Specific(Specific.To.NBT) static final ItemMetaKeyType TOOL = new ItemMetaKeyType<>(DataComponents.TOOL, "tool"); @Specific(Specific.To.NBT) static final ItemMetaKeyType EQUIPPABLE = new ItemMetaKeyType<>(DataComponents.EQUIPPABLE, "equippable"); @@ -302,6 +279,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { private ItemStack useRemainder; private CraftUseCooldownComponent useCooldown; private CraftFoodComponent food; + private CraftConsumableComponent consumable; private CraftToolComponent tool; private CraftEquippableComponent equippable; private CraftJukeboxComponent jukebox; @@ -364,6 +342,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { if (meta.hasFood()) { this.food = new CraftFoodComponent(meta.food); } + if (meta.hasConsumable()) { + this.consumable = new CraftConsumableComponent(meta.consumable); + } if (meta.hasTool()) { this.tool = new CraftToolComponent(meta.tool); } @@ -470,6 +451,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { getOrEmpty(tag, FOOD).ifPresent((foodInfo) -> { food = new CraftFoodComponent(foodInfo); }); + getOrEmpty(tag, CONSUMABLE).ifPresent((consumableInfo) -> { + consumable = new CraftConsumableComponent(consumableInfo); + }); getOrEmpty(tag, TOOL).ifPresent((toolInfo) -> { tool = new CraftToolComponent(toolInfo); }); @@ -700,6 +684,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { setFood(food); } + CraftConsumableComponent consumable = SerializableMeta.getObject(CraftConsumableComponent.class, map, CONSUMABLE.BUKKIT, true); + if (consumable != null) { + setConsumable(consumable); + } + CraftToolComponent tool = SerializableMeta.getObject(CraftToolComponent.class, map, TOOL.BUKKIT, true); if (tool != null) { setTool(tool); @@ -993,6 +982,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { itemTag.put(FOOD, food.getHandle()); } + if (hasConsumable()) { + itemTag.put(CONSUMABLE, consumable.getHandle()); + } + if (hasTool()) { itemTag.put(TOOL, tool.getHandle()); } @@ -1103,7 +1096,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { @Overridden boolean isEmpty() { - return !(hasDisplayName() || hasItemName() || hasLocalizedName() || hasEnchants() || (lore != null) || hasCustomModelData() || hasEnchantable() || hasBlockData() || hasRepairCost() || !unhandledTags.build().isEmpty() || !removedTags.isEmpty() || !persistentDataContainer.isEmpty() || hideFlag != 0 || isHideTooltip() || hasTooltipStyle() || hasItemModel() || isUnbreakable() || hasEnchantmentGlintOverride() || isGlider() || hasDamageResistant() || hasMaxStackSize() || hasRarity() || hasUseRemainder() || hasUseCooldown() || hasFood() || hasTool() || hasJukeboxPlayable() || hasEquippable() || hasDamage() || hasMaxDamage() || hasAttributeModifiers() || customTag != null); + return !(hasDisplayName() || hasItemName() || hasLocalizedName() || hasEnchants() || (lore != null) || hasCustomModelData() || hasEnchantable() || hasBlockData() || hasRepairCost() || !unhandledTags.build().isEmpty() || !removedTags.isEmpty() || !persistentDataContainer.isEmpty() || hideFlag != 0 || isHideTooltip() || hasTooltipStyle() || hasItemModel() || isUnbreakable() || hasEnchantmentGlintOverride() || isGlider() || hasDamageResistant() || hasMaxStackSize() || hasRarity() || hasUseRemainder() || hasUseCooldown() || hasFood() || hasConsumable() || hasTool() || hasJukeboxPlayable() || hasEquippable() || hasDamage() || hasMaxDamage() || hasAttributeModifiers() || customTag != null); } @Override @@ -1530,6 +1523,21 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { this.food = (food == null) ? null : new CraftFoodComponent((CraftFoodComponent) food); } + @Override + public boolean hasConsumable() { + return this.consumable != null; + } + + @Override + public ConsumableComponent getConsumable() { + return (this.hasConsumable()) ? new CraftConsumableComponent(this.consumable) : new CraftConsumableComponent(new Consumable(Consumable.DEFAULT_CONSUME_SECONDS, ItemUseAnimation.EAT, SoundEffects.GENERIC_EAT, true, List.of())); + } + + @Override + public void setConsumable(ConsumableComponent consumable) { + this.consumable = (consumable == null) ? null : new CraftConsumableComponent((CraftConsumableComponent) consumable); + } + @Override public boolean hasTool() { return this.tool != null; @@ -1833,6 +1841,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { && (this.hasUseRemainder() ? that.hasUseRemainder() && this.useRemainder.equals(that.useRemainder) : !that.hasUseRemainder()) && (this.hasUseCooldown() ? that.hasUseCooldown() && this.useCooldown.equals(that.useCooldown) : !that.hasUseCooldown()) && (this.hasFood() ? that.hasFood() && this.food.equals(that.food) : !that.hasFood()) + && (this.hasConsumable() ? that.hasConsumable() && this.consumable.equals(that.consumable) : !that.hasConsumable()) && (this.hasTool() ? that.hasTool() && this.tool.equals(that.tool) : !that.hasTool()) && (this.hasEquippable() ? that.hasEquippable() && this.equippable.equals(that.equippable) : !that.hasEquippable()) && (this.hasJukeboxPlayable() ? that.hasJukeboxPlayable() && this.jukebox.equals(that.jukebox) : !that.hasJukeboxPlayable()) @@ -1884,6 +1893,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { hash = 61 * hash + (hasUseRemainder() ? this.useRemainder.hashCode() : 0); hash = 61 * hash + (hasUseCooldown() ? this.useCooldown.hashCode() : 0); hash = 61 * hash + (hasFood() ? this.food.hashCode() : 0); + hash = 61 * hash + (hasConsumable() ? this.consumable.hashCode() : 0); hash = 61 * hash + (hasTool() ? this.tool.hashCode() : 0); hash = 61 * hash + (hasJukeboxPlayable() ? this.jukebox.hashCode() : 0); hash = 61 * hash + (hasEquippable() ? this.equippable.hashCode() : 0); @@ -1937,6 +1947,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { if (this.hasFood()) { clone.food = new CraftFoodComponent(food); } + if (this.hasConsumable()) { + clone.consumable = new CraftConsumableComponent(consumable); + } if (this.hasTool()) { clone.tool = new CraftToolComponent(tool); } @@ -2057,6 +2070,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { builder.put(FOOD.BUKKIT, food); } + if (hasConsumable()) { + builder.put(CONSUMABLE.BUKKIT, consumable); + } + if (hasTool()) { builder.put(TOOL.BUKKIT, tool); } @@ -2251,6 +2268,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { USE_REMAINDER.TYPE, USE_COOLDOWN.TYPE, FOOD.TYPE, + CONSUMABLE.TYPE, TOOL.TYPE, EQUIPPABLE.TYPE, JUKEBOX_PLAYABLE.TYPE, diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/ItemMetaKey.java b/src/main/java/org/bukkit/craftbukkit/inventory/ItemMetaKey.java new file mode 100644 index 000000000..9eb976ae3 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/ItemMetaKey.java @@ -0,0 +1,31 @@ +package org.bukkit.craftbukkit.inventory; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class ItemMetaKey { + + @Retention(value = RetentionPolicy.SOURCE) + @Target(value = ElementType.FIELD) + @interface Specific { + + public static enum To { + BUKKIT, NBT + } + + To value(); + } + public final String BUKKIT; + public final String NBT; + + public ItemMetaKey(final String both) { + this(both, both); + } + + public ItemMetaKey(final String nbt, final String bukkit) { + this.NBT = nbt; + this.BUKKIT = bukkit; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/CraftConsumableComponent.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/CraftConsumableComponent.java new file mode 100644 index 000000000..2463ac672 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/CraftConsumableComponent.java @@ -0,0 +1,137 @@ +package org.bukkit.craftbukkit.inventory.components.consumable; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import net.minecraft.core.Holder; +import net.minecraft.sounds.SoundEffect; +import net.minecraft.sounds.SoundEffects; +import net.minecraft.world.item.ItemUseAnimation; +import net.minecraft.world.item.component.Consumable; +import net.minecraft.world.item.consume_effects.ConsumeEffect; +import org.bukkit.Sound; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.craftbukkit.CraftSound; +import org.bukkit.craftbukkit.inventory.SerializableMeta; +import org.bukkit.craftbukkit.inventory.components.consumable.effects.CraftConsumableEffect; +import org.bukkit.inventory.meta.components.consumable.ConsumableComponent; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumableEffect; + +@SerializableAs("Consumable") +public class CraftConsumableComponent implements ConsumableComponent { + + private Consumable handle; + + public CraftConsumableComponent(Consumable consumable) { + this.handle = consumable; + } + + public CraftConsumableComponent(CraftConsumableComponent consumable) { + this.handle = consumable.handle; + } + + public CraftConsumableComponent(Map map) { + Float consumeSeconds = SerializableMeta.getObject(Float.class, map, "consume-seconds", false); + Animation animation = SerializableMeta.getObject(Animation.class, map, "animation", false); + Boolean hasConsumeParticles = SerializableMeta.getBoolean(map, "has-consume-particles"); + Sound sound = SerializableMeta.getObject(Sound.class, map, "sound", false); + List consumableEffects = SerializableMeta.getList(ConsumableEffect.class, map, "effects"); + + List consumeEffects = (List) consumableEffects.stream().map(consumableEffect -> CraftConsumableEffect.bukkitToMinecraftSpecific(((CraftConsumableEffect) consumableEffect))).toList(); + + this.handle = new Consumable(consumeSeconds, CraftAnimation.bukkitToMinecraft(animation), CraftSound.bukkitToMinecraftHolder(sound), hasConsumeParticles, consumeEffects); + } + + @Override + public Map serialize() { + Map result = new LinkedHashMap<>(); + result.put("consume-seconds", this.getConsumeSeconds()); + result.put("animation", this.getAnimation()); + result.put("sound", this.getSound()); + result.put("has-consume-particles", this.hasConsumeParticles()); + result.put("effects", this.getEffects()); + + return result; + } + + public Consumable getHandle() { + return handle; + } + + @Override + public float getConsumeSeconds() { + return this.handle.consumeSeconds(); + } + + @Override + public void setConsumeSeconds(float consumeSeconds) { + handle = new Consumable(consumeSeconds, this.handle.animation(), this.handle.sound(), this.handle.hasConsumeParticles(), this.handle.onConsumeEffects()); + } + + @Override + public Animation getAnimation() { + return CraftAnimation.minecraftToBukkit(this.handle.animation()); + } + + @Override + public void setAnimation(Animation animation) { + Preconditions.checkArgument(animation != null, "Animation cannot be null"); + handle = new Consumable(this.handle.consumeSeconds(), CraftAnimation.bukkitToMinecraft(animation), this.handle.sound(), this.handle.hasConsumeParticles(), this.handle.onConsumeEffects()); + } + + @Override + public Sound getSound() { + return CraftSound.minecraftHolderToBukkit(this.handle.sound()); + } + + @Override + public void setSound(Sound sound) { + Holder soundEffectHolder = (sound != null) ? CraftSound.bukkitToMinecraftHolder(sound) : SoundEffects.GENERIC_EAT; + handle = new Consumable(this.handle.consumeSeconds(), this.handle.animation(), soundEffectHolder, this.handle.hasConsumeParticles(), this.handle.onConsumeEffects()); + } + + @Override + public boolean hasConsumeParticles() { + return this.handle.hasConsumeParticles(); + } + + @Override + public void setConsumeParticles(boolean consumeParticles) { + handle = new Consumable(this.handle.consumeSeconds(), this.handle.animation(), this.handle.sound(), consumeParticles, this.handle.onConsumeEffects()); + } + + @Override + public List getEffects() { + return this.getHandle().onConsumeEffects().stream().map(CraftConsumableEffect::minecraftToBukkitSpecific).map(o -> ((ConsumableEffect) o)).toList(); + } + + @Override + public void setEffects(List effects) { + handle = new Consumable(this.handle.consumeSeconds(), this.handle.animation(), this.handle.sound(), this.handle.hasConsumeParticles(), effects.stream().map(consumableEffect -> CraftConsumableEffect.bukkitToMinecraftSpecific(((CraftConsumableEffect) consumableEffect))).toList()); + } + + @Override + public ConsumableEffect addEffect(ConsumableEffect consumableEffect) { + List effects = new ArrayList<>(this.handle.onConsumeEffects()); + + ConsumeEffect newEffect = CraftConsumableEffect.bukkitToMinecraftSpecific(((CraftConsumableEffect) consumableEffect)); + effects.add(newEffect); + + handle = new Consumable(this.handle.consumeSeconds(), this.handle.animation(), this.handle.sound(), this.handle.hasConsumeParticles(), effects); + + return consumableEffect; + } + + public static class CraftAnimation { + + public static Animation minecraftToBukkit(ItemUseAnimation minecraft) { + return Animation.valueOf(minecraft.name()); + } + + public static ItemUseAnimation bukkitToMinecraft(Animation bukkit) { + return ItemUseAnimation.valueOf(bukkit.name()); + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableApplyEffects.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableApplyEffects.java new file mode 100644 index 000000000..1ae35ddc7 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableApplyEffects.java @@ -0,0 +1,85 @@ +package org.bukkit.craftbukkit.inventory.components.consumable.effects; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect; +import org.bukkit.craftbukkit.inventory.ItemMetaKey; +import org.bukkit.craftbukkit.inventory.SerializableMeta; +import org.bukkit.craftbukkit.potion.CraftPotionUtil; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumableApplyEffects; +import org.bukkit.potion.PotionEffect; + +public class CraftConsumableApplyEffects extends CraftConsumableEffect implements ConsumableApplyEffects { + + static final ItemMetaKey POTIONS = new ItemMetaKey("effects"); + + public CraftConsumableApplyEffects(ApplyStatusEffectsConsumeEffect consumeEffect) { + super(consumeEffect); + } + + public CraftConsumableApplyEffects(CraftConsumableApplyEffects consumeEffect) { + super(consumeEffect); + } + + public CraftConsumableApplyEffects(Map map) { + super(map); + + List effectList = new ArrayList<>(); + Iterable rawEffectTypeList = SerializableMeta.getObject(Iterable.class, map, POTIONS, true); + if (rawEffectTypeList == null) { + return; + } + + for (Object obj : rawEffectTypeList) { + Preconditions.checkArgument(obj instanceof PotionEffect, "Object (%s) in effect list is not valid", obj.getClass()); + effectList.add((PotionEffect) obj); + } + + Float probability = SerializableMeta.getObject(Float.class, map, "probability", false); + + this.handle = new ApplyStatusEffectsConsumeEffect(effectList.stream().map(CraftPotionUtil::fromBukkit).toList(), probability); + } + + @Override + public List getEffects() { + List mobEffectList = this.getHandle().effects(); + return mobEffectList.stream().map(CraftPotionUtil::toBukkit).toList(); + } + + @Override + public void setEffects(List list) { + this.handle = new ApplyStatusEffectsConsumeEffect(list.stream().map(CraftPotionUtil::fromBukkit).toList()); + } + + @Override + public PotionEffect addEffect(PotionEffect potionEffect) { + List mobEffectList = this.getHandle().effects(); + mobEffectList.add(CraftPotionUtil.fromBukkit(potionEffect)); + this.handle = new ApplyStatusEffectsConsumeEffect(mobEffectList, this.handle.probability()); + return potionEffect; + } + + @Override + public float getProbability() { + return this.getHandle().probability(); + } + + @Override + public void setProbability(float probability) { + Preconditions.checkArgument(probability >= 0.0f && probability <= 1.0f, "Probability must be between 0.0f and 1.0f but is %s", probability); + this.handle = new ApplyStatusEffectsConsumeEffect(this.getHandle().effects(), probability); + } + + @Override + public Map serialize() { + Map result = new LinkedHashMap<>(); + result.put(POTIONS.BUKKIT, ImmutableList.copyOf(this.getEffects())); + + return result; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableClearEffects.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableClearEffects.java new file mode 100644 index 000000000..45ce59b57 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableClearEffects.java @@ -0,0 +1,21 @@ +package org.bukkit.craftbukkit.inventory.components.consumable.effects; + +import java.util.Map; +import net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumableClearEffects; + +public class CraftConsumableClearEffects extends CraftConsumableEffect implements ConsumableClearEffects { + + public CraftConsumableClearEffects(ClearAllStatusEffectsConsumeEffect consumeEffect) { + super(consumeEffect); + } + + public CraftConsumableClearEffects(CraftConsumableClearEffects consumeEffect) { + super(consumeEffect); + } + + @Override + public Map serialize() { + return Map.of(); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableEffect.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableEffect.java new file mode 100644 index 000000000..370d6c227 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableEffect.java @@ -0,0 +1,49 @@ +package org.bukkit.craftbukkit.inventory.components.consumable.effects; + +import java.util.Map; +import net.minecraft.world.item.consume_effects.ApplyStatusEffectsConsumeEffect; +import net.minecraft.world.item.consume_effects.ClearAllStatusEffectsConsumeEffect; +import net.minecraft.world.item.consume_effects.ConsumeEffect; +import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect; +import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumableEffect; + +public abstract class CraftConsumableEffect implements ConsumableEffect { + + public static > T minecraftToBukkitSpecific(ConsumeEffect effect) { + if (effect instanceof ApplyStatusEffectsConsumeEffect nmsEffect) { + return ((T) new CraftConsumableApplyEffects(nmsEffect)); + } else if (effect instanceof RemoveStatusEffectsConsumeEffect nmsEffect) { + return ((T) new CraftConsumableRemoveEffect(nmsEffect)); + } else if (effect instanceof ClearAllStatusEffectsConsumeEffect nmsEffect) { + return ((T) new CraftConsumableClearEffects(nmsEffect)); + } else if (effect instanceof TeleportRandomlyConsumeEffect nmsEffect) { + return ((T) new CraftConsumableTeleportRandomly(nmsEffect)); + } else if (effect instanceof PlaySoundConsumeEffect nmsEffect) { + return ((T) new CraftConsumablePlaySound(nmsEffect)); + } + throw new IllegalStateException("Unexpected value: " + effect.getType()); + } + + public static T bukkitToMinecraftSpecific(CraftConsumableEffect effect) { + return effect.getHandle(); + } + + T handle; + + public CraftConsumableEffect(T consumeEffect) { + this.handle = consumeEffect; + } + + public CraftConsumableEffect(CraftConsumableEffect consumeEffect) { + this.handle = consumeEffect.handle; + } + + public CraftConsumableEffect(Map map) { + } + + public T getHandle() { + return handle; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumablePlaySound.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumablePlaySound.java new file mode 100644 index 000000000..3bb7f1a6f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumablePlaySound.java @@ -0,0 +1,41 @@ +package org.bukkit.craftbukkit.inventory.components.consumable.effects; + +import java.util.LinkedHashMap; +import java.util.Map; +import net.minecraft.world.item.consume_effects.PlaySoundConsumeEffect; +import org.bukkit.Sound; +import org.bukkit.craftbukkit.CraftSound; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumablePlaySound; + +public class CraftConsumablePlaySound extends CraftConsumableEffect implements ConsumablePlaySound { + + public CraftConsumablePlaySound(PlaySoundConsumeEffect consumeEffect) { + super(consumeEffect); + } + + public CraftConsumablePlaySound(CraftConsumableEffect consumeEffect) { + super(consumeEffect); + } + + public CraftConsumablePlaySound(Map map) { + super(map); + } + + @Override + public Sound getSound() { + return CraftSound.minecraftHolderToBukkit(this.getHandle().sound()); + } + + @Override + public void setSound(Sound sound) { + this.handle = new PlaySoundConsumeEffect(CraftSound.bukkitToMinecraftHolder(sound)); + } + + @Override + public Map serialize() { + Map result = new LinkedHashMap<>(); + result.put("sound", getSound()); + + return result; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableRemoveEffect.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableRemoveEffect.java new file mode 100644 index 000000000..6be95a995 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableRemoveEffect.java @@ -0,0 +1,76 @@ +package org.bukkit.craftbukkit.inventory.components.consumable.effects; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import net.minecraft.core.HolderSet; +import net.minecraft.world.item.consume_effects.RemoveStatusEffectsConsumeEffect; +import org.bukkit.craftbukkit.inventory.ItemMetaKey; +import org.bukkit.craftbukkit.inventory.SerializableMeta; +import org.bukkit.craftbukkit.potion.CraftPotionEffectType; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumableRemoveEffect; +import org.bukkit.potion.PotionEffectType; + +public class CraftConsumableRemoveEffect extends CraftConsumableEffect implements ConsumableRemoveEffect { + + static final ItemMetaKey POTION_TYPES = new ItemMetaKey("effects"); + + public CraftConsumableRemoveEffect(RemoveStatusEffectsConsumeEffect consumeEffect) { + super(consumeEffect); + } + + public CraftConsumableRemoveEffect(CraftConsumableRemoveEffect consumeEffect) { + super(consumeEffect); + } + + public CraftConsumableRemoveEffect(Map map) { + super(map); + + List effectTypeList = new ArrayList<>(); + Iterable rawEffectTypeList = SerializableMeta.getObject(Iterable.class, map, POTION_TYPES.BUKKIT, true); + if (rawEffectTypeList == null) { + return; + } + + for (Object obj : rawEffectTypeList) { + Preconditions.checkArgument(obj instanceof PotionEffectType, "Object (%s) in effect type list is not valid", obj.getClass()); + effectTypeList.add((PotionEffectType) obj); + } + + super.handle = new RemoveStatusEffectsConsumeEffect(HolderSet.direct(effectTypeList.stream().map(CraftPotionEffectType::bukkitToMinecraftHolder).collect(Collectors.toList()))); + } + + public RemoveStatusEffectsConsumeEffect getHandle() { + return (RemoveStatusEffectsConsumeEffect) super.getHandle(); + } + + @Override + public List getEffectTypes() { + return this.getHandle().effects().stream().map(CraftPotionEffectType::minecraftHolderToBukkit).collect(Collectors.toList()); + } + + @Override + public void setEffectTypes(List effectTypeList) { + this.handle = new RemoveStatusEffectsConsumeEffect(HolderSet.direct(effectTypeList.stream().map(CraftPotionEffectType::bukkitToMinecraftHolder).collect(Collectors.toList()))); + } + + @Override + public PotionEffectType addEffectType(PotionEffectType potionEffect) { + List list = this.getEffectTypes(); + list.add(potionEffect); + this.setEffectTypes(list); + return potionEffect; + } + + @Override + public Map serialize() { + Map result = new LinkedHashMap<>(); + result.put(POTION_TYPES.BUKKIT, ImmutableList.copyOf(this.getEffectTypes())); + + return result; + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableTeleportRandomly.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableTeleportRandomly.java new file mode 100644 index 000000000..e516ec906 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/consumable/effects/CraftConsumableTeleportRandomly.java @@ -0,0 +1,49 @@ +package org.bukkit.craftbukkit.inventory.components.consumable.effects; + +import java.util.LinkedHashMap; +import java.util.Map; +import net.minecraft.world.item.consume_effects.TeleportRandomlyConsumeEffect; +import org.bukkit.craftbukkit.inventory.SerializableMeta; +import org.bukkit.inventory.meta.components.consumable.effects.ConsumableTeleportRandomly; +import org.jetbrains.annotations.NotNull; + +public class CraftConsumableTeleportRandomly extends CraftConsumableEffect implements ConsumableTeleportRandomly { + + private TeleportRandomlyConsumeEffect handle; + + public CraftConsumableTeleportRandomly(TeleportRandomlyConsumeEffect consumableEffect) { + super(consumableEffect); + } + + public CraftConsumableTeleportRandomly(CraftConsumableTeleportRandomly consumableEffect) { + super(consumableEffect); + this.handle = consumableEffect.handle; + } + + public CraftConsumableTeleportRandomly(Map map) { + super(map); + + Float diameter = SerializableMeta.getObject(Float.class, map, "diameter", false); + + this.handle = new TeleportRandomlyConsumeEffect(diameter); + } + + @Override + public float getDiameter() { + return this.handle.diameter(); + } + + @Override + public void setDiameter(float diameter) { + handle = new TeleportRandomlyConsumeEffect(diameter); + } + + @NotNull + @Override + public Map serialize() { + Map result = new LinkedHashMap<>(); + result.put("diameter", getDiameter()); + + return result; + } +}