diff --git a/core/build.gradle b/core/build.gradle index 9ba478520..efeefe0a4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -6,7 +6,7 @@ plugins { dependencies { implementation("org.apache.commons:commons-math4-core:4.0-beta1") implementation("com.github.ben-manes.caffeine:caffeine:3.2.2") - implementation("com.github.Chronoken:EffectLib:1da888c") + implementation("com.github.Chronoken:EffectLib:071cc3c") implementation("org.incendo:cloud-paper:2.0.0-beta.16") implementation("org.incendo:cloud-minecraft-extras:2.0.0-beta.16") implementation("org.incendo:cloud-processors-requirements:1.0.0-rc.1") diff --git a/core/src/main/java/com/nisovin/magicspells/MagicSpells.java b/core/src/main/java/com/nisovin/magicspells/MagicSpells.java index cec7b0fe1..2a757c1a8 100644 --- a/core/src/main/java/com/nisovin/magicspells/MagicSpells.java +++ b/core/src/main/java/com/nisovin/magicspells/MagicSpells.java @@ -13,7 +13,11 @@ import java.util.function.Predicate; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.annotation.Annotation; import java.net.URL; @@ -1955,20 +1959,30 @@ public static void registerEvents(final Listener listener, EventPriority customP } for (final Method method : methods) { - final EventHandler eh = method.getAnnotation(EventHandler.class); - if (eh == null) continue; - EventPriority priority = eh.priority(); + final EventHandler handler = method.getAnnotation(EventHandler.class); + if (handler == null) continue; + EventPriority priority = handler.priority(); if (hasAnnotation(method, OverridePriority.class)) priority = customPriority; - final Class checkClass = method.getParameterTypes()[0]; - if (!Event.class.isAssignableFrom(checkClass) || method.getParameterTypes().length != 1) { + final Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1 || !Event.class.isAssignableFrom(paramTypes[0])) { plugin.getLogger().severe("Wrong method arguments used for event type registered"); continue; } - final Class eventClass = checkClass.asSubclass(Event.class); + final Class eventClass = paramTypes[0].asSubclass(Event.class); method.setAccessible(true); + final MethodHandle methodHandle; + try { + MethodHandle handle = MethodHandles.lookup().unreflect(method); + if (!Modifier.isStatic(method.getModifiers())) handle = handle.bindTo(listener); + methodHandle = handle.asType(MethodType.methodType(void.class, Event.class)); + } catch (IllegalAccessException e) { + plugin.getLogger().severe("Failed to create method handle for " + method.getName() + ": " + e.getMessage()); + continue; + } + EventExecutor executor = new EventExecutor() { final String eventKey = plugin.enableProfiling ? "Event:" + listener.getClass().getName().replace("com.nisovin.magicspells.", "") + '.' + method.getName() + '(' + eventClass.getSimpleName() + ')' : null; @@ -1977,23 +1991,24 @@ public void execute(@NotNull Listener listener, @NotNull Event event) { try { if (!eventClass.isAssignableFrom(event.getClass())) return; long start = System.nanoTime(); - method.invoke(listener, event); + methodHandle.invokeExact((Event) event); if (plugin.enableProfiling) { Long total = plugin.profilingTotalTime.get(eventKey); - if (total == null) total = (long) 0; + if (total == null) total = 0L; total += System.nanoTime() - start; plugin.profilingTotalTime.put(eventKey, total); Integer runs = plugin.profilingRuns.get(eventKey); if (runs == null) runs = 0; - runs += 1; + runs++; plugin.profilingRuns.put(eventKey, runs); } - } catch (Exception ex) { - handleException(ex); + } catch (Throwable ex) { + if (ex instanceof Error e) throw e; + handleException(ex instanceof Exception exception ? exception : new RuntimeException(ex)); } } }; - Bukkit.getPluginManager().registerEvent(eventClass, listener, priority, executor, plugin, eh.ignoreCancelled()); + Bukkit.getPluginManager().registerEvent(eventClass, listener, priority, executor, plugin, handler.ignoreCancelled()); } } diff --git a/core/src/main/java/com/nisovin/magicspells/Spell.java b/core/src/main/java/com/nisovin/magicspells/Spell.java index 415e2d1f0..a3b10fe5e 100644 --- a/core/src/main/java/com/nisovin/magicspells/Spell.java +++ b/core/src/main/java/com/nisovin/magicspells/Spell.java @@ -1078,14 +1078,17 @@ public PostCastAction castSpell(LivingEntity caster, SpellCastState state, float } protected SpellCastState getCastState(LivingEntity caster) { - if (caster instanceof Player player && !MagicSpells.getSpellbook(player).canCast(this)) + if (caster instanceof Player player && !MagicSpells.getSpellbook(player).canCast(this)) { return SpellCastState.CANT_CAST; - if (worldRestrictions != null && !worldRestrictions.contains(caster.getWorld().getName())) + } + if (worldRestrictions != null && !worldRestrictions.contains(caster.getWorld().getName())) { return SpellCastState.WRONG_WORLD; - if (MagicSpells.getNoMagicZoneManager() != null && MagicSpells.getNoMagicZoneManager().willFizzle(caster, this)) - return SpellCastState.NO_MAGIC_ZONE; + } if (onCooldown(caster)) return SpellCastState.ON_COOLDOWN; if (!hasReagents(caster)) return SpellCastState.MISSING_REAGENTS; + if (MagicSpells.getNoMagicZoneManager() != null && MagicSpells.getNoMagicZoneManager().willFizzle(caster, this)) { + return SpellCastState.NO_MAGIC_ZONE; + } return SpellCastState.NORMAL; } @@ -1145,7 +1148,7 @@ public SpellCastEvent preCast(@NotNull SpellData data) { } if (castEvent.hasSpellCastStateChanged()) debug(2, " Spell cast state changed: " + state); - if (Perm.NO_CAST_TIME.has(data.caster())) castEvent.setCastTime(0); + if (castEvent.getCastTime() > 0 && Perm.NO_CAST_TIME.has(data.caster())) castEvent.setCastTime(0); return castEvent; } @@ -1331,15 +1334,24 @@ public ConfigData getCooldown() { * @return whether the spell is on cooldown */ public boolean onCooldown(LivingEntity livingEntity) { - if (Perm.NO_COOLDOWN.has(livingEntity)) return false; + UUID uuid = livingEntity.getUniqueId(); + long timeMillis = System.currentTimeMillis(); - ChargeState state = chargeStates.get(livingEntity.getUniqueId()); - if (state != null && state.isDepleted()) return true; + ChargeState state = chargeStates.get(uuid); + boolean depleted = state != null && state.isDepleted(); - if (serverCooldown > 0 && nextCastServer > System.currentTimeMillis()) return true; + if (!depleted && serverCooldown > 0 && nextCastServer > timeMillis) { + depleted = true; + } - Long next = nextCast.get(livingEntity.getUniqueId()); - return next != null && next > System.currentTimeMillis(); + if (!depleted) { + Long next = nextCast.get(uuid); + if (next != null && next > timeMillis) { + depleted = true; + } + } + + return depleted && !Perm.NO_COOLDOWN.has(livingEntity); } /** diff --git a/core/src/main/java/com/nisovin/magicspells/listeners/MagicSpellListener.java b/core/src/main/java/com/nisovin/magicspells/listeners/MagicSpellListener.java index e4945103f..ce6fe9e25 100644 --- a/core/src/main/java/com/nisovin/magicspells/listeners/MagicSpellListener.java +++ b/core/src/main/java/com/nisovin/magicspells/listeners/MagicSpellListener.java @@ -33,9 +33,17 @@ public void onSpellTarget(SpellTargetEvent event) { Spell spell = event.getSpell(); if (target == null) return; + if (isMSEntity(target)) { + event.setCancelled(true); + return; + } + + if (spell != null && noMagicZoneManager != null && noMagicZoneManager.willFizzle(target, spell)) { + event.setCancelled(true); + return; + } + if (Perm.NO_TARGET.has(target)) event.setCancelled(true); - if (spell != null && noMagicZoneManager != null && noMagicZoneManager.willFizzle(target, spell)) event.setCancelled(true); - if (isMSEntity(target)) event.setCancelled(true); } @EventHandler diff --git a/core/src/main/java/com/nisovin/magicspells/util/SpellUtil.java b/core/src/main/java/com/nisovin/magicspells/util/SpellUtil.java index fca83d43d..734cd933f 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/SpellUtil.java +++ b/core/src/main/java/com/nisovin/magicspells/util/SpellUtil.java @@ -63,9 +63,10 @@ public static boolean hasReagents(LivingEntity livingEntity, SpellReagents reage * @return true if the player has all the reagents, false otherwise */ public static boolean hasReagents(LivingEntity livingEntity, SpellReagents.ReagentItem[] reagents, double healthCost, int manaCost, int hungerCost, int experienceCost, int levelsCost, int durabilityCost, float moneyCost, Map variables) { - // Is the livingEntity exempt from reagent costs? - if (Perm.NO_REAGENTS.has(livingEntity)) return true; + return meetsCosts(livingEntity, reagents, healthCost, manaCost, hungerCost, experienceCost, levelsCost, durabilityCost, moneyCost, variables) || Perm.NO_REAGENTS.has(livingEntity); + } + private static boolean meetsCosts(LivingEntity livingEntity, SpellReagents.ReagentItem[] reagents, double healthCost, int manaCost, int hungerCost, int experienceCost, int levelsCost, int durabilityCost, float moneyCost, Map variables) { // player reagents if (livingEntity instanceof Player player) { // Mana costs @@ -147,7 +148,12 @@ public static void removeReagents(LivingEntity livingEntity, SpellReagents reage * @param manaCost the mana to remove */ public static void removeReagents(LivingEntity livingEntity, SpellReagents.ReagentItem[] reagents, double healthCost, int manaCost, int hungerCost, int experienceCost, int levelsCost, int durabilityCost, float moneyCost, Map variables) { - if (Perm.NO_REAGENTS.has(livingEntity)) return; + boolean hasCosts = (reagents != null && reagents.length > 0) + || healthCost != 0 || manaCost != 0 || hungerCost != 0 + || experienceCost != 0 || levelsCost != 0 || durabilityCost != 0 + || moneyCost != 0 || (variables != null && !variables.isEmpty()); + + if (!hasCosts || Perm.NO_REAGENTS.has(livingEntity)) return; if (reagents != null) { for (SpellReagents.ReagentItem item : reagents) {