From e95a35ac2434a275aafc529a2334c54fc25633f4 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Mon, 3 Jun 2024 04:33:09 -0400 Subject: [PATCH 01/48] Fix ExprArithmetic Set Construction (#6758) ExprArithmetic: change set construction --- .../ch/njol/skript/expressions/arithmetic/ExprArithmetic.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/arithmetic/ExprArithmetic.java b/src/main/java/ch/njol/skript/expressions/arithmetic/ExprArithmetic.java index 4e928b285ec..cffd4515c9b 100644 --- a/src/main/java/ch/njol/skript/expressions/arithmetic/ExprArithmetic.java +++ b/src/main/java/ch/njol/skript/expressions/arithmetic/ExprArithmetic.java @@ -35,6 +35,7 @@ import ch.njol.skript.util.LiteralUtils; import ch.njol.skript.util.Patterns; import ch.njol.util.Kleenean; +import com.google.common.collect.ImmutableSet; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; import org.skriptlang.skript.lang.arithmetic.Arithmetics; @@ -43,7 +44,6 @@ import java.lang.reflect.Array; import java.util.ArrayList; -import java.util.Set; import java.util.List; import java.util.Collection; @@ -258,7 +258,7 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye return error(firstClass, secondClass); } else { returnType = (Class) Classes.getSuperClassInfo(returnTypes).getC(); - knownReturnTypes = Set.of(returnTypes); + knownReturnTypes = ImmutableSet.copyOf(returnTypes); } } else if (returnType == null) { // lookup OperationInfo operationInfo = (OperationInfo) Arithmetics.lookupOperationInfo(operator, firstClass, secondClass); From b9a7ab8445092cd6fa9166f286425aa04432d58e Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Thu, 6 Jun 2024 12:28:02 -0400 Subject: [PATCH 02/48] Fix PotionData NPE (#6757) --- .../classes/data/BukkitEventValues.java | 32 +++------------ .../njol/skript/util/PotionEffectUtils.java | 41 ++++++------------- ...756-potion missing base type causes npe.sk | 3 ++ 3 files changed, 22 insertions(+), 54 deletions(-) create mode 100644 src/test/skript/tests/regressions/6756-potion missing base type causes npe.sk diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index d026bc55a19..d8e9ea0b94e 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -18,9 +18,6 @@ */ package ch.njol.skript.classes.data; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -662,34 +659,17 @@ public LivingEntity[] get(AreaEffectCloudApplyEvent event) { } }, EventValues.TIME_NOW); EventValues.registerEventValue(AreaEffectCloudApplyEvent.class, PotionEffectType.class, new Getter() { - @Nullable - private final MethodHandle BASE_POTION_DATA_HANDLE; - - { - MethodHandle basePotionDataHandle = null; - if (Skript.methodExists(AreaEffectCloud.class, "getBasePotionData")) { - try { - basePotionDataHandle = MethodHandles.lookup().findVirtual(AreaEffectCloud.class, "getBasePotionData", MethodType.methodType(PotionData.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - Skript.exception(e, "Failed to load legacy potion data support. Potions may not work as expected."); - } - } - BASE_POTION_DATA_HANDLE = basePotionDataHandle; - } - + private final boolean HAS_POTION_TYPE_METHOD = Skript.methodExists(AreaEffectCloud.class, "getBasePotionType"); @Override @Nullable public PotionEffectType get(AreaEffectCloudApplyEvent e) { - if (BASE_POTION_DATA_HANDLE != null) { - try { - return ((PotionData) BASE_POTION_DATA_HANDLE.invoke(e.getEntity())).getType().getEffectType(); - } catch (Throwable ex) { - throw Skript.exception(ex, "An error occurred while trying to invoke legacy area effect cloud potion effect support."); - } - } else { + // TODO needs to be reworked to support multiple values (there can be multiple potion effects) + if (HAS_POTION_TYPE_METHOD) { PotionType base = e.getEntity().getBasePotionType(); - if (base != null) // TODO this is deprecated... this should become a multi-value event value + if (base != null) return base.getEffectType(); + } else { + return e.getEntity().getBasePotionData().getType().getEffectType(); } return null; } diff --git a/src/main/java/ch/njol/skript/util/PotionEffectUtils.java b/src/main/java/ch/njol/skript/util/PotionEffectUtils.java index bc72f768ae6..88f4bfbfe7c 100644 --- a/src/main/java/ch/njol/skript/util/PotionEffectUtils.java +++ b/src/main/java/ch/njol/skript/util/PotionEffectUtils.java @@ -18,9 +18,6 @@ */ package ch.njol.skript.util; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -346,20 +343,7 @@ else if (HAS_SUSPICIOUS_META && meta instanceof SuspiciousStewMeta) itemType.setItemMeta(meta); } - @Nullable - private static final MethodHandle BASE_POTION_DATA_HANDLE; - - static { - MethodHandle basePotionDataHandle = null; - if (Skript.methodExists(PotionMeta.class, "getBasePotionData")) { - try { - basePotionDataHandle = MethodHandles.lookup().findVirtual(PotionMeta.class, "getBasePotionData", MethodType.methodType(PotionData.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - Skript.exception(e, "Failed to load legacy potion data support. Potions may not work as expected."); - } - } - BASE_POTION_DATA_HANDLE = basePotionDataHandle; - } + private static final boolean HAS_POTION_TYPE_METHOD = Skript.methodExists(PotionMeta.class, "hasBasePotionType"); /** * Get all the PotionEffects of an ItemType @@ -374,21 +358,22 @@ public static List getEffects(ItemType itemType) { ItemMeta meta = itemType.getItemMeta(); if (meta instanceof PotionMeta) { PotionMeta potionMeta = ((PotionMeta) meta); - effects.addAll(potionMeta.getCustomEffects()); - if (BASE_POTION_DATA_HANDLE != null) { - try { - effects.addAll(PotionDataUtils.getPotionEffects((PotionData) BASE_POTION_DATA_HANDLE.invoke(meta))); - } catch (Throwable e) { - throw Skript.exception(e, "An error occurred while trying to invoke legacy potion data support."); + if (potionMeta.hasCustomEffects()) + effects.addAll(potionMeta.getCustomEffects()); + if (HAS_POTION_TYPE_METHOD) { + if (potionMeta.hasBasePotionType()) { + //noinspection ConstantConditions - checked via hasBasePotionType + effects.addAll(potionMeta.getBasePotionType().getPotionEffects()); + } + } else { // use deprecated method + PotionData data = potionMeta.getBasePotionData(); + if (data != null) { + effects.addAll(PotionDataUtils.getPotionEffects(data)); } - } else if (potionMeta.hasBasePotionType()) { - //noinspection ConstantConditions - checked via hasBasePotionType - effects.addAll(potionMeta.getBasePotionType().getPotionEffects()); } - } else if (HAS_SUSPICIOUS_META && meta instanceof SuspiciousStewMeta) effects.addAll(((SuspiciousStewMeta) meta).getCustomEffects()); return effects; } - + } diff --git a/src/test/skript/tests/regressions/6756-potion missing base type causes npe.sk b/src/test/skript/tests/regressions/6756-potion missing base type causes npe.sk new file mode 100644 index 00000000000..8ffa025b5e6 --- /dev/null +++ b/src/test/skript/tests/regressions/6756-potion missing base type causes npe.sk @@ -0,0 +1,3 @@ +test "potion missing base type": + + assert potion effects of (plain potion of mundane) is not set with "it should not have any effects" From 23cc26ba1328bc48c8a65154cdc329638ea43b82 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Thu, 6 Jun 2024 12:50:28 -0400 Subject: [PATCH 03/48] Fix Command Syncing CME (#6763) --- .../njol/skript/structures/StructCommand.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/njol/skript/structures/StructCommand.java b/src/main/java/ch/njol/skript/structures/StructCommand.java index 5947a66ca52..e50476e5a8e 100644 --- a/src/main/java/ch/njol/skript/structures/StructCommand.java +++ b/src/main/java/ch/njol/skript/structures/StructCommand.java @@ -80,6 +80,9 @@ @Since("1.0") public class StructCommand extends Structure { + // Paper versions with the new command system need a delay before syncing commands or a CME will occur. + private static final boolean DELAY_COMMAND_SYNCING = Skript.classExists("io.papermc.paper.command.brigadier.Commands"); + public static final Priority PRIORITY = new Priority(500); private static final Pattern COMMAND_PATTERN = Pattern.compile("(?i)^command\\s+/?(\\S+)\\s*(\\s+(.+))?$"); @@ -315,7 +318,7 @@ public boolean load() { @Override public boolean postLoad() { - attemptCommandSync(); + scheduleCommandSync(); return true; } @@ -328,20 +331,28 @@ public void unload() { @Override public void postUnload() { - attemptCommandSync(); + scheduleCommandSync(); } - private void attemptCommandSync() { + private void scheduleCommandSync() { if (SYNC_COMMANDS.get()) { SYNC_COMMANDS.set(false); - if (CommandReloader.syncCommands(Bukkit.getServer())) { - Skript.debug("Commands synced to clients"); + if (DELAY_COMMAND_SYNCING) { + Bukkit.getScheduler().runTask(Skript.getInstance(), this::forceCommandSync); } else { - Skript.debug("Commands changed but not synced to clients (normal on 1.12 and older)"); + forceCommandSync(); } } } + private void forceCommandSync() { + if (CommandReloader.syncCommands(Bukkit.getServer())) { + Skript.debug("Commands synced to clients"); + } else { + Skript.debug("Commands changed but not synced to clients (normal on 1.12 and older)"); + } + } + @Override public Priority getPriority() { return PRIORITY; From 17430004b1049f70e6c5b651c45c778218d6d0be Mon Sep 17 00:00:00 2001 From: _tud <98935832+UnderscoreTud@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:29:07 +0300 Subject: [PATCH 04/48] Return API (#6118) --- .../ch/njol/skript/effects/EffReturn.java | 72 +++---- .../ch/njol/skript/lang/ReturnHandler.java | 194 ++++++++++++++++++ .../njol/skript/lang/ReturnableTrigger.java | 53 +++++ .../njol/skript/lang/function/Functions.java | 2 +- .../skript/lang/function/ScriptFunction.java | 85 +++++--- .../skript/test/runner/SecReturnable.java | 114 ++++++++++ src/test/skript/tests/misc/returns.sk | 23 +++ 7 files changed, 473 insertions(+), 70 deletions(-) create mode 100644 src/main/java/ch/njol/skript/lang/ReturnHandler.java create mode 100644 src/main/java/ch/njol/skript/lang/ReturnableTrigger.java create mode 100644 src/main/java/ch/njol/skript/test/runner/SecReturnable.java create mode 100644 src/test/skript/tests/misc/returns.sk diff --git a/src/main/java/ch/njol/skript/effects/EffReturn.java b/src/main/java/ch/njol/skript/effects/EffReturn.java index d0b4d220514..21ce3d4d475 100644 --- a/src/main/java/ch/njol/skript/effects/EffReturn.java +++ b/src/main/java/ch/njol/skript/effects/EffReturn.java @@ -26,21 +26,22 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Effect; import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ReturnHandler; +import ch.njol.skript.lang.ReturnHandler.ReturnHandlerStack; import ch.njol.skript.lang.SectionExitHandler; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.TriggerItem; import ch.njol.skript.lang.TriggerSection; -import ch.njol.skript.lang.function.FunctionEvent; -import ch.njol.skript.lang.function.Functions; -import ch.njol.skript.lang.function.ScriptFunction; +import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; +import ch.njol.skript.registrations.Classes; import ch.njol.util.Kleenean; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; @Name("Return") -@Description("Makes a function return a value") +@Description("Makes a trigger (e.g. a function) return a value") @Examples({ "function double(i: number) :: number:", "\treturn 2 * {_i}", @@ -50,90 +51,89 @@ }) @Since("2.2, 2.8.0 (returns aliases)") public class EffReturn extends Effect { - + static { Skript.registerEffect(EffReturn.class, "return %objects%"); + ParserInstance.registerData(ReturnHandlerStack.class, ReturnHandlerStack::new); } - + @SuppressWarnings("NotNullFieldNotInitialized") - private ScriptFunction function; - + private ReturnHandler handler; @SuppressWarnings("NotNullFieldNotInitialized") private Expression value; - - @SuppressWarnings("unchecked") + @Override + @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - ScriptFunction f = Functions.currentFunction; - if (f == null) { - Skript.error("The return statement can only be used in a function"); + handler = getParser().getData(ReturnHandlerStack.class).getCurrentHandler(); + if (handler == null) { + Skript.error("The return statement cannot be used here"); return false; } - + if (!isDelayed.isFalse()) { Skript.error("A return statement after a delay is useless, as the calling trigger will resume when the delay starts (and won't get any returned value)"); return false; } - - function = f; - ClassInfo returnType = function.getReturnType(); + + Class returnType = handler.returnValueType(); if (returnType == null) { - Skript.error("This function doesn't return any value. Please use 'stop' or 'exit' if you want to stop the function."); + Skript.error(handler + " doesn't return any value. Please use 'stop' or 'exit' if you want to stop the trigger."); return false; } - + RetainingLogHandler log = SkriptLogger.startRetainingLog(); Expression convertedExpr; try { - convertedExpr = exprs[0].getConvertedExpression(returnType.getC()); + convertedExpr = exprs[0].getConvertedExpression(returnType); if (convertedExpr == null) { - log.printErrors("This function is declared to return " + returnType.getName().withIndefiniteArticle() + ", but " + exprs[0].toString(null, false) + " is not of that type."); + String typeName = Classes.getSuperClassInfo(returnType).getName().withIndefiniteArticle(); + log.printErrors(handler + " is declared to return " + typeName + ", but " + exprs[0].toString(null, false) + " is not of that type."); return false; } log.printLog(); } finally { log.stop(); } - - if (f.isSingle() && !convertedExpr.isSingle()) { - Skript.error("This function is defined to only return a single " + returnType.toString() + ", but this return statement can return multiple values."); + + if (handler.isSingleReturnValue() && !convertedExpr.isSingle()) { + Skript.error(handler + " is defined to only return a single " + returnType + ", but this return statement can return multiple values."); return false; } value = convertedExpr; - + return true; } - + @Override @Nullable - @SuppressWarnings({"unchecked", "rawtypes"}) protected TriggerItem walk(Event event) { debug(event, false); - if (event instanceof FunctionEvent) { - ((ScriptFunction) function).setReturnValue(value.getArray(event)); - } else { - assert false : event; - } + //noinspection rawtypes,unchecked + ((ReturnHandler) handler).returnValues(value.getArray(event)); TriggerSection parent = getParent(); - while (parent != null) { + while (parent != null && parent != handler) { if (parent instanceof SectionExitHandler) ((SectionExitHandler) parent).exit(event); parent = parent.getParent(); } + if (handler instanceof SectionExitHandler) + ((SectionExitHandler) handler).exit(event); + return null; } - + @Override protected void execute(Event event) { assert false; } - + @Override public String toString(@Nullable Event event, boolean debug) { return "return " + value.toString(event, debug); } - + } diff --git a/src/main/java/ch/njol/skript/lang/ReturnHandler.java b/src/main/java/ch/njol/skript/lang/ReturnHandler.java new file mode 100644 index 00000000000..f908fc68c51 --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/ReturnHandler.java @@ -0,0 +1,194 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.lang; + +import ch.njol.skript.ScriptLoader; +import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.config.SectionNode; +import ch.njol.skript.lang.parser.ParserInstance; +import org.bukkit.event.Event; +import org.jetbrains.annotations.ApiStatus.NonExtendable; +import org.jetbrains.annotations.Nullable; + +import java.util.Deque; +import java.util.LinkedList; + +public interface ReturnHandler { + + /** + * Loads the code in the given {@link SectionNode} using the same logic as + * {@link Section#loadCode(SectionNode)} and pushes the section onto the + * return handler stack + *
+ * This method may only be called by a {@link Section} + * @throws SkriptAPIException if this return handler is not a {@link Section} + */ + @NonExtendable + default void loadReturnableSectionCode(SectionNode node) { + if (!(this instanceof Section)) + throw new SkriptAPIException("loadReturnableSectionCode called on a non-section object"); + ParserInstance parser = ParserInstance.get(); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + stack.push(this); + Section section = (Section) this; + try { + section.loadCode(node); + } finally { + stack.pop(); + } + } + + /** + * Loads the code in the given {@link SectionNode} using the same logic as + * {@link Section#loadCode(SectionNode, String, Class[])} and pushes the section onto the + * return handler stack + *
+ * This method may only be called by a {@link Section} + * @param node the section node + * @param name the name of the event(s) being used + * @param events the event(s) during the section's execution + * @return a returnable trigger containing the loaded section. + * This should be stored and used to run the section one or more times + * @throws SkriptAPIException if this return handler is not a {@link Section} + */ + @NonExtendable + default ReturnableTrigger loadReturnableSectionCode(SectionNode node, String name, Class[] events) { + if (!(this instanceof Section)) + throw new SkriptAPIException("loadReturnableSectionCode called on a non-section object"); + ParserInstance parser = ParserInstance.get(); + ParserInstance.Backup parserBackup = parser.backup(); + parser.reset(); + + parser.setCurrentEvent(name, events); + SkriptEvent skriptEvent = new SectionSkriptEvent(name, (Section) this); + parser.setCurrentStructure(skriptEvent); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + + try { + return new ReturnableTrigger<>( + this, + parser.getCurrentScript(), + name, + skriptEvent, + trigger -> { + stack.push(trigger); + return ScriptLoader.loadItems(node); + } + ); + } finally { + stack.pop(); + parser.restoreBackup(parserBackup); + } + } + + /** + * Loads the code in the given {@link SectionNode} into a {@link ReturnableTrigger}. + *
+ * This is a general method to load a section node without extra logic + * done to the {@link ParserInstance}. + * The calling code is expected to manage the {@code ParserInstance} accordingly, which may vary depending on + * where the code being loaded is located and what state the {@code ParserInstance} is in. + * @param node the section node to load + * @param name the name of the trigger + * @param event the {@link SkriptEvent} of the trigger + * @return a returnable trigger containing the loaded section node + */ + @NonExtendable + default ReturnableTrigger loadReturnableTrigger(SectionNode node, String name, SkriptEvent event) { + ParserInstance parser = ParserInstance.get(); + ReturnHandlerStack stack = parser.getData(ReturnHandlerStack.class); + try { + return new ReturnableTrigger( + this, + parser.getCurrentScript(), + name, + event, + trigger -> { + stack.push(trigger); + return ScriptLoader.loadItems(node); + } + ); + } finally { + stack.pop(); + } + } + + /** + * @param values the values to return + */ + void returnValues(T @Nullable [] values); + + /** + * @return whether this return handler may accept multiple return values + */ + boolean isSingleReturnValue(); + + /** + * The return type of this return handler, or null if it can't + * accept return values in this context (e.g. a function without a return type). + * + * @return the return type + */ + @Nullable Class returnValueType(); + + class ReturnHandlerStack extends ParserInstance.Data { + + private final Deque> stack = new LinkedList<>(); + + public ReturnHandlerStack(ParserInstance parserInstance) { + super(parserInstance); + } + + public Deque> getStack() { + return stack; + } + + /** + * Retrieves the current {@link ReturnHandler} + * @return the return data + */ + public @Nullable ReturnHandler getCurrentHandler() { + return stack.peek(); + } + + /** + * Pushes the current return handler onto the return stack. + *
+ * Note: After the trigger finished loading, + * {@link ReturnHandlerStack#pop()} MUST be called + * @param handler the return handler + * @see ReturnHandlerStack#pop() + */ + public void push(ReturnHandler handler) { + stack.push(handler); + } + + /** + * Pops the current handler off the return stack. + * Should be called after the trigger has finished loading. + * @return the popped return data + * @see ReturnHandlerStack#push(ReturnHandler) + */ + public ReturnHandler pop() { + return stack.pop(); + } + + } + +} diff --git a/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java new file mode 100644 index 00000000000..51c025ef70c --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/ReturnableTrigger.java @@ -0,0 +1,53 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.lang; + +import org.eclipse.jdt.annotation.Nullable; +import org.skriptlang.skript.lang.script.Script; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +public class ReturnableTrigger extends Trigger implements ReturnHandler { + + private final ReturnHandler handler; + + public ReturnableTrigger(ReturnHandler handler, @Nullable Script script, String name, SkriptEvent event, Function, List> loadItems) { + super(script, name, event, Collections.emptyList()); + this.handler = handler; + setTriggerItems(loadItems.apply(this)); + } + + @Override + public void returnValues(T @Nullable [] values) { + handler.returnValues(values); + } + + @Override + public boolean isSingleReturnValue() { + return handler.isSingleReturnValue(); + } + + @Override + public @Nullable Class returnValueType() { + return handler.returnValueType(); + } + +} diff --git a/src/main/java/ch/njol/skript/lang/function/Functions.java b/src/main/java/ch/njol/skript/lang/function/Functions.java index bc19f2bbf72..2eee7ea6c18 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -121,7 +121,7 @@ public static Function loadFunction(Script script, SectionNode node, Signatur Skript.debug((signature.local ? "local " : "") + "function " + name + "(" + StringUtils.join(params, ", ") + ")" + (c != null ? " :: " + (signature.isSingle() ? c.getName().getSingular() : c.getName().getPlural()) : "") + ":"); - Function f = new ScriptFunction<>(signature, script, node); + Function f = new ScriptFunction<>(signature, node); // Register the function for signature namespace.addFunction(f); diff --git a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java index 259c8bcad85..d3a1e85117c 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -18,58 +18,49 @@ */ package ch.njol.skript.lang.function; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.lang.ReturnHandler; +import org.jetbrains.annotations.ApiStatus; import org.skriptlang.skript.lang.script.Script; import org.eclipse.jdt.annotation.Nullable; -import ch.njol.skript.ScriptLoader; import ch.njol.skript.config.SectionNode; import ch.njol.skript.effects.EffReturn; import ch.njol.skript.lang.Trigger; import ch.njol.skript.lang.util.SimpleEvent; import ch.njol.skript.variables.Variables; -/** - * @author Peter Güttinger - */ -public class ScriptFunction extends Function { - +public class ScriptFunction extends Function implements ReturnHandler { + private final Trigger trigger; - + + private boolean returnValueSet; + private T @Nullable [] returnValues; + + /** + * @deprecated use {@link ScriptFunction#ScriptFunction(Signature, SectionNode)} + */ + @Deprecated public ScriptFunction(Signature sign, Script script, SectionNode node) { + this(sign, node); + } + + public ScriptFunction(Signature sign, SectionNode node) { super(sign); - + Functions.currentFunction = this; try { - trigger = new Trigger( - script, - "function " + sign.getName(), - new SimpleEvent(), - ScriptLoader.loadItems(node) - ); - trigger.setLineNumber(node.getLine()); + trigger = loadReturnableTrigger(node, "function " + sign.getName(), new SimpleEvent()); } finally { Functions.currentFunction = null; } + trigger.setLineNumber(node.getLine()); } - - private boolean returnValueSet = false; - @Nullable - private T[] returnValue = null; - - /** - * Should only be called by {@link EffReturn}. - */ - public final void setReturnValue(final @Nullable T[] value) { - assert !returnValueSet; - returnValueSet = true; - returnValue = value; - } - + // REMIND track possible types of local variables (including undefined variables) (consider functions, commands, and EffChange) - maybe make a general interface for this purpose // REM: use patterns, e.g. {_a%b%} is like "a.*", and thus subsequent {_axyz} may be set and of that type. @Override - @Nullable - public T[] execute(final FunctionEvent e, final Object[][] params) { + public T @Nullable [] execute(final FunctionEvent e, final Object[][] params) { Parameter[] parameters = getSignature().getParameters(); for (int i = 0; i < parameters.length; i++) { Parameter p = parameters[i]; @@ -84,14 +75,42 @@ public T[] execute(final FunctionEvent e, final Object[][] params) { } trigger.execute(e); - return returnValue; + ClassInfo returnType = getReturnType(); + return returnType != null ? returnValues : null; + } + + /** + * Should only be called by {@link EffReturn}. + * @deprecated Use {@link ScriptFunction#returnValues(Object[])} + */ + @Deprecated + @ApiStatus.Internal + public final void setReturnValue(@Nullable T[] values) { + returnValues(values); } @Override public boolean resetReturnValue() { - returnValue = null; returnValueSet = false; + returnValues = null; return true; } + @Override + public final void returnValues(T @Nullable [] values) { + assert !returnValueSet; + returnValueSet = true; + this.returnValues = values; + } + + @Override + public final boolean isSingleReturnValue() { + return isSingle(); + } + + @Override + public final @Nullable Class returnValueType() { + return getReturnType() != null ? getReturnType().getC() : null; + } + } diff --git a/src/main/java/ch/njol/skript/test/runner/SecReturnable.java b/src/main/java/ch/njol/skript/test/runner/SecReturnable.java new file mode 100644 index 00000000000..436b6d282fb --- /dev/null +++ b/src/main/java/ch/njol/skript/test/runner/SecReturnable.java @@ -0,0 +1,114 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.test.runner; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.config.SectionNode; +import ch.njol.skript.doc.NoDoc; +import ch.njol.skript.lang.*; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@NoDoc +public class SecReturnable extends Section implements ReturnHandler { + + static { + Skript.registerSection(SecReturnable.class, "returnable [:plural] %*classinfo% section"); + } + + private ClassInfo returnValueType; + private boolean singleReturnValue; + private static Object @Nullable [] returnedValues; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult, SectionNode sectionNode, List triggerItems) { + returnValueType = ((Literal>) expressions[0]).getSingle(); + singleReturnValue = !parseResult.hasTag("plural"); + loadReturnableSectionCode(sectionNode); + return true; + } + + @Override + protected @Nullable TriggerItem walk(Event event) { + return walk(event, true); + } + + @Override + public void returnValues(Object @Nullable [] values) { + returnedValues = values; + } + + @Override + public boolean isSingleReturnValue() { + return singleReturnValue; + } + + @Override + public @Nullable Class returnValueType() { + return returnValueType.getC(); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "returnable " + (singleReturnValue ? "" : "plural ") + returnValueType.toString(event, debug) + " section"; + } + + @NoDoc + public static class ExprLastReturnValues extends SimpleExpression { + + static { + Skript.registerExpression(ExprLastReturnValues.class, Object.class, ExpressionType.SIMPLE, "[the] last return[ed] value[s]"); + } + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + return true; + } + + @Override + public @Nullable Object[] get(Event event) { + Object[] returnedValues = SecReturnable.returnedValues; + SecReturnable.returnedValues = null; + return returnedValues; + } + + @Override + public boolean isSingle() { + return false; + } + + @Override + public Class getReturnType() { + return Object.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "last returned values"; + } + + } + +} diff --git a/src/test/skript/tests/misc/returns.sk b/src/test/skript/tests/misc/returns.sk new file mode 100644 index 00000000000..56e2937f7c5 --- /dev/null +++ b/src/test/skript/tests/misc/returns.sk @@ -0,0 +1,23 @@ +test "returns": + returnable string section: + return "hello" + assert last return value is "hello" with "failed single return value" + + returnable plural number section: + return (3 and 5) + assert last return values is 3 and 5 with "failed multiple return values" + + returnable integer section: + return 2.5 + assert last return value is 2 with "failed converted return value" + +test "returns (parsing)": + parse: + returnable string section: + return 1 + assert last parse logs is set with "skript shouldn't be able to return a different type" + + parse: + returnable string section: + return ("foo" and "bar") + assert last parse logs is set with "skript shouldn't be able to return multiple values for a single return trigger" From aa7955c2663f5623213b529b6d331909e00cac36 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sat, 15 Jun 2024 12:59:10 -0400 Subject: [PATCH 05/48] Fix Particle Definition Conflicts (#6760) --- .../skript/classes/data/SkriptClasses.java | 1 + src/main/resources/lang/default.lang | 38 +++++++++---------- .../pull-6760-particle itemtype conflicts.sk | 5 +++ .../tests/syntaxes/effects/EffVisualEffect.sk | 6 +-- 4 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 src/test/skript/tests/regressions/pull-6760-particle itemtype conflicts.sk diff --git a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java index 39dc6447fa5..6f92502cd2a 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -661,6 +661,7 @@ public String toVariableNameString(final Experience xp) { .usage(VisualEffects.getAllNames()) .since("2.1") .user("(visual|particle) effects?") + .after("itemtype") .parser(new Parser() { @Override @Nullable diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 9f7df2c9e4c..90a37eb9e32 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -1473,13 +1473,13 @@ visual effects: block_marker: # added in 1.18 name: block marker @a - pattern: (barrierbm:barrier|lightbm:light|%-blockdata/itemtype% block marker) + pattern: (barrierbm:barrier [particle]|lightbm:light [particle]|%-blockdata/itemtype% block marker) barrier: name: barrier @a - pattern: barrier + pattern: barrier [particle] light: # added in 1.17 name: light @- - pattern: light + pattern: light [particle] bubble: # added in 1.20.5 name: bubble @- @@ -1534,7 +1534,7 @@ visual effects: dolphin: # added in 1.13 name: dolphin @- - pattern: dolphin + pattern: dolphin [particle] dragon_breath: # added in 1.14 name: dragon breath @- @@ -1602,10 +1602,10 @@ visual effects: elder_guardian: # added in 1.20.5 name: elder guardian @- - pattern: (elder guardian|mob appearance|guardian ghost) + pattern: (elder guardian particle|mob appearance|guardian ghost) mob_appearance: # for versions below 1.20.5 name: mob appearance @- - pattern: (elder guardian|mob appearance|guardian ghost) + pattern: (elder guardian particle|mob appearance|guardian ghost) electric_spark: name: electric spark @- @@ -1627,7 +1627,7 @@ visual effects: end_rod: name: end rod @- - pattern: end rod + pattern: end rod [particle] entity_effect: # added in 1.20.5 name: entity effect @an @@ -1693,10 +1693,10 @@ visual effects: firework: # added in 1.20.5 name: firework @- - pattern: (firework|firework['s] spark) + pattern: (firework particle|firework['s] spark) fireworks_spark: # for versions below 1.20.5 name: firework's spark @- - pattern: (firework|firework['s] spark) + pattern: (firework particle|firework['s] spark) fishing: # added in 1.20.5 name: water wake @- @@ -1764,24 +1764,24 @@ visual effects: item_cobweb: # added in 1.20.5 (for 1.21) name: cobweb @- - pattern: cobweb + pattern: cobweb [item|particle] item_slime: # added in 1.20.5 name: slime @- - pattern: slime + pattern: slime [item|particle] slime: # for versions below 1.20.5 name: slime @- - pattern: slime + pattern: slime [item|particle] item_snowball: # added in 1.20.5 name: snowball @- - pattern: (snowball [break]|snow shovel|snow(man| golem) spawn) + pattern: (snowball [item|break|particle]|snow shovel|snow(man| golem) spawn) snowball: # for versions below 1.20.5 name: snowball break @- pattern: snowball break snow_shovel: # for versions below 1.20.5 name: snow shovel @- - pattern: (snowball|snow shovel|snow(man| golem) spawn) + pattern: (snowball [item|particle]|snow shovel|snow(man| golem) spawn) landing_honey: # added in 1.15 name: landing honey @- @@ -1808,7 +1808,7 @@ visual effects: mycelium: # previously town_aura, changed in 1.20.5 name: mycelium @- - pattern: (mycelium|small smoke|town aura) + pattern: (mycelium [particle]|small smoke|town aura) town_aura: name: small smoke @- pattern: (mycelium|small smoke|town aura) @@ -1931,10 +1931,10 @@ visual effects: totem_of_undying: # added in 1.20.5 name: totem of undying @a - pattern: totem [of undying] + pattern: totem [of undying] [particle] totem: # for versions below 1.20.5 name: totem @a - pattern: totem [of undying] + pattern: totem [of undying] [particle] trial omen: # added in 1.20.5 (for 1.21) name: trial omen @a @@ -1989,10 +1989,10 @@ visual effects: witch: # added in 1.20.5 name: witch @a - pattern: (witch [magic|spell]|purple spark) + pattern: (witch (magic|spell|particle)|purple spark) spell_witch: # for versions below 1.20.5 name: witch spell @a - pattern: (witch [magic|spell]|purple spark) + pattern: (witch (magic|spell|particle)|purple spark) # -- Inventory Actions -- inventory actions: diff --git a/src/test/skript/tests/regressions/pull-6760-particle itemtype conflicts.sk b/src/test/skript/tests/regressions/pull-6760-particle itemtype conflicts.sk new file mode 100644 index 00000000000..d4cac535c18 --- /dev/null +++ b/src/test/skript/tests/regressions/pull-6760-particle itemtype conflicts.sk @@ -0,0 +1,5 @@ +test "particle itemtype conflicts": + + # itemtype parsing should take priority over visual effects + assert totem of undying is an itemtype with "totem of undying is not an itemtype" + assert mycelium is an itemtype with "mycelium is not an itemtype" diff --git a/src/test/skript/tests/syntaxes/effects/EffVisualEffect.sk b/src/test/skript/tests/syntaxes/effects/EffVisualEffect.sk index b8631e79cf2..a4791d1752a 100644 --- a/src/test/skript/tests/syntaxes/effects/EffVisualEffect.sk +++ b/src/test/skript/tests/syntaxes/effects/EffVisualEffect.sk @@ -16,7 +16,7 @@ test "visual effects": play dripping water at {_} play white dust with size 2 at {_} play effect at {_} - play elder guardian at {_} + play elder guardian particle at {_} play enchant at {_} play enchanted hit at {_} play end rod at {_} @@ -26,7 +26,7 @@ test "visual effects": play large explosion at {_} play explosion emitter at {_} play falling dust of air at {_} - play firework at {_} + play firework spark at {_} play fishing at {_} play flame at {_} play happy villager at {_} @@ -51,7 +51,7 @@ test "visual effects": play totem of undying at {_} play suspended at {_} play void fog at {_} - play witch at {_} + play witch particle at {_} parse if running minecraft "1.14.4": play campfire cosy smoke at {_} play campfire signal smoke at {_} From 2627c950d1734e45c5ad8e366ee2161fd44fba32 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Sat, 15 Jun 2024 10:42:35 -0700 Subject: [PATCH 06/48] ExprStringCase - include expression in toString (#6777) --- .../skript/expressions/ExprStringCase.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprStringCase.java b/src/main/java/ch/njol/skript/expressions/ExprStringCase.java index af963a333af..7a21357dca8 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprStringCase.java +++ b/src/main/java/ch/njol/skript/expressions/ExprStringCase.java @@ -153,22 +153,28 @@ public Class getReturnType() { } @Override - public String toString(@Nullable Event e, boolean debug) { + public String toString(@Nullable Event event, boolean debug) { + String mode = ""; switch (type) { case 0: // Basic Case Change - return (casemode == 1) ? "uppercase" : "lowercase"; + mode = (casemode == 1) ? "uppercase" : "lowercase"; + break; case 1: // Proper Case - return ((casemode == 3) ? "strict" : "lenient") + " proper case"; + mode = ((casemode == 3) ? "strict" : "lenient") + " proper case"; + break; case 2: // Camel Case - return ((casemode == 3) ? "strict" : "lenient") + " camel case"; + mode = ((casemode == 3) ? "strict" : "lenient") + " camel case"; + break; case 3: // Pascal Case - return ((casemode == 3) ? "strict" : "lenient") + " pascal case"; + mode = ((casemode == 3) ? "strict" : "lenient") + " pascal case"; + break; case 4: // Snake Case - return ((casemode == 0) ? "" : ((casemode == 1)) ? "upper " : "lower ") + "snake case"; + mode = ((casemode == 0) ? "" : ((casemode == 1)) ? "upper " : "lower ") + "snake case"; + break; case 5: // Kebab Case - return ((casemode == 0) ? "" : ((casemode == 1)) ? "upper " : "lower ") + "kebab case"; + mode = ((casemode == 0) ? "" : ((casemode == 1)) ? "upper " : "lower ") + "kebab case"; } - return ""; // Shouldn't reach here anyways + return mode + " " + expr.toString(event, debug); } @SuppressWarnings("null") From c6b0bfa71179259a1c21f735588c02548d8d4d8d Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sat, 15 Jun 2024 14:06:55 -0400 Subject: [PATCH 07/48] Fix firework spawning (#6764) --- .../njol/skript/entity/SimpleEntityData.java | 36 ++++++++++++++----- .../regressions/6762-cant spawn a firework.sk | 4 +++ 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 src/test/skript/tests/regressions/6762-cant spawn a firework.sk diff --git a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java index 6cad5ec1a2c..504c4449dc7 100644 --- a/src/main/java/ch/njol/skript/entity/SimpleEntityData.java +++ b/src/main/java/ch/njol/skript/entity/SimpleEntityData.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.List; +import ch.njol.util.Kleenean; +import org.bukkit.World; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Allay; import org.bukkit.entity.Animals; @@ -155,15 +157,13 @@ public final static class SimpleEntityDataInfo { final String codeName; final Class c; final boolean isSupertype; + final Kleenean allowSpawning; - SimpleEntityDataInfo(final String codeName, final Class c) { - this(codeName, c, false); - } - - SimpleEntityDataInfo(final String codeName, final Class c, final boolean isSupertype) { + SimpleEntityDataInfo(String codeName, Class c, boolean isSupertype, Kleenean allowSpawning) { this.codeName = codeName; this.c = c; this.isSupertype = isSupertype; + this.allowSpawning = allowSpawning; } @Override @@ -191,11 +191,18 @@ public boolean equals(final @Nullable Object obj) { private final static List types = new ArrayList<>(); private static void addSimpleEntity(String codeName, Class entityClass) { - types.add(new SimpleEntityDataInfo(codeName, entityClass)); + addSimpleEntity(codeName, entityClass, Kleenean.UNKNOWN); + } + + /** + * @param allowSpawning Whether to override the default {@link #canSpawn(World)} behavior and allow this entity to be spawned. + */ + private static void addSimpleEntity(String codeName, Class entityClass, Kleenean allowSpawning) { + types.add(new SimpleEntityDataInfo(codeName, entityClass, false, allowSpawning)); } private static void addSuperEntity(String codeName, Class entityClass) { - types.add(new SimpleEntityDataInfo(codeName, entityClass, true)); + types.add(new SimpleEntityDataInfo(codeName, entityClass, true, Kleenean.UNKNOWN)); } static { // Simple Entities @@ -238,7 +245,9 @@ private static void addSuperEntity(String codeName, Class enti addSimpleEntity("witch", Witch.class); addSimpleEntity("wither", Wither.class); addSimpleEntity("wither skull", WitherSkull.class); - addSimpleEntity("firework", Firework.class); + // bukkit marks fireworks as not spawnable + // see https://hub.spigotmc.org/jira/browse/SPIGOT-7677 + addSimpleEntity("firework", Firework.class, Kleenean.TRUE); addSimpleEntity("endermite", Endermite.class); addSimpleEntity("armor stand", ArmorStand.class); addSimpleEntity("shulker", Shulker.class); @@ -449,7 +458,16 @@ protected boolean equals_i(final EntityData obj) { final SimpleEntityData other = (SimpleEntityData) obj; return info.equals(other.info); } - + + @Override + public boolean canSpawn(@Nullable World world) { + if (info.allowSpawning.isUnknown()) // unspecified, refer to default behavior + return super.canSpawn(world); + if (world == null) + return false; + return info.allowSpawning.isTrue(); + } + @Override public Fields serialize() throws NotSerializableException { final Fields f = super.serialize(); diff --git a/src/test/skript/tests/regressions/6762-cant spawn a firework.sk b/src/test/skript/tests/regressions/6762-cant spawn a firework.sk new file mode 100644 index 00000000000..f272e0bc8e0 --- /dev/null +++ b/src/test/skript/tests/regressions/6762-cant spawn a firework.sk @@ -0,0 +1,4 @@ +test "can't spawn a firework": + + spawn a firework at spawn of world "world" + assert last spawned firework is set with "firework did not spawn" From 7fc699e17299763523942d99524216480d426295 Mon Sep 17 00:00:00 2001 From: APickledWalrus Date: Sat, 15 Jun 2024 14:40:33 -0400 Subject: [PATCH 08/48] Prepare For Release (2.8.7) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0b5b23f30d5..f78ae1766eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel=true groupid=ch.njol name=skript -version=2.8.6 +version=2.8.7 jarName=Skript.jar testEnv=java21/paper-1.20.6 testEnvJavaVersion=21 From 2e0d1cf2397d3c55f8f0a6ddf3a8749e38ab45d9 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Thu, 20 Jun 2024 14:37:27 -0400 Subject: [PATCH 09/48] Fix StructCommand Illegal Task Scheduling (#6788) --- src/main/java/ch/njol/skript/structures/StructCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/structures/StructCommand.java b/src/main/java/ch/njol/skript/structures/StructCommand.java index e50476e5a8e..fc69c26aae0 100644 --- a/src/main/java/ch/njol/skript/structures/StructCommand.java +++ b/src/main/java/ch/njol/skript/structures/StructCommand.java @@ -338,7 +338,9 @@ private void scheduleCommandSync() { if (SYNC_COMMANDS.get()) { SYNC_COMMANDS.set(false); if (DELAY_COMMAND_SYNCING) { - Bukkit.getScheduler().runTask(Skript.getInstance(), this::forceCommandSync); + // if the plugin is disabled, the server is likely closing and delaying will cause an error. + if (Bukkit.getPluginManager().isPluginEnabled(Skript.getInstance())) + Bukkit.getScheduler().runTask(Skript.getInstance(), this::forceCommandSync); } else { forceCommandSync(); } From 34b303b6a8443186483379d7504b8a5118005407 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Thu, 20 Jun 2024 18:55:47 -0700 Subject: [PATCH 10/48] Registry ClassInfo Utility (#6695) --- .../skript/classes/data/BukkitClasses.java | 14 +- .../classes/registry/RegistryClassInfo.java | 53 ++++++ .../classes/registry/RegistryParser.java | 155 ++++++++++++++++++ .../classes/registry/RegistrySerializer.java | 83 ++++++++++ 4 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java create mode 100644 src/main/java/ch/njol/skript/classes/registry/RegistryParser.java create mode 100644 src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 25000a2390d..ba1a419aad0 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -38,6 +38,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.Registry; import org.bukkit.SoundCategory; import org.bukkit.World; import org.bukkit.World.Environment; @@ -77,7 +78,6 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.CachedServerIcon; import org.bukkit.util.Vector; -import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.Skript; import ch.njol.skript.SkriptConfig; @@ -90,6 +90,7 @@ import ch.njol.skript.classes.EnumClassInfo; import ch.njol.skript.classes.Parser; import ch.njol.skript.classes.Serializer; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.entity.EntityData; import ch.njol.skript.expressions.ExprDamageCause; import ch.njol.skript.expressions.base.EventValueExpression; @@ -104,6 +105,7 @@ import ch.njol.util.StringUtils; import ch.njol.yggdrasil.Fields; import io.papermc.paper.world.MoonPhase; +import org.jetbrains.annotations.Nullable; /** * @author Peter Güttinger @@ -976,8 +978,14 @@ public String toVariableNameString(final ItemStack i) { .name(ClassInfo.NO_DOC) .since("2.0") .changer(DefaultChangers.itemChanger)); - - Classes.registerClass(new EnumClassInfo<>(Biome.class, "biome", "biomes") + + ClassInfo biomeClassInfo; + if (Skript.classExists("org.bukkit.Registry") && Skript.fieldExists(Registry.class, "BIOME")) { + biomeClassInfo = new RegistryClassInfo<>(Biome.class, Registry.BIOME, "biome", "biomes"); + } else { + biomeClassInfo = new EnumClassInfo<>(Biome.class, "biome", "biomes"); + } + Classes.registerClass(biomeClassInfo .user("biomes?") .name("Biome") .description("All possible biomes Minecraft uses to generate a world.") diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java b/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java new file mode 100644 index 00000000000..ff82f966af8 --- /dev/null +++ b/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java @@ -0,0 +1,53 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.classes.registry; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.lang.DefaultExpression; +import org.bukkit.Keyed; +import org.bukkit.Registry; + +/** + * This class can be used for easily creating ClassInfos for {@link Registry}s. + * It registers a language node with usage, a serializer, default expression, and a parser. + * + * @param The Registry class. + */ +public class RegistryClassInfo extends ClassInfo { + + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode) { + this(registryClass, registry, codeName, languageNode, new EventValueExpression<>(registryClass)); + } + + /** + * @param registry The registry + * @param codeName The name used in patterns + */ + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression) { + super(registryClass, codeName); + RegistryParser registryParser = new RegistryParser<>(registry, languageNode); + usage(registryParser.getAllNames()) + .supplier(registry::iterator) + .serializer(new RegistrySerializer(registry)) + .defaultExpression(defaultExpression) + .parser(registryParser); + } + +} diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java b/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java new file mode 100644 index 00000000000..51cb4637434 --- /dev/null +++ b/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java @@ -0,0 +1,155 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.classes.registry; + +import ch.njol.skript.classes.Parser; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.localization.Language; +import ch.njol.skript.localization.Noun; +import ch.njol.util.NonNullPair; +import ch.njol.util.StringUtils; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * A parser based on a {@link Registry} used to parse data from a string or turn data into a string. + * + * @param Registry class + */ +public class RegistryParser extends Parser { + + private final Registry registry; + private final String languageNode; + + private final Map names = new HashMap<>(); + private final Map parseMap = new HashMap<>(); + + public RegistryParser(Registry registry, String languageNode) { + assert !languageNode.isEmpty() && !languageNode.endsWith(".") : languageNode; + this.registry = registry; + this.languageNode = languageNode; + refresh(); + Language.addListener(this::refresh); + } + + private void refresh() { + names.clear(); + parseMap.clear(); + for (R registryObject : registry) { + NamespacedKey namespacedKey = registryObject.getKey(); + String namespace = namespacedKey.getNamespace(); + String key = namespacedKey.getKey(); + String keyWithSpaces = key.replace("_", " "); + String languageKey = languageNode + "." + key; + + // Put the full namespaced key as a pattern + parseMap.put(namespacedKey.toString(), registryObject); + + // If the object is a vanilla Minecraft object, we'll add the key with spaces as a pattern + if (namespace.equalsIgnoreCase(NamespacedKey.MINECRAFT)) { + parseMap.put(keyWithSpaces, registryObject); + } + + String[] options = Language.getList(languageKey); + // Missing/Custom registry objects + if (options.length == 1 && options[0].equals(languageKey.toLowerCase(Locale.ENGLISH))) { + if (namespace.equalsIgnoreCase(NamespacedKey.MINECRAFT)) { + // If the object is a vanilla Minecraft object, we'll use the key with spaces as a name + names.put(registryObject, keyWithSpaces); + } else { + // If the object is a custom object, we'll use the full namespaced key as a name + names.put(registryObject, namespacedKey.toString()); + } + } else { + for (String option : options) { + option = option.toLowerCase(Locale.ENGLISH); + + // Isolate the gender if one is present + NonNullPair strippedOption = Noun.stripGender(option, languageKey); + String first = strippedOption.getFirst(); + Integer second = strippedOption.getSecond(); + + // Add to name map if needed + names.putIfAbsent(registryObject, first); + + parseMap.put(first, registryObject); + if (second != -1) { // There is a gender present + parseMap.put(Noun.getArticleWithSpace(second, Language.F_INDEFINITE_ARTICLE) + first, registryObject); + } + } + } + } + } + + /** + * This method attempts to match the string input against one of the string representations of the registry. + * + * @param input a string to attempt to match against one in the registry. + * @param context of parsing, may not be null + * @return The registry object matching the input, or null if no match could be made. + */ + @Override + public @Nullable R parse(String input, @NotNull ParseContext context) { + return parseMap.get(input.toLowerCase(Locale.ENGLISH)); + } + + /** + * This method returns the string representation of a registry. + * + * @param object The object to represent as a string. + * @param flags not currently used + * @return A string representation of the registry object. + */ + @Override + public @NotNull String toString(R object, int flags) { + return names.get(object); + } + + /** + * Returns a registry object's string representation in a variable name. + * + * @param object Object to represent in a variable name. + * @return The given object's representation in a variable name. + */ + @Override + public @NotNull String toVariableNameString(R object) { + return toString(object, 0); + } + + /** + * @return A comma-separated string containing a list of all names representing the registry. + * Note that some entries may represent the same registry object. + */ + public String getAllNames() { + List strings = new ArrayList<>(parseMap.keySet()); + Collections.sort(strings); + return StringUtils.join(strings, ", "); + } + +} diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java b/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java new file mode 100644 index 00000000000..ddec6c3225f --- /dev/null +++ b/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java @@ -0,0 +1,83 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.classes.registry; + +import ch.njol.skript.classes.Serializer; +import ch.njol.yggdrasil.Fields; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; + +import java.io.StreamCorruptedException; + +/** + * Serializer for {@link RegistryClassInfo} + * + * @param Registry class + */ +public class RegistrySerializer extends Serializer { + + private final Registry registry; + + public RegistrySerializer(Registry registry) { + this.registry = registry; + } + + @Override + public Fields serialize(R o) { + Fields fields = new Fields(); + fields.putPrimitive("name", o.getKey().toString()); + return null; + } + + @Override + protected R deserialize(Fields fields) { + try { + String name = fields.getAndRemovePrimitive("name", String.class); + NamespacedKey namespacedKey; + if (!name.contains(":")) { + // Old variables + namespacedKey = NamespacedKey.minecraft(name); + } else { + namespacedKey = NamespacedKey.fromString(name); + } + if (namespacedKey == null) + return null; + return registry.get(namespacedKey); + } catch (StreamCorruptedException e) { + return null; + } + } + + @Override + public boolean mustSyncDeserialization() { + return false; + } + + @Override + protected boolean canBeInstantiated() { + return false; + } + + @Override + public void deserialize(R o, Fields f) { + throw new UnsupportedOperationException(); + } + +} From 4e8e8e6f7e58838eb05f23ff6872d90167268464 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Sat, 22 Jun 2024 14:17:07 -0400 Subject: [PATCH 11/48] Fix releaseDocs looking for wrong Skript jar (#6789) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4cc665864db..69158646591 100644 --- a/build.gradle +++ b/build.gradle @@ -188,7 +188,7 @@ void createTestTask(String name, String desc, String environments, int javaVersi if (junit) { artifact += 'Skript-JUnit.jar' } else if (releaseDocs) { - artifact += 'Skript-github.jar' + artifact += 'Skript-' + version + '.jar' } else { artifact += 'Skript-nightly.jar' } From 975b952c6c229d863bd99ae65d1c0b54cfc490fd Mon Sep 17 00:00:00 2001 From: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Date: Sat, 22 Jun 2024 21:28:06 +0300 Subject: [PATCH 12/48] =?UTF-8?q?=E2=9A=92=20Fix=20release=20docs=20javado?= =?UTF-8?q?cs=20actions=20(#6089)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/archive-docs.yml | 46 +++++++++++++++++++ .../workflows/docs/generate-docs/action.yml | 4 +- .github/workflows/release-docs.yml | 39 +--------------- build.gradle | 15 +----- 4 files changed, 52 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/archive-docs.yml diff --git a/.github/workflows/archive-docs.yml b/.github/workflows/archive-docs.yml new file mode 100644 index 00000000000..4f921144ef2 --- /dev/null +++ b/.github/workflows/archive-docs.yml @@ -0,0 +1,46 @@ +name: Archive documentation + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + archive-docs: + if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" + needs: release-docs + runs-on: ubuntu-latest + steps: + - name: Configure workflow + id: configuration + run: | + echo "BRANCH_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + echo "DOCS_OUTPUT_DIR=${GITHUB_WORKSPACE}/skript-docs/docs/archives/${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + echo "DOCS_REPO_DIR=${GITHUB_WORKSPACE}/skript-docs" >> $GITHUB_OUTPUT + echo "SKRIPT_REPO_DIR=${GITHUB_WORKSPACE}/skript" >> $GITHUB_OUTPUT + - name: Checkout Skript + uses: actions/checkout@v4 + with: + submodules: recursive + path: skript + - name: Setup documentation environment + uses: ./skript/.github/workflows/docs/setup-docs + with: + docs_deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }} + docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} + - name: Generate documentation + uses: ./skript/.github/workflows/docs/generate-docs + with: + docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} + docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} + docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} + skript_repo_dir: ${{ steps.configuration.outputs.SKRIPT_REPO_DIR }} + is_release: true + generate_javadocs: true + - name: Push archive documentation + uses: ./skript/.github/workflows/docs/push-docs + with: + docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} + git_name: Archive Docs Bot + git_email: archivedocs@skriptlang.org + git_commit_message: "Update ${{ steps.configuration.outputs.BRANCH_NAME }} archive docs" diff --git a/.github/workflows/docs/generate-docs/action.yml b/.github/workflows/docs/generate-docs/action.yml index e033996868e..2470f852450 100644 --- a/.github/workflows/docs/generate-docs/action.yml +++ b/.github/workflows/docs/generate-docs/action.yml @@ -69,7 +69,7 @@ runs: cd $SKRIPT_REPO_DIR if [[ "${IS_RELEASE}" == "true" ]]; then - ./gradlew genReleaseDocs releaseJavadoc + ./gradlew genReleaseDocs javadoc elif [[ "${GENERATE_JAVADOCS}" == "true" ]]; then ./gradlew genNightlyDocs javadoc else @@ -77,7 +77,7 @@ runs: fi if [ -d "${DOCS_OUTPUT_DIR}" ]; then - if [[ "${GENERATE_JAVADOCS}" == "true" ]]; then + if [[ "${GENERATE_JAVADOCS}" == "true" ]] || [[ "${IS_RELEASE}" == "true" ]] ; then mkdir -p "${SKRIPT_DOCS_OUTPUT_DIR}/javadocs" && cp -a "./build/docs/javadoc/." "$_" fi diff --git a/.github/workflows/release-docs.yml b/.github/workflows/release-docs.yml index f6f7ebc8a27..9cfe198ba93 100644 --- a/.github/workflows/release-docs.yml +++ b/.github/workflows/release-docs.yml @@ -3,6 +3,7 @@ name: Release documentation on: release: types: [published] + workflow_dispatch: jobs: release-docs: @@ -33,6 +34,7 @@ jobs: docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} skript_repo_dir: ${{ steps.configuration.outputs.SKRIPT_REPO_DIR }} is_release: true + generate_javadocs: true cleanup_pattern: "!(nightly|archives|templates)" - name: Push release documentation uses: ./skript/.github/workflows/docs/push-docs @@ -41,40 +43,3 @@ jobs: git_name: Release Docs Bot git_email: releasedocs@skriptlang.org git_commit_message: "Update release docs to ${{ steps.configuration.outputs.BRANCH_NAME }}" - - archive-docs: - if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" - needs: release-docs - runs-on: ubuntu-latest - steps: - - name: Configure workflow - id: configuration - run: | - echo "BRANCH_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - echo "DOCS_OUTPUT_DIR=${GITHUB_WORKSPACE}/skript-docs/docs/archives/${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - echo "DOCS_REPO_DIR=${GITHUB_WORKSPACE}/skript-docs" >> $GITHUB_OUTPUT - echo "SKRIPT_REPO_DIR=${GITHUB_WORKSPACE}/skript" >> $GITHUB_OUTPUT - - name: Checkout Skript - uses: actions/checkout@v4 - with: - submodules: recursive - path: skript - - name: Setup documentation environment - uses: ./skript/.github/workflows/docs/setup-docs - with: - docs_deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }} - docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} - - name: Generate documentation - uses: ./skript/.github/workflows/docs/generate-docs - with: - docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} - docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} - skript_repo_dir: ${{ steps.configuration.outputs.SKRIPT_REPO_DIR }} - is_release: true - - name: Push archive documentation - uses: ./skript/.github/workflows/docs/push-docs - with: - docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} - git_name: Archive Docs Bot - git_email: archivedocs@skriptlang.org - git_commit_message: "Update ${{ steps.configuration.outputs.BRANCH_NAME }} archive docs" diff --git a/build.gradle b/build.gradle index 69158646591..9760df3cd13 100644 --- a/build.gradle +++ b/build.gradle @@ -140,15 +140,6 @@ publishing { } } -task releaseJavadoc(type: Javadoc) { - title = project.name + ' ' + project.property('version') - source = sourceSets.main.allJava - classpath = configurations.compileClasspath - options.encoding = 'UTF-8' - // currently our javadoc has a lot of errors, so we need to suppress the linter - options.addStringOption('Xdoclint:none', '-quiet') -} - // Task to check that test scripts are named correctly tasks.register('testNaming') { doLast { @@ -367,7 +358,7 @@ task nightlyResources(type: ProcessResources) { 'today' : '' + LocalTime.now(), 'release-flavor' : 'skriptlang-nightly', // SkriptLang build, automatically done by CI 'release-channel' : 'prerelease', // No update checking, but these are VERY unstable - 'release-updater' : 'ch.njol.skript.update.NoUpdateChecker', // No autoupdates for now + 'release-updater' : 'ch.njol.skript.update.NoUpdateChecker', // No auto updates for now 'release-source' : '', 'release-download': 'null' ] @@ -389,8 +380,7 @@ task nightlyRelease(type: ShadowJar) { } javadoc { - dependsOn nightlyResources - + title = 'Skript ' + project.property('version') source = sourceSets.main.allJava exclude("ch/njol/skript/conditions/**") @@ -409,4 +399,3 @@ javadoc { // currently our javadoc has a lot of errors, so we need to suppress the linter options.addStringOption('Xdoclint:none', '-quiet') } - From e02015f67d052210c8dcf8e65c8117a52f596a36 Mon Sep 17 00:00:00 2001 From: APickledWalrus Date: Sat, 22 Jun 2024 14:36:32 -0400 Subject: [PATCH 13/48] archive-docs: fix line endings Also removes a duplicate entry --- .github/workflows/archive-docs.yml | 91 +++++++++++++++--------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/.github/workflows/archive-docs.yml b/.github/workflows/archive-docs.yml index 4f921144ef2..e183f748d78 100644 --- a/.github/workflows/archive-docs.yml +++ b/.github/workflows/archive-docs.yml @@ -1,46 +1,45 @@ -name: Archive documentation - -on: - release: - types: [published] - workflow_dispatch: - -jobs: - archive-docs: - if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" - needs: release-docs - runs-on: ubuntu-latest - steps: - - name: Configure workflow - id: configuration - run: | - echo "BRANCH_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT - echo "DOCS_OUTPUT_DIR=${GITHUB_WORKSPACE}/skript-docs/docs/archives/${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT - echo "DOCS_REPO_DIR=${GITHUB_WORKSPACE}/skript-docs" >> $GITHUB_OUTPUT - echo "SKRIPT_REPO_DIR=${GITHUB_WORKSPACE}/skript" >> $GITHUB_OUTPUT - - name: Checkout Skript - uses: actions/checkout@v4 - with: - submodules: recursive - path: skript - - name: Setup documentation environment - uses: ./skript/.github/workflows/docs/setup-docs - with: - docs_deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }} - docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} - - name: Generate documentation - uses: ./skript/.github/workflows/docs/generate-docs - with: - docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} - docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} - docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} - skript_repo_dir: ${{ steps.configuration.outputs.SKRIPT_REPO_DIR }} - is_release: true - generate_javadocs: true - - name: Push archive documentation - uses: ./skript/.github/workflows/docs/push-docs - with: - docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} - git_name: Archive Docs Bot - git_email: archivedocs@skriptlang.org - git_commit_message: "Update ${{ steps.configuration.outputs.BRANCH_NAME }} archive docs" +name: Archive documentation + +on: + release: + types: [published] + workflow_dispatch: + +jobs: + archive-docs: + if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" + needs: release-docs + runs-on: ubuntu-latest + steps: + - name: Configure workflow + id: configuration + run: | + echo "BRANCH_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + echo "DOCS_OUTPUT_DIR=${GITHUB_WORKSPACE}/skript-docs/docs/archives/${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + echo "DOCS_REPO_DIR=${GITHUB_WORKSPACE}/skript-docs" >> $GITHUB_OUTPUT + echo "SKRIPT_REPO_DIR=${GITHUB_WORKSPACE}/skript" >> $GITHUB_OUTPUT + - name: Checkout Skript + uses: actions/checkout@v4 + with: + submodules: recursive + path: skript + - name: Setup documentation environment + uses: ./skript/.github/workflows/docs/setup-docs + with: + docs_deploy_key: ${{ secrets.DOCS_DEPLOY_KEY }} + docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} + - name: Generate documentation + uses: ./skript/.github/workflows/docs/generate-docs + with: + docs_output_dir: ${{ steps.configuration.outputs.DOCS_OUTPUT_DIR }} + docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} + skript_repo_dir: ${{ steps.configuration.outputs.SKRIPT_REPO_DIR }} + is_release: true + generate_javadocs: true + - name: Push archive documentation + uses: ./skript/.github/workflows/docs/push-docs + with: + docs_repo_dir: ${{ steps.configuration.outputs.DOCS_REPO_DIR }} + git_name: Archive Docs Bot + git_email: archivedocs@skriptlang.org + git_commit_message: "Update ${{ steps.configuration.outputs.BRANCH_NAME }} archive docs" From e2928ceae90c94beabe51240358d401b22524fba Mon Sep 17 00:00:00 2001 From: APickledWalrus Date: Sat, 22 Jun 2024 17:48:20 -0400 Subject: [PATCH 14/48] build.gradle: fix javadoc dependency ordering --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 9760df3cd13..161d3dd3daf 100644 --- a/build.gradle +++ b/build.gradle @@ -380,6 +380,7 @@ task nightlyRelease(type: ShadowJar) { } javadoc { + mustRunAfter(tasks.withType(ProcessResources)) title = 'Skript ' + project.property('version') source = sourceSets.main.allJava From ccc93730e85969979c32784ade0b10bfd83bc406 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:52:13 +0200 Subject: [PATCH 15/48] Add Timespan/Timespan=Number arithmetic and TimePeriod.MILLISECOND (#6749) * add ts/ts = num operation + tests + TimePeriod.Milliseconds * Update src/main/resources/lang/default.lang Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --------- Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --- .../skript/classes/data/DefaultOperations.java | 17 +++++++++++------ src/main/java/ch/njol/skript/util/Timespan.java | 1 + src/main/resources/lang/default.lang | 1 + .../syntaxes/expressions/ExprArithmetic.sk | 8 +++++++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java b/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java index e3f9965c1c4..860c7ee1030 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultOperations.java @@ -20,6 +20,7 @@ import ch.njol.skript.util.Date; import ch.njol.skript.util.Timespan; +import ch.njol.skript.util.Timespan.TimePeriod; import ch.njol.skript.util.Utils; import ch.njol.util.Math2; import org.bukkit.util.Vector; @@ -85,9 +86,9 @@ public class DefaultOperations { }); // Timespan - Timespan - Arithmetics.registerOperation(Operator.ADDITION, Timespan.class, (left, right) -> new Timespan(Math2.addClamped(left.getMilliSeconds(), right.getMilliSeconds()))); - Arithmetics.registerOperation(Operator.SUBTRACTION, Timespan.class, (left, right) -> new Timespan(Math.max(0, left.getMilliSeconds() - right.getMilliSeconds()))); - Arithmetics.registerDifference(Timespan.class, (left, right) -> new Timespan(Math.abs(left.getMilliSeconds() - right.getMilliSeconds()))); + Arithmetics.registerOperation(Operator.ADDITION, Timespan.class, (left, right) -> new Timespan(Math2.addClamped(left.getAs(TimePeriod.MILLISECOND), right.getAs(TimePeriod.MILLISECOND)))); + Arithmetics.registerOperation(Operator.SUBTRACTION, Timespan.class, (left, right) -> new Timespan(Math.max(0, left.getAs(TimePeriod.MILLISECOND) - right.getAs(TimePeriod.MILLISECOND)))); + Arithmetics.registerDifference(Timespan.class, (left, right) -> new Timespan(Math.abs(left.getAs(TimePeriod.MILLISECOND) - right.getAs(TimePeriod.MILLISECOND)))); Arithmetics.registerDefaultValue(Timespan.class, Timespan::new); // Timespan - Number @@ -96,20 +97,24 @@ public class DefaultOperations { long scalar = right.longValue(); if (scalar < 0) return null; - return new Timespan(Math2.multiplyClamped(left.getMilliSeconds(), scalar)); + return new Timespan(Math2.multiplyClamped(left.getAs(TimePeriod.MILLISECOND), scalar)); }, (left, right) -> { long scalar = left.longValue(); if (scalar < 0) return null; - return new Timespan(scalar * right.getMilliSeconds()); + return new Timespan(scalar * right.getAs(TimePeriod.MILLISECOND)); }); Arithmetics.registerOperation(Operator.DIVISION, Timespan.class, Number.class, (left, right) -> { long scalar = right.longValue(); if (scalar <= 0) return null; - return new Timespan(left.getMilliSeconds() / scalar); + return new Timespan(left.getAs(TimePeriod.MILLISECOND) / scalar); }); + // Timespan / Timespan = Number + Arithmetics.registerOperation(Operator.DIVISION, Timespan.class, Timespan.class, Number.class, + (left, right) -> left.getAs(TimePeriod.MILLISECOND) / (double) right.getAs(TimePeriod.MILLISECOND)); + // Date - Timespan Arithmetics.registerOperation(Operator.ADDITION, Date.class, Timespan.class, Date::plus); Arithmetics.registerOperation(Operator.SUBTRACTION, Date.class, Timespan.class, Date::minus); diff --git a/src/main/java/ch/njol/skript/util/Timespan.java b/src/main/java/ch/njol/skript/util/Timespan.java index a73f1ce6aaf..ff70f8b365f 100644 --- a/src/main/java/ch/njol/skript/util/Timespan.java +++ b/src/main/java/ch/njol/skript/util/Timespan.java @@ -44,6 +44,7 @@ public class Timespan implements YggdrasilSerializable, Comparable { / public enum TimePeriod { + MILLISECOND(1L), TICK(50L), SECOND(1000L), MINUTE(SECOND.time * 60L), diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 90a37eb9e32..701cd9d07b9 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -344,6 +344,7 @@ tree types: # -- Time -- time: + millisecond: millisecond¦s tick: tick¦s second: second¦s minute: minute¦s diff --git a/src/test/skript/tests/syntaxes/expressions/ExprArithmetic.sk b/src/test/skript/tests/syntaxes/expressions/ExprArithmetic.sk index 9d1276f4f85..ef17ec0d979 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprArithmetic.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprArithmetic.sk @@ -224,7 +224,13 @@ test "timespan arithmetic": assert (2 * {_t1}) is (2 seconds) with "2 * 1 second is not 2 seconds" assert (2 / {_t1}) is not set with "number divided by timespan is set" - assert ({_t1} + 2) is not set with "timespan plus number is set" + assert ({_t1} + 2) is not set with "timespan plus number is set" + + assert {_t1} / {_t2} is 0.5 with "timespan / timespan failed" + assert {_t1} / 1 tick is 20 with "timespan / timespan of different units failed" + assert 0 seconds / {_t2} is 0 with "0 timespan / timespan failed" + assert {_t1} / 0 seconds is infinity value with "timespan / 0 timespan failed" + assert isNaN(0 seconds / 0 ticks) is true with "0 timespan / 0 timespan failed", expected NaN value, got (0 seconds / 0 ticks) test "date arithmetic": set {_d1} to now From 39acfe34c7bcde3866677697cfe2f824e0b6a04f Mon Sep 17 00:00:00 2001 From: Moderocky Date: Fri, 28 Jun 2024 16:57:27 +0100 Subject: [PATCH 16/48] Whether expression for getting the result of conditions. (#6748) * Create whether expression. * Stupid e :( * Apply suggestions from code review Co-authored-by: Patrick Miller * Fix ternary + toString. * Update src/main/java/ch/njol/skript/expressions/ExprWhether.java Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Update src/main/java/ch/njol/skript/expressions/ExprWhether.java Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --------- Co-authored-by: Patrick Miller Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --- .../njol/skript/expressions/ExprTernary.java | 6 +- .../njol/skript/expressions/ExprWhether.java | 78 +++++++++++++++++++ .../tests/syntaxes/expressions/ExprWhether.sk | 14 ++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/main/java/ch/njol/skript/expressions/ExprWhether.java create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprWhether.sk diff --git a/src/main/java/ch/njol/skript/expressions/ExprTernary.java b/src/main/java/ch/njol/skript/expressions/ExprTernary.java index 3992f308623..dc4f5b518c6 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprTernary.java +++ b/src/main/java/ch/njol/skript/expressions/ExprTernary.java @@ -122,8 +122,10 @@ public boolean isSingle() { } @Override - public String toString(Event e, boolean debug) { - return ifTrue.toString(e, debug) + " if " + condition + " otherwise " + ifFalse.toString(e, debug); + public String toString(Event event, boolean debug) { + return ifTrue.toString(event, debug) + + " if " + condition.toString(event, debug) + + " otherwise " + ifFalse.toString(event, debug); } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprWhether.java b/src/main/java/ch/njol/skript/expressions/ExprWhether.java new file mode 100644 index 00000000000..4817551e181 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprWhether.java @@ -0,0 +1,78 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Condition; +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 org.bukkit.event.Event; +import org.jetbrains.annotations.UnknownNullability; + +@Name("Whether") +@Description("A shorthand for returning the result of a condition (true or false). This is functionally identical to using `true if else false`.") +@Examples({ + "set {fly} to whether player can fly", + "broadcast \"Flying: %whether player is flying%\"" +}) +@Since("INSERT VERSION") +public class ExprWhether extends SimpleExpression { + + static { + Skript.registerExpression(ExprWhether.class, Boolean.class, ExpressionType.PATTERN_MATCHES_EVERYTHING, + "whether <.+>"); + } + + private @UnknownNullability Condition condition; + + @Override + public boolean init(Expression[] expressions, int pattern, Kleenean delayed, ParseResult result) { + String input = result.regexes.get(0).group(); + this.condition = Condition.parse(input, "Can't understand this condition: " + input); + return condition != null; + } + + @Override + protected Boolean[] get(Event event) { + return new Boolean[] {condition.check(event)}; + } + + @Override + public Class getReturnType() { + return Boolean.class; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public String toString(Event event, boolean debug) { + return "whether " + condition.toString(event, debug); + } + +} diff --git a/src/test/skript/tests/syntaxes/expressions/ExprWhether.sk b/src/test/skript/tests/syntaxes/expressions/ExprWhether.sk new file mode 100644 index 00000000000..83001203599 --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprWhether.sk @@ -0,0 +1,14 @@ +test "whether": + set {_number} to 5 + set {_okay} to whether {_number} is greater than 3 + assert {_okay} is true with "Condition didn't evaluate correctly" + set {_okay} to whether {_number} is less than 6 + assert {_okay} is true with "Condition didn't evaluate correctly" + set {_okay} to whether {_number} is 5 + assert {_okay} is true with "Condition didn't evaluate correctly" + delete {_okay} + spawn a pig at spawn of "world": + set {_pig} to event-entity + assert (whether {_pig} is alive) is true with "Condition didn't evaluate correctly" + assert (whether health of {_pig} is greater than 0) is true with "Condition didn't evaluate correctly" + delete the last spawned pig From df620e4da66f4719d2da75ca74f27ecabb91a815 Mon Sep 17 00:00:00 2001 From: Phill310 Date: Fri, 28 Jun 2024 09:47:23 -0700 Subject: [PATCH 17/48] Adds the EndermanAttackPlayer event to skript (#6596) * Adds the EndermanAttackPlayer event to skript * Remove version from required plugins Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Simplify event description Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Remove redundant event-value registration Co-authored-by: Patrick Miller --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Moderocky Co-authored-by: Patrick Miller --- .../skript/classes/data/BukkitEventValues.java | 10 ++++++++++ .../java/ch/njol/skript/events/SimpleEvents.java | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 6131def1eec..76e098cb602 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -41,6 +41,7 @@ import ch.njol.skript.util.slot.InventorySlot; import ch.njol.skript.util.slot.Slot; import com.destroystokyo.paper.event.block.AnvilDamagedEvent; +import com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent; import com.destroystokyo.paper.event.entity.ProjectileCollideEvent; import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; import io.papermc.paper.event.entity.EntityMoveEvent; @@ -691,6 +692,15 @@ public Entity get(LightningStrikeEvent event) { return event.getLightning(); } }, 0); + // EndermanAttackPlayerEvent + if (Skript.classExists("com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent")) { + EventValues.registerEventValue(EndermanAttackPlayerEvent.class, Player.class, new Getter() { + @Override + public Player get(EndermanAttackPlayerEvent event) { + return event.getPlayer(); + } + }, EventValues.TIME_NOW); + } // --- PlayerEvents --- EventValues.registerEventValue(PlayerEvent.class, Player.class, new Getter() { diff --git a/src/main/java/ch/njol/skript/events/SimpleEvents.java b/src/main/java/ch/njol/skript/events/SimpleEvents.java index 23f819ea890..c470d68a07d 100644 --- a/src/main/java/ch/njol/skript/events/SimpleEvents.java +++ b/src/main/java/ch/njol/skript/events/SimpleEvents.java @@ -797,6 +797,22 @@ public class SimpleEvents { .since("INSERT VERSION") .requiredPlugins("Spigot 1.19.4+"); } + + if (Skript.classExists("com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent")) { + Skript.registerEvent("Enderman Enrage", SimpleEvent.class, com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent.class, "enderman (enrage|anger)") + .description( + "Called when an enderman gets mad because a player looked at them.", + "Note: This does not stop enderman from targeting the player as a result of getting damaged." + ) + .examples( + "# Stops endermen from getting angry players with the permission \"safeFrom.enderman\"", + "on enderman enrage:", + "\tif player has permission \"safeFrom.enderman\":", + "\t\tcancel event" + ) + .since("INSERT VERSION") + .requiredPlugins("Paper"); + } } } From 2c814b1d9c3f512f5a498476d7fef168cd543f1a Mon Sep 17 00:00:00 2001 From: Moderocky Date: Sun, 30 Jun 2024 20:06:30 +0100 Subject: [PATCH 18/48] Raise the minimum Java version to 11. (#6557) --- .../{java-8-builds.yml => java-11-builds.yml} | 4 ++-- ...{junit-8-builds.yml => junit-11-builds.yml} | 2 +- README.md | 12 ++++++------ build.gradle | 18 +++++++++--------- code-conventions.md | 2 +- .../{java8 => java11}/paper-1.13.2.json | 0 .../{java8 => java11}/paper-1.14.4.json | 0 .../{java8 => java11}/paper-1.15.2.json | 0 .../{java8 => java11}/paper-1.16.5.json | 0 9 files changed, 19 insertions(+), 19 deletions(-) rename .github/workflows/{java-8-builds.yml => java-11-builds.yml} (92%) rename .github/workflows/{junit-8-builds.yml => junit-11-builds.yml} (94%) rename src/test/skript/environments/{java8 => java11}/paper-1.13.2.json (100%) rename src/test/skript/environments/{java8 => java11}/paper-1.14.4.json (100%) rename src/test/skript/environments/{java8 => java11}/paper-1.15.2.json (100%) rename src/test/skript/environments/{java8 => java11}/paper-1.16.5.json (100%) diff --git a/.github/workflows/java-8-builds.yml b/.github/workflows/java-11-builds.yml similarity index 92% rename from .github/workflows/java-8-builds.yml rename to .github/workflows/java-11-builds.yml index 0120b950f94..443aa925d0c 100644 --- a/.github/workflows/java-8-builds.yml +++ b/.github/workflows/java-11-builds.yml @@ -1,4 +1,4 @@ -name: Java 8 CI (MC 1.13-1.16) +name: Java 11 CI (MC 1.13-1.16) on: push: @@ -26,7 +26,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build Skript and run test scripts - run: ./gradlew clean skriptTestJava8 + run: ./gradlew clean skriptTestJava11 - name: Upload Nightly Build uses: actions/upload-artifact@v4 if: success() diff --git a/.github/workflows/junit-8-builds.yml b/.github/workflows/junit-11-builds.yml similarity index 94% rename from .github/workflows/junit-8-builds.yml rename to .github/workflows/junit-11-builds.yml index ec2fef19869..cdf75e4c43a 100644 --- a/.github/workflows/junit-8-builds.yml +++ b/.github/workflows/junit-11-builds.yml @@ -26,4 +26,4 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build Skript and run JUnit - run: ./gradlew clean JUnitJava8 + run: ./gradlew clean JUnitJava11 diff --git a/README.md b/README.md index 2dba8104819..b4ab6c4e48f 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ Skript requires **Spigot** to work. You heard it right, **CraftBukkit** does *no **Paper**, which is a fork of Spigot, is recommended; it is required for some parts of Skript to be available. -Skript supports only the **latest** patch versions of Minecraft 1.9+. +Skript supports only the **latest** patch versions of Minecraft 1.13+. For example, this means that 1.16.5 is supported, but 1.16.4 is *not*. Testing with all old patch versions is not feasible for us. -Minecraft 1.8 and earlier are not, and will not be supported. New Minecraft +Minecraft 1.12 and earlier are not, and will not be supported. New Minecraft versions will be supported as soon as possible. ## Download @@ -77,15 +77,15 @@ Skript has some tests written in Skript. Running them requires a Minecraft server, but our build script will create one for you. Running the tests is easy: ``` -./gradlew (quickTest|skriptTest|skriptTestJava8|skriptTestJava17|skriptTestJava21) +./gradlew (quickTest|skriptTest|skriptTestJava11|skriptTestJava17|skriptTestJava21) ``` quickTest runs the test suite on newest supported server version. skriptTestJava21 (1.20.6+) runs the tests on Java 21 supported versions. skriptTestJava17 (1.17-1.20.4) runs the tests on Java 17 supported versions. -skriptTestJava8 (1.13-1.16) runs the tests on Java 8 supported versions. +skriptTestJava11 (1.13-1.16) runs the tests on Java 11 supported versions. skriptTest runs the tests on all versions. -That is, it runs skriptTestJava8, skriptTestJava17, and skriptTestJava21. +That is, it runs skriptTestJava11, skriptTestJava17, and skriptTestJava21. By running the tests, you agree to Mojang's End User License Agreement. @@ -164,7 +164,7 @@ dependencies { } ``` -An example of the version tag would be ```dev37c```. +An example of the version tag would be ```2.8.5```. > Note: If Gradle isn't able to resolve Skript's dependencies, just [disable the resolution of transitive dependencies](https://docs.gradle.org/current/userguide/resolution_rules.html#sec:disabling_resolution_transitive_dependencies) for Skript in your project. diff --git a/build.gradle b/build.gradle index 161d3dd3daf..d6a60fa9e37 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ task build(overwrite: true, type: ShadowJar) { from sourceSets.main.output } -// Excludes the tests for the build task. Should be using junit, junitJava17, junitJava8, skriptTest, quickTest. +// Excludes the tests for the build task. Should be using junit, junitJava17, junitJava11, skriptTest, quickTest. // We do not want tests to run for building. That's time consuming and annoying. Especially in development. test { exclude '**/*' @@ -224,7 +224,7 @@ void createTestTask(String name, String desc, String environments, int javaVersi if (!gradle.taskGraph.hasTask(":tasks") && !gradle.startParameter.dryRun && modifiers.contains(Modifiers.PROFILE)) { if (!project.hasProperty('profiler')) throw new MissingPropertyException('Add parameter -Pprofiler=', 'profiler', String.class) - + args += '-agentpath:' + project.property('profiler') + '=port=8849,nowait' } } @@ -233,11 +233,11 @@ void createTestTask(String name, String desc, String environments, int javaVersi def java21 = 21 def java17 = 17 -def java8 = 8 +def java11 = 11 def latestEnv = 'java21/paper-1.20.6.json' def latestJava = java21 -def oldestJava = java8 +def oldestJava = java11 def latestJUnitEnv = 'java17/paper-1.20.4.json' def latestJUnitJava = java17 @@ -260,14 +260,14 @@ int envJava = project.property('testEnvJavaVersion') == null ? latestJava : Inte createTestTask('quickTest', 'Runs tests on one environment being the latest supported Java and Minecraft.', environments + latestEnv, latestJava, 0) createTestTask('skriptTestJava21', 'Runs tests on all Java 21 environments.', environments + 'java21', java21, 0) createTestTask('skriptTestJava17', 'Runs tests on all Java 17 environments.', environments + 'java17', java17, 0) -createTestTask('skriptTestJava8', 'Runs tests on all Java 8 environments.', environments + 'java8', java8, 0) +createTestTask('skriptTestJava11', 'Runs tests on all Java 11 environments.', environments + 'java11', java11, 0) createTestTask('skriptTestDev', 'Runs testing server and uses \'system.in\' for command input, stop server to finish.', environments + env, envJava, 0, Modifiers.DEV_MODE, Modifiers.DEBUG) createTestTask('skriptProfile', 'Starts the testing server with JProfiler support.', environments + latestEnv, latestJava, -1, Modifiers.PROFILE) createTestTask('genNightlyDocs', 'Generates the Skript documentation website html files.', environments + env, envJava, 0, Modifiers.GEN_NIGHTLY_DOCS) createTestTask('genReleaseDocs', 'Generates the Skript documentation website html files for a release.', environments + env, envJava, 0, Modifiers.GEN_RELEASE_DOCS) tasks.register('skriptTest') { description = 'Runs tests on all environments.' - dependsOn skriptTestJava8, skriptTestJava17, skriptTestJava21 + dependsOn skriptTestJava11, skriptTestJava17, skriptTestJava21 } createTestTask('JUnitQuick', 'Runs JUnit tests on one environment being the latest supported Java and Minecraft.', environments + latestJUnitEnv, latestJUnitJava, 0, Modifiers.JUNIT) @@ -275,10 +275,10 @@ createTestTask('JUnitQuick', 'Runs JUnit tests on one environment being the late // However, we are currently using 5.0.1 (see https://github.com/SkriptLang/Skript/pull/6204#discussion_r1405302009) //createTestTask('JUnitJava21', 'Runs JUnit tests on all Java 21 environments.', environments + 'java21', java21, 0, Modifiers.JUNIT) createTestTask('JUnitJava17', 'Runs JUnit tests on all Java 17 environments.', environments + 'java17', java17, 0, Modifiers.JUNIT) -createTestTask('JUnitJava8', 'Runs JUnit tests on all Java 8 environments.', environments + 'java8', java8, 0, Modifiers.JUNIT) +createTestTask('JUnitJava11', 'Runs JUnit tests on all Java 11 environments.', environments + 'java11', java11, 0, Modifiers.JUNIT) tasks.register('JUnit') { description = 'Runs JUnit tests on all environments.' - dependsOn JUnitJava8, JUnitJava17//, JUnitJava21 + dependsOn JUnitJava11, JUnitJava17//, JUnitJava21 } // Build flavor configurations @@ -394,7 +394,7 @@ javadoc { exclude("ch/njol/skript/lang/function/ExprFunctionCall.java") exclude("ch/njol/skript/hooks/**") exclude("ch/njol/skript/test/**") - + classpath = configurations.compileClasspath + sourceSets.main.output options.encoding = 'UTF-8' // currently our javadoc has a lot of errors, so we need to suppress the linter diff --git a/code-conventions.md b/code-conventions.md index 25c6d5c4417..3bcf17c6c9f 100644 --- a/code-conventions.md +++ b/code-conventions.md @@ -194,7 +194,7 @@ Your comments should look something like these: ## Language Features ### Compatibility -[//]: # (To be updated after feature/2.9 for Java 17) +[//]: # (To be updated for 2.10 for Java 17) * Contributions should maintain Java 11 source/binary compatibility, even though compiling Skript requires Java 21 - Users must not need JRE newer than version 11 * Versions up to and including Java 21 should work too diff --git a/src/test/skript/environments/java8/paper-1.13.2.json b/src/test/skript/environments/java11/paper-1.13.2.json similarity index 100% rename from src/test/skript/environments/java8/paper-1.13.2.json rename to src/test/skript/environments/java11/paper-1.13.2.json diff --git a/src/test/skript/environments/java8/paper-1.14.4.json b/src/test/skript/environments/java11/paper-1.14.4.json similarity index 100% rename from src/test/skript/environments/java8/paper-1.14.4.json rename to src/test/skript/environments/java11/paper-1.14.4.json diff --git a/src/test/skript/environments/java8/paper-1.15.2.json b/src/test/skript/environments/java11/paper-1.15.2.json similarity index 100% rename from src/test/skript/environments/java8/paper-1.15.2.json rename to src/test/skript/environments/java11/paper-1.15.2.json diff --git a/src/test/skript/environments/java8/paper-1.16.5.json b/src/test/skript/environments/java11/paper-1.16.5.json similarity index 100% rename from src/test/skript/environments/java8/paper-1.16.5.json rename to src/test/skript/environments/java11/paper-1.16.5.json From 40307576f94ed405adbcb8d59637004bedd3d562 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:41:36 +0200 Subject: [PATCH 19/48] fix exception when removing air from slot (#6836) * fix exception when removing air from slot * Update 6830-remove air from slot.sk --- .../ch/njol/skript/bukkitutil/ItemUtils.java | 35 +++++++++++++------ .../regressions/6830-remove air from slot.sk | 4 +++ 2 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 src/test/skript/tests/regressions/6830-remove air from slot.sk diff --git a/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java b/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java index 31bf99c2d56..216be44b918 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java @@ -42,9 +42,17 @@ public class ItemUtils { * @return Damage. */ public static int getDamage(ItemStack itemStack) { - ItemMeta meta = itemStack.getItemMeta(); - if (meta instanceof Damageable) - return ((Damageable) meta).getDamage(); + return getDamage(itemStack.getItemMeta()); + } + + /** + * Gets damage/durability of an itemmeta, or 0 if it does not have damage. + * @param itemMeta ItemMeta. + * @return Damage. + */ + public static int getDamage(ItemMeta itemMeta) { + if (itemMeta instanceof Damageable) + return ((Damageable) itemMeta).getDamage(); return 0; // Non damageable item } @@ -143,15 +151,22 @@ public static Material asItem(Material type) { /** * Tests whether two item stacks are of the same type, i.e. it ignores the amounts. * - * @param is1 - * @param is2 + * @param itemStack1 + * @param itemStack2 * @return Whether the item stacks are of the same type */ - public static boolean itemStacksEqual(final @Nullable ItemStack is1, final @Nullable ItemStack is2) { - if (is1 == null || is2 == null) - return is1 == is2; - return is1.getType() == is2.getType() && ItemUtils.getDamage(is1) == ItemUtils.getDamage(is2) - && is1.getItemMeta().equals(is2.getItemMeta()); + public static boolean itemStacksEqual(@Nullable ItemStack itemStack1, @Nullable ItemStack itemStack2) { + if (itemStack1 == null || itemStack2 == null) + return itemStack1 == itemStack2; + if (itemStack1.getType() != itemStack2.getType()) + return false; + + ItemMeta itemMeta1 = itemStack1.getItemMeta(); + ItemMeta itemMeta2 = itemStack2.getItemMeta(); + if (itemMeta1 == null || itemMeta2 == null) + return itemMeta1 == itemMeta2; + + return itemStack1.getItemMeta().equals(itemStack2.getItemMeta()); } // Only 1.15 and versions after have Material#isAir method diff --git a/src/test/skript/tests/regressions/6830-remove air from slot.sk b/src/test/skript/tests/regressions/6830-remove air from slot.sk new file mode 100644 index 00000000000..13eaa29b1d3 --- /dev/null +++ b/src/test/skript/tests/regressions/6830-remove air from slot.sk @@ -0,0 +1,4 @@ +test "remove air from air slot": + set {_chest} to chest inventory with 3 rows + # throws exception if not fixed + remove 1 of (slot 0 of {_chest}) from (slot 0 of {_chest}) From a4f38d2c1da49bc5416d38979ea9f791dcf92292 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 05:42:44 -0700 Subject: [PATCH 20/48] EffBan - add option to kick (#6835) * EffBan - add option to kick - also did a lil cleanup * EffBan - toString change as per suggestion --- .../java/ch/njol/skript/effects/EffBan.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffBan.java b/src/main/java/ch/njol/skript/effects/EffBan.java index 1e674e23ded..3826fd7f372 100644 --- a/src/main/java/ch/njol/skript/effects/EffBan.java +++ b/src/main/java/ch/njol/skript/effects/EffBan.java @@ -26,7 +26,6 @@ import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; @@ -38,6 +37,7 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.util.Timespan; import ch.njol.util.Kleenean; +import org.jetbrains.annotations.Nullable; @Name("Ban") @Description({"Bans or unbans a player or an IP address.", @@ -46,19 +46,23 @@ "We recommend that you test your scripts so that no accidental permanent bans are applied.", "", "Note that banning people does not kick them from the server.", - "Consider using the kick effect after applying a ban."}) + "You can optionally use 'and kick' or consider using the kick effect after applying a ban."}) @Examples({"unban player", "ban \"127.0.0.1\"", "IP-ban the player because \"he is an idiot\"", - "ban player due to \"inappropriate language\" for 2 days"}) -@Since("1.4, 2.1.1 (ban reason), 2.5 (timespan)") + "ban player due to \"inappropriate language\" for 2 days", + "ban and kick player due to \"inappropriate language\" for 2 days"}) +@Since("1.4, 2.1.1 (ban reason), 2.5 (timespan), INSERT VERSION (kick)") public class EffBan extends Effect { static { Skript.registerEffect(EffBan.class, - "ban %strings/offlineplayers% [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", "unban %strings/offlineplayers%", - "ban %players% by IP [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", "unban %players% by IP", - "IP(-| )ban %players% [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", "(IP(-| )unban|un[-]IP[-]ban) %players%"); + "ban [kick:and kick] %strings/offlineplayers% [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", + "unban %strings/offlineplayers%", + "ban [kick:and kick] %players% by IP [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", + "unban %players% by IP", + "IP(-| )ban [kick:and kick] %players% [(by reason of|because [of]|on account of|due to) %-string%] [for %-timespan%]", + "(IP(-| )unban|un[-]IP[-]ban) %players%"); } @SuppressWarnings("null") @@ -70,6 +74,7 @@ public class EffBan extends Effect { private boolean ban; private boolean ipBan; + private boolean kick; @SuppressWarnings({"null", "unchecked"}) @Override @@ -79,6 +84,7 @@ public boolean init(final Expression[] exprs, final int matchedPattern, final expires = exprs.length > 1 ? (Expression) exprs[2] : null; ban = matchedPattern % 2 == 0; ipBan = matchedPattern >= 2; + kick = parseResult.hasTag("kick"); return true; } @@ -91,8 +97,9 @@ protected void execute(final Event e) { final String source = "Skript ban effect"; for (final Object o : players.getArray(e)) { if (o instanceof Player) { + Player player = (Player) o; if (ipBan) { - InetSocketAddress addr = ((Player) o).getAddress(); + InetSocketAddress addr = player.getAddress(); if (addr == null) return; // Can't ban unknown IP final String ip = addr.getAddress().getHostAddress(); @@ -102,10 +109,12 @@ protected void execute(final Event e) { Bukkit.getBanList(BanList.Type.IP).pardon(ip); } else { if (ban) - Bukkit.getBanList(BanList.Type.NAME).addBan(((Player) o).getName(), reason, expires, source); // FIXME [UUID] ban UUID + Bukkit.getBanList(BanList.Type.NAME).addBan(player.getName(), reason, expires, source); // FIXME [UUID] ban UUID else - Bukkit.getBanList(BanList.Type.NAME).pardon(((Player) o).getName()); + Bukkit.getBanList(BanList.Type.NAME).pardon(player.getName()); } + if (kick) + player.kickPlayer(reason); } else if (o instanceof OfflinePlayer) { String name = ((OfflinePlayer) o).getName(); if (name == null) @@ -130,9 +139,13 @@ protected void execute(final Event e) { } @Override - public String toString(final @Nullable Event e, final boolean debug) { - return (ipBan ? "IP-" : "") + (ban ? "" : "un") + "ban " + players.toString(e, debug) + - (reason != null ? " on account of " + reason.toString(e, debug) : "") + (expires != null ? " for " + expires.toString(e, debug) : ""); + public String toString(final @Nullable Event event, final boolean debug) { + return (ipBan ? "IP-" : "") + + (this.ban ? "ban " : "unban ") + + (kick ? "and kick " : "") + + this.players.toString(event, debug) + + (this.reason != null ? " on account of " + this.reason.toString(event, debug) : "") + + (expires != null ? " for " + expires.toString(event, debug) : ""); } } From 13b230c90cbfa919e69caa33ff537e613b1f18a4 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 05:47:03 -0700 Subject: [PATCH 21/48] BlockData - add cloner (#6829) * BukkitClasses - add cloner for BlockData * Add regression test for #6829 * Fix error message in BlockDataNotCloned6829 --------- Co-authored-by: Pikachu920 <28607612+Pikachu920@users.noreply.github.com> Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/classes/data/BukkitClasses.java | 2 +- .../regression/BlockDataNotCloned6829.java | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/skriptlang/skript/test/tests/regression/BlockDataNotCloned6829.java diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index ba1a419aad0..69710a1ad00 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -355,7 +355,7 @@ public boolean mustSyncDeserialization() { protected boolean canBeInstantiated() { return false; } - })); + }).cloner(BlockData::clone)); Classes.registerClass(new ClassInfo<>(Location.class, "location") .user("locations?") diff --git a/src/test/java/org/skriptlang/skript/test/tests/regression/BlockDataNotCloned6829.java b/src/test/java/org/skriptlang/skript/test/tests/regression/BlockDataNotCloned6829.java new file mode 100644 index 00000000000..b621734958d --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/regression/BlockDataNotCloned6829.java @@ -0,0 +1,53 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package org.skriptlang.skript.test.tests.regression; + +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.TriggerItem; +import ch.njol.skript.lang.util.ContextlessEvent; +import ch.njol.skript.test.runner.SkriptJUnitTest; +import ch.njol.skript.variables.Variables; +import org.bukkit.block.data.type.Tripwire; +import org.bukkit.event.Event; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Objects; + +public class BlockDataNotCloned6829 extends SkriptJUnitTest { + + public void run(String unparsedEffect, Event event) { + Effect effect = Effect.parse(unparsedEffect, "Can't understand this effect: " + unparsedEffect); + if (effect == null) + throw new IllegalStateException(); + TriggerItem.walk(effect, event); + } + + @Test + public void test() { + Event event = ContextlessEvent.get(); + run("set {_original tripwire} to tripwire[]", event); + run("set {_another tripwire} to {_original tripwire}", event); + Tripwire originalTripwire = (Tripwire) Objects.requireNonNull(Variables.getVariable("original tripwire", event, true)); + Tripwire anotherTripwire = (Tripwire) Objects.requireNonNull(Variables.getVariable("another tripwire", event, true)); + anotherTripwire.setDisarmed(true); + Assert.assertFalse(originalTripwire.isDisarmed()); + } + +} From 1e23d7f670c8d4d6f52111ccd0aea9d80d0cde81 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 05:49:22 -0700 Subject: [PATCH 22/48] default.lang - add MC 1.21 attributes (#6791) Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- src/main/resources/lang/default.lang | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 90a37eb9e32..af2d90e64b0 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -2168,6 +2168,8 @@ attribute types: generic_attack_damage: generic attack damage, attack damage generic_attack_knockback: generic attack knockback, attack knockback generic_attack_speed: generic attack speed, attack speed + generic_burning_time: generic burning time, burning time + generic_explosion_knockback_resistance: generic explosion knockback resistance, explosion knockback resistance generic_flying_speed: generic flying speed, flying speed generic_follow_range: generic follow range, follow range generic_gravity: generic gravity, gravity @@ -2176,15 +2178,22 @@ attribute types: generic_luck: generic luck, luck generic_max_absorption: generic max absorption, max absorption generic_max_health: generic max health, max health + generic_movement_efficiency: generic movement efficiency, movement efficiency generic_movement_speed: generic movement speed, movement speed + generic_oxygen_bonus: generic oxygen bonus, oxygen bonus generic_safe_fall_distance: generic safe fall distance, safe fall distance generic_fall_damage_multiplier: generic fall damage multiplier, fall damage multiplier generic_scale: generic scale, scale generic_step_height: generic step height, step height + generic_water_movement_efficiency: generic water movement efficiency, water movement efficiency horse_jump_strength: horse jump strength player_block_break_speed: player block break speed, block break speed player_block_interaction_range: player block interaction range, block interaction range player_entity_interaction_range: player entity interaction range, entity interaction range + player_mining_efficiency: player mining efficiency, mining efficiency + player_sneaking_speed: player sneaking speed, sneaking speed + player_submerged_mining_speed: player submerged mining speed, submerged mining speed + player_sweeping_damage_ratio: player sweeping damage ratio, sweeping damage ratio zombie_spawn_reinforcements: zombie spawn reinforcements # -- Environments -- From 9db54261f215b6aa47f69d106f8e92cff58168a5 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 05:51:52 -0700 Subject: [PATCH 23/48] Add potion effect type comparator (#6832) * DefaultComparators - fix potion effect type match * DefaultComparators - fix potion effect type match - add test * DefaultComparators - update pickle suggestion --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/classes/data/DefaultComparators.java | 4 ++++ .../tests/regressions/pull-6832-potioneffecttype-compare.sk | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 src/test/skript/tests/regressions/pull-6832-potioneffecttype-compare.sk diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java index e59a5678403..87fa10b1089 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java @@ -60,6 +60,7 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; import org.skriptlang.skript.lang.comparator.Comparator; import org.skriptlang.skript.lang.comparator.Comparators; import org.skriptlang.skript.lang.comparator.Relation; @@ -653,6 +654,9 @@ public boolean supportsOrdering() { return false; } }); + + // Potion Effect Type + Comparators.registerComparator(PotionEffectType.class, PotionEffectType.class, (one, two) -> Relation.get(one.equals(two))); } } diff --git a/src/test/skript/tests/regressions/pull-6832-potioneffecttype-compare.sk b/src/test/skript/tests/regressions/pull-6832-potioneffecttype-compare.sk new file mode 100644 index 00000000000..f0243b8a85f --- /dev/null +++ b/src/test/skript/tests/regressions/pull-6832-potioneffecttype-compare.sk @@ -0,0 +1,3 @@ +test "compare potion effect types": + assert nausea = nausea with "2 nausea potion effect types should match" + assert type of (potion effect of nausea for 1 minute) = nausea with "type of potion effect of nausea should equal nausea" From 9f625868680f068f0de7e21d154045f4c1fa4a21 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 06:01:42 -0700 Subject: [PATCH 24/48] ExprLastDamage - fix everything (#6797) * ExprLastDamage - fix everything * HealthUtils set last damage manually * ExprLastDamage - add test --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/bukkitutil/HealthUtils.java | 9 ++-- .../skript/expressions/ExprLastDamage.java | 46 ++++++++----------- .../syntaxes/expressions/ExprLastDamage.sk | 15 ++++++ 3 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprLastDamage.sk diff --git a/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java b/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java index 20b5120a0f1..be8675ccabb 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/HealthUtils.java @@ -26,13 +26,13 @@ import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageType; import org.bukkit.entity.Damageable; +import org.bukkit.entity.LivingEntity; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; public class HealthUtils { @@ -112,8 +112,11 @@ public static double getFinalDamage(EntityDamageEvent e) { return e.getFinalDamage() / 2; } - public static void setDamage(EntityDamageEvent e, double damage) { - e.setDamage(damage * 2); + public static void setDamage(EntityDamageEvent event, double damage) { + event.setDamage(damage * 2); + // Set last damage manually as Bukkit doesn't appear to do that + if (event.getEntity() instanceof LivingEntity) + ((LivingEntity) event.getEntity()).setLastDamage(damage * 2); } @Nullable diff --git a/src/main/java/ch/njol/skript/expressions/ExprLastDamage.java b/src/main/java/ch/njol/skript/expressions/ExprLastDamage.java index 0b62cd60a9d..ede427b156f 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLastDamage.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLastDamage.java @@ -19,47 +19,34 @@ */ package ch.njol.skript.expressions; -import org.bukkit.entity.LivingEntity; -import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; - import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.expressions.base.SimplePropertyExpression; -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.SkriptParser; -import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.Event; +import org.jetbrains.annotations.Nullable; @Name("Last Damage") @Description("The last damage that was done to an entity. Note that changing it doesn't deal more/less damage.") @Examples({"set last damage of event-entity to 2"}) @Since("2.5.1") public class ExprLastDamage extends SimplePropertyExpression { - + static { register(ExprLastDamage.class, Number.class, "last damage", "livingentities"); } - - @Nullable - private ExprDamage damageExpr; - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - damageExpr = new ExprDamage(); - return true; - } - + @Nullable @Override @SuppressWarnings("null") public Number convert(LivingEntity livingEntity) { - return damageExpr.get(livingEntity.getLastDamageCause())[0]; + return livingEntity.getLastDamage() / 2; } - + @Nullable @Override public Class[] acceptChange(ChangeMode mode) { @@ -72,35 +59,38 @@ public Class[] acceptChange(ChangeMode mode) { return null; } } - + + @SuppressWarnings("ConstantValue") @Override public void change(Event e, @Nullable Object[] delta, ChangeMode mode) { - if (delta != null) { + if (delta != null && delta[0] instanceof Number) { + double damage = ((Number) delta[0]).doubleValue() * 2; + switch (mode) { case SET: for (LivingEntity entity : getExpr().getArray(e)) - entity.setLastDamage((Long) delta[0]); + entity.setLastDamage(damage); break; case REMOVE: - delta[0] = (Long) delta[0] * -1; + damage = damage * -1; case ADD: for (LivingEntity entity : getExpr().getArray(e)) - entity.setLastDamage((Long) delta[0] + entity.getLastDamage()); + entity.setLastDamage(damage + entity.getLastDamage()); break; default: assert false; } } } - + @Override public Class getReturnType() { return Number.class; } - + @Override protected String getPropertyName() { return "last damage"; } - + } diff --git a/src/test/skript/tests/syntaxes/expressions/ExprLastDamage.sk b/src/test/skript/tests/syntaxes/expressions/ExprLastDamage.sk new file mode 100644 index 00000000000..0ed3f3f3e4c --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprLastDamage.sk @@ -0,0 +1,15 @@ +test "last damage": + set {_l} to location 0.5 above highest block at location(1,1,1) + spawn a sheep at {_l} + set {_e} to last spawned entity + + assert last damage of {_e} = 0 with "last damage of newly spawned entity should be 0" + + damage {_e} by 1 + assert last damage of {_e} = 1 with "last damage of entity should be 1 after damaging it by 1" + + set last damage of {_e} to 3 + assert last damage of {_e} = 3 with "last damage of entity should be 3 after setting to 3" + + # thank you for your service + delete entity in {_e} From abc782c34ceeb55f72c9e52d55a9912192eb31e8 Mon Sep 17 00:00:00 2001 From: Phill310 Date: Mon, 1 Jul 2024 06:02:13 -0700 Subject: [PATCH 25/48] Add delete changer to ExprMessage (#6828) * Add delete changer * Update examples * use toString Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Add JUnit test (chat event) * Call chat event in JUnit Test * Create viewer Set differently * Add more tests (join/quit) * Remove use of components --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/expressions/ExprMessage.java | 32 ++++++----- .../{ => expressions}/ExprDropsTest.java | 2 +- .../syntaxes/expressions/ExprMessageTest.java | 53 +++++++++++++++++++ src/test/skript/junit/ExprDrops.sk | 4 +- src/test/skript/junit/ExprMessage.sk | 44 +++++++++++++++ 5 files changed, 118 insertions(+), 17 deletions(-) rename src/test/java/org/skriptlang/skript/test/tests/syntaxes/{ => expressions}/ExprDropsTest.java (94%) create mode 100644 src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprMessageTest.java create mode 100644 src/test/skript/junit/ExprMessage.sk diff --git a/src/main/java/ch/njol/skript/expressions/ExprMessage.java b/src/main/java/ch/njol/skript/expressions/ExprMessage.java index a3b54f43c4c..b973852dcc5 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprMessage.java +++ b/src/main/java/ch/njol/skript/expressions/ExprMessage.java @@ -48,23 +48,27 @@ @SuppressWarnings("deprecation") @Name("Message") @Description("The (chat) message of a chat event, the join message of a join event, the quit message of a quit event, or the death message on a death event. This expression is mostly useful for being changed.") -@Examples({"on chat:", - " player has permission \"admin\"", - " set message to \"&c%message%\"", +@Examples({ + "on chat:", + "\tplayer has permission \"admin\"", + "\tset message to \"&c%message%\"", "", "on first join:", - " set join message to \"Welcome %player% to our awesome server!\"", + "\tset join message to \"Welcome %player% to our awesome server!\"", "", "on join:", - " player has played before", - " set join message to \"Welcome back, %player%!\"", + "\tplayer has played before", + "\tset join message to \"Welcome back, %player%!\"", "", "on quit:", - " set quit message to \"%player% left this awesome server!\"", + "\tif {vanish::%player's uuid%} is set:", + "\t\tclear quit message", + "\telse:", + "\t\tset quit message to \"%player% left this awesome server!\"", "", "on death:", - " set the death message to \"%player% died!\""}) -@Since("1.4.6 (chat message), 1.4.9 (join & quit messages), 2.0 (death message)") + "\tset the death message to \"%player% died!\""}) +@Since("1.4.6 (chat message), 1.4.9 (join & quit messages), 2.0 (death message), INSERT VERSION (clear message)") @Events({"chat", "join", "quit", "death"}) public class ExprMessage extends SimpleExpression { @@ -181,18 +185,18 @@ protected String[] get(final Event e) { @Override @Nullable public Class[] acceptChange(final ChangeMode mode) { - if (mode == ChangeMode.SET) + if (mode == ChangeMode.SET || mode == ChangeMode.DELETE) return CollectionUtils.array(String.class); return null; } @Override public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) { - assert mode == ChangeMode.SET; - assert delta != null; + assert mode == ChangeMode.SET || mode == ChangeMode.DELETE; for (final Class c : type.events) { - if (c.isInstance(e)) - type.set(e, "" + delta[0]); + if (c.isInstance(e)) { + type.set(e, (mode == ChangeMode.DELETE) ? "" : delta[0].toString()); + } } } diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/ExprDropsTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprDropsTest.java similarity index 94% rename from src/test/java/org/skriptlang/skript/test/tests/syntaxes/ExprDropsTest.java rename to src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprDropsTest.java index f9216d5352c..f9f9c372176 100644 --- a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/ExprDropsTest.java +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprDropsTest.java @@ -16,7 +16,7 @@ * * Copyright Peter Güttinger, SkriptLang team and contributors */ -package org.skriptlang.skript.test.tests.syntaxes; +package org.skriptlang.skript.test.tests.syntaxes.expressions; import ch.njol.skript.test.runner.SkriptJUnitTest; import org.bukkit.entity.Pig; diff --git a/src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprMessageTest.java b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprMessageTest.java new file mode 100644 index 00000000000..1b0cbefa703 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/test/tests/syntaxes/expressions/ExprMessageTest.java @@ -0,0 +1,53 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package org.skriptlang.skript.test.tests.syntaxes.expressions; + +import ch.njol.skript.test.runner.SkriptJUnitTest; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.PluginManager; +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +public class ExprMessageTest extends SkriptJUnitTest { + + private Player testPlayer; + + @Before + public void setup() { + testPlayer = EasyMock.niceMock(Player.class); + } + + @Test + public void test() { + Set viewers = new HashSet<>(); + viewers.add(testPlayer); + PluginManager manager = Bukkit.getServer().getPluginManager(); + manager.callEvent(new AsyncPlayerChatEvent(false, testPlayer, "hi", viewers)); + manager.callEvent(new PlayerJoinEvent(testPlayer, "hi")); + manager.callEvent(new PlayerQuitEvent(testPlayer, "hi")); + } +} diff --git a/src/test/skript/junit/ExprDrops.sk b/src/test/skript/junit/ExprDrops.sk index 3df908f9f03..c13fe31671f 100644 --- a/src/test/skript/junit/ExprDrops.sk +++ b/src/test/skript/junit/ExprDrops.sk @@ -12,13 +12,13 @@ test "ExprDropsJUnit" when running JUnit: set {_tests::11} to "add and remove experience from drops doesn't modify items" set {_tests::12} to "remove all experience from drops doesn't modify items" set {_tests::13} to "drops test complete" - ensure junit test "org.skriptlang.skript.test.tests.syntaxes.ExprDropsTest" completes {_tests::*} + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.expressions.ExprDropsTest" completes {_tests::*} # NOTE: Do NOT take the behavior described in this test as a guide for how ExprDrops SHOULD work, only for how it DOES work in 2.7.x. # The behavior should change in 2.8 and this test will be updated accordingly. on death of pig: - set {_test} to "org.skriptlang.skript.test.tests.syntaxes.ExprDropsTest" + set {_test} to "org.skriptlang.skript.test.tests.syntaxes.expressions.ExprDropsTest" junit test is {_test} # Items diff --git a/src/test/skript/junit/ExprMessage.sk b/src/test/skript/junit/ExprMessage.sk new file mode 100644 index 00000000000..656e4d88109 --- /dev/null +++ b/src/test/skript/junit/ExprMessage.sk @@ -0,0 +1,44 @@ +test "ExprMessageJUnit" when running JUnit: + set {_tests::1} to "set chat message" + set {_tests::2} to "clear chat message" + set {_tests::3} to "set join message" + set {_tests::4} to "clear join message" + set {_tests::5} to "set quit message" + set {_tests::6} to "clear quit message" + ensure junit test "org.skriptlang.skript.test.tests.syntaxes.expressions.ExprMessageTest" completes {_tests::*} + +on chat: + set {_test} to "org.skriptlang.skript.test.tests.syntaxes.expressions.ExprMessageTest" + junit test is {_test} + + set chat message to "hello" + if chat message is "hello": + complete objective "set chat message" for {_test} + + clear message + if message is "": + complete objective "clear chat message" for {_test} + +on join: + set {_test} to "org.skriptlang.skript.test.tests.syntaxes.expressions.ExprMessageTest" + junit test is {_test} + + set login message to "I joined" + if log in message is "I joined": + complete objective "set join message" for {_test} + + clear join message + if join message is "": + complete objective "clear join message" for {_test} + +on quit: + set {_test} to "org.skriptlang.skript.test.tests.syntaxes.expressions.ExprMessageTest" + junit test is {_test} + + set logout message to "I left" + if log out message is "I left": + complete objective "set quit message" for {_test} + + clear quit message + if leave message is "": + complete objective "clear quit message" for {_test} \ No newline at end of file From 6ee4628d324e4f499319b40c7fd493d912d7f538 Mon Sep 17 00:00:00 2001 From: _Mads <75088349+TFSMads@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:08:59 +0200 Subject: [PATCH 26/48] Added holder instanceof DoubleChest check to converter (#6823) * Added holder instanceof DoubleChest check to IntentoryHolder - Location converter * Apply suggestions from code review Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Change ternary to if statements * Fix indentation in test * Address reviews Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> * Update src/main/java/ch/njol/skript/classes/data/DefaultConverters.java Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Pikachu920 <28607612+Pikachu920@users.noreply.github.com> Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --- .../classes/data/DefaultConverters.java | 8 ++++++++ ...1-inventory-holder-location-doublechest.sk | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/test/skript/tests/regressions/6811-inventory-holder-location-doublechest.sk diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java index 00f419f4901..a90de6957d3 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultConverters.java @@ -176,6 +176,14 @@ public DefaultConverters() {} return ((Block) holder).getLocation(); if (holder instanceof BlockState) return BlockUtils.getLocation(((BlockState) holder).getBlock()); + if (holder instanceof DoubleChest) { + DoubleChest doubleChest = (DoubleChest) holder; + if (doubleChest.getLeftSide() != null) { + return BlockUtils.getLocation(((BlockState) doubleChest.getLeftSide()).getBlock()); + } else if (((DoubleChest) holder).getRightSide() != null) { + return BlockUtils.getLocation(((BlockState) doubleChest.getRightSide()).getBlock()); + } + } return null; }); diff --git a/src/test/skript/tests/regressions/6811-inventory-holder-location-doublechest.sk b/src/test/skript/tests/regressions/6811-inventory-holder-location-doublechest.sk new file mode 100644 index 00000000000..f46d4b52794 --- /dev/null +++ b/src/test/skript/tests/regressions/6811-inventory-holder-location-doublechest.sk @@ -0,0 +1,20 @@ +test "inventory holder location double chest": + set {_b::1} to the block at spawn of world "world" + set {_b::2} to the block north of {_b::1} + set {_prev::1} to type of block at {_b::1} + set {_prev::2} to type of block at {_b::2} + + set block at {_b::1} to chest[facing=east;type=right] + set block at {_b::2} to chest[facing=east;type=left] + + set {_inv} to inventory of {_b::1} + set {_holder} to holder of {_inv} + + set {_a-loc} to location of {_holder} + set {_b-loc::*} to location of {_b::1} and location of {_b::2} + + # clean up first in case assert fails + set block at {_b::1} to {_prev::1} + set block at {_b::2} to {_prev::2} + + assert {_b-loc::*} contains {_a-loc} with "holder location of double chest differs from block location" From f6f41ca2ab9a9e7bdd381b111aa8929fb2443be6 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 06:10:17 -0700 Subject: [PATCH 27/48] Registry - more registries (#6806) * Registry - more registries * BukkitClasses - make registryExists method public * BukkitUtils - create a bukkit utils class --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../njol/skript/bukkitutil/BukkitUtils.java | 21 +++++++++ .../skript/classes/data/BukkitClasses.java | 19 ++++++-- src/main/resources/lang/default.lang | 43 +++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java diff --git a/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java b/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java new file mode 100644 index 00000000000..df551c268f5 --- /dev/null +++ b/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java @@ -0,0 +1,21 @@ +package ch.njol.skript.bukkitutil; + +import ch.njol.skript.Skript; +import org.bukkit.Registry; + +/** + * Utility class with methods pertaining to Bukkit API + */ +public class BukkitUtils { + + /** + * Check if a registry exists + * + * @param registry Registry to check for (Fully qualified name of registry) + * @return True if registry exists else false + */ + public static boolean registryExists(String registry) { + return Skript.classExists("org.bukkit.Registry") && Skript.fieldExists(Registry.class, registry); + } + +} diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 69710a1ad00..530c3df07a5 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import ch.njol.skript.bukkitutil.BukkitUtils; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Difficulty; @@ -980,7 +981,7 @@ public String toVariableNameString(final ItemStack i) { .changer(DefaultChangers.itemChanger)); ClassInfo biomeClassInfo; - if (Skript.classExists("org.bukkit.Registry") && Skript.fieldExists(Registry.class, "BIOME")) { + if (BukkitUtils.registryExists("BIOME")) { biomeClassInfo = new RegistryClassInfo<>(Biome.class, Registry.BIOME, "biome", "biomes"); } else { biomeClassInfo = new EnumClassInfo<>(Biome.class, "biome", "biomes"); @@ -1453,7 +1454,13 @@ public String toVariableNameString(FireworkEffect effect) { .examples("") .since("2.5")); if (Skript.classExists("org.bukkit.entity.Cat$Type")) { - Classes.registerClass(new EnumClassInfo<>(Cat.Type.class, "cattype", "cat types") + ClassInfo catTypeClassInfo; + if (BukkitUtils.registryExists("CAT_VARIANT")) { + catTypeClassInfo = new RegistryClassInfo<>(Cat.Type.class, Registry.CAT_VARIANT, "cattype", "cat types"); + } else { + catTypeClassInfo = new EnumClassInfo<>(Cat.Type.class, "cattype", "cat types"); + } + Classes.registerClass(catTypeClassInfo .user("cat ?(type|race)s?") .name("Cat Type") .description("Represents the race/type of a cat entity.") @@ -1514,7 +1521,13 @@ public String toVariableNameString(EnchantmentOffer eo) { } })); - Classes.registerClass(new EnumClassInfo<>(Attribute.class, "attributetype", "attribute types") + ClassInfo attributeClassInfo; + if (BukkitUtils.registryExists("ATTRIBUTE")) { + attributeClassInfo = new RegistryClassInfo<>(Attribute.class, Registry.ATTRIBUTE, "attributetype", "attribute types"); + } else { + attributeClassInfo = new EnumClassInfo<>(Attribute.class, "attributetype", "attribute types"); + } + Classes.registerClass(attributeClassInfo .user("attribute ?types?") .name("Attribute Type") .description("Represents the type of an attribute. Note that this type does not contain any numerical values." diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 701cd9d07b9..38d8cc68f1f 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -2164,11 +2164,14 @@ genes: # -- Attribute Types -- attribute types: + # Enum variation generic_armor: generic armor, armor generic_armor_toughness: generic armor toughness, armor toughness generic_attack_damage: generic attack damage, attack damage generic_attack_knockback: generic attack knockback, attack knockback generic_attack_speed: generic attack speed, attack speed + generic_burning_time: generic burning time, burning time + generic_explosion_knockback_resistance: generic explosion knockback resistance, explosion knockback resistance generic_flying_speed: generic flying speed, flying speed generic_follow_range: generic follow range, follow range generic_gravity: generic gravity, gravity @@ -2177,16 +2180,56 @@ attribute types: generic_luck: generic luck, luck generic_max_absorption: generic max absorption, max absorption generic_max_health: generic max health, max health + generic_movement_efficiency: generic movement efficiency, movement efficiency generic_movement_speed: generic movement speed, movement speed + generic_oxygen_bonus: generic oxygen bonus, oxygen bonus generic_safe_fall_distance: generic safe fall distance, safe fall distance generic_fall_damage_multiplier: generic fall damage multiplier, fall damage multiplier generic_scale: generic scale, scale generic_step_height: generic step height, step height + generic_water_movement_efficiency: generic water movement efficiency, water movement efficiency horse_jump_strength: horse jump strength player_block_break_speed: player block break speed, block break speed player_block_interaction_range: player block interaction range, block interaction range player_entity_interaction_range: player entity interaction range, entity interaction range + player_mining_efficiency: player mining efficiency, mining efficiency + player_sneaking_speed: player sneaking speed, sneaking speed + player_submerged_mining_speed: player submerged mining speed, submerged mining speed + player_sweeping_damage_ratio: player sweeping damage ratio, sweeping damage ratio zombie_spawn_reinforcements: zombie spawn reinforcements + # Registry variation (minecraft keys) + generic.armor: generic armor, armor + generic.armor_toughness: generic armor toughness, armor toughness + generic.attack_damage: generic attack damage, attack damage + generic.attack_knockback: generic attack knockback, attack knockback + generic.attack_speed: generic attack speed, attack speed + generic.burning_time: generic burning time, burning time + generic.explosion_knockback_resistance: generic explosion knockback resistance, explosion knockback resistance + generic.flying_speed: generic flying speed, flying speed + generic.follow_range: generic follow range, follow range + generic.gravity: generic gravity, gravity + generic.jump_strength: generic jump strength, jump strength + generic.knockback_resistance: generic knockback resistance, knockback resistance + generic.luck: generic luck, luck + generic.max_absorption: generic max absorption, max absorption + generic.max_health: generic max health, max health + generic.movement_efficiency: generic movement efficiency, movement efficiency + generic.movement_speed: generic movement speed, movement speed + generic.oxygen_bonus: generic oxygen bonus, oxygen bonus + generic.safe_fall_distance: generic safe fall distance, safe fall distance + generic.fall_damage_multiplier: generic fall damage multiplier, fall damage multiplier + generic.scale: generic scale, scale + generic.step_height: generic step height, step height + generic.water_movement_efficiency: generic water movement efficiency, water movement efficiency + horse_jump_strength: horse jump strength + player.block_break_speed: player block break speed, block break speed + player.block_interaction_range: player block interaction range, block interaction range + player.entity_interaction_range: player entity interaction range, entity interaction range + player.mining_efficiency: player mining efficiency, mining efficiency + player.sneaking_speed: player sneaking speed, sneaking speed + player.submerged_mining_speed: player submerged mining speed, submerged mining speed + player.sweeping_damage_ratio: player sweeping damage ratio, sweeping damage ratio + zombie.spawn_reinforcements: zombie spawn reinforcements # -- Environments -- environments: From 7d84902264624572c93736f431ca067c433210f1 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 06:15:46 -0700 Subject: [PATCH 28/48] Fix/registry pr whoops (#6805) RegistrySerializer - big whoops Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../ch/njol/skript/classes/registry/RegistrySerializer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java b/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java index ddec6c3225f..2c0796e4218 100644 --- a/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java +++ b/src/main/java/ch/njol/skript/classes/registry/RegistrySerializer.java @@ -23,6 +23,7 @@ import org.bukkit.Keyed; import org.bukkit.NamespacedKey; import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; import java.io.StreamCorruptedException; @@ -40,10 +41,10 @@ public RegistrySerializer(Registry registry) { } @Override - public Fields serialize(R o) { + public @NotNull Fields serialize(R o) { Fields fields = new Fields(); fields.putPrimitive("name", o.getKey().toString()); - return null; + return fields; } @Override From 1d587d4acb0ea04498b328030b84c5fdc217dad2 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Mon, 1 Jul 2024 14:25:37 +0100 Subject: [PATCH 29/48] Improve new line pattern. (#6733) * Make new line literal not silly. * Clean init header. --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/expressions/LitNewLine.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/LitNewLine.java b/src/main/java/ch/njol/skript/expressions/LitNewLine.java index d6ca312fb6d..fbe845946a3 100644 --- a/src/main/java/ch/njol/skript/expressions/LitNewLine.java +++ b/src/main/java/ch/njol/skript/expressions/LitNewLine.java @@ -19,7 +19,6 @@ package ch.njol.skript.expressions; import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; @@ -28,9 +27,10 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; -import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.util.Kleenean; +import org.jetbrains.annotations.Nullable; @Name("New Line") @Description("Returns a line break separator.") @@ -39,7 +39,7 @@ public class LitNewLine extends SimpleLiteral { static { - Skript.registerExpression(LitNewLine.class, String.class, ExpressionType.SIMPLE, "n[ew]l[ine]", "line[ ]break"); + Skript.registerExpression(LitNewLine.class, String.class, ExpressionType.SIMPLE, "nl", "new[ ]line", "line[ ]break"); } public LitNewLine() { @@ -47,12 +47,12 @@ public LitNewLine() { } @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final SkriptParser.ParseResult parseResult) { + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult result) { return true; } @Override - public String toString(@Nullable Event e, boolean debug) { - return "newline"; + public String toString(@Nullable Event event, boolean debug) { + return "new line"; } } From 333b5fb4641c9943a6adbbd21fb366d79c4e2022 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Mon, 1 Jul 2024 14:35:33 +0100 Subject: [PATCH 30/48] Add tests for item syntaxes. (#6164) * Add 'with custom model data' test. * Add 'amount of items' test. * Add 'items in' test. * Make safe for 1.13.2. * Update src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../syntaxes/expressions/ExprAmountOfItems.sk | 14 ++++++++++ .../ExprItemWithCustomModelData.sk | 13 +++++++++ .../tests/syntaxes/expressions/ExprItemsIn.sk | 27 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprAmountOfItems.sk create mode 100644 src/test/skript/tests/syntaxes/expressions/ExprItemWithCustomModelData.sk diff --git a/src/test/skript/tests/syntaxes/expressions/ExprAmountOfItems.sk b/src/test/skript/tests/syntaxes/expressions/ExprAmountOfItems.sk new file mode 100644 index 00000000000..9e40ff707ca --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprAmountOfItems.sk @@ -0,0 +1,14 @@ +test "amount of items": + set {_inventory} to a hopper inventory named "test" + assert the amount of stone in {_inventory} is 0 with "default amount failed" + add stone to {_inventory} + assert the amount of stone in {_inventory} is 1 with "single amount failed" + add stone named "bread" to {_inventory} + assert the amount of stone in {_inventory} is 2 with "different named items amount failed" + add 100 of iron ingot to {_inventory} + assert the amount of stone in {_inventory} is 2 with "add different item amount failed" + assert the amount of iron ingot in {_inventory} is 100 with "add 100 item amount failed" + remove stone from {_inventory} + assert the amount of stone in {_inventory} is 1 with "removed one amount failed" + remove stone from {_inventory} + assert the amount of stone in {_inventory} is 0 with "removed all amount failed" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprItemWithCustomModelData.sk b/src/test/skript/tests/syntaxes/expressions/ExprItemWithCustomModelData.sk new file mode 100644 index 00000000000..098cda9abee --- /dev/null +++ b/src/test/skript/tests/syntaxes/expressions/ExprItemWithCustomModelData.sk @@ -0,0 +1,13 @@ +test "item with custom model data" when minecraft version is not "1.13.2": + set {_i} to stone + assert the custom model data of {_i} is 0 with "default model data failed" + set {_i} to stone with custom model data 5 + assert the custom model data of {_i} is 5 with "simple model data set failed" + set {_i} to stone with custom model data -1 + assert the custom model data of {_i} is -1 with "negative model data set failed" + set {_i} to {_i} with custom model data 2 + assert the custom model data of {_i} is 2 with "existing item model data set failed" + set {_i} to {_i} with custom model data 3.3 + assert the custom model data of {_i} is 3 with "decimal item model data set failed" + set {_i} to {_i} with custom model data 3.999 + assert the custom model data of {_i} is 3 with "close decimal item model data set failed" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk b/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk index 9b2c90fb25f..a18bef586af 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk @@ -1,3 +1,29 @@ + +test "items in (inventory)": + set {_inventory} to a hopper inventory named "test" + add stone to {_inventory} + add stone named "bread" to {_inventory} + add 100 of iron ingot to {_inventory} + loop items in {_inventory}: + if loop-value is stone: + continue + else if loop-value is iron ingot: + continue + else: + assert true is false with "unexpected item in the inventory area: %loop-value%" + set {_list::*} to items in {_inventory} + assert size of {_list::*} is 4 with "size of items in failed" + assert {_list::1} is stone with "first item failed" + assert {_list::2} is stone named "bread" with "second item failed" + assert {_list::3} is 64 of iron ingot with "third item failed" + assert {_list::4} is 36 of iron ingot with "split fourth item failed" + remove stone from {_inventory} + set {_list::*} to items in {_inventory} + assert size of {_list::*} is 3 with "size of second items in failed" + assert {_list::1} is stone named "bread" with "new first item failed" + assert {_list::2} is 64 of iron ingot with "new second item failed" + assert {_list::3} is 36 of iron ingot with "new third item failed" + test "filtering ExprItemsIn": set {_world} to random world out of all worlds set block at spawn of {_world} to chest @@ -19,3 +45,4 @@ test "unfiltered ExprItemsIn": set slot 3 of {_inv} to bucket assert all items in inventory {_inv} are dirt, stone or bucket with "found correct items with ExprItemsIn##get" assert (all items in inventory {_inv} where [true is true]) are dirt, stone or bucket with "found correct items with ExprItemsIn##iterator" + From 1a42f52c641116d966013cdef6295d10212e92f7 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:46:30 +0200 Subject: [PATCH 31/48] Add config toggle for whether to listen to cancelled events by default. (#6680) * Add config toggle * Update src/main/resources/config.sk Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --------- Co-authored-by: _tud <98935832+UnderscoreTud@users.noreply.github.com> --- src/main/java/ch/njol/skript/SkriptConfig.java | 6 ++++++ src/main/java/ch/njol/skript/lang/SkriptEventInfo.java | 5 +++-- src/main/resources/config.sk | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/njol/skript/SkriptConfig.java b/src/main/java/ch/njol/skript/SkriptConfig.java index 5a3bb954cd4..ee715ce038e 100644 --- a/src/main/java/ch/njol/skript/SkriptConfig.java +++ b/src/main/java/ch/njol/skript/SkriptConfig.java @@ -170,6 +170,12 @@ public static String formatDate(final long timestamp) { } }); + /** + * Determines whether `on <event>` will be triggered by cancelled events or not. + */ + public static final Option listenCancelledByDefault = new Option<>("listen to cancelled events by default", false) + .optional(true); + /** * Maximum number of digits to display after the period for floats and doubles diff --git a/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java b/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java index 610b0671529..b645f7675b1 100644 --- a/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java +++ b/src/main/java/ch/njol/skript/lang/SkriptEventInfo.java @@ -19,6 +19,7 @@ package ch.njol.skript.lang; import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.SkriptConfig; import org.bukkit.event.Event; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import org.eclipse.jdt.annotation.Nullable; @@ -74,8 +75,8 @@ public SkriptEventInfo(String name, String[] patterns, Class eventClass, Stri // uses the name without 'on ' or '*' this.id = "" + name.toLowerCase(Locale.ENGLISH).replaceAll("[#'\"<>/&]", "").replaceAll("\\s+", "_"); - // default listening behavior should be to listen to uncancelled events - this.listeningBehavior = ListeningBehavior.UNCANCELLED; + // default listening behavior should be dependent on config setting + this.listeningBehavior = SkriptConfig.listenCancelledByDefault.value() ? ListeningBehavior.ANY : ListeningBehavior.UNCANCELLED; } /** diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 68d9bfd926f..85784c06a92 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -122,6 +122,9 @@ plugin priority: high # - effects 'remove ... from drops'/'clear drops': Drops added by other plugins are not removed => increase priority # Skript removes drops it shouldn't => decrease priority or specify which item types to remove +listen to cancelled events by default: false +# Determines whether `on ` will be triggered by all events (true) or only uncancelled events (false). +# The default is false, which maintains traditional Skript behavior. number accuracy: 2 # How many digits should be displayed after the dot at maximum when displaying numbers. From e8c8b5ff28e3d86fa08a00388bb6267d4efbe181 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Mon, 1 Jul 2024 16:13:54 +0200 Subject: [PATCH 32/48] Change comment parsing (#6583) * Rework to only allow single # in quoted strings * fix save() * fix state machine bug with exiting %s * Update skript-aliases * remove unreachable code --------- Co-authored-by: Moderocky --- src/main/java/ch/njol/skript/config/Node.java | 157 +++++++++++++++--- .../expressions/ExprChestInventory.java | 2 +- .../skript/expressions/ExprLoopIteration.java | 2 +- .../skript/expressions/ExprLoopValue.java | 2 +- src/main/java/ch/njol/skript/util/Utils.java | 2 +- src/main/resources/config.sk | 2 +- .../scripts/-examples/text formatting.sk | 4 +- .../skript/test/tests/config/NodeTest.java | 1 + src/test/skript/tests/misc/comments.sk | 46 +++++ ...loating point errors rounding functions.sk | 12 +- .../tests/regressions/4664-formatted time.sk | 10 +- .../regressions/4769-fire-visualeffect.sk | 4 +- .../535-math is done in the wrong order.sk | 14 +- ...required in some places it shouldn't be.sk | 4 +- ...837-getting a uuid of a var inside code.sk | 4 +- .../tests/syntaxes/conditions/CondContains.sk | 40 ++--- .../tests/syntaxes/conditions/CondIsWithin.sk | 36 ++-- .../syntaxes/conditions/CondMethodExists.sk | 18 +- .../tests/syntaxes/effects/EffContinue.sk | 6 +- .../tests/syntaxes/effects/EffHealth.sk | 10 +- .../syntaxes/expressions/ExprArrowsStuck.sk | 10 +- .../expressions/ExprEntityAttribute.sk | 4 +- .../syntaxes/expressions/ExprFreezeTicks.sk | 8 +- .../tests/syntaxes/expressions/ExprItemsIn.sk | 8 +- .../syntaxes/expressions/ExprNoDamageTicks.sk | 10 +- .../expressions/ExprPortalCooldown.sk | 6 +- .../tests/syntaxes/functions/caseEquals.sk | 10 +- .../tests/syntaxes/sections/SecConditional.sk | 10 +- 28 files changed, 298 insertions(+), 144 deletions(-) create mode 100644 src/test/skript/tests/misc/comments.sk diff --git a/src/main/java/ch/njol/skript/config/Node.java b/src/main/java/ch/njol/skript/config/Node.java index f59c7210f5c..cff13bda618 100644 --- a/src/main/java/ch/njol/skript/config/Node.java +++ b/src/main/java/ch/njol/skript/config/Node.java @@ -23,6 +23,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import ch.njol.skript.SkriptConfig; import org.eclipse.jdt.annotation.Nullable; import ch.njol.skript.Skript; @@ -114,17 +115,27 @@ public void move(final SectionNode newParent) { p.remove(this); newParent.add(this); } - - @SuppressWarnings("null") - private final static Pattern linePattern = Pattern.compile("^((?:[^#]|##)*)(\\s*#(?!#).*)$"); - + /** * Splits a line into value and comment. *

* Whitespace is preserved (whitespace in front of the comment is added to the value), and any ## in the value are replaced by a single #. The comment is returned with a * leading #, except if there is no comment in which case it will be the empty string. + * + * @param line the line to split + * @return A pair (value, comment). + */ + public static NonNullPair splitLine(String line) { + return splitLine(line, new AtomicBoolean(false)); + } + + /** + * Splits a line into value and comment. + *

+ * Whitespace is preserved (whitespace in front of the comment is added to the value), and any ## not in quoted strings in the value are replaced by a single #. The comment is returned with a + * leading #, except if there is no comment in which case it will be the empty string. * - * @param line + * @param line the line to split * @param inBlockComment Whether we are currently inside a block comment * @return A pair (value, comment). */ @@ -138,34 +149,99 @@ public static NonNullPair splitLine(String line, AtomicBoolean i } else if (inBlockComment.get()) { // we're inside a comment, all text is a comment return new NonNullPair<>("", line); } - final Matcher m = linePattern.matcher(line); - boolean matches = false; - try { - matches = line.contains("#") && m.matches(); - } catch (StackOverflowError e) { // Probably a very long line - handleNodeStackOverflow(e, line); + + // idea: find first # that is not within a string or variable name. Use state machine to determine whether a # is a comment or not. + int length = line.length(); + StringBuilder finalLine = new StringBuilder(line); + int numRemoved = 0; + SplitLineState state = SplitLineState.CODE; + SplitLineState previousState = SplitLineState.CODE; // stores the state prior to entering %, so it can be re-set when leaving + // find next " or % + for (int i = 0; i < length; i++) { + char c = line.charAt(i); + // check for things that can be escaped by doubling + if (c == '%' || c == '"' || c == '#') { + // skip if doubled (only skip ## outside of strings) + if ((c != '#' || state != SplitLineState.STRING) && i + 1 < length && line.charAt(i + 1) == c) { + if (c == '#') { // remove duplicate # + finalLine.deleteCharAt(i - numRemoved); + numRemoved++; + } + i++; + continue; + } + SplitLineState tmp = state; + state = SplitLineState.update(c, state, previousState); + if (state == SplitLineState.HALT) + return new NonNullPair<>(finalLine.substring(0, i - numRemoved), line.substring(i)); + // only update previous state when we go from !CODE -> CODE due to % + if (c == '%' && state == SplitLineState.CODE) + previousState = tmp; + } } - if (matches) - return new NonNullPair<>("" + m.group(1).replace("##", "#"), "" + m.group(2)); - return new NonNullPair<>("" + line.replace("##", "#"), ""); + return new NonNullPair<>(finalLine.toString(), ""); } /** - * Splits a line into value and comment. - *

- * Whitespace is preserved (whitespace in front of the comment is added to the value), and any ## in the value are replaced by a single #. The comment is returned with a - * leading #, except if there is no comment in which case it will be the empty string. - * - * @param line - * @return A pair (value, comment). + * state machine:
+ * ": CODE -> STRING, STRING -> CODE, VARIABLE -> VARIABLE
+ * %: CODE -> PREVIOUS_STATE, STRING -> CODE, VARIABLE -> CODE
+ * {: CODE -> VARIABLE, STRING -> STRING, VARIABLE -> VARIABLE
+ * }: CODE -> CODE, STRING -> STRING, VARIABLE -> CODE
+ * #: CODE -> HALT, STRING -> STRING, VARIABLE -> HALT
+ * invalid characters simply return given state.
*/ - public static NonNullPair splitLine(String line) { - return splitLine(line, new AtomicBoolean(false)); + private enum SplitLineState { + HALT, + CODE, + STRING, + VARIABLE; + + /** + * Updates the state given a character input. + * @param c character input. '"', '%', '{', '}', and '#' are valid. + * @param state the current state of the machine + * @param previousState the state of the machine when it last entered a % CODE % section + * @return the new state of the machine + */ + private static SplitLineState update(char c, SplitLineState state, SplitLineState previousState) { + if (state == HALT) + return HALT; + + switch (c) { + case '%': + if (state == CODE) + return previousState; + return CODE; + case '"': + switch (state) { + case CODE: + return STRING; + case STRING: + return CODE; + default: + return state; + } + case '{': + if (state == STRING) + return STRING; + return VARIABLE; + case '}': + if (state == STRING) + return STRING; + return CODE; + case '#': + if (state == STRING) + return STRING; + return HALT; + } + return state; + } } static void handleNodeStackOverflow(StackOverflowError e, String line) { Node n = SkriptLogger.getNode(); - SkriptLogger.setNode(null); // Avoid duplicating the which node error occurred in paranthesis on every error message + SkriptLogger.setNode(null); // Avoid duplicating the which node error occurred in parentheses on every error message Skript.error("There was a StackOverFlowError occurred when loading a node. This maybe from your scripts, aliases or Skript configuration."); Skript.error("Please make your script lines shorter! Do NOT report this to SkriptLang unless it occurs with a short script line or built-in aliases!"); @@ -214,12 +290,43 @@ protected String getIndentation() { abstract String save_i(); public final String save() { - return getIndentation() + save_i().replace("#", "##") + comment; + return getIndentation() + escapeUnquotedHashtags(save_i()) + comment; } public void save(final PrintWriter w) { w.println(save()); } + + private static String escapeUnquotedHashtags(String input) { + int length = input.length(); + StringBuilder output = new StringBuilder(input); + int numAdded = 0; + SplitLineState state = SplitLineState.CODE; + SplitLineState previousState = SplitLineState.CODE; + // find next " or % + for (int i = 0; i < length; i++) { + char c = input.charAt(i); + // check for things that can be escaped by doubling + if (c == '%' || c == '"' || c == '#') { + // escaped #s outside of strings + if (c == '#' && state != SplitLineState.STRING) { + output.insert(i + numAdded, "#"); + numAdded++; + continue; + } + // skip if doubled (not #s) + if (i + 1 < length && input.charAt(i + 1) == c) { + i++; + continue; + } + SplitLineState tmp = state; + state = SplitLineState.update(c, state, previousState); + previousState = tmp; + } + } + return output.toString(); + } + @Nullable public SectionNode getParent() { diff --git a/src/main/java/ch/njol/skript/expressions/ExprChestInventory.java b/src/main/java/ch/njol/skript/expressions/ExprChestInventory.java index 0ba4548c184..a01ac6b9dcc 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprChestInventory.java +++ b/src/main/java/ch/njol/skript/expressions/ExprChestInventory.java @@ -52,7 +52,7 @@ "set slot 4 of {_inventory} to a diamond named \"example\"", "open {_inventory} to player", "", - "open chest inventory named \"<##00ff00>hex coloured title!\" with 6 rows to player", + "open chest inventory named \"<#00ff00>hex coloured title!\" with 6 rows to player", }) @RequiredPlugins("Paper 1.16+ (chat format)") @Since("2.2-dev34, 2.8.0 (chat format)") diff --git a/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java b/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java index 37640b26db7..12fb9b0a782 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLoopIteration.java @@ -45,7 +45,7 @@ "", "loop {top-balances::*}:", "\tif loop-iteration <= 10:", - "\t\tbroadcast \"##%loop-iteration% %loop-index% has $%loop-value%\"", + "\t\tbroadcast \"#%loop-iteration% %loop-index% has $%loop-value%\"", }) @Since("2.8.0") public class ExprLoopIteration extends SimpleExpression { diff --git a/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java b/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java index 343cc9461c3..10142cb0268 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java +++ b/src/main/java/ch/njol/skript/expressions/ExprLoopValue.java @@ -62,7 +62,7 @@ "", "loop {top-balances::*}:", "\tloop-iteration <= 10", - "\tsend \"##%loop-iteration% %loop-index% has $%loop-value%\"", + "\tsend \"#%loop-iteration% %loop-index% has $%loop-value%\"", }) @Since("1.0, 2.8.0 (loop-counter)") public class ExprLoopValue extends SimpleExpression { diff --git a/src/main/java/ch/njol/skript/util/Utils.java b/src/main/java/ch/njol/skript/util/Utils.java index 862e440ceb8..879e1aabe74 100644 --- a/src/main/java/ch/njol/skript/util/Utils.java +++ b/src/main/java/ch/njol/skript/util/Utils.java @@ -641,7 +641,7 @@ public String run(final Matcher m) { return "" + m; } - private static final Pattern HEX_PATTERN = Pattern.compile("(?i)#?[0-9a-f]{6}"); + private static final Pattern HEX_PATTERN = Pattern.compile("(?i)#{0,2}[0-9a-f]{6}"); /** * Tries to get a {@link ChatColor} from the given string. diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 85784c06a92..4ba081e7c33 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -22,7 +22,7 @@ # This file, all scripts and other files ending in .sk are NOT .yml/YAML files, but very similar! # Please remember the following when editing files: # - To indent sections you can use spaces like in YAML, but tabs are also allowed. Just remember to stick to the one or the other for a section/trigger. -# - '#' starts a comment like in YAML. If you don't want it to start a comment simply double it: '##' (You also have to double these in "quoted text") +# - '#' starts a comment like in YAML. If you don't want it to start a comment simply double it: '##' (You do NOT have to double these in "quoted text") # - If you use special characters (§, äöü, éèàôç, ñ, etc.) you have to encode the file in UTF-8. # diff --git a/src/main/resources/scripts/-examples/text formatting.sk b/src/main/resources/scripts/-examples/text formatting.sk index 8a5a1ab277a..5163ca116c3 100644 --- a/src/main/resources/scripts/-examples/text formatting.sk +++ b/src/main/resources/scripts/-examples/text formatting.sk @@ -4,7 +4,7 @@ # You can also use <> for colours and formats, like `` for red and `` for bold # # In Minecraft 1.16, support was added for 6-digit hexadecimal colors to specify custom colors other than the 16 default color codes. -# The tag for these colors looks like this: <##hex code> e.g. `<##123456>` +# The tag for these colors looks like this: <#hex code> e.g. `<#123456>` # command /color: @@ -12,7 +12,7 @@ command /color: trigger: send "&6This message is golden." send "This message is light red and bold." - send "<##FF0000>This message is red." + send "<#FF0000>This message is red." # # Other formatting options are also available. diff --git a/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java b/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java index c7e0eb3b29f..b0a7ff2b606 100644 --- a/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java +++ b/src/test/java/org/skriptlang/skript/test/tests/config/NodeTest.java @@ -47,6 +47,7 @@ public void splitLineTest() { {"#########", "", "#########"}, {"a##b#c##d#e", "a#b", "#c##d#e"}, {" a ## b # c ## d # e ", " a # b ", "# c ## d # e "}, + {"a b \"#a ##\" # b \"", "a b \"#a ##\" ", "# b \""}, }; for (String[] d : data) { NonNullPair p = Node.splitLine(d[0]); diff --git a/src/test/skript/tests/misc/comments.sk b/src/test/skript/tests/misc/comments.sk new file mode 100644 index 00000000000..348f7560d6d --- /dev/null +++ b/src/test/skript/tests/misc/comments.sk @@ -0,0 +1,46 @@ +test "comments": + parse: + set {_a} to {_b} # test + assert last parse logs is not set with "skript should be able to handle inline comments but did not" + + + parse: + assert "a" is "a" with "wrong number of hashtags" + assert "#a" is join "#", and "a" with "wrong number of hashtags" + assert "##a" is join "#", "#", and "a" with "wrong number of hashtags" + assert "###a" is join "#", "#", "#", and "a" with "wrong number of hashtags" + assert last parse logs is not set with "skript should be able to handle strings any number of hashtags but did not" + + + parse: + assert "a%"#"%" is join "a", and "#" with "wrong number of hashtags" + assert "#a%"#}"%" is join "#", "a", and "#}" with "wrong number of hashtags" + assert "##a%"#"%" is join "#", "#", "a", and "#" with "wrong number of hashtags" + assert "#{##a%"#"%" is join "#{", "#", "#", "a", and "#" with "wrong number of hashtags" + assert last parse logs is not set with "skript should be able to handle complex strings any number of hashtags but did not" + + + parse: + set {_a} to "<#abcdef>test" + set {_b} to "<##abcdef>test" + assert uncoloured {_a} is "test" with "failed to parse single hashtag colour code" + assert uncoloured {_b} is "test" with "failed to parse double hashtag colour code" + assert last parse logs is not set with "skript should be able to handle hex colour codes but did not" + + parse: + set {_a} to "###SH#JABJ#BJ#JB#K#BH#G#J##J#HJ%%KJB#JKK%%""%%""%%""%%#""##%%""#""%%##""#%""%##%"#"""%#"#!!""#""#L@$L@:@#L@K:L%@^$:"#^#:^J$%:K^J%&LK:#::&&^^^%%test + assert last parse logs is not set with "skript should be able to handle very messy string but did not" + + + parse: + set {_a##} to "test" + set {##_b} to "test" + set {##_b::%"{}"%} to "test" + set {##_b::%"{}#"%} to "#test" + assert {##_b::%"{}#"%} is join "#" and "test" with "failed state machine check" + assert last parse logs is not set with "skript should be able to handle hashtags in variable names but did not" + + + parse: + set {##_b::%"{}"#%} to "#test" + assert last parse logs is set with "skript should not be able to handle hashtags in an expression in a variable name but did" diff --git a/src/test/skript/tests/regressions/4235-floating point errors rounding functions.sk b/src/test/skript/tests/regressions/4235-floating point errors rounding functions.sk index ad8ecff83d5..4afe12bc824 100644 --- a/src/test/skript/tests/regressions/4235-floating point errors rounding functions.sk +++ b/src/test/skript/tests/regressions/4235-floating point errors rounding functions.sk @@ -1,12 +1,12 @@ test "floating point errors in rounding functions": - #assert ceil(100*0.07) is 7 with "ceil function doesn't adjust for floating point errors ##1" - #assert ceil(100*0.033 - 0.3) is 3 with "ceil function doesn't adjust for floating point errors ##2" + #assert ceil(100*0.07) is 7 with "ceil function doesn't adjust for floating point errors" + #assert ceil(100*0.033 - 0.3) is 3 with "ceil function doesn't adjust for floating point errors" - #assert rounded up 100*0.07 is 7 with "ceil expression doesn't adjust for floating point errors ##1" - #assert rounded up 100*0.033 - 0.3 is 3 with "ceil expression doesn't adjust for floating point errors ##2" + #assert rounded up 100*0.07 is 7 with "ceil expression doesn't adjust for floating point errors" + #assert rounded up 100*0.033 - 0.3 is 3 with "ceil expression doesn't adjust for floating point errors" set {_sum} to 0 loop 100 times: add 0.1 to {_sum} - assert floor({_sum}) is 10 with "floor function doesn't adjust for floating point errors ##1" - assert rounded down {_sum} is 10 with "floor expression doesn't adjust for floating point errors ##1" + assert floor({_sum}) is 10 with "floor function doesn't adjust for floating point errors" + assert rounded down {_sum} is 10 with "floor expression doesn't adjust for floating point errors" diff --git a/src/test/skript/tests/regressions/4664-formatted time.sk b/src/test/skript/tests/regressions/4664-formatted time.sk index 329c06bb9fa..a43029af91b 100644 --- a/src/test/skript/tests/regressions/4664-formatted time.sk +++ b/src/test/skript/tests/regressions/4664-formatted time.sk @@ -3,18 +3,18 @@ test "formatted time": set {_now} to now set {_date1} to {_now} formatted - assert {_date1} = {_now} formatted as {_default} with "default date format failed ##1" + assert {_date1} = {_now} formatted as {_default} with "default date format failed" set {_date2} to {_now} formatted human-readable - assert {_date2} = {_now} formatted as {_default} with "default date format failed ##2" + assert {_date2} = {_now} formatted as {_default} with "default date format failed" set {_date3} to {_now} formatted as "HH:mm" - assert length of {_date3} = 5 with "custom date format failed ##1" + assert length of {_date3} = 5 with "custom date format failed" set {_cFormat} to "hh:mm" set {_date4} to {_now} formatted as {_cFormat} - assert length of {_date4} = 5 with "custom date format failed ##2" + assert length of {_date4} = 5 with "custom date format failed" set {_cFormat2} to "i" set {_date5} to {_now} formatted as {_cFormat2} - assert {_date5} is not set with "custom date format failed ##3" + assert {_date5} is not set with "custom date format failed" diff --git a/src/test/skript/tests/regressions/4769-fire-visualeffect.sk b/src/test/skript/tests/regressions/4769-fire-visualeffect.sk index 3965e6c877f..c490fa9e9cb 100644 --- a/src/test/skript/tests/regressions/4769-fire-visualeffect.sk +++ b/src/test/skript/tests/regressions/4769-fire-visualeffect.sk @@ -8,8 +8,8 @@ test "fire visual effect comparison": set block at spawn of world "world" to {_block} play 5 fire at spawn of world "world" assert "fire" parsed as visual effect is fire with "failed to compare visual effects" - assert "fire" parsed as visual effect is "fire" parsed as itemtype to fail with "failed to compare visual effects ##2" + assert "fire" parsed as visual effect is "fire" parsed as itemtype to fail with "failed to compare visual effects" spawn a chicken at spawn of world "world": assert event-entity is a chicken with "failed to compare a chicken" - assert event-entity is a "chicken" parsed as itemtype to fail with "failed to compare a chicken ##2" + assert event-entity is a "chicken" parsed as itemtype to fail with "failed to compare a chicken" clear event-entity diff --git a/src/test/skript/tests/regressions/535-math is done in the wrong order.sk b/src/test/skript/tests/regressions/535-math is done in the wrong order.sk index 665ce0346f8..b404c89846d 100644 --- a/src/test/skript/tests/regressions/535-math is done in the wrong order.sk +++ b/src/test/skript/tests/regressions/535-math is done in the wrong order.sk @@ -1,10 +1,10 @@ test "math order": - assert 1 + 1 = 2 with "basic math ##1 failed" - assert 5 - 3 = 2 with "basic math ##2 failed" + assert 1 + 1 = 2 with "basic math failed" + assert 5 - 3 = 2 with "basic math failed" - assert 5 - 3 - 1 = 1 with "basic chained math ##1 failed" - assert 5-3-2 = 0 with "basic chained math ##2 failed" - assert 10 - 1 - 5 = 4 with "basic chained math ##3 failed" + assert 5 - 3 - 1 = 1 with "basic chained math failed" + assert 5-3-2 = 0 with "basic chained math failed" + assert 10 - 1 - 5 = 4 with "basic chained math failed" - assert (9 - 1) - 3 = 5 with "basic chained math with parentheses ##1 failed" - assert 9 - (1 - 3) = 11 with "basic chained math with parentheses ##2 failed" + assert (9 - 1) - 3 = 5 with "basic chained math with parentheses failed" + assert 9 - (1 - 3) = 11 with "basic chained math with parentheses failed" diff --git a/src/test/skript/tests/regressions/590-escaping quotes is required in some places it shouldn't be.sk b/src/test/skript/tests/regressions/590-escaping quotes is required in some places it shouldn't be.sk index cfa2aaa63e9..f0b63a43f23 100644 --- a/src/test/skript/tests/regressions/590-escaping quotes is required in some places it shouldn't be.sk +++ b/src/test/skript/tests/regressions/590-escaping quotes is required in some places it shouldn't be.sk @@ -8,8 +8,8 @@ test "double quote parsing": assert "Testing """ is set with "simple string with escaped quote failed" - assert "Testing %length of "abc"%" is set with "string with expression with string ##1 failed" - assert "%myFunction_five_nine_zero("Hello")% world" is "Hello world" with "string with expression with string ##2 failed" + assert "Testing %length of "abc"%" is set with "string with expression with string failed" + assert "%myFunction_five_nine_zero("Hello")% world" is "Hello world" with "string with expression with string failed" assert {_abc} is not set with "simple variable failed" diff --git a/src/test/skript/tests/regressions/837-getting a uuid of a var inside code.sk b/src/test/skript/tests/regressions/837-getting a uuid of a var inside code.sk index 1da43a1f75d..e05e22b9639 100644 --- a/src/test/skript/tests/regressions/837-getting a uuid of a var inside code.sk +++ b/src/test/skript/tests/regressions/837-getting a uuid of a var inside code.sk @@ -2,8 +2,8 @@ test "variable parsing": assert {_test::%{_x}%} is not set with "simple list index using local variable failed" assert {_test::{_x}} is not set to fail with "list index with local variable without percentage signs failed" - assert {_test::%uuid of {_x}%} is not set with "list index with expression and local variable ##1 failed" - assert {_test::%{_x}'s uuid%} is not set with "list index with expression and local variable ##2 failed" + assert {_test::%uuid of {_x}%} is not set with "list index with expression and local variable failed" + assert {_test::%{_x}'s uuid%} is not set with "list index with expression and local variable failed" assert {_test::%{_a} split at {_b}%} is not set with "list index with expression with local variables on both ends failed" diff --git a/src/test/skript/tests/syntaxes/conditions/CondContains.sk b/src/test/skript/tests/syntaxes/conditions/CondContains.sk index e076c90f83f..fcc247bb7c0 100644 --- a/src/test/skript/tests/syntaxes/conditions/CondContains.sk +++ b/src/test/skript/tests/syntaxes/conditions/CondContains.sk @@ -1,34 +1,34 @@ test "contains condition": # Strings - assert "abc" contains "b" with "simple string contains failed ##1" - assert "abc" contains "abc" with "simple string contains failed ##2" - assert "abc" and "cde" contain "c" with "simple string contains failed ##3" - assert "abc" does not contain "d" with "simple string contains failed ##4" - assert "abc" and "def" do not contain "ghi" with "simple string contains failed ##5" - assert "abc" and "def" does not contain "ab" with "string object failed ##6" + assert "abc" contains "b" with "simple string contains failed" + assert "abc" contains "abc" with "simple string contains failed" + assert "abc" and "cde" contain "c" with "simple string contains failed" + assert "abc" does not contain "d" with "simple string contains failed" + assert "abc" and "def" do not contain "ghi" with "simple string contains failed" + assert "abc" and "def" does not contain "ab" with "string object failed" # Objects - assert "abc" and "def" contains "abc" with "object contains failed ##1" - assert 123 and 456 contains 456 with "object contains failed ##2" - assert 123 and 456 does not contain 789 with "object contains failed ##3" - assert 123 contains 123 with "object contains failed ##4" + assert "abc" and "def" contains "abc" with "object contains failed" + assert 123 and 456 contains 456 with "object contains failed" + assert 123 and 456 does not contain 789 with "object contains failed" + assert 123 contains 123 with "object contains failed" set {_l::*} to 5 times - assert {_l::*} contains 5 with "object contains failed ##5" - assert {_l::*} does not contain 6 with "object contains failed ##6" + assert {_l::*} contains 5 with "object contains failed" + assert {_l::*} does not contain 6 with "object contains failed" # Inventory set {_inv} to chest inventory with 3 rows set slot 0 of {_inv} to stone named "pp" set slot 1 of {_inv} to dirt - assert {_inv} contains stone with "inventory contains failed ##1" - assert {_inv} contains stone named "pp" with "inventory contains failed ##2" - assert {_inv} does not contain stone named "abc" with "inventory contains failed ##3" - assert {_inv} does not contain sand with "inventory contains failed ##4" - assert {_inv} contains dirt with "inventory contains failed ##5" - assert {_inv} does not contain 2 dirt with "inventory contains failed ##6" + assert {_inv} contains stone with "inventory contains failed" + assert {_inv} contains stone named "pp" with "inventory contains failed" + assert {_inv} does not contain stone named "abc" with "inventory contains failed" + assert {_inv} does not contain sand with "inventory contains failed" + assert {_inv} contains dirt with "inventory contains failed" + assert {_inv} does not contain 2 dirt with "inventory contains failed" set slot 2 of {_inv} to dirt - assert {_inv} contains 2 dirt with "inventory contains failed ##7" - assert {_inv} does not contain 3 dirt with "inventory contains failed ##8" + assert {_inv} contains 2 dirt with "inventory contains failed" + assert {_inv} does not contain 3 dirt with "inventory contains failed" set {_inv1} to chest inventory with 3 rows set {_inv2} to chest inventory with 3 rows diff --git a/src/test/skript/tests/syntaxes/conditions/CondIsWithin.sk b/src/test/skript/tests/syntaxes/conditions/CondIsWithin.sk index a0c4339f556..b432c72f0ff 100644 --- a/src/test/skript/tests/syntaxes/conditions/CondIsWithin.sk +++ b/src/test/skript/tests/syntaxes/conditions/CondIsWithin.sk @@ -2,32 +2,32 @@ test "within condition" when running minecraft "1.17": # two locations set {_loc1} to location(0, 0, 0, "world") set {_loc2} to location(20, 20, 20, "world") - assert location(10, 10, 10, "world") is within {_loc1} and {_loc2} with "failed within two locs ##1" - assert location(10, -10, 10, "world") is not within {_loc1} and {_loc2} with "failed within two locs ##2" + assert location(10, 10, 10, "world") is within {_loc1} and {_loc2} with "failed within two locs" + assert location(10, -10, 10, "world") is not within {_loc1} and {_loc2} with "failed within two locs" # chunks set {_chunk1} to chunk at {_loc1} - assert location(10, 10, 10, "world") is within {_chunk1} with "failed within chunk ##1" - assert location(-10, 10, -10, "world") is not within {_chunk1} with "failed within chunk ##2" + assert location(10, 10, 10, "world") is within {_chunk1} with "failed within chunk" + assert location(-10, 10, -10, "world") is not within {_chunk1} with "failed within chunk" # worlds - assert location(10, 10, 10, "world") is within world("world") with "failed within world ##1" + assert location(10, 10, 10, "world") is within world("world") with "failed within world" # blocks set block at {_loc1} to stone - assert {_loc1} is within block at {_loc1} with "failed within block ##1" - assert {_loc2} is not within block at {_loc1} with "failed within block ##2" + assert {_loc1} is within block at {_loc1} with "failed within block" + assert {_loc2} is not within block at {_loc1} with "failed within block" # special case, non-full blocks set block at {_loc1} to lime carpet - assert {_loc1} is within block at {_loc1} with "failed within block ##3" - assert ({_loc1} ~ vector(0,0.3,0)) is not within block at {_loc1} with "failed within block ##4" + assert {_loc1} is within block at {_loc1} with "failed within block" + assert ({_loc1} ~ vector(0,0.3,0)) is not within block at {_loc1} with "failed within block" # entities set {_loc} to spawn of world "world" spawn a pig at {_loc} set {_pig} to last spawned entity - assert {_loc} is within {_pig} with "failed within entity ##1" - assert {_loc1} is not within {_pig} with "failed within entity ##2" + assert {_loc} is within {_pig} with "failed within entity" + assert {_loc1} is not within {_pig} with "failed within entity" delete random entity of {_pig} @@ -35,22 +35,22 @@ test "within condition" when running below minecraft "1.17": # two locations set {_loc1} to location(0, 0, 0, "world") set {_loc2} to location(20, 20, 20, "world") - assert location(10, 10, 10, "world") is within {_loc1} and {_loc2} with "failed within two locs ##1" - assert location(10, -10, 10, "world") is not within {_loc1} and {_loc2} with "failed within two locs ##2" + assert location(10, 10, 10, "world") is within {_loc1} and {_loc2} with "failed within two locs" + assert location(10, -10, 10, "world") is not within {_loc1} and {_loc2} with "failed within two locs" # chunks set {_chunk1} to chunk at {_loc1} - assert location(10, 10, 10, "world") is within {_chunk1} with "failed within chunk ##1" - assert location(-10, 10, -10, "world") is not within {_chunk1} with "failed within chunk ##2" + assert location(10, 10, 10, "world") is within {_chunk1} with "failed within chunk" + assert location(-10, 10, -10, "world") is not within {_chunk1} with "failed within chunk" # worlds - assert location(10, 10, 10, "world") is within world("world") with "failed within world ##1" + assert location(10, 10, 10, "world") is within world("world") with "failed within world" # entities set {_loc} to spawn of world "world" spawn a pig at {_loc} set {_pig} to last spawned entity - assert {_loc} is within {_pig} with "failed within entity ##1" - assert {_loc1} is not within {_pig} with "failed within entity ##2" + assert {_loc} is within {_pig} with "failed within entity" + assert {_loc1} is not within {_pig} with "failed within entity" delete random entity of {_pig} diff --git a/src/test/skript/tests/syntaxes/conditions/CondMethodExists.sk b/src/test/skript/tests/syntaxes/conditions/CondMethodExists.sk index e34bac7daac..538f8f5be04 100644 --- a/src/test/skript/tests/syntaxes/conditions/CondMethodExists.sk +++ b/src/test/skript/tests/syntaxes/conditions/CondMethodExists.sk @@ -1,10 +1,10 @@ test "method exists": - assert method "java.lang.String##length()" exists with "Existing method was not found" - assert method "java.lang.String##notReal()" doesn't exist with "Fake method was found" - assert method "java.lang.SuperFakeClass##notReal()" doesn't exist with "Fake class was found" - assert method "java.lang.String##indexOf(java.lang.String)" exists with "Existing method with parameter was not found" - assert method "java.lang.String##replace(java.lang.CharSequence,java.lang.CharSequence)" exists with "Existing method with multiple parameters not found" - assert method "java.lang.String##charAt(int)" exists with "Existing method with primitive parameter not found" - assert method "java.lang.String##substring(int,int)" exists with "Existing method with multiple primitive parameters not found" - assert method "java.lang.String##valueOf(char[])" exists with "Existing method with primitive array parameter not found" - assert method "java.lang.Runtime##exec(java.lang.String[],java.lang.String[])" exists with "Existing method with array parameter not found" + assert method "java.lang.String#length()" exists with "Existing method was not found" + assert method "java.lang.String#notReal()" doesn't exist with "Fake method was found" + assert method "java.lang.SuperFakeClass#notReal()" doesn't exist with "Fake class was found" + assert method "java.lang.String#indexOf(java.lang.String)" exists with "Existing method with parameter was not found" + assert method "java.lang.String#replace(java.lang.CharSequence,java.lang.CharSequence)" exists with "Existing method with multiple parameters not found" + assert method "java.lang.String#charAt(int)" exists with "Existing method with primitive parameter not found" + assert method "java.lang.String#substring(int,int)" exists with "Existing method with multiple primitive parameters not found" + assert method "java.lang.String#valueOf(char[])" exists with "Existing method with primitive array parameter not found" + assert method "java.lang.Runtime#exec(java.lang.String[],java.lang.String[])" exists with "Existing method with array parameter not found" diff --git a/src/test/skript/tests/syntaxes/effects/EffContinue.sk b/src/test/skript/tests/syntaxes/effects/EffContinue.sk index 83804bc908f..2400e7441e0 100644 --- a/src/test/skript/tests/syntaxes/effects/EffContinue.sk +++ b/src/test/skript/tests/syntaxes/effects/EffContinue.sk @@ -11,9 +11,9 @@ test "continue effect": assert {_i} is not 5 with "continue in while failed" loop integers from 1 to 10: continue this loop if loop-value is 5 - assert loop-value is not 5 with "leveled continue failed ##1" + assert loop-value is not 5 with "leveled continue failed" loop integers from 11 to 20: continue 2nd loop if loop-value-2 is 15 - assert loop-value-2 is not 15 with "leveled continue failed ##2" + assert loop-value-2 is not 15 with "leveled continue failed" continue 1st loop if loop-value-1 is 10 - assert loop-value is not 10 with "leveled continue failed ##3" + assert loop-value is not 10 with "leveled continue failed" diff --git a/src/test/skript/tests/syntaxes/effects/EffHealth.sk b/src/test/skript/tests/syntaxes/effects/EffHealth.sk index 41ecf33904b..b77c566076b 100644 --- a/src/test/skript/tests/syntaxes/effects/EffHealth.sk +++ b/src/test/skript/tests/syntaxes/effects/EffHealth.sk @@ -11,13 +11,13 @@ test "health effect": set {_m} to last spawned cow assert health of {_m} is 5 with "default cow health failed" damage {_m} by 0.5 - assert health of {_m} is 4.5 with "damage cow ##1 failed" + assert health of {_m} is 4.5 with "damage cow failed" damage {_m} by 99 - assert health of {_m} is 0 with "damage cow ##2 failed" + assert health of {_m} is 0 with "damage cow failed" heal {_m} by 1 - assert health of {_m} is 1 with "heal cow ##1 failed" + assert health of {_m} is 1 with "heal cow failed" heal {_m} by 0.5 - assert health of {_m} is 1.5 with "heal cow ##2 failed" + assert health of {_m} is 1.5 with "heal cow failed" heal {_m} by 99 - assert health of {_m} is 5 with "heal cow ##3 failed" + assert health of {_m} is 5 with "heal cow failed" clear all entities diff --git a/src/test/skript/tests/syntaxes/expressions/ExprArrowsStuck.sk b/src/test/skript/tests/syntaxes/expressions/ExprArrowsStuck.sk index 9b41359b7af..aa577e45d4c 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprArrowsStuck.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprArrowsStuck.sk @@ -4,15 +4,15 @@ test "arrows stuck": set arrows stuck in event-entity to 31 assert arrows stuck in event-entity is 31 with "arrows stuck set failed" add 5 to arrows stuck in event-entity - assert arrows stuck in event-entity is 36 with "arrows stuck add ##1 failed" + assert arrows stuck in event-entity is 36 with "arrows stuck add failed" remove 10 from arrows stuck in event-entity - assert arrows stuck in event-entity is 26 with "arrows stuck remove ##1 failed" + assert arrows stuck in event-entity is 26 with "arrows stuck remove failed" remove 999 from arrows stuck in event-entity - assert arrows stuck in event-entity is 0 with "arrows stuck remove ##2 failed" + assert arrows stuck in event-entity is 0 with "arrows stuck remove failed" remove -2 from arrows stuck in event-entity - assert arrows stuck in event-entity is 2 with "arrows stuck remove ##3 failed" + assert arrows stuck in event-entity is 2 with "arrows stuck remove failed" add -1 to arrows stuck in event-entity - assert arrows stuck in event-entity is 1 with "arrows stuck add ##2 failed" + assert arrows stuck in event-entity is 1 with "arrows stuck add failed" delete arrows stuck in event-entity assert arrows stuck in event-entity is 0 with "arrows stuck delete failed" delete event-entity diff --git a/src/test/skript/tests/syntaxes/expressions/ExprEntityAttribute.sk b/src/test/skript/tests/syntaxes/expressions/ExprEntityAttribute.sk index 730afc9d164..ed04483dbe0 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprEntityAttribute.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprEntityAttribute.sk @@ -6,9 +6,9 @@ test "attributes 1": add 5 to movement speed attribute of event-entity assert movement speed attribute of event-entity is 8.14 with "attribute add failed" remove 4 from movement speed attribute of event-entity - assert movement speed attribute of event-entity is 4.14 with "attribute remove ##1 failed" + assert movement speed attribute of event-entity is 4.14 with "attribute remove failed" remove 10 from movement speed attribute of event-entity - assert movement speed attribute of event-entity is -5.86 with "attribute remove ##2 failed" # Negative attribute values should be safe + assert movement speed attribute of event-entity is -5.86 with "attribute remove failed" # Negative attribute values should be safe delete movement speed attribute of event-entity assert movement speed attribute of event-entity is 0 with "attribute delete failed" delete event-entity diff --git a/src/test/skript/tests/syntaxes/expressions/ExprFreezeTicks.sk b/src/test/skript/tests/syntaxes/expressions/ExprFreezeTicks.sk index c6e47ec617e..7caec6fcb3f 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprFreezeTicks.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprFreezeTicks.sk @@ -4,14 +4,14 @@ test "freeze time" when running minecraft "1.18": set freeze time of entity to 3 seconds assert freeze time of entity is 3 seconds with "freeze time set failed" add 2 seconds to freeze time of entity - assert freeze time of entity is 5 seconds with "freeze time add ##1 failed" + assert freeze time of entity is 5 seconds with "freeze time add failed" add 10 seconds to freeze time of entity - assert freeze time of entity is 15 seconds with "freeze time add ##2 failed" # freeze time should not be capped at entity's max freeze time (7 seconds for a cow) + assert freeze time of entity is 15 seconds with "freeze time add failed" # freeze time should not be capped at entity's max freeze time (7 seconds for a cow) remove 6 seconds from freeze time of entity - assert freeze time of entity is 9 seconds with "freeze time remove ##1 failed" + assert freeze time of entity is 9 seconds with "freeze time remove failed" remove 10 seconds from freeze time of entity - assert freeze time of entity is 0 seconds with "freeze time remove ##2 failed" # freeze time should not be negative + assert freeze time of entity is 0 seconds with "freeze time remove failed" # freeze time should not be negative delete freeze time of entity assert freeze time of entity is 0 seconds with "freeze time delete failed" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk b/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk index 9b2c90fb25f..b5ef324b555 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprItemsIn.sk @@ -5,8 +5,8 @@ test "filtering ExprItemsIn": set slot 1 of {_inv} to dirt set slot 2 of {_inv} to stone set slot 3 of {_inv} to bucket - assert all blocks in inventory {_inv} are dirt or stone with "found correct items with ExprItemsIn##get" - assert (all blocks in inventory {_inv} where [true is true]) are dirt or stone with "found correct items with ExprItemsIn##iterator" + assert all blocks in inventory {_inv} are dirt or stone with "found correct items with ExprItemsIn#get" + assert (all blocks in inventory {_inv} where [true is true]) are dirt or stone with "found correct items with ExprItemsIn#iterator" set {_dirt} to dirt assert all {_dirt} in inventory {_inv} is dirt with "found incorrect items with variable itemtypes" @@ -17,5 +17,5 @@ test "unfiltered ExprItemsIn": set slot 1 of {_inv} to dirt set slot 2 of {_inv} to stone set slot 3 of {_inv} to bucket - assert all items in inventory {_inv} are dirt, stone or bucket with "found correct items with ExprItemsIn##get" - assert (all items in inventory {_inv} where [true is true]) are dirt, stone or bucket with "found correct items with ExprItemsIn##iterator" + assert all items in inventory {_inv} are dirt, stone or bucket with "found correct items with ExprItemsIn#get" + assert (all items in inventory {_inv} where [true is true]) are dirt, stone or bucket with "found correct items with ExprItemsIn#iterator" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprNoDamageTicks.sk b/src/test/skript/tests/syntaxes/expressions/ExprNoDamageTicks.sk index 794dffa6e87..2d314c9e6d2 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprNoDamageTicks.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprNoDamageTicks.sk @@ -5,14 +5,14 @@ test "no damage ticks": set {_m}'s invulnerability ticks to 25 assert {_m}'s invulnerability ticks is 25 with "no damage ticks set failed" add 5 to {_m}'s invulnerability ticks - assert {_m}'s invulnerability ticks is 30 with "no damage ticks add ##1 failed" + assert {_m}'s invulnerability ticks is 30 with "no damage ticks add failed" remove 12 from {_m}'s invulnerability ticks - assert {_m}'s invulnerability ticks is 18 with "no damage ticks remove ##1 failed" + assert {_m}'s invulnerability ticks is 18 with "no damage ticks remove failed" remove 999 from {_m}'s invulnerability ticks - assert {_m}'s invulnerability ticks is 0 with "no damage ticks remove ##2 failed" + assert {_m}'s invulnerability ticks is 0 with "no damage ticks remove failed" remove -2 from {_m}'s invulnerability ticks - assert {_m}'s invulnerability ticks is 2 with "no damage ticks remove ##3 failed" + assert {_m}'s invulnerability ticks is 2 with "no damage ticks remove failed" add -1 to {_m}'s invulnerability ticks - assert {_m}'s invulnerability ticks is 1 with "no damage ticks add ##2 failed" + assert {_m}'s invulnerability ticks is 1 with "no damage ticks add failed" delete {_m}'s invulnerability ticks assert {_m}'s invulnerability ticks is 0 with "no damage ticks delete failed" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprPortalCooldown.sk b/src/test/skript/tests/syntaxes/expressions/ExprPortalCooldown.sk index 7d919d788da..36b95564dc7 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprPortalCooldown.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprPortalCooldown.sk @@ -4,11 +4,11 @@ test "portal cooldown": set event-entity's portal cooldown to 25 ticks assert event-entity's portal cooldown is 25 ticks with "portal cooldown set failed" add 5 seconds to event-entity's portal cooldown - assert event-entity's portal cooldown is 125 ticks with "portal cooldown add ##1 failed" + assert event-entity's portal cooldown is 125 ticks with "portal cooldown add failed" remove 12 ticks from event-entity's portal cooldown - assert event-entity's portal cooldown is 113 ticks with "portal cooldown remove ##1 failed" + assert event-entity's portal cooldown is 113 ticks with "portal cooldown remove failed" remove 999 ticks from event-entity's portal cooldown - assert event-entity's portal cooldown is 0 ticks with "portal cooldown remove ##2 failed" + assert event-entity's portal cooldown is 0 ticks with "portal cooldown remove failed" delete event-entity's portal cooldown assert event-entity's portal cooldown is 0 ticks with "portal cooldown delete failed" reset event-entity's portal cooldown diff --git a/src/test/skript/tests/syntaxes/functions/caseEquals.sk b/src/test/skript/tests/syntaxes/functions/caseEquals.sk index f777e6ff7df..ace8276da6e 100644 --- a/src/test/skript/tests/syntaxes/functions/caseEquals.sk +++ b/src/test/skript/tests/syntaxes/functions/caseEquals.sk @@ -1,6 +1,6 @@ test "case equals function": - assert caseEquals("") is true with "case equals function failed ##1" - assert caseEquals("dummy") is true with "case equals function failed ##2" - assert caseEquals("hi", "Hi") is false with "case equals function failed ##3" - assert caseEquals("text", "text", "tExt") is false with "case equals function failed ##4" - assert caseEquals("🐢<-turtle", "🐢<-turtle", "🐢<-turtle", "🐢<-turtle") is true with "case equals function failed ##5" + assert caseEquals("") is true with "case equals function failed" + assert caseEquals("dummy") is true with "case equals function failed" + assert caseEquals("hi", "Hi") is false with "case equals function failed" + assert caseEquals("text", "text", "tExt") is false with "case equals function failed" + assert caseEquals("🐢<-turtle", "🐢<-turtle", "🐢<-turtle", "🐢<-turtle") is true with "case equals function failed" diff --git a/src/test/skript/tests/syntaxes/sections/SecConditional.sk b/src/test/skript/tests/syntaxes/sections/SecConditional.sk index f9991e9f621..4957319f369 100644 --- a/src/test/skript/tests/syntaxes/sections/SecConditional.sk +++ b/src/test/skript/tests/syntaxes/sections/SecConditional.sk @@ -18,18 +18,18 @@ test "SecConditional": delete {_b} 1 = 2 else if 1 = 1: - assert 1 = 2 with "conditional failed ##1" + assert 1 = 2 with "conditional failed" else: - assert 1 = 2 with "conditional failed ##2" + assert 1 = 2 with "conditional failed" if {_b} is set: - assert 1 = 2 with "conditional failed ##3" + assert 1 = 2 with "conditional failed" if 1 = 2: - assert 1 = 2 with "conditional failed ##4" + assert 1 = 2 with "conditional failed" else if 1 = 1: exit 1 section else: - assert 1 = 2 with "conditional failed ##5" + assert 1 = 2 with "conditional failed" test "SecConditional - if all true": if: From 585e8d15be016f9a87a38c59cd7f43885d5fdc57 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Mon, 1 Jul 2024 11:54:52 -0400 Subject: [PATCH 33/48] 1.21 Support (#6798) * Update Paper API to 1.21 * Patch up aliases * Update 5491-xp orb merge overwrite.sk --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- build.gradle | 4 +- gradle.properties | 2 +- .../njol/skript/aliases/AliasesProvider.java | 11 ++- .../java/ch/njol/skript/aliases/ItemData.java | 98 +++++++++++-------- .../java/ch/njol/skript/aliases/ItemType.java | 36 +++++-- .../skript/bukkitutil/block/BlockCompat.java | 3 + .../bukkitutil/block/NewBlockCompat.java | 7 ++ .../skript/classes/data/BukkitClasses.java | 1 + .../environments/java21/paper-1.21.0.json | 17 ++++ .../5491-xp orb merge overwrite.sk | 4 + .../tests/syntaxes/effects/EffGlowingText.sk | 2 +- 11 files changed, 128 insertions(+), 57 deletions(-) create mode 100644 src/test/skript/environments/java21/paper-1.21.0.json diff --git a/build.gradle b/build.gradle index 161d3dd3daf..0826a6af81d 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ dependencies { shadow group: 'org.bstats', name: 'bstats-bukkit', version: '3.0.2' shadow group: 'net.kyori', name: 'adventure-text-serializer-bungeecord', version: '4.3.2' - implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.20.6-R0.1-SNAPSHOT' + implementation group: 'io.papermc.paper', name: 'paper-api', version: '1.21-R0.1-SNAPSHOT' implementation group: 'org.eclipse.jdt', name: 'org.eclipse.jdt.annotation', version: '2.2.700' implementation group: 'com.google.code.findbugs', name: 'findbugs', version: '3.0.1' implementation group: 'com.sk89q.worldguard', name: 'worldguard-legacy', version: '7.0.0-SNAPSHOT' @@ -235,7 +235,7 @@ def java21 = 21 def java17 = 17 def java8 = 8 -def latestEnv = 'java21/paper-1.20.6.json' +def latestEnv = 'java21/paper-1.21.0.json' def latestJava = java21 def oldestJava = java8 diff --git a/gradle.properties b/gradle.properties index f78ae1766eb..469d1e072ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,5 +7,5 @@ groupid=ch.njol name=skript version=2.8.7 jarName=Skript.jar -testEnv=java21/paper-1.20.6 +testEnv=java21/paper-1.21.0 testEnvJavaVersion=21 diff --git a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java index a3b8b215b75..cccf91618ed 100644 --- a/src/main/java/ch/njol/skript/aliases/AliasesProvider.java +++ b/src/main/java/ch/njol/skript/aliases/AliasesProvider.java @@ -304,16 +304,19 @@ public void addAlias(AliasName name, String id, @Nullable Map ta } // Apply (NBT) tags to item stack - ItemStack stack = new ItemStack(material); + ItemStack stack = null; int itemFlags = 0; - if (tags != null) { - itemFlags = applyTags(stack, new HashMap<>(tags)); + if (material.isItem()) { + stack = new ItemStack(material); + if (tags != null) { + itemFlags = applyTags(stack, new HashMap<>(tags)); + } } // Parse block state to block values BlockValues blockValues = BlockCompat.INSTANCE.createBlockValues(material, blockStates, stack, itemFlags); - ItemData data = new ItemData(stack, blockValues); + ItemData data = stack != null ? new ItemData(stack, blockValues) : new ItemData(material, blockValues); data.isAlias = true; data.itemFlags = itemFlags; diff --git a/src/main/java/ch/njol/skript/aliases/ItemData.java b/src/main/java/ch/njol/skript/aliases/ItemData.java index 32323c57e6f..0f68279f43a 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemData.java +++ b/src/main/java/ch/njol/skript/aliases/ItemData.java @@ -37,7 +37,7 @@ import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.NotSerializableException; @@ -91,11 +91,6 @@ public static class OldItemData { @Deprecated public static final boolean itemDataValues = false; - /** - * ItemStack, which is used for everything but serialization. - */ - transient ItemStack stack; - /** * Type of the item as Bukkit material. Serialized manually. */ @@ -105,14 +100,18 @@ public static class OldItemData { * If this represents all possible items. */ boolean isAnything; - + + /** + * ItemStack, which is used for everything but serialization. + */ + transient @Nullable ItemStack stack; + /** * When this ItemData represents a block, this contains information to * allow comparing it against other blocks. */ - @Nullable - BlockValues blockValues; - + @Nullable BlockValues blockValues; + /** * Whether this represents an item (that definitely cannot have * block states) or a block, which might have them. @@ -140,9 +139,10 @@ public static class OldItemData { public ItemData(Material type, @Nullable String tags) { this.type = type; - - this.stack = new ItemStack(type); - this.blockValues = BlockCompat.INSTANCE.getBlockValues(stack); + + if (type.isItem()) + this.stack = new ItemStack(type); + this.blockValues = BlockCompat.INSTANCE.getBlockValues(type); if (tags != null) { applyTags(tags); } @@ -150,8 +150,9 @@ public ItemData(Material type, @Nullable String tags) { public ItemData(Material type, int amount) { this.type = type; - this.stack = new ItemStack(type, Math.abs(amount)); - this.blockValues = BlockCompat.INSTANCE.getBlockValues(stack); + if (type.isItem()) + this.stack = new ItemStack(type, Math.abs(amount)); + this.blockValues = BlockCompat.INSTANCE.getBlockValues(type); } public ItemData(Material type) { @@ -159,13 +160,18 @@ public ItemData(Material type) { } public ItemData(ItemData data) { - this.stack = data.stack.clone(); + this.stack = data.stack != null ? data.stack.clone() : null; this.type = data.type; this.blockValues = data.blockValues; this.isAlias = data.isAlias; this.plain = data.plain; this.itemFlags = data.itemFlags; } + + public ItemData(Material material, @Nullable BlockValues values) { + this.type = material; + this.blockValues = values; + } public ItemData(ItemStack stack, @Nullable BlockValues values) { this.stack = stack; @@ -200,7 +206,8 @@ public ItemData(BlockState blockState) { public ItemData(BlockData blockData) { this.type = blockData.getMaterial(); - this.stack = new ItemStack(type); + if (type.isItem()) + this.stack = new ItemStack(type); this.blockValues = BlockCompat.INSTANCE.getBlockValues(blockData); } @@ -227,13 +234,12 @@ public boolean isOfType(@Nullable ItemStack item) { if (type != item.getType()) return false; // Obvious mismatch - if (itemFlags != 0) { // Either stack has tags (or durability) + if (stack != null && itemFlags != 0) { // Either stack has tags (or durability) if (ItemUtils.getDamage(stack) != ItemUtils.getDamage(item)) return false; // On 1.12 and below, damage is not in meta if (stack.hasItemMeta() == item.hasItemMeta()) // Compare ItemMeta as in isSimilar() of ItemStack - return stack.hasItemMeta() ? itemFactory.equals(stack.getItemMeta(), item.getItemMeta()) : true; - else - return false; + return !stack.hasItemMeta() || itemFactory.equals(stack.getItemMeta(), item.getItemMeta()); + return false; } return true; } @@ -249,7 +255,7 @@ public String toString() { public String toString(final boolean debug, final boolean plural) { StringBuilder builder = new StringBuilder(Aliases.getMaterialName(this, plural)); - ItemMeta meta = stack.getItemMeta(); + ItemMeta meta = stack != null ? stack.getItemMeta() : null; if (meta != null && meta.hasDisplayName()) { builder.append(" ").append(m_named).append(" "); builder.append(meta.getDisplayName()); @@ -282,7 +288,7 @@ public boolean equals(final @Nullable Object obj) { @Override public int hashCode() { int hash = type.hashCode(); // Has collisions, but probably not too many of them - if (blockValues == null || (blockValues != null && blockValues.isDefault())) { + if (blockValues == null || blockValues.isDefault()) { hash = hash * 37 + 1; } return hash; @@ -351,7 +357,7 @@ public MatchQuality matchAlias(ItemData item) { } // See if we need to compare item metas (excluding durability) - if (quality.isAtLeast(MatchQuality.SAME_ITEM) && stack.hasItemMeta() || item.stack.hasItemMeta()) { // Item meta checks could lower this + if (quality.isAtLeast(MatchQuality.SAME_ITEM) && this.hasItemMeta() || item.hasItemMeta()) { // Item meta checks could lower this MatchQuality metaQuality = compareItemMetas(getItemMeta(), item.getItemMeta()); // If given item doesn't care about meta, promote to SAME_ITEM @@ -489,9 +495,13 @@ public ItemData intersection(final ItemData other) { * It is not a copy, so please be careful. * @return Item stack. */ - public ItemStack getStack() { + public @Nullable ItemStack getStack() { return stack; } + + private boolean hasItemMeta() { + return stack != null && stack.hasItemMeta(); + } @Override public ItemData clone() { @@ -508,7 +518,7 @@ public BlockValues getBlockValues() { } public ItemMeta getItemMeta() { - ItemMeta meta = stack.getItemMeta(); + ItemMeta meta = stack != null ? stack.getItemMeta() : null; if (meta == null) { // AIR has null item meta! meta = itemFactory.getItemMeta(Material.STONE); } @@ -517,6 +527,8 @@ public ItemMeta getItemMeta() { } public void setItemMeta(ItemMeta meta) { + if (stack == null) + return; stack.setItemMeta(meta); isAlias = false; // This is no longer exact alias plain = false; // This is no longer a plain item @@ -524,10 +536,14 @@ public void setItemMeta(ItemMeta meta) { } public int getDurability() { + if (stack == null) + return 0; // no damage? return ItemUtils.getDamage(stack); } public void setDurability(int durability) { + if (stack == null) + return; ItemUtils.setDamage(stack, durability); isAlias = false; // Change happened plain = false; // This is no longer a plain item @@ -567,7 +583,7 @@ public boolean matchPlain(ItemData other) { public Fields serialize() throws NotSerializableException { Fields fields = new Fields(this); // ItemStack is transient, will be ignored fields.putPrimitive("id", type.ordinal()); - fields.putObject("meta", stack.getItemMeta()); + fields.putObject("meta", stack != null ? stack.getItemMeta() : null); return fields; } @@ -579,8 +595,10 @@ public void deserialize(Fields fields) throws StreamCorruptedException, NotSeria ItemMeta meta = fields.getAndRemoveObject("meta", ItemMeta.class); // Initialize ItemStack - this.stack = new ItemStack(type); - stack.setItemMeta(meta); // Just set meta to it + if (meta != null && type.isItem()) { + this.stack = new ItemStack(type); + stack.setItemMeta(meta); // Just set meta to it + } fields.setFields(this); // Everything but ItemStack and Material } @@ -598,17 +616,17 @@ public void deserialize(Fields fields) throws StreamCorruptedException, NotSeria */ public ItemData aliasCopy() { ItemData data = new ItemData(); - data.stack = new ItemStack(type, 1); - - if (stack.hasItemMeta()) { - ItemMeta meta = stack.getItemMeta(); // Creates a copy - meta.setDisplayName(null); // Clear display name - if (!itemFactory.getItemMeta(type).equals(meta)) // there may be different tags (e.g. potions) - data.itemFlags |= ItemFlags.CHANGED_TAGS; - data.stack.setItemMeta(meta); + if (stack != null) { + data.stack = new ItemStack(type, 1); + if (stack.hasItemMeta()) { + ItemMeta meta = stack.getItemMeta(); // Creates a copy + meta.setDisplayName(null); // Clear display name + if (!itemFactory.getItemMeta(type).equals(meta)) // there may be different tags (e.g. potions) + data.itemFlags |= ItemFlags.CHANGED_TAGS; + data.stack.setItemMeta(meta); + } + ItemUtils.setDamage(data.stack, 0); // Set to undamaged } - ItemUtils.setDamage(data.stack, 0); // Set to undamaged - data.type = type; data.blockValues = blockValues; data.itemForm = itemForm; @@ -620,6 +638,8 @@ public ItemData aliasCopy() { * @param tags Tags in Mojang's JSON format. */ public void applyTags(String tags) { + if (stack == null) + return; BukkitUnsafe.modifyItemStack(stack, tags); itemFlags |= ItemFlags.CHANGED_TAGS; } diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index 7341667fa91..0b47617ef5c 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -68,6 +68,7 @@ import java.util.Random; import java.util.RandomAccess; import java.util.Set; +import java.util.stream.Collectors; @ContainerType(ItemStack.class) public class ItemType implements Unit, Iterable, Container, YggdrasilExtendedSerializable { @@ -315,7 +316,7 @@ public boolean isOfType(Material id, @Nullable String tags) { public boolean isOfType(Material id) { // TODO avoid object creation - return isOfType(new ItemData(id, null)); + return isOfType(new ItemData(id, (String) null)); } /** @@ -343,7 +344,7 @@ public ItemType getBlock() { */ public boolean hasItem() { for (ItemData d : types) { - if (!d.type.isBlock()) + if (d.type.isItem()) return true; } return false; @@ -487,9 +488,13 @@ public boolean hasNext() { @Override public ItemStack next() { - if (!hasNext()) - throw new NoSuchElementException(); - ItemStack is = iter.next().getStack().clone(); + ItemStack is = null; + while (is == null) { + if (!hasNext()) + throw new NoSuchElementException(); + is = iter.next().getStack(); + } + is = is.clone(); is.setAmount(getAmount()); return is; } @@ -588,10 +593,17 @@ public ItemType clone() { * @see #removeFrom(ItemStack) * @see #removeFrom(List...) */ - public ItemStack getRandom() { - int numItems = types.size(); + public @Nullable ItemStack getRandom() { + List datas = types.stream() + .filter(data -> data.stack != null) + .collect(Collectors.toList()); + if (datas.isEmpty()) + return null; + int numItems = datas.size(); int index = random.nextInt(numItems); - ItemStack is = types.get(index).getStack().clone(); + ItemStack is = datas.get(index).getStack(); + assert is != null; // verified above + is = is.clone(); is.setAmount(getAmount()); return is; } @@ -869,7 +881,9 @@ public final boolean removeFrom(boolean replaceWithNull, List... list */ public void addTo(final List list) { if (!isAll()) { - list.add(getItem().getRandom()); + ItemStack random = getItem().getRandom(); + if (random != null) + list.add(getItem().getRandom()); return; } for (final ItemStack is : getItem().getAll()) @@ -936,7 +950,9 @@ private static boolean addTo(@Nullable ItemStack is, ItemStack[] buf) { public boolean addTo(final ItemStack[] buf) { if (!isAll()) { - return addTo(getItem().getRandom(), buf); + ItemStack random = getItem().getRandom(); + if (random != null) + return addTo(getItem().getRandom(), buf); } boolean ok = true; for (ItemStack is : getItem().getAll()) { diff --git a/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java b/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java index eef0866a01b..87bc45a6ec8 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java +++ b/src/main/java/ch/njol/skript/bukkitutil/block/BlockCompat.java @@ -65,6 +65,9 @@ default BlockValues getBlockValues(Block block) { return getBlockValues(block.getBlockData()); } + @Nullable + BlockValues getBlockValues(Material material); + @Nullable BlockValues getBlockValues(BlockData blockData); diff --git a/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java b/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java index 2a0ecd36da8..e5cf9c0567b 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java +++ b/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java @@ -336,6 +336,13 @@ public BlockValues getBlockValues(BlockState blockState) { return getBlockValues(blockState.getBlockData()); } + @Override + public @Nullable BlockValues getBlockValues(Material material) { + if (material.isBlock()) + return new NewBlockValues(material, Bukkit.createBlockData(material), false); + return null; + } + @Nullable @Override public BlockValues getBlockValues(BlockData blockData) { diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 25000a2390d..168585cf801 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -930,6 +930,7 @@ public String toVariableNameString(InventoryHolder holder) { .since("1.0") .after("number") .supplier(() -> Arrays.stream(Material.values()) + .filter(Material::isItem) .map(ItemStack::new) .iterator()) .parser(new Parser() { diff --git a/src/test/skript/environments/java21/paper-1.21.0.json b/src/test/skript/environments/java21/paper-1.21.0.json new file mode 100644 index 00000000000..22a72e3a73b --- /dev/null +++ b/src/test/skript/environments/java21/paper-1.21.0.json @@ -0,0 +1,17 @@ +{ + "name": "paper-1.21.0", + "resources": [ + {"source": "server.properties.generic", "target": "server.properties"} + ], + "paperDownloads": [ + { + "version": "1.21", + "target": "paperclip.jar" + } + ], + "skriptTarget": "plugins/Skript.jar", + "commandLine": [ + "-Dcom.mojang.eula.agree=true", + "-jar", "paperclip.jar", "--nogui" + ] +} diff --git a/src/test/skript/tests/regressions/5491-xp orb merge overwrite.sk b/src/test/skript/tests/regressions/5491-xp orb merge overwrite.sk index ca5b9b46d6e..c925f51c590 100644 --- a/src/test/skript/tests/regressions/5491-xp orb merge overwrite.sk +++ b/src/test/skript/tests/regressions/5491-xp orb merge overwrite.sk @@ -1,6 +1,10 @@ test "spawn xp orb overwriting merged value" when running minecraft "1.14.4": # 1.13.2 seems not to merge xp orbs in the same way, so this test is skipped + # 1.21 also does not merge orbs, so this test is disabled. + # TODO: figure out how to force the orbs to merge on 1.21+ + running below minecraft "1.21" + # sanitize kill all xp orbs set {_spawn} to spawn of world "world" diff --git a/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk b/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk index b43a5786755..d39b3e6988a 100644 --- a/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk +++ b/src/test/skript/tests/syntaxes/effects/EffGlowingText.sk @@ -10,7 +10,7 @@ test "glowing sign blocks" when running minecraft "1.17.1": set block at {_loc} to {_original block} test "glowing sign items" when running minecraft "1.17.1": - set {_sign} to sign + set {_sign} to floor sign assert {_sign} doesn't have glowing text with "Sign had glowing text erroneously (1)" make {_sign} have glowing text assert {_sign} has glowing text with "Sign had normal text erroneously" From 2f629f138676486025729921b4fca13e60905561 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Mon, 1 Jul 2024 17:23:58 +0100 Subject: [PATCH 34/48] Attempt to estimate world in teleport when it's not specified. (#6611) --- .../ch/njol/skript/effects/EffTeleport.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffTeleport.java b/src/main/java/ch/njol/skript/effects/EffTeleport.java index 3ba06d7d150..990e1aa7ec1 100644 --- a/src/main/java/ch/njol/skript/effects/EffTeleport.java +++ b/src/main/java/ch/njol/skript/effects/EffTeleport.java @@ -19,7 +19,6 @@ package ch.njol.skript.effects; import ch.njol.skript.Skript; -import ch.njol.skript.sections.EffSecSpawn; import ch.njol.skript.sections.EffSecSpawn.SpawnEvent; import ch.njol.skript.bukkitutil.EntityUtils; import ch.njol.skript.doc.Description; @@ -31,7 +30,6 @@ import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.Trigger; import ch.njol.skript.lang.TriggerItem; -import ch.njol.skript.lang.TriggerSection; import ch.njol.skript.timings.SkriptTimings; import ch.njol.skript.util.Direction; import ch.njol.skript.variables.Variables; @@ -53,7 +51,7 @@ "which may cause lag spikes or server crashes when using this effect to teleport entities to unloaded chunks." }) @Examples({ - "teleport the player to {homes.%player%}", + "teleport the player to {homes::%player%}", "teleport the attacker to the victim" }) @Since("1.0") @@ -101,6 +99,7 @@ protected TriggerItem walk(Event e) { Location loc = location.getSingle(e); if (loc == null) return next; + boolean unknownWorld = !loc.isWorldLoaded(); Entity[] entityArray = entities.getArray(e); // We have to fetch this before possible async execution to avoid async local variable access. if (entityArray.length == 0) @@ -108,11 +107,17 @@ protected TriggerItem walk(Event e) { if (!delayed) { if (e instanceof PlayerRespawnEvent && entityArray.length == 1 && entityArray[0].equals(((PlayerRespawnEvent) e).getPlayer())) { + if (unknownWorld) + return next; ((PlayerRespawnEvent) e).setRespawnLocation(loc); return next; } if (e instanceof PlayerMoveEvent && entityArray.length == 1 && entityArray[0].equals(((PlayerMoveEvent) e).getPlayer())) { + if (unknownWorld) { // we can approximate the world + loc = loc.clone(); + loc.setWorld(((PlayerMoveEvent) e).getFrom().getWorld()); + } ((PlayerMoveEvent) e).setTo(loc); return next; } @@ -125,6 +130,19 @@ protected TriggerItem walk(Event e) { return next; } + if (unknownWorld) { // we can't fetch the chunk without a world + if (entityArray.length == 1) { // if there's 1 thing we can borrow its world + Entity entity = entityArray[0]; + if (entity == null) + return next; + // assume it's a local teleport, use the first entity we find as a reference + loc = loc.clone(); + loc.setWorld(entity.getWorld()); + } else { + return next; // no entities = no chunk = nobody teleporting + } + } + final Location fixed = loc; Delay.addDelayedEvent(e); Object localVars = Variables.removeLocals(e); @@ -132,7 +150,7 @@ protected TriggerItem walk(Event e) { PaperLib.getChunkAtAsync(loc).thenAccept(chunk -> { // The following is now on the main thread for (Entity entity : entityArray) { - EntityUtils.teleport(entity, loc); + EntityUtils.teleport(entity, fixed); } // Re-set local variables From 60658915fbcd3da0379c74dbeb6e7fed420dae79 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:33:25 +0200 Subject: [PATCH 35/48] Add EffSort, rework ExprInput to use ParserData (#6737) * Refactor ExprFilter * Change rawCond to unparsedCondition * Switch dependent input expressions to weak references * Revert "Switch dependent input expressions to weak references" This reverts commit 20169a11cdfa4fdf0c8b2a2e9e387f623ac50301. * Fix chained/nested filters * add sorted by and rework input expression and tests! * updates to test, tostring, and docs * i guess it didn't want {} * indentation * toString NPE * Requested Changes, except async sorting * Update InputSource.java * Apply suggestions from code review Co-authored-by: Patrick Miller * requested changes + correct annotations --------- Co-authored-by: Pikachu920 <28607612+Pikachu920@users.noreply.github.com> Co-authored-by: Patrick Miller --- .../java/ch/njol/skript/effects/EffSort.java | 167 +++++++++++ .../njol/skript/expressions/ExprFilter.java | 260 +++++++----------- .../ch/njol/skript/expressions/ExprInput.java | 170 ++++++++++++ .../java/ch/njol/skript/lang/InputSource.java | 101 +++++++ .../skript/tests/syntaxes/effects/EffSort.sk | 47 ++++ .../tests/syntaxes/expressions/ExprFilter.sk | 11 + 6 files changed, 591 insertions(+), 165 deletions(-) create mode 100644 src/main/java/ch/njol/skript/effects/EffSort.java create mode 100644 src/main/java/ch/njol/skript/expressions/ExprInput.java create mode 100644 src/main/java/ch/njol/skript/lang/InputSource.java create mode 100644 src/test/skript/tests/syntaxes/effects/EffSort.sk diff --git a/src/main/java/ch/njol/skript/effects/EffSort.java b/src/main/java/ch/njol/skript/effects/EffSort.java new file mode 100644 index 00000000000..40061777d50 --- /dev/null +++ b/src/main/java/ch/njol/skript/effects/EffSort.java @@ -0,0 +1,167 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.effects; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Keywords; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.ExprInput; +import ch.njol.skript.expressions.ExprSortedList; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.InputSource; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.Variable; +import ch.njol.skript.lang.parser.ParserInstance; +import ch.njol.util.Kleenean; +import ch.njol.util.Pair; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +@Name("Sort") +@Description({ + "Sorts a list variable using either the natural ordering of the contents or the results of the given expression.", + "Be warned, this will overwrite the indices of the list variable." +}) +@Examples({ + "set {_words::*} to \"pineapple\", \"banana\", \"yoghurt\", and \"apple\"", + "sort {_words::*} # alphabetical sort", + "sort {_words::*} by length of input # shortest to longest", + "sort {_words::*} based on {tastiness::%input%} # sort based on custom value" +}) +@Since("INSERT VERSION") +@Keywords("input") +public class EffSort extends Effect implements InputSource { + + static { + Skript.registerEffect(EffSort.class, "sort %~objects% [(by|based on) <.+>]"); + if (!ParserInstance.isRegistered(InputData.class)) + ParserInstance.registerData(InputData.class, InputData::new); + } + + @Nullable + private Expression mappingExpr; + @Nullable + private String unparsedExpression; + private Variable unsortedObjects; + + private Set> dependentInputs = new HashSet<>(); + + @Nullable + private Object currentValue; + @UnknownNullability + private String currentIndex; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + if (expressions[0].isSingle() || !(expressions[0] instanceof Variable)) { + Skript.error("You can only sort list variables!"); + return false; + } + unsortedObjects = (Variable) expressions[0]; + + if (!parseResult.regexes.isEmpty()) { + unparsedExpression = parseResult.regexes.get(0).group(); + assert unparsedExpression != null; + InputData inputData = getParser().getData(InputData.class); + InputSource originalSource = inputData.getSource(); + inputData.setSource(this); + mappingExpr = new SkriptParser(unparsedExpression, SkriptParser.PARSE_EXPRESSIONS, ParseContext.DEFAULT) + .parseExpression(Object.class); + inputData.setSource(originalSource); + return mappingExpr != null && mappingExpr.isSingle(); + } + return true; + } + + @Override + protected void execute(Event event) { + Object[] sorted; + if (mappingExpr == null) { + try { + sorted = unsortedObjects.stream(event) + .sorted(ExprSortedList::compare) + .toArray(); + } catch (IllegalArgumentException | ClassCastException e) { + return; + } + } else { + Map valueToMappedValue = new LinkedHashMap<>(); + for (Iterator> it = unsortedObjects.variablesIterator(event); it.hasNext(); ) { + Pair pair = it.next(); + currentIndex = pair.getKey(); + currentValue = pair.getValue(); + Object mappedValue = mappingExpr.getSingle(event); + if (mappedValue == null) + return; + valueToMappedValue.put(currentValue, mappedValue); + } + try { + sorted = valueToMappedValue.entrySet().stream() + .sorted(Map.Entry.comparingByValue(ExprSortedList::compare)) + .map(Map.Entry::getKey) + .toArray(); + } catch (IllegalArgumentException | ClassCastException e) { + return; + } + } + + unsortedObjects.change(event, sorted, ChangeMode.SET); + } + + @Override + public Set> getDependentInputs() { + return dependentInputs; + } + + @Override + public @Nullable Object getCurrentValue() { + return currentValue; + } + + @Override + public boolean hasIndices() { + return true; + } + + @Override + public @UnknownNullability String getCurrentIndex() { + return currentIndex; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "sort" + unsortedObjects.toString(event, debug) + + (mappingExpr == null ? "" : " by " + mappingExpr.toString(event, debug)); + } + +} diff --git a/src/main/java/ch/njol/skript/expressions/ExprFilter.java b/src/main/java/ch/njol/skript/expressions/ExprFilter.java index 239b720e418..a9747bbc3fc 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprFilter.java +++ b/src/main/java/ch/njol/skript/expressions/ExprFilter.java @@ -27,83 +27,97 @@ import ch.njol.skript.lang.Condition; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; -import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.InputSource; import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.skript.lang.Variable; +import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; import org.skriptlang.skript.lang.converter.Converters; import ch.njol.skript.util.LiteralUtils; -import ch.njol.skript.util.Utils; import ch.njol.util.Kleenean; -import ch.njol.util.coll.iterator.ArrayIterator; import com.google.common.collect.Iterators; import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; -import java.lang.reflect.Array; -import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; -import java.util.List; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.regex.Pattern; +import java.util.stream.StreamSupport; @Name("Filter") -@Description("Filters a list based on a condition. " + - "For example, if you ran 'broadcast \"something\" and \"something else\" where [string input is \"something\"]', " + - "only \"something\" would be broadcast as it is the only string that matched the condition.") +@Description({ + "Filters a list based on a condition. ", + "For example, if you ran 'broadcast \"something\" and \"something else\" where [string input is \"something\"]', ", + "only \"something\" would be broadcast as it is the only string that matched the condition." +}) @Examples("send \"congrats on being staff!\" to all players where [player input has permission \"staff\"]") @Since("2.2-dev36") @SuppressWarnings({"null", "unchecked"}) -public class ExprFilter extends SimpleExpression { - - @Nullable - private static ExprFilter parsing; +public class ExprFilter extends SimpleExpression implements InputSource { static { Skript.registerExpression(ExprFilter.class, Object.class, ExpressionType.COMBINED, "%objects% (where|that match) \\[<.+>\\]"); + if (!ParserInstance.isRegistered(InputData.class)) + ParserInstance.registerData(InputData.class, InputData::new); } - private Object current; - private List> children = new ArrayList<>(); - private Condition condition; - private String rawCond; - private Expression objects; + private Condition filterCondition; + private String unparsedCondition; + private Expression unfilteredObjects; + private Set> dependentInputs = new HashSet<>(); @Nullable - public static ExprFilter getParsing() { - return parsing; - } + private Object currentFilterValue; + @Nullable + private String currentFilterIndex; @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - try { - parsing = this; - objects = LiteralUtils.defendExpression(exprs[0]); - if (objects.isSingle()) - return false; - rawCond = parseResult.regexes.get(0).group(); - condition = Condition.parse(rawCond, "Can't understand this condition: " + rawCond); - } finally { - parsing = null; - } - return condition != null && LiteralUtils.canInitSafely(objects); + unfilteredObjects = LiteralUtils.defendExpression(exprs[0]); + if (unfilteredObjects.isSingle() || !LiteralUtils.canInitSafely(unfilteredObjects)) + return false; + unparsedCondition = parseResult.regexes.get(0).group(); + InputData inputData = getParser().getData(InputData.class); + InputSource originalSource = inputData.getSource(); + inputData.setSource(this); + filterCondition = Condition.parse(unparsedCondition, "Can't understand this condition: " + unparsedCondition); + inputData.setSource(originalSource); + return filterCondition != null; } - @NonNull + @NotNull @Override public Iterator iterator(Event event) { - Iterator objIterator = this.objects.iterator(event); - if (objIterator == null) + if (unfilteredObjects instanceof Variable) { + Iterator> variableIterator = ((Variable) unfilteredObjects).variablesIterator(event); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(variableIterator, Spliterator.ORDERED), false) + .filter(pair -> { + currentFilterValue = pair.getValue(); + currentFilterIndex = pair.getKey(); + return filterCondition.check(event); + }) + .map(Pair::getValue) + .iterator(); + } + + // clear current index just to be safe + currentFilterIndex = null; + + Iterator unfilteredObjectIterator = unfilteredObjects.iterator(event); + if (unfilteredObjectIterator == null) return Collections.emptyIterator(); - try { - return Iterators.filter(objIterator, object -> { - current = object; - return condition.check(event); - }); - } finally { - current = null; - } + return Iterators.filter(unfilteredObjectIterator, candidateObject -> { + currentFilterValue = candidateObject; + return filterCondition.check(event); + }); } @Override @@ -115,148 +129,64 @@ protected Object[] get(Event event) { } } - public Object getCurrent() { - return current; - } - - private void addChild(ExprInput child) { - children.add(child); - } - - private void removeChild(ExprInput child) { - children.remove(child); + @Override + public boolean isSingle() { + return false; } @Override public Class getReturnType() { - return objects.getReturnType(); + return unfilteredObjects.getReturnType(); } - @Override - public boolean isSingle() { - return objects.isSingle(); - } @Override public String toString(Event event, boolean debug) { - return String.format("%s where [%s]", objects.toString(event, debug), rawCond); + return unfilteredObjects.toString(event, debug) + " that match [" + unparsedCondition + "]"; } - @Override - public boolean isLoopOf(String s) { - for (ExprInput child : children) { // if they used player input, let's assume loop-player is valid - if (child.getClassInfo() == null || child.getClassInfo().getUserInputPatterns() == null) - continue; + private boolean matchesAnySpecifiedTypes(String candidateString) { + for (ExprInput dependentInput : dependentInputs) { + ClassInfo specifiedType = dependentInput.getSpecifiedType(); + if (specifiedType == null) + return false; + Pattern[] specifiedTypePatterns = specifiedType.getUserInputPatterns(); + if (specifiedTypePatterns == null) + return false; - for (Pattern pattern : child.getClassInfo().getUserInputPatterns()) { - if (pattern.matcher(s).matches()) + for (Pattern typePattern : specifiedTypePatterns) { + if (typePattern.matcher(candidateString).matches()) { return true; + } } } - return objects.isLoopOf(s); // nothing matched, so we'll rely on the object expression's logic + return false; } - @Name("Filter Input") - @Description("Represents the input in a filter expression. " + - "For example, if you ran 'broadcast \"something\" and \"something else\" where [input is \"something\"]" + - "the condition would be checked twice, using \"something\" and \"something else\" as the inputs.") - @Examples("send \"congrats on being staff!\" to all players where [input has permission \"staff\"]") - @Since("2.2-dev36") - public static class ExprInput extends SimpleExpression { - - static { - Skript.registerExpression(ExprInput.class, Object.class, ExpressionType.COMBINED, - "input", - "%*classinfo% input" - ); - } - - @Nullable - private final ExprInput source; - private final Class[] types; - private final Class superType; - @SuppressWarnings("NotNullFieldNotInitialized") - private ExprFilter parent; - @Nullable - private ClassInfo inputType; - - public ExprInput() { - this(null, (Class) Object.class); - } - - public ExprInput(@Nullable ExprInput source, Class... types) { - this.source = source; - if (source != null) { - this.parent = source.parent; - this.inputType = source.inputType; - parent.removeChild(source); - parent.addChild(this); - } - - this.types = types; - this.superType = (Class) Utils.getSuperType(types); - } - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - parent = ExprFilter.getParsing(); - - if (parent == null) - return false; - - parent.addChild(this); - inputType = matchedPattern == 0 ? null : ((Literal>) exprs[0]).getSingle(); - return true; - } - - @Override - protected T[] get(Event event) { - Object current = parent.getCurrent(); - if (inputType != null && !inputType.getC().isInstance(current)) { - return null; - } - - try { - return Converters.convert(new Object[]{current}, types, superType); - } catch (ClassCastException e1) { - return (T[]) Array.newInstance(superType, 0); - } - } - - public void setParent(ExprFilter parent) { - this.parent = parent; - } - - @Override - public Expression getConvertedExpression(Class... to) { - return new ExprInput<>(this, to); - } - @Override - public Expression getSource() { - return source == null ? this : source; - } - - @Override - public Class getReturnType() { - return superType; - } + @Override + public boolean isLoopOf(String candidateString) { + return unfilteredObjects.isLoopOf(candidateString) || matchesAnySpecifiedTypes(candidateString); + } - @Nullable - private ClassInfo getClassInfo() { - return inputType; - } + public Set> getDependentInputs() { + return dependentInputs; + } - @Override - public boolean isSingle() { - return true; - } + @Nullable + public Object getCurrentValue() { + return currentFilterValue; + } - @Override - public String toString(Event event, boolean debug) { - return inputType == null ? "input" : inputType.getCodeName() + " input"; - } + @Override + public boolean hasIndices() { + return unfilteredObjects instanceof Variable; + } + @Override + @UnknownNullability + public String getCurrentIndex() { + return currentFilterIndex; } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprInput.java b/src/main/java/ch/njol/skript/expressions/ExprInput.java new file mode 100644 index 00000000000..334fc79cd93 --- /dev/null +++ b/src/main/java/ch/njol/skript/expressions/ExprInput.java @@ -0,0 +1,170 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.expressions; + +import ch.njol.skript.Skript; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.ExpressionType; +import ch.njol.skript.lang.InputSource; +import ch.njol.skript.lang.InputSource.InputData; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.util.SimpleExpression; +import ch.njol.skript.registrations.DefaultClasses; +import ch.njol.skript.util.ClassInfoReference; +import ch.njol.skript.util.Utils; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.eclipse.jdt.annotation.Nullable; +import org.skriptlang.skript.lang.converter.Converters; + +import java.lang.reflect.Array; +import java.util.Set; + +@Name("Input") +@Description({ + "Represents the input in a filter expression or sort effect.", + "For example, if you ran 'broadcast \"something\" and \"something else\" where [input is \"something\"]", + "the condition would be checked twice, using \"something\" and \"something else\" as the inputs.", + "The 'input index' pattern can be used when acting on a variable to access the index of the input." +}) +@Examples({ + "send \"congrats on being staff!\" to all players where [input has permission \"staff\"]", + "sort {_list::*} based on length of input index" +}) +@Since("2.2-dev36, INSERT_VERSION (input index)") +public class ExprInput extends SimpleExpression { + + static { + Skript.registerExpression(ExprInput.class, Object.class, ExpressionType.COMBINED, + "input", + "%*classinfo% input", + "input index" + ); + } + + @Nullable + private final ExprInput source; + private final Class[] types; + private final Class superType; + + private InputSource inputSource; + + @Nullable + private ClassInfo specifiedType; + private boolean isIndex = false; + + public ExprInput() { + this(null, (Class) Object.class); + } + + public ExprInput(@Nullable ExprInput source, Class... types) { + this.source = source; + if (source != null) { + isIndex = source.isIndex; + specifiedType = source.specifiedType; + inputSource = source.inputSource; + Set> dependentInputs = inputSource.getDependentInputs(); + dependentInputs.remove(this.source); + dependentInputs.add(this); + } + this.types = types; + this.superType = (Class) Utils.getSuperType(types); + } + + @Override + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + inputSource = getParser().getData(InputData.class).getSource(); + if (inputSource == null) + return false; + switch (matchedPattern) { + case 1: + ClassInfoReference classInfoReference = ((Literal) ClassInfoReference.wrap((Expression>) exprs[0])).getSingle(); + if (classInfoReference.isPlural().isTrue()) { + Skript.error("An input can only be a single value! Please use a singular type (for example: players input -> player input)."); + return false; + } + specifiedType = classInfoReference.getClassInfo(); + break; + case 2: + if (!inputSource.hasIndices()) { + Skript.error("You cannot use 'input index' on lists without indices!"); + return false; + } + specifiedType = DefaultClasses.STRING; + isIndex = true; + break; + default: + specifiedType = null; + } + return true; + } + + @Override + protected T[] get(Event event) { + Object currentValue = isIndex ? inputSource.getCurrentIndex() : inputSource.getCurrentValue(); + if (currentValue == null || (specifiedType != null && !specifiedType.getC().isInstance(currentValue))) + return (T[]) Array.newInstance(superType, 0); + + try { + return Converters.convert(new Object[]{currentValue}, types, superType); + } catch (ClassCastException exception) { + return (T[]) Array.newInstance(superType, 0); + } + } + + @Override + public Expression getConvertedExpression(Class... to) { + return new ExprInput<>(this, to); + } + + @Override + public Expression getSource() { + return source == null ? this : source; + } + + @Override + public boolean isSingle() { + return true; + } + + @Override + public Class getReturnType() { + return superType; + } + + @Nullable + public ClassInfo getSpecifiedType() { + return specifiedType; + } + + + @Override + public String toString(Event event, boolean debug) { + if (isIndex) + return "input index"; + return specifiedType == null ? "input" : specifiedType.getCodeName() + " input"; + } + +} diff --git a/src/main/java/ch/njol/skript/lang/InputSource.java b/src/main/java/ch/njol/skript/lang/InputSource.java new file mode 100644 index 00000000000..df541a0e40f --- /dev/null +++ b/src/main/java/ch/njol/skript/lang/InputSource.java @@ -0,0 +1,101 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.lang; + +import ch.njol.skript.expressions.ExprInput; +import ch.njol.skript.lang.parser.ParserInstance; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnknownNullability; + +import java.util.Set; + +/** + * An InputSource represents a syntax that can provide a + * value for {@link ExprInput} to use. + *
+ * @see ch.njol.skript.expressions.ExprFilter + * @see ch.njol.skript.effects.EffSort + */ +public interface InputSource { + + /** + * @return A mutable {@link Set} of {@link ExprInput}s that depend on this source. + */ + Set> getDependentInputs(); + + /** + * @return The current value that {@link ExprInput} should use. + */ + @Nullable Object getCurrentValue(); + + /** + * {@link InputSource}s that can supply indices along with values should override this + * method to indicate their ability. + * + * @return Whether this source can return indices. + */ + default boolean hasIndices() { + return false; + } + + /** + * This should only be used by {@link InputSource}s that return true for {@link InputSource#hasIndices()}. + * + * @return The current value's index. + */ + default @UnknownNullability String getCurrentIndex() { + return null; + } + + /** + * A {@link ch.njol.skript.lang.parser.ParserInstance.Data} used for + * linking {@link InputSource}s and {@link ExprInput}s. + */ + class InputData extends ParserInstance.Data { + + @Nullable + private InputSource source; + + public InputData(ParserInstance parserInstance) { + super(parserInstance); + } + + /** + * {@link InputSource} should call this during init() to declare that they are the current source for future + * {@link ExprInput}s, and then reset it to its previous value once out of scope. + * + * @param source the source of information. + */ + public void setSource(@Nullable InputSource source) { + this.source = source; + } + + /** + * ExprInput should use this to get the information source, and then call + * {@link InputSource#getCurrentValue()} to get the current value of the source. + * + * @return the source of information. + */ + @Nullable + public InputSource getSource() { + return source; + } + + } +} diff --git a/src/test/skript/tests/syntaxes/effects/EffSort.sk b/src/test/skript/tests/syntaxes/effects/EffSort.sk new file mode 100644 index 00000000000..4d832cd61fa --- /dev/null +++ b/src/test/skript/tests/syntaxes/effects/EffSort.sk @@ -0,0 +1,47 @@ +test "sorting": + set {_numbers::*} to shuffled integers from 1 to 50 + sort {_numbers::*} + assert {_numbers::*} is integers from 1 to 50 with "improper sorting of numbers" + + set {_numbers::*} to shuffled integers from 1 to 5 + sort {_numbers::*} by input * 20 + 4 - 3 # linear transformations don't affect order + assert {_numbers::*} is integers from 1 to 5 with "improper custom sorting of numbers" + + set {_numbers::*} to shuffled integers from 1 to 5 + set {_pre-sort-numbers::*} to {_numbers::*} + sort {_numbers::*} by "%input%" parsed as time # map expression returns null + assert {_numbers::*} is {_pre-sort-numbers::*} with "Invalid sorting expression adjusted list" + + set {_numbers::*} to shuffled integers from 1 to 5 + set {_pre-sort-numbers::*} to {_numbers::*} + sort {_numbers::*} by {_} + assert {_numbers::*} is {_pre-sort-numbers::*} with "Invalid sorting expression adjusted list" + + set {_numbers::*} to {_} + sort {_numbers::*} by input + 3 + assert {_numbers::*} is not set with "Invalid sorting of unset list" + + set {_chars::*} to shuffled characters between "a" and "f" + sort {_chars::*} + assert {_chars::*} is characters between "a" and "f" with "improper sorting of chars" + + set {_chars::*} to shuffled characters between "a" and "f" + sort {_chars::*} based on codepoint of input + assert {_chars::*} is characters between "a" and "f" with "improper custom sorting of chars" + + set {_mixed::*} to shuffled (characters between "a" and "f", integers from 1 to 5) + set {_pre-sort-mixed::*} to {_mixed::*} + sort {_mixed::*} + assert {_mixed::*} is {_pre-sort-mixed::*} with "incomparable mixed list was adjusted" + + set {_mixed::*} to shuffled (characters between "a" and "f", integers from 1 to 5) + sort {_mixed::*} by "%input%" + assert {_mixed::*} is 1, 2, 3, 4, 5, and characters between "a" and "f" with "improper custom sorting of mixed list" + + set {_list::x} to 1 + set {_list::aa} to 2 + set {_list::bxs} to 3 + set {_list::zysa} to 4 + set {_list::aaaaa} to 5 + sort {_list::*} by length of input index + assert {_list::*} is integers from 1 to 5 with "improper custom sorting based on index" diff --git a/src/test/skript/tests/syntaxes/expressions/ExprFilter.sk b/src/test/skript/tests/syntaxes/expressions/ExprFilter.sk index b1f1738f20d..bc195b894c8 100644 --- a/src/test/skript/tests/syntaxes/expressions/ExprFilter.sk +++ b/src/test/skript/tests/syntaxes/expressions/ExprFilter.sk @@ -3,4 +3,15 @@ test "where filter": assert first element of ({_list::*} where [string input is "foo"]) is "foo" with "ExprFilter filtered incorrectly" assert {_list::*} where [number input is set] is not set with "ExprFilter provided input value when classinfo did not match" assert first element of ({_list::*} where [input is "foo"]) is "foo" with "ExprFilter filtered object input incorrectly" + assert first element of ({_list::*} where [input is "bar"]) is "bar" with "ExprFilter filtered object input incorrectly" + assert size of ({_list::*} where [input is "bar"]) is 1 with "ExprFilter filtered object input incorrectly" + assert first element of ({_list::*} where [input is "bar"]) is "bar" with "ExprFilter filtered object input incorrectly" + assert size of ({_list::*} where [input is "bar"]) is 1 with "ExprFilter filtered object input incorrectly" + assert first element of ({_list::*} where [input is "foobar"]) is "foobar" with "ExprFilter filtered object input incorrectly" + assert size of ({_list::*} where [input is "foobar"]) is 1 with "ExprFilter filtered object input incorrectly" + assert size of ({_list::*} where [input is "foo" or "bar"]) is 2 with "ExprFilter filtered object input incorrectly" + assert size of ({_list::*} where [input is set]) is 3 with "ExprFilter filtered object input incorrectly" assert {_list::*} where [false is true] is not set with "ExprFilter returned objects with false condition" + assert ({_list::*} where [input is (("foo" and "bar") where [input is "bar"])]) is "bar" with "Failed filter with filter within condition" + assert (({_list::*} where [input is "foo"]) where [input is "foo"]) is "foo" with "Failed chained filters" + assert {_list::*} where [input index is "2" or "3"] is "bar" and "foobar" with "Failed input index filter" From 27094a6581155d3868f101c900eb12dc4777d4b4 Mon Sep 17 00:00:00 2001 From: Mwexim <38397639+Mwexim@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:52:30 +0200 Subject: [PATCH 36/48] ExprYawPitch: support for entities, better description (#5298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 💡 ExprYawPitch: added support for entities, made description more clear, made code consistent * 💡 ExprYawPitch * 💡 ExprYawPitch: apply suggestion Co-authored-by: Ayham Al Ali * 💡 ExprYawPitch: apply suggestion Co-authored-by: Ayham Al Ali * 💡 ExprYawPitch: apply suggestion Co-authored-by: Ayham Al Ali * 💡 ExprYawPitch: apply other suggestions * Update src/main/java/ch/njol/skript/expressions/ExprYawPitch.java * Revert changes to pitch/yaw meaning * Update ExprYawPitch.java * Float return! --------- Co-authored-by: Mwexim Co-authored-by: Ayham Al Ali Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Moderocky --- .../njol/skript/expressions/ExprYawPitch.java | 201 ++++++++++++------ 1 file changed, 137 insertions(+), 64 deletions(-) diff --git a/src/main/java/ch/njol/skript/expressions/ExprYawPitch.java b/src/main/java/ch/njol/skript/expressions/ExprYawPitch.java index cbbc3ba085d..6aca873d614 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprYawPitch.java +++ b/src/main/java/ch/njol/skript/expressions/ExprYawPitch.java @@ -18,8 +18,13 @@ */ package ch.njol.skript.expressions; +import ch.njol.skript.ServerPlatform; +import ch.njol.skript.Skript; +import ch.njol.skript.doc.RequiredPlugins; import ch.njol.util.VectorMath; import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.event.Event; import ch.njol.skript.classes.Changer.ChangeMode; @@ -33,125 +38,193 @@ import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.Nullable; @Name("Yaw / Pitch") -@Description("The yaw or pitch of a location or vector.") -@Examples({"log \"%player%: %location of player%, %player's yaw%, %player's pitch%\" to \"playerlocs.log\"", - "set {_yaw} to yaw of player", - "set {_p} to pitch of target entity"}) -@Since("2.0, 2.2-dev28 (vector yaw/pitch)") -public class ExprYawPitch extends SimplePropertyExpression { +@Description({ + "The yaw or pitch of a location or vector.", + "A yaw of 0 or 360 represents the positive z direction. Adding a positive number to the yaw of a player will rotate it clockwise.", + "A pitch of 90 represents the negative y direction, or downward facing. A pitch of -90 represents upward facing. Adding a positive number to the pitch will rotate the direction downwards.", + "Only Paper 1.19+ users may directly change the yaw/pitch of players." +}) +@Examples({ + "log \"%player%: %location of player%, %player's yaw%, %player's pitch%\" to \"playerlocs.log\"", + "set {_yaw} to yaw of player", + "set {_p} to pitch of target entity", + "set pitch of player to -90 # Makes the player look upwards, Paper 1.19+ only", + "add 180 to yaw of target of player # Makes the target look behind themselves" +}) +@Since("2.0, 2.2-dev28 (vector yaw/pitch), INSERT VERSION (entity changers)") +@RequiredPlugins("Paper 1.19+ (player changers)") +public class ExprYawPitch extends SimplePropertyExpression { static { - register(ExprYawPitch.class, Number.class, "(0¦yaw|1¦pitch)", "locations/vectors"); + register(ExprYawPitch.class, Float.class, "(:yaw|pitch)", "entities/locations/vectors"); } + // For non-Paper versions lower than 1.19, changing the rotation of an entity is not supported for players. + private static final boolean SUPPORTS_PLAYERS = Skript.isRunningMinecraft(1, 19) && Skript.getServerPlatform() == ServerPlatform.BUKKIT_PAPER; + private boolean usesYaw; @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - usesYaw = parseResult.mark == 0; + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + usesYaw = parseResult.hasTag("yaw"); return super.init(exprs, matchedPattern, isDelayed, parseResult); } @Override - public Number convert(final Object object) { - if (object instanceof Location) { - Location l = ((Location) object); - return usesYaw ? convertToPositive(l.getYaw()) : l.getPitch(); + public Float convert(Object object) { + if (object instanceof Entity) { + Location location = ((Entity) object).getLocation(); + return usesYaw + ? normalizeYaw(location.getYaw()) + : location.getPitch(); + } else if (object instanceof Location) { + Location location = (Location) object; + return usesYaw + ? normalizeYaw(location.getYaw()) + : location.getPitch(); } else if (object instanceof Vector) { - Vector vector = ((Vector) object); - if (usesYaw) - return VectorMath.skriptYaw(VectorMath.getYaw(vector)); - return VectorMath.skriptPitch(VectorMath.getPitch(vector)); + Vector vector = (Vector) object; + return usesYaw + ? VectorMath.skriptYaw((VectorMath.getYaw(vector))) + : VectorMath.skriptPitch(VectorMath.getPitch(vector)); } return null; } - @SuppressWarnings({"null"}) @Override - public Class[] acceptChange(final ChangeMode mode) { - if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) - return CollectionUtils.array(Number.class); - return null; + public Class[] acceptChange(ChangeMode mode) { + if (Player.class.isAssignableFrom(getExpr().getReturnType()) && !SUPPORTS_PLAYERS) + return null; + + switch (mode) { + case SET: + case ADD: + case REMOVE: + return CollectionUtils.array(Number.class); + case RESET: + return new Class[0]; + default: + return null; + } } - @SuppressWarnings("null") @Override - public void change(Event e, Object[] delta, ChangeMode mode) { - if (delta == null) + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { + if (delta == null && mode != ChangeMode.RESET) return; float value = ((Number) delta[0]).floatValue(); - for (Object single : getExpr().getArray(e)) { - if (single instanceof Location) { - changeLocation(((Location) single), value, mode); - } else if (single instanceof Vector) { - changeVector(((Vector) single), value, mode); + for (Object object : getExpr().getArray(event)) { + if (object instanceof Player && !SUPPORTS_PLAYERS) + continue; + + if (object instanceof Entity) { + changeForEntity((Entity) object, value, mode); + } else if (object instanceof Location) { + changeForLocation(((Location) object), value, mode); + } else if (object instanceof Vector) { + changeForVector(((Vector) object), value, mode); } } } - private void changeLocation(Location l, float value, ChangeMode mode) { + private void changeForEntity(Entity entity, float value, ChangeMode mode) { + Location location = entity.getLocation(); switch (mode) { case SET: - if (usesYaw) - l.setYaw(convertToPositive(value)); - else - l.setPitch(value); + if (usesYaw) { + entity.setRotation(value, location.getPitch()); + } else { + entity.setRotation(location.getYaw(), value); + } break; + case REMOVE: + value = -value; case ADD: - if (usesYaw) - l.setYaw(convertToPositive(l.getYaw()) + value); - else - l.setPitch(l.getPitch() + value); + if (usesYaw) { + entity.setRotation(location.getYaw() + value, location.getPitch()); + } else { + // Subtracting because of Minecraft's upside-down pitch. + entity.setRotation(location.getYaw(), location.getPitch() - value); + } + break; + case RESET: + if (usesYaw) { + entity.setRotation(0, location.getPitch()); + } else { + entity.setRotation(location.getYaw(), 0); + } + break; + default: + break; + } + } + + private void changeForLocation(Location location, float value, ChangeMode mode) { + switch (mode) { + case SET: + if (usesYaw) { + location.setYaw(value); + } else { + location.setPitch(value); + } break; case REMOVE: - if (usesYaw) - l.setYaw(convertToPositive(l.getYaw()) - value); - else - l.setPitch(l.getPitch() - value); + value = -value; + case ADD: + if (usesYaw) { + location.setYaw(location.getYaw() + value); + } else { + // Subtracting because of Minecraft's upside-down pitch. + location.setPitch(location.getPitch() - value); + } break; + case RESET: + if (usesYaw) { + location.setYaw(0); + } else { + location.setPitch(0); + } default: break; } } - private void changeVector(Vector vector, float n, ChangeMode mode) { + private void changeForVector(Vector vector, float value, ChangeMode mode) { float yaw = VectorMath.getYaw(vector); float pitch = VectorMath.getPitch(vector); switch (mode) { case REMOVE: - n = -n; - //$FALL-THROUGH$ + value = -value; + // $FALL-THROUGH$ case ADD: - if (usesYaw) - yaw += n; - else - pitch -= n; // Negative because of Minecraft's / Skript's upside down pitch - Vector newVector = VectorMath.fromYawAndPitch(yaw, pitch).multiply(vector.length()); - VectorMath.copyVector(vector, newVector); + if (usesYaw) { + yaw += value; + } else { + // Subtracting because of Minecraft's upside-down pitch. + pitch -= value; + } break; case SET: if (usesYaw) - yaw = VectorMath.fromSkriptYaw(n); + yaw = VectorMath.fromSkriptYaw(value); else - pitch = VectorMath.fromSkriptPitch(n); - newVector = VectorMath.fromYawAndPitch(yaw, pitch).multiply(vector.length()); - VectorMath.copyVector(vector, newVector); + pitch = VectorMath.fromSkriptPitch(value); } + Vector newVector = VectorMath.fromYawAndPitch(yaw, pitch).multiply(vector.length()); + VectorMath.copyVector(vector, newVector); } - - //Some random method decided to use for converting to positive values. - public float convertToPositive(float f) { - if (f != 0 && f * -1 == Math.abs(f)) - return 360 + f; - return f; + private static float normalizeYaw(float yaw) { + yaw = Location.normalizeYaw(yaw); + return yaw < 0 ? yaw + 360 : yaw; } @Override - public Class getReturnType() { - return Number.class; + public Class getReturnType() { + return Float.class; } @Override From 55d7fdc53faa008980d18e01916087c5fa494b4d Mon Sep 17 00:00:00 2001 From: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Date: Mon, 1 Jul 2024 22:33:37 +0300 Subject: [PATCH 37/48] =?UTF-8?q?=F0=9F=9A=80=20Add=20Cancellable=20sectio?= =?UTF-8?q?n=20(#5691)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Cancellable section * Correct effect name EffCancelEvent * Support EffCancelEvent extra hardcoded event * Nothing, just better logic! --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Moderocky --- .../ch/njol/skript/doc/HTMLGenerator.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/ch/njol/skript/doc/HTMLGenerator.java b/src/main/java/ch/njol/skript/doc/HTMLGenerator.java index e882057cebd..d79b1e65320 100644 --- a/src/main/java/ch/njol/skript/doc/HTMLGenerator.java +++ b/src/main/java/ch/njol/skript/doc/HTMLGenerator.java @@ -36,6 +36,9 @@ import com.google.common.collect.Lists; import com.google.common.io.Files; import org.apache.commons.lang.StringUtils; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.block.BlockCanBuildEvent; import org.eclipse.jdt.annotation.Nullable; import org.skriptlang.skript.lang.entry.EntryData; import org.skriptlang.skript.lang.entry.EntryValidator; @@ -481,6 +484,9 @@ private String generateAnnotated(String descTemp, SyntaxElementInfo info, @Nu } desc = desc.replace("${element.id}", ID); + // Cancellable + desc = handleIf(desc, "${if cancellable}", false); + // Events Events events = c.getAnnotation(Events.class); desc = handleIf(desc, "${if events}", events != null); @@ -603,6 +609,17 @@ private String generateEvent(String descTemp, SkriptEventInfo info, @Nullable String[] keywords = info.getKeywords(); desc = desc.replace("${element.keywords}", keywords == null ? "" : Joiner.on(", ").join(keywords)); + // Cancellable + boolean cancellable = false; + for (Class event : info.events) { + if (Cancellable.class.isAssignableFrom(event) || BlockCanBuildEvent.class.isAssignableFrom(event)) { + cancellable = true; // let's assume all are cancellable otherwise EffCancelEvent would do the rest in action + break; + } + } + desc = handleIf(desc, "${if cancellable}", cancellable); + desc = desc.replace("${element.cancellable}", cancellable ? "Yes" : ""); // if not cancellable the section is hidden + // Documentation ID String ID = info.getDocumentationID() != null ? info.getDocumentationID() : info.getId(); // Fix duplicated IDs @@ -719,6 +736,9 @@ private String generateClass(String descTemp, ClassInfo info, @Nullable Strin } desc = desc.replace("${element.id}", ID); + // Cancellable + desc = handleIf(desc, "${if cancellable}", false); + // Events Events events = c.getAnnotation(Events.class); desc = handleIf(desc, "${if events}", events != null); @@ -821,6 +841,9 @@ private String generateFunction(String descTemp, JavaFunction info) { // Documentation ID desc = desc.replace("${element.id}", info.getName()); + // Cancellable + desc = handleIf(desc, "${if cancellable}", false); + // Events desc = handleIf(desc, "${if events}", false); // Functions do not require events nor plugins (at time writing this) From 6d1a00ad8a267d77e44b02018378d5919da728ff Mon Sep 17 00:00:00 2001 From: NotSoDelayed <72163224+NotSoDelayed@users.noreply.github.com> Date: Tue, 2 Jul 2024 03:38:51 +0800 Subject: [PATCH 38/48] Make EffBroadcast calls BroadcastMessageEvent (#5979) * Support BroadcastMessageEvent * Clean up * Clean up * Recipient set unmodifiable * Use deprecated constructor for 1.13 users * Move version tag to javadocs * Fix version check for legacy constructor call --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Moderocky --- .../ch/njol/skript/effects/EffBroadcast.java | 80 +++++++++++++++---- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffBroadcast.java b/src/main/java/ch/njol/skript/effects/EffBroadcast.java index 5e2b8fbdefb..fd6ef3ff245 100644 --- a/src/main/java/ch/njol/skript/effects/EffBroadcast.java +++ b/src/main/java/ch/njol/skript/effects/EffBroadcast.java @@ -18,6 +18,13 @@ */ package ch.njol.skript.effects; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Examples; @@ -31,19 +38,20 @@ import ch.njol.skript.lang.VariableString; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.LiteralUtils; +import ch.njol.skript.util.SkriptColor; +import ch.njol.skript.util.Utils; import ch.njol.skript.util.chat.BungeeConverter; import ch.njol.skript.util.chat.ChatMessages; import ch.njol.util.Kleenean; +import ch.njol.util.StringUtils; import ch.njol.util.coll.CollectionUtils; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.event.Event; -import org.eclipse.jdt.annotation.Nullable; - -import java.util.ArrayList; -import java.util.List; +import org.bukkit.event.server.BroadcastMessageEvent; +import org.jetbrains.annotations.Nullable; @Name("Broadcast") @Description("Broadcasts a message to the server.") @@ -54,6 +62,8 @@ @Since("1.0, 2.6 (broadcasting objects), 2.6.1 (using advanced formatting)") public class EffBroadcast extends Effect { + private static final Pattern HEX_PATTERN = Pattern.compile("(?i)&x((?:&\\p{XDigit}){6})"); + static { Skript.registerEffect(EffBroadcast.class, "broadcast %objects% [(to|in) %-worlds%]"); } @@ -65,8 +75,8 @@ public class EffBroadcast extends Effect { @Nullable private Expression worlds; - @SuppressWarnings("unchecked") @Override + @SuppressWarnings("unchecked") public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { messageExpr = LiteralUtils.defendExpression(exprs[0]); messages = messageExpr instanceof ExpressionList ? @@ -74,37 +84,51 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye worlds = (Expression) exprs[1]; return LiteralUtils.canInitSafely(messageExpr); } - + + /** + * This effect will call {@link BroadcastMessageEvent} as of INSERT_VERSION. + */ @Override @SuppressWarnings("deprecation") - public void execute(Event e) { + public void execute(Event event) { List receivers = new ArrayList<>(); if (worlds == null) { receivers.addAll(Bukkit.getOnlinePlayers()); receivers.add(Bukkit.getConsoleSender()); } else { - for (World world : worlds.getArray(e)) + for (World world : worlds.getArray(event)) receivers.addAll(world.getPlayers()); } for (Expression message : getMessages()) { if (message instanceof VariableString) { - BaseComponent[] components = BungeeConverter.convert(((VariableString) message).getMessageComponents(e)); + if (!dispatchEvent(getRawString(event, (VariableString) message), receivers)) + continue; + BaseComponent[] components = BungeeConverter.convert(((VariableString) message).getMessageComponents(event)); receivers.forEach(receiver -> receiver.spigot().sendMessage(components)); } else if (message instanceof ExprColoured && ((ExprColoured) message).isUnsafeFormat()) { // Manually marked as trusted - for (Object realMessage : message.getArray(e)) { + for (Object realMessage : message.getArray(event)) { + if (!dispatchEvent(Utils.replaceChatStyles((String) realMessage), receivers)) + continue; BaseComponent[] components = BungeeConverter.convert(ChatMessages.parse((String) realMessage)); receivers.forEach(receiver -> receiver.spigot().sendMessage(components)); } } else { - for (Object messageObject : message.getArray(e)) { + for (Object messageObject : message.getArray(event)) { String realMessage = messageObject instanceof String ? (String) messageObject : Classes.toString(messageObject); + if (!dispatchEvent(Utils.replaceChatStyles(realMessage), receivers)) + continue; receivers.forEach(receiver -> receiver.sendMessage(realMessage)); } } } } + @Override + public String toString(@Nullable Event event, boolean debug) { + return "broadcast " + messageExpr.toString(event, debug) + (worlds == null ? "" : " to " + worlds.toString(event, debug)); + } + private Expression[] getMessages() { if (messageExpr instanceof ExpressionList && !messageExpr.getAnd()) { return new Expression[] {CollectionUtils.getRandom(messages)}; @@ -112,9 +136,35 @@ private Expression[] getMessages() { return messages; } - @Override - public String toString(@Nullable Event e, boolean debug) { - return "broadcast " + messageExpr.toString(e, debug) + (worlds == null ? "" : " to " + worlds.toString(e, debug)); + /** + * Manually calls a {@link BroadcastMessageEvent}. + * @param message the message + * @return true if the dispatched event does not get cancelled + */ + @SuppressWarnings("deprecation") + private static boolean dispatchEvent(String message, List receivers) { + Set recipients = Collections.unmodifiableSet(new HashSet<>(receivers)); + BroadcastMessageEvent broadcastEvent; + if (Skript.isRunningMinecraft(1, 14)) { + broadcastEvent = new BroadcastMessageEvent(!Bukkit.isPrimaryThread(), message, recipients); + } else { + broadcastEvent = new BroadcastMessageEvent(message, recipients); + } + Bukkit.getPluginManager().callEvent(broadcastEvent); + return !broadcastEvent.isCancelled(); } - + + @Nullable + private static String getRawString(Event event, Expression string) { + if (string instanceof VariableString) + return ((VariableString) string).toUnformattedString(event); + String rawString = string.getSingle(event); + rawString = SkriptColor.replaceColorChar(rawString); + if (rawString.toLowerCase().contains("&x")) { + rawString = StringUtils.replaceAll(rawString, HEX_PATTERN, matchResult -> + "<#" + matchResult.group(1).replace("&", "") + '>'); + } + return rawString; + } + } From 69cd4cdc771abb69b03b2e8c14cdd20c72fa5824 Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:49:55 -0600 Subject: [PATCH 39/48] Fixes JUnit before and after being called on cleanup regardless of override (#6276) Remove JUnit annotations on cleanup Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- src/main/java/ch/njol/skript/Skript.java | 4 ++++ src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index d98a51a7763..3755525edb1 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -752,6 +752,8 @@ protected void afterErrors() { TestTracker.JUnitTestFailed(test, message); Skript.exception(failure.getException(), "JUnit test '" + failure.getTestHeader() + " failed."); }); + if (SkriptJUnitTest.class.isAssignableFrom(clazz)) + ((SkriptJUnitTest) clazz.getConstructor().newInstance()).cleanup(); SkriptJUnitTest.clearJUnitTest(); } } catch (IOException e) { @@ -759,6 +761,8 @@ protected void afterErrors() { } catch (ClassNotFoundException e) { // Should be the Skript test jar gradle task. assert false : "Class 'ch.njol.skript.variables.FlatFileStorageTest' was not found."; + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + Skript.exception(e, "Failed to initalize test JUnit classes."); } if (ignored > 0) Skript.warning("There were " + ignored + " ignored test cases! This can mean they are not properly setup in order in that class!"); diff --git a/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java b/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java index 65a52f0ac1a..6988d537cff 100644 --- a/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java +++ b/src/main/java/ch/njol/skript/test/runner/SkriptJUnitTest.java @@ -27,8 +27,6 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Pig; -import org.junit.After; -import org.junit.Before; import ch.njol.skript.Skript; @@ -79,8 +77,6 @@ public static void setShutdownDelay(long delay) { /** * Override this method if your JUnit test requires block modification with delay over 1 tick. */ - @Before - @After public void cleanup() { getTestWorld().getEntities().forEach(Entity::remove); setBlock(Material.AIR); From dfaacd6e46c732f66681fddcbf2d3acc261a192c Mon Sep 17 00:00:00 2001 From: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:56:21 -0600 Subject: [PATCH 40/48] Update healing event and related expressions (#6339) * Update healing event and related expressions * Update src/main/java/ch/njol/skript/expressions/ExprHealAmount.java Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Update src/main/java/ch/njol/skript/events/EvtHealing.java Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> * Apply changes * Sync aliases with master * Add event to setTime * Update src/main/java/ch/njol/skript/events/EvtHealing.java --------- Co-authored-by: Ayham Al Ali <20037329+AyhamAl-Ali@users.noreply.github.com> Co-authored-by: Moderocky Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../skript/classes/data/BukkitClasses.java | 11 +- .../classes/data/BukkitEventValues.java | 11 ++ .../ch/njol/skript/events/EvtHealing.java | 100 ++++++++++++++++++ .../ch/njol/skript/events/SimpleEvents.java | 5 - .../skript/expressions/ExprHealAmount.java | 75 +++++++------ .../skript/expressions/ExprHealReason.java | 71 ++++--------- 6 files changed, 180 insertions(+), 93 deletions(-) create mode 100644 src/main/java/ch/njol/skript/events/EvtHealing.java diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index a54e5a54cc4..7ff72f27e69 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -1448,12 +1448,13 @@ public String toVariableNameString(FireworkEffect effect) { .since("2.4") .requiredPlugins("Minecraft 1.14 or newer")); } + Classes.registerClass(new EnumClassInfo<>(RegainReason.class, "healreason", "heal reasons") - .user("(regen|heal) (reason|cause)") - .name("Heal Reason") - .description("The heal reason in a heal event.") - .examples("") - .since("2.5")); + .user("(regen|heal) (reason|cause)") + .name("Heal Reason") + .description("The health regain reason in a heal event.") + .since("2.5")); + if (Skript.classExists("org.bukkit.entity.Cat$Type")) { ClassInfo catTypeClassInfo; if (BukkitUtils.registryExists("CAT_VARIANT")) { diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 76e098cb602..6a49566c827 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -101,6 +101,8 @@ import org.bukkit.event.entity.EntityDropItemEvent; import org.bukkit.event.entity.EntityEvent; import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; import org.bukkit.event.entity.EntityResurrectEvent; import org.bukkit.event.entity.EntityTameEvent; import org.bukkit.event.entity.EntityTransformEvent; @@ -1908,5 +1910,14 @@ public ItemStack get(InventoryMoveItemEvent event) { return event.getItem(); } }, EventValues.TIME_NOW); + + // EntityRegainHealthEvent + EventValues.registerEventValue(EntityRegainHealthEvent.class, RegainReason.class, new Getter() { + @Override + @Nullable + public RegainReason get(EntityRegainHealthEvent event) { + return event.getRegainReason(); + } + }, EventValues.TIME_NOW); } } diff --git a/src/main/java/ch/njol/skript/events/EvtHealing.java b/src/main/java/ch/njol/skript/events/EvtHealing.java new file mode 100644 index 00000000000..21dd2c2e642 --- /dev/null +++ b/src/main/java/ch/njol/skript/events/EvtHealing.java @@ -0,0 +1,100 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.events; + +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; +import org.eclipse.jdt.annotation.Nullable; + +import ch.njol.skript.Skript; +import ch.njol.skript.entity.EntityData; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptEvent; +import ch.njol.skript.lang.SkriptParser.ParseResult; + +public class EvtHealing extends SkriptEvent { + + static { + Skript.registerEvent("Heal", EvtHealing.class, EntityRegainHealthEvent.class, "heal[ing] [of %-entitydatas%] [(from|due to|by) %-healreasons%]", "%entitydatas% heal[ing] [(from|due to|by) %-healreasons%]") + .description("Called when an entity is healed, e.g. by eating (players), being fed (pets), or by the effect of a potion of healing (overworld mobs) or harm (nether mobs).") + .examples( + "on heal:", + "on player healing from a regeneration potion:", + "on healing of a zombie, cow or a wither:", + "\theal reason is healing potion", + "\tcancel event" + ) + .since("1.0, INSERT VERSION (by reason)"); + } + + @Nullable + private Literal> entityDatas; + + @Nullable + private Literal healReasons; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Literal[] args, int matchedPattern, ParseResult parser) { + entityDatas = (Literal>) args[0]; + healReasons = (Literal) args[1]; + return true; + } + + @Override + public boolean check(Event event) { + if (!(event instanceof EntityRegainHealthEvent)) + return false; + EntityRegainHealthEvent healthEvent = (EntityRegainHealthEvent) event; + if (entityDatas != null) { + Entity compare = healthEvent.getEntity(); + boolean result = false; + for (EntityData entityData : entityDatas.getAll()) { + if (entityData.isInstance(compare)) { + result = true; + break; + } + } + if (!result) + return false; + } + if (healReasons != null) { + RegainReason compare = healthEvent.getRegainReason(); + boolean result = false; + for (RegainReason healReason : healReasons.getAll()) { + if (healReason == compare) { + result = true; + break; + } + } + if (!result) + return false; + } + return true; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "heal" + (entityDatas != null ? " of " + entityDatas.toString(event, debug) : "") + + (healReasons != null ? " by " + healReasons.toString(event, debug) : ""); + } + +} diff --git a/src/main/java/ch/njol/skript/events/SimpleEvents.java b/src/main/java/ch/njol/skript/events/SimpleEvents.java index c470d68a07d..3ac8cce5e1a 100644 --- a/src/main/java/ch/njol/skript/events/SimpleEvents.java +++ b/src/main/java/ch/njol/skript/events/SimpleEvents.java @@ -57,7 +57,6 @@ import org.bukkit.event.entity.EntityMountEvent; import org.bukkit.event.entity.EntityPortalEnterEvent; import org.bukkit.event.entity.EntityPortalEvent; -import org.bukkit.event.entity.EntityRegainHealthEvent; import org.bukkit.event.entity.EntityResurrectEvent; import org.bukkit.event.entity.EntityTameEvent; import org.bukkit.event.entity.EntityToggleGlideEvent; @@ -209,10 +208,6 @@ public class SimpleEvents { .description("Called when an entity enters a nether portal or an end portal. Please note that this event will be fired many times for a nether portal.") .examples("on portal enter:") .since("1.0"); - Skript.registerEvent("Heal", SimpleEvent.class, EntityRegainHealthEvent.class, "heal[ing]") - .description("Called when an entity is healed, e.g. by eating (players), being fed (pets), or by the effect of a potion of healing (overworld mobs) or harm (nether mobs).") - .examples("on heal:") - .since("1.0"); Skript.registerEvent("Tame", SimpleEvent.class, EntityTameEvent.class, "[entity] tam(e|ing)") .description("Called when a player tames a wolf or ocelot. Can be cancelled to prevent the entity from being tamed.") .examples("on tame:") diff --git a/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java b/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java index 9e74f6aa68e..ae905e96fd7 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHealAmount.java @@ -25,6 +25,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.Changer; +import ch.njol.skript.classes.Changer.ChangeMode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Events; import ch.njol.skript.doc.Examples; @@ -32,49 +33,49 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.ExpressionType; -import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.lang.util.SimpleExpression; -import ch.njol.skript.log.ErrorQuality; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; @Name("Heal Amount") -@Description("The amount of health healed in a healing event.") -@Examples({"increase heal amount by 2", - "remove 0.5 from heal amount"}) -@Since("2.5.1") +@Description("The amount of health healed in a heal event.") +@Examples({ + "on player healing:", + "\tincrease the heal amount by 2", + "\tremove 0.5 from the healing amount" +}) @Events("heal") -public class ExprHealAmount extends SimpleExpression { - +@Since("2.5.1") +public class ExprHealAmount extends SimpleExpression { + static { - Skript.registerExpression(ExprHealAmount.class, Number.class, ExpressionType.SIMPLE, "[the] heal amount"); + Skript.registerExpression(ExprHealAmount.class, Double.class, ExpressionType.SIMPLE, "[the] heal[ing] amount"); } - - @SuppressWarnings("null") + private Kleenean delay; - + @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + 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", ErrorQuality.SEMANTIC_ERROR); + Skript.error("The expression 'heal amount' may only be used in a healing event"); return false; } delay = isDelayed; return true; } - + @Nullable @Override - protected Number[] get(Event e) { - if (!(e instanceof EntityRegainHealthEvent)) + protected Double[] get(Event event) { + if (!(event instanceof EntityRegainHealthEvent)) return null; - - return new Number[]{((EntityRegainHealthEvent) e).getAmount()}; + return new Double[]{((EntityRegainHealthEvent) event).getAmount()}; } - + @Nullable @Override - public Class[] acceptChange(Changer.ChangeMode mode) { + public Class[] acceptChange(ChangeMode mode) { if (delay != Kleenean.FALSE) { Skript.error("The heal amount cannot be changed after the event has already passed"); return null; @@ -83,42 +84,48 @@ public Class[] acceptChange(Changer.ChangeMode mode) { return null; return CollectionUtils.array(Number.class); } - + @Override - public void change(Event e, @Nullable Object[] delta, Changer.ChangeMode mode) { - if (!(e instanceof EntityRegainHealthEvent)) + public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { + if (!(event instanceof EntityRegainHealthEvent)) return; + EntityRegainHealthEvent healthEvent = (EntityRegainHealthEvent) event; double value = delta == null ? 0 : ((Number) delta[0]).doubleValue(); switch (mode) { case SET: case DELETE: - ((EntityRegainHealthEvent) e).setAmount(value); + healthEvent.setAmount(value); break; case ADD: - ((EntityRegainHealthEvent) e).setAmount(((EntityRegainHealthEvent) e).getAmount() + value); + healthEvent.setAmount(healthEvent.getAmount() + value); break; case REMOVE: - ((EntityRegainHealthEvent) e).setAmount(((EntityRegainHealthEvent) e).getAmount() - value); + healthEvent.setAmount(healthEvent.getAmount() - value); break; default: break; } } - + + @Override + public boolean setTime(int time) { + return super.setTime(time, EntityRegainHealthEvent.class); + } + @Override public boolean isSingle() { return true; } - + @Override - public Class getReturnType() { - return Number.class; + public Class getReturnType() { + return Double.class; } - + @Override - public String toString(@Nullable Event e, boolean debug) { + public String toString(@Nullable Event event, boolean debug) { return "heal amount"; } - + } diff --git a/src/main/java/ch/njol/skript/expressions/ExprHealReason.java b/src/main/java/ch/njol/skript/expressions/ExprHealReason.java index 12dc0270976..330ec4ed4d7 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprHealReason.java +++ b/src/main/java/ch/njol/skript/expressions/ExprHealReason.java @@ -18,67 +18,40 @@ */ package ch.njol.skript.expressions; -import org.bukkit.event.Event; -import org.bukkit.event.entity.EntityRegainHealthEvent; -import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; -import org.eclipse.jdt.annotation.Nullable; - -import ch.njol.skript.Skript; import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Events; import ch.njol.skript.doc.Examples; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; -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.skript.log.ErrorQuality; -import ch.njol.util.Kleenean; +import ch.njol.skript.expressions.base.EventValueExpression; +import ch.njol.skript.registrations.EventValues; +import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; @Name("Heal Reason") -@Description("The heal reason of a heal event. Please click on the link for more information.") -@Examples({"on heal:", - "\tif heal reason = satiated:", - "\t\tsend \"You ate enough food and gained health back!\" to player"}) +@Description("The heal reason of a heal event.") +@Examples({ + "on heal:", + "\theal reason is satiated", + "\tsend \"You ate enough food and gained full health back!\"" +}) +@Events("heal") @Since("2.5") -public class ExprHealReason extends SimpleExpression { - +public class ExprHealReason extends EventValueExpression { + static { - Skript.registerExpression(ExprHealReason.class, RegainReason.class, ExpressionType.SIMPLE, "(regen|health regain|heal) (reason|cause)"); + register(ExprHealReason.class, RegainReason.class, "(regen|health regain|heal[ing]) (reason|cause)"); } - - @Override - public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { - if (!getParser().isCurrentEvent(EntityRegainHealthEvent.class)) { - Skript.error("Heal reason can only be used in an on heal event", ErrorQuality.SEMANTIC_ERROR); - return false; - } - return true; - } - - @Nullable - @Override - protected RegainReason[] get(Event e) { - if (!(e instanceof EntityRegainHealthEvent)) - return null; - return new RegainReason[]{((EntityRegainHealthEvent) e).getRegainReason()}; + public ExprHealReason() { + super(RegainReason.class); } - - @Override - public boolean isSingle() { - return true; - } - - @Override - public Class getReturnType() { - return RegainReason.class; - } - + @Override - public String toString(@Nullable Event e, boolean debug) { - return "heal reason"; + public boolean setTime(int time) { + if (time == EventValues.TIME_FUTURE) + return false; + return super.setTime(time); } - + } From befa81f902ddf5cb22bcb2c7bcf7dd9bcf2a4896 Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 13:04:49 -0700 Subject: [PATCH 41/48] RegistryParser - strip out minecraft namespaces from docs (#6844) * RegistryParser - strip out minecraft namespaces from docs * RegistryParser - Stream#toList() not available on older java versions --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/classes/data/BukkitClasses.java | 9 ++++++--- .../ch/njol/skript/classes/registry/RegistryParser.java | 6 ++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index 7ff72f27e69..b48c69149d7 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -990,7 +990,8 @@ public String toVariableNameString(final ItemStack i) { Classes.registerClass(biomeClassInfo .user("biomes?") .name("Biome") - .description("All possible biomes Minecraft uses to generate a world.") + .description("All possible biomes Minecraft uses to generate a world.", + "NOTE: Minecraft namespaces are supported, ex: 'minecraft:basalt_deltas'.") .examples("biome at the player is desert") .since("1.4.4") .after("damagecause")); @@ -1465,7 +1466,8 @@ public String toVariableNameString(FireworkEffect effect) { Classes.registerClass(catTypeClassInfo .user("cat ?(type|race)s?") .name("Cat Type") - .description("Represents the race/type of a cat entity.") + .description("Represents the race/type of a cat entity.", + "NOTE: Minecraft namespaces are supported, ex: 'minecraft:british_shorthair'.") .since("2.4") .requiredPlugins("Minecraft 1.14 or newer") .documentationId("CatType")); @@ -1533,7 +1535,8 @@ public String toVariableNameString(EnchantmentOffer eo) { .user("attribute ?types?") .name("Attribute Type") .description("Represents the type of an attribute. Note that this type does not contain any numerical values." - + "See attribute types for more info.") + + "See attribute types for more info.", + "NOTE: Minecraft namespaces are supported, ex: 'minecraft:generic.attack_damage'.") .since("2.5")); Classes.registerClass(new EnumClassInfo<>(Environment.class, "environment", "environments") diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java b/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java index 51cb4637434..dc9bf9f00b7 100644 --- a/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java +++ b/src/main/java/ch/njol/skript/classes/registry/RegistryParser.java @@ -30,12 +30,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.stream.Collectors; /** * A parser based on a {@link Registry} used to parse data from a string or turn data into a string. @@ -147,8 +146,7 @@ private void refresh() { * Note that some entries may represent the same registry object. */ public String getAllNames() { - List strings = new ArrayList<>(parseMap.keySet()); - Collections.sort(strings); + List strings = parseMap.keySet().stream().filter(s -> !s.startsWith("minecraft:")).sorted().collect(Collectors.toList()); return StringUtils.join(strings, ", "); } From 3c0119f4351dae51b02936d37e02625769451d0c Mon Sep 17 00:00:00 2001 From: cheeezburga <47320303+cheeezburga@users.noreply.github.com> Date: Tue, 2 Jul 2024 06:24:03 +1000 Subject: [PATCH 42/48] Adds tooltip syntax (#6712) * Adds effect, condition, and tests for tooltip-related syntax * Removed star imports * Apply suggestions from code review Co-authored-by: Patrick Miller Co-authored-by: Fusezion * Suggestions from Pickle and Lyric - Changed the parse marks/tags in the effect and patterns for the condition - Added some info to the condition's description * Apply suggestions from code review Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> * Some more suggestions - Changed the condition to use setNegated and isNegated, and use Sovde's pattern changes - Changed the effect description and fixed to work with Sovde's pattern changes * Small fix * Apply suggestions from code review Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Update src/main/java/ch/njol/skript/conditions/CondTooltip.java Co-authored-by: Patrick Miller * Adds "reveal" as option in syntax --------- Co-authored-by: Patrick Miller Co-authored-by: Fusezion Co-authored-by: Moderocky Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> --- .../njol/skript/conditions/CondTooltip.java | 87 +++++++++++++++++ .../ch/njol/skript/effects/EffTooltip.java | 95 +++++++++++++++++++ .../tests/syntaxes/conditions/CondTooltip.sk | 15 +++ 3 files changed, 197 insertions(+) create mode 100644 src/main/java/ch/njol/skript/conditions/CondTooltip.java create mode 100644 src/main/java/ch/njol/skript/effects/EffTooltip.java create mode 100644 src/test/skript/tests/syntaxes/conditions/CondTooltip.sk diff --git a/src/main/java/ch/njol/skript/conditions/CondTooltip.java b/src/main/java/ch/njol/skript/conditions/CondTooltip.java new file mode 100644 index 00000000000..dc7c795ca76 --- /dev/null +++ b/src/main/java/ch/njol/skript/conditions/CondTooltip.java @@ -0,0 +1,87 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.conditions; + +import ch.njol.skript.Skript; +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Condition; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.meta.ItemMeta; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Has Item Tooltips") +@Description({ + "Whether the entire or additional tooltip of an item is shown or hidden.", + "The 'entire tooltip' is what shows to the player when they hover an item (i.e. name, lore, etc.).", + "The 'additional tooltip' hides certain information from certain items (potions, maps, books, fireworks, and banners)." +}) +@Examples({ + "send true if entire tooltip of player's tool is shown", + "if additional tooltip of {_item} is hidden:" +}) +@RequiredPlugins("Spigot 1.20.5+") +@Since("INSERT VERSION") +public class CondTooltip extends Condition { + + static { + if (Skript.methodExists(ItemMeta.class, "isHideTooltip")) {// this method was added in the same version as the additional tooltip item flag + Skript.registerCondition(CondTooltip.class, + "[the] [entire|:additional] tool[ ]tip[s] of %itemtypes% (is|are) (:shown|hidden)", + "[the] [entire|:additional] tool[ ]tip[s] of %itemtypes% (isn't|is not|aren't|are not) (:shown|hidden)", + "%itemtypes%'[s] [entire|:additional] tool[ ]tip[s] (is|are) (:shown|hidden)", + "%itemtypes%'[s] [entire|:additional] tool[ ]tip[s] (isn't|is not|aren't|are not) (:shown|hidden)" + ); + } + } + + @SuppressWarnings("NotNullFieldNotInitialized") + private Expression items; + private boolean entire; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + items = (Expression) exprs[0]; + entire = !parseResult.hasTag("additional"); + setNegated(parseResult.hasTag("shown") ^ (matchedPattern == 1 || matchedPattern == 3)); + return true; + } + + @Override + public boolean check(Event event) { + if (entire) + return items.check(event, item -> item.getItemMeta().isHideTooltip(), isNegated()); + return items.check(event, item -> item.getItemMeta().hasItemFlag(ItemFlag.HIDE_ADDITIONAL_TOOLTIP), isNegated()); + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return "the " + (entire ? "entire" : "additional") + " tooltip of " + items.toString(event, debug) + " is " + (isNegated() ? "hidden" : "shown"); + } + +} diff --git a/src/main/java/ch/njol/skript/effects/EffTooltip.java b/src/main/java/ch/njol/skript/effects/EffTooltip.java new file mode 100644 index 00000000000..26272c4b716 --- /dev/null +++ b/src/main/java/ch/njol/skript/effects/EffTooltip.java @@ -0,0 +1,95 @@ +/** + * This file is part of Skript. + * + * Skript is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Skript is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Skript. If not, see . + * + * Copyright Peter Güttinger, SkriptLang team and contributors + */ +package ch.njol.skript.effects; + +import ch.njol.skript.Skript; +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.RequiredPlugins; +import ch.njol.skript.doc.Since; +import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.event.Event; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.meta.ItemMeta; +import org.eclipse.jdt.annotation.Nullable; + +@Name("Item Tooltips") +@Description({ + "Show or hide the tooltip of an item.", + "If changing the 'entire' tooltip of an item, nothing will show up when a player hovers over it.", + "If changing the 'additional' tooltip, only specific parts (which change per item) will be hidden." +}) +@Examples({ + "hide the entire tooltip of player's tool", + "hide {_item}'s additional tool tip" +}) +@RequiredPlugins("Spigot 1.20.5+") +@Since("INSERT VERSION") +public class EffTooltip extends Effect { + + static { + if (Skript.methodExists(ItemMeta.class, "setHideTooltip", boolean.class)) { // this method was added in the same version as the additional tooltip item flag + Skript.registerEffect(EffTooltip.class, + "(show|reveal|:hide) %itemtypes%'[s] [entire|:additional] tool[ ]tip", + "(show|reveal|:hide) [the] [entire|:additional] tool[ ]tip of %itemtypes%" + ); + } + } + + @SuppressWarnings("NotNullFieldNotInitialized") + private Expression items; + private boolean hide, entire; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + items = (Expression) exprs[0]; + hide = parseResult.hasTag("hide"); + entire = !parseResult.hasTag("additional"); + return true; + } + + @Override + protected void execute(Event event) { + for (ItemType item : items.getArray(event)) { + ItemMeta meta = item.getItemMeta(); + if (entire) { + meta.setHideTooltip(hide); + } else { + if (hide) { + meta.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); + } else { + meta.removeItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); + } + } + item.setItemMeta(meta); + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return (hide ? "hide" : "show") + " the " + (entire ? "entire" : "additional") + " tooltip of " + items.toString(event, debug); + } + +} diff --git a/src/test/skript/tests/syntaxes/conditions/CondTooltip.sk b/src/test/skript/tests/syntaxes/conditions/CondTooltip.sk new file mode 100644 index 00000000000..117a2b5bbb1 --- /dev/null +++ b/src/test/skript/tests/syntaxes/conditions/CondTooltip.sk @@ -0,0 +1,15 @@ +test "tooltip" when running minecraft "1.20.5": + + set {_item} to a diamond + assert entire tooltip of {_item} is shown with "item unexpectedly doesn't have entire tooltip" + assert additional tooltip of {_item} is shown with "item unexpectedly doesn't have additional tooltip" + + hide entire tooltip of {_item} + assert entire tooltip of {_item} is hidden with "item unexpectedly has entire tooltip" + hide additional tooltip of {_item} + assert additional tooltip of {_item} is hidden with "item unexpectedly has additional tooltip" + + show entire tooltip of {_item} + assert entire tooltip of {_item} is shown with "item unexpectedly doesn't have entire tooltip" + show additional tooltip of {_item} + assert additional tooltip of {_item} is shown with "item unexpectedly doesn't have additional tooltip" From d4ccff74c90614375b4bc41c032bfa19c6bb2ae5 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Mon, 1 Jul 2024 22:38:24 +0200 Subject: [PATCH 43/48] Exposes 'load default aliases' config option and removes hardcoded alias dependencies (#6834) * Removes dependencies on hardcoded aliases (javaItemType) and exposes 'load deafult aliases' config option * Update ItemUtils.java * Apply suggestions from code review Co-authored-by: Patrick Miller * requested changes * requested * Update src/main/java/ch/njol/skript/expressions/ExprSpawnerType.java Co-authored-by: Patrick Miller --------- Co-authored-by: Patrick Miller --- .../java/ch/njol/skript/aliases/Aliases.java | 11 ++- .../java/ch/njol/skript/aliases/ItemType.java | 13 +++ .../ch/njol/skript/bukkitutil/ItemUtils.java | 59 +++++++++++- .../bukkitutil/block/NewBlockCompat.java | 90 ++++++++++--------- .../classes/data/BukkitEventValues.java | 6 +- .../classes/data/DefaultComparators.java | 3 +- .../java/ch/njol/skript/effects/EffEquip.java | 61 +++++++++++-- .../ch/njol/skript/effects/EffOpenBook.java | 5 +- .../ch/njol/skript/entity/BoatChestData.java | 29 ++---- .../java/ch/njol/skript/entity/BoatData.java | 24 ++--- .../njol/skript/entity/ThrownPotionData.java | 19 ++-- .../java/ch/njol/skript/events/EvtMoveOn.java | 14 ++- .../njol/skript/events/EvtPressurePlate.java | 31 +++++-- .../skript/expressions/ExprBurnCookTime.java | 15 ++-- .../ch/njol/skript/expressions/ExprName.java | 4 +- .../njol/skript/expressions/ExprSignText.java | 22 ++--- .../ch/njol/skript/expressions/ExprSkull.java | 5 +- .../skript/expressions/ExprSpawnerType.java | 19 ++-- src/main/resources/config.sk | 10 +++ 19 files changed, 290 insertions(+), 150 deletions(-) diff --git a/src/main/java/ch/njol/skript/aliases/Aliases.java b/src/main/java/ch/njol/skript/aliases/Aliases.java index 56bffc24f9a..fe1a3a624c4 100644 --- a/src/main/java/ch/njol/skript/aliases/Aliases.java +++ b/src/main/java/ch/njol/skript/aliases/Aliases.java @@ -25,6 +25,7 @@ import ch.njol.skript.config.Node; import ch.njol.skript.config.SectionNode; import ch.njol.skript.entity.EntityData; +import org.bukkit.entity.EntityType; import org.skriptlang.skript.lang.script.Script; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.localization.ArgsMessage; @@ -431,7 +432,6 @@ private static void loadInternal() throws IOException { Path aliasesPath = zipFs.getPath("/", "aliases-english"); assert aliasesPath != null; loadDirectory(aliasesPath); - loadMissingAliases(); } } catch (URISyntaxException e) { assert false; @@ -445,6 +445,9 @@ private static void loadInternal() throws IOException { assert aliasesFolder != null; loadDirectory(aliasesFolder); } + + // generate aliases from item names for any missing items + loadMissingAliases(); // Update tracked item types for (Map.Entry entry : trackedTypes.entrySet()) { @@ -554,10 +557,16 @@ public static EntityData getRelatedEntity(ItemData data) { *

Item types provided by this method are updated when aliases are * reloaded. However, this also means they are tracked by aliases system * and NOT necessarily garbage-collected. + * + *

Relying on this method to create item types is not safe, + * as users can change aliases at any point. ItemTypes should instead be created + * via {@link Material}s, {@link org.bukkit.Tag}s, or any other manual method. + * * @param name Name of item to search from aliases. * @return An item. * @throws IllegalArgumentException When item is not found. */ + @Deprecated(forRemoval = true, since = "2.9.0") public static ItemType javaItemType(String name) { ItemType type = parseItemType(name); if (type == null) { diff --git a/src/main/java/ch/njol/skript/aliases/ItemType.java b/src/main/java/ch/njol/skript/aliases/ItemType.java index 0b47617ef5c..efaa59249f2 100644 --- a/src/main/java/ch/njol/skript/aliases/ItemType.java +++ b/src/main/java/ch/njol/skript/aliases/ItemType.java @@ -40,6 +40,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Skull; @@ -173,6 +174,18 @@ public ItemType(Material id) { add_(new ItemData(id)); } + public ItemType(Material... ids) { + for (Material id : ids) { + add_(new ItemData(id)); + } + } + + public ItemType(Tag tag) { + for (Material id : tag.getValues()) { + add_(new ItemData(id)); + } + } + public ItemType(Material id, String tags) { add_(new ItemData(id, tags)); } diff --git a/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java b/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java index 216be44b918..611aba0d1d7 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/ItemUtils.java @@ -21,7 +21,13 @@ import ch.njol.skript.Skript; import ch.njol.skript.aliases.ItemType; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.TreeType; +import org.bukkit.block.Block; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Fence; +import org.bukkit.block.data.type.Gate; +import org.bukkit.block.data.type.Wall; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; @@ -234,5 +240,56 @@ public static boolean isAir(Material type) { public static Material getTreeSapling(TreeType treeType) { return TREE_TO_SAPLING_MAP.get(treeType); } - + + + private static final boolean HAS_FENCE_TAGS = !Skript.isRunningMinecraft(1, 14); + + /** + * Whether the block is a fence or a wall. + * @param block the block to check. + * @return whether the block is a fence/wall. + */ + public static boolean isFence(Block block) { + // TODO: 1.13 only, so remove in 2.10 + if (!HAS_FENCE_TAGS) { + BlockData data = block.getBlockData(); + return data instanceof Fence + || data instanceof Wall + || data instanceof Gate; + } + + Material type = block.getType(); + return Tag.FENCES.isTagged(type) + || Tag.FENCE_GATES.isTagged(type) + || Tag.WALLS.isTagged(type); + } + + /** + * @param material The material to check + * @return whether the material is a full glass block + */ + public static boolean isGlass(Material material) { + switch (material) { + case GLASS: + case RED_STAINED_GLASS: + case ORANGE_STAINED_GLASS: + case YELLOW_STAINED_GLASS: + case LIGHT_BLUE_STAINED_GLASS: + case BLUE_STAINED_GLASS: + case CYAN_STAINED_GLASS: + case LIME_STAINED_GLASS: + case GREEN_STAINED_GLASS: + case MAGENTA_STAINED_GLASS: + case PURPLE_STAINED_GLASS: + case PINK_STAINED_GLASS: + case WHITE_STAINED_GLASS: + case LIGHT_GRAY_STAINED_GLASS: + case GRAY_STAINED_GLASS: + case BLACK_STAINED_GLASS: + case BROWN_STAINED_GLASS: + return true; + default: + return false; + } + } } diff --git a/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java b/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java index e5cf9c0567b..e72a947996f 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java +++ b/src/main/java/ch/njol/skript/bukkitutil/block/NewBlockCompat.java @@ -22,6 +22,7 @@ import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemType; import ch.njol.skript.aliases.MatchQuality; +import ch.njol.skript.bukkitutil.ItemUtils; import ch.njol.skript.variables.Variables; import ch.njol.yggdrasil.Fields; import org.bukkit.Bukkit; @@ -31,10 +32,12 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.BlockSupport; import org.bukkit.block.data.Bisected; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.block.data.type.Bed; +import org.bukkit.block.data.type.Snow; import org.bukkit.entity.FallingBlock; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -141,15 +144,6 @@ public void deserialize(@NonNull Fields fields) throws StreamCorruptedException } private static class NewBlockSetter implements BlockSetter { - - private ItemType floorTorch; - private ItemType wallTorch; - - private ItemType specialTorchSides; - private ItemType specialTorchFloors; - - private boolean typesLoaded = false; - private static final BlockFace[] CARDINAL_FACES = new BlockFace[] {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; @@ -158,14 +152,11 @@ public NewBlockSetter() {} @Override public void setBlock(Block block, Material type, @Nullable BlockValues values, int flags) { - if (!typesLoaded) - loadTypes(); - - boolean rotate = (flags | ROTATE) != 0; - boolean rotateForce = (flags | ROTATE_FORCE) != 0; - boolean rotateFixType = (flags | ROTATE_FIX_TYPE) != 0; - boolean multipart = (flags | MULTIPART) != 0; - boolean applyPhysics = (flags | APPLY_PHYSICS) != 0; + boolean rotate = (flags & ROTATE) != 0; + boolean rotateForce = (flags & ROTATE_FORCE) != 0; + boolean rotateFixType = (flags & ROTATE_FIX_TYPE) != 0; + boolean multipart = (flags & MULTIPART) != 0; + boolean applyPhysics = (flags & APPLY_PHYSICS) != 0; NewBlockValues ourValues = null; if (values != null) ourValues = (NewBlockValues) values; @@ -178,31 +169,23 @@ public void setBlock(Block block, Material type, @Nullable BlockValues values, i */ boolean placed = false; if (rotate) { - if (floorTorch.isOfType(type) || (rotateFixType && wallTorch.isOfType(type))) { + if (type == Material.TORCH || (rotateFixType && type == Material.WALL_TORCH)) { // If floor torch cannot be placed, try a wall torch Block under = block.getRelative(0, -1, 0); - boolean canPlace = true; - if (!under.getType().isOccluding()) { // Usually cannot be placed, but there are exceptions - // TODO check for stairs and slabs, currently complicated since there is no 'any' alias - if (specialTorchFloors.isOfType(under)) { - canPlace = true; - } else { - canPlace = false; - } - } + boolean canPlace = canSupportFloorTorch(under); // Can't really place a floor torch, try wall one instead if (!canPlace) { BlockFace face = findWallTorchSide(block); if (face != null) { // Found better torch spot - block.setType(wallTorch.getMaterial()); + block.setType(Material.WALL_TORCH); Directional data = (Directional) block.getBlockData(); data.setFacing(face); block.setBlockData(data, applyPhysics); placed = true; } } - } else if (wallTorch.isOfType(type)) { + } else if (type == Material.WALL_TORCH) { Directional data; if (ourValues != null) data = (Directional) ourValues.data; @@ -210,7 +193,7 @@ public void setBlock(Block block, Material type, @Nullable BlockValues values, i data = (Directional) Bukkit.createBlockData(type); Block relative = block.getRelative(data.getFacing()); - if ((!relative.getType().isOccluding() && !specialTorchSides.isOfType(relative)) || rotateForce) { + if ((!canSupportWallTorch(relative, data.getFacing().getOppositeFace())) || rotateForce) { // Attempt to figure out a better rotation BlockFace face = findWallTorchSide(block); if (face != null) { // Found better torch spot @@ -294,22 +277,49 @@ public void setBlock(Block block, Material type, @Nullable BlockValues values, i block.setBlockData(ourValues.data, applyPhysics); } } - - private void loadTypes() { - floorTorch = Aliases.javaItemType("floor torch"); - wallTorch = Aliases.javaItemType("wall torch"); - - specialTorchSides = Aliases.javaItemType("special torch sides"); - specialTorchFloors = Aliases.javaItemType("special torch floors"); - - typesLoaded = true; + + // 1.19+ + // TODO: remove in 2.10 + private static final boolean HAS_BLOCK_SUPPORT = Skript.classExists("org.bukkit.block.BlockSupport"); + + /** + * Returns whether this block can support a floor torch. + * @param block The block the torch will be placed on + * @return whether the block can support the torch. + */ + private static boolean canSupportFloorTorch(Block block) { + if (HAS_BLOCK_SUPPORT) + return block.getBlockData().isFaceSturdy(BlockFace.UP, BlockSupport.CENTER); + + Material material = block.getType(); + return (material.isOccluding() + || canSupportWallTorch(block, null) + || ItemUtils.isFence(block) + || ItemUtils.isGlass(material) + || material == Material.HOPPER + || (material == Material.SNOW && (((Snow) block.getBlockData()).getLayers() == 8)) + ); + } + + /** + * Returns whether this block can support a wall torch. In 1.19+, a face can be specified. + * @param block The block the torch will be placed on + * @param face The face the torch will be placed on (only considered in 1.19+) + * @return whether the block face can support the torch. + */ + private static boolean canSupportWallTorch(Block block, @Nullable BlockFace face) { + if (HAS_BLOCK_SUPPORT && face != null) + return block.getBlockData().isFaceSturdy(face, BlockSupport.FULL); + + Material material = block.getType(); + return material.isOccluding() || material == Material.SOUL_SAND || material == Material.SPAWNER; } @Nullable private BlockFace findWallTorchSide(Block block) { for (BlockFace face : CARDINAL_FACES) { Block relative = block.getRelative(face); - if (relative.getType().isOccluding() || specialTorchSides.isOfType(relative)) + if (relative.getType().isOccluding() || canSupportWallTorch(relative, face.getOppositeFace())) return face.getOppositeFace(); // Torch can be rotated towards from this face } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java index 6a49566c827..34792550178 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java @@ -392,12 +392,11 @@ public Block get(final BlockBreakEvent e) { return new DelayedChangeBlock(e.getBlock()); } }, 0); - ItemType stationaryWater = Aliases.javaItemType("stationary water"); EventValues.registerEventValue(BlockBreakEvent.class, Block.class, new Getter() { @Override public Block get(final BlockBreakEvent e) { final BlockState s = e.getBlock().getState(); - s.setType(s.getType() == Material.ICE ? stationaryWater.getMaterial() : Material.AIR); + s.setType(s.getType() == Material.ICE ? Material.WATER : Material.AIR); s.setRawData((byte) 0); return new BlockStateBlock(s, true); } @@ -760,12 +759,11 @@ public Block get(final PlayerBucketEmptyEvent e) { return e.getBlockClicked().getRelative(e.getBlockFace()); } }, -1); - ItemType stationaryLava = Aliases.javaItemType("stationary lava"); EventValues.registerEventValue(PlayerBucketEmptyEvent.class, Block.class, new Getter() { @Override public Block get(final PlayerBucketEmptyEvent e) { final BlockState s = e.getBlockClicked().getRelative(e.getBlockFace()).getState(); - s.setType(e.getBucket() == Material.WATER_BUCKET ? stationaryWater.getMaterial() : stationaryLava.getMaterial()); + s.setType(e.getBucket() == Material.WATER_BUCKET ? Material.WATER : Material.LAVA); s.setRawData((byte) 0); return new BlockStateBlock(s, true); } diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java index 87fa10b1089..e5e42bf1423 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultComparators.java @@ -496,7 +496,6 @@ public boolean supportsOrdering() { }); // DamageCause - ItemType - ItemType lava = Aliases.javaItemType("lava"); Comparators.registerComparator(DamageCause.class, ItemType.class, new Comparator() { @Override public Relation compare(DamageCause dc, ItemType t) { @@ -504,7 +503,7 @@ public Relation compare(DamageCause dc, ItemType t) { case FIRE: return Relation.get(t.isOfType(Material.FIRE)); case LAVA: - return Relation.get(t.equals(lava)); + return Relation.get(t.getMaterial() == Material.LAVA); case MAGIC: return Relation.get(t.isOfType(Material.POTION)); case HOT_FLOOR: diff --git a/src/main/java/ch/njol/skript/effects/EffEquip.java b/src/main/java/ch/njol/skript/effects/EffEquip.java index c66a5a1d2fd..baa7334b160 100644 --- a/src/main/java/ch/njol/skript/effects/EffEquip.java +++ b/src/main/java/ch/njol/skript/effects/EffEquip.java @@ -18,7 +18,9 @@ */ package ch.njol.skript.effects; +import ch.njol.skript.aliases.ItemData; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.ChestedHorse; import org.bukkit.entity.LivingEntity; @@ -93,13 +95,58 @@ public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelaye private static final boolean SUPPORTS_STEERABLE = Skript.classExists("org.bukkit.entity.Steerable"); - private static final ItemType CHESTPLATE = Aliases.javaItemType("chestplate"); - private static final ItemType LEGGINGS = Aliases.javaItemType("leggings"); - private static final ItemType BOOTS = Aliases.javaItemType("boots"); - private static final ItemType HORSE_ARMOR = Aliases.javaItemType("horse armor"); - private static final ItemType SADDLE = Aliases.javaItemType("saddle"); - private static final ItemType CHEST = Aliases.javaItemType("chest"); - private static final ItemType CARPET = Aliases.javaItemType("carpet"); + private static ItemType CHESTPLATE; + private static ItemType LEGGINGS; + private static ItemType BOOTS; + private static ItemType CARPET; + private static final ItemType HORSE_ARMOR = new ItemType(Material.IRON_HORSE_ARMOR, Material.GOLDEN_HORSE_ARMOR, Material.DIAMOND_HORSE_ARMOR); + private static final ItemType SADDLE = new ItemType(Material.SADDLE); + private static final ItemType CHEST = new ItemType(Material.CHEST); + + static { + boolean usesWoolCarpetTag = Skript.fieldExists(Tag.class, "WOOL_CARPET"); + CARPET = new ItemType(usesWoolCarpetTag ? Tag.WOOL_CARPETS : Tag.CARPETS); + // added in 1.20.6 + if (Skript.fieldExists(Tag.class, "ITEM_CHEST_ARMOR")) { + CHESTPLATE = new ItemType(Tag.ITEMS_CHEST_ARMOR); + LEGGINGS = new ItemType(Tag.ITEMS_LEG_ARMOR); + BOOTS = new ItemType(Tag.ITEMS_FOOT_ARMOR); + } else { + CHESTPLATE = new ItemType( + Material.LEATHER_CHESTPLATE, + Material.CHAINMAIL_CHESTPLATE, + Material.GOLDEN_CHESTPLATE, + Material.IRON_CHESTPLATE, + Material.DIAMOND_CHESTPLATE, + Material.ELYTRA + ); + + LEGGINGS = new ItemType( + Material.LEATHER_LEGGINGS, + Material.CHAINMAIL_LEGGINGS, + Material.GOLDEN_LEGGINGS, + Material.IRON_LEGGINGS, + Material.DIAMOND_LEGGINGS + ); + + BOOTS = new ItemType( + Material.LEATHER_BOOTS, + Material.CHAINMAIL_BOOTS, + Material.GOLDEN_BOOTS, + Material.IRON_BOOTS, + Material.DIAMOND_BOOTS + ); + + // netherite + if (Skript.isRunningMinecraft(1,16)) { + CHESTPLATE.add(new ItemData(Material.NETHERITE_CHESTPLATE)); + LEGGINGS.add(new ItemData(Material.NETHERITE_LEGGINGS)); + BOOTS.add(new ItemData(Material.NETHERITE_BOOTS)); + } + } + } + + private static final ItemType[] ALL_EQUIPMENT = new ItemType[] {CHESTPLATE, LEGGINGS, BOOTS, HORSE_ARMOR, SADDLE, CHEST, CARPET}; diff --git a/src/main/java/ch/njol/skript/effects/EffOpenBook.java b/src/main/java/ch/njol/skript/effects/EffOpenBook.java index 5b96114818b..f49df089abe 100644 --- a/src/main/java/ch/njol/skript/effects/EffOpenBook.java +++ b/src/main/java/ch/njol/skript/effects/EffOpenBook.java @@ -18,6 +18,7 @@ */ package ch.njol.skript.effects; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.inventory.ItemStack; @@ -49,8 +50,6 @@ public class EffOpenBook extends Effect { } } - private static final ItemType bookItemType = Aliases.javaItemType("written book"); - @SuppressWarnings("null") private Expression book; @SuppressWarnings("null") @@ -69,7 +68,7 @@ protected void execute(final Event e) { ItemType itemType = book.getSingle(e); if (itemType != null) { ItemStack itemStack = itemType.getRandom(); - if (itemStack != null && bookItemType.isOfType(itemStack)) { + if (itemStack.getType() == Material.WRITTEN_BOOK) { for (Player player : players.getArray(e)) { player.openBook(itemStack); } diff --git a/src/main/java/ch/njol/skript/entity/BoatChestData.java b/src/main/java/ch/njol/skript/entity/BoatChestData.java index bb8d8506e23..bb29004028d 100644 --- a/src/main/java/ch/njol/skript/entity/BoatChestData.java +++ b/src/main/java/ch/njol/skript/entity/BoatChestData.java @@ -23,6 +23,7 @@ import ch.njol.skript.aliases.ItemType; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser; +import org.bukkit.Material; import org.bukkit.TreeSpecies; import org.bukkit.entity.ChestBoat; import org.bukkit.inventory.ItemStack; @@ -32,21 +33,8 @@ public class BoatChestData extends EntityData { - private static ItemType oakBoat = null; - private static ItemType spruceBoat = null; - private static ItemType birchBoat = null; - private static ItemType jungleBoat = null; - private static ItemType acaciaBoat = null; - private static ItemType darkOakBoat = null; - static { if (Skript.classExists("org.bukkit.entity.ChestBoat")) { - oakBoat = Aliases.javaItemType("oak chest boat"); - spruceBoat = Aliases.javaItemType("spruce chest boat"); - birchBoat = Aliases.javaItemType("birch chest boat"); - jungleBoat = Aliases.javaItemType("jungle chest boat"); - acaciaBoat = Aliases.javaItemType("acacia chest boat"); - darkOakBoat = Aliases.javaItemType("dark oak chest boat"); EntityData.register(BoatChestData.class, "chest boat", ChestBoat.class, 0, "chest boat", "any chest boat", "oak chest boat", "spruce chest boat", "birch chest boat", "jungle chest boat", "acacia chest boat", "dark oak chest boat"); @@ -120,22 +108,21 @@ public boolean isSupertypeOf(EntityData e) { } public boolean isOfItemType(ItemType itemType) { - if (itemType.getRandom() == null) - return false; int ordinal = -1; ItemStack stack = itemType.getRandom(); - if (oakBoat.isOfType(stack)) + Material type = stack.getType(); + if (type == Material.OAK_CHEST_BOAT) ordinal = 0; - else if (spruceBoat.isOfType(stack)) + else if (type == Material.SPRUCE_CHEST_BOAT) ordinal = TreeSpecies.REDWOOD.ordinal(); - else if (birchBoat.isOfType(stack)) + else if (type == Material.BIRCH_CHEST_BOAT) ordinal = TreeSpecies.BIRCH.ordinal(); - else if (jungleBoat.isOfType(stack)) + else if (type == Material.JUNGLE_CHEST_BOAT) ordinal = TreeSpecies.JUNGLE.ordinal(); - else if (acaciaBoat.isOfType(stack)) + else if (type == Material.ACACIA_CHEST_BOAT) ordinal = TreeSpecies.ACACIA.ordinal(); - else if (darkOakBoat.isOfType(stack)) + else if (type == Material.DARK_OAK_CHEST_BOAT) ordinal = TreeSpecies.DARK_OAK.ordinal(); return hashCode_i() == ordinal + 2 || (matchedPattern + ordinal == 0) || ordinal == 0; } diff --git a/src/main/java/ch/njol/skript/entity/BoatData.java b/src/main/java/ch/njol/skript/entity/BoatData.java index dfeb1f22890..b273f2ca370 100644 --- a/src/main/java/ch/njol/skript/entity/BoatData.java +++ b/src/main/java/ch/njol/skript/entity/BoatData.java @@ -18,8 +18,10 @@ */ package ch.njol.skript.entity; +import java.lang.reflect.Method; import java.util.Random; +import org.bukkit.Material; import org.bukkit.TreeSpecies; import org.bukkit.entity.Boat; import org.bukkit.inventory.ItemStack; @@ -108,30 +110,22 @@ public boolean isSupertypeOf(EntityData e) { return false; } - private static final ItemType oakBoat = Aliases.javaItemType("oak boat"); - private static final ItemType spruceBoat = Aliases.javaItemType("spruce boat"); - private static final ItemType birchBoat = Aliases.javaItemType("birch boat"); - private static final ItemType jungleBoat = Aliases.javaItemType("jungle boat"); - private static final ItemType acaciaBoat = Aliases.javaItemType("acacia boat"); - private static final ItemType darkOakBoat = Aliases.javaItemType("dark oak boat"); - public boolean isOfItemType(ItemType i){ - if (i.getRandom() == null) - return false; int ordinal = -1; ItemStack stack = i.getRandom(); - if (oakBoat.isOfType(stack)) + Material type = stack.getType(); + if (type == Material.OAK_BOAT) ordinal = 0; - else if (spruceBoat.isOfType(stack)) + else if (type == Material.SPRUCE_BOAT) ordinal = TreeSpecies.REDWOOD.ordinal(); - else if (birchBoat.isOfType(stack)) + else if (type == Material.BIRCH_BOAT) ordinal = TreeSpecies.BIRCH.ordinal(); - else if (jungleBoat.isOfType(stack)) + else if (type == Material.JUNGLE_BOAT) ordinal = TreeSpecies.JUNGLE.ordinal(); - else if (acaciaBoat.isOfType(stack)) + else if (type == Material.ACACIA_BOAT) ordinal = TreeSpecies.ACACIA.ordinal(); - else if (darkOakBoat.isOfType(stack)) + else if (type == Material.DARK_OAK_BOAT) ordinal = TreeSpecies.DARK_OAK.ordinal(); return hashCode_i() == ordinal + 2 || (matchedPattern + ordinal == 0) || ordinal == 0; diff --git a/src/main/java/ch/njol/skript/entity/ThrownPotionData.java b/src/main/java/ch/njol/skript/entity/ThrownPotionData.java index 407460d56c5..12d03199fbe 100644 --- a/src/main/java/ch/njol/skript/entity/ThrownPotionData.java +++ b/src/main/java/ch/njol/skript/entity/ThrownPotionData.java @@ -22,6 +22,7 @@ import java.util.function.Consumer; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.entity.LingeringPotion; import org.bukkit.entity.ThrownPotion; import org.bukkit.inventory.ItemStack; @@ -51,9 +52,9 @@ public class ThrownPotionData extends EntityData { @SuppressWarnings("removal") private static final Class LINGERING_POTION_ENTITY_CLASS = LINGERING_POTION_ENTITY_USED ? LingeringPotion.class : ThrownPotion.class; - private static final ItemType POTION = Aliases.javaItemType("potion"); - private static final ItemType SPLASH_POTION = Aliases.javaItemType("splash potion"); - private static final ItemType LINGER_POTION = Aliases.javaItemType("lingering potion"); + private static final Material POTION = Material.POTION; + private static final Material SPLASH_POTION = Material.SPLASH_POTION; + private static final Material LINGER_POTION = Material.LINGERING_POTION; @Nullable private ItemType[] types; @@ -63,18 +64,18 @@ protected boolean init(Literal[] exprs, int matchedPattern, ParseResult parse if (exprs.length > 0 && exprs[0] != null) { return (types = Converters.convert((ItemType[]) exprs[0].getAll(), ItemType.class, t -> { // If the itemtype is a potion, lets make it a splash potion (required by Bukkit) - if (t.isSupertypeOf(POTION)) { + if (t.getMaterial() == POTION) { ItemMeta meta = t.getItemMeta(); - ItemType itemType = SPLASH_POTION.clone(); + ItemType itemType = new ItemType(SPLASH_POTION); itemType.setItemMeta(meta); return itemType; - } else if (!t.isSupertypeOf(SPLASH_POTION ) && !t.isSupertypeOf(LINGER_POTION)) { + } else if (t.getMaterial() != SPLASH_POTION && t.getMaterial() != LINGER_POTION) { return null; } return t; })).length != 0; // no error message - other things can be thrown as well } else { - types = new ItemType[]{SPLASH_POTION.clone()}; + types = new ItemType[]{new ItemType(SPLASH_POTION)}; } return true; } @@ -109,7 +110,7 @@ protected boolean match(ThrownPotion entity) { if (i == null) return null; - Class thrownPotionClass = (Class) (LINGER_POTION.isOfType(i) ? LINGERING_POTION_ENTITY_CLASS : ThrownPotion.class); + Class thrownPotionClass = (Class) (i.getType() == LINGER_POTION ? LINGERING_POTION_ENTITY_CLASS : ThrownPotion.class); ThrownPotion potion; if (consumer != null) { potion = EntityData.spawn(location, thrownPotionClass, consumer); @@ -131,7 +132,7 @@ public void set(ThrownPotion entity) { ItemStack i = t.getRandom(); if (i == null) return; // Missing item, can't make thrown potion of it - if (LINGERING_POTION_ENTITY_USED && (LINGERING_POTION_ENTITY_CLASS.isInstance(entity) != LINGER_POTION.isOfType(i))) + if (LINGERING_POTION_ENTITY_USED && (LINGERING_POTION_ENTITY_CLASS.isInstance(entity) != (LINGER_POTION == i.getType()))) return; entity.setItem(i); } diff --git a/src/main/java/ch/njol/skript/events/EvtMoveOn.java b/src/main/java/ch/njol/skript/events/EvtMoveOn.java index 52b552b0040..c5677e5783c 100644 --- a/src/main/java/ch/njol/skript/events/EvtMoveOn.java +++ b/src/main/java/ch/njol/skript/events/EvtMoveOn.java @@ -33,6 +33,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.Listener; @@ -66,9 +67,6 @@ public class EvtMoveOn extends SkriptEvent { ).since("2.0"); } - // Fence blocks and fence gates - private static final ItemType FENCE_PART = Aliases.javaItemType("fence part"); - private static final Map> ITEM_TYPE_TRIGGERS = new ConcurrentHashMap<>(); private static final AtomicBoolean REGISTERED_EXECUTOR = new AtomicBoolean(); @@ -87,10 +85,10 @@ public class EvtMoveOn extends SkriptEvent { if (triggers == null) return; - int y = getBlockY(to.getY(), id); + int y = getBlockY(to.getY(), block); if (to.getWorld().equals(from.getWorld()) && to.getBlockX() == from.getBlockX() && to.getBlockZ() == from.getBlockZ()) { Block fromOnBlock = getOnBlock(from); - if (fromOnBlock != null && y == getBlockY(from.getY(), fromOnBlock.getType()) && fromOnBlock.getType() == id) + if (fromOnBlock != null && y == getBlockY(from.getY(), fromOnBlock) && fromOnBlock.getType() == id) return; } @@ -114,14 +112,14 @@ private static Block getOnBlock(Location location) { Block block = location.getWorld().getBlockAt(location.getBlockX(), (int) (Math.ceil(location.getY()) - 1), location.getBlockZ()); if (block.getType() == Material.AIR && Math.abs((location.getY() - location.getBlockY()) - 0.5) < Skript.EPSILON) { // Fences block = location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() - 1, location.getBlockZ()); - if (!FENCE_PART.isOfType(block)) + if (!ItemUtils.isFence(block)) return null; } return block; } - private static int getBlockY(double y, Material id) { - if (FENCE_PART.isOfType(id) && Math.abs((y - Math.floor(y)) - 0.5) < Skript.EPSILON) + private static int getBlockY(double y, Block block) { + if (ItemUtils.isFence(block) && Math.abs((y - Math.floor(y)) - 0.5) < Skript.EPSILON) return (int) Math.floor(y) - 1; return (int) Math.ceil(y) - 1; } diff --git a/src/main/java/ch/njol/skript/events/EvtPressurePlate.java b/src/main/java/ch/njol/skript/events/EvtPressurePlate.java index de320c57f26..4d3f876a3b5 100644 --- a/src/main/java/ch/njol/skript/events/EvtPressurePlate.java +++ b/src/main/java/ch/njol/skript/events/EvtPressurePlate.java @@ -19,6 +19,7 @@ package ch.njol.skript.events; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.block.Action; @@ -36,6 +37,9 @@ * @author Peter Güttinger */ public class EvtPressurePlate extends SkriptEvent { + + private static final boolean HAS_PRESSURE_PLATE_TAG = Skript.fieldExists(Tag.class, "PRESSURE_PLATES"); + static { // TODO is EntityInteractEvent similar for entities? Skript.registerEvent("Pressure Plate / Trip", EvtPressurePlate.class, PlayerInteractEvent.class, @@ -46,8 +50,6 @@ public class EvtPressurePlate extends SkriptEvent { .since("1.0 (pressure plate), 1.4.4 (tripwire)"); } - private static final ItemType plate = Aliases.javaItemType("pressure plate"); - private boolean tripwire; @Override @@ -55,14 +57,25 @@ public boolean init(final Literal[] args, final int matchedPattern, final Par tripwire = matchedPattern == 1; return true; } - + @Override - public boolean check(final Event e) { - final Block b = ((PlayerInteractEvent) e).getClickedBlock(); - final Material type = b == null ? null : b.getType(); - return type != null && ((PlayerInteractEvent) e).getAction() == Action.PHYSICAL && - (tripwire ? (type == Material.TRIPWIRE || type == Material.TRIPWIRE_HOOK) - : plate.isOfType(type)); + public boolean check(Event event) { + Block clickedBlock = ((PlayerInteractEvent) event).getClickedBlock(); + Material type = clickedBlock == null ? null : clickedBlock.getType(); + if (type == null || ((PlayerInteractEvent) event).getAction() != Action.PHYSICAL ) + return false; + + if (tripwire) + return(type == Material.TRIPWIRE || type == Material.TRIPWIRE_HOOK); + + // TODO: 1.16+, remove check in 2.10 + if (HAS_PRESSURE_PLATE_TAG) + return Tag.PRESSURE_PLATES.isTagged(type); + + return Tag.WOODEN_PRESSURE_PLATES.isTagged(type) + || type == Material.HEAVY_WEIGHTED_PRESSURE_PLATE + || type == Material.LIGHT_WEIGHTED_PRESSURE_PLATE + || type == Material.STONE_PRESSURE_PLATE; } @Override diff --git a/src/main/java/ch/njol/skript/expressions/ExprBurnCookTime.java b/src/main/java/ch/njol/skript/expressions/ExprBurnCookTime.java index 3763d242dc1..0d98c551925 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprBurnCookTime.java +++ b/src/main/java/ch/njol/skript/expressions/ExprBurnCookTime.java @@ -23,6 +23,7 @@ import ch.njol.skript.classes.Changer.ChangeMode; import org.bukkit.block.Block; +import org.bukkit.block.BlockState; import org.bukkit.block.Furnace; import org.bukkit.event.Event; import org.bukkit.event.inventory.FurnaceBurnEvent; @@ -65,8 +66,6 @@ public class ExprBurnCookTime extends PropertyExpression { "[the] (burn|1:cook)[ing] time of %blocks%", "%blocks%'[s] (burn|1:cook)[ing] time"); } - - static final ItemType anyFurnace = Aliases.javaItemType("any furnace"); private boolean cookTime; private boolean isEvent; @@ -92,10 +91,11 @@ protected Timespan[] get(Event event, Block[] source) { return CollectionUtils.array(Timespan.fromTicks(((FurnaceBurnEvent) event).getBurnTime())); } else { return Arrays.stream(source) - .filter(anyFurnace::isOfType) - .map(furnace -> { - Furnace state = (Furnace) furnace.getState(); - return Timespan.fromTicks(cookTime ? state.getCookTime() : state.getBurnTime()); + .map(Block::getState) + .filter(blockState -> blockState instanceof Furnace) + .map(state -> { + Furnace furnace = (Furnace) state; + return Timespan.fromTicks(cookTime ? furnace.getCookTime() : furnace.getBurnTime()); }) .toArray(Timespan[]::new); } @@ -141,7 +141,8 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { } for (Block block : getExpr().getArray(event)) { - if (!anyFurnace.isOfType(block)) + BlockState state = block.getState(); + if (!(state instanceof Furnace)) continue; Furnace furnace = (Furnace) block.getState(); long time = value.apply(Timespan.fromTicks(cookTime ? furnace.getCookTime() : furnace.getBurnTime())).getTicks(); diff --git a/src/main/java/ch/njol/skript/expressions/ExprName.java b/src/main/java/ch/njol/skript/expressions/ExprName.java index b6f873e0b89..666a39092a1 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprName.java +++ b/src/main/java/ch/njol/skript/expressions/ExprName.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; +import ch.njol.skript.bukkitutil.ItemUtils; import org.bukkit.Bukkit; import org.bukkit.GameRule; import org.bukkit.Nameable; @@ -139,7 +140,6 @@ public class ExprName extends SimplePropertyExpression { * 3 = "tablist name" */ private int mark; - private static final ItemType AIR = Aliases.javaItemType("air"); @Override public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { @@ -282,7 +282,7 @@ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) { } else if (object instanceof Slot) { Slot s = (Slot) object; ItemStack is = s.getItem(); - if (is != null && !AIR.isOfType(is)) { + if (is != null && !ItemUtils.isAir(is.getType())) { ItemMeta m = is.hasItemMeta() ? is.getItemMeta() : Bukkit.getItemFactory().getItemMeta(is.getType()); m.setDisplayName(name); is.setItemMeta(m); diff --git a/src/main/java/ch/njol/skript/expressions/ExprSignText.java b/src/main/java/ch/njol/skript/expressions/ExprSignText.java index 592420ed647..0b549483b17 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprSignText.java +++ b/src/main/java/ch/njol/skript/expressions/ExprSignText.java @@ -18,7 +18,10 @@ */ package ch.njol.skript.expressions; +import ch.njol.skript.bukkitutil.ItemUtils; +import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.BlockState; import org.bukkit.block.Sign; import org.bukkit.event.Event; import org.bukkit.event.block.SignChangeEvent; @@ -56,8 +59,6 @@ public class ExprSignText extends SimpleExpression { "[the] line %number% [of %block%]", "[the] (1¦1st|1¦first|2¦2nd|2¦second|3¦3rd|3¦third|4¦4th|4¦fourth) line [of %block%]"); } - private static final ItemType sign = Aliases.javaItemType("sign"); - @SuppressWarnings("null") private Expression line; @SuppressWarnings("null") @@ -99,7 +100,7 @@ protected String[] get(final Event e) { final Block b = block.getSingle(e); if (b == null) return new String[0]; - if (!sign.isOfType(b)) + if (!(b.getState() instanceof Sign)) return new String[0]; return new String[] {((Sign) b.getState()).getLine(line)}; } @@ -143,27 +144,28 @@ public void change(final Event e, final @Nullable Object[] delta, final ChangeMo break; } } else { - if (!sign.isOfType(b)) + BlockState state = b.getState(); + if (!(state instanceof Sign)) return; - final Sign s = (Sign) b.getState(); + Sign sign = (Sign) b.getState(); switch (mode) { case DELETE: - s.setLine(line, ""); + sign.setLine(line, ""); break; case SET: assert delta != null; - s.setLine(line, (String) delta[0]); + sign.setLine(line, (String) delta[0]); break; } if (hasUpdateBooleanBoolean) { try { - s.update(false, false); + sign.update(false, false); } catch (final NoSuchMethodError err) { hasUpdateBooleanBoolean = false; - s.update(); + sign.update(); } } else { - s.update(); + sign.update(); } } } diff --git a/src/main/java/ch/njol/skript/expressions/ExprSkull.java b/src/main/java/ch/njol/skript/expressions/ExprSkull.java index e3a0fa2767d..4e133a79bfd 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprSkull.java +++ b/src/main/java/ch/njol/skript/expressions/ExprSkull.java @@ -18,6 +18,7 @@ */ package ch.njol.skript.expressions; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.meta.SkullMeta; import org.eclipse.jdt.annotation.Nullable; @@ -48,8 +49,6 @@ public class ExprSkull extends SimplePropertyExpression { register(ExprSkull.class, ItemType.class, "(head|skull)", "offlineplayers"); } - private static final ItemType playerSkull = Aliases.javaItemType("player skull"); - /** * In 2017, SkullMeta finally got a method that takes OfflinePlayer. */ @@ -64,7 +63,7 @@ public boolean init(final Expression[] exprs, final int matchedPattern, final @Override @Nullable public ItemType convert(final Object o) { - ItemType skull = playerSkull.clone(); + ItemType skull = new ItemType(Material.PLAYER_HEAD); SkullMeta meta = (SkullMeta) skull.getItemMeta(); if (newSkullOwner) meta.setOwningPlayer((OfflinePlayer) o); diff --git a/src/main/java/ch/njol/skript/expressions/ExprSpawnerType.java b/src/main/java/ch/njol/skript/expressions/ExprSpawnerType.java index d9d31b824cc..24b59ae9dd3 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprSpawnerType.java +++ b/src/main/java/ch/njol/skript/expressions/ExprSpawnerType.java @@ -22,6 +22,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.EntityType; import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; @@ -44,18 +45,19 @@ @Since("2.4") public class ExprSpawnerType extends SimplePropertyExpression { - private static final Material MATERIAL_SPAWNER = Aliases.javaItemType("spawner").getMaterial(); - static { register(ExprSpawnerType.class, EntityData.class, "(spawner|entity|creature) type[s]", "blocks"); } @Override @Nullable - public EntityData convert(final Block b) { - if (b.getType() != MATERIAL_SPAWNER) + public EntityData convert(Block block) { + if (!(block.getState() instanceof CreatureSpawner)) + return null; + EntityType type = ((CreatureSpawner) block.getState()).getSpawnedType(); + if (type == null) return null; - return EntityUtils.toSkriptEntityData(((CreatureSpawner) b.getState()).getSpawnedType()); + return EntityUtils.toSkriptEntityData(type); } @Nullable @@ -68,13 +70,14 @@ public Class[] acceptChange(Changer.ChangeMode mode) { @SuppressWarnings("null") @Override - public void change(final Event e, final @Nullable Object[] delta, final ChangeMode mode) { - for (Block b : getExpr().getArray(e)) { - if (b.getType() != MATERIAL_SPAWNER) + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + for (Block b : getExpr().getArray(event)) { + if (!(b.getState() instanceof CreatureSpawner)) continue; CreatureSpawner s = (CreatureSpawner) b.getState(); switch (mode) { case SET: + assert delta != null; s.setSpawnedType(EntityUtils.toBukkitEntityType((EntityData) delta[0])); break; case RESET: diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 4ba081e7c33..ff5e1e9e40a 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -80,6 +80,16 @@ log effect commands: false # Whether Skript should log the usage of effect commands. # They will be logged as [INFORMATION] in this format: ' issued effect command: ' + +load default aliases: true +# Whether Skript should use the default, Skript-provided aliases for items. +# Disabling this will cause all aliases to be automatically generated. Some items, like potions, +# will likely not have aliases in this case. +# +# You can provide your own aliases by making an 'aliases' folder in the Skript directory and putting the alias files there. +# The default aliases folder is available with Skript releases on GitHub. + + player variable fix: true # Whether to enable the player variable fix if a player has rejoined and was reciding inside a variable. # Player objects inside a variable(list or normal) are not updated to the new player object From b48f66d83760be7ebdfcc1dfcc833d3760709ba2 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Mon, 1 Jul 2024 17:19:22 -0400 Subject: [PATCH 44/48] Fix current section update issues (#6845) --- src/main/java/ch/njol/skript/lang/Section.java | 12 +++++++++--- .../java/ch/njol/skript/lang/TriggerSection.java | 12 +++++++++--- .../6843-current section reference issue.sk | 9 +++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/test/skript/tests/regressions/6843-current section reference issue.sk diff --git a/src/main/java/ch/njol/skript/lang/Section.java b/src/main/java/ch/njol/skript/lang/Section.java index c511e88b2a2..5a665165eff 100644 --- a/src/main/java/ch/njol/skript/lang/Section.java +++ b/src/main/java/ch/njol/skript/lang/Section.java @@ -27,6 +27,7 @@ import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Supplier; @@ -81,12 +82,17 @@ public abstract boolean init(Expression[] expressions, * (although the loaded code may change it), the calling code must deal with this. */ protected void loadCode(SectionNode sectionNode) { - List currentSections = getParser().getCurrentSections(); - currentSections.add(this); + ParserInstance parser = getParser(); + List previousSections = parser.getCurrentSections(); + + List sections = new ArrayList<>(previousSections); + sections.add(this); + parser.setCurrentSections(sections); + try { setTriggerItems(ScriptLoader.loadItems(sectionNode)); } finally { - currentSections.remove(currentSections.size() - 1); + parser.setCurrentSections(previousSections); } } diff --git a/src/main/java/ch/njol/skript/lang/TriggerSection.java b/src/main/java/ch/njol/skript/lang/TriggerSection.java index d1343d43605..e7945784752 100644 --- a/src/main/java/ch/njol/skript/lang/TriggerSection.java +++ b/src/main/java/ch/njol/skript/lang/TriggerSection.java @@ -24,6 +24,7 @@ import org.bukkit.event.Event; import org.eclipse.jdt.annotation.Nullable; +import java.util.ArrayList; import java.util.List; /** @@ -42,12 +43,17 @@ protected TriggerSection(List items) { } protected TriggerSection(SectionNode node) { - List currentSections = ParserInstance.get().getCurrentSections(); - currentSections.add(this); + ParserInstance parser = ParserInstance.get(); + List previousSections = parser.getCurrentSections(); + + List sections = new ArrayList<>(previousSections); + sections.add(this); + parser.setCurrentSections(sections); + try { setTriggerItems(ScriptLoader.loadItems(node)); } finally { - currentSections.remove(currentSections.size() - 1); + parser.setCurrentSections(previousSections); } } diff --git a/src/test/skript/tests/regressions/6843-current section reference issue.sk b/src/test/skript/tests/regressions/6843-current section reference issue.sk new file mode 100644 index 00000000000..20e8beb20ef --- /dev/null +++ b/src/test/skript/tests/regressions/6843-current section reference issue.sk @@ -0,0 +1,9 @@ +test "outdated current section references": + parse: + loop {_list::*}: + spawn a sheep at {_loc}: + set {_e} to event-entity + loop {_list::*}: + set {_var} to loop-value + set {_var} to loop-value-1 + assert last parse logs is not set with "loop-value and loop-value-1 should've worked" From e65fe2f7934259468d6bd3d46a88c232f073dfaa Mon Sep 17 00:00:00 2001 From: EquipableMC <66171067+EquipableMC@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:21:27 -0400 Subject: [PATCH 45/48] Support for LogLevel in EffLog (#6659) * Improved EffLog Added wats to declare severity when logging * Update EffLog.java * Update EffLog.java Updated `@Since` to include when severity to logs was added * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java Adds the `[Skript]` prefix to `Level.INFO` * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java Fixes a spacing issue * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java * Update EffLog.java changes to use .toString instead of String.valueOf * Update EffLog.java * Update EffLog.java * Update EffLog.java --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Moderocky --- .../java/ch/njol/skript/effects/EffLog.java | 84 +++++++++++-------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/src/main/java/ch/njol/skript/effects/EffLog.java b/src/main/java/ch/njol/skript/effects/EffLog.java index bce9d3bae76..a3b7cc23f44 100644 --- a/src/main/java/ch/njol/skript/effects/EffLog.java +++ b/src/main/java/ch/njol/skript/effects/EffLog.java @@ -46,28 +46,31 @@ import ch.njol.util.Closeable; import ch.njol.util.Kleenean; -/** - * @author Peter Güttinger - */ @Name("Log") @Description({"Writes text into a .log file. Skript will write these files to /plugins/Skript/logs.", "NB: Using 'server.log' as the log file will write to the default server log. Omitting the log file altogether will log the message as '[Skript] [<script>.sk] <message>' in the server log."}) -@Examples({"on place of TNT:", - " log \"%player% placed TNT in %world% at %location of block%\" to \"tnt/placement.log\""}) -@Since("2.0") +@Examples({ + "on join:", + "\tlog \"%player% has just joined the server!\"", + "on world change:", + "\tlog \"Someone just went to %event-world%!\" to file \"worldlog/worlds.log\"", + "on command:", + "\tlog \"%player% just executed %full command%!\" to file \"server/commands.log\" with a severity of warning" +}) +@Since("2.0, INSERT VERSION (severities)") public class EffLog extends Effect { static { - Skript.registerEffect(EffLog.class, "log %strings% [(to|in) [file[s]] %-strings%]"); + Skript.registerEffect(EffLog.class, "log %strings% [(to|in) [file[s]] %-strings%] [with [the|a] severity [of] (1:warning|2:severe)]"); } - private final static File logsFolder = new File(Skript.getInstance().getDataFolder(), "logs"); + private static final File logsFolder = new File(Skript.getInstance().getDataFolder(), "logs"); final static HashMap writers = new HashMap<>(); static { Skript.closeOnDisable(new Closeable() { @Override public void close() { - for (final PrintWriter pw : writers.values()) + for (PrintWriter pw : writers.values()) pw.close(); } }); @@ -77,42 +80,55 @@ public void close() { private Expression messages; @Nullable private Expression files; - + + private Level logLevel = Level.INFO; + private static String getLogPrefix(Level logLevel) { + String timestamp = SkriptConfig.formatDate(System.currentTimeMillis()); + if (logLevel == Level.INFO) + return "[" + timestamp + "]"; + return "[" + timestamp + " " + logLevel + "]"; + } + @SuppressWarnings({"unchecked", "null"}) @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parser) { + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parser) { messages = (Expression) exprs[0]; files = (Expression) exprs[1]; + if (parser.mark == 1) { + logLevel = Level.WARNING; + } else if (parser.mark == 2) { + logLevel = Level.SEVERE; + } return true; } @SuppressWarnings("resource") @Override - protected void execute(final Event e) { - for (final String message : messages.getArray(e)) { + protected void execute(Event event) { + for (String message : messages.getArray(event)) { if (files != null) { - for (String s : files.getArray(e)) { - s = s.toLowerCase(Locale.ENGLISH); - if (!s.endsWith(".log")) - s += ".log"; - if (s.equals("server.log")) { - SkriptLogger.LOGGER.log(Level.INFO, message); + for (String logFile : files.getArray(event)) { + logFile = logFile.toLowerCase(Locale.ENGLISH); + if (!logFile.endsWith(".log")) + logFile += ".log"; + if (logFile.equals("server.log")) { + SkriptLogger.LOGGER.log(logLevel, message); continue; } - PrintWriter w = writers.get(s); - if (w == null) { - final File f = new File(logsFolder, s); // REMIND what if s contains '..'? + PrintWriter logWriter = writers.get(logFile); + if (logWriter == null) { + File logFolder = new File(logsFolder, logFile); // REMIND what if logFile contains '..'? try { - f.getParentFile().mkdirs(); - w = new PrintWriter(new BufferedWriter(new FileWriter(f, true))); - writers.put(s, w); - } catch (final IOException ex) { - Skript.error("Cannot write to log file '" + s + "' (" + f.getPath() + "): " + ExceptionUtils.toString(ex)); + logFolder.getParentFile().mkdirs(); + logWriter = new PrintWriter(new BufferedWriter(new FileWriter(logFolder, true))); + writers.put(logFile, logWriter); + } catch (IOException ex) { + Skript.error("Cannot write to log file '" + logFile + "' (" + logFolder.getPath() + "): " + ExceptionUtils.toString(ex)); return; } } - w.println("[" + SkriptConfig.formatDate(System.currentTimeMillis()) + "] " + message); - w.flush(); + logWriter.println(getLogPrefix(logLevel) + " " + message); + logWriter.flush(); } } else { Trigger t = getTrigger(); @@ -122,13 +138,15 @@ protected void execute(final Event e) { if (script != null) scriptName = script.getConfig().getFileName(); } - Skript.info("[" + scriptName + "] " + message); + SkriptLogger.LOGGER.log(logLevel, "[" + scriptName + "] " + message); } } } - + @Override - public String toString(final @Nullable Event e, final boolean debug) { - return "log " + messages.toString(e, debug) + (files != null ? " to " + files.toString(e, debug) : ""); + public String toString(@Nullable Event event, boolean debug) { + return "log " + messages.toString(event, debug) + + (files != null ? " to " + files.toString(event, debug) : "") + + (logLevel != Level.INFO ? "with severity " + logLevel.toString().toLowerCase(Locale.ENGLISH) : ""); } } From f1e0dd63b86f1e04f3bbf52e243d03faf693d287 Mon Sep 17 00:00:00 2001 From: sovdee <10354869+sovdeeth@users.noreply.github.com> Date: Tue, 2 Jul 2024 00:33:18 +0200 Subject: [PATCH 46/48] add allowLookups option to offlineplayer function (#6831) * add allowLookups option to offlineplayer function * typo * less ambiguous wording in description --- .../skript/classes/data/DefaultFunctions.java | 66 ++++++++++++++----- .../tests/syntaxes/functions/offlinePlayer.sk | 7 ++ 2 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 src/test/skript/tests/syntaxes/functions/offlinePlayer.sk diff --git a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java index afc46cba346..525e639fbc3 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -18,6 +18,7 @@ */ package ch.njol.skript.classes.data; +import ch.njol.skript.Skript; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.Expression; import ch.njol.skript.lang.function.FunctionEvent; @@ -45,7 +46,9 @@ import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.ArrayList; import java.util.Calendar; +import java.util.List; import java.util.UUID; public class DefaultFunctions { @@ -556,23 +559,54 @@ public Player[] executeSimple(Object[][] params) { .examples("set {_p} to player(\"Notch\") # will return an online player whose name is or starts with 'Notch'", "set {_p} to player(\"Notch\", true) # will return the only online player whose name is 'Notch'", "set {_p} to player(\"069a79f4-44e9-4726-a5be-fca90e38aaf5\") # if player is offline") .since("2.8.0"); - Functions.registerFunction(new SimpleJavaFunction("offlineplayer", new Parameter[] { - new Parameter<>("nameOrUUID", DefaultClasses.STRING, true, null) - }, DefaultClasses.OFFLINE_PLAYER, true) { - @Override - public OfflinePlayer[] executeSimple(Object[][] params) { - String name = (String) params[0][0]; - UUID uuid = null; - if (name.length() > 16 || name.contains("-")) { // shortcut - try { - uuid = UUID.fromString(name); - } catch (IllegalArgumentException ignored) {} + { // offline player function + boolean hasIfCached = Skript.methodExists(Bukkit.class, "getOfflinePlayerIfCached", String.class); + + List> params = new ArrayList<>(); + params.add(new Parameter<>("nameOrUUID", DefaultClasses.STRING, true, null)); + if (hasIfCached) + params.add(new Parameter<>("allowLookups", DefaultClasses.BOOLEAN, true, new SimpleLiteral<>(true, true))); + + Functions.registerFunction(new SimpleJavaFunction("offlineplayer", params.toArray(new Parameter[0]), + DefaultClasses.OFFLINE_PLAYER, true) { + @Override + public OfflinePlayer[] executeSimple(Object[][] params) { + String name = (String) params[0][0]; + UUID uuid = null; + if (name.length() > 16 || name.contains("-")) { // shortcut + try { + uuid = UUID.fromString(name); + } catch (IllegalArgumentException ignored) { + } + } + OfflinePlayer result; + + if (uuid != null) { + result = Bukkit.getOfflinePlayer(uuid); // doesn't do lookups + } else if (hasIfCached && !((Boolean) params[1][0])) { + result = Bukkit.getOfflinePlayerIfCached(name); + if (result == null) + return new OfflinePlayer[0]; + } else { + result = Bukkit.getOfflinePlayer(name); + } + + return CollectionUtils.array(result); } - return CollectionUtils.array(uuid != null ? Bukkit.getOfflinePlayer(uuid) : Bukkit.getOfflinePlayer(name)); - } - }).description("Returns a offline player from their name or UUID. This function will still return the player if they're online.") - .examples("set {_p} to offlineplayer(\"Notch\")", "set {_p} to offlineplayer(\"069a79f4-44e9-4726-a5be-fca90e38aaf5\")") - .since("2.8.0"); + + }).description( + "Returns a offline player from their name or UUID. This function will still return the player if they're online. " + + "If Paper 1.16.5+ is used, the 'allowLookup' parameter can be set to false to prevent this function from doing a " + + "web lookup for players who have not joined before. Lookups can cause lag spikes of up to multiple seconds, so " + + "use offline players with caution." + ) + .examples( + "set {_p} to offlineplayer(\"Notch\")", + "set {_p} to offlineplayer(\"069a79f4-44e9-4726-a5be-fca90e38aaf5\")", + "set {_p} to offlineplayer(\"Notch\", false)" + ) + .since("2.8.0, INSERT VERSION (prevent lookups)"); + } // end offline player function Functions.registerFunction(new SimpleJavaFunction("isNaN", numberParam, DefaultClasses.BOOLEAN, true) { @Override diff --git a/src/test/skript/tests/syntaxes/functions/offlinePlayer.sk b/src/test/skript/tests/syntaxes/functions/offlinePlayer.sk new file mode 100644 index 00000000000..b96d67a7487 --- /dev/null +++ b/src/test/skript/tests/syntaxes/functions/offlinePlayer.sk @@ -0,0 +1,7 @@ +test "offline player function": + set {_lookup} to offlineplayer("Notch") + assert {_lookup} is set with "Failed to look up offline player" + +test "offline player function no lookup" when running minecraft "1.16": + set {_non-lookup} to offlineplayer("Dinnerbone", false) + assert {_non-lookup} is not set with "Looked up offline player when told not to" From 5c3074ec531a2c43d17ceced1cb9569a63d3de3e Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Mon, 1 Jul 2024 15:38:20 -0700 Subject: [PATCH 47/48] EnchantmentType - prepare for MC 1.21 custom enchantments (#6687) * EnchantmentType - prepare for MC 1.21 custom enchantments * EnchantmentType - change wording, key to namespace * EnchantmentType - clean up repetitive code * EnchantmentType - to lower case * default.lang - add this back and deal with it later * Aliases/default.lang - change "of" location in lang file * EnchantmentUtils - more changes * EnchantmentUtils - fix stream issue on older java versions * EnchantmentUtils - small cleanup * Update src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java Co-authored-by: Patrick Miller * Update src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java Co-authored-by: Patrick Miller * EnchantmentUtils - small changes as suggested - skip adding names if not in lang file - toString should return full namespaced key if no name found * EnchantmentUtils - more changes * EnchantmentUtils - update serialization * EnchantmentUtils - change up how classinfo is done * BukkitClasses - move usage to EnchantmentUtils * pull-6687-enchantment-update.sk - add test * BukkitClasses - add note regarding namespaces and custom enchants * EnchantmentUtils - only register listener if Registry is not found * Update src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java Co-authored-by: Patrick Miller --------- Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Patrick Miller Co-authored-by: Moderocky --- .../java/ch/njol/skript/aliases/Aliases.java | 2 +- .../skript/bukkitutil/EnchantmentUtils.java | 151 +++++++++++++++++- .../skript/classes/data/BukkitClasses.java | 84 ++-------- .../ch/njol/skript/util/EnchantmentType.java | 104 +++++------- src/main/resources/lang/default.lang | 103 ++++++------ .../pull-6687-enchantment-update.sk | 11 ++ 6 files changed, 262 insertions(+), 193 deletions(-) create mode 100644 src/test/skript/tests/regressions/pull-6687-enchantment-update.sk diff --git a/src/main/java/ch/njol/skript/aliases/Aliases.java b/src/main/java/ch/njol/skript/aliases/Aliases.java index fe1a3a624c4..111ffc5adcd 100644 --- a/src/main/java/ch/njol/skript/aliases/Aliases.java +++ b/src/main/java/ch/njol/skript/aliases/Aliases.java @@ -268,7 +268,7 @@ public static ItemType parseItemType(String s) { } String lc = s.toLowerCase(Locale.ENGLISH); - String of = Language.getSpaced("enchantments.of").toLowerCase(); + String of = Language.getSpaced("of").toLowerCase(); int c = -1; outer: while ((c = lc.indexOf(of, c + 1)) != -1) { ItemType t2 = t.clone(); diff --git a/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java b/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java index fcebff78917..7247bba5d37 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/EnchantmentUtils.java @@ -18,26 +18,165 @@ */ package ch.njol.skript.bukkitutil; +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.skript.localization.Language; +import ch.njol.util.StringUtils; +import ch.njol.yggdrasil.Fields; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.enchantments.Enchantment; -import org.eclipse.jdt.annotation.Nullable; +import org.jetbrains.annotations.Nullable; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import ch.njol.skript.Skript; +import java.io.StreamCorruptedException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; /** * Maps enchantments to their keys. */ public class EnchantmentUtils { + private static final Map NAMES = new HashMap<>(); + private static final Map PATTERNS = new HashMap<>(); + private static final boolean HAS_REGISTRY = BukkitUtils.registryExists("ENCHANTMENT"); + + static { + if (!HAS_REGISTRY) { + Language.addListener(() -> { + NAMES.clear(); + PATTERNS.clear(); + for (Enchantment enchantment : Enchantment.values()) { + NamespacedKey key = enchantment.getKey(); + final String[] names = Language.getList("enchantments." + key.getKey()); + + if (!names[0].startsWith("enchantments.")) { + NAMES.put(enchantment, names[0]); + // Add lang file names + for (String name : names) + PATTERNS.put(name.toLowerCase(Locale.ENGLISH), enchantment); + } + // If Minecraft provided, add key without namespace and underscores (ex: "fire aspect") + if (key.getNamespace().equalsIgnoreCase(NamespacedKey.MINECRAFT)) + PATTERNS.put(key.getKey().replace("_", " "), enchantment); + // Add full namespaced key as pattern (ex: "minecraft:fire_aspect", "custom:floopy_floopy") + PATTERNS.put(key.toString(), enchantment); + } + }); + } + } + public static String getKey(Enchantment enchantment) { - return enchantment.getKey().getKey(); + return enchantment.getKey().toString(); } @Nullable public static Enchantment getByKey(String key) { - return Enchantment.getByKey(NamespacedKey.minecraft(key)); + if (!key.contains(":")) { + // Old method for old variables + return Enchantment.getByKey(NamespacedKey.minecraft(key)); + } else { + NamespacedKey namespacedKey = NamespacedKey.fromString(key); + if (namespacedKey == null) + return null; + + if (HAS_REGISTRY) { + return Registry.ENCHANTMENT.get(namespacedKey); + } else { + return Enchantment.getByKey(namespacedKey); + } + } + } + + @Nullable + public static Enchantment parseEnchantment(String s) { + return PATTERNS.get(s); + } + + @SuppressWarnings("null") + public static Collection getNames() { + return NAMES.values(); + } + + @SuppressWarnings("null") + public static String toString(final Enchantment enchantment) { + // If we have a name in the lang file, return that first + if (NAMES.containsKey(enchantment)) + return NAMES.get(enchantment); + + // If no name is available, return the namespaced key + return enchantment.getKey().toString(); + } + + // REMIND flags? + @SuppressWarnings("null") + public static String toString(final Enchantment enchantment, final int flags) { + return toString(enchantment); + } + + public static ClassInfo createClassInfo() { + return new ClassInfo<>(Enchantment.class, "enchantment") + .parser(new Parser<>() { + @Override + @Nullable + public Enchantment parse(final String s, final ParseContext context) { + return EnchantmentUtils.parseEnchantment(s); + } + + @Override + public String toString(final Enchantment e, final int flags) { + return EnchantmentUtils.toString(e, flags); + } + + @Override + public String toVariableNameString(final Enchantment e) { + return "" + EnchantmentUtils.getKey(e); + } + }).serializer(new Serializer<>() { + @Override + public Fields serialize(final Enchantment ench) { + final Fields f = new Fields(); + f.putObject("key", EnchantmentUtils.getKey(ench)); + return f; + } + + @Override + public boolean canBeInstantiated() { + return false; + } + + @Override + public void deserialize(final Enchantment o, final Fields f) { + assert false; + } + + @Override + protected Enchantment deserialize(final Fields fields) throws StreamCorruptedException { + final String key = fields.getObject("key", String.class); + assert key != null; // If a key happens to be null, something went really wrong... + final Enchantment e = EnchantmentUtils.getByKey(key); + if (e == null) + throw new StreamCorruptedException("Invalid enchantment " + key); + return e; + } + + @Override + @Nullable + public Enchantment deserialize(String s) { + return Enchantment.getByName(s); + } + + @Override + public boolean mustSyncDeserialization() { + return false; + } + }) + .usage(StringUtils.join(EnchantmentUtils.getNames(), ", ")) + .supplier(Enchantment.values()); } } diff --git a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java index b48c69149d7..231d5ef9986 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -100,7 +100,6 @@ import ch.njol.skript.localization.Language; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.BlockUtils; -import ch.njol.skript.util.EnchantmentType; import ch.njol.skript.util.PotionEffectUtils; import ch.njol.skript.util.StringMode; import ch.njol.util.StringUtils; @@ -1078,12 +1077,12 @@ protected boolean canBeInstantiated() { public PotionEffectType parse(final String s, final ParseContext context) { return PotionEffectUtils.parseType(s); } - + @Override public String toString(final PotionEffectType p, final int flags) { return PotionEffectUtils.toString(p, flags); } - + @Override public String toVariableNameString(final PotionEffectType p) { return "" + p.getName(); @@ -1223,74 +1222,23 @@ public boolean mustSyncDeserialization() { return true; } })); - - Classes.registerClass(new ClassInfo<>(Enchantment.class, "enchantment") + + ClassInfo enchantmentClassInfo; + if (BukkitUtils.registryExists("ENCHANTMENT")) { + enchantmentClassInfo = new RegistryClassInfo<>(Enchantment.class, Registry.ENCHANTMENT, "enchantment", "enchantments"); + } else { + enchantmentClassInfo = EnchantmentUtils.createClassInfo(); + } + Classes.registerClass(enchantmentClassInfo .user("enchantments?") .name("Enchantment") .description("An enchantment, e.g. 'sharpness' or 'fortune'. Unlike enchantment type " + - "this type has no level, but you usually don't need to use this type anyway.") - .usage(StringUtils.join(EnchantmentType.getNames(), ", ")) + "this type has no level, but you usually don't need to use this type anyway.", + "NOTE: Minecraft namespaces are supported, ex: 'minecraft:basalt_deltas'.", + "As of Minecraft 1.21 this will also support custom enchantments using namespaces, ex: 'myenchants:explosive'.") .examples("") .since("1.4.6") - .before("enchantmenttype") - .supplier(Enchantment.values()) - .parser(new Parser() { - @Override - @Nullable - public Enchantment parse(final String s, final ParseContext context) { - return EnchantmentType.parseEnchantment(s); - } - - @Override - public String toString(final Enchantment e, final int flags) { - return EnchantmentType.toString(e, flags); - } - - @Override - public String toVariableNameString(final Enchantment e) { - return "" + EnchantmentUtils.getKey(e); - } - }) - .serializer(new Serializer() { - @Override - public Fields serialize(final Enchantment ench) { - final Fields f = new Fields(); - f.putObject("key", EnchantmentUtils.getKey(ench)); - return f; - } - - @Override - public boolean canBeInstantiated() { - return false; - } - - @Override - public void deserialize(final Enchantment o, final Fields f) { - assert false; - } - - @Override - protected Enchantment deserialize(final Fields fields) throws StreamCorruptedException { - final String key = fields.getObject("key", String.class); - assert key != null; // If a key happens to be null, something went really wrong... - final Enchantment e = EnchantmentUtils.getByKey(key); - if (e == null) - throw new StreamCorruptedException("Invalid enchantment " + key); - return e; - } - - // return "" + e.getId(); - @Override - @Nullable - public Enchantment deserialize(String s) { - return Enchantment.getByName(s); - } - - @Override - public boolean mustSyncDeserialization() { - return false; - } - })); + .before("enchantmenttype")); Material[] allMaterials = Material.values(); Classes.registerClass(new ClassInfo<>(Material.class, "material") @@ -1516,12 +1464,12 @@ public boolean canParse(ParseContext context) { @Override public String toString(EnchantmentOffer eo, int flags) { - return EnchantmentType.toString(eo.getEnchantment(), flags) + " " + eo.getEnchantmentLevel(); + return EnchantmentUtils.toString(eo.getEnchantment(), flags) + " " + eo.getEnchantmentLevel(); } @Override public String toVariableNameString(EnchantmentOffer eo) { - return "offer:" + EnchantmentType.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel(); + return "offer:" + EnchantmentUtils.toString(eo.getEnchantment()) + "=" + eo.getEnchantmentLevel(); } })); diff --git a/src/main/java/ch/njol/skript/util/EnchantmentType.java b/src/main/java/ch/njol/skript/util/EnchantmentType.java index 1ef13893fc9..97f4b8d22d0 100644 --- a/src/main/java/ch/njol/skript/util/EnchantmentType.java +++ b/src/main/java/ch/njol/skript/util/EnchantmentType.java @@ -18,30 +18,27 @@ */ package ch.njol.skript.util; -import java.util.Collection; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Pattern; - -import org.bukkit.enchantments.Enchantment; -import org.eclipse.jdt.annotation.Nullable; - import ch.njol.skript.aliases.ItemType; import ch.njol.skript.bukkitutil.EnchantmentUtils; -import ch.njol.skript.localization.Language; +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.registrations.Classes; import ch.njol.yggdrasil.YggdrasilSerializable; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Pattern; /** * @author Peter Güttinger */ public class EnchantmentType implements YggdrasilSerializable { - - private final static String LANGUAGE_NODE = "enchantments"; - + + private static @Nullable Parser ENCHANTMENT_PARSER = null; private final Enchantment type; private final int level; - + /** * Used for deserialisation only */ @@ -50,39 +47,41 @@ private EnchantmentType() { type = null; level = -1; } - + public EnchantmentType(final Enchantment type) { assert type != null; this.type = type; this.level = -1; } + public EnchantmentType(final Enchantment type, final int level) { assert type != null; this.type = type; this.level = level; } - + /** * @return level or 1 if level == -1 */ public int getLevel() { return level == -1 ? 1 : level; } - + /** * @return the internal level, can be -1 */ public int getInternalLevel() { return level; } - + @Nullable public Enchantment getType() { return type; } - + /** * Checks whether the given item type has this enchantment. + * * @param item the item to be checked. * @deprecated Use {@link ItemType#hasEnchantments(Enchantment...)} */ @@ -90,77 +89,50 @@ public Enchantment getType() { public boolean has(final ItemType item) { return item.hasEnchantments(type); } - + @Override public String toString() { - return toString(type) + (level == -1 ? "" : " " + level); - } - - @SuppressWarnings("null") - public static String toString(final Enchantment e) { - return NAMES.get(e); - } - - // REMIND flags? - @SuppressWarnings("null") - public static String toString(final Enchantment e, final int flags) { - return NAMES.get(e); - } - - private final static Map NAMES = new HashMap<>(); - private final static Map PATTERNS = new HashMap<>(); - - static { - Language.addListener(() -> { - NAMES.clear(); - for (Enchantment e : Enchantment.values()) { - assert e != null; - final String[] names = Language.getList(LANGUAGE_NODE + ".names." + EnchantmentUtils.getKey(e)); - NAMES.put(e, names[0]); - - for (String name : names) - PATTERNS.put(name.toLowerCase(Locale.ENGLISH), e); - } - }); + return EnchantmentUtils.toString(type) + (level == -1 ? "" : " " + level); } - + @SuppressWarnings("null") private final static Pattern pattern = Pattern.compile(".+ \\d+"); - + /** * Parses an enchantment type from string. This includes an {@link Enchantment} * and its level. + * * @param s String to parse. * @return Enchantment type, or null if parsing failed. */ @Nullable public static EnchantmentType parse(final String s) { + if (ENCHANTMENT_PARSER == null) { + ClassInfo classInfo = Classes.getExactClassInfo(Enchantment.class); + if (classInfo == null) { + throw new IllegalStateException("Enchantment ClassInfo not found"); + } + ENCHANTMENT_PARSER = (Parser) classInfo.getParser(); + if (ENCHANTMENT_PARSER == null) { + throw new IllegalStateException("Enchantment parser not found"); + } + } if (pattern.matcher(s).matches()) { String name = s.substring(0, s.lastIndexOf(' ')); assert name != null; - final Enchantment ench = parseEnchantment(name); + final Enchantment ench = ENCHANTMENT_PARSER.parse(name, ParseContext.DEFAULT); if (ench == null) return null; String level = s.substring(s.lastIndexOf(' ') + 1); assert level != null; return new EnchantmentType(ench, Utils.parseInt(level)); } - final Enchantment ench = parseEnchantment(s); + final Enchantment ench = ENCHANTMENT_PARSER.parse(s, ParseContext.DEFAULT); if (ench == null) return null; return new EnchantmentType(ench, -1); } - - @Nullable - public static Enchantment parseEnchantment(final String s) { - return PATTERNS.get(s.toLowerCase(Locale.ENGLISH)); - } - - @SuppressWarnings("null") - public static Collection getNames() { - return NAMES.values(); - } - + @Override public int hashCode() { final int prime = 31; @@ -169,7 +141,7 @@ public int hashCode() { result = prime * result + type.hashCode(); return result; } - + @Override public boolean equals(final @Nullable Object obj) { if (this == obj) @@ -183,5 +155,5 @@ public boolean equals(final @Nullable Object obj) { return false; return type.equals(other.type); } - + } diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 38d8cc68f1f..7ba10ec99f7 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -13,6 +13,7 @@ not: not # not a: not a(n) # @a:not a @an:not an @x:not neither: neither nor: nor +of: of genders: 0: @@ -47,58 +48,56 @@ aliases: # -- Enchantments -- enchantments: - of: of - names: - protection: Protection - fire_protection: Fire Protection - feather_falling: Feather Falling - blast_protection: Blast Protection - projectile_protection: Projectile Protection - respiration: Respiration - aqua_affinity: Aqua Affinity - sharpness: Sharpness - smite: Smite - bane_of_arthropods: Bane of Arthropods - knockback: Knockback - fire_aspect: Fire Aspect - looting: Looting - efficiency: Efficiency - silk_touch: Silk Touch - unbreaking: Unbreaking - fortune: Fortune - power: Power - punch: Punch - flame: Flame - infinity: Infinity - thorns: Thorns - luck: Luck of the Sea - lure: Lure - depth_strider: Depth Strider - mending: Mending - frost_walker: Frost Walker - vanishing_curse: Curse of Vanishing - binding_curse: Curse of Binding - # It's sweeping on 1.10 and below for some reason. - sweeping: Sweeping Edge - sweeping_edge: Sweeping Edge - # New 1.13 Enchantments - channeling: Channeling, Channelling - riptide: Riptide - impaling: Impaling - loyalty: Loyalty - luck_of_the_sea: Luck of The Sea - # new 1.14 Enchantments - multishot: Multishot, Multi-Shot - piercing: Piercing - quick_charge: Quick Charge - # new 1.16 Enchantment - soul_speed: Soul Speed - # new 1.19 Enchantment - swift_sneak: Swift Sneak - # new 1.21 Enchantments (added in 1.20.5 experimental) - density: Density - breach: Breach - wind_burst: Wind Burst + protection: Protection + fire_protection: Fire Protection + feather_falling: Feather Falling + blast_protection: Blast Protection + projectile_protection: Projectile Protection + respiration: Respiration + aqua_affinity: Aqua Affinity + sharpness: Sharpness + smite: Smite + bane_of_arthropods: Bane of Arthropods + knockback: Knockback + fire_aspect: Fire Aspect + looting: Looting + efficiency: Efficiency + silk_touch: Silk Touch + unbreaking: Unbreaking + fortune: Fortune + power: Power + punch: Punch + flame: Flame + infinity: Infinity + thorns: Thorns + luck: Luck of the Sea + lure: Lure + depth_strider: Depth Strider + mending: Mending + frost_walker: Frost Walker + vanishing_curse: Curse of Vanishing + binding_curse: Curse of Binding + # It's sweeping on 1.10 and below for some reason. + sweeping: Sweeping Edge + sweeping_edge: Sweeping Edge + # New 1.13 Enchantments + channeling: Channeling, Channelling + riptide: Riptide + impaling: Impaling + loyalty: Loyalty + luck_of_the_sea: Luck of The Sea + # new 1.14 Enchantments + multishot: Multishot, Multi-Shot + piercing: Piercing + quick_charge: Quick Charge + # new 1.16 Enchantment + soul_speed: Soul Speed + # new 1.19 Enchantment + swift_sneak: Swift Sneak + # new 1.21 Enchantments (added in 1.20.5 experimental) + density: Density + breach: Breach + wind_burst: Wind Burst # -- Potion Effects -- potions: diff --git a/src/test/skript/tests/regressions/pull-6687-enchantment-update.sk b/src/test/skript/tests/regressions/pull-6687-enchantment-update.sk new file mode 100644 index 00000000000..94f4d9a3c42 --- /dev/null +++ b/src/test/skript/tests/regressions/pull-6687-enchantment-update.sk @@ -0,0 +1,11 @@ +test "Enchantment Registry Update": + set {_i} to diamond sword of unbreaking 3 and sharpness 10 + + assert enchantment level of unbreaking of {_i} = 3 with "Unbreaking enchant on {_i} should have been 3" + assert enchantment level of sharpness of {_i} = 10 with "Sharpness enchant on {_i} should have been 10" + + set enchantment level of lure of {_i} to 5 + assert enchantment level of lure of {_i} = 5 with "Lure enchant on {_i} should have been 5" + + set enchantment level of sharpness of {_i} to 5 + assert enchantment level of sharpness of {_i} = 5 with "Sharpness enchant on {_i} should have been 5" From 28fbc6c24cb563a15b3e03fd24e2ff88d7711ec7 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Mon, 1 Jul 2024 18:43:30 -0400 Subject: [PATCH 48/48] More Keyword Tweaks (#6694) * Replace GroupKeyword with ChoiceKeyword ChoiceKeywords add support for more advanced choice groups * Fix ending keywords * Fix depth --------- Co-authored-by: Moderocky Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- .../java/ch/njol/skript/patterns/Keyword.java | 142 +++++++++++------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/src/main/java/ch/njol/skript/patterns/Keyword.java b/src/main/java/ch/njol/skript/patterns/Keyword.java index 91dca7d86b9..c4159af547b 100644 --- a/src/main/java/ch/njol/skript/patterns/Keyword.java +++ b/src/main/java/ch/njol/skript/patterns/Keyword.java @@ -18,10 +18,17 @@ */ package ch.njol.skript.patterns; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSet; +import org.jetbrains.annotations.Contract; + import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; /** @@ -42,57 +49,49 @@ abstract class Keyword { * @param first The pattern to build keywords from. * @return A list of all keywords within first. */ + @Contract("_ -> new") public static Keyword[] buildKeywords(PatternElement first) { + return buildKeywords(first, true, 0); + } + + /** + * Builds a list of keywords starting from the provided pattern element. + * @param first The pattern to build keywords from. + * @param starting Whether this is the start of a pattern. + * @return A list of all keywords within first. + */ + @Contract("_, _, _ -> new") + private static Keyword[] buildKeywords(PatternElement first, boolean starting, int depth) { List keywords = new ArrayList<>(); PatternElement next = first; - boolean starting = true; // whether it is the start of the pattern - boolean ending = next.next == null; // whether it is the end of the pattern while (next != null) { if (next instanceof LiteralPatternElement) { // simple literal strings are keywords String literal = next.toString().trim(); while (literal.contains(" ")) literal = literal.replace(" ", " "); - keywords.add(new SimpleKeyword(literal, starting, ending)); - } else if (next instanceof ChoicePatternElement) { // this element might contain some keywords - List choiceElements = flatten(next); - if (choiceElements.stream().allMatch(e -> e instanceof LiteralPatternElement)) { - // all elements are literals, and this is a choice, meaning one of them must be required - // thus, we build a keyword that requires one of them to be present. - List groupKeywords = choiceElements.stream() - .map(e -> { - String literal = e.toString().trim(); - while (literal.contains(" ")) - literal = literal.replace(" ", " "); - return literal; - }) - .collect(Collectors.toList()); - keywords.add(new GroupKeyword(groupKeywords, starting, ending)); - } - } else if (next instanceof GroupPatternElement) { // groups need to be unwrapped (they might contain choices) - next = ((GroupPatternElement) next).getPatternElement(); - continue; + if (!literal.isEmpty()) // empty string is not useful + keywords.add(new SimpleKeyword(literal, starting, next.next == null)); + } else if (depth <= 1 && next instanceof ChoicePatternElement) { // attempt to build keywords from choices + final boolean finalStarting = starting; + final int finalDepth = depth; + // build the keywords for each choice + Set> choices = ((ChoicePatternElement) next).getPatternElements().stream() + .map(element -> buildKeywords(element, finalStarting, finalDepth)) + .map(ImmutableSet::copyOf) + .collect(Collectors.toSet()); + if (choices.stream().noneMatch(Collection::isEmpty)) // each choice must have a keyword for this to work + keywords.add(new ChoiceKeyword(choices)); // a keyword where only one choice much + } else if (next instanceof GroupPatternElement) { // add in keywords from the group + Collections.addAll(keywords, buildKeywords(((GroupPatternElement) next).getPatternElement(), starting, depth + 1)); } - starting = false; - next = next.next; - } - return keywords.toArray(new Keyword[0]); - } - /** - * A method for flattening a pattern element. - * For example, a {@link ChoicePatternElement} wraps multiple elements. This method unwraps it. - * @param element The element to flatten. - * @return A list of all pattern elements contained within element. - */ - private static List flatten(PatternElement element) { - if (element instanceof ChoicePatternElement) { - return ((ChoicePatternElement) element).getPatternElements().stream() - .flatMap(e -> flatten(e).stream()) - .collect(Collectors.toList()); - } else if (element instanceof GroupPatternElement) { - element = ((GroupPatternElement) element).getPatternElement(); + // a parse tag does not represent actual content in a pattern, therefore it should not affect starting + if (!(next instanceof ParseTagPatternElement)) + starting = false; + + next = next.originalNext; } - return Collections.singletonList(element); + return keywords.toArray(new Keyword[0]); } /** @@ -118,31 +117,70 @@ public boolean isPresent(String expr) { return expr.contains(keyword); } + @Override + public int hashCode() { + return Objects.hash(keyword, starting, ending); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof SimpleKeyword)) + return false; + SimpleKeyword other = (SimpleKeyword) obj; + return this.keyword.equals(other.keyword) && + this.starting == other.starting && + this.ending == other.ending; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("keyword", keyword) + .add("starting", starting) + .add("ending", ending) + .toString(); + } + } /** * A keyword implementation that requires at least one string out of a collection of strings to be present. */ - private static final class GroupKeyword extends Keyword { + private static final class ChoiceKeyword extends Keyword { - private final Collection keywords; - private final boolean starting, ending; + private final Set> choices; - GroupKeyword(Collection keywords, boolean starting, boolean ending) { - this.keywords = keywords; - this.starting = starting; - this.ending = ending; + ChoiceKeyword(Set> choices) { + this.choices = choices; } @Override public boolean isPresent(String expr) { - if (starting) - return keywords.stream().anyMatch(expr::startsWith); - if (ending) - return keywords.stream().anyMatch(expr::endsWith); - return keywords.stream().anyMatch(expr::contains); + return choices.stream().anyMatch(keywords -> keywords.stream().allMatch(keyword -> keyword.isPresent(expr))); } + @Override + public int hashCode() { + return Arrays.hashCode(choices.toArray()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof ChoiceKeyword)) + return false; + return choices.equals(((ChoiceKeyword) obj).choices); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("choices", choices.stream().map(Object::toString).collect(Collectors.joining(", "))) + .toString(); + } } }