From 31e0039fb0d39ed57e6e5deb04763c37a0a4cdfe Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 21 Nov 2024 08:03:20 +0000 Subject: [PATCH 01/20] Script type & expression. (#6702) * Create Script type. * Support script string/name conversion. * Script expression. * Add script lang entry. * Tests for script expression & names. * Support all scripts expression. * Script effects & tests. * Create dummy Script handle for disabled scripts. * Script reflection feature flag. * Restrict literal parsing to commands & parse. * Test feature flag for resolving name. * Split ExprScripts by feature to support disabled scripts. * Fix ExprName tests for new & old behaviour. * Add tests for disabled script handles. * Apply suggestions from code review Co-authored-by: Patrick Miller * Improve script loading/unloading safety. * Add feature check for script hotswapping. * Use expression stream. * Conformity for file names and proper loading safety. * Document validity & add condition support. * Add script is loaded condition + tests. * Some changes from code review. * More changes. * Apply suggestions from code review Co-authored-by: Patrick Miller * Clean up EffScriptFile and rename ExprScripts variations. * Finish code review changes. * Clean up script loader stuff. * Fix conflicts. * Suggested changes. * Apply suggestions from code review Co-authored-by: Patrick Miller * Finished. --------- Co-authored-by: Patrick Miller Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> --- src/main/java/ch/njol/skript/config/Config.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ch/njol/skript/config/Config.java b/src/main/java/ch/njol/skript/config/Config.java index f5564218249..b2e732b521e 100644 --- a/src/main/java/ch/njol/skript/config/Config.java +++ b/src/main/java/ch/njol/skript/config/Config.java @@ -23,6 +23,7 @@ import java.util.Set; import ch.njol.skript.log.SkriptLogger; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; From 9ba4a7ff3930dc641426cbe0b1d7cdcd0ba63350 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 11:36:46 +0100 Subject: [PATCH 02/20] Move script validator to config. --- src/main/java/ch/njol/skript/config/Config.java | 13 +++++++++++++ .../org/skriptlang/skript/lang/script/Script.java | 7 ++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/ch/njol/skript/config/Config.java b/src/main/java/ch/njol/skript/config/Config.java index b2e732b521e..0527ff9abbe 100644 --- a/src/main/java/ch/njol/skript/config/Config.java +++ b/src/main/java/ch/njol/skript/config/Config.java @@ -24,12 +24,14 @@ import ch.njol.skript.log.SkriptLogger; import org.jetbrains.annotations.ApiStatus; +import org.skriptlang.skript.util.Validated; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Represents a config file. */ +public class Config implements Comparable, Validated { public class Config implements Comparable, AnyNamed { /** @@ -53,6 +55,7 @@ public class Config implements Comparable, AnyNamed { String fileName; @Nullable Path file = null; + private final Validated validator = Validated.validator(); public Config(InputStream source, String fileName, @Nullable File file, boolean simple, boolean allowEmptySections, String defaultSeparator) throws IOException { @@ -405,6 +408,16 @@ public int compareTo(@Nullable Config other) { return fileName.compareTo(other.fileName); } + @Override + public void invalidate() throws UnsupportedOperationException { + this.validator.invalidate(); + } + + @Override + public boolean valid() { + return validator.valid(); + } + /** * @return The name of this config (excluding path and file extensions) */ diff --git a/src/main/java/org/skriptlang/skript/lang/script/Script.java b/src/main/java/org/skriptlang/skript/lang/script/Script.java index f0415b0b09a..a93d459b90b 100644 --- a/src/main/java/org/skriptlang/skript/lang/script/Script.java +++ b/src/main/java/org/skriptlang/skript/lang/script/Script.java @@ -31,9 +31,6 @@ public final class Script implements Validated, AnyNamed { private final List structures; - // Note: this class will eventually become a record, so its members should be final. - private transient final Validated validator = Validated.validator(); - /** * Creates a new Script to be used across the API. * Only one Script should be created per Config. A loaded Script may be obtained through {@link ch.njol.skript.ScriptLoader}. @@ -190,7 +187,7 @@ public EventRegistry eventRegistry() { */ @Override public void invalidate() { - this.validator.invalidate(); + this.config.invalidate(); } /** @@ -207,7 +204,7 @@ public void invalidate() { */ @Override public boolean valid() { - if (validator.valid()) { + if (config.valid()) { @Nullable File file = config.getFile(); return file == null || file.exists(); // If this is file-linked and that file was moved/deleted (e.g. this was disabled) From 28c97ff5a96326f676b0d7e9d4d8be6f06c1c053 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 11:36:52 +0100 Subject: [PATCH 03/20] Add config type. --- .../skript/classes/data/SkriptClasses.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 7e0fe03a44c..451ba5a5a63 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -12,6 +12,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.ScriptLoader; import ch.njol.skript.SkriptCommand; +import ch.njol.skript.config.Config; import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemData; import ch.njol.skript.aliases.ItemType; @@ -677,6 +678,40 @@ public String toVariableNameString(VisualEffect e) { .serializer(new YggdrasilSerializer()) ); + Classes.registerClass(new ClassInfo<>(Config.class, "config") + .user("configs?") + .name("Config") + .description("A configuration (or code) loaded by Skript, such as the config.sk or aliases.", + "Configs can be reloaded or navigated to find options.") + .usage("") + .examples("the skript config") + .since("INSERT VERSION") + .parser(new Parser() { + + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public @Nullable Config parse(String name, ParseContext context) { + return null; + } + + @Override + public String toString(Config config, int flags) { + return this.toVariableNameString(config); + } + + @Override + public String toVariableNameString(Config config) { + @Nullable File file = config.getFile(); + if (file == null) + return config.getFileName(); + return ExprScripts.FOLDER_PATH.relativize(file.toPath().toAbsolutePath()).toString(); + } + })); + Classes.registerClass(new ClassInfo<>(Script.class, "script") .user("scripts?") .name("Script") From 699524c976ea8f46772c7e2fa3dff956974aa6ab Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 17:32:13 +0100 Subject: [PATCH 04/20] Register config and node as types. --- .../skript/classes/data/SkriptClasses.java | 33 +++++++++++++++++++ src/main/resources/lang/default.lang | 2 ++ 2 files changed, 35 insertions(+) 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 451ba5a5a63..a65bd4c56f0 100644 --- a/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/SkriptClasses.java @@ -13,6 +13,7 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.SkriptCommand; import ch.njol.skript.config.Config; +import ch.njol.skript.config.Node; import ch.njol.skript.aliases.Aliases; import ch.njol.skript.aliases.ItemData; import ch.njol.skript.aliases.ItemType; @@ -712,6 +713,38 @@ public String toVariableNameString(Config config) { } })); + Classes.registerClass(new ClassInfo<>(Node.class, "node") + .user("nodes?") + .name("Node") + .description("A node (entry) from a script config file.", + "This may have navigable children.") + .usage("") + .examples("the current script") + .since("INSERT VERSION") + .parser(new Parser() { + + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public @Nullable Node parse(String name, ParseContext context) { + return null; + } + + @Override + public String toString(Node node, int flags) { + return this.toVariableNameString(node); + } + + @Override + public String toVariableNameString(Node node) { + return node.getPath(); + } + + })); + Classes.registerClass(new ClassInfo<>(Script.class, "script") .user("scripts?") .name("Script") diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index 6ea580d9053..65acad25992 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -2661,6 +2661,8 @@ types: classinfo: type¦s @a visualeffect: visual effect¦s @a script: script¦s @a + config: config¦s @a + node: node¦s @a executable: executable¦s @an function: function¦s @a named: named thing¦s @a From 68394b187a0515c34f8f60e47a4e1385efceb828 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 17:32:39 +0100 Subject: [PATCH 05/20] The mystical moving Node Navigator has arrived! --- .../java/ch/njol/skript/config/EntryNode.java | 6 + src/main/java/ch/njol/skript/config/Node.java | 45 +++++- .../ch/njol/skript/config/NodeNavigator.java | 136 ++++++++++++++++++ .../ch/njol/skript/config/SectionNode.java | 17 ++- .../ch/njol/skript/config/SimpleNode.java | 7 + .../java/ch/njol/skript/config/VoidNode.java | 7 + 6 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ch/njol/skript/config/NodeNavigator.java diff --git a/src/main/java/ch/njol/skript/config/EntryNode.java b/src/main/java/ch/njol/skript/config/EntryNode.java index 9eddb3653bf..a6d207c1e54 100644 --- a/src/main/java/ch/njol/skript/config/EntryNode.java +++ b/src/main/java/ch/njol/skript/config/EntryNode.java @@ -2,6 +2,7 @@ import java.util.Map.Entry; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -46,4 +47,9 @@ String save_i() { return key + config.getSaveSeparator() + value; } + @Override + public @Nullable Node get(String step) { + return null; + } + } diff --git a/src/main/java/ch/njol/skript/config/Node.java b/src/main/java/ch/njol/skript/config/Node.java index 1dbd3ad1612..a52a6b7fa98 100644 --- a/src/main/java/ch/njol/skript/config/Node.java +++ b/src/main/java/ch/njol/skript/config/Node.java @@ -1,13 +1,20 @@ package ch.njol.skript.config; +import java.io.PrintWriter; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ch.njol.skript.SkriptConfig; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; + import ch.njol.skript.Skript; import ch.njol.skript.lang.util.common.AnyNamed; import ch.njol.skript.log.SkriptLogger; import ch.njol.util.NonNullPair; import ch.njol.util.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnknownNullability; +import org.skriptlang.skript.util.Validated; import java.io.PrintWriter; import java.util.*; @@ -17,6 +24,7 @@ * @author Peter Güttinger */ public abstract class Node implements AnyNamed { +public abstract class Node implements Validated, NodeNavigator { @Nullable protected String key; @@ -98,6 +106,9 @@ public void move(final SectionNode newParent) { newParent.add(this); } + @SuppressWarnings("null") + private final static Pattern linePattern = Pattern.compile("^((?:[^#]|##)*)(\\s*#(?!#).*)$"); + /** * Splits a line into value and comment. *

@@ -409,6 +420,34 @@ public boolean debug() { return debug; } + @Override + public void invalidate() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean valid() { + //noinspection ConstantValue + return config != null && config.valid(); + } + + public @Nullable String getPath() { + if (key == null) + return null; + else if (parent == null) + return key; + @Nullable String path = parent.getPath(); + if (path == null) + return key; + return path + '.' + key; + } + + @Override + public @NotNull Node getCurrentNode() { + return this; + } + + /** * @return The index of this node relative to the other children of this node's parent, * or -1 if this node does not have a parent. The index includes counted void nodes. diff --git a/src/main/java/ch/njol/skript/config/NodeNavigator.java b/src/main/java/ch/njol/skript/config/NodeNavigator.java new file mode 100644 index 00000000000..6f070dc8432 --- /dev/null +++ b/src/main/java/ch/njol/skript/config/NodeNavigator.java @@ -0,0 +1,136 @@ +/** + * 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.config; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Iterator; + +/** + * Something that contains or references nodes and can be navigated. + * Not all navigation options are universally-supported. + *

+ * All nodes are node navigators, even if they are {@link EntryNode}s that do not support + * children. + * Entry nodes will return {@code null} for all child-based operations + * (as if the requested node was simply not present). + */ +public interface NodeNavigator extends Iterable { + + /** + * If this navigator represents a node with no children (e.g. an entry node) + * this iterator will be empty. + * + * @return An iterator for all children represented by this node navigator + */ + default Iterator iterator() { + return Collections.emptyIterator(); + } + + /** + * Obtains the immediate child node at this (direct) key. + * If this does not represent a node that can have children (e.g. an {@link EntryNode}) then + * it must return {@code null}. + * + * @param key The name of the node + * @return The child node if one is present, otherwise {@code null} + */ + @Nullable Node get(String key); + + /** + * Obtains the current node represented by this navigator. + * If this navigator is itself a node, it should return itself. + * + * @return The main node represented by this navigator + */ + @NotNull Node getCurrentNode(); + + /** + * Fetches a node at position {@param steps} inside the current node. + * If any stage represents a node with no children (e.g. an entry node) + * the result will be null. + *

+ * In the following example, the two entry nodes can be obtained from the root with + * {@code "first", "one"} and {@code "first", "two", "three"} (respectively). + *

{@code
+	 * first:
+	 * 	one: value
+	 * 	two:
+	 * 		three: value
+	 * }
+ * + * @param steps The node steps to traverse + * @return The node at the final step (or nothing) + */ + default @Nullable Node getNodeAt(@NotNull String @NotNull... steps) { + Node node = this.getCurrentNode(); + for (String step : steps) { + if (node == null) return null; + node = node.get(step); + } + return node; + } + + /** + * Fetches a node at a path inside the current node. + * If any stage represents a node with no children (e.g. an entry node) + * the result will be null. + *

+ * In the following example, the two entry nodes can be obtained from the root with + * {@code "first.one"} and {@code "first.two.three"} (respectively). + *
{@code
+	 * first:
+	 * 	one: value
+	 * 	two:
+	 * 		three: value
+	 * }
+ *

+ * If the path is {@code null} or empty, this node will be returned. + * + * @see #getNodeAt(String...) + * @param path The path to the node, separated by {@code .} + * @return The node at the path (or nothing) + */ + @Contract("null -> this") + default @Nullable Node getNodeAt(@Nullable String path) { + if (path == null || path.isEmpty()) + return this.getCurrentNode(); + else if (!path.contains(".")) + return this.getNodeAt(new String[]{path}); + return this.getNodeAt(path.split("\\.")); + } + + /** + * Gets the raw value from the node at the given path. + * If any part of the path is invalid, this will return null. + * + * @param path The node path from which to get the value + * @return If such a node exists, its value, otherwise null + */ + default @Nullable String getValue(String path) { + @Nullable Node node = this.getNodeAt(path); + if (node instanceof EntryNode) + return ((EntryNode) node).getValue(); + return null; + } + +} diff --git a/src/main/java/ch/njol/skript/config/SectionNode.java b/src/main/java/ch/njol/skript/config/SectionNode.java index c280851570c..b245da9588b 100644 --- a/src/main/java/ch/njol/skript/config/SectionNode.java +++ b/src/main/java/ch/njol/skript/config/SectionNode.java @@ -21,7 +21,7 @@ /** * @author Peter Güttinger */ -public class SectionNode extends Node implements Iterable { +public class SectionNode extends Node implements Iterable, NodeNavigator { private final ArrayList nodes = new ArrayList<>(); @@ -541,4 +541,19 @@ private boolean modify(SectionNode other, boolean compareValues, String... exclu return different; } + @Override + public @NotNull Node getCurrentNode() { + return this; + } + + @Override + public @Nullable Node getNodeAt(@NotNull String @NotNull ... keys) { + Node node = this; + for (String s : keys) { + if (!(node instanceof SectionNode)) return null; + node = ((SectionNode) node).get(s); + } + return node; + } + } diff --git a/src/main/java/ch/njol/skript/config/SimpleNode.java b/src/main/java/ch/njol/skript/config/SimpleNode.java index 33a4a215ec2..0eb120fbf56 100644 --- a/src/main/java/ch/njol/skript/config/SimpleNode.java +++ b/src/main/java/ch/njol/skript/config/SimpleNode.java @@ -1,5 +1,7 @@ package ch.njol.skript.config; +import org.jetbrains.annotations.Nullable; + /** * @author Peter Güttinger */ @@ -23,4 +25,9 @@ public void set(final String s) { key = s; } + @Override + public @Nullable Node get(String key) { + return null; + } + } diff --git a/src/main/java/ch/njol/skript/config/VoidNode.java b/src/main/java/ch/njol/skript/config/VoidNode.java index cc93d4af082..389f40b4d12 100644 --- a/src/main/java/ch/njol/skript/config/VoidNode.java +++ b/src/main/java/ch/njol/skript/config/VoidNode.java @@ -1,5 +1,7 @@ package ch.njol.skript.config; +import org.jetbrains.annotations.Nullable; + /** * An empty line or a comment. *

@@ -50,4 +52,9 @@ String save_i() { return "" + key; } + @Override + public @Nullable Node get(String key) { + return null; + } + } From 2caeed865fb938166def04a86322c4c8f802da79 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 17:32:48 +0100 Subject: [PATCH 06/20] Nothing in the world used this, sorry. --- .../ch/njol/skript/expressions/ExprValue.java | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 src/main/java/ch/njol/skript/expressions/ExprValue.java diff --git a/src/main/java/ch/njol/skript/expressions/ExprValue.java b/src/main/java/ch/njol/skript/expressions/ExprValue.java deleted file mode 100644 index 83d561352e3..00000000000 --- a/src/main/java/ch/njol/skript/expressions/ExprValue.java +++ /dev/null @@ -1,64 +0,0 @@ -package ch.njol.skript.expressions; - -import java.lang.reflect.Array; - -import org.bukkit.event.Event; -import org.jetbrains.annotations.Nullable; - -import ch.njol.skript.lang.Expression; -import ch.njol.skript.lang.Literal; -import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.lang.Unit; -import ch.njol.skript.lang.util.SimpleExpression; -import ch.njol.util.Kleenean; - -/** - * @author Peter Güttinger - */ -public class ExprValue extends SimpleExpression { -// static { // REMIND add this (>2.0) -// Skript.registerExpression(ExprValue.class, Unit.class, ExpressionType.PATTERN_MATCHES_EVERYTHING, "%~number% %*unit%"); -// } - - @SuppressWarnings("null") - private Expression amount; - @SuppressWarnings("null") - private Unit unit; - - @SuppressWarnings({"unchecked", "null"}) - @Override - public boolean init(final Expression[] exprs, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) { - amount = (Expression) exprs[0]; - unit = ((Literal) exprs[1]).getSingle(); - return true; - } - - @Override - @Nullable - protected Unit[] get(final Event e) { - final Number a = amount.getSingle(e); - if (a == null) - return null; - final Unit u = unit.clone(); - u.setAmount(a.doubleValue()); - final Unit[] one = (Unit[]) Array.newInstance(unit.getClass(), 1); - one[0] = u; - return one; - } - - @Override - public boolean isSingle() { - return true; - } - - @Override - public Class getReturnType() { - return unit.getClass(); - } - - @Override - public String toString(final @Nullable Event e, final boolean debug) { - return amount.toString(e, debug) + " " + unit.toString(); - } - -} From 3fa27e663dd8da5e380429f66af6149a2a2d1b12 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 17:33:02 +0100 Subject: [PATCH 07/20] Make Config a Node Navigator. --- .../java/ch/njol/skript/config/Config.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/njol/skript/config/Config.java b/src/main/java/ch/njol/skript/config/Config.java index 0527ff9abbe..3a6247617cd 100644 --- a/src/main/java/ch/njol/skript/config/Config.java +++ b/src/main/java/ch/njol/skript/config/Config.java @@ -24,6 +24,7 @@ import ch.njol.skript.log.SkriptLogger; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.skriptlang.skript.util.Validated; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,6 +34,7 @@ */ public class Config implements Comparable, Validated { public class Config implements Comparable, AnyNamed { +public class Config implements Comparable, Validated, NodeNavigator { /** * One level of the indentation, e.g. a tab or 4 spaces. @@ -177,13 +179,10 @@ public String getFileName() { * @throws IOException If the file could not be written to. */ public void save(File file) throws IOException { - separator = defaultSeparator; - PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8); - try { - main.save(writer); - } finally { + this.separator = defaultSeparator; + try (final PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8)) { + this.main.save(writer); writer.flush(); - writer.close(); } } @@ -418,6 +417,27 @@ public boolean valid() { return validator.valid(); } + @Override + public @NotNull Node getCurrentNode() { + return main; + } + + @Override + public @Nullable Node getNodeAt(@NotNull String @NotNull ... steps) { + return main.getNodeAt(steps); + } + + @NotNull + @Override + public Iterator iterator() { + return main.iterator(); + } + + @Override + public @Nullable Node get(String step) { + return main.get(step); + } + /** * @return The name of this config (excluding path and file extensions) */ From d3f33d8041ba9f83650367a6a585971179283dc5 Mon Sep 17 00:00:00 2001 From: Moderocky Date: Thu, 23 May 2024 17:33:11 +0100 Subject: [PATCH 08/20] Add config resolver path. --- src/main/java/ch/njol/skript/expressions/ExprScripts.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/ch/njol/skript/expressions/ExprScripts.java b/src/main/java/ch/njol/skript/expressions/ExprScripts.java index b27f4910921..6d02cfa30f1 100644 --- a/src/main/java/ch/njol/skript/expressions/ExprScripts.java +++ b/src/main/java/ch/njol/skript/expressions/ExprScripts.java @@ -39,6 +39,8 @@ public class ExprScripts extends SimpleExpression