diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java index 93cc71caa..1ad99450a 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/ComponentItemWrapper.java @@ -8,13 +8,19 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; +import net.momirealms.craftengine.bukkit.util.EquipmentSlotUtils; import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.ItemWrapper; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Objects; import java.util.Optional; @@ -201,4 +207,14 @@ public class ComponentItemWrapper implements ItemWrapper { public void shrink(int amount) { count(count() - amount); } + + @Override + public void hurtAndBreak(int amount, @NotNull Player player, @Nullable EquipmentSlot slot) { + FastNMS.INSTANCE.method$ItemStack$hurtAndBreak( + this.handle, + amount, + player.serverPlayer(), + slot != null ? EquipmentSlotUtils.toNMSEquipmentSlot(slot) : null + ); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyItemWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyItemWrapper.java index 43fd07c84..d9dcfbe97 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyItemWrapper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/LegacyItemWrapper.java @@ -3,10 +3,15 @@ package net.momirealms.craftengine.bukkit.item; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; +import net.momirealms.craftengine.bukkit.util.EquipmentSlotUtils; import net.momirealms.craftengine.bukkit.util.ItemStackUtils; +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.ItemWrapper; import net.momirealms.sparrow.nbt.Tag; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class LegacyItemWrapper implements ItemWrapper { private final Object nmsStack; @@ -150,4 +155,14 @@ public class LegacyItemWrapper implements ItemWrapper { public void shrink(int amount) { this.count(count() - amount); } + + @Override + public void hurtAndBreak(int amount, @NotNull Player player, @Nullable EquipmentSlot slot) { + FastNMS.INSTANCE.method$ItemStack$hurtAndBreak( + this.nmsStack, + amount, + player.serverPlayer(), + slot != null ? EquipmentSlotUtils.toNMSEquipmentSlot(slot) : null + ); + } } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java index 6d23f2ec7..7cdccabc5 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -9,6 +9,7 @@ import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.entity.EquipmentSlot; import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionResult; import net.momirealms.craftengine.core.entity.player.Player; @@ -107,19 +108,7 @@ public class AxeItemBehavior extends ItemBehavior { player.swingHand(context.getHand()); } // shrink item amount - if (VersionHelper.isOrAbove1_20_5()) { - Object itemStack = item.getLiteralObject(); - Object serverPlayer = player.serverPlayer(); - Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND; - try { - CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot); - } catch (ReflectiveOperationException e) { - CraftEngine.instance().logger().warn("Failed to hurt itemStack", e); - } - } else { - ItemStack itemStack = item.getItem(); - itemStack.damage(1, bukkitPlayer); - } + item.hurtAndBreak(1, player, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.MAIN_HAND : EquipmentSlot.OFF_HAND); } return InteractionResult.SUCCESS_AND_CANCEL; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index 15db9d237..22996e12b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -1664,7 +1664,8 @@ public final class CoreReflections { public static final Object instance$EquipmentSlot$LEGS; public static final Object instance$EquipmentSlot$CHEST; public static final Object instance$EquipmentSlot$HEAD; -// public static final Object instance$EquipmentSlot$BODY; + public static final Object instance$EquipmentSlot$BODY; + public static final Object instance$EquipmentSlot$SADDLE; static { try { @@ -1675,7 +1676,16 @@ public final class CoreReflections { instance$EquipmentSlot$LEGS = instance$EquipmentSlot$values[3]; instance$EquipmentSlot$CHEST = instance$EquipmentSlot$values[4]; instance$EquipmentSlot$HEAD = instance$EquipmentSlot$values[5]; -// instance$EquipmentSlot$BODY = instance$EquipmentSlot$values[6]; + if (VersionHelper.isOrAbove1_20_5()) { + instance$EquipmentSlot$BODY = instance$EquipmentSlot$values[6]; + } else { + instance$EquipmentSlot$BODY = null; + } + if (VersionHelper.isOrAbove1_21_5()) { + instance$EquipmentSlot$SADDLE = instance$EquipmentSlot$values[7]; + } else { + instance$EquipmentSlot$SADDLE = null; + } } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to init EquipmentSlot", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EquipmentSlotUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EquipmentSlotUtils.java new file mode 100644 index 000000000..d334f9f47 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EquipmentSlotUtils.java @@ -0,0 +1,27 @@ +package net.momirealms.craftengine.bukkit.util; + +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.core.entity.EquipmentSlot; + +public final class EquipmentSlotUtils { + private EquipmentSlotUtils() {} + + public static Object toNMSEquipmentSlot(EquipmentSlot equipmentSlot) { + return switch (equipmentSlot) { + case MAIN_HAND -> CoreReflections.instance$EquipmentSlot$MAINHAND; + case OFF_HAND -> CoreReflections.instance$EquipmentSlot$OFFHAND; + case FEET -> CoreReflections.instance$EquipmentSlot$FEET; + case LEGS -> CoreReflections.instance$EquipmentSlot$LEGS; + case CHEST -> CoreReflections.instance$EquipmentSlot$CHEST; + case HEAD -> CoreReflections.instance$EquipmentSlot$HEAD; + case BODY -> CoreReflections.instance$EquipmentSlot$BODY; + case SADDLE -> CoreReflections.instance$EquipmentSlot$SADDLE; + }; + } + + public static EquipmentSlot fromNMSEquipmentSlot(Object equipmentSlot) { + Enum directionEnum = (Enum) equipmentSlot; + int index = directionEnum.ordinal(); + return EquipmentSlot.values()[index]; + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java b/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java index 55c343103..b0d53d96c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/EquipmentSlot.java @@ -1,12 +1,12 @@ package net.momirealms.craftengine.core.entity; public enum EquipmentSlot { - HEAD, - CHEST, - LEGS, - FEET, - BODY, MAIN_HAND, OFF_HAND, + FEET, + LEGS, + CHEST, + HEAD, + BODY, SADDLE } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java index e219878b1..0d99ac953 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItem.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.core.item; import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.attribute.AttributeModifier; +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; @@ -500,4 +502,9 @@ public class AbstractItem, I> implements Item { public void shrink(int amount) { this.item.shrink(amount); } + + @Override + public void hurtAndBreak(int amount, @NotNull Player player, @Nullable EquipmentSlot slot) { + this.item.hurtAndBreak(amount, player, slot); + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java index 9c33b50a2..d14825f97 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/Item.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/Item.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.core.item; import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; import net.momirealms.craftengine.core.attribute.AttributeModifier; +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.FireworkExplosion; @@ -214,6 +216,8 @@ public interface Item { void shrink(int amount); + void hurtAndBreak(int amount, @NotNull Player player, @Nullable EquipmentSlot slot); + default Item transmuteCopy(Key another) { return transmuteCopy(another, this.count()); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemWrapper.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemWrapper.java index 896967727..ca1d2da73 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemWrapper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemWrapper.java @@ -1,5 +1,10 @@ package net.momirealms.craftengine.core.item; +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.player.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + public interface ItemWrapper { I getItem(); @@ -13,4 +18,6 @@ public interface ItemWrapper { ItemWrapper copyWithCount(int count); void shrink(int amount); + + void hurtAndBreak(int amount, @NotNull Player player, @Nullable EquipmentSlot slot); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java index 16cadd38a..eaf5d0e17 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java @@ -53,6 +53,7 @@ public class EventFunctions { register(CommonFunctions.IF_ELSE, new IfElseFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap)); register(CommonFunctions.ALTERNATIVES, new IfElseFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap)); register(CommonFunctions.WHEN, new WhenFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap)); + register(CommonFunctions.DAMAGE_ITEM, new DamageItemFunction.FactoryImpl<>(EventConditions::fromMap)); } public static void register(Key key, FunctionFactory factory) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java index a9be4559c..5cd4a5d0a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java @@ -44,4 +44,5 @@ public final class CommonFunctions { public static final Key WHEN = Key.of("craftengine:when"); public static final Key ALL_OF = Key.of("craftengine:all_of"); public static final Key DUMMY = Key.of("craftengine:dummy"); + public static final Key DAMAGE_ITEM = Key.of("craftengine:damage_item"); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java new file mode 100644 index 000000000..0d11ec816 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/DamageItemFunction.java @@ -0,0 +1,60 @@ +package net.momirealms.craftengine.core.plugin.context.function; + +import net.momirealms.craftengine.core.entity.EquipmentSlot; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.context.Condition; +import net.momirealms.craftengine.core.plugin.context.Context; +import net.momirealms.craftengine.core.plugin.context.number.NumberProvider; +import net.momirealms.craftengine.core.plugin.context.number.NumberProviders; +import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector; +import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; + +import java.util.List; +import java.util.Map; + +public class DamageItemFunction extends AbstractConditionalFunction { + private final NumberProvider amount; + + public DamageItemFunction(NumberProvider amount, List> predicates) { + super(predicates); + this.amount = amount; + } + + @Override + protected void runInternal(CTX ctx) { + Player player = ctx.getOptionalParameter(DirectContextParameters.PLAYER).orElse(null); + if (player == null) return; + Item item = ctx.getOptionalParameter(DirectContextParameters.ITEM).orElse(null); + InteractionHand hand = ctx.getOptionalParameter(DirectContextParameters.HAND).orElse(null); + if (item == null && hand != null) { + item = player.getItemInHand(hand); + } else if (item == null) { + return; + } + EquipmentSlot slot = hand == null ? null : hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAIN_HAND : EquipmentSlot.OFF_HAND; + item.hurtAndBreak(amount.getInt(ctx), player, slot); + } + + @Override + public Key type() { + return CommonFunctions.DAMAGE_ITEM; + } + + public static class FactoryImpl extends AbstractFactory { + + public FactoryImpl(java.util.function.Function, Condition> factory) { + super(factory); + } + + @Override + public Function create(Map arguments) { + NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1)); + return new DamageItemFunction<>(amount, getPredicates(arguments)); + } + } +} diff --git a/gradle.properties b/gradle.properties index fc8a9e3f3..9f0dac670 100644 --- a/gradle.properties +++ b/gradle.properties @@ -50,7 +50,7 @@ byte_buddy_version=1.17.8 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.4 -nms_helper_version=1.0.120 +nms_helper_version=1.0.121 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.34.5