diff --git a/code-conventions.md b/code-conventions.md index cea1d2777..6cfe99df0 100644 --- a/code-conventions.md +++ b/code-conventions.md @@ -303,8 +303,9 @@ Skript **must** run on these MC versions, in one way or another. If your contribution breaks compatibility for any of these versions we cannot accept it. Please try to make sure contributions are future-safe, to the best of your ability. -Where possible, avoid using version-specific code to target a feature (e.g. accessing different copies of an internal class via reflection for each minecraft version) as this creates additional maintenance work every time a new version releases. +Where possible, avoid using version-specific code to target a feature (e.g. accessing different copies of an internal class via reflection for each Minecraft version) as this creates additional maintenance work every time a new version releases. Checking whether a class exists in order to target supported versions is acceptable. +If a feature requires a version check to support older versions, mark it for future cleanup. Above any checks, add a comment stating the target version where the check can be safely removed: `// TODO: UNTIL MC/SKRIPT x.y.z`. ### Support the Target Servers diff --git a/gradle.properties b/gradle.properties index d6cea8bfe..20b39b036 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true groupid=ch.njol name=skript -version=2.15.0 +version=2.15.2 jarName=Skript.jar testEnv=java25/paper-26.1.1 testEnvJavaVersion=25 diff --git a/src/main/java/ch/njol/skript/SkriptEventHandler.java b/src/main/java/ch/njol/skript/SkriptEventHandler.java index 4e0e3e34b..272433e94 100644 --- a/src/main/java/ch/njol/skript/SkriptEventHandler.java +++ b/src/main/java/ch/njol/skript/SkriptEventHandler.java @@ -76,6 +76,7 @@ private static List getTriggers(Class event) { return triggers.asMap().entrySet().stream() .filter(entry -> entry.getKey().isAssignableFrom(event) && getHandlerList(entry.getKey()) == eventHandlerList) .flatMap(entry -> entry.getValue().stream()) + .distinct() .collect(Collectors.toList()); // forces evaluation now and prevents us from having to call getTriggers again if very high logging is enabled } diff --git a/src/main/java/ch/njol/skript/classes/Changer.java b/src/main/java/ch/njol/skript/classes/Changer.java index d0cf56e44..3f08e8b92 100644 --- a/src/main/java/ch/njol/skript/classes/Changer.java +++ b/src/main/java/ch/njol/skript/classes/Changer.java @@ -1,7 +1,11 @@ package ch.njol.skript.classes; +import ch.njol.skript.expressions.base.WrapperExpression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; import com.google.common.base.Preconditions; import org.bukkit.event.Event; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -10,8 +14,11 @@ import org.skriptlang.skript.lang.arithmetic.Arithmetics; import org.skriptlang.skript.lang.arithmetic.OperationInfo; import org.skriptlang.skript.lang.arithmetic.Operator; +import org.skriptlang.skript.lang.converter.Converters; +import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -84,6 +91,77 @@ public static boolean acceptsChange(@NotNull Expression expression, ChangeMod return acceptsChangeTypes(validTypes, types); } + /** + * Tests whether an expression accepts changes of a certain type. + * If multiple types are given it test for whether any of the types is accepted. + * This method goes further than {@link #acceptsChange(Expression, ChangeMode, Class[])} by considering + * {@link Converters} and attempting to convert {@code expression} to accept at least one of {@code types}. + * + * @param expression The expression to test (and potentially convert) + * @param mode The ChangeMode to use in the test + * @param types The types to test for + * @return {@code expression} (or a conversion of it) that allows {@link #change(Object[], Object[], ChangeMode)} + * to be called with one of {@code types} (as a delta value). + * Returns {@code null} if no such conversion of {@code expression} exists. + */ + @ApiStatus.Internal + public static @Nullable Expression acceptsChangeWithConverters(@NotNull Expression expression, ChangeMode mode, Class... types) { + Class[] validTypes = expression.acceptChange(mode); + if (validTypes == null) + return null; + + for (int i = 0; i < validTypes.length; i++) { + if (validTypes[i].isArray()) { + validTypes[i] = validTypes[i].getComponentType(); + } + } + + if (acceptsChangeTypes(validTypes, types)) { + return expression; + } + + for (Class type : types) { + if (Converters.converterExists(type, validTypes)) { + class ChangeWrapper extends WrapperExpression { + public ChangeWrapper(Expression expression) { + super(expression); + } + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + throw new UnsupportedOperationException(); + } + + @Override + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { + super.change(event, Converters.convert(delta, validTypes, Object.class), mode); + } + + @Override + public void changeInPlace(Event event, Function changeFunction, boolean getAll) { + T[] values = getAll ? getAll(event) : getArray(event); + if (values.length == 0) + return; + + List newValues = new ArrayList<>(); + for (T value : values) { + newValues.add(changeFunction.apply(value)); + } + change(event, newValues.toArray(), ChangeMode.SET); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return getExpr().toString(event, debug); + } + } + return new ChangeWrapper(expression); + } + } + + return null; + } + /** * Tests whether any of the given types is accepted by the given array of valid types. * diff --git a/src/main/java/ch/njol/skript/command/Commands.java b/src/main/java/ch/njol/skript/command/Commands.java index 2df57ab97..d7cacc5ff 100644 --- a/src/main/java/ch/njol/skript/command/Commands.java +++ b/src/main/java/ch/njol/skript/command/Commands.java @@ -189,23 +189,25 @@ static boolean handleEffectCommand(CommandSender sender, String command) { Effect effect = Effect.parse(command, null); parserInstance.deleteCurrentEvent(); + TextComponentParser textParser = TextComponentParser.instance(); if (effect != null) { log.clear(); // ignore warnings and stuff log.printLog(); if (!effectCommand.isCancelled()) { - sender.sendRichMessage("Executing '" + TextComponentParser.instance().escape(command) + "'"); + sender.sendMessage(textParser.parse("Executing '" + textParser.escape(command) + "'")); if (SkriptConfig.logEffectCommands.value() && !(sender instanceof ConsoleCommandSender)) - Skript.info(sender.getName() + " issued effect command: " + TextComponentParser.instance().escape(command)); + Skript.info(sender.getName() + " issued effect command: " + textParser.escape(command)); TriggerItem.walk(effect, effectCommand); Variables.removeLocals(effectCommand); } else { - sender.sendRichMessage("Your effect command '" + TextComponentParser.instance().escape(command) + "' was cancelled."); + sender.sendMessage(textParser.parse("Your effect command '" + textParser.escape(command) + "' was cancelled.")); } } else { if (sender == Bukkit.getConsoleSender()) // log as SEVERE instead of INFO like printErrors below - SkriptLogger.LOGGER.severe("Error in: " + TextComponentParser.instance().escape(command)); + // No need to escape command here, logger will do it + SkriptLogger.LOGGER.severe("Error in: " + command); else - sender.sendRichMessage("Error in: " + TextComponentParser.instance().escape(command)); + sender.sendMessage(textParser.parse("Error in: " + textParser.escape(command))); // TODO errors likely need to be escaped too log.printErrors(sender, "(No specific information is available)"); } diff --git a/src/main/java/ch/njol/skript/conditions/CondElytraBoostConsume.java b/src/main/java/ch/njol/skript/conditions/CondElytraBoostConsume.java index fb3d079d0..e5ecfa662 100644 --- a/src/main/java/ch/njol/skript/conditions/CondElytraBoostConsume.java +++ b/src/main/java/ch/njol/skript/conditions/CondElytraBoostConsume.java @@ -3,9 +3,11 @@ import ch.njol.skript.Skript; import ch.njol.skript.doc.*; import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.event.player.PlayerElytraBoostEvent; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -18,7 +20,7 @@ prevent the used firework from being consumed """) @Since("2.10") -public class CondElytraBoostConsume extends Condition { +public class CondElytraBoostConsume extends Condition implements EventRestrictedSyntax { static { if (Skript.classExists("com.destroystokyo.paper.event.player.PlayerElytraBoostEvent")) { @@ -32,14 +34,15 @@ public class CondElytraBoostConsume extends Condition { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerElytraBoostEvent.class)) { - Skript.error("This condition can only be used in an 'elytra boost' event."); - return false; - } checkConsume = matchedPattern == 0; return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerElytraBoostEvent.class); + } + @Override public boolean check(Event event) { if (!(event instanceof PlayerElytraBoostEvent boostEvent)) diff --git a/src/main/java/ch/njol/skript/conditions/CondLeashWillDrop.java b/src/main/java/ch/njol/skript/conditions/CondLeashWillDrop.java index 05d7f640e..d9bf5a2c3 100644 --- a/src/main/java/ch/njol/skript/conditions/CondLeashWillDrop.java +++ b/src/main/java/ch/njol/skript/conditions/CondLeashWillDrop.java @@ -1,5 +1,7 @@ package ch.njol.skript.conditions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityUnleashEvent; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ @Keywords("lead") @Events("Leash / Unleash") @Since("2.10") -public class CondLeashWillDrop extends Condition { +public class CondLeashWillDrop extends Condition implements EventRestrictedSyntax { static { // TODO - remove this when Spigot support is dropped @@ -33,14 +35,15 @@ public class CondLeashWillDrop extends Condition { @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityUnleashEvent.class)) { - Skript.error("The 'leash will drop' condition can only be used in an 'unleash' event"); - return false; - } setNegated(parseResult.hasTag("not")); return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityUnleashEvent.class); + } + @Override public boolean check(Event event) { if (!(event instanceof EntityUnleashEvent unleashEvent)) diff --git a/src/main/java/ch/njol/skript/conditions/CondResourcePack.java b/src/main/java/ch/njol/skript/conditions/CondResourcePack.java index f0ee8e6c4..0e7893b0b 100644 --- a/src/main/java/ch/njol/skript/conditions/CondResourcePack.java +++ b/src/main/java/ch/njol/skript/conditions/CondResourcePack.java @@ -1,5 +1,7 @@ package ch.njol.skript.conditions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerResourcePackStatusEvent; import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status; @@ -25,7 +27,7 @@ """) @Since("2.4") @Events("resource pack request response") -public class CondResourcePack extends Condition { +public class CondResourcePack extends Condition implements EventRestrictedSyntax { static { Skript.registerCondition(CondResourcePack.class, @@ -39,14 +41,15 @@ public class CondResourcePack extends Condition { @SuppressWarnings({"unchecked", "null"}) @Override public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerResourcePackStatusEvent.class)) { - Skript.error("The resource pack condition can't be used outside of a resource pack response event"); - return false; - } states = (Expression) exprs[0]; setNegated(matchedPattern == 1); return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerResourcePackStatusEvent.class); + } @Override public boolean check(Event e) { diff --git a/src/main/java/ch/njol/skript/conditions/CondRespawnLocation.java b/src/main/java/ch/njol/skript/conditions/CondRespawnLocation.java index 055b678cd..210428ef1 100644 --- a/src/main/java/ch/njol/skript/conditions/CondRespawnLocation.java +++ b/src/main/java/ch/njol/skript/conditions/CondRespawnLocation.java @@ -8,9 +8,11 @@ import ch.njol.skript.doc.RequiredPlugins; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerRespawnEvent; import org.jetbrains.annotations.Nullable; @@ -25,7 +27,7 @@ @RequiredPlugins("Minecraft 1.16+") @Since("2.7") @Events("respawn") -public class CondRespawnLocation extends Condition { +public class CondRespawnLocation extends Condition implements EventRestrictedSyntax { static { Skript.registerCondition(CondRespawnLocation.class, "[the] respawn location (was|is)[1:(n'| no)t] [a] (:bed|respawn anchor)"); @@ -35,15 +37,16 @@ public class CondRespawnLocation extends Condition { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerRespawnEvent.class)) { - Skript.error("The 'respawn location' condition may only be used in a respawn event"); - return false; - } setNegated(parseResult.mark == 1); bedSpawn = parseResult.hasTag("bed"); return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerRespawnEvent.class); + } + @Override public boolean check(Event event) { if (event instanceof PlayerRespawnEvent) { diff --git a/src/main/java/ch/njol/skript/conditions/CondWillHatch.java b/src/main/java/ch/njol/skript/conditions/CondWillHatch.java index f7370647e..8349215bc 100644 --- a/src/main/java/ch/njol/skript/conditions/CondWillHatch.java +++ b/src/main/java/ch/njol/skript/conditions/CondWillHatch.java @@ -7,9 +7,11 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerEggThrowEvent; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ """) @Events("Egg Throw") @Since("2.7") -public class CondWillHatch extends Condition { +public class CondWillHatch extends Condition implements EventRestrictedSyntax { static { Skript.registerCondition(CondWillHatch.class, @@ -33,14 +35,15 @@ public class CondWillHatch extends Condition { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerEggThrowEvent.class)) { - Skript.error("You can't use the 'egg will hatch' condition outside of a Player Egg Throw event."); - return false; - } setNegated(!parseResult.hasTag("will")); return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerEggThrowEvent.class); + } + @Override public boolean check(Event event) { if (!(event instanceof PlayerEggThrowEvent)) diff --git a/src/main/java/ch/njol/skript/effects/EffCancelCooldown.java b/src/main/java/ch/njol/skript/effects/EffCancelCooldown.java index 076b9867f..eb84feae6 100644 --- a/src/main/java/ch/njol/skript/effects/EffCancelCooldown.java +++ b/src/main/java/ch/njol/skript/effects/EffCancelCooldown.java @@ -1,5 +1,7 @@ package ch.njol.skript.effects; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -30,7 +32,7 @@ set the player's display name to arg-1 """) @Since("2.2-dev34") -public class EffCancelCooldown extends Effect { +public class EffCancelCooldown extends Effect implements EventRestrictedSyntax { static { Skript.registerEffect(EffCancelCooldown.class, @@ -42,14 +44,15 @@ public class EffCancelCooldown extends Effect { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - if (!getParser().isCurrentEvent(ScriptCommandEvent.class)) { - Skript.error("The cancel cooldown effect may only be used in a command", ErrorQuality.SEMANTIC_ERROR); - return false; - } cancel = matchedPattern == 0; return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(ScriptCommandEvent.class); + } + @Override protected void execute(Event e) { if (!(e instanceof ScriptCommandEvent)) diff --git a/src/main/java/ch/njol/skript/effects/EffDropLeash.java b/src/main/java/ch/njol/skript/effects/EffDropLeash.java index 7ebe6294f..17d9b5447 100644 --- a/src/main/java/ch/njol/skript/effects/EffDropLeash.java +++ b/src/main/java/ch/njol/skript/effects/EffDropLeash.java @@ -1,5 +1,7 @@ package ch.njol.skript.effects; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityUnleashEvent; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ @Keywords("lead") @Events("Leash / Unleash") @Since("2.10") -public class EffDropLeash extends Effect { +public class EffDropLeash extends Effect implements EventRestrictedSyntax { static { Skript.registerEffect(EffDropLeash.class, @@ -36,14 +38,15 @@ public class EffDropLeash extends Effect { @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityUnleashEvent.class)) { - Skript.error("The 'drop leash' effect can only be used in an 'unleash' event"); - return false; - } allowLeashDrop = matchedPattern == 0; return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityUnleashEvent.class); + } + @Override protected void execute(Event event) { if (!(event instanceof EntityUnleashEvent unleashEvent)) diff --git a/src/main/java/ch/njol/skript/effects/EffElytraBoostConsume.java b/src/main/java/ch/njol/skript/effects/EffElytraBoostConsume.java index 2199efee9..1df5046be 100644 --- a/src/main/java/ch/njol/skript/effects/EffElytraBoostConsume.java +++ b/src/main/java/ch/njol/skript/effects/EffElytraBoostConsume.java @@ -3,9 +3,11 @@ import ch.njol.skript.Skript; import ch.njol.skript.doc.*; import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.event.player.PlayerElytraBoostEvent; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -18,7 +20,7 @@ prevent the used firework from being consume """) @Since("2.10") -public class EffElytraBoostConsume extends Effect { +public class EffElytraBoostConsume extends Effect implements EventRestrictedSyntax { static { if (Skript.classExists("com.destroystokyo.paper.event.player.PlayerElytraBoostEvent")) { @@ -32,14 +34,15 @@ public class EffElytraBoostConsume extends Effect { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerElytraBoostEvent.class)) { - Skript.error("This effect can only be used in an 'elytra boost' event."); - return false; - } consume = matchedPattern == 1; return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerElytraBoostEvent.class); + } + @Override protected void execute(Event event) { if (!(event instanceof PlayerElytraBoostEvent boostEvent)) diff --git a/src/main/java/ch/njol/skript/effects/EffHidePlayerFromServerList.java b/src/main/java/ch/njol/skript/effects/EffHidePlayerFromServerList.java index 2c2671916..0daa0b285 100644 --- a/src/main/java/ch/njol/skript/effects/EffHidePlayerFromServerList.java +++ b/src/main/java/ch/njol/skript/effects/EffHidePlayerFromServerList.java @@ -3,6 +3,8 @@ import java.util.Arrays; import java.util.Iterator; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.server.ServerListPingEvent; @@ -28,7 +30,7 @@ hide {vanished::*} from the server list """) @Since("2.3") -public class EffHidePlayerFromServerList extends Effect { +public class EffHidePlayerFromServerList extends Effect implements EventRestrictedSyntax { static { Skript.registerEffect(EffHidePlayerFromServerList.class, @@ -36,20 +38,13 @@ public class EffHidePlayerFromServerList extends Effect { "hide %players%'[s] info[rmation] (in|on|from) [the] server list"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - @SuppressWarnings("null") private Expression players; @SuppressWarnings({"unchecked", "null"}) @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - boolean isServerPingEvent = getParser().isCurrentEvent(ServerListPingEvent.class) || - (PAPER_EVENT_EXISTS && getParser().isCurrentEvent(PaperServerListPingEvent.class)); - if (!isServerPingEvent) { - Skript.error("The hide player from server list effect can't be used outside of a server list ping event"); - return false; - } else if (isDelayed == Kleenean.TRUE) { + if (isDelayed == Kleenean.TRUE) { Skript.error("Can't hide players from the server list anymore after the server list ping event has already passed"); return false; } @@ -57,6 +52,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(ServerListPingEvent.class, PaperServerListPingEvent.class); + } + @Override @SuppressWarnings("removal") protected void execute(Event e) { diff --git a/src/main/java/ch/njol/skript/effects/EffKeepInventory.java b/src/main/java/ch/njol/skript/effects/EffKeepInventory.java index 1a9c43add..9f05beb95 100644 --- a/src/main/java/ch/njol/skript/effects/EffKeepInventory.java +++ b/src/main/java/ch/njol/skript/effects/EffKeepInventory.java @@ -1,5 +1,7 @@ package ch.njol.skript.effects; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent; @@ -25,7 +27,7 @@ """) @Since("2.4") @Events("death") -public class EffKeepInventory extends Effect { +public class EffKeepInventory extends Effect implements EventRestrictedSyntax { static { Skript.registerEffect(EffKeepInventory.class, @@ -39,10 +41,6 @@ public class EffKeepInventory extends Effect { public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { keepItems = matchedPattern == 0 || parseResult.mark == 1; keepExp = matchedPattern == 1 || parseResult.mark == 1; - if (!getParser().isCurrentEvent(EntityDeathEvent.class)) { - Skript.error("The keep inventory/experience effect can't be used outside of a death event"); - return false; - } if (isDelayed.isTrue()) { Skript.error("Can't keep the inventory/experience anymore after the event has already passed"); return false; @@ -50,6 +48,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityDeathEvent.class); + } + @Override protected void execute(Event event) { if (event instanceof PlayerDeathEvent) { diff --git a/src/main/java/ch/njol/skript/effects/EffLoadServerIcon.java b/src/main/java/ch/njol/skript/effects/EffLoadServerIcon.java index ff87e2b06..7f568089e 100644 --- a/src/main/java/ch/njol/skript/effects/EffLoadServerIcon.java +++ b/src/main/java/ch/njol/skript/effects/EffLoadServerIcon.java @@ -39,8 +39,6 @@ public class EffLoadServerIcon extends AsyncEffect { Skript.registerEffect(EffLoadServerIcon.class, "load [the] server icon (from|of) [the] [image] [file] %string%"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - @SuppressWarnings("null") private Expression path; @@ -51,10 +49,6 @@ public class EffLoadServerIcon extends AsyncEffect { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { getParser().setHasDelayBefore(Kleenean.TRUE); - if (!PAPER_EVENT_EXISTS) { - Skript.error("The load server icon effect requires Paper 1.12.2 or newer"); - return false; - } path = (Expression) exprs[0]; return true; } diff --git a/src/main/java/ch/njol/skript/effects/EffMakeEggHatch.java b/src/main/java/ch/njol/skript/effects/EffMakeEggHatch.java index 83c62f644..38303592c 100644 --- a/src/main/java/ch/njol/skript/effects/EffMakeEggHatch.java +++ b/src/main/java/ch/njol/skript/effects/EffMakeEggHatch.java @@ -7,9 +7,11 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerEggThrowEvent; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ """) @Events("Egg Throw") @Since("2.7") -public class EffMakeEggHatch extends Effect { +public class EffMakeEggHatch extends Effect implements EventRestrictedSyntax { static { Skript.registerEffect(EffMakeEggHatch.class, @@ -35,14 +37,15 @@ public class EffMakeEggHatch extends Effect { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerEggThrowEvent.class)) { - Skript.error("You can't use the 'make the egg hatch' effect outside of a Player Egg Throw event."); - return false; - } not = parseResult.hasTag("not"); return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerEggThrowEvent.class); + } + @Override protected void execute(Event e) { if (e instanceof PlayerEggThrowEvent) { diff --git a/src/main/java/ch/njol/skript/effects/EffPlayerInfoVisibility.java b/src/main/java/ch/njol/skript/effects/EffPlayerInfoVisibility.java index ced4b44dc..50489423e 100644 --- a/src/main/java/ch/njol/skript/effects/EffPlayerInfoVisibility.java +++ b/src/main/java/ch/njol/skript/effects/EffPlayerInfoVisibility.java @@ -3,9 +3,11 @@ import ch.njol.skript.Skript; import ch.njol.skript.doc.*; import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.event.server.PaperServerListPingEvent; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -22,7 +24,7 @@ @Example("reveal all player related info") @Since("2.3") @Events("server list ping") -public class EffPlayerInfoVisibility extends Effect { +public class EffPlayerInfoVisibility extends Effect implements EventRestrictedSyntax { static { Skript.registerEffect(EffPlayerInfoVisibility.class, @@ -30,19 +32,11 @@ public class EffPlayerInfoVisibility extends Effect { "(show|reveal) [all] player [related] info[rmation] [(in|to|on|from) [the] server list]"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - private boolean shouldHide; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!PAPER_EVENT_EXISTS) { - Skript.error("The player info visibility effect requires Paper 1.12.2 or newer"); - return false; - } else if (!getParser().isCurrentEvent(PaperServerListPingEvent.class)) { - Skript.error("The player info visibility effect can't be used outside of a server list ping event"); - return false; - } else if (isDelayed == Kleenean.TRUE) { + if (isDelayed == Kleenean.TRUE) { Skript.error("Can't change the player info visibility anymore after the server list ping event has already passed"); return false; } @@ -50,6 +44,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PaperServerListPingEvent.class); + } + @Override protected void execute(Event e) { if (!(e instanceof PaperServerListPingEvent)) diff --git a/src/main/java/ch/njol/skript/effects/EffReplace.java b/src/main/java/ch/njol/skript/effects/EffReplace.java index ac0fffd16..293326a8e 100644 --- a/src/main/java/ch/njol/skript/effects/EffReplace.java +++ b/src/main/java/ch/njol/skript/effects/EffReplace.java @@ -70,9 +70,13 @@ public boolean init(Expression[] expressions, int matchedPattern, replaceFirst = parseResult.hasTag("first"); replaceRegex = matchedPattern == 2 || matchedPattern == 3; - if (replaceString && !ChangerUtils.acceptsChange(haystack, ChangeMode.SET, String.class)) { - Skript.error(haystack + " cannot be changed and can thus not have parts replaced"); - return false; + if (replaceString) { + Expression newHaystack = ChangerUtils.acceptsChangeWithConverters(haystack, ChangeMode.SET, String.class); + if (newHaystack == null) { + Skript.error(haystack.toString(null, Skript.debug()) + " cannot be changed to a text and can thus not have parts replaced"); + return false; + } + haystack = newHaystack; } if (SkriptConfig.caseSensitive.value() || parseResult.hasTag("case")) { diff --git a/src/main/java/ch/njol/skript/events/EvtClick.java b/src/main/java/ch/njol/skript/events/EvtClick.java index 27ecdab5c..a75ca55e7 100644 --- a/src/main/java/ch/njol/skript/events/EvtClick.java +++ b/src/main/java/ch/njol/skript/events/EvtClick.java @@ -30,6 +30,9 @@ public class EvtClick extends SkriptEvent { + // TODO: UNTIL MC 26.1.1 + private final static boolean USE_OLD_PIAEE_BEHAVIOR = !Skript.isRunningMinecraft(26,1,1); + /** * Click types. */ @@ -111,19 +114,27 @@ public boolean check(Event event) { if (event instanceof PlayerInteractEntityEvent interactEntityEvent) { Entity clicked = interactEntityEvent.getRightClicked(); - // Usually, don't handle these events - if (interactEntityEvent instanceof PlayerInteractAtEntityEvent) { - // But armor stands are an exception - // Later, there may be more exceptions... - if (!(clicked instanceof ArmorStand)) - return false; + if (USE_OLD_PIAEE_BEHAVIOR) { + // Usually, don't handle these events + if (interactEntityEvent instanceof PlayerInteractAtEntityEvent) { + // But armor stands are an exception + // Later, there may be more exceptions... + if (!(clicked instanceof ArmorStand)) + return false; + } } if (click == LEFT) // Lefts clicks on entities don't work return false; - // PlayerInteractAtEntityEvent called only once for armor stands - if (!(event instanceof PlayerInteractAtEntityEvent)) { + if (USE_OLD_PIAEE_BEHAVIOR) { + // PlayerInteractAtEntityEvent called only once for armor stands + if (!(event instanceof PlayerInteractAtEntityEvent)) { + if (!interactTracker.checkEvent(interactEntityEvent.getPlayer(), interactEntityEvent, interactEntityEvent.getHand())) { + return false; // Not first event this tick + } + } + } else { if (!interactTracker.checkEvent(interactEntityEvent.getPlayer(), interactEntityEvent, interactEntityEvent.getHand())) { return false; // Not first event this tick } diff --git a/src/main/java/ch/njol/skript/expressions/ExprAbsorbedBlocks.java b/src/main/java/ch/njol/skript/expressions/ExprAbsorbedBlocks.java index d41a808df..153a40484 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprAbsorbedBlocks.java +++ b/src/main/java/ch/njol/skript/expressions/ExprAbsorbedBlocks.java @@ -3,6 +3,8 @@ import java.util.Iterator; import java.util.List; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.block.BlockState; import org.bukkit.event.Event; import org.bukkit.event.block.SpongeAbsorbEvent; @@ -27,7 +29,7 @@ @Events("sponge absorb") @Example("the absorbed blocks") @Since("2.5") -public class ExprAbsorbedBlocks extends SimpleExpression { +public class ExprAbsorbedBlocks extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprAbsorbedBlocks.class, BlockStateBlock.class, ExpressionType.SIMPLE, "[the] absorbed blocks"); @@ -35,12 +37,13 @@ public class ExprAbsorbedBlocks extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(SpongeAbsorbEvent.class)) { - Skript.error("The 'absorbed blocks' are only usable in sponge absorb events", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(SpongeAbsorbEvent.class); + } @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprAffectedEntities.java b/src/main/java/ch/njol/skript/expressions/ExprAffectedEntities.java index 56aaa587a..ee463e3ea 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprAffectedEntities.java +++ b/src/main/java/ch/njol/skript/expressions/ExprAffectedEntities.java @@ -4,6 +4,7 @@ import java.util.Objects; import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.util.coll.CollectionUtils; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; @@ -30,7 +31,7 @@ send "WARNING: you've step on an area effect cloud!" to loop-value """) @Since("2.4") -public class ExprAffectedEntities extends SimpleExpression { +public class ExprAffectedEntities extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprAffectedEntities.class, LivingEntity.class, ExpressionType.SIMPLE, "[the] affected entities"); @@ -38,13 +39,14 @@ public class ExprAffectedEntities extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { - if (!getParser().isCurrentEvent(AreaEffectCloudApplyEvent.class)) { - Skript.error("The 'affected entities' expression may only be used in an area cloud effect event."); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(AreaEffectCloudApplyEvent.class); + } + @Override protected LivingEntity @Nullable [] get(Event event) { if (event instanceof AreaEffectCloudApplyEvent areaEvent) diff --git a/src/main/java/ch/njol/skript/expressions/ExprAppliedEffect.java b/src/main/java/ch/njol/skript/expressions/ExprAppliedEffect.java index f02a191d8..3dee2c5b0 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprAppliedEffect.java +++ b/src/main/java/ch/njol/skript/expressions/ExprAppliedEffect.java @@ -2,11 +2,13 @@ import ch.njol.skript.Skript; import ch.njol.skript.doc.*; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.event.block.BeaconEffectEvent; import org.bukkit.event.Event; import org.bukkit.potion.PotionEffectType; @@ -23,7 +25,7 @@ """) @Events("Beacon Effect") @Since("2.10") -public class ExprAppliedEffect extends SimpleExpression { +public class ExprAppliedEffect extends SimpleExpression implements EventRestrictedSyntax { static { if (Skript.classExists("com.destroystokyo.paper.event.block.BeaconEffectEvent")) { @@ -34,13 +36,14 @@ public class ExprAppliedEffect extends SimpleExpression { @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(BeaconEffectEvent.class)) { - Skript.error("You can only use 'applied effect' in a beacon effect event."); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(BeaconEffectEvent.class); + } + @Override protected PotionEffectType @Nullable [] get(Event event) { if (!(event instanceof BeaconEffectEvent effectEvent)) diff --git a/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java b/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java index 4faa757cb..85885ddcc 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java +++ b/src/main/java/ch/njol/skript/expressions/ExprAppliedEnchantments.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.enchantments.Enchantment; import org.bukkit.event.Event; import org.bukkit.event.enchantment.EnchantItemEvent; @@ -30,7 +31,7 @@ """) @Events("enchant") @Since("2.5") -public class ExprAppliedEnchantments extends SimpleExpression { +public class ExprAppliedEnchantments extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprAppliedEnchantments.class, EnchantmentType.class, ExpressionType.SIMPLE, "[the] applied enchant[ment]s"); @@ -38,13 +39,14 @@ public class ExprAppliedEnchantments extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EnchantItemEvent.class)) { - Skript.error("The applied enchantments are only usable in an enchant event.", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EnchantItemEvent.class); + } + @SuppressWarnings("null") @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprBarterDrops.java b/src/main/java/ch/njol/skript/expressions/ExprBarterDrops.java index 385daca44..1486dc89f 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprBarterDrops.java +++ b/src/main/java/ch/njol/skript/expressions/ExprBarterDrops.java @@ -7,6 +7,7 @@ import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -29,7 +30,7 @@ broadcast "it's not halloween yet!" """) @Since("2.10") -public class ExprBarterDrops extends SimpleExpression { +public class ExprBarterDrops extends SimpleExpression implements EventRestrictedSyntax { static { if (Skript.classExists("org.bukkit.event.entity.PiglinBarterEvent")) { @@ -42,15 +43,15 @@ public class ExprBarterDrops extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult result) { - if (!getParser().isCurrentEvent(PiglinBarterEvent.class)) { - Skript.error("The expression 'barter drops' can only be used in the piglin bartering event"); - return false; - } - delay = isDelayed; return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PiglinBarterEvent.class); + } @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprBarterInput.java b/src/main/java/ch/njol/skript/expressions/ExprBarterInput.java index f677e0814..bd0f5adc6 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprBarterInput.java +++ b/src/main/java/ch/njol/skript/expressions/ExprBarterInput.java @@ -6,11 +6,13 @@ import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.entity.PiglinBarterEvent; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ broadcast "my precious..." """) @Since("2.10") -public class ExprBarterInput extends SimpleExpression { +public class ExprBarterInput extends SimpleExpression implements EventRestrictedSyntax { static { if (Skript.classExists("org.bukkit.event.entity.PiglinBarterEvent")) { @@ -34,13 +36,14 @@ public class ExprBarterInput extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult result) { - if (!getParser().isCurrentEvent(PiglinBarterEvent.class)) { - Skript.error("The expression 'barter input' can only be used in the piglin bartering event"); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PiglinBarterEvent.class); + } + @Override @Nullable protected ItemType[] get(Event event) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprCmdCooldownInfo.java b/src/main/java/ch/njol/skript/expressions/ExprCmdCooldownInfo.java index 1afd010d1..9cb0eaf78 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprCmdCooldownInfo.java +++ b/src/main/java/ch/njol/skript/expressions/ExprCmdCooldownInfo.java @@ -2,6 +2,8 @@ import java.util.UUID; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -35,7 +37,7 @@ teleport player to {home::%player%} """) @Since("2.2-dev33") -public class ExprCmdCooldownInfo extends SimpleExpression { +public class ExprCmdCooldownInfo extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprCmdCooldownInfo.class, Object.class, ExpressionType.SIMPLE, @@ -51,13 +53,14 @@ public class ExprCmdCooldownInfo extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { pattern = matchedPattern; - if (!getParser().isCurrentEvent(ScriptCommandEvent.class)) { - Skript.error("The " + getExpressionName() + " expression can only be used within a command", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(ScriptCommandEvent.class); + } + @Override @Nullable protected Object[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprDamage.java b/src/main/java/ch/njol/skript/expressions/ExprDamage.java index 77af9b89b..de0c2f4a3 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprDamage.java +++ b/src/main/java/ch/njol/skript/expressions/ExprDamage.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.player.PlayerItemDamageEvent; @@ -39,7 +40,7 @@ """) @Since("1.3.5, 2.8.0 (item damage event)") @Events({"Damage", "Vehicle Damage", "Item Damage"}) -public class ExprDamage extends SimpleExpression { +public class ExprDamage extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprDamage.class, Number.class, ExpressionType.SIMPLE, "[the] damage"); @@ -50,13 +51,14 @@ public class ExprDamage extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityDamageEvent.class, VehicleDamageEvent.class, PlayerItemDamageEvent.class)) { - Skript.error("The 'damage' expression may only be used in damage events"); - return false; - } delay = isDelayed; return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityDamageEvent.class, VehicleDamageEvent.class, PlayerItemDamageEvent.class); + } @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java index 59c31f6b3..b76aeccad 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java +++ b/src/main/java/ch/njol/skript/expressions/ExprEnchantItem.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.enchantment.EnchantItemEvent; import org.bukkit.event.enchantment.PrepareItemEnchantEvent; @@ -34,7 +35,7 @@ """) @Events({"enchant prepare", "enchant"}) @Since("2.5") -public class ExprEnchantItem extends SimpleExpression { +public class ExprEnchantItem extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprEnchantItem.class, ItemType.class, ExpressionType.SIMPLE, "[the] enchant[ed] item"); @@ -42,13 +43,14 @@ public class ExprEnchantItem extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EnchantItemEvent.class) && !getParser().isCurrentEvent(PrepareItemEnchantEvent.class)) { - Skript.error("The enchant item is only usable in an enchant prepare event or enchant event.", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EnchantItemEvent.class, PrepareItemEnchantEvent.class); + } + @Override @Nullable protected ItemType[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java index e22595e94..97d0d4f2f 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java +++ b/src/main/java/ch/njol/skript/expressions/ExprEnchantingExpCost.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.enchantment.EnchantItemEvent; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ """) @Events("enchant") @Since("2.5") -public class ExprEnchantingExpCost extends SimpleExpression { +public class ExprEnchantingExpCost extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprEnchantingExpCost.class, Long.class, ExpressionType.SIMPLE, @@ -38,13 +39,14 @@ public class ExprEnchantingExpCost extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EnchantItemEvent.class)) { - Skript.error("The experience cost of enchanting is only usable in an enchant event.", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EnchantItemEvent.class); + } + @Override @Nullable protected Long[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java index fce4094a1..42b53e166 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java +++ b/src/main/java/ch/njol/skript/expressions/ExprEnchantmentBonus.java @@ -1,5 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.enchantment.PrepareItemEnchantEvent; import org.jetbrains.annotations.Nullable; @@ -25,7 +27,7 @@ """) @Events("enchant prepare") @Since("2.5") -public class ExprEnchantmentBonus extends SimpleExpression { +public class ExprEnchantmentBonus extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprEnchantmentBonus.class, Long.class, ExpressionType.SIMPLE, "[the] enchantment bonus"); @@ -33,13 +35,14 @@ public class ExprEnchantmentBonus extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PrepareItemEnchantEvent.class)) { - Skript.error("The enchantment bonus is only usable in an enchant prepare event.", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PrepareItemEnchantEvent.class); + } + @Override @Nullable protected Long[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java b/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java index ffac6e923..d574d0c8e 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java +++ b/src/main/java/ch/njol/skript/expressions/ExprEnchantmentOffer.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Random; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.enchantments.EnchantmentOffer; import org.bukkit.event.Event; import org.bukkit.event.enchantment.PrepareItemEnchantEvent; @@ -36,7 +37,7 @@ @Since("2.5") @Events("enchant prepare") @RequiredPlugins("1.11 or newer") -public class ExprEnchantmentOffer extends SimpleExpression { +public class ExprEnchantmentOffer extends SimpleExpression implements EventRestrictedSyntax { static { if (Skript.classExists("org.bukkit.enchantments.EnchantmentOffer")) { @@ -58,10 +59,6 @@ public class ExprEnchantmentOffer extends SimpleExpression { @SuppressWarnings({"null", "unchecked"}) @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PrepareItemEnchantEvent.class)) { - Skript.error("Enchantment offers are only usable in enchant prepare events", ErrorQuality.SEMANTIC_ERROR); - return false; - } if (matchedPattern == 0) { all = true; } else { @@ -71,6 +68,11 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PrepareItemEnchantEvent.class); + } + @SuppressWarnings({"null", "unused"}) @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprEvtInitiator.java b/src/main/java/ch/njol/skript/expressions/ExprEvtInitiator.java index 6ec2f5031..d25242e7f 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprEvtInitiator.java +++ b/src/main/java/ch/njol/skript/expressions/ExprEvtInitiator.java @@ -6,6 +6,7 @@ import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -26,7 +27,7 @@ """) @Events("Inventory Item Move") @Since("2.8.0") -public class ExprEvtInitiator extends SimpleExpression { +public class ExprEvtInitiator extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprEvtInitiator.class, Inventory.class, ExpressionType.SIMPLE, "[the] [event-]initiator[( |-)inventory]"); @@ -34,13 +35,14 @@ public class ExprEvtInitiator extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(InventoryMoveItemEvent.class)) { - Skript.error("'event-initiator' can only be used in an 'inventory item move' event."); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(InventoryMoveItemEvent.class); + } + @Override protected Inventory[] get(Event event) { if (!(event instanceof InventoryMoveItemEvent)) diff --git a/src/main/java/ch/njol/skript/expressions/ExprExperience.java b/src/main/java/ch/njol/skript/expressions/ExprExperience.java index fd3785da2..97a51e71a 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprExperience.java +++ b/src/main/java/ch/njol/skript/expressions/ExprExperience.java @@ -4,6 +4,7 @@ import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.*; import ch.njol.skript.events.bukkit.ExperienceSpawnEvent; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -15,10 +16,7 @@ import org.bukkit.event.player.PlayerExpChangeEvent; import org.bukkit.event.player.PlayerFishEvent; import ch.njol.util.coll.CollectionUtils; -import org.bukkit.event.Event; -import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.entity.EntityBreedEvent; -import org.bukkit.event.player.PlayerExpChangeEvent; import org.jetbrains.annotations.Nullable; @Name("Experience") @@ -47,7 +45,7 @@ """) @Since("2.1, 2.5.3 (block break event), 2.7 (experience change event), 2.10 (breeding, fishing)") @Events({"experience spawn", "break / mine", "experience change", "entity breed"}) -public class ExprExperience extends SimpleExpression { +public class ExprExperience extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprExperience.class, Experience.class, ExpressionType.SIMPLE, @@ -56,17 +54,16 @@ public class ExprExperience extends SimpleExpression { @Override public boolean init(Expression[] expressions, int matchedPattern, - Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(ExperienceSpawnEvent.class, BlockBreakEvent.class, - PlayerExpChangeEvent.class, EntityBreedEvent.class, PlayerFishEvent.class)) { - Skript.error("The 'experience' expression can only be used in experience spawn, " + - "block break, player experience change, entity breed or fishing events"); - return false; - } - + Kleenean isDelayed, ParseResult parseResult) { return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(ExperienceSpawnEvent.class, BlockBreakEvent.class, + PlayerExpChangeEvent.class, EntityBreedEvent.class, PlayerFishEvent.class); + } + @Override protected Experience @Nullable [] get(Event event) { Experience[] exp; diff --git a/src/main/java/ch/njol/skript/expressions/ExprExplosionBlockYield.java b/src/main/java/ch/njol/skript/expressions/ExprExplosionBlockYield.java index b24b984bb..38b7b2040 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprExplosionBlockYield.java +++ b/src/main/java/ch/njol/skript/expressions/ExprExplosionBlockYield.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityExplodeEvent; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ """) @Events("explode") @Since("2.5") -public class ExprExplosionBlockYield extends SimpleExpression { +public class ExprExplosionBlockYield extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprExplosionBlockYield.class, Number.class, ExpressionType.PROPERTY, @@ -40,13 +41,14 @@ public class ExprExplosionBlockYield extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityExplodeEvent.class)) { - Skript.error("The 'explosion block yield' is only usable in an explosion event", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityExplodeEvent.class); + } + @Override @Nullable protected Number[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprExplosionYield.java b/src/main/java/ch/njol/skript/expressions/ExprExplosionYield.java index a1012d7f9..f982cbc10 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprExplosionYield.java +++ b/src/main/java/ch/njol/skript/expressions/ExprExplosionYield.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.entity.ExplosionPrimeEvent; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ """) @Events("explosion prime") @Since("2.5") -public class ExprExplosionYield extends SimpleExpression { +public class ExprExplosionYield extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprExplosionYield.class, Number.class, ExpressionType.SIMPLE, @@ -40,13 +41,14 @@ public class ExprExplosionYield extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(ExplosionPrimeEvent.class)) { - Skript.error("The explosion radius is only usable in explosion prime events", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(ExplosionPrimeEvent.class); + } + @Override @Nullable protected Number[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprFertilizedBlocks.java b/src/main/java/ch/njol/skript/expressions/ExprFertilizedBlocks.java index 750967d0b..3050ca08d 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprFertilizedBlocks.java +++ b/src/main/java/ch/njol/skript/expressions/ExprFertilizedBlocks.java @@ -3,6 +3,8 @@ import java.util.Iterator; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.block.BlockFertilizeEvent; import org.jetbrains.annotations.Nullable; @@ -27,7 +29,7 @@ @Events("block fertilize") @Example("the fertilized blocks") @Since("2.5") -public class ExprFertilizedBlocks extends SimpleExpression { +public class ExprFertilizedBlocks extends SimpleExpression implements EventRestrictedSyntax { static { if (Skript.classExists("org.bukkit.event.block.BlockFertilizeEvent")) @@ -36,12 +38,13 @@ public class ExprFertilizedBlocks extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(BlockFertilizeEvent.class)) { - Skript.error("The 'fertilized blocks' are only usable in block fertilize events"); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(BlockFertilizeEvent.class); + } @Nullable @Override diff --git a/src/main/java/ch/njol/skript/expressions/ExprFinalDamage.java b/src/main/java/ch/njol/skript/expressions/ExprFinalDamage.java index d9c80c518..ab2f52611 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprFinalDamage.java +++ b/src/main/java/ch/njol/skript/expressions/ExprFinalDamage.java @@ -1,5 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityDamageEvent; import org.jetbrains.annotations.Nullable; @@ -24,7 +26,7 @@ @Example("send \"%final damage%\" to victim") @Since("2.2-dev19") @Events("damage") -public class ExprFinalDamage extends SimpleExpression { +public class ExprFinalDamage extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprFinalDamage.class, Number.class, ExpressionType.SIMPLE, "[the] final damage"); @@ -32,12 +34,13 @@ public class ExprFinalDamage extends SimpleExpression { @Override public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityDamageEvent.class)) { - Skript.error("The expression 'final damage' can only be used in damage events", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityDamageEvent.class); + } @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprHanging.java b/src/main/java/ch/njol/skript/expressions/ExprHanging.java index 01395ec06..fec9a5ac7 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHanging.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHanging.java @@ -5,11 +5,13 @@ import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.hanging.HangingBreakByEntityEvent; @@ -28,7 +30,7 @@ send "You can't break that item frame!" to hanging remover """) @Since("2.6.2") -public class ExprHanging extends SimpleExpression { +public class ExprHanging extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprHanging.class, Entity.class, ExpressionType.SIMPLE, "[the] hanging (entity|:remover)"); @@ -43,13 +45,15 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye if (isRemover && !getParser().isCurrentEvent(HangingBreakEvent.class)) { Skript.error("The expression 'hanging remover' can only be used in break event"); return false; - } else if (!getParser().isCurrentEvent(HangingBreakEvent.class, HangingPlaceEvent.class)) { - Skript.error("The expression 'hanging entity' can only be used in break and place events"); - return false; } return true; } - + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(HangingBreakEvent.class, HangingPlaceEvent.class); + } + @Override @Nullable public Entity[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprHatchingNumber.java b/src/main/java/ch/njol/skript/expressions/ExprHatchingNumber.java index 6004abae9..aeb93039e 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHatchingNumber.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHatchingNumber.java @@ -7,6 +7,7 @@ import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -28,7 +29,7 @@ """) @Events("Egg Throw") @Since("2.7") -public class ExprHatchingNumber extends SimpleExpression { +public class ExprHatchingNumber extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprHatchingNumber.class, Byte.class, ExpressionType.SIMPLE, @@ -38,13 +39,14 @@ public class ExprHatchingNumber extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerEggThrowEvent.class)) { - Skript.error("You can't use 'the hatching number' outside of a Player Egg Throw event."); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerEggThrowEvent.class); + } + @Override @Nullable protected Byte[] get(Event event) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprHatchingType.java b/src/main/java/ch/njol/skript/expressions/ExprHatchingType.java index 46dcec547..2ff1c118e 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHatchingType.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHatchingType.java @@ -9,6 +9,7 @@ import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.entity.EntityData; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -28,7 +29,7 @@ """) @Events("Egg Throw") @Since("2.7") -public class ExprHatchingType extends SimpleExpression> { +public class ExprHatchingType extends SimpleExpression> implements EventRestrictedSyntax { static { //noinspection unchecked @@ -39,13 +40,14 @@ public class ExprHatchingType extends SimpleExpression> { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerEggThrowEvent.class)) { - Skript.error("You can't use 'the hatching entity type' outside of a Player Egg Throw event."); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerEggThrowEvent.class); + } + @Override @Nullable protected EntityData[] get(Event event) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java b/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java index 433c514e3..ecc962a78 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java @@ -1,6 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityRegainHealthEvent; import org.jetbrains.annotations.Nullable; @@ -29,7 +30,7 @@ """) @Events("heal") @Since("2.5.1") -public class ExprHealAmount extends SimpleExpression { +public class ExprHealAmount extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprHealAmount.class, Double.class, ExpressionType.SIMPLE, "[the] heal[ing] amount"); @@ -39,14 +40,15 @@ public class ExprHealAmount extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityRegainHealthEvent.class)) { - Skript.error("The expression 'heal amount' may only be used in a healing event"); - return false; - } delay = isDelayed; return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityRegainHealthEvent.class); + } + @Nullable @Override protected Double[] get(Event event) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprHostname.java b/src/main/java/ch/njol/skript/expressions/ExprHostname.java index 2ce724c0c..bc892d173 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHostname.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHostname.java @@ -1,5 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerLoginEvent; import org.jetbrains.annotations.Nullable; @@ -23,7 +25,7 @@ send "Welcome back tester!" """) @Since("2.6.1") -public class ExprHostname extends SimpleExpression { +public class ExprHostname extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprHostname.class, String.class, ExpressionType.SIMPLE, "[the] (host|domain)[ ][name]"); @@ -32,12 +34,13 @@ public class ExprHostname extends SimpleExpression { @Override @SuppressWarnings({"null"}) public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerLoginEvent.class)) { - Skript.error("The hostname expression must be used in a player connect event"); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerLoginEvent.class); + } @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprHotbarButton.java b/src/main/java/ch/njol/skript/expressions/ExprHotbarButton.java index cdad734b1..e82f50675 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHotbarButton.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHotbarButton.java @@ -1,5 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.inventory.InventoryClickEvent; import org.jetbrains.annotations.Nullable; @@ -22,7 +24,7 @@ send "You clicked the hotbar button %hotbar button%!" """) @Since("2.5") -public class ExprHotbarButton extends SimpleExpression { +public class ExprHotbarButton extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprHotbarButton.class, Long.class, ExpressionType.SIMPLE, "[the] hotbar button"); @@ -30,12 +32,13 @@ public class ExprHotbarButton extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { - if (!getParser().isCurrentEvent(InventoryClickEvent.class)) { - Skript.error("The 'hotbar button' expression may only be used in an inventory click event."); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(InventoryClickEvent.class); + } @Nullable @Override diff --git a/src/main/java/ch/njol/skript/expressions/ExprHoverList.java b/src/main/java/ch/njol/skript/expressions/ExprHoverList.java index 11d31f9d8..787c08db3 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHoverList.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHoverList.java @@ -3,6 +3,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.*; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -10,9 +11,7 @@ import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import com.destroystokyo.paper.event.server.PaperServerListPingEvent; -import com.destroystokyo.paper.profile.PlayerProfile; import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; @@ -27,9 +26,9 @@ @Description({ "The list when you hover on the player counts of the server in the server list.", "This can be changed using texts or players in a server list ping event only. " + - "Adding players to the list means adding the name of the players.", + "Adding players to the list means adding the name of the players.", "And note that, for example if there are 5 online players (includes fake online count) " + - "in the server and the hover list is set to 3 values, Minecraft will show \"... and 2 more ...\" at end of the list." + "in the server and the hover list is set to 3 values, Minecraft will show \"... and 2 more ...\" at end of the list." }) @Example(""" on server list ping: @@ -40,54 +39,52 @@ """) @Since("2.3") @Events("server list ping") -public class ExprHoverList extends SimpleExpression { +public class ExprHoverList extends SimpleExpression implements EventRestrictedSyntax +{ - static { + static + { Skript.registerExpression(ExprHoverList.class, String.class, ExpressionType.SIMPLE, "[the] [custom] [player|server] (hover|sample) ([message] list|message)", "[the] [custom] player [hover|sample] list"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - private static final boolean HAS_NEW_LISTED_PLAYER_INFO = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent$ListedPlayerInfo"); - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!PAPER_EVENT_EXISTS) { - Skript.error("The hover list expression requires Paper 1.12.2 or newer"); - return false; - } else if (!getParser().isCurrentEvent(PaperServerListPingEvent.class)) { - Skript.error("The hover list expression can't be used outside of a server list ping event"); - return false; - } + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) + { return true; } @Override - @SuppressWarnings({"removal"}) - public String @Nullable [] get(Event event) { - if (!(event instanceof PaperServerListPingEvent)) - return null; + public Class[] supportedEvents() + { + return CollectionUtils.array(PaperServerListPingEvent.class); + } - if (HAS_NEW_LISTED_PLAYER_INFO) { - return ((PaperServerListPingEvent) event).getListedPlayers().stream() - .map(PaperServerListPingEvent.ListedPlayerInfo::name) - .toArray(String[]::new); - } else { - return ((PaperServerListPingEvent) event).getPlayerSample().stream() - .map(PlayerProfile::getName) - .toArray(String[]::new); + @Override + public String @Nullable [] get(Event event) + { + if (!(event instanceof PaperServerListPingEvent pingEvent)) + { + return null; } + + return pingEvent.getListedPlayers().stream() + .map(PaperServerListPingEvent.ListedPlayerInfo::name) + .toArray(String[]::new); } @Override @Nullable - public Class[] acceptChange(ChangeMode mode) { - if (getParser().getHasDelayBefore().isTrue()) { + public Class[] acceptChange(ChangeMode mode) + { + if (getParser().getHasDelayBefore().isTrue()) + { Skript.error("Can't change the hover list anymore after the server list ping event has already passed"); return null; } - switch (mode) { + switch (mode) + { case SET: case ADD: case REMOVE: @@ -100,65 +97,41 @@ public Class[] acceptChange(ChangeMode mode) { @Override @SuppressWarnings({"null", "removal"}) - public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { - if (!(event instanceof PaperServerListPingEvent)) + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) + { + if (!(event instanceof PaperServerListPingEvent pingEvent)) + { return; + } // convert components to legacy strings - if (delta != null) { + if (delta != null) + { delta = Arrays.stream(delta) .map(obj -> obj instanceof Component component ? TextComponentParser.instance().toLegacyString(component) : obj) .toArray(); } - if (HAS_NEW_LISTED_PLAYER_INFO) { - List values = new ArrayList<>(); - if (mode != ChangeMode.DELETE && mode != ChangeMode.RESET && mode != ChangeMode.REMOVE) { - for (Object object : delta) { - if (object instanceof Player) { - Player player = (Player) object; - values.add(new PaperServerListPingEvent.ListedPlayerInfo(player.getName(), player.getUniqueId())); - } else { - values.add(new PaperServerListPingEvent.ListedPlayerInfo((String) object, UUID.randomUUID())); - } - } - } - - List sample = ((PaperServerListPingEvent) event).getListedPlayers(); - switch (mode) { - case SET: - sample.clear(); - // $FALL-THROUGH$ - case ADD: - sample.addAll(values); - break; - case REMOVE: - for (Object value : delta) { - sample.removeIf(profile -> profile.name().equals(value)); - } - break; - case DELETE: - case RESET: - sample.clear(); - break; - } - return; - } - - List values = new ArrayList<>(); - if (mode != ChangeMode.DELETE && mode != ChangeMode.RESET && mode != ChangeMode.REMOVE) { - for (Object object : delta) { - if (object instanceof Player) { + List values = new ArrayList<>(); + if (mode != ChangeMode.DELETE && mode != ChangeMode.RESET && mode != ChangeMode.REMOVE) + { + for (Object object : delta) + { + if (object instanceof Player) + { Player player = (Player) object; - values.add(Bukkit.createProfile(player.getUniqueId(), player.getName())); - } else { - values.add(Bukkit.createProfile(UUID.randomUUID(), (String) object)); + values.add(new PaperServerListPingEvent.ListedPlayerInfo(player.getName(), player.getUniqueId())); + } + else + { + values.add(new PaperServerListPingEvent.ListedPlayerInfo((String) object, UUID.randomUUID())); } } } - List sample = ((PaperServerListPingEvent) event).getPlayerSample(); - switch (mode) { + List sample = pingEvent.getListedPlayers(); + switch (mode) + { case SET: sample.clear(); // $FALL-THROUGH$ @@ -166,8 +139,9 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { sample.addAll(values); break; case REMOVE: - for (Object value : delta) { - sample.removeIf(profile -> profile.getName() != null && profile.getName().equals(value)); + for (Object value : delta) + { + sample.removeIf(profile -> profile.name().equals(value)); } break; case DELETE: @@ -178,17 +152,20 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { } @Override - public boolean isSingle() { + public boolean isSingle() + { return false; } @Override - public Class getReturnType() { + public Class getReturnType() + { return String.class; } @Override - public String toString(@Nullable Event e, boolean debug) { + public String toString(@Nullable Event e, boolean debug) + { return "the hover list"; } diff --git a/src/main/java/ch/njol/skript/expressions/ExprIP.java b/src/main/java/ch/njol/skript/expressions/ExprIP.java index c6da46554..c473b5359 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprIP.java +++ b/src/main/java/ch/njol/skript/expressions/ExprIP.java @@ -48,8 +48,6 @@ public class ExprIP extends SimpleExpression { "IP[( |-)address]"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - @SuppressWarnings("null") private Expression players; @@ -61,7 +59,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye isProperty = matchedPattern < 2; boolean isConnectEvent = getParser().isCurrentEvent(PlayerLoginEvent.class); boolean isServerPingEvent = getParser().isCurrentEvent(ServerListPingEvent.class) || - (PAPER_EVENT_EXISTS && getParser().isCurrentEvent(PaperServerListPingEvent.class)); + getParser().isCurrentEvent(PaperServerListPingEvent.class); if (isProperty) { players = (Expression) exprs[0]; } else if (!isConnectEvent && !isServerPingEvent) { @@ -76,14 +74,15 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye protected String[] get(Event e) { if (!isProperty) { InetAddress address; - if (e instanceof PlayerLoginEvent) + if (e instanceof PlayerLoginEvent loginEvent) { // Return IP address of the connected player in connect event - address = ((PlayerLoginEvent) e).getAddress(); - else if (e instanceof ServerListPingEvent) + address = loginEvent.getAddress(); + } else if (e instanceof ServerListPingEvent pingEvent) // Return IP address of the pinger in server list ping event - address = ((ServerListPingEvent) e).getAddress(); - else + address = pingEvent.getAddress(); + else { return null; + } return CollectionUtils.array(address.getHostAddress()); } @@ -106,7 +105,7 @@ private String getIP(Player player, Event e) { assert sockAddr != null; // Not in connect event address = sockAddr.getAddress(); } - + String hostAddress = address == null ? "unknown" : address.getHostAddress(); assert hostAddress != null; return hostAddress; diff --git a/src/main/java/ch/njol/skript/expressions/ExprLastLoadedServerIcon.java b/src/main/java/ch/njol/skript/expressions/ExprLastLoadedServerIcon.java index c91ecb47e..6fa2aeafa 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLastLoadedServerIcon.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLastLoadedServerIcon.java @@ -26,14 +26,8 @@ public class ExprLastLoadedServerIcon extends SimpleExpression Skript.registerExpression(ExprLastLoadedServerIcon.class, CachedServerIcon.class, ExpressionType.SIMPLE, "[the] [last[ly]] loaded server icon"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!PAPER_EVENT_EXISTS) { - Skript.error("The last loaded server icon expression requires Paper 1.12.2+"); - return false; - } return true; } diff --git a/src/main/java/ch/njol/skript/expressions/ExprMaxPlayers.java b/src/main/java/ch/njol/skript/expressions/ExprMaxPlayers.java index 95a4e6cb5..340e248fc 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprMaxPlayers.java +++ b/src/main/java/ch/njol/skript/expressions/ExprMaxPlayers.java @@ -36,16 +36,12 @@ public class ExprMaxPlayers extends SimpleExpression { ); } - // TODO - remove these fields when Spigot support is dropped - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - private static final boolean SET_MAX_PLAYERS_EXISTS = Skript.methodExists(Server.class, "setMaxPlayers", int.class); - private boolean isReal; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { boolean isServerPingEvent = getParser().isCurrentEvent(ServerListPingEvent.class) || - (PAPER_EVENT_EXISTS && getParser().isCurrentEvent(PaperServerListPingEvent.class)); + getParser().isCurrentEvent(PaperServerListPingEvent.class); if (parseResult.mark == 2 && !isServerPingEvent) { Skript.error("The 'shown' max players count expression can't be used outside of a server list ping event"); @@ -77,11 +73,6 @@ public Class[] acceptChange(ChangeMode mode) { return null; } - if (isReal && !SET_MAX_PLAYERS_EXISTS) { - Skript.error("Modifying the 'real max player count' is only supported on Paper 1.16 and newer"); - return null; - } - switch (mode) { case SET: case ADD: diff --git a/src/main/java/ch/njol/skript/expressions/ExprMendingRepairAmount.java b/src/main/java/ch/njol/skript/expressions/ExprMendingRepairAmount.java index e562b75e3..3892f3bc3 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprMendingRepairAmount.java +++ b/src/main/java/ch/njol/skript/expressions/ExprMendingRepairAmount.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerItemMendEvent; import org.jetbrains.annotations.Nullable; @@ -27,7 +28,7 @@ set the mending repair amount to 100 """) @Since("2.5.1") -public class ExprMendingRepairAmount extends SimpleExpression { +public class ExprMendingRepairAmount extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprMendingRepairAmount.class, Long.class, ExpressionType.SIMPLE, "[the] [mending] repair amount"); @@ -35,13 +36,14 @@ public class ExprMendingRepairAmount extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerItemMendEvent.class)) { - Skript.error("The 'mending repair amount' is only usable in item mend events", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerItemMendEvent.class); + } + @Override protected Long[] get(final Event e) { if (!(e instanceof PlayerItemMendEvent)) diff --git a/src/main/java/ch/njol/skript/expressions/ExprOnlinePlayersCount.java b/src/main/java/ch/njol/skript/expressions/ExprOnlinePlayersCount.java index e0a8af841..bc52420da 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprOnlinePlayersCount.java +++ b/src/main/java/ch/njol/skript/expressions/ExprOnlinePlayersCount.java @@ -38,23 +38,18 @@ public class ExprOnlinePlayersCount extends SimpleExpression { "[the] [(1:(real|default)|2:(fake|shown|displayed))] (count|amount|number|size) of online players"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - private boolean isReal; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - boolean isPaperEvent = PAPER_EVENT_EXISTS && getParser().isCurrentEvent(PaperServerListPingEvent.class); + boolean isListPingEvent = getParser().isCurrentEvent(PaperServerListPingEvent.class); if (parseResult.mark == 2) { - if (!PAPER_EVENT_EXISTS && getParser().isCurrentEvent(ServerListPingEvent.class)) { - Skript.error("The 'fake' online players count expression requires Paper 1.12.2 or newer"); - return false; - } else if (!isPaperEvent) { + if (!isListPingEvent) { Skript.error("The 'fake' online players count expression can't be used outside of a server list ping event"); return false; } } - isReal = (parseResult.mark == 0 && !isPaperEvent) || parseResult.mark == 1; + isReal = (parseResult.mark == 0 && !isListPingEvent) || parseResult.mark == 1; return true; } diff --git a/src/main/java/ch/njol/skript/expressions/ExprParse.java b/src/main/java/ch/njol/skript/expressions/ExprParse.java index edd49d2af..adbe3e01b 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprParse.java +++ b/src/main/java/ch/njol/skript/expressions/ExprParse.java @@ -60,7 +60,10 @@ public class ExprParse extends SimpleExpression { static { - Skript.registerExpression(ExprParse.class, Object.class, ExpressionType.COMBINED, + // Historically, the priority here has been COMBINED; it was changed because property expressions are between COMBINED and PME by default. + // We need `uuid of arg-1 parsed as player` to be ExprUuid(ExprParse(ExprArgument)), not ExprParse(ExprUuid(ExprArgument)) + // The latter can occur because ExprUUID is valid for worlds, which may be converted to from strings. + Skript.registerExpression(ExprParse.class, Object.class, ExpressionType.PATTERN_MATCHES_EVERYTHING, "%string% parsed as (%-*classinfo%|\"<.*>\")"); } diff --git a/src/main/java/ch/njol/skript/expressions/ExprPortal.java b/src/main/java/ch/njol/skript/expressions/ExprPortal.java index 5bde4f7ce..b8c911329 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprPortal.java +++ b/src/main/java/ch/njol/skript/expressions/ExprPortal.java @@ -3,6 +3,8 @@ import java.util.Iterator; import java.util.List; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.event.Event; @@ -30,7 +32,7 @@ """) @Since("2.4") @Events("portal_create") -public class ExprPortal extends SimpleExpression { +public class ExprPortal extends SimpleExpression implements EventRestrictedSyntax { // 1.14+ returns List, 1.13.2 and below returns ArrayList private static final boolean USING_BLOCKSTATE = Skript.isRunningMinecraft(1, 14); @@ -43,10 +45,12 @@ public class ExprPortal extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { - if (getParser().isCurrentEvent(PortalCreateEvent.class)) - return true; - Skript.error("The 'portal' expression may only be used in a portal creation event."); - return false; + return true; + } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PortalCreateEvent.class); } @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprProtocolVersion.java b/src/main/java/ch/njol/skript/expressions/ExprProtocolVersion.java index 26505c244..24e89444d 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprProtocolVersion.java +++ b/src/main/java/ch/njol/skript/expressions/ExprProtocolVersion.java @@ -3,6 +3,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.*; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -33,26 +34,22 @@ """) @Since("2.3") @Events("server list ping") -public class ExprProtocolVersion extends SimpleExpression { +public class ExprProtocolVersion extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprProtocolVersion.class, Long.class, ExpressionType.SIMPLE, "[the] [server] [(sent|required|fake)] protocol version [number]"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!PAPER_EVENT_EXISTS) { - Skript.error("The protocol version expression requires Paper 1.12.2 or newer"); - return false; - } else if (!getParser().isCurrentEvent(PaperServerListPingEvent.class)) { - Skript.error("The protocol version expression can't be used outside of a server list ping event"); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PaperServerListPingEvent.class); + } + @Override @Nullable public Long[] get(Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprRespawnLocation.java b/src/main/java/ch/njol/skript/expressions/ExprRespawnLocation.java index ba480d512..9aede4a2b 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprRespawnLocation.java +++ b/src/main/java/ch/njol/skript/expressions/ExprRespawnLocation.java @@ -1,5 +1,6 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; import org.bukkit.Location; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerRespawnEvent; @@ -27,7 +28,7 @@ set respawn location to {example::spawn} """) @Since("2.2-dev35") -public class ExprRespawnLocation extends SimpleExpression { +public class ExprRespawnLocation extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprRespawnLocation.class, Location.class, ExpressionType.SIMPLE, "[the] respawn location"); @@ -35,12 +36,13 @@ public class ExprRespawnLocation extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(PlayerRespawnEvent.class)) { - Skript.error("The expression 'respawn location' may only be used in the respawn event", ErrorQuality.SEMANTIC_ERROR); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PlayerRespawnEvent.class); + } @Override @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprServerIcon.java b/src/main/java/ch/njol/skript/expressions/ExprServerIcon.java index 722008dc7..e90046abb 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprServerIcon.java +++ b/src/main/java/ch/njol/skript/expressions/ExprServerIcon.java @@ -35,16 +35,10 @@ public class ExprServerIcon extends SimpleExpression { "[the] [(1¦(default)|2¦(shown|sent))] [server] icon"); } - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); - private boolean isServerPingEvent, isDefault; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!PAPER_EVENT_EXISTS) { - Skript.error("The server icon expression requires Paper 1.12.2 or newer"); - return false; - } isServerPingEvent = getParser().isCurrentEvent(PaperServerListPingEvent.class); isDefault = (parseResult.mark == 0 && !isServerPingEvent) || parseResult.mark == 1; if (!isServerPingEvent && !isDefault) { @@ -57,8 +51,8 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye @Override @Nullable public CachedServerIcon[] get(Event e) { - CachedServerIcon icon = null; - if ((isServerPingEvent && !isDefault) && PAPER_EVENT_EXISTS) { + CachedServerIcon icon; + if ((isServerPingEvent && !isDefault)) { if (!(e instanceof PaperServerListPingEvent)) return null; icon = ((PaperServerListPingEvent) e).getServerIcon(); diff --git a/src/main/java/ch/njol/skript/expressions/ExprSourceBlock.java b/src/main/java/ch/njol/skript/expressions/ExprSourceBlock.java index ef2bc7aa5..0274e0c10 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprSourceBlock.java +++ b/src/main/java/ch/njol/skript/expressions/ExprSourceBlock.java @@ -7,11 +7,13 @@ import ch.njol.skript.doc.Events; import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.util.Kleenean; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.block.BlockSpreadEvent; @@ -26,7 +28,7 @@ set the source block to dirt """) @Since("2.7") -public class ExprSourceBlock extends SimpleExpression { +public class ExprSourceBlock extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprSourceBlock.class, Block.class, ExpressionType.SIMPLE, "[the] source block"); @@ -34,13 +36,14 @@ public class ExprSourceBlock extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(BlockSpreadEvent.class)) { - Skript.error("The 'source block' is only usable in a spread event"); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(BlockSpreadEvent.class); + } + @Override protected Block[] get(Event event) { if (!(event instanceof BlockSpreadEvent)) diff --git a/src/main/java/ch/njol/skript/expressions/ExprTamer.java b/src/main/java/ch/njol/skript/expressions/ExprTamer.java index 1f8d9a042..4cb9419d8 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprTamer.java +++ b/src/main/java/ch/njol/skript/expressions/ExprTamer.java @@ -1,5 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityTameEvent; @@ -24,7 +26,7 @@ send "someone tamed something!" to console """) @Since("2.2-dev25") -public class ExprTamer extends SimpleExpression { +public class ExprTamer extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprTamer.class, Player.class, ExpressionType.SIMPLE, "[the] tamer"); @@ -32,12 +34,13 @@ public class ExprTamer extends SimpleExpression { @Override public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parser) { - if (!getParser().isCurrentEvent(EntityTameEvent.class)) { - Skript.error("the expression 'tamer' may only be used in the entity tame event."); - return false; - } return true; } + + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityTameEvent.class); + } @Override protected Player[] get(final Event e) { diff --git a/src/main/java/ch/njol/skript/expressions/ExprUnleashReason.java b/src/main/java/ch/njol/skript/expressions/ExprUnleashReason.java index 3242d51c1..aea0e3fa9 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprUnleashReason.java +++ b/src/main/java/ch/njol/skript/expressions/ExprUnleashReason.java @@ -1,5 +1,7 @@ package ch.njol.skript.expressions; +import ch.njol.skript.lang.EventRestrictedSyntax; +import ch.njol.util.coll.CollectionUtils; import org.bukkit.event.Event; import org.bukkit.event.entity.EntityUnleashEvent; import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; @@ -21,7 +23,7 @@ """) @Events("Leash / Unleash") @Since("2.10") -public class ExprUnleashReason extends EventValueExpression { +public class ExprUnleashReason extends EventValueExpression implements EventRestrictedSyntax { public ExprUnleashReason() { super(UnleashReason.class); @@ -33,13 +35,14 @@ public ExprUnleashReason() { @Override public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityUnleashEvent.class)) { - Skript.error("The 'unleash reason' expression can only be used in an 'unleash' event"); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(EntityUnleashEvent.class); + } + @Override protected UnleashReason[] get(Event event) { if (!(event instanceof EntityUnleashEvent unleashEvent)) diff --git a/src/main/java/ch/njol/skript/expressions/ExprVersionString.java b/src/main/java/ch/njol/skript/expressions/ExprVersionString.java index 05b74bb05..105cf5179 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprVersionString.java +++ b/src/main/java/ch/njol/skript/expressions/ExprVersionString.java @@ -3,6 +3,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.*; +import ch.njol.skript.lang.EventRestrictedSyntax; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; import ch.njol.skript.lang.SkriptParser.ParseResult; @@ -26,9 +27,7 @@ """) @Since("2.3") @Events("Server List Ping") -public class ExprVersionString extends SimpleExpression { - - private static final boolean PAPER_EVENT_EXISTS = Skript.classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); +public class ExprVersionString extends SimpleExpression implements EventRestrictedSyntax { static { Skript.registerExpression(ExprVersionString.class, String.class, ExpressionType.SIMPLE, "[the] [shown|custom] version [string|text]"); @@ -36,16 +35,14 @@ public class ExprVersionString extends SimpleExpression { @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!PAPER_EVENT_EXISTS) { - Skript.error("The 'version string' expression requires Paper 1.12.2+"); - return false; - } else if (!getParser().isCurrentEvent(PaperServerListPingEvent.class)) { - Skript.error("The 'version string' expression can't be used outside of a 'server list ping' event"); - return false; - } return true; } + @Override + public Class[] supportedEvents() { + return CollectionUtils.array(PaperServerListPingEvent.class); + } + @Override @Nullable public String[] get(Event event) { diff --git a/src/main/java/ch/njol/skript/expressions/base/WrapperExpression.java b/src/main/java/ch/njol/skript/expressions/base/WrapperExpression.java index 4aed8cf1a..a90d35411 100644 --- a/src/main/java/ch/njol/skript/expressions/base/WrapperExpression.java +++ b/src/main/java/ch/njol/skript/expressions/base/WrapperExpression.java @@ -14,7 +14,7 @@ import java.util.Iterator; /** - * Represents an expression which is a wrapper of another one. Remember to set the wrapped expression in the constructor ({@link #WrapperExpression(SimpleExpression)}) + * Represents an expression which is a wrapper of another one. Remember to set the wrapped expression in the constructor ({@link #WrapperExpression(Expression)}) * or with {@link #setExpr(Expression)} in {@link SyntaxElement#init(Expression[], int, Kleenean, ParseResult) init()}.
* If you override {@link #get(Event)} you must override {@link #iterator(Event)} as well. * @@ -27,7 +27,7 @@ public abstract class WrapperExpression extends SimpleExpression { @SuppressWarnings("null") protected WrapperExpression() {} - public WrapperExpression(SimpleExpression expr) { + public WrapperExpression(Expression expr) { this.expr = expr; } diff --git a/src/main/java/ch/njol/skript/patterns/TypePatternElement.java b/src/main/java/ch/njol/skript/patterns/TypePatternElement.java index 7759d430d..f1fcb9244 100644 --- a/src/main/java/ch/njol/skript/patterns/TypePatternElement.java +++ b/src/main/java/ch/njol/skript/patterns/TypePatternElement.java @@ -132,7 +132,6 @@ public static TypePatternElement fromString(String string, int expressionIndex) } // time states need to match and be valid if (!applyTimeState(expression)) { - loopLog.printError(); return null; } diff --git a/src/main/java/ch/njol/skript/util/slot/InventorySlot.java b/src/main/java/ch/njol/skript/util/slot/InventorySlot.java index c97788bf0..047507832 100644 --- a/src/main/java/ch/njol/skript/util/slot/InventorySlot.java +++ b/src/main/java/ch/njol/skript/util/slot/InventorySlot.java @@ -58,7 +58,7 @@ public int getRawIndex() { if (index == -999) //Non-existent slot, e.g. Outside GUI return null; ItemStack item = inventory.getItem(index); - return item == null ? new ItemStack(Material.AIR, 1) : item.clone(); + return item == null ? new ItemStack(Material.AIR, 1) : item; } @Override diff --git a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java index 816b70464..7669a8a51 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java +++ b/src/main/java/org/skriptlang/skript/bukkit/block/sign/elements/expressions/ExprSignText.java @@ -58,11 +58,24 @@ public boolean init(final Expression[] exprs, final int matchedPattern, final block = (Expression) exprs[exprs.length - 1]; return true; } - + + private int getLine(Event event) { + Integer line = this.line.getSingle(event); + if (line == null) { + return -1; + } + if (line < 1 || line > 4) { + error("Signs only have lines from 1 to 4, but tried to obtain line " + line); + return -1; + } + line--; // we accept 1-indexed, convert to 0-indexed + return line; + } + @Override protected Component[] get(Event event) { - Integer line = this.line.getSingle(event); - if (line == null || line < 0 || line > 3) { + int line = getLine(event); + if (line == -1) { return new Component[0]; } if (getTime() >= 0 && block.isDefault() && event instanceof SignChangeEvent signEvent && !Delay.isDelayed(event)) { @@ -86,8 +99,8 @@ protected Component[] get(Event event) { @Override public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { - Integer line = this.line.getSingle(event); - if (line == null || line < 0 || line > 3) { + int line = getLine(event); + if (line == -1) { return; } diff --git a/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java b/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java index 4c6af3868..152dec37b 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java +++ b/src/main/java/org/skriptlang/skript/bukkit/lang/eventvalue/EventValueRegistryImpl.java @@ -31,8 +31,10 @@ public void register(EventValue eventValue) { Preconditions.checkNotNull(eventValue, "eventValue"); if (eventValue instanceof ConvertedEventValue) throw new SkriptAPIException("Cannot register a converted event value: " + eventValue); - if (isRegistered(eventValue)) - throw new SkriptAPIException(eventValue + " is already registered"); + if (isRegistered(eventValue)) { + Skript.warning(eventValue + " is already registered."); + return; + } List> eventValues = eventValues(eventValue.time()); eventValues.add(eventValue); eventValuesCache.clear(); diff --git a/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentParser.java b/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentParser.java index ccc3a5ed4..c28f85edb 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentParser.java +++ b/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentParser.java @@ -1,7 +1,6 @@ package org.skriptlang.skript.bukkit.text; import ch.njol.skript.registrations.Classes; -import ch.njol.util.StringUtils; import ch.njol.util.coll.CollectionUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; @@ -21,7 +20,6 @@ import net.kyori.adventure.text.minimessage.tag.standard.StandardTags; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import org.bukkit.ChatColor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -119,6 +117,18 @@ public TextReplacementConfig textReplacementConfig() { */ private static final Pattern MULTI_WORD_COLOR_PATTERN = Pattern.compile("(\\\\*)<([a-zA-Z]+ [a-zA-Z]+)>"); + /** + * A pattern for matching double hashtag hex color tags ({@code <##123456>}). + * It also matches all preceding backslashes to determine whether the supposed tag is escaped. + */ + private static final Pattern LEGACY_DOUBLE_HASHTAG_PATTERN = Pattern.compile("(\\\\*)<(##[a-f0-9]{6})>"); + + /** + * A pattern for matching legacy hex codes ({@code &x&1&2&3&4&5&6}). + * It also matches all preceding backslashes to determine whether the supposed tag is escaped. + */ + static final Pattern LEGACY_CODE_PATTERN = Pattern.compile("(\\\\*)([&§][a-f0-9klomnr])"); + static { INSTANCE = new TextComponentParser(); } @@ -444,17 +454,27 @@ private Component parse(Object message, boolean safe) { public String reformatText(String text) { // TODO improve... // replace spaces with underscores for simple tags - text = StringUtils.replaceAll(text, MULTI_WORD_COLOR_PATTERN, matcher -> { - if (matcher.group(1).length() % 2 == 1) { // tag is escaped - return Matcher.quoteReplacement(matcher.group()); + text = MULTI_WORD_COLOR_PATTERN.matcher(text).replaceAll(result -> { + if (result.group(1).length() % 2 == 1) { // tag is escaped + return Matcher.quoteReplacement(result.group()); } - String mappedTag = matcher.group(2).replace(" ", "_"); + String mappedTag = result.group(2).replace(" ", "_"); if (simplePlaceholders.containsKey(mappedTag) || StandardTags.color().has(mappedTag)) { // only replace if it makes a valid tag - return Matcher.quoteReplacement(matcher.group(1) + "<" + mappedTag + ">"); + return Matcher.quoteReplacement(result.group(1) + "<" + mappedTag + ">"); } - return Matcher.quoteReplacement(matcher.group()); + return Matcher.quoteReplacement(result.group()); + }); + + text = LEGACY_DOUBLE_HASHTAG_PATTERN.matcher(text).replaceAll(result -> { + if (result.group(1).length() % 2 == 1) { // tag is escaped + return Matcher.quoteReplacement(result.group()); + } + String mappedTag = result.group(2).substring(1); + if (StandardTags.color().has(mappedTag)) { + return Matcher.quoteReplacement("<" + mappedTag + ">"); + } + return Matcher.quoteReplacement(result.group()); }); - assert text != null; // legacy compatibility, transform color codes into tags text = TextComponentUtils.replaceLegacyFormattingCodes(text); @@ -471,25 +491,23 @@ public String reformatText(String text) { public String escape(String string) { // legacy compatibility, escape color codes if (string.contains("&") || string.contains("§")) { - StringBuilder reconstructedString = new StringBuilder(); - char[] messageChars = string.toCharArray(); - for (int i = 0; i < messageChars.length; i++) { - char current = messageChars[i]; - char next = (i + 1 != messageChars.length) ? messageChars[i + 1] : ' '; - boolean isCode = (current == '&' || current == '§') && (i == 0 || messageChars[i - 1] != '\\'); - if (isCode && next == 'x' && i + 13 <= messageChars.length) { // assume hex -> &x&1&2&3&4&5&6 - reconstructedString.append('\\'); - for (int i2 = i; i2 < i + 14; i2++) { // append the rest of the hex code, don't escape these symbols - reconstructedString.append(messageChars[i2]); - } - i += 13; // skip to the end - } else if (isCode && ChatColor.getByChar(next) != null) { - reconstructedString.append('\\'); + string = LEGACY_CODE_PATTERN.matcher(string).replaceAll(result -> { + if (result.group(1).length() % 2 == 1) { // tag is already escaped + return Matcher.quoteReplacement(result.group()); } - reconstructedString.append(current); - } - string = reconstructedString.toString(); + return Matcher.quoteReplacement('\\' + result.group()); + }); } + string = LEGACY_DOUBLE_HASHTAG_PATTERN.matcher(string).replaceAll(result -> { + if (result.group(1).length() % 2 == 1) { // tag is escaped + return Matcher.quoteReplacement(result.group()); + } + String mappedTag = result.group(2).substring(1); + if (StandardTags.color().has(mappedTag)) { + return Matcher.quoteReplacement("\\<#" + mappedTag) + ">"; + } + return Matcher.quoteReplacement(result.group()); + }); return parser.escapeTags(string); } diff --git a/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentUtils.java b/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentUtils.java index d829afb66..24aa974ea 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentUtils.java +++ b/src/main/java/org/skriptlang/skript/bukkit/text/TextComponentUtils.java @@ -13,6 +13,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Utilities for working with {@link Component}s. @@ -71,49 +73,48 @@ private static Component appendToLastChild(Component base, Component appendee) { return base.children(baseChildren); } + /** + * A pattern for matching standard legacy codes ({@code &1}). + * It also matches all preceding backslashes to determine whether the supposed tag is escaped. + */ + private static final Pattern LEGACY_HEX_PATTERN = Pattern.compile("[&§]x(?:[&§][a-f0-9]){6}"); + /** * Replaces all legacy formatting codes in a string with {@link net.kyori.adventure.text.minimessage.MiniMessage} equivalents. * @param text The string to reformat. * @return Reformatted {@code text}. */ public static String replaceLegacyFormattingCodes(String text) { - char[] chars = text.toCharArray(); - boolean hasLegacyFormatting = false; - for (char ch : chars) { - if (ch == '&' || ch == '§') { - hasLegacyFormatting = true; - break; - } - } - if (!hasLegacyFormatting) { + if (!text.contains("&") && !text.contains("§")) { return text; } - StringBuilder reconstructedMessage = new StringBuilder(); - for (int i = 0; i < chars.length; i++) { - char current = chars[i]; - char next = (i + 1 != chars.length) ? chars[i + 1] : ' '; - boolean isCode = (current == '&' || current == '§') && (i == 0 || chars[i - 1] != '\\'); - if (isCode && next == 'x' && i + 13 <= chars.length) { // try to parse as hex -> &x&1&2&3&4&5&6 - reconstructedMessage.append("<#"); - for (int i2 = i + 3; i2 < i + 14; i2 += 2) { // isolate the specific numbers - reconstructedMessage.append(chars[i2]); - } - reconstructedMessage.append('>'); - i += 13; // skip to the end - } else if (isCode) { - ChatColor color = ChatColor.getByChar(next); - if (color != null) { // this is a valid code - reconstructedMessage.append('<').append(color.asBungee().getName()).append('>'); - i++; // skip to the end - } else { // not a valid color :( - reconstructedMessage.append(current); - } - } else { - reconstructedMessage.append(current); + text = LEGACY_HEX_PATTERN.matcher(text).replaceAll(result -> { + String hex = result.group(); + StringBuilder replacement = new StringBuilder(); + replacement.append("<#"); + for (int i = 3; i <= 13; i += 2) { // isolate the specific numbers + replacement.append(hex.charAt(i)); } - } - return reconstructedMessage.toString(); + replacement.append('>'); + return Matcher.quoteReplacement(replacement.toString()); + }); + + text = TextComponentParser.LEGACY_CODE_PATTERN.matcher(text).replaceAll(result -> { + String backslashes = result.group(1); + if (backslashes.length() % 2 == 1) { // tag is escaped + return Matcher.quoteReplacement(result.group().substring(1)); + } else if (!backslashes.isEmpty()) { + backslashes = backslashes.substring(1); + } + StringBuilder replacement = new StringBuilder(backslashes); + ChatColor color = ChatColor.getByChar(result.group(2).charAt(1)); + assert color != null; + replacement.append('<').append(color.asBungee().getName()).append('>'); + return Matcher.quoteReplacement(replacement.toString()); + }); + + return text; } /** diff --git a/src/main/java/org/skriptlang/skript/bukkit/text/TextModule.java b/src/main/java/org/skriptlang/skript/bukkit/text/TextModule.java index da3d29393..cb270e111 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/text/TextModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/text/TextModule.java @@ -21,7 +21,7 @@ public TextModule(AddonModule parentModule) { @Override public void initSelf(SkriptAddon addon) { - Classes.registerClass(new TextComponentClassInfo()); + Classes.registerClass(new TextComponentClassInfo(addon)); Classes.registerClass(new AudienceClassInfo()); Converters.registerConverter(String.class, Component.class, diff --git a/src/main/java/org/skriptlang/skript/bukkit/text/elements/expressions/ExprStringColor.java b/src/main/java/org/skriptlang/skript/bukkit/text/elements/expressions/ExprStringColor.java index 2d1b3c967..b8ec9c58e 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/text/elements/expressions/ExprStringColor.java +++ b/src/main/java/org/skriptlang/skript/bukkit/text/elements/expressions/ExprStringColor.java @@ -136,6 +136,9 @@ private List getColors(String string) { continue; } String tag = string.substring(index + 1, end); + if (tag.isEmpty()) { // malformed (empty tag) + continue; + } if (tag.charAt(0) == '#') { // should be a hex tag = tag.substring(1); int tagLength = tag.length(); diff --git a/src/main/java/org/skriptlang/skript/bukkit/text/types/TextComponentClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/text/types/TextComponentClassInfo.java index 2308e88f2..21f52c2c3 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/text/types/TextComponentClassInfo.java +++ b/src/main/java/org/skriptlang/skript/bukkit/text/types/TextComponentClassInfo.java @@ -1,20 +1,26 @@ package org.skriptlang.skript.bukkit.text.types; +import ch.njol.skript.SkriptConfig; import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.classes.Parser; import ch.njol.skript.classes.Serializer; import ch.njol.skript.lang.ParseContext; +import ch.njol.util.StringUtils; import ch.njol.yggdrasil.Fields; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.json.JSONComponentSerializer; import org.jetbrains.annotations.ApiStatus; +import org.skriptlang.skript.addon.SkriptAddon; +import org.skriptlang.skript.bukkit.text.TextComponentParser; +import org.skriptlang.skript.lang.properties.Property; +import org.skriptlang.skript.lang.properties.handlers.ContainsHandler; import java.io.StreamCorruptedException; @ApiStatus.Internal public final class TextComponentClassInfo extends ClassInfo { - public TextComponentClassInfo() { + public TextComponentClassInfo(SkriptAddon addon) { super(Component.class, "textcomponent"); this.user("text ?components?") .name("Text Component") @@ -23,7 +29,24 @@ public TextComponentClassInfo() { .examples("\"This text is red and bold!\"") .since("2.15") .parser(new TextComponentParser()) - .serializer(new TextComponentSerializer()); + .serializer(new TextComponentSerializer()) + .property(Property.CONTAINS, + "Components can contain other components.", + addon, + new ContainsHandler() { + @Override + public boolean contains(Component container, Component element) { + var parser = org.skriptlang.skript.bukkit.text.TextComponentParser.instance(); + return StringUtils.contains(parser.toString(container), parser.toString(element), SkriptConfig.caseSensitive.value()); + } + + @Override + public Class[] elementTypes() { + //noinspection unchecked + return new Class[]{Component.class}; + } + } + ); } private static final class TextComponentParser extends Parser { diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java index 136f503be..94514f17a 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java @@ -32,6 +32,7 @@ public final class FunctionReference implements Debuggable { private final Signature signature; private final Argument>[] arguments; + private boolean unloaded = false; private Function cachedFunction; private LinkedHashMap cachedArguments; @@ -40,9 +41,9 @@ private record ArgInfo(Expression expression, Class type, Set mo } public FunctionReference(@Nullable String namespace, - @NotNull String name, - @NotNull Signature signature, - @NotNull Argument>[] arguments) { + @NotNull String name, + @NotNull Signature signature, + @NotNull Argument>[] arguments) { Preconditions.checkNotNull(name, "name cannot be null"); Preconditions.checkNotNull(signature, "signature cannot be null"); Preconditions.checkNotNull(arguments, "arguments cannot be null"); @@ -57,7 +58,7 @@ public FunctionReference(@Nullable String namespace, * Invalidate the cached function used in this reference. */ public void invalidateCache() { - cachedFunction = null; + unloaded = true; } /** @@ -147,17 +148,17 @@ public T execute(Event event) { return null; } - LinkedHashMap args = new LinkedHashMap<>(); + SequencedMap args = new LinkedHashMap<>(); cachedArguments.forEach((k, v) -> { if (v.modifiers().contains(Modifier.KEYED)) { - args.put(k, evaluateKeyed(v.expression(), event)); + args.put(k, Classes.clone(evaluateKeyed(v.expression(), event))); return; } if (!v.type().isArray()) { - args.put(k, v.expression().getSingle(event)); + args.put(k, Classes.clone(v.expression().getSingle(event))); } else { - args.put(k, v.expression().getArray(event)); + args.put(k, Classes.clone(v.expression().getArray(event))); } }); @@ -225,7 +226,7 @@ private KeyedValue[] evaluateParameter(Expression argument, Event event) { * @return The function referred to by this reference. */ public Function function() { - if (cachedFunction == null) { + if (unloaded || cachedFunction == null) { Class[] parameters = Arrays.stream(signature.parameters().all()) .map(Parameter::type) .toArray(Class[]::new); @@ -235,6 +236,7 @@ public Function function() { if (retrieval.result() == RetrievalResult.EXACT) { //noinspection unchecked cachedFunction = (Function) retrieval.retrieved(); + unloaded = false; } } diff --git a/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentParserTest.java b/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentParserTest.java index f5e433e71..fbae9a5c3 100644 --- a/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentParserTest.java +++ b/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentParserTest.java @@ -4,6 +4,8 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.ChatColor; import org.junit.Test; import java.util.Set; @@ -51,4 +53,33 @@ public void testColorsCauseReset() { assertEquals(expected, parsed); } + @Test + public void testLegacy() { + TextComponentParser parser = new TextComponentParser(); + assertEquals(parser.parse("hello"), parser.parse("&chello")); + assertEquals(parser.parse("<#123456>hello"), parser.parse("&x&1&2&3&4&5&6hello")); + assertEquals(Component.text("&chello"), parser.parse("\\&chello")); + assertEquals(Component.text("&x&1&2&3&4&5&6hello"), parser.parse("&x\\&1\\&2\\&3\\&4\\&5\\&6hello")); + + //noinspection deprecation - yes i know + for (ChatColor color : ChatColor.values()) { + String message = "&" + color.getChar() + "hello"; + assertEquals(LegacyComponentSerializer.legacyAmpersand().deserialize(message), parser.parse(message)); + } + } + + @Test + public void testLegacyEscaping() { + TextComponentParser parser = new TextComponentParser(); + assertEquals("\\&chello", parser.escape("&chello")); + assertEquals("&x\\&1\\&2\\&3\\&4\\&5\\&6hello &x", parser.escape("&x&1&2&3&4&5&6hello &x")); + } + + @Test + public void testLegacyDoubleHashtag() { + TextComponentParser parser = new TextComponentParser(); + assertEquals(parser.parse("<#123456>hello"), parser.parse("<##123456>hello")); + assertEquals("\\<##123456>hello", parser.escape("<##123456>hello")); + } + } diff --git a/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentUtilsTest.java b/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentUtilsTest.java new file mode 100644 index 000000000..c8bc544c4 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/bukkit/text/TextComponentUtilsTest.java @@ -0,0 +1,24 @@ +package org.skriptlang.skript.bukkit.text; + +import org.junit.Test; + +import static org.junit.Assert.*; + +import static org.skriptlang.skript.bukkit.text.TextComponentUtils.replaceLegacyFormattingCodes; + +public class TextComponentUtilsTest { + + @Test + public void testReplaceLegacyFormattingCodes() { + // validate code escaping + assertEquals("Hello!", replaceLegacyFormattingCodes("&cHello!")); + assertEquals("&cHello!", replaceLegacyFormattingCodes("\\&cHello!")); + assertEquals("\\Hello!", replaceLegacyFormattingCodes("\\\\&cHello!")); + assertEquals("\\\\&cHello!", replaceLegacyFormattingCodes("\\\\\\&cHello!")); + assertEquals("<#123456>Hello!", replaceLegacyFormattingCodes("&x&1&2&3&4&5&6Hello!")); + assertEquals("<#123456>Hello!", replaceLegacyFormattingCodes("&x&1&2&3&4&5&6&cHello!")); + // validate internal metacharacter escaping + assertEquals("You have $10", replaceLegacyFormattingCodes("&cYou have $10")); + } + +} diff --git a/src/test/skript/tests/bukkit/TextModule.sk b/src/test/skript/tests/bukkit/TextModule.sk index 519c92388..0d642678b 100644 --- a/src/test/skript/tests/bukkit/TextModule.sk +++ b/src/test/skript/tests/bukkit/TextModule.sk @@ -6,3 +6,7 @@ test "component serialization": parse: set {x} to line 1 of lore of {_item} assert last parse logs are not set + +test "component contains": + set {_item} to a dirt block with lore "Hello world!" + assert last element of lore of {_item} contains "Hello" diff --git a/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk b/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk new file mode 100644 index 000000000..eb06a8266 --- /dev/null +++ b/src/test/skript/tests/bukkit/block/sign/ExprSignText.sk @@ -0,0 +1,22 @@ +using error catching + +test "ExprSignText": + # test setting/getting + set {_old_block} to event-block + set {_old_block_below} to block below event-block + set block below event-block to dirt + set block at event-block to oak sign + + loop 4 times: + set line loop-number of event-block to "%loop-number%" + assert line loop-number of event-block is "%loop-number%" with "setting line %loop-number% did not work" + + set block at event-block to {_old_block} + set block below event-block to {_old_block_below} + + # test invalid line for get/set + catch runtime errors: + set {_x} to line 0 of {_sign} + set line 5 of {_sign} to "5" + assert last caught runtime errors contains "Signs only have lines from 1 to 4, but tried to obtain line 0" + assert last caught runtime errors contains "Signs only have lines from 1 to 4, but tried to obtain line 5" diff --git a/src/test/skript/tests/regressions/8550-string colours sioobe.sk b/src/test/skript/tests/regressions/8550-string colours sioobe.sk new file mode 100644 index 000000000..449dece6a --- /dev/null +++ b/src/test/skript/tests/regressions/8550-string colours sioobe.sk @@ -0,0 +1,3 @@ +test "empty tags with colours of string expression": + # throws exception if not fixed + set {_} to string colour of "<>" diff --git a/src/test/skript/tests/regressions/8584-adding lore to slot.sk b/src/test/skript/tests/regressions/8584-adding lore to slot.sk new file mode 100644 index 000000000..e9b942e74 --- /dev/null +++ b/src/test/skript/tests/regressions/8584-adding lore to slot.sk @@ -0,0 +1,5 @@ +test "regression 8584 - adding lore to slot": + set {_inv} to a new chest inventory named "Testing Chest" + set slot 0 of {_inv} to a book + add "Hello world!" to lore of (slot 0 of {_inv}) + assert lore of (slot 0 of {_inv}) is "Hello world!" diff --git a/src/test/skript/tests/regressions/8599-using string based operations on component syntaxes.sk b/src/test/skript/tests/regressions/8599-using string based operations on component syntaxes.sk new file mode 100644 index 000000000..c72c54baa --- /dev/null +++ b/src/test/skript/tests/regressions/8599-using string based operations on component syntaxes.sk @@ -0,0 +1,4 @@ +test "regression 8599 - using string based operations on component syntaxes": + set {_item} to a dirt block named "Dirty Dirt" + replace all "Dirt" with "Dirty Dirt" in the name of {_item} + assert the name of {_item} is "Dirty Dirty Dirty Dirt" diff --git a/src/test/skript/tests/syntaxes/structures/StructFunction.sk b/src/test/skript/tests/syntaxes/structures/StructFunction.sk index 98d07f5b0..3d3eee94c 100644 --- a/src/test/skript/tests/syntaxes/structures/StructFunction.sk +++ b/src/test/skript/tests/syntaxes/structures/StructFunction.sk @@ -16,6 +16,9 @@ local function bar() :: boolean: local function _blob() :: string: return "blub" +local function mutate(v: vector) :: vector: + add 1 to x of {_v} + test "functions": assert foo() is true with "function return type failed" assert local() is not 1 with "global function parsed before local function" @@ -208,3 +211,8 @@ test "duplicate keys in function arguments": set {_a::foo} to "first" set {_b::foo} to "second" assert duplicate_keys_test(keyed {_a::*}, keyed {_b::*}) is "first" + +test "clone objects at function boundary": + set {_vector} to vector(0, 0, 0) + mutate({_vector}) + assert {_vector} is vector(0, 0, 0) with "Values aren't cloned at function boundary"