Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds ability to listen to cancelled events #6207

Merged
merged 18 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 81 additions & 41 deletions src/main/java/ch/njol/skript/SkriptEventHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,12 @@
*/
package ch.njol.skript;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.Trigger;
import ch.njol.skript.timings.SkriptTimings;
import ch.njol.skript.util.Task;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.bukkit.Bukkit;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
Expand All @@ -42,13 +37,16 @@
import org.bukkit.plugin.RegisteredListener;
import org.eclipse.jdt.annotation.Nullable;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.Trigger;
import ch.njol.skript.timings.SkriptTimings;
import ch.njol.skript.util.Task;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

public final class SkriptEventHandler {

Expand Down Expand Up @@ -112,39 +110,32 @@ private static List<Trigger> getTriggers(Class<? extends Event> event) {
* @param priority The priority of the Event.
*/
private static void check(Event event, EventPriority priority) {
// get all triggers for this event, return if none
List<Trigger> triggers = getTriggers(event.getClass());
if (triggers.isEmpty())
return;

if (Skript.logVeryHigh()) {
boolean hasTrigger = false;
for (Trigger trigger : triggers) {
SkriptEvent triggerEvent = trigger.getEvent();
if (triggerEvent.getEventPriority() == priority && Boolean.TRUE.equals(Task.callSync(() -> triggerEvent.check(event)))) {
hasTrigger = true;
break;
}
}
if (!hasTrigger)
return;

logEventStart(event);
}

boolean isCancelled = event instanceof Cancellable && ((Cancellable) event).isCancelled() && !listenCancelled.contains(event.getClass());
boolean isResultDeny = !(event instanceof PlayerInteractEvent && (((PlayerInteractEvent) event).getAction() == Action.LEFT_CLICK_AIR || ((PlayerInteractEvent) event).getAction() == Action.RIGHT_CLICK_AIR) && ((PlayerInteractEvent) event).useItemInHand() != Result.DENY);

if (isCancelled && isResultDeny) {
if (Skript.logVeryHigh())
Skript.info(" -x- was cancelled");
return;
}
// Check if this event should be treated as cancelled
boolean isCancelled = isCancelled(event);

// This logs events even if there isn't a trigger that's going to run at that priority.
// However, there should only be a priority listener IF there's a trigger at that priority.
// So the time will be logged even if no triggers pass check(), which is still useful information.
logEventStart(event, priority);

for (Trigger trigger : triggers) {
SkriptEvent triggerEvent = trigger.getEvent();

// check if the trigger is at the right priority
if (triggerEvent.getEventPriority() != priority)
continue;

// check if the cancel state of the event is correct
if (triggerEvent.isListeningBehaviorSupported() && !triggerEvent.matchesListeningBehavior(isCancelled))
continue;

// these methods need to be run on whatever thread the trigger is
Runnable execute = () -> {
logTriggerStart(trigger);
Expand Down Expand Up @@ -172,6 +163,34 @@ private static void check(Event event, EventPriority priority) {
logEventEnd();
}

/**
* Helper method to check if we should treat the provided Event as cancelled.
*
* @param event The event to check.
* @return Whether the event should be treated as cancelled.
*/
private static boolean isCancelled(Event event) {
return event instanceof Cancellable &&
(((Cancellable) event).isCancelled() && isResultDeny(event)) &&
// listenCancelled is deprecated and should be removed in 2.9
!listenCancelled.contains(event.getClass());
}

/**
* Helper method for when the provided Event is a {@link PlayerInteractEvent}.
* These events are special in that they are called as cancelled when the player is left/right clicking on air.
* We don't want to treat those as cancelled, so we need to check if the {@link PlayerInteractEvent#useItemInHand()} result is DENY.
* That means the event was purposefully cancelled, and we should treat it as cancelled.
*
* @param event The event to check.
* @return Whether the event was a PlayerInteractEvent with air and the result was DENY.
*/
private static boolean isResultDeny(Event event) {
return !(event instanceof PlayerInteractEvent &&
(((PlayerInteractEvent) event).getAction() == Action.LEFT_CLICK_AIR || ((PlayerInteractEvent) event).getAction() == Action.RIGHT_CLICK_AIR) &&
((PlayerInteractEvent) event).useItemInHand() != Result.DENY);
}

private static long startEvent;

/**
Expand All @@ -180,11 +199,30 @@ private static void check(Event event, EventPriority priority) {
* @param event The Event that started.
*/
public static void logEventStart(Event event) {
logEventStart(event, null);
}

/**
* Logs that the provided Event has started with a priority.
* Requires {@link Skript#logVeryHigh()} to be true to log anything.
* @param event The Event that started.
* @param priority The priority of the Event.
*/
public static void logEventStart(Event event, @Nullable EventPriority priority) {
startEvent = System.nanoTime();
if (!Skript.logVeryHigh())
return;
Skript.info("");
Skript.info("== " + event.getClass().getName() + " ==");

String message = "== " + event.getClass().getName();

if (priority != null)
message += " with priority " + priority;

if (event instanceof Cancellable && ((Cancellable) event).isCancelled())
message += " (cancelled)";

Skript.info(message + " ==");
}

/**
Expand Down Expand Up @@ -307,8 +345,10 @@ public static void unregisterBukkitEvents(Trigger trigger) {
}

/**
* Events which are listened even if they are cancelled.
* Events which are listened even if they are cancelled. This should no longer be used.
* @deprecated Users should specify the listening behavior in the event declaration. "on any %event%:", "on cancelled %event%:".
*/
@Deprecated
public static final Set<Class<? extends Event>> listenCancelled = new HashSet<>();

/**
Expand Down
60 changes: 60 additions & 0 deletions src/main/java/ch/njol/skript/events/EvtResurrect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* This file is part of Skript.
*
* Skript is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Skript is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Skript. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright Peter Güttinger, SkriptLang team and contributors
*/
package ch.njol.skript.events;

import ch.njol.skript.Skript;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import org.bukkit.event.Event;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.eclipse.jdt.annotation.Nullable;

public class EvtResurrect extends SkriptEvent {
static {
Skript.registerEvent("Resurrect Attempt", EvtResurrect.class, EntityResurrectEvent.class, "[entity] resurrect[ion] [attempt]")
.description("Called when an entity dies, always. If they are not holding a totem, the event will be cancelled - you can, however, uncancel it.")
.examples(
"on resurrect attempt:",
"\tentity is player",
"\tentity has permission \"admin.undying\"",
"\tuncancel the event"
)
.since("2.2-dev28");
}

@Override
public boolean init(Literal<?>[] args, int matchedPattern, SkriptParser.ParseResult parseResult) {
// if the user hasn't specified a listening behavior, we'll default to ANY
if (listeningBehavior == null)
listeningBehavior = ListeningBehavior.ANY;
sovdeeth marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

@Override
public boolean check(Event event) {
return true;
}

@Override
public String toString(@Nullable Event event, boolean debug) {
return "resurrect attempt";
}

}
31 changes: 9 additions & 22 deletions src/main/java/ch/njol/skript/events/SimpleEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@
*/
package ch.njol.skript.events;

import ch.njol.skript.Skript;
import ch.njol.skript.lang.util.SimpleEvent;
import com.destroystokyo.paper.event.block.AnvilDamagedEvent;
import com.destroystokyo.paper.event.entity.EntityJumpEvent;
import com.destroystokyo.paper.event.entity.ProjectileCollideEvent;
import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent;
import com.destroystokyo.paper.event.player.PlayerJumpEvent;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import io.papermc.paper.event.player.PlayerDeepSleepEvent;
import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent;
import io.papermc.paper.event.player.PlayerTradeEvent;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockFertilizeEvent;
Expand All @@ -33,9 +41,9 @@
import org.bukkit.event.block.BlockSpreadEvent;
import org.bukkit.event.block.LeavesDecayEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.block.SpongeAbsorbEvent;
import org.bukkit.event.enchantment.EnchantItemEvent;
import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
import org.bukkit.event.block.SpongeAbsorbEvent;
import org.bukkit.event.entity.AreaEffectCloudApplyEvent;
import org.bukkit.event.entity.CreeperPowerEvent;
import org.bukkit.event.entity.EntityBreakDoorEvent;
Expand All @@ -44,7 +52,6 @@
import org.bukkit.event.entity.EntityPortalEnterEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.bukkit.event.entity.EntityTameEvent;
import org.bukkit.event.entity.EntityToggleGlideEvent;
import org.bukkit.event.entity.EntityToggleSwimEvent;
Expand Down Expand Up @@ -110,16 +117,6 @@
import org.spigotmc.event.entity.EntityDismountEvent;
import org.spigotmc.event.entity.EntityMountEvent;

import com.destroystokyo.paper.event.entity.ProjectileCollideEvent;
import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent;
import com.destroystokyo.paper.event.player.PlayerJumpEvent;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import com.destroystokyo.paper.event.entity.EntityJumpEvent;
import io.papermc.paper.event.player.PlayerTradeEvent;
import ch.njol.skript.Skript;
import ch.njol.skript.SkriptEventHandler;
import ch.njol.skript.lang.util.SimpleEvent;

/**
* @author Peter Güttinger
*/
Expand Down Expand Up @@ -472,16 +469,6 @@ public class SimpleEvents {
.description("Called when a slime splits. Usually this happens when a big slime dies.")
.examples("on slime split:")
.since("2.2-dev26");
Skript.registerEvent("Resurrect Attempt", SimpleEvent.class, EntityResurrectEvent.class, "[entity] resurrect[ion] [attempt]")
.description("Called when an entity dies, always. If they are not holding a totem, this is cancelled - you can, however, uncancel it.")
.examples(
"on resurrect attempt:",
"\tentity is player",
"\tentity has permission \"admin.undying\"",
"\tuncancel the event"
)
.since("2.2-dev28");
SkriptEventHandler.listenCancelled.add(EntityResurrectEvent.class); // Listen this even when cancelled
Skript.registerEvent("Player World Change", SimpleEvent.class, PlayerChangedWorldEvent.class, "[player] world chang(ing|e[d])")
.description("Called when a player enters a world. Does not work with other entities!")
.examples("on player world change:",
Expand Down
Loading