From fcdd2296ce3742ad0be6b944cc08a2e3e5da9078 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 5 May 2026 22:41:52 +0200 Subject: [PATCH 01/32] init commit --- src/main/java/ch/njol/skript/Skript.java | 3 + .../lang/function/FunctionReference.java | 5 +- .../lang/function/FunctionRegistry.java | 217 ++++++++---------- .../njol/skript/lang/function/Signature.java | 23 +- .../common/function/FunctionReference.java | 7 +- .../function/FunctionReferenceParser.java | 1 - .../common/function/FunctionRegistry.java | 212 +++++++++++++++++ .../skript/common/function/Signature.java | 52 ++++- 8 files changed, 388 insertions(+), 132 deletions(-) create mode 100644 src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 216bc6687d8..fb5a10961b1 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -12,6 +12,7 @@ import ch.njol.skript.lang.*; import ch.njol.skript.lang.Condition.ConditionType; import ch.njol.skript.lang.Effect; +import ch.njol.skript.lang.function.FunctionRegistry; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.localization.Language; import ch.njol.skript.localization.Message; @@ -480,6 +481,8 @@ public void onEnable() { //noinspection removal EventValues.setEventValueRegistry(eventValueRegistry); + skript.storeRegistry(FunctionRegistry.class, new FunctionRegistry()); + // TODO this upcoming portion is a bad circular dependency // some modules depend on the config // the config depends on some modules (for the types they register) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index d8cc497288b..370d0071e35 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -4,8 +4,6 @@ import ch.njol.skript.SkriptAPIException; import ch.njol.skript.config.Node; import ch.njol.skript.lang.*; -import ch.njol.skript.lang.function.FunctionRegistry.Retrieval; -import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; import ch.njol.skript.log.RetainingLogHandler; import ch.njol.skript.log.SkriptLogger; import ch.njol.skript.registrations.Classes; @@ -17,6 +15,8 @@ import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.FunctionReference.Argument; import org.skriptlang.skript.common.function.FunctionReference.ArgumentType; +import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; +import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import org.skriptlang.skript.common.function.Parameter; import org.skriptlang.skript.common.function.Parameter.Modifier; import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier; @@ -346,7 +346,6 @@ private Function getRegisteredFunction() { } Retrieval> attempt = FunctionRegistry.getRegistry().getFunction(script, functionName, parameterTypes); - if (attempt.result() == RetrievalResult.EXACT) { return attempt.retrieved(); } diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index b26a33a2c58..9dd60622861 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -9,10 +9,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NonNull; +import org.skriptlang.skript.common.function.Function; import org.skriptlang.skript.common.function.Parameter; import org.skriptlang.skript.common.function.Parameter.Modifier; +import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.lang.converter.Converters; -import org.skriptlang.skript.util.Registry; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -23,7 +25,7 @@ * A registry for functions. */ @ApiStatus.Internal -public final class FunctionRegistry implements Registry> { +public final class FunctionRegistry implements org.skriptlang.skript.common.function.FunctionRegistry { private static FunctionRegistry registry; @@ -56,7 +58,7 @@ public static FunctionRegistry getRegistry() { private final Map namespaces = new ConcurrentHashMap<>(); @Override - public @Unmodifiable @NotNull Collection> elements() { + public Collection> elements() { Set> functions = new HashSet<>(); for (Namespace namespace : namespaces.values()) { @@ -66,6 +68,16 @@ public static FunctionRegistry getRegistry() { return Collections.unmodifiableSet(functions); } + @Override + public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { + return null; + } + + @Override + public void register(@NotNull Signature signature) { + register(null, signature); + } + /** * Registers a signature. *

@@ -85,14 +97,14 @@ public static FunctionRegistry getRegistry() { */ public void register(@Nullable String namespace, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - if (signature.isLocal() && namespace == null) { + if (signature.hasModifier(Signature.Modifier.LOCAL) && namespace == null) { throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); } - if (!signature.isLocal() && namespace != null) { + if (!signature.hasModifier(Signature.Modifier.LOCAL) && namespace != null) { throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); } - Skript.debug("Registering signature '%s'", signature.getName()); + Skript.debug("Registering signature '%s'", signature.name()); // namespace NamespaceIdentifier namespaceId; @@ -112,16 +124,21 @@ public void register(@Nullable String namespace, @NotNull Signature signature Set identifiersWithName = ns.identifiers.computeIfAbsent(identifier.name, s -> new HashSet<>()); boolean exists = identifiersWithName.add(identifier); if (!exists) { - alreadyRegisteredError(signature.getName(), identifier, namespaceId); + alreadyRegisteredError(signature.name(), identifier, namespaceId); } } Signature existing = ns.signatures.putIfAbsent(identifier, signature); if (existing != null) { - alreadyRegisteredError(signature.getName(), identifier, namespaceId); + alreadyRegisteredError(signature.name(), identifier, namespaceId); } } + @Override + public void register(@NotNull Function function) { + + } + /** * Registers a function. *

@@ -141,16 +158,17 @@ public void register(@Nullable String namespace, @NotNull Signature signature * if the function is local and namespace is null. */ public void register(@Nullable String namespace, @NotNull Function function) { + Signature signature = function.signature(); Preconditions.checkNotNull(function, "function cannot be null"); - if (function.getSignature().isLocal() && namespace == null) { + if (signature.hasModifier(Signature.Modifier.LOCAL) && namespace == null) { throw new IllegalArgumentException("Cannot register a local function in the global namespace"); } - if (!function.getSignature().isLocal() && namespace != null) { + if (!signature.hasModifier(Signature.Modifier.LOCAL) && namespace != null) { throw new IllegalArgumentException("Cannot register a global function in a local namespace"); } - Skript.debug("Registering function '%s'", function.getName()); + Skript.debug("Registering function '%s'", signature.name()); - String name = function.getName(); + String name = signature.name(); if (!FUNCTION_NAME_PATTERN.matcher(name).matches()) { throw new SkriptAPIException("Invalid function name '" + name + "'"); } @@ -163,9 +181,9 @@ public void register(@Nullable String namespace, @NotNull Function function) namespaceId = GLOBAL_NAMESPACE; } - FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); + FunctionIdentifier identifier = FunctionIdentifier.of(signature); if (!signatureExists(namespaceId, identifier)) { - register(namespace, function.getSignature()); + register(namespace, signature); } Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); @@ -176,10 +194,15 @@ public void register(@Nullable String namespace, @NotNull Function function) } } + @Override + public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getFunction(@NotNull String name, @NotNull Class... args) { + return getFunction(null, name, args); + } + private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, NamespaceIdentifier namespace) { throw new SkriptAPIException("Function '%s' with parameters %s is already registered in %s" - .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), - namespace)); + .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), + namespace)); } /** @@ -211,63 +234,6 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull return false; } - /** - * The result of attempting to retrieve a function. - * Depending on the type, a {@link Retrieval} will feature different data. - */ - public enum RetrievalResult { - - /** - * The specified function or signature has not been registered. - */ - NOT_REGISTERED, - - /** - * There are multiple functions or signatures that may fit the provided name and argument types. - */ - AMBIGUOUS, - - /** - * A single function or signature has been found which matches the name and argument types. - */ - EXACT - - } - - /** - * The result of trying to retrieve a function or signature. - *

- * When getting a function or signature, the following situations may occur. - * These are specified by {@code type}. - *

    - *
  • - * {@code NOT_REGISTERED}. The specified function or signature is not registered. - * Both {@code retrieved} and {@code conflictingArgs} will be null. - *
  • - *
  • - * {@code AMBIGUOUS}. There are multiple functions or signatures that - * may fit the provided name and argument types. - * {@code retrieved} will be null, and {@code conflictingArgs} - * will contain the conflicting function or signature parameters. - *
  • - *
  • - * {@code EXACT}. A single function or signature has been found which matches the name and argument types. - * {@code retrieved} will contain the function or signature, and {@code conflictingArgs} will be null. - *
  • - *
- *

- * - * @param result The result of the function or signature retrieval. - * @param retrieved The function or signature that was found if {@code result} is {@code EXACT}. - * @param conflictingArgs The conflicting arguments if {@code result} is {@code AMBIGUOUS}. - */ - public record Retrieval( - @NotNull RetrievalResult result, - T retrieved, - Class[][] conflictingArgs - ) { - } - /** * Gets a function from a script. If no local function is found, checks for global functions. * If {@code namespace} is null, only global functions will be checked. @@ -280,14 +246,14 @@ public record Retrieval( * stored in a {@link Retrieval} object. */ public @NotNull Retrieval> getFunction( - @Nullable String namespace, - @NotNull String name, - @NotNull Class... args + @Nullable String namespace, + @NotNull String name, + @NotNull Class... args ) { Retrieval> attempt = null; if (namespace != null) { attempt = getFunction(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args)); + FunctionIdentifier.of(name, true, args)); } if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { attempt = getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); @@ -295,6 +261,11 @@ public record Retrieval( return attempt; } + @Override + public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getSignature(@NotNull String name, @NotNull Class... args) { + return getSignature(null, name, args); + } + /** * Gets a function from a namespace. * @@ -324,8 +295,8 @@ public record Retrieval( Skript.debug("Matched function for '%s': %s", provided.name, candidates.stream().findAny().orElse(null)); } return new Retrieval<>(RetrievalResult.EXACT, - ns.functions.get(candidates.stream().findAny().orElse(null)), - null); + ns.functions.get(candidates.stream().findAny().orElse(null)), + null); } else { if (Skript.debug()) { String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); @@ -334,10 +305,10 @@ public record Retrieval( Skript.debug("Options: %s", options); } return new Retrieval<>(RetrievalResult.AMBIGUOUS, - null, - candidates.stream() - .map(FunctionIdentifier::args) - .toArray(Class[][]::new)); + null, + candidates.stream() + .map(FunctionIdentifier::args) + .toArray(Class[][]::new)); } } @@ -351,15 +322,15 @@ public record Retrieval( * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ - public Retrieval> getSignature( - @Nullable String namespace, - @NotNull String name, - @NotNull Class... args + public @NonNull Retrieval> getSignature( + @Nullable String namespace, + @NotNull String name, + @NotNull Class... args ) { Retrieval> attempt = null; if (namespace != null) { attempt = getSignature(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args), false); + FunctionIdentifier.of(name, true, args), false); } if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { attempt = getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), false); @@ -367,6 +338,11 @@ public Retrieval> getSignature( return attempt; } + @Override + public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { + return getSignatures(null, name); + } + /** * Gets the signature for a function with the given name and arguments. If no local function is found, * checks for global functions. If {@code namespace} is null, only global signatures will be checked. @@ -383,14 +359,14 @@ public Retrieval> getSignature( * @return The signature for the function with the given name and argument types, or null if no such function exists. */ Retrieval> getExactSignature( - @Nullable String namespace, - @NotNull String name, - @NotNull Class... args + @Nullable String namespace, + @NotNull String name, + @NotNull Class... args ) { Retrieval> attempt = null; if (namespace != null) { attempt = getSignature(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args), true); + FunctionIdentifier.of(name, true, args), true); } if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { @@ -402,6 +378,7 @@ Retrieval> getExactSignature( /** * Gets every signature with the name {@code name}. * This includes global functions and, if {@code namespace} is not null, functions under that namespace (if valid). + * * @param namespace The additional namespace to obtain signatures from. * Usually represents the path of the script this function is registered in. * @param name The name of the signature(s) to obtain. @@ -435,8 +412,8 @@ Retrieval> getExactSignature( * * @param namespace The namespace to get the function from. * @param provided The provided identifier of the function. - * @param exact When false, will convert arguments to different types to attempt to find a match. - * When true, will not convert arguments. + * @param exact When false, will convert arguments to different types to attempt to find a match. + * When true, will not convert arguments. * @return The signature for the function with the given name and argument types, or null if no such signature exists * in the specified namespace. */ @@ -457,11 +434,11 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp } else if (candidates.size() == 1) { if (Skript.debug()) { Skript.debug("Matched signature for '%s': %s", - provided.name, ns.signatures.get(candidates.stream().findAny().orElse(null))); + provided.name, ns.signatures.get(candidates.stream().findAny().orElse(null))); } return new Retrieval<>(RetrievalResult.EXACT, - ns.signatures.get(candidates.stream().findAny().orElse(null)), - null); + ns.signatures.get(candidates.stream().findAny().orElse(null)), + null); } else { if (Skript.debug()) { String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); @@ -470,10 +447,10 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp Skript.debug("Options: %s", options); } return new Retrieval<>(RetrievalResult.AMBIGUOUS, - null, - candidates.stream() - .map(FunctionIdentifier::args) - .toArray(Class[][]::new)); + null, + candidates.stream() + .map(FunctionIdentifier::args) + .toArray(Class[][]::new)); } } @@ -482,14 +459,14 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp * * @param provided The provided function. * @param existing The existing functions with the same name. - * @param exact When false, will convert arguments to different types to attempt to find a match. - * When true, will not convert arguments. + * @param exact When false, will convert arguments to different types to attempt to find a match. + * When true, will not convert arguments. * @return An unmodifiable list of candidates for the provided function. */ private static @Unmodifiable @NotNull Set candidates( - @NotNull FunctionIdentifier provided, - Set existing, - boolean exact + @NotNull FunctionIdentifier provided, + Set existing, + boolean exact ) { Set candidates = new HashSet<>(); @@ -498,14 +475,14 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp // by this point, all candidates have matching names if (Arrays.stream(candidate.args).filter(Class::isArray).count() == 1 - && candidate.args.length == 1 - && candidate.args[0].isArray()) { + && candidate.args.length == 1 + && candidate.args[0].isArray()) { // if a function has single list value param, check all types // make sure all types in the passed array are valid for the array parameter Class arrayType = candidate.args[0].componentType(); for (Class arrayArg : provided.args) { arrayArg = Utils.getComponentType(arrayArg); - + if (!Converters.converterExists(arrayArg, arrayType)) { continue candidates; } @@ -517,7 +494,7 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp // if argument counts are not possible, skip if (provided.args.length > candidate.args.length - || provided.args.length < candidate.minArgCount) { + || provided.args.length < candidate.minArgCount) { continue; } @@ -583,11 +560,11 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp public void remove(@NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - String name = signature.getName(); + String name = signature.name(); FunctionIdentifier identifier = FunctionIdentifier.of(signature); Namespace namespace; - if (signature.isLocal()) { + if (signature.hasModifier(Signature.Modifier.LOCAL)) { namespace = namespaces.get(new NamespaceIdentifier(signature.namespace())); } else { namespace = namespaces.get(GLOBAL_NAMESPACE); @@ -675,7 +652,7 @@ private static final class Namespace { * @param args The arguments of the function. */ record FunctionIdentifier(@NotNull String name, boolean local, int minArgCount, - @NotNull Class... args) { + @NotNull Class... args) { /** * Returns the identifier for the given arguments. @@ -713,8 +690,8 @@ static FunctionIdentifier of(@NotNull Signature signature) { parameters[i] = param.type(); } - return new FunctionIdentifier(signature.getName(), signature.isLocal(), - parameters.length - optionalArgs, parameters); + return new FunctionIdentifier(signature.name(), signature.hasModifier(Signature.Modifier.LOCAL), + parameters.length - optionalArgs, parameters); } @Override @@ -748,11 +725,11 @@ public boolean equals(Object obj) { @Override public @NotNull String toString() { return MoreObjects.toStringHelper(this) - .add("name", name) - .add("local", local) - .add("minArgCount", minArgCount) - .add("args", Arrays.stream(args).map(Class::getSimpleName).collect(Collectors.joining(", "))) - .toString(); + .add("name", name) + .add("local", local) + .add("minArgCount", minArgCount) + .add("args", Arrays.stream(args).map(Class::getSimpleName).collect(Collectors.joining(", "))) + .toString(); } } diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index 349ed2b25d0..fc9e94dc59f 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -10,8 +10,8 @@ import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.common.function.FunctionReference; -import org.skriptlang.skript.common.function.Parameter.Modifier; import org.skriptlang.skript.common.function.Parameters; import java.util.*; @@ -68,6 +68,8 @@ public class Signature implements org.skriptlang.skript.common.function.Signa */ @Nullable String originClassPath; + private final Set modifiers = new HashSet<>(); + static Class getReturns(boolean single, Class cls) { if (single) { return cls; @@ -81,6 +83,8 @@ public Signature(@Nullable String script, String name, Parameter[] parameters this.name = name; this.parameters = initParameters(parameters); this.local = local; + if (local) + modifiers.add(Modifier.LOCAL); this.returnType = returnType; this.single = single; if (returnType == null) { @@ -109,6 +113,8 @@ public Signature(@Nullable String script, String name, Parameters parameters, Cl this.name = name; this.parameters = parameters; this.local = local; + if (local) + modifiers.add(Modifier.LOCAL); this.returns = returnType; if (returnType != null) { //noinspection unchecked @@ -130,6 +136,11 @@ public Signature(String namespace, String name, org.skriptlang.skript.common.fun this(namespace, name, initParameters(parameters), returnType, false, contract); } + @Override + public @NotNull String name() { + return name; + } + private static Parameters initParameters(org.skriptlang.skript.common.function.Parameter[] params) { SequencedMap> map = new LinkedHashMap<>(); for (org.skriptlang.skript.common.function.Parameter parameter : params) { @@ -140,6 +151,7 @@ private static Parameters initParameters(org.skriptlang.skript.common.function.P /** * Converts a {@link org.skriptlang.skript.common.function.Parameter} to a {@link Parameter}. + * * @param parameter The parameter to use to convert. * @return The converted parameter. */ @@ -150,7 +162,7 @@ static Parameter toOldParameter(org.skriptlang.skript.common.function.Paramet ClassInfo classInfo = Classes.getExactClassInfo(Utils.getComponentType(parameter.type())); return new Parameter<>(parameter.name(), classInfo, !parameter.type().isArray(), null, - parameter.modifiers().toArray(new Modifier[0])); + parameter.modifiers().toArray(new org.skriptlang.skript.common.function.Parameter.Modifier[0])); } /** @@ -232,6 +244,11 @@ public boolean isSingle() { return single; } + @Override + public @Unmodifiable @NotNull Set modifiers() { + return Collections.unmodifiableSet(modifiers); + } + /** * @deprecated Unused. */ @@ -270,7 +287,7 @@ public int getMinParameters() { int i = parameters.size() - 1; for (org.skriptlang.skript.common.function.Parameter parameter : Lists.reverse(params)) { - if (!parameter.hasModifier(Modifier.OPTIONAL)) { + if (!parameter.hasModifier(org.skriptlang.skript.common.function.Parameter.Modifier.OPTIONAL)) { return i + 1; } i--; diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java index 94514f17a7a..ae7179e9cfd 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java @@ -4,8 +4,6 @@ import ch.njol.skript.lang.*; import ch.njol.skript.lang.function.FunctionEvent; import ch.njol.skript.lang.function.FunctionRegistry; -import ch.njol.skript.lang.function.FunctionRegistry.Retrieval; -import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; import ch.njol.skript.lang.function.Functions; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.LiteralUtils; @@ -16,6 +14,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.FunctionReferenceParser.EmptyExpression; +import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; +import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import org.skriptlang.skript.common.function.Parameter.Modifier; import java.util.*; @@ -231,8 +231,7 @@ public Function function() { .map(Parameter::type) .toArray(Class[]::new); - Retrieval> retrieval = FunctionRegistry.getRegistry().getFunction(namespace, name, parameters); - + Retrieval> retrieval = FunctionRegistry.getRegistry().getFunction(namespace, name, parameters); if (retrieval.result() == RetrievalResult.EXACT) { //noinspection unchecked cachedFunction = (Function) retrieval.retrieved(); diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java index f5d93fd7ef1..17d2cf1e9d6 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java @@ -7,7 +7,6 @@ import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.function.FunctionRegistry; -import ch.njol.skript.lang.function.Signature; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.localization.ArgsMessage; diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java new file mode 100644 index 00000000000..77064ff79e2 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java @@ -0,0 +1,212 @@ +package org.skriptlang.skript.common.function; + +import ch.njol.skript.SkriptAPIException; +import org.jetbrains.annotations.ApiStatus.Experimental; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.ApiStatus.NonExtendable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.util.Registry; +import org.skriptlang.skript.util.ViewProvider; + +import java.util.Set; + +/** + * A registry for functions. + */ +@NonExtendable +@Internal +@Experimental +public interface FunctionRegistry extends Registry>, ViewProvider { + + /** + * Registers a global signature. + *

+ * Attempting to register a local signature in the global namespace, or a global signature in + * a local namespace, will throw an {@link IllegalArgumentException}. + *

+ * + * @param signature The signature to register. + * @throws SkriptAPIException if a signature with the same name and parameters is already registered + * in this namespace. + * @throws IllegalArgumentException if the signature is local. + */ + void register(@NotNull Signature signature); + + /** + * Registers a local signature. + *

+ * Attempting to register a local signature in the global namespace, or a global signature in + * a local namespace, will throw an {@link IllegalArgumentException}. + *

+ * + * @param namespace The namespace to register the signature in. + * Usually represents the path of the script this signature is registered in. + * @param signature The signature to register. + * @throws SkriptAPIException if a signature with the same name and parameters is already registered + * in this namespace. + * @throws IllegalArgumentException if the signature is global and namespace is not null. + */ + void register(@NotNull String namespace, @NotNull Signature signature); + + /** + * Registers a global function. + * + * @param function The function to register. + * @throws SkriptAPIException if a signature with the same name and parameters is already registered + * in this namespace. + */ + void register(@NotNull Function function); + + /** + * Registers a local function. + * + * @param function The function to register. + * @param namespace The namespace to register the function in. + * Usually represents the path of the script this signature is registered in. + * @throws SkriptAPIException if a signature with the same name and parameters is already registered + * in this namespace. + */ + void register(@NotNull String namespace, @NotNull Function function); + + /** + * The result of attempting to retrieve a function. + * Depending on the type, a {@link Retrieval} will feature different data. + */ + enum RetrievalResult { + + /** + * The specified function or signature has not been registered. + */ + NOT_REGISTERED, + + /** + * There are multiple functions or signatures that may fit the provided name and argument types. + */ + AMBIGUOUS, + + /** + * A single function or signature has been found which matches the name and argument types. + */ + EXACT + + } + + /** + * The result of trying to retrieve a function or signature. + *

+ * When getting a function or signature, the following situations may occur. + * These are specified by {@code type}. + *

    + *
  • + * {@code NOT_REGISTERED}. The specified function or signature is not registered. + * Both {@code retrieved} and {@code conflictingArgs} will be null. + *
  • + *
  • + * {@code AMBIGUOUS}. There are multiple functions or signatures that + * may fit the provided name and argument types. + * {@code retrieved} will be null, and {@code conflictingArgs} + * will contain the conflicting function or signature parameters. + *
  • + *
  • + * {@code EXACT}. A single function or signature has been found which matches the name and argument types. + * {@code retrieved} will contain the function or signature, and {@code conflictingArgs} will be null. + *
  • + *
+ *

+ * + * @param result The result of the function or signature retrieval. + * @param retrieved The function or signature that was found if {@code result} is {@code EXACT}. + * @param conflictingArgs The conflicting arguments if {@code result} is {@code AMBIGUOUS}. + */ + record Retrieval( + @NotNull RetrievalResult result, + T retrieved, + Class[][] conflictingArgs + ) { + } + + /** + * Gets a function. + * + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return Information related to the attempt to get the specified function, stored in a {@link Retrieval} object. + */ + @NotNull Retrieval> getFunction( + @NotNull String name, + @NotNull Class... args + ); + + /** + * Gets a function. If no local function is found, checks for global functions. + * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return Information related to the attempt to get the specified function, stored in a {@link Retrieval} object. + */ + @NotNull Retrieval> getFunction( + @NotNull String namespace, + @NotNull String name, + @NotNull Class... args + ); + + /** + * Gets the signature for a global function with the given name and arguments. + * + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return Information related to the attempt to get the specified signature, stored in a {@link Retrieval} object. + */ + @NotNull Retrieval> getSignature( + @NotNull String name, + @NotNull Class... args + ); + + /** + * Gets the signature for a function with the given name and arguments. + * If no local function is found, checks for global functions. + * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return Information related to the attempt to get the specified signature, stored in a {@link Retrieval} object. + */ + @NotNull Retrieval> getSignature( + @NotNull String namespace, + @NotNull String name, + @NotNull Class... args + ); + + /** + * Gets every signature with the name {@code name} in the global namespace. + * + * @param name The name of the signature(s) to obtain. + * @return A list of all signatures named {@code name}. + */ + @Unmodifiable + @NotNull Set> getSignatures(@NotNull String name); + + /** + * Gets every signature with the name {@code name}. + * This includes global functions and functions registered in the namespace (if valid). + * + * @param namespace The additional namespace to obtain signatures from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the signature(s) to obtain. + * @return A list of all signatures named {@code name}. + */ + @Unmodifiable + @NotNull Set> getSignatures(@NotNull String namespace, @NotNull String name); + + /** + * Removes a function's signature from the registry. + * + * @param signature The signature to remove. + */ + void remove(@NotNull Signature signature); + +} diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index 3b64b177b67..aad8f0b5bef 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -6,8 +6,11 @@ import org.jetbrains.annotations.ApiStatus.NonExtendable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; +import java.util.Set; + /** * Represents a function signature. * @@ -19,6 +22,11 @@ @Experimental public interface Signature { + /** + * @return The name of the function. + */ + @NotNull String name(); + /** * @return The type of this parameter. */ @@ -27,7 +35,13 @@ public interface Signature { /** * @return An unmodifiable view of all the parameters that this signature has. */ - @UnmodifiableView @NotNull Parameters parameters(); + @UnmodifiableView + @NotNull Parameters parameters(); + + /** + * @return The namespace of this signature. + */ + @Nullable String namespace(); /** * @return The contract of this signature. @@ -37,6 +51,7 @@ public interface Signature { /** * Adds a reference to the clearing list. + * * @param reference The reference. */ @Experimental @@ -52,4 +67,39 @@ default boolean isSingle() { return !returnType().isArray(); } + /** + * @return All modifiers belonging to this signature. + */ + @Unmodifiable + @NotNull Set modifiers(); + + /** + * Returns whether this signature has the specified modifier. + * + * @param modifier The modifier. + * @return True when {@link #modifiers()} contains the specified modifier, false if not. + */ + default boolean hasModifier(Modifier modifier) { + return modifiers().contains(modifier); + } + + /** + * Represents a modifier that can be applied to a function signature. + */ + interface Modifier { + + /** + * @return A new Modifier instance to be used as a custom flag. + */ + static Modifier of() { + return new Modifier() { }; + } + + /** + * Indicates the signature is local. + */ + Modifier LOCAL = of(); + + } + } From 87acbb4cbd87f22c9cb6eac9991005d0740b3d24 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 8 May 2026 17:04:31 +0200 Subject: [PATCH 02/32] fix tests --- .../lang/function/FunctionRegistryTest.java | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index d4896dbf59e..b8cd0d3f95e 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -2,23 +2,23 @@ import ch.njol.skript.SkriptAPIException; import ch.njol.skript.lang.function.FunctionRegistry.FunctionIdentifier; -import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.registrations.DefaultClasses; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import org.junit.Test; +import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import static org.junit.Assert.*; public class FunctionRegistryTest { - private static final FunctionRegistry registry = FunctionRegistry.getRegistry(); + private static final ch.njol.skript.lang.function.FunctionRegistry registry = FunctionRegistry.getRegistry(); private static final String FUNCTION_NAME = "testFunctionRegistry"; private static final String TEST_SCRIPT = "test"; - private static final Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, new Parameter[0], + private static final ch.njol.skript.lang.function.Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, new ch.njol.skript.lang.function.Parameter[0], DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -101,7 +101,7 @@ public void testSimpleRegisterRemoveRegisterGlobal() { registry.remove(TEST_FUNCTION.getSignature()); } - private static final Function LOCAL_TEST_FUNCTION = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, new Parameter[0], + private static final ch.njol.skript.lang.function.Function LOCAL_TEST_FUNCTION = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, new ch.njol.skript.lang.function.Parameter[0], DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -146,9 +146,9 @@ public void testSimpleRegisterRemoveRegisterLocal() { registry.remove(TEST_FUNCTION.getSignature()); } - private static final Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.BOOLEAN, true, null) + private static final ch.njol.skript.lang.function.Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -156,9 +156,9 @@ public void testSimpleRegisterRemoveRegisterLocal() { } }; - private static final Function TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.NUMBER, true, null) + private static final ch.njol.skript.lang.function.Function TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.NUMBER, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -256,9 +256,9 @@ public void testRegisterRemoveRegisterGlobal() { registry.remove(TEST_FUNCTION_N.getSignature()); } - private static final Function LOCAL_TEST_FUNCTION_B = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.BOOLEAN, true, null) + private static final ch.njol.skript.lang.function.Function LOCAL_TEST_FUNCTION_B = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -266,9 +266,9 @@ public void testRegisterRemoveRegisterGlobal() { } }; - private static final Function LOCAL_TEST_FUNCTION_N = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.NUMBER, true, null) + private static final ch.njol.skript.lang.function.Function LOCAL_TEST_FUNCTION_N = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.NUMBER, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -359,9 +359,9 @@ public void testIdentifierOf() { @Test public void testIdentifierSignatureOf() { SimpleJavaFunction function = new SimpleJavaFunction<>(FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.BOOLEAN, true, null), - new Parameter<>("b", DefaultClasses.NUMBER, false, new SimpleLiteral(1, true)) + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null), + new ch.njol.skript.lang.function.Parameter<>("b", DefaultClasses.NUMBER, false, new SimpleLiteral(1, true)) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -377,9 +377,9 @@ public void testIdentifierSignatureOf() { assertArrayEquals(new Class[]{Boolean.class, Number[].class}, identifier.args()); SimpleJavaFunction function2 = new SimpleJavaFunction<>(FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.BOOLEAN, true, null), - new Parameter<>("b", DefaultClasses.NUMBER, false, null) + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null), + new ch.njol.skript.lang.function.Parameter<>("b", DefaultClasses.NUMBER, false, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -400,7 +400,7 @@ public void testRemoveGlobalScriptFunctions8015() { assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); // construct a global function with a non-null script, which happens in script functions - Signature signature = new Signature<>(TEST_SCRIPT, FUNCTION_NAME, new Parameter[0], + ch.njol.skript.lang.function.Signature signature = new Signature<>(TEST_SCRIPT, FUNCTION_NAME, new ch.njol.skript.lang.function.Parameter[0], false, DefaultClasses.BOOLEAN, true, ""); SimpleJavaFunction fn = new SimpleJavaFunction<>(signature) { @Override @@ -421,9 +421,9 @@ public void testRemoveGlobalScriptFunctions8015() { assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); } - private static final Function TEST_FUNCTION_P = new SimpleJavaFunction<>(FUNCTION_NAME, - new Parameter[]{ - new Parameter<>("a", DefaultClasses.PLAYER, true, null) + private static final ch.njol.skript.lang.function.Function TEST_FUNCTION_P = new SimpleJavaFunction<>(FUNCTION_NAME, + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.PLAYER, true, null) }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { @@ -432,7 +432,7 @@ public void testRemoveGlobalScriptFunctions8015() { }; private static final Function TEST_FUNCTION_OP = new SimpleJavaFunction<>(FUNCTION_NAME, - new Parameter[]{ + new ch.njol.skript.lang.function.Parameter[]{ new Parameter<>("a", DefaultClasses.OFFLINE_PLAYER, true, null) }, DefaultClasses.BOOLEAN, true) { @Override From 33a69e1c34a74bc622ffca1b08502c0a4440c62f Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 8 May 2026 17:04:48 +0200 Subject: [PATCH 03/32] add unmodifiable view --- .../lang/function/FunctionRegistry.java | 149 +++++++++--------- .../njol/skript/lang/function/Functions.java | 13 +- .../njol/skript/lang/function/Signature.java | 3 +- .../function/FunctionReferenceParser.java | 6 +- .../skript/common/function/Parameters.java | 29 +++- .../skript/common/function/Signature.java | 5 +- 6 files changed, 118 insertions(+), 87 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 9dd60622861..9bc2b664c42 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -68,9 +68,78 @@ public Collection> elements() { return Collections.unmodifiableSet(functions); } + private record UnmodifiableFunctionRegistry( + org.skriptlang.skript.common.function.FunctionRegistry registry) implements org.skriptlang.skript.common.function.FunctionRegistry { + + @Override + public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { + return this; + } + + @Override + public Collection> elements() { + return registry.elements(); + } + + @Override + public void register(@NotNull Signature signature) { + throw new UnsupportedOperationException("Cannot register signatures on an unmodifiable function registry"); + } + + @Override + public void register(@NotNull String namespace, @NotNull Signature signature) { + throw new UnsupportedOperationException("Cannot register signatures on an unmodifiable function registry"); + } + + @Override + public void register(@NotNull Function function) { + throw new UnsupportedOperationException("Cannot register functions on an unmodifiable function registry"); + } + + @Override + public void register(@NotNull String namespace, @NotNull Function function) { + throw new UnsupportedOperationException("Cannot register functions on an unmodifiable function registry"); + } + + @Override + public @NotNull Retrieval> getFunction(@NotNull String name, @NonNull @NotNull Class... args) { + return registry.getFunction(name, args); + } + + @Override + public @NotNull Retrieval> getFunction(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + return registry.getFunction(namespace, name, args); + } + + @Override + public @NotNull Retrieval> getSignature(@NotNull String name, @NonNull @NotNull Class... args) { + return registry.getSignature(name, args); + } + + @Override + public @NotNull Retrieval> getSignature(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + return registry.getSignature(namespace, name, args); + } + + @Override + public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { + return registry.getSignatures(name); + } + + @Override + public @Unmodifiable @NotNull Set> getSignatures(@NotNull String namespace, @NotNull String name) { + return registry.getSignatures(namespace, name); + } + + @Override + public void remove(@NotNull Signature signature) { + throw new UnsupportedOperationException("Cannot remove functions on an unmodifiable function registry"); + } + } + @Override public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { - return null; + return new UnmodifiableFunctionRegistry(this); } @Override @@ -78,23 +147,7 @@ public void register(@NotNull Signature signature) { register(null, signature); } - /** - * Registers a signature. - *

- * Attempting to register a local signature in the global namespace, or a global signature in - * a local namespace, will throw an {@link IllegalArgumentException}. - * If {@code namespace} is null, will register this signature globally, - * only if the signature is global. - *

- * - * @param namespace The namespace to register the signature in. - * Usually represents the path of the script this signature is registered in. - * @param signature The signature to register. - * @throws SkriptAPIException if a signature with the same name and parameters is already registered - * in this namespace. - * @throws IllegalArgumentException if the signature is global and namespace is not null, or - * if the signature is local and namespace is null. - */ + @Override public void register(@Nullable String namespace, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); if (signature.hasModifier(Signature.Modifier.LOCAL) && namespace == null) { @@ -136,27 +189,10 @@ public void register(@Nullable String namespace, @NotNull Signature signature @Override public void register(@NotNull Function function) { - + register(null, function); } - /** - * Registers a function. - *

- * Attempting to register a local function in the global namespace, or a global function in - * a local namespace, will throw an {@link IllegalArgumentException}. - * If {@code namespace} is null, will register this function globally, - * only if the function is global. - *

- * - * @param namespace The namespace to register the function in. - * Usually represents the path of the script this function is registered in. - * @param function The function to register. - * @throws SkriptAPIException if the function name is invalid or if - * a function with the same name and parameters is already registered - * in this namespace. - * @throws IllegalArgumentException if the function is global and namespace is not null, or - * if the function is local and namespace is null. - */ + @Override public void register(@Nullable String namespace, @NotNull Function function) { Signature signature = function.signature(); Preconditions.checkNotNull(function, "function cannot be null"); @@ -234,17 +270,7 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull return false; } - /** - * Gets a function from a script. If no local function is found, checks for global functions. - * If {@code namespace} is null, only global functions will be checked. - * - * @param namespace The namespace to get the function from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the function. - * @param args The types of the arguments of the function. - * @return Information related to the attempt to get the specified function, - * stored in a {@link Retrieval} object. - */ + @Override public @NotNull Retrieval> getFunction( @Nullable String namespace, @NotNull String name, @@ -312,16 +338,7 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull } } - /** - * Gets the signature for a function with the given name and arguments. If no local function is found, - * checks for global functions. If {@code namespace} is null, only global signatures will be checked. - * - * @param namespace The namespace to get the function from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the function. - * @param args The types of the arguments of the function. - * @return The signature for the function with the given name and argument types, or null if no such function exists. - */ + @Override public @NonNull Retrieval> getSignature( @Nullable String namespace, @NotNull String name, @@ -384,6 +401,7 @@ Retrieval> getExactSignature( * @param name The name of the signature(s) to obtain. * @return A list of all signatures named {@code name}. */ + @Override public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { Preconditions.checkNotNull(name, "name cannot be null"); @@ -552,11 +570,7 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp return Collections.unmodifiableSet(candidates); } - /** - * Removes a function's signature from the registry. - * - * @param signature The signature to remove. - */ + @Override public void remove(@NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); @@ -611,15 +625,6 @@ private void removeUpdateMaps(Namespace namespace, FunctionIdentifier toRemove, */ private record NamespaceIdentifier(@Nullable String name) { - /** - * Returns whether this identifier is for local namespaces. - * - * @return Whether this identifier is for local namespaces. - */ - public boolean local() { - return name == null; - } - } /** 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 6ce1da73e51..ab8a9be3477 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -4,13 +4,14 @@ import ch.njol.skript.SkriptAPIException; import ch.njol.skript.SkriptAddon; import ch.njol.skript.config.SectionNode; -import ch.njol.skript.lang.function.FunctionRegistry.Retrieval; -import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.DefaultFunction; import org.skriptlang.skript.common.function.FunctionParser; import org.skriptlang.skript.common.function.FunctionReference; +import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; +import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import org.skriptlang.skript.common.function.Parameter; +import org.skriptlang.skript.common.function.Signature.Modifier; import org.skriptlang.skript.lang.script.Script; import java.util.*; @@ -72,7 +73,7 @@ public static DefaultFunction register(DefaultFunction function) { } globalFunctions.put(function.name(), javaNamespace); - FunctionRegistry.getRegistry().register(null, (Function) function); + FunctionRegistry.getRegistry().register(null, function); return function; } @@ -161,7 +162,7 @@ public static JavaFunction registerFunction(JavaFunction function) { * @see Functions#parseSignature(String, String, String, String, boolean) */ public static @Nullable Signature registerSignature(Signature signature) { - Retrieval> existing; + Retrieval> existing; Parameter[] parameters = signature.parameters().all(); if (parameters.length == 1 && !parameters[0].isSingle()) { @@ -177,10 +178,10 @@ public static JavaFunction registerFunction(JavaFunction function) { // if this function has already been registered, only allow it if one function is local and one is global. // if both are global or both are local, disallow. - if (existing.result() == RetrievalResult.EXACT && existing.retrieved().isLocal() == signature.isLocal()) { + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Modifier.LOCAL) == signature.isLocal()) { StringBuilder error = new StringBuilder(); - if (existing.retrieved().isLocal()) { + if (existing.retrieved().hasModifier(Modifier.LOCAL)) { error.append("Local function "); } else { error.append("Function "); diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index fc9e94dc59f..4e251358def 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -228,6 +228,7 @@ public boolean isLocal() { /** * @return The namespace of this signature. */ + @Override public String namespace() { return script; } @@ -318,7 +319,7 @@ public String toString(boolean includeReturnType, boolean debug) { .append(')'); if (includeReturnType && returns != null) { - signatureBuilder.append(" :: "); + signatureBuilder.append(" returns "); signatureBuilder.append(Utils.toEnglishPlural(returnType.getCodeName(), returns.isArray())); } diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java index 17d2cf1e9d6..fa09a082afb 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java @@ -212,7 +212,7 @@ private Set> getExactReferences( Argument[] originalArguments = arguments; for (Signature signature : signatures) { // if arguments arent possible, skip - if (arguments.length > signature.getMaxParameters() || arguments.length < signature.getMinParameters()) { + if (arguments.length > signature.parameters().maxCount() || arguments.length < signature.parameters().minCount()) { continue; } arguments = originalArguments; @@ -534,9 +534,9 @@ private void doesNotExist(String name, FunctionReference.Argument[] argu }); return currentArgumentTypes.isEmpty(); }) - .min(Comparator.comparingInt(signature -> Math.abs(arguments.length - signature.getMaxParameters()))); + .min(Comparator.comparingInt(signature -> Math.abs(arguments.length - signature.parameters().maxCount()))); if (intended.isPresent()) { - possibleMatch = " " + POTENTIAL_SIGNATURE.toString(intended.get().toString(false, false)); + possibleMatch = " " + POTENTIAL_SIGNATURE.toString(intended.get().toString()); } Skript.error(UNKNOWN_FUNCTION.toString(name, joiner) + possibleMatch); } diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameters.java b/src/main/java/org/skriptlang/skript/common/function/Parameters.java index ebb65867ba7..10c47a350c1 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameters.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameters.java @@ -1,11 +1,10 @@ package org.skriptlang.skript.common.function; +import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -import java.util.Arrays; -import java.util.Collections; -import java.util.SequencedMap; +import java.util.*; /** * Holding class for a parameters of a function. @@ -67,6 +66,30 @@ public int size() { return indexed.length; } + /** + * @return The most amount of parameters this function supports. + */ + public int maxCount() { + return size(); + } + + /** + * @return The least amount of parameters this function supports. + */ + public int minCount() { + List> params = new LinkedList<>(List.of(indexed)); + + int i = size() - 1; + for (Parameter parameter : Lists.reverse(params)) { + if (!parameter.hasModifier(Parameter.Modifier.OPTIONAL)) { + return i + 1; + } + i--; + } + + return 0; + } + /** * @return A copy of the backing sequenced map. */ diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index aad8f0b5bef..f2a259fceeb 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -61,10 +61,11 @@ public interface Signature { * @return Whether this signature returns single values. */ default boolean isSingle() { - if (returnType() == null) { + Class returnType = returnType(); + if (returnType == null) { return false; } - return !returnType().isArray(); + return !returnType.isArray(); } /** From 7c621a040bcaa60f515f1e99fa89ff18249eb41c Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 8 May 2026 17:33:49 +0200 Subject: [PATCH 04/32] deprecate getRegistry --- src/main/java/ch/njol/skript/Skript.java | 4 +- .../lang/function/FunctionReference.java | 34 +++++----- .../lang/function/FunctionRegistry.java | 19 +++--- .../njol/skript/lang/function/Functions.java | 62 ------------------- .../skript/common/function/Function.java | 1 + .../common/function/FunctionRegistry.java | 18 ++++++ .../skript/common/function/Parameters.java | 40 ++++++------ 7 files changed, 74 insertions(+), 104 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index fb5a10961b1..d776306f9f0 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -12,7 +12,6 @@ import ch.njol.skript.lang.*; import ch.njol.skript.lang.Condition.ConditionType; import ch.njol.skript.lang.Effect; -import ch.njol.skript.lang.function.FunctionRegistry; import ch.njol.skript.lang.util.SimpleExpression; import ch.njol.skript.localization.Language; import ch.njol.skript.localization.Message; @@ -69,6 +68,7 @@ import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos; import org.skriptlang.skript.bukkit.text.TextComponentParser; import org.skriptlang.skript.common.CommonModule; +import org.skriptlang.skript.common.function.FunctionRegistry; import org.skriptlang.skript.docs.Origin; import org.skriptlang.skript.lang.comparator.Comparator; import org.skriptlang.skript.lang.comparator.Comparators; @@ -481,7 +481,7 @@ public void onEnable() { //noinspection removal EventValues.setEventValueRegistry(eventValueRegistry); - skript.storeRegistry(FunctionRegistry.class, new FunctionRegistry()); + skript.storeRegistry(FunctionRegistry.class, FunctionRegistry.empty(skript)); // TODO this upcoming portion is a bad circular dependency // some modules depend on the config diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 370d0071e35..034282f6187 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -18,6 +18,8 @@ import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import org.skriptlang.skript.common.function.Parameter; +import org.skriptlang.skript.common.function.Function; +import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.common.function.Parameter.Modifier; import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier; import org.skriptlang.skript.lang.converter.Converters; @@ -46,13 +48,13 @@ public class FunctionReference implements Contract, Executable { * Signature of referenced function. If {@link #validateFunction(boolean)} * succeeds, this is not null. */ - private @Nullable Signature signature; + private @Nullable org.skriptlang.skript.common.function.Signature signature; /** * Actual function reference. Null before the function is called for first * time. */ - private @Nullable Function function; + private @Nullable org.skriptlang.skript.common.function.Function function; /** * If all function parameters can be condensed to a single list. @@ -116,7 +118,7 @@ public boolean validateParameterArity(boolean first) { return false; // Not enough parameters - return parameters.length >= sign.getMinParameters(); + return parameters.length >= sign.parameters().minCount(); } private Class[] parameterTypes; @@ -191,16 +193,16 @@ public boolean validateFunction(boolean first) { } // Validate parameter count - singleListParam = sign.getMaxParameters() == 1 && !sign.parameters().getFirst().isSingle(); + singleListParam = sign.parameters().maxCount() == 1 && !sign.parameters().getFirst().isSingle(); if (!singleListParam) { // Check that parameter count is within allowed range // Too many parameters - if (parameters.length > sign.getMaxParameters()) { + if (parameters.length > sign.parameters().maxCount()) { if (first) { - if (sign.getMaxParameters() == 0) { + if (sign.parameters().maxCount() == 0) { Skript.error("The function '" + stringified + "' has no arguments, but " + parameters.length + " are given." + " To call a function without parameters, just write the function name followed by '()', e.g. 'func()'."); } else { - Skript.error("The function '" + stringified + "' has only " + sign.getMaxParameters() + " argument" + (sign.getMaxParameters() == 1 ? "" : "s") + "," + Skript.error("The function '" + stringified + "' has only " + sign.parameters().maxCount() + " argument" + (sign.parameters().maxCount() == 1 ? "" : "s") + "," + " but " + parameters.length + " are given." + " If you want to use lists in function calls, you have to use additional parentheses, e.g. 'give(player, (iron ore and gold ore))'"); } @@ -214,9 +216,9 @@ public boolean validateFunction(boolean first) { } // Not enough parameters - if (parameters.length < sign.getMinParameters()) { + if (parameters.length < sign.parameters().minCount()) { if (first) { - Skript.error("The function '" + stringified + "' requires at least " + sign.getMinParameters() + " argument" + (sign.getMinParameters() == 1 ? "" : "s") + "," + Skript.error("The function '" + stringified + "' requires at least " + sign.parameters().minCount() + " argument" + (sign.parameters().minCount() == 1 ? "" : "s") + "," + " but only " + parameters.length + " " + (parameters.length == 1 ? "is" : "are") + " given."); } else { Skript.error("The function '" + stringified + "' was redefined with a different, incompatible amount of arguments, but is still used in other script(s)." @@ -289,9 +291,9 @@ public boolean validateFunction(boolean first) { .map(it -> new Argument<>(ArgumentType.UNNAMED, null, it)) .toArray(Argument[]::new); - sign.calls().add(new org.skriptlang.skript.common.function.FunctionReference<>(script, functionName, signature, arguments)); + sign.addCall(new org.skriptlang.skript.common.function.FunctionReference<>(script, functionName, signature, arguments)); - Contract contract = sign.getContract(); + Contract contract = sign.contract(); if (contract != null) this.contract = contract; @@ -322,7 +324,7 @@ private Signature getRegisteredSignature() { functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); } - Retrieval> attempt = FunctionRegistry.getRegistry().getSignature(script, functionName, parameterTypes); + Retrieval> attempt = Skript.instance().registry(org.skriptlang.skript.common.function.FunctionRegistry.class).getSignature(script, functionName, parameterTypes); if (attempt.result() == RetrievalResult.EXACT) { return attempt.retrieved(); } @@ -345,7 +347,7 @@ private Function getRegisteredFunction() { functionName, Arrays.toString(Arrays.stream(parameterTypes).map(Class::getSimpleName).toArray())); } - Retrieval> attempt = FunctionRegistry.getRegistry().getFunction(script, functionName, parameterTypes); + Retrieval> attempt = Skript.instance().registry(org.skriptlang.skript.common.function.FunctionRegistry.class).getFunction(script, functionName, parameterTypes); if (attempt.result() == RetrievalResult.EXACT) { return attempt.retrieved(); } @@ -395,7 +397,8 @@ public boolean resetReturnValue() { } // Execute the function - return function.execute(params); + //noinspection unchecked + return (T[]) ((ch.njol.skript.lang.function.Function) function).execute(params); } private Object[] evaluateSingleListParameter(Parameter parameter, Expression[] arguments, Event event) { @@ -494,7 +497,8 @@ public T[] execute(Event event, Object... arguments) { // We shouldn't trust the caller provided an array of arrays Object[][] consigned = consign(arguments); try { - return function.execute(consigned); + //noinspection unchecked + return (T[]) ((ch.njol.skript.lang.function.Function) function).execute(consigned); } finally { this.resetReturnValue(); } diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 9bc2b664c42..e21001fd627 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -6,6 +6,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; @@ -21,22 +22,26 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -/** - * A registry for functions. - */ +@Experimental @ApiStatus.Internal public final class FunctionRegistry implements org.skriptlang.skript.common.function.FunctionRegistry { + private final org.skriptlang.skript.Skript skript; + + public FunctionRegistry(org.skriptlang.skript.Skript skript) { + this.skript = skript; + } + private static FunctionRegistry registry; /** - * Gets the global function registry. - * - * @return The global function registry. + * @deprecated Accessing the function registry requires a Skript instance to allow modification. + * Therefore, this method only returns an unmodifiable registry. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static FunctionRegistry getRegistry() { if (registry == null) { - registry = new FunctionRegistry(); + registry = new FunctionRegistry(Skript.instance()); } return registry; } 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 ab8a9be3477..7d6af73a66d 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -217,20 +217,6 @@ public static JavaFunction registerFunction(JavaFunction function) { return signature; } - /** - * Gets a function, if it exists. Note that even if function exists in scripts, - * it might not have been parsed yet. If you want to check for existence, - * then use {@link #getGlobalSignature(String)}. - * - * @deprecated in favour of {@link #getGlobalFunction(String)} for proper name. - * @param name Name of function. - * @return Function, or null if it does not exist. - */ - @Deprecated(since = "2.7.0", forRemoval = true) - public static @Nullable Function getFunction(String name) { - return getGlobalFunction(name); - } - /** * Gets a function, if it exists. Note that even if function exists in scripts, * it might not have been parsed yet. If you want to check for existence, @@ -283,18 +269,6 @@ public static JavaFunction registerFunction(JavaFunction function) { return function; } - /** - * Gets a signature of function with given name. - * - * @deprecated in favour of {@link #getGlobalSignature(String)} for proper name. - * @param name Name of function. - * @return Signature, or null if function does not exist. - */ - @Deprecated(since = "2.7.0", forRemoval = true) - public static @Nullable Signature getSignature(String name) { - return getGlobalSignature(name); - } - /** * Gets a signature of function with given name. * @@ -347,29 +321,6 @@ public static JavaFunction registerFunction(JavaFunction function) { private final static Collection> toValidate = new ArrayList<>(); - @Deprecated(since = "2.7.0", forRemoval = true) - public static int clearFunctions(String script) { - // Get and remove function namespace of script - Namespace namespace = namespaces.remove(new Namespace.Key(Namespace.Origin.SCRIPT, script)); - if (namespace == null) { // No functions defined - return 0; - } - - // Remove references to this namespace from global functions - globalFunctions.values().removeIf(loopedNamespaced -> loopedNamespaced == namespace); - - // Queue references to signatures we have for revalidation - // Can't validate here, because other scripts might be loaded soon - for (Signature sign : namespace.getSignatures()) { - for (FunctionReference ref : sign.calls()) { - if (!script.equals(ref.namespace())) { - toValidate.add(ref); - } - } - } - return namespace.getSignatures().size(); - } - public static void unregisterFunction(Signature signature) { FunctionRegistry.getRegistry().remove(signature); @@ -400,19 +351,6 @@ public static void validateFunctions() { toValidate.clear(); } - /** - * Clears all function calls and removes script functions. - */ - @Deprecated(since = "2.7.0", forRemoval = true) - public static void clearFunctions() { - // Keep Java functions, remove everything else - globalFunctions.values().removeIf(namespace -> namespace != javaNamespace); - namespaces.clear(); - - assert toValidate.isEmpty() : toValidate; - toValidate.clear(); - } - /** * @deprecated Use {@link #getFunctions()} instead. */ diff --git a/src/main/java/org/skriptlang/skript/common/function/Function.java b/src/main/java/org/skriptlang/skript/common/function/Function.java index 5266f57d2ad..306d373a369 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Function.java +++ b/src/main/java/org/skriptlang/skript/common/function/Function.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.ApiStatus.NonExtendable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.util.event.Event; /** * Represents a function implementation. diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java index 77064ff79e2..4be635f48f4 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java @@ -4,8 +4,11 @@ import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.ApiStatus.NonExtendable; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.Skript; +import org.skriptlang.skript.addon.SkriptAddon; import org.skriptlang.skript.util.Registry; import org.skriptlang.skript.util.ViewProvider; @@ -13,12 +16,27 @@ /** * A registry for functions. + *

+ * Obtain an instance using {@code SkriptAddon#registry(FunctionRegistry.class)}. + *
+ * Or an unmodifiable view using {@code Skript.instance().registry(FunctionRegistry.class)}. */ @NonExtendable @Internal @Experimental public interface FunctionRegistry extends Registry>, ViewProvider { + /** + * Creates an empty function registry. + * + * @param skript the Skript instance + * @return a new empty function registry + */ + @Contract("_ -> new") + static FunctionRegistry empty(Skript skript) { + return new ch.njol.skript.lang.function.FunctionRegistry(skript); + } + /** * Registers a global signature. *

diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameters.java b/src/main/java/org/skriptlang/skript/common/function/Parameters.java index 10c47a350c1..25fbc5ab124 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameters.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameters.java @@ -11,17 +11,31 @@ */ public final class Parameters { + private final int maxCount; + private int minCount = 0; private final SequencedMap> named; private final Parameter[] indexed; public Parameters(SequencedMap> parameters) { this.named = parameters; + this.maxCount = parameters.size(); + + this.indexed = new Parameter[this.maxCount]; + { + int i = 0; + for (Parameter parameter : parameters.values()) { + this.indexed[i] = parameter; + i++; + } + } - indexed = new Parameter[parameters.size()]; - int i = 0; - for (Parameter parameter : parameters.values()) { - indexed[i] = parameter; - i++; + int j = size() - 1; + for (Parameter parameter : Lists.reverse(new LinkedList<>(parameters.values()))) { + if (!parameter.hasModifier(Parameter.Modifier.OPTIONAL)) { + this.minCount = j + 1; + break; + } + j--; } } @@ -63,31 +77,21 @@ public Parameter[] all() { * @return The amount of parameters. */ public int size() { - return indexed.length; + return maxCount; } /** * @return The most amount of parameters this function supports. */ public int maxCount() { - return size(); + return maxCount; } /** * @return The least amount of parameters this function supports. */ public int minCount() { - List> params = new LinkedList<>(List.of(indexed)); - - int i = size() - 1; - for (Parameter parameter : Lists.reverse(params)) { - if (!parameter.hasModifier(Parameter.Modifier.OPTIONAL)) { - return i + 1; - } - i--; - } - - return 0; + return minCount; } /** From a16c33cee052512c180551d875f709db7d635680 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Wed, 13 May 2026 15:12:25 +0200 Subject: [PATCH 05/32] deprecate existing Functions methods --- .../lang/function/FunctionRegistry.java | 4 +- .../njol/skript/lang/function/Functions.java | 36 ++--- .../skript/structures/StructFunction.java | 126 +++++++++++++----- .../common/function/FunctionParser.java | 2 +- 4 files changed, 107 insertions(+), 61 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index e21001fd627..9e249933d09 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -50,7 +50,7 @@ public static FunctionRegistry getRegistry() { * The pattern for a valid function name. * Functions must start with a letter or underscore and can only contain letters, numbers, and underscores. */ - final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile(Functions.functionNamePattern); + final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile("[\\p{IsAlphabetic}_][\\p{IsAlphabetic}\\d_]*"); /** * The namespace for registered global functions. @@ -380,7 +380,7 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ - Retrieval> getExactSignature( + public Retrieval> getExactSignature( @Nullable String namespace, @NotNull String name, @NotNull Class... args 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 7d6af73a66d..fba9d17cf7f 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -4,6 +4,7 @@ import ch.njol.skript.SkriptAPIException; import ch.njol.skript.SkriptAddon; import ch.njol.skript.config.SectionNode; +import ch.njol.skript.structures.StructFunction; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.DefaultFunction; import org.skriptlang.skript.common.function.FunctionParser; @@ -22,14 +23,12 @@ */ public abstract class Functions { - private static final String INVALID_FUNCTION_DEFINITION = - "Invalid function definition. Please check for " + - "typos and make sure that the function's name " + - "only contains letters and underscores. " + - "Refer to the documentation for more information."; - private Functions() {} + /** + * @deprecated Unused. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable ScriptFunction currentFunction = null; /** @@ -102,13 +101,9 @@ public static JavaFunction registerFunction(JavaFunction function) { public final static String functionNamePattern = "[\\p{IsAlphabetic}_][\\p{IsAlphabetic}\\d_]*"; /** - * Loads a script function from given node. - * @param script The script the function is declared in - * @param node Section node. - * @param signature The signature of the function. Use {@link Functions#parseSignature(String, String, String, String, boolean)} - * to get a new signature instance and {@link Functions#registerSignature(Signature)} to register the signature - * @return Script function, or null if something went wrong. + * @deprecated Handled by {@link StructFunction}. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Function loadFunction(Script script, SectionNode node, Signature signature) { String name = signature.getName(); Namespace namespace = getScriptNamespace(script.getConfig().getFileName()); @@ -118,10 +113,6 @@ public static JavaFunction registerFunction(JavaFunction function) { return null; // Probably duplicate signature; reported before } - if (Skript.debug() || node.debug()) { - Skript.debug(signature.toString()); - } - Function function; try { function = new ScriptFunction<>(signature, node); @@ -152,15 +143,13 @@ public static JavaFunction registerFunction(JavaFunction function) { */ @Deprecated(forRemoval = true, since = "2.14") public static @Nullable Signature parseSignature(String script, String name, String args, @Nullable String returnType, boolean local) { - return FunctionParser.parse(script, name, args, returnType, local); + return (Signature) FunctionParser.parse(script, name, args, returnType, local); } /** - * Registers the signature. - * @param signature The signature to register. - * @return Signature of function, or null if something went wrong. - * @see Functions#parseSignature(String, String, String, String, boolean) + * @deprecated Handled by {@link StructFunction}. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Signature registerSignature(Signature signature) { Retrieval> existing; Parameter[] parameters = signature.parameters().all(); @@ -364,10 +353,9 @@ public static Collection> getJavaFunctions() { } /** - * Returns all functions registered using Java. - * - * @return All {@link JavaFunction} or {@link DefaultFunction} functions. + * @return Use {@link org.skriptlang.skript.common.function.FunctionRegistry#elements()} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static Collection> getFunctions() { return javaNamespace.getFunctions(); } diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index dda314d6d3a..f6a4f356000 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -2,57 +2,50 @@ import ch.njol.skript.ScriptLoader; import ch.njol.skript.Skript; -import ch.njol.skript.SkriptConfig; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.SkriptAPIException; import ch.njol.skript.config.SectionNode; import ch.njol.skript.doc.Description; import ch.njol.skript.doc.Example; import ch.njol.skript.doc.Name; import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Literal; -import ch.njol.skript.lang.ParseContext; -import ch.njol.skript.lang.SkriptParser; import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.lang.function.FunctionEvent; -import ch.njol.skript.lang.function.Functions; -import ch.njol.skript.lang.function.Signature; +import ch.njol.skript.lang.function.*; import ch.njol.skript.lang.parser.ParserInstance; -import ch.njol.skript.registrations.Classes; -import ch.njol.skript.util.Utils; -import ch.njol.skript.util.Utils.PluralResult; -import ch.njol.util.StringUtils; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.common.function.*; +import org.skriptlang.skript.common.function.FunctionParser; +import org.skriptlang.skript.common.function.FunctionReference; +import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; +import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; +import org.skriptlang.skript.common.function.Parameter; +import org.skriptlang.skript.common.function.Signature.Modifier; import org.skriptlang.skript.lang.entry.EntryContainer; +import org.skriptlang.skript.lang.script.Script; import org.skriptlang.skript.lang.structure.Structure; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Locale; -import java.util.SequencedMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @Name("Function") @Description({ - "Functions are structures that can be executed with arguments/parameters to run code.", - "They can also return a value to the trigger that is executing the function.", - "Note that local functions come before global functions execution" + "Functions are structures that can be executed with arguments/parameters to run code.", + "They can also return a value to the trigger that is executing the function.", + "Note that local functions come before global functions execution" }) @Example(""" - function sayMessage(message: text): - broadcast {_message} # our message argument is available in '{_message}' - """) + function sayMessage(message: text): + broadcast {_message} # our message argument is available in '{_message}' + """) @Example(""" - local function giveApple(amount: number) :: item: - return {_amount} of apple - """) + local function giveApple(amount: number) :: item: + return {_amount} of apple + """) @Example(""" - function getPoints(p: player) returns number: - return {points::%{_p}%} - """) + function getPoints(p: player) returns number: + return {points::%{_p}%} + """) @Since("2.2, 2.7 (local functions)") public class StructFunction extends Structure { @@ -81,12 +74,12 @@ public class StructFunction extends Structure { *

*/ private static final Pattern SIGNATURE_PATTERN = - Pattern.compile("^(?:local )?function (?" + Functions.functionNamePattern + ")\\((?.*?)\\)(?:\\s*(?:->|::| returns )\\s*(?.+))?$"); + Pattern.compile("^(?:local )?function (?[\\p{IsAlphabetic}_][\\p{IsAlphabetic}\\d_]*)\\((?.*?)\\)(?:\\s*(?:->|::| returns )\\s*(?.+))?$"); private static final AtomicBoolean VALIDATE_FUNCTIONS = new AtomicBoolean(); static { Skript.registerStructure(StructFunction.class, - "[:local] function <.+>" + "[:local] function <.+>" ); } @@ -119,13 +112,58 @@ public boolean preLoad() { // parse signature getParser().setCurrentEvent((local ? "local " : "") + "function", FunctionEvent.class); signature = FunctionParser.parse( - getParser().getCurrentScript().getConfig().getFileName(), - matcher.group("name"), matcher.group("args"), matcher.group("returns"), local + getParser().getCurrentScript().getConfig().getFileName(), + matcher.group("name"), matcher.group("args"), matcher.group("returns"), local ); getParser().deleteCurrentEvent(); // attempt registration - return signature != null && Functions.registerSignature(signature) != null; + return signature != null && registerSignature(signature) != null; + } + + private static @Nullable Signature registerSignature(Signature signature) { + Retrieval> existing; + Parameter[] parameters = signature.parameters().all(); + + if (parameters.length == 1 && !parameters[0].isSingle()) { + existing = FunctionRegistry.getRegistry().getExactSignature(signature.namespace(), signature.getName(), parameters[0].type().arrayType()); + } else { + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + types[i] = parameters[i].type(); + } + + existing = FunctionRegistry.getRegistry().getExactSignature(signature.namespace(), signature.getName(), types); + } + + // if this function has already been registered, only allow it if one function is local and one is global. + // if both are global or both are local, disallow. + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Modifier.LOCAL) == signature.isLocal()) { + StringBuilder error = new StringBuilder(); + + if (existing.retrieved().hasModifier(Modifier.LOCAL)) { + error.append("Local function "); + } else { + error.append("Function "); + } + error.append("'%s' with the same argument types already exists".formatted(signature.getName())); + if (existing.retrieved().namespace() != null) { + error.append(" in script '%s'.".formatted(existing.retrieved().namespace())); + } else { + error.append("."); + } + + Skript.error(error.toString()); + return null; + } + + if (signature.isLocal()) { + ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature.namespace(), signature); + } else { + ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(null, signature); + } + + return signature; } @Override @@ -135,7 +173,7 @@ public boolean load() { assert signature != null; // noinspection ConstantConditions - entry container cannot be null as this structure is not simple - Functions.loadFunction(parser.getCurrentScript(), source, signature); + loadFunction(parser.getCurrentScript(), source, signature); parser.deleteCurrentEvent(); @@ -144,6 +182,26 @@ public boolean load() { return true; } + private static void loadFunction(Script script, SectionNode node, Signature signature) { + Function function; + try { + function = new ScriptFunction<>(signature, node); + } catch (SkriptAPIException ex) { + //noinspection ThrowableNotThrown + Skript.exception(ex, "Error while trying to load a function"); + + // avoid getting a "function is already registered" error when the function implementation is not known yet + Functions.unregisterFunction(signature); + return; + } + + if (function.getSignature().isLocal()) { + FunctionRegistry.getRegistry().register(script.getConfig().getFileName(), function); + } else { + FunctionRegistry.getRegistry().register(null, function); + } + } + @Override public boolean postLoad() { if (VALIDATE_FUNCTIONS.get()) { diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java b/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java index bba55aa7a26..9436a801b10 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java @@ -34,7 +34,7 @@ public class FunctionParser { * @return Parsed signature or null if something went wrong. * @see Functions#registerSignature(ch.njol.skript.lang.function.Signature) */ - public static @Nullable ch.njol.skript.lang.function.Signature parse(String script, String name, String args, @Nullable String returns, boolean local) { + public static @Nullable Signature parse(String script, String name, String args, @Nullable String returns, boolean local) { Parameters parameters = parseParameters(args); if (parameters == null) return null; From 3b19331f4a0e1b71db362805beb6cce0b3b2bb53 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Thu, 14 May 2026 19:13:09 +0200 Subject: [PATCH 06/32] deprecate more methods!! --- .../njol/skript/lang/function/Functions.java | 63 +++++++------------ .../njol/skript/lang/function/Namespace.java | 35 ++++++++--- .../skript/structures/StructFunction.java | 4 +- 3 files changed, 51 insertions(+), 51 deletions(-) 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 fba9d17cf7f..3623785a293 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -54,11 +54,9 @@ private Functions() {} public static boolean callFunctionEvents = false; /** - * Registers a {@link DefaultFunction}. - * - * @param function The function to register. - * @return The registered function. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#register(org.skriptlang.skript.common.function.Function)} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static DefaultFunction register(DefaultFunction function) { Skript.checkAcceptRegistrations(); @@ -207,13 +205,9 @@ public static JavaFunction registerFunction(JavaFunction function) { } /** - * Gets a function, if it exists. Note that even if function exists in scripts, - * it might not have been parsed yet. If you want to check for existence, - * then use {@link #getGlobalSignature(String)}. - * - * @param name Name of function. - * @return Function, or null if it does not exist. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getFunction(String, Class[])} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Function getGlobalFunction(String name) { Namespace namespace = globalFunctions.get(name); if (namespace == null) @@ -222,14 +216,9 @@ public static JavaFunction registerFunction(JavaFunction function) { } /** - * Gets a function, if it exists. Note that even if function exists in scripts, - * it might not have been parsed yet. If you want to check for existence, - * then use {@link #getLocalSignature(String, String)}. - * - * @param name Name of function. - * @param script The script where the function is declared in. Used to get local functions. - * @return Function, or null if it does not exist. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getFunction(String, String, Class[])} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Function getLocalFunction(String name, String script) { Namespace namespace = null; Function function = null; @@ -239,16 +228,11 @@ public static JavaFunction registerFunction(JavaFunction function) { return function; } + /** - * Gets a local function, if it doesn't exist it'll fall back to a global function, - * if it exists. Note that even if function exists in scripts, - * it might not have been parsed yet. If you want to check for existence, - * then use {@link #getSignature(String, String)}. - * - * @param name Name of function. - * @param script The script where the function is declared in. Used to get local functions. - * @return Function, or null if it does not exist. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getFunction(String, Class[])} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Function getFunction(String name, @Nullable String script) { if (script == null) return getGlobalFunction(name); @@ -259,11 +243,9 @@ public static JavaFunction registerFunction(JavaFunction function) { } /** - * Gets a signature of function with given name. - * - * @param name Name of function. - * @return Signature, or null if function does not exist. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getSignature(String, Class[])} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Signature getGlobalSignature(String name) { Namespace namespace = globalFunctions.get(name); if (namespace == null) @@ -272,12 +254,9 @@ public static JavaFunction registerFunction(JavaFunction function) { } /** - * Gets a signature of function with given name. - * - * @param name Name of function. - * @param script The script where the function is declared in. Used to get local functions. - * @return Signature, or null if function does not exist. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getSignature(String, String, Class[])} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Signature getLocalSignature(String name, String script) { Namespace namespace = null; Signature signature = null; @@ -288,13 +267,9 @@ public static JavaFunction registerFunction(JavaFunction function) { } /** - * Gets a signature of local function with the given name, if no signature was found, - * it will fall back to a global function. - * - * @param name Name of function. - * @param script The script where the function is declared in. Used to get local functions. - * @return Signature, or null if function does not exist. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getSignature(String, Class[])} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Signature getSignature(String name, @Nullable String script) { if (script == null) return getGlobalSignature(name); @@ -304,12 +279,20 @@ public static JavaFunction registerFunction(JavaFunction function) { return signature; } + /** + * @deprecated Pass script as a string instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Namespace getScriptNamespace(String script) { return namespaces.get(new Namespace.Key(Namespace.Origin.SCRIPT, script)); } private final static Collection> toValidate = new ArrayList<>(); + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#remove(org.skriptlang.skript.common.function.Signature)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static void unregisterFunction(Signature signature) { FunctionRegistry.getRegistry().remove(signature); diff --git a/src/main/java/ch/njol/skript/lang/function/Namespace.java b/src/main/java/ch/njol/skript/lang/function/Namespace.java index 53ace1f0794..d971ee2aab7 100644 --- a/src/main/java/ch/njol/skript/lang/function/Namespace.java +++ b/src/main/java/ch/njol/skript/lang/function/Namespace.java @@ -1,6 +1,7 @@ package ch.njol.skript.lang.function; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.common.function.FunctionRegistry; import java.util.Collection; import java.util.HashMap; @@ -8,10 +9,11 @@ import java.util.Objects; /** - * Contains a set of functions. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry} instead. */ +@Deprecated(forRemoval = true, since = "INSERT VERSION") public class Namespace { - + /** * Origin of functions in namespace. */ @@ -20,18 +22,18 @@ public enum Origin { * Functions implemented in Java. */ JAVA, - + /** * Script functions. */ SCRIPT } - + /** * Key to a namespace. */ public static class Key { - + private final Origin origin; private final @Nullable String scriptName; @@ -41,7 +43,7 @@ public Key(Origin origin, @Nullable String scriptName) { this.origin = origin; this.scriptName = scriptName; } - + public Origin getOrigin() { return origin; } @@ -121,7 +123,7 @@ public boolean equals(Object o) { return getName().equals(info.getName()); } } - + /** * Signatures of known functions. */ @@ -160,28 +162,43 @@ public boolean removeSignature(Signature sign) { signatures.remove(info); return true; } - + @SuppressWarnings("null") public Collection> getSignatures() { return signatures.values(); } + /** + * @deprecated Use {@link FunctionRegistry#getFunction(String, String, Class[])} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public @Nullable Function getFunction(String name, boolean local) { return functions.get(new Info(name, local)); } + /** + * @deprecated Use {@link FunctionRegistry#getFunction(String, String, Class[])} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public @Nullable Function getFunction(String name) { Function function = getFunction(name, true); return function == null ? getFunction(name, false) : function; } + /** + * @deprecated Use {@link FunctionRegistry#register(org.skriptlang.skript.common.function.Function)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public void addFunction(Function func) { Info info = new Info(func.getName(), func.getSignature().isLocal()); assert signatures.containsKey(info) : "missing signature for function"; functions.put(info, func); } - @SuppressWarnings("null") + /** + * @deprecated Use {@link FunctionRegistry#elements()} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public Collection> getFunctions() { return functions.values(); } diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index f6a4f356000..9aa0c0ff4e1 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -191,7 +191,7 @@ private static void loadFunction(Script script, SectionNode node, Signature s Skript.exception(ex, "Error while trying to load a function"); // avoid getting a "function is already registered" error when the function implementation is not known yet - Functions.unregisterFunction(signature); + FunctionRegistry.getRegistry().remove(signature); return; } @@ -214,7 +214,7 @@ public boolean postLoad() { @Override public void unload() { assert signature != null; - Functions.unregisterFunction(signature); + FunctionRegistry.getRegistry().remove(signature); signature.calls().forEach(FunctionReference::invalidateCache); VALIDATE_FUNCTIONS.set(true); } From 94372328931393c04cb5a2c09dc34d6f12ae7ebb Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 22 May 2026 20:33:45 +0200 Subject: [PATCH 07/32] commih --- .../lang/function/FunctionRegistry.java | 30 ++++++++----------- .../njol/skript/lang/function/Functions.java | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 9e249933d09..cecbadde503 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -149,30 +149,26 @@ public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() @Override public void register(@NotNull Signature signature) { - register(null, signature); + Preconditions.checkNotNull(signature, "signature cannot be null"); + if (signature.hasModifier(Signature.Modifier.LOCAL)) { + throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); + } + register0(GLOBAL_NAMESPACE, signature); } @Override - public void register(@Nullable String namespace, @NotNull Signature signature) { + public void register(@NotNull String namespace, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - if (signature.hasModifier(Signature.Modifier.LOCAL) && namespace == null) { - throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); - } - if (!signature.hasModifier(Signature.Modifier.LOCAL) && namespace != null) { + if (!signature.hasModifier(Signature.Modifier.LOCAL)) { throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); } + register0(new NamespaceIdentifier(namespace), signature); + } + private void register0(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { Skript.debug("Registering signature '%s'", signature.name()); - // namespace - NamespaceIdentifier namespaceId; - if (namespace != null) { - namespaceId = new NamespaceIdentifier(namespace); - } else { - namespaceId = GLOBAL_NAMESPACE; - } - - Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); + Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); FunctionIdentifier identifier = FunctionIdentifier.of(signature); // register @@ -182,13 +178,13 @@ public void register(@Nullable String namespace, @NotNull Signature signature Set identifiersWithName = ns.identifiers.computeIfAbsent(identifier.name, s -> new HashSet<>()); boolean exists = identifiersWithName.add(identifier); if (!exists) { - alreadyRegisteredError(signature.name(), identifier, namespaceId); + alreadyRegisteredError(signature.name(), identifier, namespace); } } Signature existing = ns.signatures.putIfAbsent(identifier, signature); if (existing != null) { - alreadyRegisteredError(signature.name(), identifier, namespaceId); + alreadyRegisteredError(signature.name(), identifier, namespace); } } 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 3623785a293..ceffc472fb4 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -196,7 +196,7 @@ public static JavaFunction registerFunction(JavaFunction function) { if (signature.isLocal()) { FunctionRegistry.getRegistry().register(signature.namespace(), signature); } else { - FunctionRegistry.getRegistry().register(null, signature); + FunctionRegistry.getRegistry().register(signature); } Skript.debug("Registered function signature: " + signature.getName()); From aa2ddcd8e63aeab612a1b5dd65428cee3636fc2b Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 22 May 2026 20:50:45 +0200 Subject: [PATCH 08/32] idk --- .../lang/function/FunctionRegistry.java | 43 ++++++++++++++++- .../skript/structures/StructFunction.java | 47 ++++--------------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index cecbadde503..7384b0f9fd6 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -162,10 +162,50 @@ public void register(@NotNull String namespace, @NotNull Signature signature) if (!signature.hasModifier(Signature.Modifier.LOCAL)) { throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); } + register0(new NamespaceIdentifier(namespace), signature); } private void register0(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { + Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), + "Invalid signature name '%s'".formatted(signature.name())); + + Retrieval> existing; + Parameter[] parameters = signature.parameters().all(); + + if (parameters.length == 1 && !parameters[0].isSingle()) { + existing = getExactSignature(signature.namespace(), signature.name(), parameters[0].type().arrayType()); + } else { + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + types[i] = parameters[i].type(); + } + + existing = getExactSignature(signature.namespace(), signature.name(), types); + } + + // if this function has already been registered, only allow it if one function is local and one is global. + // if both are global or both are local, disallow. + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Signature.Modifier.LOCAL) == signature.hasModifier(Signature.Modifier.LOCAL)) { + StringBuilder error = new StringBuilder(); + + if (existing.retrieved().hasModifier(Signature.Modifier.LOCAL)) { + error.append("Local function "); + } else { + error.append("Function "); + } + error.append("'%s' with the same argument types already exists".formatted(signature.name())); + if (existing.retrieved().namespace() != null) { + error.append(" in script '%s'.".formatted(existing.retrieved().namespace())); + } else { + error.append("."); + } + + Skript.error(error.toString()); + + return; + } + Skript.debug("Registering signature '%s'", signature.name()); Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); @@ -182,8 +222,7 @@ private void register0(@NotNull NamespaceIdentifier namespace, @NotNull Signatur } } - Signature existing = ns.signatures.putIfAbsent(identifier, signature); - if (existing != null) { + if (ns.signatures.putIfAbsent(identifier, signature) != null) { alreadyRegisteredError(signature.name(), identifier, namespace); } } diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index 9aa0c0ff4e1..9525ceca33b 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -111,7 +111,7 @@ public boolean preLoad() { // parse signature getParser().setCurrentEvent((local ? "local " : "") + "function", FunctionEvent.class); - signature = FunctionParser.parse( + signature = (Signature) FunctionParser.parse( getParser().getCurrentScript().getConfig().getFileName(), matcher.group("name"), matcher.group("args"), matcher.group("returns"), local ); @@ -121,46 +121,15 @@ public boolean preLoad() { return signature != null && registerSignature(signature) != null; } - private static @Nullable Signature registerSignature(Signature signature) { - Retrieval> existing; - Parameter[] parameters = signature.parameters().all(); - - if (parameters.length == 1 && !parameters[0].isSingle()) { - existing = FunctionRegistry.getRegistry().getExactSignature(signature.namespace(), signature.getName(), parameters[0].type().arrayType()); - } else { - Class[] types = new Class[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - types[i] = parameters[i].type(); - } - - existing = FunctionRegistry.getRegistry().getExactSignature(signature.namespace(), signature.getName(), types); - } - - // if this function has already been registered, only allow it if one function is local and one is global. - // if both are global or both are local, disallow. - if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Modifier.LOCAL) == signature.isLocal()) { - StringBuilder error = new StringBuilder(); - - if (existing.retrieved().hasModifier(Modifier.LOCAL)) { - error.append("Local function "); - } else { - error.append("Function "); - } - error.append("'%s' with the same argument types already exists".formatted(signature.getName())); - if (existing.retrieved().namespace() != null) { - error.append(" in script '%s'.".formatted(existing.retrieved().namespace())); + private static Signature registerSignature(Signature signature) { + try { + if (signature.isLocal()) { + ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature.namespace(), signature); } else { - error.append("."); + ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature); } - - Skript.error(error.toString()); - return null; - } - - if (signature.isLocal()) { - ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature.namespace(), signature); - } else { - ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(null, signature); + } catch (SkriptAPIException ex) { + Skript.error(ex.getMessage()); } return signature; From c339816a7e31a9a5dbd5817fbde15cb6f6b2d13f Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Mon, 20 Jul 2026 13:36:13 -0400 Subject: [PATCH 09/32] Add alternative Patterns constructor (#8755) --- .../java/ch/njol/skript/util/Patterns.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/ch/njol/skript/util/Patterns.java b/src/main/java/ch/njol/skript/util/Patterns.java index 5f2b9061a29..486e911d77c 100644 --- a/src/main/java/ch/njol/skript/util/Patterns.java +++ b/src/main/java/ch/njol/skript/util/Patterns.java @@ -40,6 +40,24 @@ public Patterns(Object[][] info) { } } + /** + * Creates a new {@link Patterns} with the provided paired inputs. + * @param info Pattern-Value pairs, for example: + * + * new Patterns("pattern1", value1, "pattern2", value2); + * + */ + public Patterns(Object... info) { + patterns = new String[info.length / 2]; + types = new Object[info.length / 2]; + for (int i = 0; i < info.length; i += 2) { + int pattern = i / 2; + patterns[pattern] = (String) info[i]; + types[pattern] = info[i + 1]; + matchedPatterns.computeIfAbsent(info[i + 1], list -> new ArrayList<>()).add(pattern); + } + } + /** * Returns an array of the registered patterns. * @return An {@link java.lang.reflect.Array} of {@link String}s. From 40b51f651253d21261f817b0308d7f2853532f1e Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Thu, 30 Jul 2026 10:28:01 -0400 Subject: [PATCH 10/32] Remove 1.21.4 environment (#8784) --- .github/workflows/java-21-builds.yml | 4 ++-- .github/workflows/junit-21-builds.yml | 4 ++-- build.gradle | 2 +- .../environments/java21/paper-1.21.4.json | 17 ----------------- 4 files changed, 5 insertions(+), 22 deletions(-) delete mode 100644 src/test/skript/environments/java21/paper-1.21.4.json diff --git a/.github/workflows/java-21-builds.yml b/.github/workflows/java-21-builds.yml index 631b25d70c1..dfd4ffc9456 100644 --- a/.github/workflows/java-21-builds.yml +++ b/.github/workflows/java-21-builds.yml @@ -13,9 +13,9 @@ jobs: if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" uses: ./.github/workflows/parallelize-tests.yml with: - environments: 1.21.4,1.21.5,1.21.8,1.21.10,1.21.11 + environments: 1.21.5,1.21.8,1.21.10,1.21.11 java_version: 21 - parallel_jobs: 3 + parallel_jobs: 2 build: name: ${{ matrix.display }} diff --git a/.github/workflows/junit-21-builds.yml b/.github/workflows/junit-21-builds.yml index 71dcde9b041..4050da5d768 100644 --- a/.github/workflows/junit-21-builds.yml +++ b/.github/workflows/junit-21-builds.yml @@ -13,9 +13,9 @@ jobs: if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" uses: ./.github/workflows/parallelize-tests.yml with: - environments: 1.21.4,1.21.5,1.21.8,1.21.10,1.21.11 + environments: 1.21.5,1.21.8,1.21.10,1.21.11 java_version: 21 - parallel_jobs: 3 + parallel_jobs: 2 build: name: ${{ matrix.display }} diff --git a/build.gradle b/build.gradle index d447088cefe..586d1fbda18 100644 --- a/build.gradle +++ b/build.gradle @@ -295,7 +295,7 @@ tasks.register('JUnit') { // custom test task // usage: gradle customTest -PtestEnvs= -PtestEnvJavaVersion= -Ptimeout= -Pjunit=[true|false] // defaults: testEnvJavaVersion=latestJava, timeout=0, junit=false -// example: gradle customTest -PtestEnvs="java21/paper-1.21.4,java21/paper-1.21.8" -PtestEnvJavaVersion=21 -Ptimeout=600000 -Pjunit=true +// example: gradle customTest -PtestEnvs="java21/paper-1.21.5,java21/paper-1.21.8" -PtestEnvJavaVersion=21 -Ptimeout=600000 -Pjunit=true // get environments String propEnvs = project.hasProperty('testEnvs') ? project.property('testEnvs') as String : project.testEnv diff --git a/src/test/skript/environments/java21/paper-1.21.4.json b/src/test/skript/environments/java21/paper-1.21.4.json deleted file mode 100644 index 5957458a7a9..00000000000 --- a/src/test/skript/environments/java21/paper-1.21.4.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "paper-1.21.4", - "resources": [ - {"source": "server.properties.generic", "target": "server.properties"} - ], - "paperDownloads": [ - { - "version": "1.21.4", - "target": "paperclip.jar" - } - ], - "skriptTarget": "plugins/Skript.jar", - "commandLine": [ - "-Dcom.mojang.eula.agree=true", - "-jar", "paperclip.jar", "--nogui" - ] -} From 1000a66d4acd7f9ad7b691eb8c5b6e790f5c3901 Mon Sep 17 00:00:00 2001 From: Patrick Miller Date: Fri, 31 Jul 2026 17:17:53 -0400 Subject: [PATCH 11/32] RegistryClassInfo Housekeeping (#8759) --- .../njol/skript/bukkitutil/BukkitUtils.java | 28 ++-- .../ch/njol/skript/classes/ClassInfo.java | 1 - .../skript/classes/data/BukkitClasses.java | 31 +---- .../classes/registry/RegistryClassInfo.java | 105 ++++++++++++-- .../java/ch/njol/skript/entity/CatData.java | 13 +- .../ch/njol/skript/entity/ChickenData.java | 57 +++----- .../java/ch/njol/skript/entity/CowData.java | 128 +++--------------- .../java/ch/njol/skript/entity/FrogData.java | 20 ++- .../java/ch/njol/skript/entity/PigData.java | 53 ++------ .../java/ch/njol/skript/entity/WolfData.java | 55 ++------ .../java/ch/njol/skript/util/PaperUtils.java | 33 +++-- .../damagesource/DamageSourceModule.java | 2 +- .../types/EnchantmentClassInfo.java | 3 +- .../entity/entitydata/ZombieNautilusData.java | 7 +- .../skript/bukkit/potion/PotionModule.java | 14 +- src/main/resources/plugin.yml | 2 +- 16 files changed, 225 insertions(+), 327 deletions(-) diff --git a/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java b/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java index 0b336b01b3a..cf73abf6bdd 100644 --- a/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java +++ b/src/main/java/ch/njol/skript/bukkitutil/BukkitUtils.java @@ -7,6 +7,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.Keyed; import org.bukkit.Registry; import org.bukkit.inventory.EquipmentSlot; @@ -89,8 +90,25 @@ public static EquipmentSlot getEquipmentSlotFromIndex(int slotIndex) { String codeName, String languageNode ) { + // validate class if (!Skript.classExists(classPath)) return null; + Class registryClass; + try { + //noinspection unchecked + registryClass = (Class) Class.forName(classPath); + } catch (ClassNotFoundException e) { + Skript.debug("Could not retrieve the class with the path: '" + classPath + "'."); + throw new RuntimeException(e); + } + + // first, try better RegistryKey + if (PaperUtils.registryExists(registryName)) { + RegistryKey registryKey = PaperUtils.getBukkitRegistryKey(registryName); + return new RegistryClassInfo<>(registryClass, registryKey, codeName, languageNode); + } + + // otherwise, standard Registry Registry registry = null; if (BukkitUtils.registryExists(registryName)) { try { @@ -99,18 +117,8 @@ public static EquipmentSlot getEquipmentSlotFromIndex(int slotIndex) { } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } - } else if (PaperUtils.registryExists(registryName)) { - registry = PaperUtils.getBukkitRegistry(registryName); } if (registry != null) { - Class registryClass; - try { - //noinspection unchecked - registryClass = (Class) Class.forName(classPath); - } catch (ClassNotFoundException e) { - Skript.debug("Could not retrieve the class with the path: '" + classPath + "'."); - throw new RuntimeException(e); - } return new RegistryClassInfo<>(registryClass, registry, codeName, languageNode); } Skript.debug("There were no registries found for '" + registryName + "'."); diff --git a/src/main/java/ch/njol/skript/classes/ClassInfo.java b/src/main/java/ch/njol/skript/classes/ClassInfo.java index 2852a33ac26..ee47a37fea8 100644 --- a/src/main/java/ch/njol/skript/classes/ClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/ClassInfo.java @@ -133,7 +133,6 @@ public ClassInfo user(final String... userInputPatterns) throws PatternSyntax * @see SimpleLiteral */ public ClassInfo defaultExpression(final DefaultExpression defaultExpression) { - assert this.defaultExpression == null; if (!defaultExpression.isDefault()) throw new IllegalArgumentException("defaultExpression.isDefault() must return true for the default expression of a class"); this.defaultExpression = defaultExpression; 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 3ad45cd73b2..7089b80c64a 100644 --- a/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java +++ b/src/main/java/ch/njol/skript/classes/data/BukkitClasses.java @@ -11,6 +11,7 @@ import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.BlockUtils; import ch.njol.yggdrasil.Fields; +import io.papermc.paper.registry.RegistryKey; import io.papermc.paper.world.MoonPhase; import net.kyori.adventure.text.Component; import org.bukkit.*; @@ -326,7 +327,7 @@ public String toVariableNameString(InventoryHolder holder) { .since("2.0") .changer(DefaultChangers.itemChanger)); - Classes.registerClass(new RegistryClassInfo<>(Biome.class, Registry.BIOME, "biome", "biomes") + Classes.registerClass(new RegistryClassInfo<>(Biome.class, RegistryKey.BIOME, "biome", "biomes") .user("biomes?") .name("Biome") .description("All possible biomes Minecraft uses to generate a world.", @@ -600,7 +601,7 @@ public String[] getPatterns() { ExpressionPropertyHandler.of(GameRule::getName, String.class) )); - Classes.registerClass(new RegistryClassInfo<>(Attribute.class, Registry.ATTRIBUTE, "attributetype", "attribute types") + Classes.registerClass(new RegistryClassInfo<>(Attribute.class, RegistryKey.ATTRIBUTE, "attributetype", "attribute types") .user("attribute ?types?") .name("Attribute Type") .description("Represents the type of an attribute. Note that this type does not contain any numerical values." + @@ -659,14 +660,14 @@ public String[] getPatterns() { .description("Represents a change reason of an experience cooldown change event.") .since("2.10")); - Classes.registerClass(new RegistryClassInfo<>(Villager.Type.class, Registry.VILLAGER_TYPE, "villagertype", "villager types") + Classes.registerClass(new RegistryClassInfo<>(Villager.Type.class, RegistryKey.VILLAGER_TYPE, "villagertype", "villager types") .user("villager ?types?") .name("Villager Type") .description("Represents the different types of villagers. These are usually the biomes a villager can be from.") .after("biome") .since("2.10")); - Classes.registerClass(new RegistryClassInfo<>(Villager.Profession.class, Registry.VILLAGER_PROFESSION, "villagerprofession", "villager professions") + Classes.registerClass(new RegistryClassInfo<>(Villager.Profession.class, RegistryKey.VILLAGER_PROFESSION, "villagerprofession", "villager professions") .user("villager ?professions?") .name("Villager Profession") .description("Represents the different professions of villagers.") @@ -705,27 +706,7 @@ public String toVariableNameString(EntitySnapshot snapshot) { .description("Represents a banner pattern.") .since("2.10")); - ClassInfo patternTypeInfo; - Registry patternRegistry = Bukkit.getRegistry(PatternType.class); - if (patternRegistry != null) { - patternTypeInfo = new RegistryClassInfo<>(PatternType.class, patternRegistry, "bannerpatterntype", "banner pattern types"); - } else { - try { - Class patternClass = Class.forName("org.bukkit.block.banner.PatternType"); - if (patternClass.isEnum()) { - //noinspection unchecked,rawtypes - Class enumClass = (Class) patternClass; - //noinspection rawtypes,unchecked - patternTypeInfo = new EnumClassInfo<>(enumClass, "bannerpatterntype", "banner pattern types"); - } else { - throw new IllegalStateException("PatternType is neither an enum nor a valid registry."); - } - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - Classes.registerClass(patternTypeInfo + Classes.registerClass(new RegistryClassInfo<>(PatternType.class, RegistryKey.BANNER_PATTERN, "bannerpatterntype", "banner pattern types") .user("banner ?pattern ?types?") .name("Banner Pattern Type") .description("Represents the various banner patterns that can be applied to a banner.") diff --git a/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java b/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java index ee197f03600..c93bfb5856d 100644 --- a/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java +++ b/src/main/java/ch/njol/skript/classes/registry/RegistryClassInfo.java @@ -3,10 +3,16 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.DefaultExpression; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.Keyed; import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Iterator; import java.util.function.Consumer; +import java.util.function.Supplier; /** * This class can be used for easily creating ClassInfos for {@link Registry}s. @@ -16,12 +22,87 @@ */ public class RegistryClassInfo extends ClassInfo { + private final @Nullable RegistryKey registryKey; + + /** + * @param registryClass The registry class + * @param registryKey The registry key + * @param codeName The name used in patterns + * @param languageNode The language node of the type + */ + public RegistryClassInfo(Class registryClass, RegistryKey registryKey, String codeName, String languageNode) { + this(registryClass, registryKey, RegistryAccess.registryAccess().getRegistry(registryKey), codeName, + languageNode, null, null); + } + + /** + * @param registryClass The registry class + * @param registryKey The registry key + * @param codeName The name used in patterns + * @param languageNode The language node of the type + * @param parseCallback A consumer to run on a successful parse. + */ + public RegistryClassInfo(Class registryClass, RegistryKey registryKey, String codeName, String languageNode, + Consumer parseCallback) { + this(registryClass, registryKey, RegistryAccess.registryAccess().getRegistry(registryKey), codeName, + languageNode, null, parseCallback); + } + + private RegistryClassInfo(Class registryClass, @Nullable RegistryKey registryKey, Registry registry, + String codeName, String languageNode, @Nullable DefaultExpression defaultExpression, + @Nullable Consumer parseCallback) { + super(registryClass, codeName); + + if (defaultExpression == null) { + defaultExpression = new EventValueExpression<>(registryClass); + } + if (parseCallback == null) { + parseCallback = ignored -> { }; + } + + this.registryKey = registryKey; + RegistryParser registryParser = new RegistryParser<>(registry, languageNode, parseCallback); + usage(registryParser.getCombinedPatterns()) + .supplier(registry::iterator) + .serializer(new RegistrySerializer<>(registry)) + .defaultExpression(defaultExpression) + .parser(registryParser); + } + + public @Nullable RegistryKey registryKey() { + return registryKey; + } + + @Override + public @NotNull RegistryParser getParser() { + //noinspection ConstantConditions, unchecked + return (RegistryParser) super.getParser(); + } + + @Override + public @NotNull RegistrySerializer getSerializer() { + //noinspection ConstantConditions, unchecked + return (RegistrySerializer) super.getSerializer(); + } + + @Override + public @NotNull Supplier> getSupplier() { + //noinspection ConstantConditions + return super.getSupplier(); + } + + /* + * Legacy Constructors + */ + /** * @param registryClass The registry class * @param registry The registry * @param codeName The name used in patterns * @param languageNode The language node of the type + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String)}. */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode) { this(registryClass, registry, codeName, languageNode, new EventValueExpression<>(registryClass)); } @@ -32,7 +113,9 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @param codeName The name used in patterns * @param languageNode The language node of the type * @param parseCallback A consumer to run on a successful parse. + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String, Consumer)}. */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, Consumer parseCallback) { this(registryClass, registry, codeName, languageNode, new EventValueExpression<>(registryClass), parseCallback); } @@ -43,8 +126,11 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @param codeName The name used in patterns * @param languageNode The language node of the type * @param defaultExpression The default expression of the type + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String)} with {@link #defaultExpression(DefaultExpression)}. */ - public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression) { + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, + DefaultExpression defaultExpression) { this(registryClass, registry, codeName, languageNode, defaultExpression, ignored -> {}); } @@ -55,15 +141,13 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @param languageNode The language node of the type * @param defaultExpression The default expression of the type * @param parseCallback A consumer to run on a successful parse. + * @deprecated Use {@link #RegistryClassInfo(Class, RegistryKey, String, String, Consumer)} + * with {@link #defaultExpression(DefaultExpression)}. */ - public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression, Consumer parseCallback) { - super(registryClass, codeName); - RegistryParser registryParser = new RegistryParser<>(registry, languageNode, parseCallback); - usage(registryParser.getCombinedPatterns()) - .supplier(registry::iterator) - .serializer(new RegistrySerializer<>(registry)) - .defaultExpression(defaultExpression) - .parser(registryParser); + @Deprecated(since = "INSERT VERSION", forRemoval = true) + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, + DefaultExpression defaultExpression, Consumer parseCallback) { + this(registryClass, null, registry, codeName, languageNode, defaultExpression, parseCallback); } /** @@ -89,7 +173,8 @@ public RegistryClassInfo(Class registryClass, Registry registry, String co * @deprecated {@code registerComparator} is no longer necessary. */ @Deprecated(since = "2.16", forRemoval = true) - public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, DefaultExpression defaultExpression, boolean registerComparator) { + public RegistryClassInfo(Class registryClass, Registry registry, String codeName, String languageNode, + DefaultExpression defaultExpression, boolean registerComparator) { this(registryClass, registry, codeName, languageNode, defaultExpression); } diff --git a/src/main/java/ch/njol/skript/entity/CatData.java b/src/main/java/ch/njol/skript/entity/CatData.java index a49ad39205f..e387bce9d0c 100644 --- a/src/main/java/ch/njol/skript/entity/CatData.java +++ b/src/main/java/ch/njol/skript/entity/CatData.java @@ -1,15 +1,12 @@ package ch.njol.skript.entity; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.classes.EnumClassInfo; import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; -import org.bukkit.Registry; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Cat; import org.bukkit.entity.Cat.Type; import org.jetbrains.annotations.NotNull; @@ -22,13 +19,7 @@ public class CatData extends EntityData { private static final Type[] TYPES; static { - ClassInfo catTypeClassInfo; - if (BukkitUtils.registryExists("CAT_VARIANT")) { - catTypeClassInfo = new RegistryClassInfo<>(Cat.Type.class, Registry.CAT_VARIANT, "cattype", "cat types"); - } else { - //noinspection unchecked, rawtypes - it is an enum on other versions - catTypeClassInfo = new EnumClassInfo<>((Class) Cat.Type.class, "cattype", "cat types"); - } + var catTypeClassInfo = new RegistryClassInfo<>(Cat.Type.class, RegistryKey.CAT_VARIANT, "cattype", "cat types"); Classes.registerClass(catTypeClassInfo .user("cat ?(type|race)s?") .name("Cat Type") diff --git a/src/main/java/ch/njol/skript/entity/ChickenData.java b/src/main/java/ch/njol/skript/entity/ChickenData.java index c4e414e76ef..46176e0697f 100644 --- a/src/main/java/ch/njol/skript/entity/ChickenData.java +++ b/src/main/java/ch/njol/skript/entity/ChickenData.java @@ -1,13 +1,12 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Chicken; import org.bukkit.entity.Chicken.Variant; import org.jetbrains.annotations.NotNull; @@ -17,21 +16,11 @@ public class ChickenData extends EntityData { - private static final boolean VARIANTS_ENABLED; - private static final Object[] VARIANTS; + private static final Variant[] VARIANTS; static { - ClassInfo chickenVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Chicken$Variant", - "CHICKEN_VARIANT", - "chickenvariant", - "chicken variants" - ); - if (chickenVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Chicken.Variant' (1.21.4-) - chickenVariantClassInfo = new ClassInfo<>(ChickenVariantDummy.class, "chickenvariant"); - } - Classes.registerClass(chickenVariantClassInfo + var chickenVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.CHICKEN_VARIANT, "chickenvariant", "chicken variants"); + Classes.registerClass(chickenVariantInfo .user("chicken ?variants?") .name("Chicken Variant") .description("Represents the variant of a chicken entity.", @@ -40,38 +29,31 @@ public class ChickenData extends EntityData { .requiredPlugins("Minecraft 1.21.5+") .documentationId("ChickenVariant") ); + VARIANTS = Iterators.toArray(chickenVariantInfo.getSupplier().get(), Variant.class); register(ChickenData.class, "chicken", Chicken.class, "chicken"); - if (Skript.classExists("org.bukkit.entity.Chicken$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Chicken.Variant.class).getSupplier().get(), Chicken.Variant.class); - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - } } - private @Nullable Object variant = null; + private @Nullable Variant variant = null; public ChickenData() {} - // TODO: When safe, 'variant' should have the type changed to 'Chicken.Variant' when 1.21.6 is minimum supported version - public ChickenData(@Nullable Object variant) { + public ChickenData(@Nullable Variant variant) { this.variant = variant; } @Override protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { - if (VARIANTS_ENABLED && exprs[0] != null) { + if (exprs[0] != null) { //noinspection unchecked - variant = ((Literal) exprs[0]).getSingle(); + variant = ((Literal) exprs[0]).getSingle(); } return true; } @Override protected boolean init(@Nullable Class entityClass, @Nullable Chicken chicken) { - if (chicken != null && VARIANTS_ENABLED) { + if (chicken != null) { variant = chicken.getVariant(); } return true; @@ -79,13 +61,11 @@ protected boolean init(@Nullable Class entityClass, @Nullable @Override public void set(Chicken chicken) { - if (VARIANTS_ENABLED) { - Variant variant = (Variant) this.variant; - if (variant == null) - variant = (Variant) CollectionUtils.getRandom(VARIANTS); - assert variant != null; - chicken.setVariant(variant); - } + Variant variant = this.variant; + if (variant == null) + variant = CollectionUtils.getRandom(VARIANTS); + assert variant != null; + chicken.setVariant(variant); } @Override @@ -122,9 +102,4 @@ public boolean isSupertypeOf(EntityData entityData) { return dataMatch(variant, other.variant); } - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have `Chicken.Variant` - */ - public static class ChickenVariantDummy {} - } diff --git a/src/main/java/ch/njol/skript/entity/CowData.java b/src/main/java/ch/njol/skript/entity/CowData.java index 242a791b064..fa05e6fcb5f 100644 --- a/src/main/java/ch/njol/skript/entity/CowData.java +++ b/src/main/java/ch/njol/skript/entity/CowData.java @@ -1,43 +1,26 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Cow; import org.bukkit.entity.Cow.Variant; -import org.bukkit.entity.Entity; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Objects; public class CowData extends EntityData { - private static final boolean VARIANTS_ENABLED; private static final Object[] VARIANTS; - private static final Class COW_CLASS; - private static final @Nullable Method getVariantMethod; - private static final @Nullable Method setVariantMethod; static { - ClassInfo cowVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Cow$Variant", - "COW_VARIANT", - "cowvariant", - "cow variants" - ); - if (cowVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Cow.Variant' (1.21.4-) - cowVariantClassInfo = new ClassInfo<>(CowVariantDummy.class, "cowvariant"); - } - Classes.registerClass(cowVariantClassInfo + var cowVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.COW_VARIANT, "cowvariant", "cow variants"); + Classes.registerClass(cowVariantInfo .user("cow ?variants?") .name("Cow Variant") .description("Represents the variant of a cow entity.", @@ -46,78 +29,53 @@ public class CowData extends EntityData { .requiredPlugins("Minecraft 1.21.5+") .documentationId("CowVariant") ); + VARIANTS = Iterators.toArray(cowVariantInfo.getSupplier().get(), Variant.class); - Class cowClass = null; - - try { - //noinspection unchecked - cowClass = (Class) Class.forName("org.bukkit.entity.Cow"); - } catch (Exception ignored) {} - - COW_CLASS = cowClass; - register(CowData.class, "cow", COW_CLASS, 0, "cow"); - if (Skript.classExists("org.bukkit.entity.Cow$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Cow.Variant.class).getSupplier().get(), Cow.Variant.class); - try { - getVariantMethod = COW_CLASS.getDeclaredMethod("getVariant"); - setVariantMethod = COW_CLASS.getDeclaredMethod("setVariant", Cow.Variant.class); - } catch (Exception e) { - throw new RuntimeException("Could not retrieve get/set variant methods for Cow.", e); - } - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - getVariantMethod = null; - setVariantMethod = null; - } + register(CowData.class, "cow", Cow.class, 0, "cow"); } - private @Nullable Object variant = null; + private @Nullable Variant variant = null; public CowData() {} - // TODO: When the api-version is 1.21.5, 'variant' should have the type changed to 'Cow.Variant' and reflection can be removed - public CowData(@Nullable Object variant) { + public CowData(@Nullable Variant variant) { this.variant = variant; } @Override protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { - if (VARIANTS_ENABLED && exprs[0] != null) { + if (exprs[0] != null) { //noinspection unchecked - variant = ((Literal) exprs[0]).getSingle(); + variant = ((Literal) exprs[0]).getSingle(); } return true; } @Override protected boolean init(@Nullable Class entityClass, @Nullable Cow cow) { - if (cow != null && VARIANTS_ENABLED) { - variant = getVariant(cow); + if (cow != null) { + variant = cow.getVariant(); } return true; } @Override public void set(Cow cow) { - if (VARIANTS_ENABLED) { - Variant variant = (Variant) this.variant; - if (variant == null) - variant = (Variant) CollectionUtils.getRandom(VARIANTS); - assert variant != null; - setVariant(cow, variant); - } + Variant variant = this.variant; + if (variant == null) + variant = (Variant) CollectionUtils.getRandom(VARIANTS); + assert variant != null; + cow.setVariant(variant); } @Override protected boolean match(Cow cow) { - return variant == null || getVariant(cow) == variant; + return variant == null || cow.getVariant() == variant; } @Override public Class getType() { - return COW_CLASS; + return Cow.class; } @Override @@ -144,52 +102,4 @@ public boolean isSupertypeOf(EntityData entityData) { return dataMatch(variant, other.variant); } - /** - * Due to the addition of 'AbstractCow' and 'api-version' being '1.19' - * This helper method is required in order to set the {@link #variant} of the {@link Cow} - * @param cow The {@link Cow} to set the variant - */ - public void setVariant(Cow cow) { - setVariant(cow, variant); - } - - /** - * Due to the addition of 'AbstractCow' and 'api-version' being '1.19' - * This helper method is required in order to set the {@code object} of the {@link Cow} - * @param cow The {@link Cow} to set the variant - * @param object The 'Cow.Variant' - */ - public void setVariant(Cow cow, Object object) { - if (!VARIANTS_ENABLED || setVariantMethod == null) - return; - Entity entity = COW_CLASS.cast(cow); - try { - setVariantMethod.invoke(entity, (Cow.Variant) object); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * Due to the addition of 'AbstractCow' and 'api-version' being '1.19' - * This helper method is required in order to get the 'Cow.Variant' of the {@link Cow} - * @param cow The {@link Cow} to get the variant - * @return The 'Cow.Variant' - */ - public @Nullable Object getVariant(Cow cow) { - if (!VARIANTS_ENABLED || getVariantMethod == null) - return null; - Entity entity = COW_CLASS.cast(cow); - try { - return getVariantMethod.invoke(entity); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have 'Cow.Variant' - */ - public static class CowVariantDummy {} - } diff --git a/src/main/java/ch/njol/skript/entity/FrogData.java b/src/main/java/ch/njol/skript/entity/FrogData.java index e5021428cd0..f775c2380a9 100644 --- a/src/main/java/ch/njol/skript/entity/FrogData.java +++ b/src/main/java/ch/njol/skript/entity/FrogData.java @@ -1,12 +1,13 @@ package ch.njol.skript.entity; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Patterns; import ch.njol.util.coll.CollectionUtils; +import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Frog; import org.bukkit.entity.Frog.Variant; import org.jetbrains.annotations.NotNull; @@ -26,16 +27,8 @@ public class FrogData extends EntityData { private static final Variant[] VARIANTS; static { - EntityData.register(FrogData.class, "frog", Frog.class, 0, PATTERNS.getPatterns()); - VARIANTS = new Variant[]{Variant.TEMPERATE, Variant.WARM, Variant.COLD}; - ClassInfo frogVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Frog$Variant", - "FROG_VARIANT", - "frogvariant", - "frog variants" - ); - assert frogVariantClassInfo != null; - Classes.registerClass(frogVariantClassInfo + var frogVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.FROG_VARIANT, "frogvariant", "frog variants"); + Classes.registerClass(frogVariantInfo .user("frog ?variants?") .name("Frog Variant") .description("Represents the variant of a frog entity.", @@ -43,6 +36,9 @@ public class FrogData extends EntityData { .since("2.13") .documentationId("FrogVariant") ); + VARIANTS = Iterators.toArray(frogVariantInfo.getSupplier().get(), Variant.class); + + EntityData.register(FrogData.class, "frog", Frog.class, 0, PATTERNS.getPatterns()); } private @Nullable Variant variant = null; diff --git a/src/main/java/ch/njol/skript/entity/PigData.java b/src/main/java/ch/njol/skript/entity/PigData.java index 6fc309307c8..0be397c9173 100644 --- a/src/main/java/ch/njol/skript/entity/PigData.java +++ b/src/main/java/ch/njol/skript/entity/PigData.java @@ -1,8 +1,6 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; @@ -10,7 +8,9 @@ import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.entity.Pig; +import org.bukkit.entity.Pig.Variant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -18,8 +18,7 @@ public class PigData extends EntityData { - private static final boolean VARIANTS_ENABLED; - private static final Object[] VARIANTS; + private static final Variant[] VARIANTS; private static final Patterns PATTERNS = new Patterns<>(new Object[][]{ {"pig", Kleenean.UNKNOWN}, {"saddled pig", Kleenean.TRUE}, @@ -27,17 +26,8 @@ public class PigData extends EntityData { }); static { - ClassInfo pigVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Pig$Variant", - "PIG_VARIANT", - "pigvariant", - "pig variants" - ); - if (pigVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Pig.Variant' (1.21.4-) - pigVariantClassInfo = new ClassInfo<>(PigVariantDummy.class, "pigvariant"); - } - Classes.registerClass(pigVariantClassInfo + var pigVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.PIG_VARIANT, "pigvariant", "pig variants"); + Classes.registerClass(pigVariantInfo .user("pig ?variants?") .name("Pig Variant") .description("Represents the variant of a pig entity.", @@ -45,24 +35,17 @@ public class PigData extends EntityData { .since("2.12") .requiredPlugins("Minecraft 1.21.5+") .documentationId("PigVariant")); + VARIANTS = Iterators.toArray(pigVariantInfo.getSupplier().get(), Pig.Variant.class); register(PigData.class, "pig", Pig.class, 0, PATTERNS.getPatterns()); - if (Skript.classExists("org.bukkit.entity.Pig$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Pig.Variant.class).getSupplier().get(), Pig.Variant.class); - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - } } private Kleenean saddled = Kleenean.UNKNOWN; - private @Nullable Object variant = null; + private @Nullable Variant variant; public PigData() {} - // TODO: When safe, 'variant' should have the type changed to 'Pig.Variant' when 1.21.5 is minimum supported version - public PigData(@Nullable Kleenean saddled, @Nullable Object variant) { + public PigData(@Nullable Kleenean saddled, @Nullable Variant variant) { this.saddled = saddled != null ? saddled : Kleenean.UNKNOWN; this.variant = variant; super.codeNameIndex = PATTERNS.getMatchedPattern(this.saddled, 0).orElse(0); @@ -71,7 +54,7 @@ public PigData(@Nullable Kleenean saddled, @Nullable Object variant) { @Override protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPattern, ParseResult parseResult) { saddled = PATTERNS.getInfo(matchedCodeName); - if (VARIANTS_ENABLED && exprs[0] != null) { + if (exprs[0] != null) { //noinspection unchecked variant = ((Literal) exprs[0]).getSingle(); } @@ -83,8 +66,7 @@ protected boolean init(@Nullable Class entityClass, @Nullable Pig if (pig != null) { saddled = Kleenean.get(pig.hasSaddle()); super.codeNameIndex = PATTERNS.getMatchedPattern(saddled, 0).orElse(0); - if (VARIANTS_ENABLED) - variant = pig.getVariant(); + variant = pig.getVariant(); } return true; } @@ -92,11 +74,9 @@ protected boolean init(@Nullable Class entityClass, @Nullable Pig @Override public void set(Pig pig) { pig.setSaddle(saddled.isTrue()); - if (VARIANTS_ENABLED) { - Object finalVariant = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); - assert finalVariant != null; - pig.setVariant((Pig.Variant) finalVariant); - } + Variant finalVariant = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); + assert finalVariant != null; + pig.setVariant(finalVariant); } @Override @@ -139,9 +119,4 @@ public boolean isSupertypeOf(EntityData entityData) { return variant == null || variant == other.variant; } - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have `Pig.Variant` - */ - public static class PigVariantDummy {} - } diff --git a/src/main/java/ch/njol/skript/entity/WolfData.java b/src/main/java/ch/njol/skript/entity/WolfData.java index a1f0613212c..3530ac29bec 100644 --- a/src/main/java/ch/njol/skript/entity/WolfData.java +++ b/src/main/java/ch/njol/skript/entity/WolfData.java @@ -1,8 +1,6 @@ package ch.njol.skript.entity; -import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; -import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; import ch.njol.skript.registrations.Classes; @@ -11,8 +9,10 @@ import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; import com.google.common.collect.Iterators; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.DyeColor; import org.bukkit.entity.Wolf; +import org.bukkit.entity.Wolf.Variant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,21 +30,11 @@ public record WolfStates(Kleenean angry, Kleenean tamed) {} {"peaceful wolf", new WolfStates(Kleenean.FALSE, Kleenean.UNKNOWN)} }); - private static final boolean VARIANTS_ENABLED; - private static final Object[] VARIANTS; + private static final Variant[] VARIANTS; static { - ClassInfo wolfVariantClassInfo = BukkitUtils.getRegistryClassInfo( - "org.bukkit.entity.Wolf$Variant", - "WOLF_VARIANT", - "wolfvariant", - "wolf variants" - ); - if (wolfVariantClassInfo == null) { - // Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Wolf.Variant' (1.20.4-) - wolfVariantClassInfo = new ClassInfo<>(WolfVariantDummy.class, "wolfvariant"); - } - Classes.registerClass(wolfVariantClassInfo + var wolfVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.WOLF_VARIANT, "wolfvariant", "wolf variants"); + Classes.registerClass(wolfVariantInfo .user("wolf ?variants?") .name("Wolf Variant") .description("Represents the variant of a wolf entity.", @@ -52,18 +42,12 @@ public record WolfStates(Kleenean angry, Kleenean tamed) {} .since("2.10") .requiredPlugins("Minecraft 1.21+") .documentationId("WolfVariant")); + VARIANTS = Iterators.toArray(wolfVariantInfo.getSupplier().get(), Variant.class); EntityData.register(WolfData.class, "wolf", Wolf.class, 0, PATTERNS.getPatterns()); - if (Skript.classExists("org.bukkit.entity.Wolf$Variant")) { - VARIANTS_ENABLED = true; - VARIANTS = Iterators.toArray(Classes.getExactClassInfo(Wolf.Variant.class).getSupplier().get(), Wolf.Variant.class); - } else { - VARIANTS_ENABLED = false; - VARIANTS = null; - } } - private @Nullable Object variant = null; + private @Nullable Variant variant = null; private @Nullable DyeColor collarColor = null; private Kleenean isAngry = Kleenean.UNKNOWN; private Kleenean isTamed = Kleenean.UNKNOWN; @@ -94,9 +78,9 @@ protected boolean init(Literal[] exprs, int matchedCodeName, int matchedPatte assert state != null; isAngry = state.angry; isTamed = state.tamed; - if (exprs[0] != null && VARIANTS_ENABLED) { + if (exprs[0] != null) { //noinspection unchecked - variant = ((Literal) exprs[0]).getSingle(); + variant = ((Literal) exprs[0]).getSingle(); } if (exprs[1] != null) { //noinspection unchecked @@ -111,8 +95,7 @@ protected boolean init(@Nullable Class entityClass, @Nullable Wo isAngry = Kleenean.get(wolf.isAngry()); isTamed = Kleenean.get(wolf.isTamed()); collarColor = wolf.getCollarColor(); - if (VARIANTS_ENABLED) - variant = wolf.getVariant(); + variant = wolf.getVariant(); super.codeNameIndex = PATTERNS.getMatchedPattern(new WolfStates(isAngry, isTamed), 0).orElse(0); } return true; @@ -124,11 +107,9 @@ public void set(Wolf wolf) { wolf.setTamed(isTamed.isTrue()); if (collarColor != null) wolf.setCollarColor(collarColor); - if (VARIANTS_ENABLED) { - Object variantSet = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); - assert variantSet != null; - wolf.setVariant((Wolf.Variant) variantSet); - } + Variant variantSet = variant != null ? variant : CollectionUtils.getRandom(VARIANTS); + assert variantSet != null; + wolf.setVariant(variantSet); } @Override @@ -158,8 +139,7 @@ protected int hashCode_i() { result = prime * result + isAngry.hashCode(); result = prime * result + isTamed.hashCode(); result = prime * result + Objects.hashCode(collarColor); - if (VARIANTS_ENABLED) - result = prime * result + Objects.hashCode(variant); + result = prime * result + Objects.hashCode(variant); return result; } @@ -189,9 +169,4 @@ public boolean isSupertypeOf(EntityData entityData) { return dataMatch(variant, other.variant); } - /** - * A dummy/placeholder class to ensure working operation on MC versions that do not have `Wolf.Variant` - */ - public static class WolfVariantDummy {}; - } diff --git a/src/main/java/ch/njol/skript/util/PaperUtils.java b/src/main/java/ch/njol/skript/util/PaperUtils.java index 6f312a9c50e..2106fed72d0 100644 --- a/src/main/java/ch/njol/skript/util/PaperUtils.java +++ b/src/main/java/ch/njol/skript/util/PaperUtils.java @@ -13,9 +13,9 @@ public class PaperUtils { private static final boolean REGISTRY_KEY_EXISTS = Skript.classExists("io.papermc.paper.registry.RegistryKey"); /** - * Check if a registry exists within {@link RegistryKey} - * @param registry Registry to check for (Fully qualified name of registry) - * @return True if registry exists else false + * Check if a registry exists within {@link RegistryKey}. + * @param registry Registry to check for. + * @return True if registry exists else false. */ public static boolean registryExists(String registry) { return REGISTRY_ACCESS_EXISTS @@ -24,21 +24,34 @@ public static boolean registryExists(String registry) { } /** - * Gets the Bukkit {@link Registry} from Paper's {@link RegistryKey}. - * @param registry Registry to get (Fully qualified name of registry). + * Gets a Paper {@link RegistryKey}. + * @param registry Registry key to get. * @return The Bukkit {@link Registry} if registry exists else {@code null}. */ - public static @Nullable Registry getBukkitRegistry(String registry) { + public static @Nullable RegistryKey getBukkitRegistryKey(String registry) { if (!registryExists(registry)) return null; - RegistryKey registryKey; + RegistryKey registryKey; try { - registryKey = (RegistryKey) RegistryKey.class.getField(registry).get(null); + //noinspection unchecked + registryKey = (RegistryKey) RegistryKey.class.getField(registry).get(null); } catch (NoSuchFieldException | IllegalAccessException ignored) { return null; } - //noinspection unchecked - return (Registry) RegistryAccess.registryAccess().getRegistry(registryKey); + return registryKey; + } + + /** + * Gets the Bukkit {@link Registry} from Paper's {@link RegistryKey}. + * @param registry Registry to get. + * @return The Bukkit {@link Registry} if registry exists else {@code null}. + */ + public static @Nullable Registry getBukkitRegistry(String registry) { + RegistryKey registryKey = getBukkitRegistryKey(registry); + if (registryKey == null) { + return null; + } + return RegistryAccess.registryAccess().getRegistry(registryKey); } } diff --git a/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java b/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java index f3ea90c5eaf..aaaeaf03f4c 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/damagesource/DamageSourceModule.java @@ -43,7 +43,7 @@ protected void initSelf(SkriptAddon addon) { ); Classes.registerClass( - new RegistryClassInfo<>(DamageType.class, RegistryAccess.registryAccess().getRegistry(RegistryKey.DAMAGE_TYPE), + new RegistryClassInfo<>(DamageType.class, RegistryKey.DAMAGE_TYPE, "damagetype", "damage types") .user("damage ?types?") .name("Damage Type") diff --git a/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java b/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java index c5b83769200..57d8eb83f71 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java +++ b/src/main/java/org/skriptlang/skript/bukkit/enchantments/types/EnchantmentClassInfo.java @@ -1,7 +1,6 @@ package org.skriptlang.skript.bukkit.enchantments.types; import ch.njol.skript.classes.registry.RegistryClassInfo; -import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import org.bukkit.enchantments.Enchantment; import org.jetbrains.annotations.ApiStatus; @@ -10,7 +9,7 @@ public class EnchantmentClassInfo extends RegistryClassInfo { public EnchantmentClassInfo() { - super(Enchantment.class, RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT), + super(Enchantment.class, RegistryKey.ENCHANTMENT, "enchantment", "enchantments"); user("enchantments?") diff --git a/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java b/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java index 8084009f35b..ed6eadc59e0 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java +++ b/src/main/java/org/skriptlang/skript/bukkit/entity/entitydata/ZombieNautilusData.java @@ -8,6 +8,7 @@ import ch.njol.skript.variables.Variables; import ch.njol.util.Kleenean; import ch.njol.util.coll.CollectionUtils; +import com.google.common.collect.Iterators; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import org.bukkit.Registry; @@ -26,14 +27,14 @@ public static void register() { EntityData.register(ZombieNautilusData.class, "zombie nautilus", ZombieNautilus.class, 0, "zombie nautilus"); Variables.yggdrasil.registerSingleClass(Variant.class, "ZombieNautilus.Variant"); - Registry<@NotNull Variant> variantRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.ZOMBIE_NAUTILUS_VARIANT); - VARIANTS = variantRegistry.stream().toArray(Variant[]::new); - Classes.registerClass(new RegistryClassInfo<>(Variant.class, variantRegistry, "zombienautilusvariant", "zombie nautilus variants") + var zombieNautilusVariantInfo = new RegistryClassInfo<>(Variant.class, RegistryKey.ZOMBIE_NAUTILUS_VARIANT, "zombienautilusvariant", "zombie nautilus variants"); + Classes.registerClass(zombieNautilusVariantInfo .user("zombie ?nautilus ?variants?") .name("Zombie Nautilus Variant") .description("Represents the variant of a zombie nautilus.") .since("2.14") .documentationId("ZombieNautilusVariant")); + VARIANTS = Iterators.toArray(zombieNautilusVariantInfo.getSupplier().get(), Variant.class); } private Kleenean isTamed = Kleenean.UNKNOWN; diff --git a/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java b/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java index da3d82029cc..813f8d8862b 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/potion/PotionModule.java @@ -1,19 +1,17 @@ package org.skriptlang.skript.bukkit.potion; import ch.njol.skript.Skript; -import ch.njol.skript.bukkitutil.BukkitUtils; import ch.njol.skript.classes.*; import ch.njol.skript.classes.registry.RegistryClassInfo; import ch.njol.skript.expressions.base.EventValueExpression; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.registrations.Classes; import ch.njol.yggdrasil.Fields; -import org.bukkit.Registry; +import io.papermc.paper.registry.RegistryKey; import org.bukkit.event.entity.EntityPotionEffectEvent; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectTypeCategory; -import org.jetbrains.annotations.NotNull; import org.skriptlang.skript.addon.AddonModule; import org.skriptlang.skript.addon.HierarchicalAddonModule; import org.skriptlang.skript.addon.SkriptAddon; @@ -129,15 +127,7 @@ protected boolean canBeInstantiated() { } })); - Registry<@NotNull PotionEffectType> petRegistry; - if (BukkitUtils.registryExists("MOB_EFFECT")) { // Paper (1.21.4) - petRegistry = Registry.MOB_EFFECT; - } else if (BukkitUtils.registryExists("EFFECT")) { // Bukkit (1.20.3) - petRegistry = Registry.EFFECT; - } else { - throw new IllegalStateException("Potion effect registry does not exist"); - } - Classes.registerClass(new RegistryClassInfo<>(PotionEffectType.class, petRegistry, "potioneffecttype", "potion effect types") + Classes.registerClass(new RegistryClassInfo<>(PotionEffectType.class, RegistryKey.MOB_EFFECT, "potioneffecttype", "potion effect types") .user("potion ?effect ?types?") .name("Potion Effect Type") .description("A potion effect type, e.g. 'strength' or 'swiftness'.") diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fae9ec3e6fa..89543e79207 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -29,7 +29,7 @@ website: https://skriptlang.org main: ch.njol.skript.Skript version: @version@ -api-version: 1.21 +api-version: 1.21.5 commands: skript: From dd20ff25c305afadd4045fcda3ac3f5104fa7d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nova=20=E2=9C=A9?= <150085350+novystar@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:04:49 +0100 Subject: [PATCH 12/32] Add Item Model Support (#8779) --- .../skript/bukkit/item/ItemModule.java | 2 + .../bukkit/item/elements/ExprItemModel.java | 82 +++++++++++++++++++ .../item/elements/ExprItemWithModel.java | 80 ++++++++++++++++++ .../skript/tests/bukkit/item/ExprItemModel.sk | 46 +++++++++++ 4 files changed, 210 insertions(+) create mode 100644 src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java create mode 100644 src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java create mode 100644 src/test/skript/tests/bukkit/item/ExprItemModel.sk diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java b/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java index d3b5d311cda..759f7a4b236 100644 --- a/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java +++ b/src/main/java/org/skriptlang/skript/bukkit/item/ItemModule.java @@ -24,7 +24,9 @@ public Iterable children() { @Override public void loadSelf(SkriptAddon addon) { register(addon, + ExprItemModel::register, ExprItemWithLore::register, + ExprItemWithModel::register, ExprLore::register ); } diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java new file mode 100644 index 00000000000..45a69823aa6 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemModel.java @@ -0,0 +1,82 @@ +package org.skriptlang.skript.bukkit.item.elements; + +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.NamespacedUtils; +import ch.njol.skript.classes.Changer.ChangeMode; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Example; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +import ch.njol.util.coll.CollectionUtils; +import org.bukkit.NamespacedKey; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item Model") +@Description(""" + The item model of an item. \ + Accepts a Namespaced Key which takes the form of "namespace:key", e.g. "minecraft:dirt". \ + See this article for more detail. \ + """) +@Example("set the item model of player's held item to \"diamond\"") +@Example("set the item model of {_item} to \"minecraft:dirt\"") +@Since("INSERT VERSION") +public class ExprItemModel extends SimplePropertyExpression { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register( + SyntaxRegistry.EXPRESSION, + infoBuilder(ExprItemModel.class, String.class, "item model", "itemtypes", true) + .supplier(ExprItemModel::new) + .build() + ); + } + + @Override + public @Nullable String convert(ItemType from) { + NamespacedKey key = from.getItemMeta().getItemModel(); + if (key == null) + return null; + + return key.asString(); + } + + @Override + public Class @Nullable [] acceptChange(ChangeMode mode) { + return switch (mode) { + case SET, DELETE, RESET -> CollectionUtils.array(String.class); + default -> null; + }; + } + + @Override + public void change(Event event, Object @Nullable [] delta, ChangeMode mode) { + NamespacedKey key = null; + if (delta != null) { + key = NamespacedUtils.checkValidationAndSend((String) delta[0], this); + if (key == null) { + return; + } + } + + for (ItemType item : getExpr().getArray(event)) { + ItemMeta meta = item.getItemMeta(); + meta.setItemModel(key); + item.setItemMeta(meta); + } + } + + @Override + public Class getReturnType() { + return String.class; + } + + @Override + protected String getPropertyName() { + return "item model"; + } + +} diff --git a/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java new file mode 100644 index 00000000000..2e390b46d02 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/bukkit/item/elements/ExprItemWithModel.java @@ -0,0 +1,80 @@ +package org.skriptlang.skript.bukkit.item.elements; + +import ch.njol.skript.aliases.ItemType; +import ch.njol.skript.bukkitutil.NamespacedUtils; +import ch.njol.skript.doc.*; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser.ParseResult; +import ch.njol.util.Kleenean; +import org.bukkit.NamespacedKey; +import org.bukkit.event.Event; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.registration.DefaultSyntaxInfos; +import org.skriptlang.skript.registration.SyntaxRegistry; + +@Name("Item With Model") +@Description(""" + Returns a copy of an item with a custom item model. \ + Accepts a Namespaced Key which takes the form of "namespace:key", e.g. "minecraft:dirt". \ + See this article for more detail. \ + """) +@Example(""" + set {_item} to emerald with model "minecraft:diamond" named "fake diamond"' + give {_item} to player + """) +@Since("INSERT VERSION") +@Keywords("item model") +public class ExprItemWithModel extends PropertyExpression { + + public static void register(SyntaxRegistry syntaxRegistry) { + syntaxRegistry.register( + SyntaxRegistry.EXPRESSION, + DefaultSyntaxInfos.Expression.builder(ExprItemWithModel.class, ItemType.class) + .addPattern("%itemtype% with [the] [item] model %string%") + .supplier(ExprItemWithModel::new) + .build() + ); + } + + private Expression key; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) { + setExpr((Expression) exprs[0]); + key = (Expression) exprs[1]; + return true; + } + + @Override + protected ItemType[] get(Event event, ItemType[] source) { + String key = this.key.getSingle(event); + if (key == null) + return source; + + NamespacedKey namespacedKey = NamespacedUtils.checkValidationAndSend(key, this); + if (namespacedKey == null) + return source; + + return get(source, itemType -> { + itemType = itemType.clone(); + ItemMeta itemMeta = itemType.getItemMeta(); + itemMeta.setItemModel(namespacedKey); + itemType.setItemMeta(itemMeta); + return itemType; + }); + } + + @Override + public Class getReturnType() { + return ItemType.class; + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return getExpr().toString(event, debug) + " with item model " + key.toString(event, debug); + } + +} diff --git a/src/test/skript/tests/bukkit/item/ExprItemModel.sk b/src/test/skript/tests/bukkit/item/ExprItemModel.sk new file mode 100644 index 00000000000..5236e990e07 --- /dev/null +++ b/src/test/skript/tests/bukkit/item/ExprItemModel.sk @@ -0,0 +1,46 @@ +using error catching + +test "ExprItemModel": + set {_item} to diamond + + set item model of {_item} to "emerald" + assert item model of {_item} is "minecraft:emerald" with "Set item model of item" + + set {_inv} to chest inventory with 1 row named "test slots" + set slot 1 of {_inv} to diamond + + set item model of slot 1 of {_inv} to "emerald" + + assert item model of slot 1 of {_inv} is "minecraft:emerald" with "Set item model of slot from container" + + set {_item} to dirt with item model "diamond" + assert item model of {_item} is "minecraft:diamond" with "ItemType with item model" + + reset item model of {_item} + assert item model of {_item} is not set with "Reset item model of item" + + set item model of {_item} to "minecraft:redstone" + delete item model of {_item} + assert item model of {_item} is not set with "Delete item model of item" + + set {_string} to "test_string" + set item model of {_item} to {_string} + assert item model of {_item} is "minecraft:test_string" with "Set item model from variable string" + + set item model of {_item} to {_} + assert item model of {_item} is not set with "Set item model of item to null" + + set {_item} to diamond with model {_} + assert item model of {_item} is not set with "Item with null item model" + + set {_item} to emerald with model {_string} + assert item model of {_item} is "minecraft:test_string" with "Item with item model of variable string" + + set {_item} to diamond + catch runtime errors: + set item model of {_item} to "with spaces" + assert last caught runtime errors are set with "Runtime errors produced for invalid key for item model" + + catch runtime errors: + set {_item} to diamond with model "with spaces" + assert last caught runtime errors are set with "Runtime errors produced for invalid key for item with model" From c19e24f3b98c01e6817889171985028fa6dffb4e Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 11 Aug 2026 23:50:24 +0200 Subject: [PATCH 13/32] changes --- .../lang/function/FunctionRegistry.java | 49 ++++++++----------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 7384b0f9fd6..b3bbf1e9535 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -153,7 +153,7 @@ public void register(@NotNull Signature signature) { if (signature.hasModifier(Signature.Modifier.LOCAL)) { throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); } - register0(GLOBAL_NAMESPACE, signature); + register(GLOBAL_NAMESPACE, signature); } @Override @@ -163,10 +163,10 @@ public void register(@NotNull String namespace, @NotNull Signature signature) throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); } - register0(new NamespaceIdentifier(namespace), signature); + register(new NamespaceIdentifier(namespace), signature); } - private void register0(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { + private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), "Invalid signature name '%s'".formatted(signature.name())); @@ -201,9 +201,7 @@ private void register0(@NotNull NamespaceIdentifier namespace, @NotNull Signatur error.append("."); } - Skript.error(error.toString()); - - return; + throw new SkriptAPIException(error.toString()); } Skript.debug("Registering signature '%s'", signature.name()); @@ -229,44 +227,39 @@ private void register0(@NotNull NamespaceIdentifier namespace, @NotNull Signatur @Override public void register(@NotNull Function function) { - register(null, function); + Preconditions.checkNotNull(function, "function cannot be null"); + if (function.signature().hasModifier(Signature.Modifier.LOCAL)) { + throw new IllegalArgumentException("Cannot register a local function in the global namespace"); + } + register(GLOBAL_NAMESPACE, function); } @Override - public void register(@Nullable String namespace, @NotNull Function function) { - Signature signature = function.signature(); + public void register(@NotNull String namespace, @NotNull Function function) { Preconditions.checkNotNull(function, "function cannot be null"); - if (signature.hasModifier(Signature.Modifier.LOCAL) && namespace == null) { - throw new IllegalArgumentException("Cannot register a local function in the global namespace"); - } - if (!signature.hasModifier(Signature.Modifier.LOCAL) && namespace != null) { + if (!function.signature().hasModifier(Signature.Modifier.LOCAL)) { throw new IllegalArgumentException("Cannot register a global function in a local namespace"); } - Skript.debug("Registering function '%s'", signature.name()); + register(new NamespaceIdentifier(namespace), function); + } - String name = signature.name(); - if (!FUNCTION_NAME_PATTERN.matcher(name).matches()) { - throw new SkriptAPIException("Invalid function name '" + name + "'"); - } + private void register(@NotNull NamespaceIdentifier namespace, @NotNull Function function) { + Signature signature = function.signature(); + Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), + "Invalid signature name '%s'".formatted(signature.name())); - // namespace - NamespaceIdentifier namespaceId; - if (namespace != null) { - namespaceId = new NamespaceIdentifier(namespace); - } else { - namespaceId = GLOBAL_NAMESPACE; - } + Skript.debug("Registering function '%s'", signature.name()); FunctionIdentifier identifier = FunctionIdentifier.of(signature); - if (!signatureExists(namespaceId, identifier)) { + if (!signatureExists(namespace, identifier)) { register(namespace, signature); } - Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); + Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); Function existing = ns.functions.putIfAbsent(identifier, function); if (existing != null) { - alreadyRegisteredError(name, identifier, namespaceId); + alreadyRegisteredError(signature.name(), identifier, namespace); } } From 696d2beb648ac5fc4a55518b310f09dd5d193a72 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Wed, 12 Aug 2026 00:03:43 +0200 Subject: [PATCH 14/32] merge with dev/feature --- src/main/java/ch/njol/skript/structures/StructFunction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index 332f95014b2..dc85b97aa0b 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -180,7 +180,7 @@ public boolean postLoad() { public void unload() { assert signature != null; FunctionRegistry.getRegistry().remove(signature); - signature.calls().forEach(FunctionReference::invalidateCache); + signature.calls().forEach(FunctionReference::invalidate); VALIDATE_FUNCTIONS.set(true); } From f2768092228c57312f56fb3b95c539e5af5e02d9 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Wed, 12 Aug 2026 17:23:06 +0200 Subject: [PATCH 15/32] fix tests --- .../lang/function/FunctionRegistryTest.java | 495 ++++++++---------- 1 file changed, 221 insertions(+), 274 deletions(-) diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java index b8cd0d3f95e..2db41c41c72 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java @@ -1,5 +1,6 @@ package ch.njol.skript.lang.function; +import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; import ch.njol.skript.lang.function.FunctionRegistry.FunctionIdentifier; import ch.njol.skript.lang.util.SimpleLiteral; @@ -8,274 +9,232 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import org.junit.Test; +import org.skriptlang.skript.common.function.DefaultFunction; +import org.skriptlang.skript.common.function.Function; import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import static org.junit.Assert.*; public class FunctionRegistryTest { - private static final ch.njol.skript.lang.function.FunctionRegistry registry = FunctionRegistry.getRegistry(); + private static final FunctionRegistry registry = + (FunctionRegistry) org.skriptlang.skript.common.function.FunctionRegistry.empty(Skript.instance()); private static final String FUNCTION_NAME = "testFunctionRegistry"; private static final String TEST_SCRIPT = "test"; - private static final ch.njol.skript.lang.function.Function TEST_FUNCTION = new SimpleJavaFunction<>(FUNCTION_NAME, new ch.njol.skript.lang.function.Parameter[0], - DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; + private static final Function TEST_FUNCTION = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .build(_ -> true); @Test public void testGetFunctionRetrieval() { - assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); + assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); - assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getSignature(null, FUNCTION_NAME).conflictingArgs()); + assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); + assertNull(registry.getSignature(FUNCTION_NAME).conflictingArgs()); - assertEquals(RetrievalResult.NOT_REGISTERED, registry.getFunction(null, FUNCTION_NAME).result()); - assertNull(registry.getFunction(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).conflictingArgs()); + assertEquals(RetrievalResult.NOT_REGISTERED, registry.getFunction(FUNCTION_NAME).result()); + assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).conflictingArgs()); - registry.register(null, TEST_FUNCTION); + registry.register(TEST_FUNCTION); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); - assertEquals(RetrievalResult.EXACT, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.getSignature(), registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getSignature(null, FUNCTION_NAME).conflictingArgs()); + assertEquals(RetrievalResult.EXACT, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); + assertNull(registry.getSignature(FUNCTION_NAME).conflictingArgs()); - assertEquals(RetrievalResult.EXACT, registry.getFunction(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION, registry.getFunction(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).conflictingArgs()); + assertEquals(RetrievalResult.EXACT, registry.getFunction(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).conflictingArgs()); - registry.remove(TEST_FUNCTION.getSignature()); + registry.remove(TEST_FUNCTION.signature()); } @Test public void testSimpleMultipleRegistrationsFunction() { - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(null, TEST_FUNCTION); + registry.register(TEST_FUNCTION); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.getSignature(), registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); - assertThrows(SkriptAPIException.class, () -> registry.register(null, TEST_FUNCTION)); + assertThrows(SkriptAPIException.class, () -> registry.register(TEST_FUNCTION)); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.getSignature(), registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); - registry.remove(TEST_FUNCTION.getSignature()); + registry.remove(TEST_FUNCTION.signature()); } @Test public void testSimpleRegisterRemoveRegisterGlobal() { - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(null, TEST_FUNCTION); + registry.register(TEST_FUNCTION); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.getSignature(), registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); - registry.remove(TEST_FUNCTION.getSignature()); + registry.remove(TEST_FUNCTION.signature()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(null, TEST_FUNCTION); + registry.register(TEST_FUNCTION); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.getSignature(), registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); - registry.remove(TEST_FUNCTION.getSignature()); + registry.remove(TEST_FUNCTION.signature()); } - private static final ch.njol.skript.lang.function.Function LOCAL_TEST_FUNCTION = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, new ch.njol.skript.lang.function.Parameter[0], - DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; - @Test public void testSimpleRegisterRemoveRegisterLocal() { assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); + registry.register(TEST_SCRIPT, TEST_FUNCTION); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(LOCAL_TEST_FUNCTION.getSignature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertEquals(LOCAL_TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - registry.remove(LOCAL_TEST_FUNCTION.getSignature()); + registry.remove(TEST_FUNCTION.signature()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertNull(registry.getSignature(null, FUNCTION_NAME).retrieved()); + assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); - registry.register(null, TEST_FUNCTION); + registry.register(TEST_SCRIPT, TEST_FUNCTION); + registry.register(TEST_FUNCTION); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); - assertEquals(LOCAL_TEST_FUNCTION.getSignature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertEquals(LOCAL_TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.getSignature(), registry.getSignature(null, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(null, FUNCTION_NAME).retrieved()); - - registry.remove(LOCAL_TEST_FUNCTION.getSignature()); - registry.remove(TEST_FUNCTION.getSignature()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); + assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); + assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); + + registry.remove(TEST_FUNCTION.signature()); + registry.remove(TEST_FUNCTION.signature()); } - private static final ch.njol.skript.lang.function.Function TEST_FUNCTION_B = new SimpleJavaFunction<>(FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null) - }, DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; - - private static final ch.njol.skript.lang.function.Function TEST_FUNCTION_N = new SimpleJavaFunction<>(FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.NUMBER, true, null) - }, DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; + private static final Function TEST_FUNCTION_B = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .parameter("a", Boolean.class) + .build(_ -> true); + + private static final Function TEST_FUNCTION_N = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .parameter("a", Number.class) + .build(_ -> true); @Test public void testMultipleRegistrations() { - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.register(null, TEST_FUNCTION_B); - - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertEquals(TEST_FUNCTION_B.getSignature(), registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(TEST_FUNCTION_B, registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.register(null, TEST_FUNCTION_N); - - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertEquals(TEST_FUNCTION_B.getSignature(), registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(TEST_FUNCTION_B, registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertEquals(TEST_FUNCTION_N.getSignature(), registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(TEST_FUNCTION_N, registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - assertThrows(SkriptAPIException.class, () -> registry.register(null, TEST_FUNCTION_B)); - assertThrows(SkriptAPIException.class, () -> registry.register(null, TEST_FUNCTION_N)); - - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertEquals(TEST_FUNCTION_B.getSignature(), registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(TEST_FUNCTION_B, registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertEquals(TEST_FUNCTION_N.getSignature(), registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(TEST_FUNCTION_N, registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.remove(TEST_FUNCTION_B.getSignature()); - registry.remove(TEST_FUNCTION_N.getSignature()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.register(TEST_FUNCTION_B); + + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(TEST_FUNCTION_B, registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.register(TEST_FUNCTION_N); + + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(TEST_FUNCTION_B, registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertEquals(TEST_FUNCTION_N, registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + assertThrows(SkriptAPIException.class, () -> registry.register(TEST_FUNCTION_B)); + assertThrows(SkriptAPIException.class, () -> registry.register(TEST_FUNCTION_N)); + + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(TEST_FUNCTION_B, registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertEquals(TEST_FUNCTION_N, registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.remove(TEST_FUNCTION_B.signature()); + registry.remove(TEST_FUNCTION_N.signature()); } @Test public void testRegisterRemoveRegisterGlobal() { - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.register(null, TEST_FUNCTION_B); - - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertEquals(TEST_FUNCTION_B.getSignature(), registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(TEST_FUNCTION_B, registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.remove(TEST_FUNCTION_B.getSignature()); - - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.register(null, TEST_FUNCTION_N); - - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertEquals(TEST_FUNCTION_N.getSignature(), registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(TEST_FUNCTION_N, registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.remove(TEST_FUNCTION_N.getSignature()); - - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Number.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Number.class).retrieved()); - - registry.remove(TEST_FUNCTION_B.getSignature()); - registry.remove(TEST_FUNCTION_N.getSignature()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.register(TEST_FUNCTION_B); + + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(TEST_FUNCTION_B, registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.remove(TEST_FUNCTION_B.signature()); + + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.register(TEST_FUNCTION_N); + + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertEquals(TEST_FUNCTION_N, registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.remove(TEST_FUNCTION_N.signature()); + + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Number.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Number.class).retrieved()); + + registry.remove(TEST_FUNCTION_B.signature()); + registry.remove(TEST_FUNCTION_N.signature()); } - private static final ch.njol.skript.lang.function.Function LOCAL_TEST_FUNCTION_B = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null) - }, DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; - - private static final ch.njol.skript.lang.function.Function LOCAL_TEST_FUNCTION_N = new SimpleJavaFunction<>(TEST_SCRIPT, FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.NUMBER, true, null) - }, DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; - @Test public void testRegisterRemoveRegisterLocal() { assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); @@ -285,31 +244,31 @@ public void testRegisterRemoveRegisterLocal() { assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_B); + registry.register(TEST_SCRIPT, TEST_FUNCTION_B); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); - assertEquals(LOCAL_TEST_FUNCTION_B.getSignature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(LOCAL_TEST_FUNCTION_B, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); + assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(TEST_FUNCTION_B, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); - registry.remove(LOCAL_TEST_FUNCTION_B.getSignature()); + registry.remove(TEST_FUNCTION_B.signature()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); - registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); + registry.register(TEST_SCRIPT, TEST_FUNCTION_N); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Number.class).result()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).result()); - assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(LOCAL_TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - registry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); + registry.remove(TEST_FUNCTION_N.signature()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); @@ -318,18 +277,18 @@ public void testRegisterRemoveRegisterLocal() { assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); - registry.register(null, TEST_FUNCTION_B); + registry.register(TEST_SCRIPT, TEST_FUNCTION_N); + registry.register(TEST_FUNCTION_B); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).result()); - assertEquals(LOCAL_TEST_FUNCTION_N.getSignature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(LOCAL_TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Boolean.class).result()); - assertEquals(TEST_FUNCTION_B.getSignature(), registry.getSignature(null, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(TEST_FUNCTION_B, registry.getFunction(null, FUNCTION_NAME, Boolean.class).retrieved()); - - registry.remove(LOCAL_TEST_FUNCTION_N.getSignature()); - registry.remove(TEST_FUNCTION_B.getSignature()); + assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); + assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(TEST_FUNCTION_B, registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); + + registry.remove(TEST_FUNCTION_N.signature()); + registry.remove(TEST_FUNCTION_B.signature()); } @Test @@ -359,17 +318,17 @@ public void testIdentifierOf() { @Test public void testIdentifierSignatureOf() { SimpleJavaFunction function = new SimpleJavaFunction<>(FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null), - new ch.njol.skript.lang.function.Parameter<>("b", DefaultClasses.NUMBER, false, new SimpleLiteral(1, true)) - }, DefaultClasses.BOOLEAN, true) { + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null), + new ch.njol.skript.lang.function.Parameter<>("b", DefaultClasses.NUMBER, false, new SimpleLiteral(1, true)) + }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { return new Boolean[]{true}; } }; - FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); + FunctionIdentifier identifier = FunctionIdentifier.of(function.signature()); assertEquals(FUNCTION_NAME, identifier.name()); assertFalse(identifier.local()); @@ -377,90 +336,78 @@ public void testIdentifierSignatureOf() { assertArrayEquals(new Class[]{Boolean.class, Number[].class}, identifier.args()); SimpleJavaFunction function2 = new SimpleJavaFunction<>(FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null), - new ch.njol.skript.lang.function.Parameter<>("b", DefaultClasses.NUMBER, false, null) - }, DefaultClasses.BOOLEAN, true) { + new ch.njol.skript.lang.function.Parameter[]{ + new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.BOOLEAN, true, null), + new ch.njol.skript.lang.function.Parameter<>("b", DefaultClasses.NUMBER, false, null) + }, DefaultClasses.BOOLEAN, true) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { return new Boolean[]{true}; } }; - assertEquals(FunctionIdentifier.of(function2.getSignature()), identifier); + assertEquals(FunctionIdentifier.of(function2.signature()), identifier); } // see https://github.com/SkriptLang/Skript/pull/8015 @Test public void testRemoveGlobalScriptFunctions8015() { // create empty TEST_SCRIPT namespace such that it is not null - registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); - registry.remove(LOCAL_TEST_FUNCTION.getSignature()); + registry.register(TEST_SCRIPT, TEST_FUNCTION); + registry.remove(TEST_FUNCTION.signature()); assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); // construct a global function with a non-null script, which happens in script functions ch.njol.skript.lang.function.Signature signature = new Signature<>(TEST_SCRIPT, FUNCTION_NAME, new ch.njol.skript.lang.function.Parameter[0], - false, DefaultClasses.BOOLEAN, true, ""); + false, DefaultClasses.BOOLEAN, true, ""); SimpleJavaFunction fn = new SimpleJavaFunction<>(signature) { @Override public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[] { true }; + return new Boolean[]{true}; } }; - // ensure new behaviour + // ensure new behavior assertThrows(IllegalArgumentException.class, () -> registry.register(TEST_SCRIPT, fn)); - registry.register(null, fn); + registry.register(fn); - assertEquals(RetrievalResult.EXACT, registry.getSignature(null, FUNCTION_NAME).result()); + assertEquals(RetrievalResult.EXACT, registry.getSignature(FUNCTION_NAME).result()); registry.remove(signature); - assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME).result()); + assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); } - private static final ch.njol.skript.lang.function.Function TEST_FUNCTION_P = new SimpleJavaFunction<>(FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new ch.njol.skript.lang.function.Parameter<>("a", DefaultClasses.PLAYER, true, null) - }, DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; - - private static final Function TEST_FUNCTION_OP = new SimpleJavaFunction<>(FUNCTION_NAME, - new ch.njol.skript.lang.function.Parameter[]{ - new Parameter<>("a", DefaultClasses.OFFLINE_PLAYER, true, null) - }, DefaultClasses.BOOLEAN, true) { - @Override - public Boolean @Nullable [] executeSimple(Object[][] params) { - return new Boolean[]{true}; - } - }; + private static final Function TEST_FUNCTION_P = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .parameter("a", Player.class) + .build(_ -> true); + + private static final Function TEST_FUNCTION_OP = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .parameter("a", OfflinePlayer.class) + .build(_ -> true); @Test public void testGetExactSignature() { - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, Player.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, Player.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, Player.class).retrieved()); - assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(null, FUNCTION_NAME, OfflinePlayer.class).result()); - assertNull(registry.getSignature(null, FUNCTION_NAME, OfflinePlayer.class).retrieved()); - assertNull(registry.getFunction(null, FUNCTION_NAME, OfflinePlayer.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Player.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, Player.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, Player.class).retrieved()); + assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, OfflinePlayer.class).result()); + assertNull(registry.getSignature(FUNCTION_NAME, OfflinePlayer.class).retrieved()); + assertNull(registry.getFunction(FUNCTION_NAME, OfflinePlayer.class).retrieved()); - registry.register(null, TEST_FUNCTION_P); + registry.register(TEST_FUNCTION_P); assertSame(RetrievalResult.EXACT, registry.getExactSignature(null, FUNCTION_NAME, Player.class).result()); - assertEquals(TEST_FUNCTION_P.getSignature(), registry.getExactSignature(null, FUNCTION_NAME, Player.class).retrieved()); + assertEquals(TEST_FUNCTION_P.signature(), registry.getExactSignature(null, FUNCTION_NAME, Player.class).retrieved()); assertNull(registry.getExactSignature(null, FUNCTION_NAME, OfflinePlayer.class).retrieved()); - assertEquals(TEST_FUNCTION_P.getSignature(), registry.getSignature(null, FUNCTION_NAME, Player.class).retrieved()); - assertEquals(TEST_FUNCTION_P.getSignature(), registry.getSignature(null, FUNCTION_NAME, OfflinePlayer.class).retrieved()); + assertEquals(TEST_FUNCTION_P.signature(), registry.getSignature(FUNCTION_NAME, Player.class).retrieved()); + assertEquals(TEST_FUNCTION_P.signature(), registry.getSignature(FUNCTION_NAME, OfflinePlayer.class).retrieved()); - registry.remove(TEST_FUNCTION_P.getSignature()); - registry.remove(TEST_FUNCTION_OP.getSignature()); + registry.remove(TEST_FUNCTION_P.signature()); + registry.remove(TEST_FUNCTION_OP.signature()); } } From 2cd234bc79afb81f1b9eb520b8de0e92509036f1 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Wed, 12 Aug 2026 17:39:01 +0200 Subject: [PATCH 16/32] improvements --- .../java/ch/njol/skript/lang/function/FunctionRegistry.java | 2 ++ src/main/java/ch/njol/skript/lang/function/Functions.java | 6 +++--- src/main/java/ch/njol/skript/lang/function/Signature.java | 3 --- src/main/java/ch/njol/skript/structures/StructFunction.java | 2 +- .../org/skriptlang/skript/common/function/Function.java | 1 - .../org/skriptlang/skript/common/function/Signature.java | 2 -- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index b3bbf1e9535..6e33e879cef 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -169,6 +169,7 @@ public void register(@NotNull String namespace, @NotNull Signature signature) private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), "Invalid signature name '%s'".formatted(signature.name())); + Skript.checkAcceptRegistrations(); Retrieval> existing; Parameter[] parameters = signature.parameters().all(); @@ -247,6 +248,7 @@ private void register(@NotNull NamespaceIdentifier namespace, @NotNull Function< Signature signature = function.signature(); Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), "Invalid signature name '%s'".formatted(signature.name())); + Skript.checkAcceptRegistrations(); Skript.debug("Registering function '%s'", signature.name()); 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 ceffc472fb4..f42e4a7bda8 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -70,7 +70,7 @@ public static DefaultFunction register(DefaultFunction function) { } globalFunctions.put(function.name(), javaNamespace); - FunctionRegistry.getRegistry().register(null, function); + FunctionRegistry.getRegistry().register(function); return function; } @@ -91,7 +91,7 @@ public static JavaFunction registerFunction(JavaFunction function) { } globalFunctions.put(function.getName(), javaNamespace); - FunctionRegistry.getRegistry().register(null, function); + FunctionRegistry.getRegistry().register(function); return function; } @@ -130,7 +130,7 @@ public static JavaFunction registerFunction(JavaFunction function) { if (function.getSignature().isLocal()) { FunctionRegistry.getRegistry().register(script.getConfig().getFileName(), function); } else { - FunctionRegistry.getRegistry().register(null, function); + FunctionRegistry.getRegistry().register(function); } return function; diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index 4e251358def..5a1441d9006 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -189,9 +189,6 @@ public Parameter[] getParameters() { return (Class) returns; } - /** - * @return A {@link SequencedMap} containing all parameters. - */ @Override public @NotNull Parameters parameters() { return parameters; diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index dc85b97aa0b..08ca5c67ea0 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -163,7 +163,7 @@ private static void loadFunction(Script script, SectionNode node, Signature s if (function.getSignature().isLocal()) { FunctionRegistry.getRegistry().register(script.getConfig().getFileName(), function); } else { - FunctionRegistry.getRegistry().register(null, function); + FunctionRegistry.getRegistry().register(function); } } diff --git a/src/main/java/org/skriptlang/skript/common/function/Function.java b/src/main/java/org/skriptlang/skript/common/function/Function.java index 306d373a369..5266f57d2ad 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Function.java +++ b/src/main/java/org/skriptlang/skript/common/function/Function.java @@ -6,7 +6,6 @@ import org.jetbrains.annotations.ApiStatus.NonExtendable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.util.event.Event; /** * Represents a function implementation. diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index f2a259fceeb..5056b9f75b5 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -7,7 +7,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import org.jetbrains.annotations.UnmodifiableView; import java.util.Set; @@ -35,7 +34,6 @@ public interface Signature { /** * @return An unmodifiable view of all the parameters that this signature has. */ - @UnmodifiableView @NotNull Parameters parameters(); /** From 30ca84dac5a43d6e41ccd2855bc31426ab044648 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:19:24 +0200 Subject: [PATCH 17/32] add impl --- .../common/function/FunctionRegistryImpl.java | 763 ++++++++++++++++++ 1 file changed, 763 insertions(+) create mode 100644 src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java new file mode 100644 index 00000000000..7ea4dc56ab3 --- /dev/null +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java @@ -0,0 +1,763 @@ +package ch.njol.skript.lang.function; + +import ch.njol.skript.Skript; +import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.util.Utils; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.ApiStatus.Experimental; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; +import org.jspecify.annotations.NonNull; +import org.skriptlang.skript.common.function.Function; +import org.skriptlang.skript.common.function.Parameter; +import org.skriptlang.skript.common.function.Parameter.Modifier; +import org.skriptlang.skript.common.function.Signature; +import org.skriptlang.skript.lang.converter.Converters; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@Experimental +@ApiStatus.Internal +public final class FunctionRegistryImpl implements org.skriptlang.skript.common.function.FunctionRegistry { + + private final org.skriptlang.skript.Skript skript; + + public FunctionRegistryImpl(org.skriptlang.skript.Skript skript) { + this.skript = skript; + } + + /** + * The pattern for a valid function name. + * Functions must start with a letter or underscore and can only contain letters, numbers, and underscores. + */ + final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile("[\\p{IsAlphabetic}_][\\p{IsAlphabetic}\\d_]*"); + + /** + * The namespace for registered global functions. + */ + private final NamespaceIdentifier GLOBAL_NAMESPACE = new NamespaceIdentifier(null); + + /** + * All registered namespaces. + */ + private final Map namespaces = new ConcurrentHashMap<>(); + + @Override + public Collection> elements() { + Set> functions = new HashSet<>(); + + for (Namespace namespace : namespaces.values()) { + functions.addAll(namespace.functions.values()); + } + + return Collections.unmodifiableSet(functions); + } + + private record UnmodifiableFunctionRegistry( + org.skriptlang.skript.common.function.FunctionRegistry registry) implements org.skriptlang.skript.common.function.FunctionRegistry { + + @Override + public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { + return this; + } + + @Override + public Collection> elements() { + return registry.elements(); + } + + @Override + public void register(@NotNull Signature signature) { + throw new UnsupportedOperationException("Cannot register signatures on an unmodifiable function registry"); + } + + @Override + public void register(@NotNull String namespace, @NotNull Signature signature) { + throw new UnsupportedOperationException("Cannot register signatures on an unmodifiable function registry"); + } + + @Override + public void register(@NotNull Function function) { + throw new UnsupportedOperationException("Cannot register functions on an unmodifiable function registry"); + } + + @Override + public void register(@NotNull String namespace, @NotNull Function function) { + throw new UnsupportedOperationException("Cannot register functions on an unmodifiable function registry"); + } + + @Override + public @NotNull Retrieval> getFunction(@NotNull String name, @NonNull @NotNull Class... args) { + return registry.getFunction(name, args); + } + + @Override + public @NotNull Retrieval> getFunction(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + return registry.getFunction(namespace, name, args); + } + + @Override + public @NotNull Retrieval> getSignature(@NotNull String name, @NonNull @NotNull Class... args) { + return registry.getSignature(name, args); + } + + @Override + public @NotNull Retrieval> getSignature(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + return registry.getSignature(namespace, name, args); + } + + @Override + public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { + return registry.getSignatures(name); + } + + @Override + public @Unmodifiable @NotNull Set> getSignatures(@NotNull String namespace, @NotNull String name) { + return registry.getSignatures(namespace, name); + } + + @Override + public void remove(@NotNull Signature signature) { + throw new UnsupportedOperationException("Cannot remove functions on an unmodifiable function registry"); + } + } + + @Override + public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { + return new UnmodifiableFunctionRegistry(this); + } + + @Override + public void register(@NotNull Signature signature) { + Preconditions.checkNotNull(signature, "signature cannot be null"); + if (signature.hasModifier(Signature.Modifier.LOCAL)) { + throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); + } + register(GLOBAL_NAMESPACE, signature); + } + + @Override + public void register(@NotNull String namespace, @NotNull Signature signature) { + Preconditions.checkNotNull(signature, "signature cannot be null"); + if (!signature.hasModifier(Signature.Modifier.LOCAL)) { + throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); + } + + register(new NamespaceIdentifier(namespace), signature); + } + + private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { + Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), + "Invalid signature name '%s'".formatted(signature.name())); + Skript.checkAcceptRegistrations(); + + Retrieval> existing; + Parameter[] parameters = signature.parameters().all(); + + if (parameters.length == 1 && !parameters[0].isSingle()) { + existing = getExactSignature(signature.namespace(), signature.name(), parameters[0].type().arrayType()); + } else { + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + types[i] = parameters[i].type(); + } + + existing = getExactSignature(signature.namespace(), signature.name(), types); + } + + // if this function has already been registered, only allow it if one function is local and one is global. + // if both are global or both are local, disallow. + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Signature.Modifier.LOCAL) == signature.hasModifier(Signature.Modifier.LOCAL)) { + StringBuilder error = new StringBuilder(); + + if (existing.retrieved().hasModifier(Signature.Modifier.LOCAL)) { + error.append("Local function "); + } else { + error.append("Function "); + } + error.append("'%s' with the same argument types already exists".formatted(signature.name())); + if (existing.retrieved().namespace() != null) { + error.append(" in script '%s'.".formatted(existing.retrieved().namespace())); + } else { + error.append("."); + } + + throw new SkriptAPIException(error.toString()); + } + + Skript.debug("Registering signature '%s'", signature.name()); + + Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); + FunctionIdentifier identifier = FunctionIdentifier.of(signature); + + // register + // since we are getting a set and then updating it, + // avoid race conditions by ensuring only one thread can access this namespace for this operation + synchronized (ns) { + Set identifiersWithName = ns.identifiers.computeIfAbsent(identifier.name, s -> new HashSet<>()); + boolean exists = identifiersWithName.add(identifier); + if (!exists) { + alreadyRegisteredError(signature.name(), identifier, namespace); + } + } + + if (ns.signatures.putIfAbsent(identifier, signature) != null) { + alreadyRegisteredError(signature.name(), identifier, namespace); + } + } + + @Override + public void register(@NotNull Function function) { + Preconditions.checkNotNull(function, "function cannot be null"); + if (function.signature().hasModifier(Signature.Modifier.LOCAL)) { + throw new IllegalArgumentException("Cannot register a local function in the global namespace"); + } + register(GLOBAL_NAMESPACE, function); + } + + @Override + public void register(@NotNull String namespace, @NotNull Function function) { + Preconditions.checkNotNull(function, "function cannot be null"); + if (!function.signature().hasModifier(Signature.Modifier.LOCAL)) { + throw new IllegalArgumentException("Cannot register a global function in a local namespace"); + } + register(new NamespaceIdentifier(namespace), function); + } + + private void register(@NotNull NamespaceIdentifier namespace, @NotNull Function function) { + Signature signature = function.signature(); + Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), + "Invalid signature name '%s'".formatted(signature.name())); + Skript.checkAcceptRegistrations(); + + Skript.debug("Registering function '%s'", signature.name()); + + FunctionIdentifier identifier = FunctionIdentifier.of(signature); + if (!signatureExists(namespace, identifier)) { + register(namespace, signature); + } + + Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); + + Function existing = ns.functions.putIfAbsent(identifier, function); + if (existing != null) { + alreadyRegisteredError(signature.name(), identifier, namespace); + } + } + + @Override + public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getFunction(@NotNull String name, @NotNull Class... args) { + return getFunction(null, name, args); + } + + private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, NamespaceIdentifier namespace) { + throw new SkriptAPIException("Function '%s' with parameters %s is already registered in %s" + .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), + namespace)); + } + + /** + * Checks if a function with the given name and arguments exists in the namespace. + * + * @param namespace The namespace to check in. + * @param identifier The identifier of the function. + * @return True if a function with the given name and arguments exists in the namespace, false otherwise. + */ + private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull FunctionIdentifier identifier) { + Preconditions.checkNotNull(namespace, "namespace cannot be null"); + Preconditions.checkNotNull(identifier, "identifier cannot be null"); + + Namespace ns = namespaces.get(namespace); + if (ns == null) { + return false; + } + + if (!ns.identifiers.containsKey(identifier.name)) { + return false; + } + + for (FunctionIdentifier other : ns.identifiers.get(identifier.name)) { + if (identifier.equals(other)) { + return true; + } + } + + return false; + } + + @Override + public @NotNull Retrieval> getFunction( + @Nullable String namespace, + @NotNull String name, + @NotNull Class... args + ) { + Retrieval> attempt = null; + if (namespace != null) { + attempt = getFunction(new NamespaceIdentifier(namespace), + FunctionIdentifier.of(name, true, args)); + } + if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { + attempt = getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); + } + return attempt; + } + + @Override + public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getSignature(@NotNull String name, @NotNull Class... args) { + return getSignature(null, name, args); + } + + /** + * Gets a function from a namespace. + * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param provided The provided identifier of the function. + * @return Information related to the attempt to get the specified function, + * stored in a {@link Retrieval} object. + */ + private @NotNull Retrieval> getFunction(@NotNull NamespaceIdentifier namespace, @NotNull FunctionIdentifier provided) { + Preconditions.checkNotNull(namespace, "namespace cannot be null"); + Preconditions.checkNotNull(provided, "provided cannot be null"); + + Namespace ns = namespaces.getOrDefault(namespace, new Namespace()); + Set existing = ns.identifiers.get(provided.name); + if (existing == null) { + Skript.debug("No functions named '%s' exist in the '%s' namespace", provided.name, namespace.name); + return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + } + + Set candidates = candidates(provided, existing, false); + if (candidates.isEmpty()) { + Skript.debug("Failed to find a function for '%s'", provided.name); + return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + } else if (candidates.size() == 1) { + if (Skript.debug()) { + Skript.debug("Matched function for '%s': %s", provided.name, candidates.stream().findAny().orElse(null)); + } + return new Retrieval<>(RetrievalResult.EXACT, + ns.functions.get(candidates.stream().findAny().orElse(null)), + null); + } else { + if (Skript.debug()) { + String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); + Skript.debug("Failed to match an exact function for '%s'", provided.name); + Skript.debug("Identifier: %s", provided); + Skript.debug("Options: %s", options); + } + return new Retrieval<>(RetrievalResult.AMBIGUOUS, + null, + candidates.stream() + .map(FunctionIdentifier::args) + .toArray(Class[][]::new)); + } + } + + @Override + public @NonNull Retrieval> getSignature( + @Nullable String namespace, + @NotNull String name, + @NotNull Class... args + ) { + Retrieval> attempt = null; + if (namespace != null) { + attempt = getSignature(new NamespaceIdentifier(namespace), + FunctionIdentifier.of(name, true, args), false); + } + if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { + attempt = getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), false); + } + return attempt; + } + + @Override + public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { + return getSignatures(null, name); + } + + /** + * Gets the signature for a function with the given name and arguments. If no local function is found, + * checks for global functions. If {@code namespace} is null, only global signatures will be checked. + *

+ * This function checks performs no argument conversions, and is only used for determining whether a + * signature already exists with the exact specified arguments. In almost all cases, {@link #getSignature(String, String, Class[])} + * should be used. + *

+ * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return The signature for the function with the given name and argument types, or null if no such function exists. + */ + public Retrieval> getExactSignature( + @Nullable String namespace, + @NotNull String name, + @NotNull Class... args + ) { + Retrieval> attempt = null; + if (namespace != null) { + attempt = getSignature(new NamespaceIdentifier(namespace), + FunctionIdentifier.of(name, true, args), true); + } + + if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { + attempt = getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), true); + } + return attempt; + } + + /** + * Gets every signature with the name {@code name}. + * This includes global functions and, if {@code namespace} is not null, functions under that namespace (if valid). + * + * @param namespace The additional namespace to obtain signatures from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the signature(s) to obtain. + * @return A list of all signatures named {@code name}. + */ + @Override + public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { + Preconditions.checkNotNull(name, "name cannot be null"); + + Map> total = new HashMap<>(); + + // obtain all local functions of "name" + if (namespace != null) { + Namespace local = namespaces.getOrDefault(new NamespaceIdentifier(namespace), new Namespace()); + + for (FunctionIdentifier identifier : local.identifiers.getOrDefault(name, Collections.emptySet())) { + total.putIfAbsent(identifier, local.signatures.get(identifier)); + } + } + + // obtain all global functions of "name" + Namespace global = namespaces.getOrDefault(GLOBAL_NAMESPACE, new Namespace()); + for (FunctionIdentifier identifier : global.identifiers.getOrDefault(name, Collections.emptySet())) { + total.putIfAbsent(identifier, global.signatures.get(identifier)); + } + + return Set.copyOf(total.values()); + } + + /** + * Gets the signature for a function with the given name and arguments. + * + * @param namespace The namespace to get the function from. + * @param provided The provided identifier of the function. + * @param exact When false, will convert arguments to different types to attempt to find a match. + * When true, will not convert arguments. + * @return The signature for the function with the given name and argument types, or null if no such signature exists + * in the specified namespace. + */ + private Retrieval> getSignature(@NotNull NamespaceIdentifier namespace, @NotNull FunctionIdentifier provided, boolean exact) { + Preconditions.checkNotNull(namespace, "namespace cannot be null"); + Preconditions.checkNotNull(provided, "provided cannot be null"); + + Namespace ns = namespaces.getOrDefault(namespace, new Namespace()); + if (!ns.identifiers.containsKey(provided.name)) { + Skript.debug("No signatures named '%s' exist in the '%s' namespace", provided.name, namespace.name); + return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + } + + Set candidates = candidates(provided, ns.identifiers.get(provided.name), exact); + if (candidates.isEmpty()) { + Skript.debug("Failed to find a signature for '%s'", provided.name); + return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + } else if (candidates.size() == 1) { + if (Skript.debug()) { + Skript.debug("Matched signature for '%s': %s", + provided.name, ns.signatures.get(candidates.stream().findAny().orElse(null))); + } + return new Retrieval<>(RetrievalResult.EXACT, + ns.signatures.get(candidates.stream().findAny().orElse(null)), + null); + } else { + if (Skript.debug()) { + String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); + Skript.debug("Failed to match an exact signature for '%s'", provided.name); + Skript.debug("Identifier: %s", provided); + Skript.debug("Options: %s", options); + } + return new Retrieval<>(RetrievalResult.AMBIGUOUS, + null, + candidates.stream() + .map(FunctionIdentifier::args) + .toArray(Class[][]::new)); + } + } + + /** + * Returns an unmodifiable list of candidates for the provided function identifier. + * + * @param provided The provided function. + * @param existing The existing functions with the same name. + * @param exact When false, will convert arguments to different types to attempt to find a match. + * When true, will not convert arguments. + * @return An unmodifiable list of candidates for the provided function. + */ + private static @Unmodifiable @NotNull Set candidates( + @NotNull FunctionIdentifier provided, + Set existing, + boolean exact + ) { + Set candidates = new HashSet<>(); + + candidates: + for (FunctionIdentifier candidate : existing) { + // by this point, all candidates have matching names + + if (Arrays.stream(candidate.args).filter(Class::isArray).count() == 1 + && candidate.args.length == 1 + && candidate.args[0].isArray()) { + // if a function has single list value param, check all types + // make sure all types in the passed array are valid for the array parameter + Class arrayType = candidate.args[0].componentType(); + for (Class arrayArg : provided.args) { + arrayArg = Utils.getComponentType(arrayArg); + + if (!Converters.converterExists(arrayArg, arrayType)) { + continue candidates; + } + } + + candidates.add(candidate); + continue; + } + + // if argument counts are not possible, skip + if (provided.args.length > candidate.args.length + || provided.args.length < candidate.minArgCount) { + continue; + } + + // if the types of the provided arguments do not match the candidate arguments, skip + for (int i = 0; i < provided.args.length; i++) { + // allows single passed values to still match array type in candidate (e.g. clamp) + Class providedType = Utils.getComponentType(provided.args[i]); + + Class candidateType = Utils.getComponentType(candidate.args[i]); + Class providedArg = provided.args[i]; + if (exact) { + if (providedArg != candidateType) { + continue candidates; + } + } else { + if (!Converters.converterExists(providedType, candidateType)) { + continue candidates; + } + } + } + + candidates.add(candidate); + } + + if (candidates.size() <= 1) { + // if there is only one candidate, then return without trying to convert + return Collections.unmodifiableSet(candidates); + } + + // let overloaded(Long, Long) and overloaded(String, String) be two functions. + // the code below allows overloaded(1, {_x}) to match Long, Long and avoid String, String, + // and allow overloaded({_x}, 1) to match Long, Long and avoid String, String + // despite not being an exact match in all arguments, + // since variables have an unknown type at runtime. + Iterator iterator = candidates.iterator(); + while (iterator.hasNext()) { + FunctionIdentifier candidate = iterator.next(); + int argIndex = 0; + + while (argIndex < provided.args.length) { + if (provided.args[argIndex] == Object.class) { + argIndex++; + continue; + } + + if (provided.args[argIndex] != candidate.args[argIndex]) { + iterator.remove(); + break; + } + + argIndex++; + } + } + + return Collections.unmodifiableSet(candidates); + } + + @Override + public void remove(@NotNull Signature signature) { + Preconditions.checkNotNull(signature, "signature cannot be null"); + + String name = signature.name(); + FunctionIdentifier identifier = FunctionIdentifier.of(signature); + + Namespace namespace; + if (signature.hasModifier(Signature.Modifier.LOCAL)) { + namespace = namespaces.get(new NamespaceIdentifier(signature.namespace())); + } else { + namespace = namespaces.get(GLOBAL_NAMESPACE); + } + + if (namespace == null) { + return; + } + + for (FunctionIdentifier other : namespace.identifiers.getOrDefault(name, Set.of())) { + if (!identifier.equals(other)) { + continue; + } + + removeUpdateMaps(namespace, other, name); + return; + } + } + + /** + * Updates the maps by removing the provided function identifier from the maps. + * + * @param namespace The namespace + * @param toRemove The identifier to remove + * @param name The name of the function + */ + private void removeUpdateMaps(Namespace namespace, FunctionIdentifier toRemove, String name) { + namespace.identifiers.computeIfPresent(name, (k, set) -> { + if (set.remove(toRemove)) { + Skript.debug("Removed identifier '%s' from %s", toRemove, namespace); + } + return set.isEmpty() ? null : set; + }); + if (namespace.functions.remove(toRemove) != null) { + Skript.debug("Removed function '%s' from %s", toRemove, namespace); + } + if (namespace.signatures.remove(toRemove) != null) { + Skript.debug("Removed signature '%s' from %s", toRemove, namespace); + } + } + + /** + * An identifier for a function namespace. + */ + private record NamespaceIdentifier(@Nullable String name) { + + } + + /** + * The data a namespace contains. + */ + private static final class Namespace { + + /** + * Map for all function names to their identifiers, allowing for quicker lookup. + */ + private final Map> identifiers = new HashMap<>(); + + /** + * Map for all identifier to function combinations. + */ + private final Map> functions = new HashMap<>(); + + /** + * Map for all identifier to signature combinations. + */ + private final Map> signatures = new HashMap<>(); + + } + + /** + * An identifier for a function. + *

Used to differentiate between functions with the same name but different parameters.

+ * + * @param name The name of the function. + * @param args The arguments of the function. + */ + record FunctionIdentifier(@NotNull String name, boolean local, int minArgCount, + @NotNull Class... args) { + + /** + * Returns the identifier for the given arguments. + * + * @param name The name of the function. + * @param args The types of the arguments. + * @return The identifier for the signature. + */ + static FunctionIdentifier of(@NotNull String name, boolean local, @NotNull Class... args) { + Preconditions.checkNotNull(name, "name cannot be null"); + Preconditions.checkNotNull(args, "args cannot be null"); + + return new FunctionIdentifier(name, local, args.length, args); + } + + /** + * Returns the identifier for the given signature. + * + * @param signature The signature to get the identifier for. + * @return The identifier for the signature. + */ + static FunctionIdentifier of(@NotNull Signature signature) { + Preconditions.checkNotNull(signature, "signature cannot be null"); + + Parameter[] signatureParams = signature.parameters().all(); + Class[] parameters = new Class[signatureParams.length]; + + int optionalArgs = 0; + for (int i = 0; i < signatureParams.length; i++) { + Parameter param = signatureParams[i]; + if (param.hasModifier(Modifier.OPTIONAL)) { + optionalArgs++; + } + + parameters[i] = param.type(); + } + + return new FunctionIdentifier(signature.name(), signature.hasModifier(Signature.Modifier.LOCAL), + parameters.length - optionalArgs, parameters); + } + + @Override + public int hashCode() { + return Objects.hash(name, Arrays.hashCode(args)); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FunctionIdentifier other)) { + return false; + } + + if (!name.equals(other.name)) { + return false; + } + + if (args.length != other.args.length) { + return false; + } + + for (int i = 0; i < args.length; i++) { + if (args[i] != other.args[i]) { + return false; + } + } + + return true; + } + + @Override + public @NotNull String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name) + .add("local", local) + .add("minArgCount", minArgCount) + .add("args", Arrays.stream(args).map(Class::getSimpleName).collect(Collectors.joining(", "))) + .toString(); + } + + } + +} From 88fe2097d01e9b5af86b817fe9d577c8acc46302 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:21:13 +0200 Subject: [PATCH 18/32] add old registry back --- .../lang/function/FunctionRegistry.java | 379 +++++++++--------- 1 file changed, 181 insertions(+), 198 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 6e33e879cef..1fe91e35bbc 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -6,42 +6,35 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import org.jspecify.annotations.NonNull; -import org.skriptlang.skript.common.function.Function; import org.skriptlang.skript.common.function.Parameter; import org.skriptlang.skript.common.function.Parameter.Modifier; -import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.lang.converter.Converters; +import org.skriptlang.skript.util.Registry; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import java.util.stream.Collectors; -@Experimental +/** + * A registry for functions. + */ @ApiStatus.Internal -public final class FunctionRegistry implements org.skriptlang.skript.common.function.FunctionRegistry { - - private final org.skriptlang.skript.Skript skript; - - public FunctionRegistry(org.skriptlang.skript.Skript skript) { - this.skript = skript; - } +public final class FunctionRegistry implements Registry> { private static FunctionRegistry registry; /** - * @deprecated Accessing the function registry requires a Skript instance to allow modification. - * Therefore, this method only returns an unmodifiable registry. + * Gets the global function registry. + * + * @return The global function registry. */ - @Deprecated(forRemoval = true, since = "INSERT VERSION") public static FunctionRegistry getRegistry() { if (registry == null) { - registry = new FunctionRegistry(Skript.instance()); + registry = new FunctionRegistry(); } return registry; } @@ -50,7 +43,7 @@ public static FunctionRegistry getRegistry() { * The pattern for a valid function name. * Functions must start with a letter or underscore and can only contain letters, numbers, and underscores. */ - final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile("[\\p{IsAlphabetic}_][\\p{IsAlphabetic}\\d_]*"); + final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile(Functions.functionNamePattern); /** * The namespace for registered global functions. @@ -63,7 +56,7 @@ public static FunctionRegistry getRegistry() { private final Map namespaces = new ConcurrentHashMap<>(); @Override - public Collection> elements() { + public @Unmodifiable @NotNull Collection> elements() { Set> functions = new HashSet<>(); for (Namespace namespace : namespaces.values()) { @@ -73,141 +66,43 @@ public Collection> elements() { return Collections.unmodifiableSet(functions); } - private record UnmodifiableFunctionRegistry( - org.skriptlang.skript.common.function.FunctionRegistry registry) implements org.skriptlang.skript.common.function.FunctionRegistry { - - @Override - public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { - return this; - } - - @Override - public Collection> elements() { - return registry.elements(); - } - - @Override - public void register(@NotNull Signature signature) { - throw new UnsupportedOperationException("Cannot register signatures on an unmodifiable function registry"); - } - - @Override - public void register(@NotNull String namespace, @NotNull Signature signature) { - throw new UnsupportedOperationException("Cannot register signatures on an unmodifiable function registry"); - } - - @Override - public void register(@NotNull Function function) { - throw new UnsupportedOperationException("Cannot register functions on an unmodifiable function registry"); - } - - @Override - public void register(@NotNull String namespace, @NotNull Function function) { - throw new UnsupportedOperationException("Cannot register functions on an unmodifiable function registry"); - } - - @Override - public @NotNull Retrieval> getFunction(@NotNull String name, @NonNull @NotNull Class... args) { - return registry.getFunction(name, args); - } - - @Override - public @NotNull Retrieval> getFunction(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { - return registry.getFunction(namespace, name, args); - } - - @Override - public @NotNull Retrieval> getSignature(@NotNull String name, @NonNull @NotNull Class... args) { - return registry.getSignature(name, args); - } - - @Override - public @NotNull Retrieval> getSignature(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { - return registry.getSignature(namespace, name, args); - } - - @Override - public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { - return registry.getSignatures(name); - } - - @Override - public @Unmodifiable @NotNull Set> getSignatures(@NotNull String namespace, @NotNull String name) { - return registry.getSignatures(namespace, name); - } - - @Override - public void remove(@NotNull Signature signature) { - throw new UnsupportedOperationException("Cannot remove functions on an unmodifiable function registry"); - } - } - - @Override - public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() { - return new UnmodifiableFunctionRegistry(this); - } - - @Override - public void register(@NotNull Signature signature) { + /** + * Registers a signature. + *

+ * Attempting to register a local signature in the global namespace, or a global signature in + * a local namespace, will throw an {@link IllegalArgumentException}. + * If {@code namespace} is null, will register this signature globally, + * only if the signature is global. + *

+ * + * @param namespace The namespace to register the signature in. + * Usually represents the path of the script this signature is registered in. + * @param signature The signature to register. + * @throws SkriptAPIException if a signature with the same name and parameters is already registered + * in this namespace. + * @throws IllegalArgumentException if the signature is global and namespace is not null, or + * if the signature is local and namespace is null. + */ + public void register(@Nullable String namespace, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - if (signature.hasModifier(Signature.Modifier.LOCAL)) { + if (signature.isLocal() && namespace == null) { throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); } - register(GLOBAL_NAMESPACE, signature); - } - - @Override - public void register(@NotNull String namespace, @NotNull Signature signature) { - Preconditions.checkNotNull(signature, "signature cannot be null"); - if (!signature.hasModifier(Signature.Modifier.LOCAL)) { + if (!signature.isLocal() && namespace != null) { throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); } - register(new NamespaceIdentifier(namespace), signature); - } - - private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature signature) { - Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), - "Invalid signature name '%s'".formatted(signature.name())); - Skript.checkAcceptRegistrations(); + Skript.debug("Registering signature '%s'", signature.getName()); - Retrieval> existing; - Parameter[] parameters = signature.parameters().all(); - - if (parameters.length == 1 && !parameters[0].isSingle()) { - existing = getExactSignature(signature.namespace(), signature.name(), parameters[0].type().arrayType()); + // namespace + NamespaceIdentifier namespaceId; + if (namespace != null) { + namespaceId = new NamespaceIdentifier(namespace); } else { - Class[] types = new Class[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - types[i] = parameters[i].type(); - } - - existing = getExactSignature(signature.namespace(), signature.name(), types); + namespaceId = GLOBAL_NAMESPACE; } - // if this function has already been registered, only allow it if one function is local and one is global. - // if both are global or both are local, disallow. - if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Signature.Modifier.LOCAL) == signature.hasModifier(Signature.Modifier.LOCAL)) { - StringBuilder error = new StringBuilder(); - - if (existing.retrieved().hasModifier(Signature.Modifier.LOCAL)) { - error.append("Local function "); - } else { - error.append("Function "); - } - error.append("'%s' with the same argument types already exists".formatted(signature.name())); - if (existing.retrieved().namespace() != null) { - error.append(" in script '%s'.".formatted(existing.retrieved().namespace())); - } else { - error.append("."); - } - - throw new SkriptAPIException(error.toString()); - } - - Skript.debug("Registering signature '%s'", signature.name()); - - Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); + Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); FunctionIdentifier identifier = FunctionIdentifier.of(signature); // register @@ -217,59 +112,70 @@ private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature Set identifiersWithName = ns.identifiers.computeIfAbsent(identifier.name, s -> new HashSet<>()); boolean exists = identifiersWithName.add(identifier); if (!exists) { - alreadyRegisteredError(signature.name(), identifier, namespace); + alreadyRegisteredError(signature.getName(), identifier, namespaceId); } } - if (ns.signatures.putIfAbsent(identifier, signature) != null) { - alreadyRegisteredError(signature.name(), identifier, namespace); + Signature existing = ns.signatures.putIfAbsent(identifier, signature); + if (existing != null) { + alreadyRegisteredError(signature.getName(), identifier, namespaceId); } } - @Override - public void register(@NotNull Function function) { + /** + * Registers a function. + *

+ * Attempting to register a local function in the global namespace, or a global function in + * a local namespace, will throw an {@link IllegalArgumentException}. + * If {@code namespace} is null, will register this function globally, + * only if the function is global. + *

+ * + * @param namespace The namespace to register the function in. + * Usually represents the path of the script this function is registered in. + * @param function The function to register. + * @throws SkriptAPIException if the function name is invalid or if + * a function with the same name and parameters is already registered + * in this namespace. + * @throws IllegalArgumentException if the function is global and namespace is not null, or + * if the function is local and namespace is null. + */ + public void register(@Nullable String namespace, @NotNull Function function) { Preconditions.checkNotNull(function, "function cannot be null"); - if (function.signature().hasModifier(Signature.Modifier.LOCAL)) { + if (function.getSignature().isLocal() && namespace == null) { throw new IllegalArgumentException("Cannot register a local function in the global namespace"); } - register(GLOBAL_NAMESPACE, function); - } - - @Override - public void register(@NotNull String namespace, @NotNull Function function) { - Preconditions.checkNotNull(function, "function cannot be null"); - if (!function.signature().hasModifier(Signature.Modifier.LOCAL)) { + if (!function.getSignature().isLocal() && namespace != null) { throw new IllegalArgumentException("Cannot register a global function in a local namespace"); } - register(new NamespaceIdentifier(namespace), function); - } + Skript.debug("Registering function '%s'", function.getName()); - private void register(@NotNull NamespaceIdentifier namespace, @NotNull Function function) { - Signature signature = function.signature(); - Preconditions.checkArgument(FUNCTION_NAME_PATTERN.matcher(signature.name()).matches(), - "Invalid signature name '%s'".formatted(signature.name())); - Skript.checkAcceptRegistrations(); + String name = function.getName(); + if (!FUNCTION_NAME_PATTERN.matcher(name).matches()) { + throw new SkriptAPIException("Invalid function name '" + name + "'"); + } - Skript.debug("Registering function '%s'", signature.name()); + // namespace + NamespaceIdentifier namespaceId; + if (namespace != null) { + namespaceId = new NamespaceIdentifier(namespace); + } else { + namespaceId = GLOBAL_NAMESPACE; + } - FunctionIdentifier identifier = FunctionIdentifier.of(signature); - if (!signatureExists(namespace, identifier)) { - register(namespace, signature); + FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); + if (!signatureExists(namespaceId, identifier)) { + register(namespace, function.getSignature()); } - Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); + Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); Function existing = ns.functions.putIfAbsent(identifier, function); if (existing != null) { - alreadyRegisteredError(signature.name(), identifier, namespace); + alreadyRegisteredError(name, identifier, namespaceId); } } - @Override - public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getFunction(@NotNull String name, @NotNull Class... args) { - return getFunction(null, name, args); - } - private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, NamespaceIdentifier namespace) { throw new SkriptAPIException("Function '%s' with parameters %s is already registered in %s" .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), @@ -305,7 +211,74 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull return false; } - @Override + /** + * The result of attempting to retrieve a function. + * Depending on the type, a {@link Retrieval} will feature different data. + */ + public enum RetrievalResult { + + /** + * The specified function or signature has not been registered. + */ + NOT_REGISTERED, + + /** + * There are multiple functions or signatures that may fit the provided name and argument types. + */ + AMBIGUOUS, + + /** + * A single function or signature has been found which matches the name and argument types. + */ + EXACT + + } + + /** + * The result of trying to retrieve a function or signature. + *

+ * When getting a function or signature, the following situations may occur. + * These are specified by {@code type}. + *

    + *
  • + * {@code NOT_REGISTERED}. The specified function or signature is not registered. + * Both {@code retrieved} and {@code conflictingArgs} will be null. + *
  • + *
  • + * {@code AMBIGUOUS}. There are multiple functions or signatures that + * may fit the provided name and argument types. + * {@code retrieved} will be null, and {@code conflictingArgs} + * will contain the conflicting function or signature parameters. + *
  • + *
  • + * {@code EXACT}. A single function or signature has been found which matches the name and argument types. + * {@code retrieved} will contain the function or signature, and {@code conflictingArgs} will be null. + *
  • + *
+ *

+ * + * @param result The result of the function or signature retrieval. + * @param retrieved The function or signature that was found if {@code result} is {@code EXACT}. + * @param conflictingArgs The conflicting arguments if {@code result} is {@code AMBIGUOUS}. + */ + public record Retrieval( + @NotNull RetrievalResult result, + T retrieved, + Class[][] conflictingArgs + ) { + } + + /** + * Gets a function from a script. If no local function is found, checks for global functions. + * If {@code namespace} is null, only global functions will be checked. + * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return Information related to the attempt to get the specified function, + * stored in a {@link Retrieval} object. + */ public @NotNull Retrieval> getFunction( @Nullable String namespace, @NotNull String name, @@ -322,11 +295,6 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull return attempt; } - @Override - public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getSignature(@NotNull String name, @NotNull Class... args) { - return getSignature(null, name, args); - } - /** * Gets a function from a namespace. * @@ -373,8 +341,17 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull } } - @Override - public @NonNull Retrieval> getSignature( + /** + * Gets the signature for a function with the given name and arguments. If no local function is found, + * checks for global functions. If {@code namespace} is null, only global signatures will be checked. + * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return The signature for the function with the given name and argument types, or null if no such function exists. + */ + public Retrieval> getSignature( @Nullable String namespace, @NotNull String name, @NotNull Class... args @@ -390,11 +367,6 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull return attempt; } - @Override - public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { - return getSignatures(null, name); - } - /** * Gets the signature for a function with the given name and arguments. If no local function is found, * checks for global functions. If {@code namespace} is null, only global signatures will be checked. @@ -410,7 +382,7 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ - public Retrieval> getExactSignature( + Retrieval> getExactSignature( @Nullable String namespace, @NotNull String name, @NotNull Class... args @@ -430,13 +402,11 @@ public Retrieval> getExactSignature( /** * Gets every signature with the name {@code name}. * This includes global functions and, if {@code namespace} is not null, functions under that namespace (if valid). - * * @param namespace The additional namespace to obtain signatures from. * Usually represents the path of the script this function is registered in. * @param name The name of the signature(s) to obtain. * @return A list of all signatures named {@code name}. */ - @Override public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { Preconditions.checkNotNull(name, "name cannot be null"); @@ -465,8 +435,8 @@ public Retrieval> getExactSignature( * * @param namespace The namespace to get the function from. * @param provided The provided identifier of the function. - * @param exact When false, will convert arguments to different types to attempt to find a match. - * When true, will not convert arguments. + * @param exact When false, will convert arguments to different types to attempt to find a match. + * When true, will not convert arguments. * @return The signature for the function with the given name and argument types, or null if no such signature exists * in the specified namespace. */ @@ -512,8 +482,8 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp * * @param provided The provided function. * @param existing The existing functions with the same name. - * @param exact When false, will convert arguments to different types to attempt to find a match. - * When true, will not convert arguments. + * @param exact When false, will convert arguments to different types to attempt to find a match. + * When true, will not convert arguments. * @return An unmodifiable list of candidates for the provided function. */ private static @Unmodifiable @NotNull Set candidates( @@ -605,15 +575,19 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp return Collections.unmodifiableSet(candidates); } - @Override + /** + * Removes a function's signature from the registry. + * + * @param signature The signature to remove. + */ public void remove(@NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - String name = signature.name(); + String name = signature.getName(); FunctionIdentifier identifier = FunctionIdentifier.of(signature); Namespace namespace; - if (signature.hasModifier(Signature.Modifier.LOCAL)) { + if (signature.isLocal()) { namespace = namespaces.get(new NamespaceIdentifier(signature.namespace())); } else { namespace = namespaces.get(GLOBAL_NAMESPACE); @@ -660,6 +634,15 @@ private void removeUpdateMaps(Namespace namespace, FunctionIdentifier toRemove, */ private record NamespaceIdentifier(@Nullable String name) { + /** + * Returns whether this identifier is for local namespaces. + * + * @return Whether this identifier is for local namespaces. + */ + public boolean local() { + return name == null; + } + } /** @@ -730,7 +713,7 @@ static FunctionIdentifier of(@NotNull Signature signature) { parameters[i] = param.type(); } - return new FunctionIdentifier(signature.name(), signature.hasModifier(Signature.Modifier.LOCAL), + return new FunctionIdentifier(signature.getName(), signature.isLocal(), parameters.length - optionalArgs, parameters); } From b2ba6106bd0a79892ccb19c1ec43af919d71937f Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:52:27 +0200 Subject: [PATCH 19/32] migrate FunctionRegistry to separate impl --- .../lang/function/FunctionRegistry.java | 653 ++---------------- .../common/function/FunctionRegistry.java | 42 +- .../common/function/FunctionRegistryImpl.java | 70 +- 3 files changed, 117 insertions(+), 648 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 1fe91e35bbc..74381b3fe9e 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -1,22 +1,13 @@ package ch.njol.skript.lang.function; -import ch.njol.skript.Skript; -import ch.njol.skript.SkriptAPIException; -import ch.njol.skript.util.Utils; -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import org.skriptlang.skript.common.function.Parameter; -import org.skriptlang.skript.common.function.Parameter.Modifier; -import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.util.Registry; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; +import java.util.Collection; +import java.util.Set; import java.util.stream.Collectors; /** @@ -25,6 +16,13 @@ @ApiStatus.Internal public final class FunctionRegistry implements Registry> { + private static org.skriptlang.skript.common.function.FunctionRegistry newRegistry; + + @ApiStatus.Internal + public static void setNewRegistry(org.skriptlang.skript.common.function.FunctionRegistry newRegistry) { + FunctionRegistry.newRegistry = newRegistry; + } + private static FunctionRegistry registry; /** @@ -39,178 +37,29 @@ public static FunctionRegistry getRegistry() { return registry; } - /** - * The pattern for a valid function name. - * Functions must start with a letter or underscore and can only contain letters, numbers, and underscores. - */ - final static Pattern FUNCTION_NAME_PATTERN = Pattern.compile(Functions.functionNamePattern); - - /** - * The namespace for registered global functions. - */ - private final NamespaceIdentifier GLOBAL_NAMESPACE = new NamespaceIdentifier(null); - - /** - * All registered namespaces. - */ - private final Map namespaces = new ConcurrentHashMap<>(); - @Override public @Unmodifiable @NotNull Collection> elements() { - Set> functions = new HashSet<>(); - - for (Namespace namespace : namespaces.values()) { - functions.addAll(namespace.functions.values()); - } - - return Collections.unmodifiableSet(functions); + return newRegistry.elements().stream() + .map(it -> (Function) it) + .collect(Collectors.toUnmodifiableSet()); } - /** - * Registers a signature. - *

- * Attempting to register a local signature in the global namespace, or a global signature in - * a local namespace, will throw an {@link IllegalArgumentException}. - * If {@code namespace} is null, will register this signature globally, - * only if the signature is global. - *

- * - * @param namespace The namespace to register the signature in. - * Usually represents the path of the script this signature is registered in. - * @param signature The signature to register. - * @throws SkriptAPIException if a signature with the same name and parameters is already registered - * in this namespace. - * @throws IllegalArgumentException if the signature is global and namespace is not null, or - * if the signature is local and namespace is null. - */ public void register(@Nullable String namespace, @NotNull Signature signature) { - Preconditions.checkNotNull(signature, "signature cannot be null"); - if (signature.isLocal() && namespace == null) { - throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); - } - if (!signature.isLocal() && namespace != null) { - throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); - } - - Skript.debug("Registering signature '%s'", signature.getName()); - - // namespace - NamespaceIdentifier namespaceId; - if (namespace != null) { - namespaceId = new NamespaceIdentifier(namespace); + if (namespace == null) { + newRegistry.register(signature); } else { - namespaceId = GLOBAL_NAMESPACE; - } - - Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); - FunctionIdentifier identifier = FunctionIdentifier.of(signature); - - // register - // since we are getting a set and then updating it, - // avoid race conditions by ensuring only one thread can access this namespace for this operation - synchronized (ns) { - Set identifiersWithName = ns.identifiers.computeIfAbsent(identifier.name, s -> new HashSet<>()); - boolean exists = identifiersWithName.add(identifier); - if (!exists) { - alreadyRegisteredError(signature.getName(), identifier, namespaceId); - } - } - - Signature existing = ns.signatures.putIfAbsent(identifier, signature); - if (existing != null) { - alreadyRegisteredError(signature.getName(), identifier, namespaceId); + newRegistry.register(namespace, signature); } } - /** - * Registers a function. - *

- * Attempting to register a local function in the global namespace, or a global function in - * a local namespace, will throw an {@link IllegalArgumentException}. - * If {@code namespace} is null, will register this function globally, - * only if the function is global. - *

- * - * @param namespace The namespace to register the function in. - * Usually represents the path of the script this function is registered in. - * @param function The function to register. - * @throws SkriptAPIException if the function name is invalid or if - * a function with the same name and parameters is already registered - * in this namespace. - * @throws IllegalArgumentException if the function is global and namespace is not null, or - * if the function is local and namespace is null. - */ public void register(@Nullable String namespace, @NotNull Function function) { - Preconditions.checkNotNull(function, "function cannot be null"); - if (function.getSignature().isLocal() && namespace == null) { - throw new IllegalArgumentException("Cannot register a local function in the global namespace"); - } - if (!function.getSignature().isLocal() && namespace != null) { - throw new IllegalArgumentException("Cannot register a global function in a local namespace"); - } - Skript.debug("Registering function '%s'", function.getName()); - - String name = function.getName(); - if (!FUNCTION_NAME_PATTERN.matcher(name).matches()) { - throw new SkriptAPIException("Invalid function name '" + name + "'"); - } - - // namespace - NamespaceIdentifier namespaceId; - if (namespace != null) { - namespaceId = new NamespaceIdentifier(namespace); + if (namespace == null) { + newRegistry.register(function); } else { - namespaceId = GLOBAL_NAMESPACE; - } - - FunctionIdentifier identifier = FunctionIdentifier.of(function.getSignature()); - if (!signatureExists(namespaceId, identifier)) { - register(namespace, function.getSignature()); - } - - Namespace ns = namespaces.computeIfAbsent(namespaceId, n -> new Namespace()); - - Function existing = ns.functions.putIfAbsent(identifier, function); - if (existing != null) { - alreadyRegisteredError(name, identifier, namespaceId); + newRegistry.register(namespace, function); } } - private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, NamespaceIdentifier namespace) { - throw new SkriptAPIException("Function '%s' with parameters %s is already registered in %s" - .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), - namespace)); - } - - /** - * Checks if a function with the given name and arguments exists in the namespace. - * - * @param namespace The namespace to check in. - * @param identifier The identifier of the function. - * @return True if a function with the given name and arguments exists in the namespace, false otherwise. - */ - private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull FunctionIdentifier identifier) { - Preconditions.checkNotNull(namespace, "namespace cannot be null"); - Preconditions.checkNotNull(identifier, "identifier cannot be null"); - - Namespace ns = namespaces.get(namespace); - if (ns == null) { - return false; - } - - if (!ns.identifiers.containsKey(identifier.name)) { - return false; - } - - for (FunctionIdentifier other : ns.identifiers.get(identifier.name)) { - if (identifier.equals(other)) { - return true; - } - } - - return false; - } - /** * The result of attempting to retrieve a function. * Depending on the type, a {@link Retrieval} will feature different data. @@ -268,103 +117,36 @@ public record Retrieval( ) { } - /** - * Gets a function from a script. If no local function is found, checks for global functions. - * If {@code namespace} is null, only global functions will be checked. - * - * @param namespace The namespace to get the function from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the function. - * @param args The types of the arguments of the function. - * @return Information related to the attempt to get the specified function, - * stored in a {@link Retrieval} object. - */ public @NotNull Retrieval> getFunction( @Nullable String namespace, @NotNull String name, @NotNull Class... args ) { - Retrieval> attempt = null; - if (namespace != null) { - attempt = getFunction(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args)); - } - if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { - attempt = getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); - } - return attempt; - } + org.skriptlang.skript.common.function.FunctionRegistry.Retrieval> retrieval; - /** - * Gets a function from a namespace. - * - * @param namespace The namespace to get the function from. - * Usually represents the path of the script this function is registered in. - * @param provided The provided identifier of the function. - * @return Information related to the attempt to get the specified function, - * stored in a {@link Retrieval} object. - */ - private @NotNull Retrieval> getFunction(@NotNull NamespaceIdentifier namespace, @NotNull FunctionIdentifier provided) { - Preconditions.checkNotNull(namespace, "namespace cannot be null"); - Preconditions.checkNotNull(provided, "provided cannot be null"); - - Namespace ns = namespaces.getOrDefault(namespace, new Namespace()); - Set existing = ns.identifiers.get(provided.name); - if (existing == null) { - Skript.debug("No functions named '%s' exist in the '%s' namespace", provided.name, namespace.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); - } - - Set candidates = candidates(provided, existing, false); - if (candidates.isEmpty()) { - Skript.debug("Failed to find a function for '%s'", provided.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); - } else if (candidates.size() == 1) { - if (Skript.debug()) { - Skript.debug("Matched function for '%s': %s", provided.name, candidates.stream().findAny().orElse(null)); - } - return new Retrieval<>(RetrievalResult.EXACT, - ns.functions.get(candidates.stream().findAny().orElse(null)), - null); + if (namespace == null) { + retrieval = newRegistry.getFunction(name, args); } else { - if (Skript.debug()) { - String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); - Skript.debug("Failed to match an exact function for '%s'", provided.name); - Skript.debug("Identifier: %s", provided); - Skript.debug("Options: %s", options); - } - return new Retrieval<>(RetrievalResult.AMBIGUOUS, - null, - candidates.stream() - .map(FunctionIdentifier::args) - .toArray(Class[][]::new)); + retrieval = newRegistry.getFunction(namespace, name, args); } + + return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Function) retrieval.retrieved(), retrieval.conflictingArgs()); } - /** - * Gets the signature for a function with the given name and arguments. If no local function is found, - * checks for global functions. If {@code namespace} is null, only global signatures will be checked. - * - * @param namespace The namespace to get the function from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the function. - * @param args The types of the arguments of the function. - * @return The signature for the function with the given name and argument types, or null if no such function exists. - */ public Retrieval> getSignature( @Nullable String namespace, @NotNull String name, @NotNull Class... args ) { - Retrieval> attempt = null; - if (namespace != null) { - attempt = getSignature(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args), false); - } - if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { - attempt = getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), false); + org.skriptlang.skript.common.function.FunctionRegistry.Retrieval> retrieval; + + if (namespace == null) { + retrieval = newRegistry.getSignature(name, args); + } else { + retrieval = newRegistry.getSignature(namespace, name, args); } - return attempt; + + return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Signature) retrieval.retrieved(), retrieval.conflictingArgs()); } /** @@ -387,374 +169,33 @@ Retrieval> getExactSignature( @NotNull String name, @NotNull Class... args ) { - Retrieval> attempt = null; - if (namespace != null) { - attempt = getSignature(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args), true); - } - - if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { - attempt = getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), true); - } - return attempt; - } - - /** - * Gets every signature with the name {@code name}. - * This includes global functions and, if {@code namespace} is not null, functions under that namespace (if valid). - * @param namespace The additional namespace to obtain signatures from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the signature(s) to obtain. - * @return A list of all signatures named {@code name}. - */ - public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { - Preconditions.checkNotNull(name, "name cannot be null"); - - Map> total = new HashMap<>(); - - // obtain all local functions of "name" - if (namespace != null) { - Namespace local = namespaces.getOrDefault(new NamespaceIdentifier(namespace), new Namespace()); + org.skriptlang.skript.common.function.FunctionRegistry.Retrieval> retrieval; - for (FunctionIdentifier identifier : local.identifiers.getOrDefault(name, Collections.emptySet())) { - total.putIfAbsent(identifier, local.signatures.get(identifier)); - } - } - - // obtain all global functions of "name" - Namespace global = namespaces.getOrDefault(GLOBAL_NAMESPACE, new Namespace()); - for (FunctionIdentifier identifier : global.identifiers.getOrDefault(name, Collections.emptySet())) { - total.putIfAbsent(identifier, global.signatures.get(identifier)); - } - - return Set.copyOf(total.values()); - } - - /** - * Gets the signature for a function with the given name and arguments. - * - * @param namespace The namespace to get the function from. - * @param provided The provided identifier of the function. - * @param exact When false, will convert arguments to different types to attempt to find a match. - * When true, will not convert arguments. - * @return The signature for the function with the given name and argument types, or null if no such signature exists - * in the specified namespace. - */ - private Retrieval> getSignature(@NotNull NamespaceIdentifier namespace, @NotNull FunctionIdentifier provided, boolean exact) { - Preconditions.checkNotNull(namespace, "namespace cannot be null"); - Preconditions.checkNotNull(provided, "provided cannot be null"); - - Namespace ns = namespaces.getOrDefault(namespace, new Namespace()); - if (!ns.identifiers.containsKey(provided.name)) { - Skript.debug("No signatures named '%s' exist in the '%s' namespace", provided.name, namespace.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); - } - - Set candidates = candidates(provided, ns.identifiers.get(provided.name), exact); - if (candidates.isEmpty()) { - Skript.debug("Failed to find a signature for '%s'", provided.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); - } else if (candidates.size() == 1) { - if (Skript.debug()) { - Skript.debug("Matched signature for '%s': %s", - provided.name, ns.signatures.get(candidates.stream().findAny().orElse(null))); - } - return new Retrieval<>(RetrievalResult.EXACT, - ns.signatures.get(candidates.stream().findAny().orElse(null)), - null); + if (namespace == null) { + retrieval = newRegistry.getSignature(name, args); } else { - if (Skript.debug()) { - String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); - Skript.debug("Failed to match an exact signature for '%s'", provided.name); - Skript.debug("Identifier: %s", provided); - Skript.debug("Options: %s", options); - } - return new Retrieval<>(RetrievalResult.AMBIGUOUS, - null, - candidates.stream() - .map(FunctionIdentifier::args) - .toArray(Class[][]::new)); + retrieval = newRegistry.getSignature(namespace, name, args); } - } - - /** - * Returns an unmodifiable list of candidates for the provided function identifier. - * - * @param provided The provided function. - * @param existing The existing functions with the same name. - * @param exact When false, will convert arguments to different types to attempt to find a match. - * When true, will not convert arguments. - * @return An unmodifiable list of candidates for the provided function. - */ - private static @Unmodifiable @NotNull Set candidates( - @NotNull FunctionIdentifier provided, - Set existing, - boolean exact - ) { - Set candidates = new HashSet<>(); - - candidates: - for (FunctionIdentifier candidate : existing) { - // by this point, all candidates have matching names - - if (Arrays.stream(candidate.args).filter(Class::isArray).count() == 1 - && candidate.args.length == 1 - && candidate.args[0].isArray()) { - // if a function has single list value param, check all types - // make sure all types in the passed array are valid for the array parameter - Class arrayType = candidate.args[0].componentType(); - for (Class arrayArg : provided.args) { - arrayArg = Utils.getComponentType(arrayArg); - - if (!Converters.converterExists(arrayArg, arrayType)) { - continue candidates; - } - } - - candidates.add(candidate); - continue; - } - - // if argument counts are not possible, skip - if (provided.args.length > candidate.args.length - || provided.args.length < candidate.minArgCount) { - continue; - } - - // if the types of the provided arguments do not match the candidate arguments, skip - for (int i = 0; i < provided.args.length; i++) { - // allows single passed values to still match array type in candidate (e.g. clamp) - Class providedType = Utils.getComponentType(provided.args[i]); - - Class candidateType = Utils.getComponentType(candidate.args[i]); - Class providedArg = provided.args[i]; - if (exact) { - if (providedArg != candidateType) { - continue candidates; - } - } else { - if (!Converters.converterExists(providedType, candidateType)) { - continue candidates; - } - } - } - candidates.add(candidate); - } - - if (candidates.size() <= 1) { - // if there is only one candidate, then return without trying to convert - return Collections.unmodifiableSet(candidates); - } - - // let overloaded(Long, Long) and overloaded(String, String) be two functions. - // the code below allows overloaded(1, {_x}) to match Long, Long and avoid String, String, - // and allow overloaded({_x}, 1) to match Long, Long and avoid String, String - // despite not being an exact match in all arguments, - // since variables have an unknown type at runtime. - Iterator iterator = candidates.iterator(); - while (iterator.hasNext()) { - FunctionIdentifier candidate = iterator.next(); - int argIndex = 0; - - while (argIndex < provided.args.length) { - if (provided.args[argIndex] == Object.class) { - argIndex++; - continue; - } - - if (provided.args[argIndex] != candidate.args[argIndex]) { - iterator.remove(); - break; - } - - argIndex++; - } - } - - return Collections.unmodifiableSet(candidates); + return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Signature) retrieval.retrieved(), retrieval.conflictingArgs()); } - /** - * Removes a function's signature from the registry. - * - * @param signature The signature to remove. - */ - public void remove(@NotNull Signature signature) { - Preconditions.checkNotNull(signature, "signature cannot be null"); - - String name = signature.getName(); - FunctionIdentifier identifier = FunctionIdentifier.of(signature); - - Namespace namespace; - if (signature.isLocal()) { - namespace = namespaces.get(new NamespaceIdentifier(signature.namespace())); - } else { - namespace = namespaces.get(GLOBAL_NAMESPACE); - } + public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { + Set> signatures; if (namespace == null) { - return; - } - - for (FunctionIdentifier other : namespace.identifiers.getOrDefault(name, Set.of())) { - if (!identifier.equals(other)) { - continue; - } - - removeUpdateMaps(namespace, other, name); - return; - } - } - - /** - * Updates the maps by removing the provided function identifier from the maps. - * - * @param namespace The namespace - * @param toRemove The identifier to remove - * @param name The name of the function - */ - private void removeUpdateMaps(Namespace namespace, FunctionIdentifier toRemove, String name) { - namespace.identifiers.computeIfPresent(name, (k, set) -> { - if (set.remove(toRemove)) { - Skript.debug("Removed identifier '%s' from %s", toRemove, namespace); - } - return set.isEmpty() ? null : set; - }); - if (namespace.functions.remove(toRemove) != null) { - Skript.debug("Removed function '%s' from %s", toRemove, namespace); - } - if (namespace.signatures.remove(toRemove) != null) { - Skript.debug("Removed signature '%s' from %s", toRemove, namespace); - } - } - - /** - * An identifier for a function namespace. - */ - private record NamespaceIdentifier(@Nullable String name) { - - /** - * Returns whether this identifier is for local namespaces. - * - * @return Whether this identifier is for local namespaces. - */ - public boolean local() { - return name == null; + signatures = newRegistry.getSignatures(name); + } else { + signatures = newRegistry.getSignatures(namespace, name); } + return signatures.stream() + .map(it -> (Signature) it) + .collect(Collectors.toUnmodifiableSet()); } - /** - * The data a namespace contains. - */ - private static final class Namespace { - - /** - * Map for all function names to their identifiers, allowing for quicker lookup. - */ - private final Map> identifiers = new HashMap<>(); - - /** - * Map for all identifier to function combinations. - */ - private final Map> functions = new HashMap<>(); - - /** - * Map for all identifier to signature combinations. - */ - private final Map> signatures = new HashMap<>(); - - } - - /** - * An identifier for a function. - *

Used to differentiate between functions with the same name but different parameters.

- * - * @param name The name of the function. - * @param args The arguments of the function. - */ - record FunctionIdentifier(@NotNull String name, boolean local, int minArgCount, - @NotNull Class... args) { - - /** - * Returns the identifier for the given arguments. - * - * @param name The name of the function. - * @param args The types of the arguments. - * @return The identifier for the signature. - */ - static FunctionIdentifier of(@NotNull String name, boolean local, @NotNull Class... args) { - Preconditions.checkNotNull(name, "name cannot be null"); - Preconditions.checkNotNull(args, "args cannot be null"); - - return new FunctionIdentifier(name, local, args.length, args); - } - - /** - * Returns the identifier for the given signature. - * - * @param signature The signature to get the identifier for. - * @return The identifier for the signature. - */ - static FunctionIdentifier of(@NotNull Signature signature) { - Preconditions.checkNotNull(signature, "signature cannot be null"); - - Parameter[] signatureParams = signature.parameters().all(); - Class[] parameters = new Class[signatureParams.length]; - - int optionalArgs = 0; - for (int i = 0; i < signatureParams.length; i++) { - Parameter param = signatureParams[i]; - if (param.hasModifier(Modifier.OPTIONAL)) { - optionalArgs++; - } - - parameters[i] = param.type(); - } - - return new FunctionIdentifier(signature.getName(), signature.isLocal(), - parameters.length - optionalArgs, parameters); - } - - @Override - public int hashCode() { - return Objects.hash(name, Arrays.hashCode(args)); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof FunctionIdentifier other)) { - return false; - } - - if (!name.equals(other.name)) { - return false; - } - - if (args.length != other.args.length) { - return false; - } - - for (int i = 0; i < args.length; i++) { - if (args[i] != other.args[i]) { - return false; - } - } - - return true; - } - - @Override - public @NotNull String toString() { - return MoreObjects.toStringHelper(this) - .add("name", name) - .add("local", local) - .add("minArgCount", minArgCount) - .add("args", Arrays.stream(args).map(Class::getSimpleName).collect(Collectors.joining(", "))) - .toString(); - } - + public void remove(@NotNull Signature signature) { + newRegistry.remove(signature); } } diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java index 4be635f48f4..13db9e0b13a 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java @@ -8,7 +8,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.Skript; -import org.skriptlang.skript.addon.SkriptAddon; import org.skriptlang.skript.util.Registry; import org.skriptlang.skript.util.ViewProvider; @@ -34,7 +33,7 @@ public interface FunctionRegistry extends Registry>, ViewProvider new") static FunctionRegistry empty(Skript skript) { - return new ch.njol.skript.lang.function.FunctionRegistry(skript); + return new FunctionRegistryImpl(skript); } /** @@ -199,6 +198,45 @@ record Retrieval( @NotNull Class... args ); + /** + * Gets the signature for a function with the given name and arguments. + * Only checks for global functions. + *

+ * This function checks performs no argument conversions, and is only used for determining whether a + * signature already exists with the exact specified arguments. In almost all cases, {@link #getSignature(String, Class[])} + * should be used. + *

+ * + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return The signature for the function with the given name and argument types, or null if no such function exists. + */ + Retrieval> getExactSignature( + @NotNull String name, + @NotNull Class... args + ); + + /** + * Gets the signature for a function with the given name and arguments. If no local function is found, + * checks for global functions. + *

+ * This function checks performs no argument conversions, and is only used for determining whether a + * signature already exists with the exact specified arguments. In almost all cases, {@link #getSignature(String, String, Class[])} + * should be used. + *

+ * + * @param namespace The namespace to get the function from. + * Usually represents the path of the script this function is registered in. + * @param name The name of the function. + * @param args The types of the arguments of the function. + * @return The signature for the function with the given name and argument types, or null if no such function exists. + */ + Retrieval> getExactSignature( + @NotNull String namespace, + @NotNull String name, + @NotNull Class... args + ); + /** * Gets every signature with the name {@code name} in the global namespace. * diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java index 7ea4dc56ab3..2f3d4e6e84b 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java @@ -1,4 +1,4 @@ -package ch.njol.skript.lang.function; +package org.skriptlang.skript.common.function; import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; @@ -11,14 +11,12 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.jspecify.annotations.NonNull; -import org.skriptlang.skript.common.function.Function; -import org.skriptlang.skript.common.function.Parameter; import org.skriptlang.skript.common.function.Parameter.Modifier; -import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.lang.converter.Converters; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -93,25 +91,35 @@ public void register(@NotNull String namespace, @NotNull Function function) { } @Override - public @NotNull Retrieval> getFunction(@NotNull String name, @NonNull @NotNull Class... args) { + public @NotNull Retrieval> getFunction(@NotNull String name, @NotNull Class... args) { return registry.getFunction(name, args); } @Override - public @NotNull Retrieval> getFunction(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + public @NotNull Retrieval> getFunction(@NotNull String namespace, @NotNull String name, @NotNull Class... args) { return registry.getFunction(namespace, name, args); } @Override - public @NotNull Retrieval> getSignature(@NotNull String name, @NonNull @NotNull Class... args) { + public @NotNull Retrieval> getSignature(@NotNull String name, @NotNull Class... args) { return registry.getSignature(name, args); } @Override - public @NotNull Retrieval> getSignature(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + public @NotNull Retrieval> getSignature(@NotNull String namespace, @NotNull String name, @NotNull Class... args) { return registry.getSignature(namespace, name, args); } + @Override + public Retrieval> getExactSignature(@NotNull String name, @NonNull @NotNull Class... args) { + return registry.getExactSignature(name, args); + } + + @Override + public Retrieval> getExactSignature(@NotNull String namespace, @NotNull String name, @NonNull @NotNull Class... args) { + return registry.getExactSignature(namespace, name, args); + } + @Override public @Unmodifiable @NotNull Set> getSignatures(@NotNull String name) { return registry.getSignatures(name); @@ -360,7 +368,7 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull } @Override - public @NonNull Retrieval> getSignature( + public @NotNull Retrieval> getSignature( @Nullable String namespace, @NotNull String name, @NotNull Class... args @@ -381,47 +389,29 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull return getSignatures(null, name); } - /** - * Gets the signature for a function with the given name and arguments. If no local function is found, - * checks for global functions. If {@code namespace} is null, only global signatures will be checked. - *

- * This function checks performs no argument conversions, and is only used for determining whether a - * signature already exists with the exact specified arguments. In almost all cases, {@link #getSignature(String, String, Class[])} - * should be used. - *

- * - * @param namespace The namespace to get the function from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the function. - * @param args The types of the arguments of the function. - * @return The signature for the function with the given name and argument types, or null if no such function exists. - */ + @Override public Retrieval> getExactSignature( - @Nullable String namespace, @NotNull String name, @NotNull Class... args ) { - Retrieval> attempt = null; - if (namespace != null) { - attempt = getSignature(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args), true); - } + return getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), true); + } - if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { + @Override + public Retrieval> getExactSignature( + @NotNull String namespace, + @NotNull String name, + @NotNull Class... args + ) { + Retrieval> attempt = getSignature(new NamespaceIdentifier(namespace), + FunctionIdentifier.of(name, true, args), true); + + if (attempt.result() == RetrievalResult.NOT_REGISTERED) { attempt = getSignature(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args), true); } return attempt; } - /** - * Gets every signature with the name {@code name}. - * This includes global functions and, if {@code namespace} is not null, functions under that namespace (if valid). - * - * @param namespace The additional namespace to obtain signatures from. - * Usually represents the path of the script this function is registered in. - * @param name The name of the signature(s) to obtain. - * @return A list of all signatures named {@code name}. - */ @Override public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { Preconditions.checkNotNull(name, "name cannot be null"); From 737ae28b02d564244b8345ebb8ac4f874d42dcc9 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:52:31 +0200 Subject: [PATCH 20/32] fix tests --- src/main/java/ch/njol/skript/Skript.java | 4 +- .../njol/skript/lang/function/Functions.java | 18 ++-- .../njol/skript/lang/function/Signature.java | 4 + .../skript/structures/StructFunction.java | 4 +- .../common/function/DefaultFunction.java | 9 ++ .../common/function/DefaultFunctionImpl.java | 21 +++-- .../common/function/FunctionReference.java | 28 ++++-- .../function/FunctionReferenceParser.java | 51 +++++------ .../function/FunctionRegistryTest.java | 85 +++++++++++-------- 9 files changed, 138 insertions(+), 86 deletions(-) rename src/test/java/{ch/njol/skript/lang => org/skriptlang/skript/common}/function/FunctionRegistryTest.java (86%) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index d776306f9f0..1cee840c141 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -481,7 +481,9 @@ public void onEnable() { //noinspection removal EventValues.setEventValueRegistry(eventValueRegistry); - skript.storeRegistry(FunctionRegistry.class, FunctionRegistry.empty(skript)); + FunctionRegistry functionRegistry = FunctionRegistry.empty(skript); + skript.storeRegistry(FunctionRegistry.class, functionRegistry); + ch.njol.skript.lang.function.FunctionRegistry.setNewRegistry(functionRegistry); // TODO this upcoming portion is a bad circular dependency // some modules depend on the config 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 f42e4a7bda8..1657677d709 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -4,13 +4,13 @@ import ch.njol.skript.SkriptAPIException; import ch.njol.skript.SkriptAddon; import ch.njol.skript.config.SectionNode; +import ch.njol.skript.lang.function.FunctionRegistry.Retrieval; +import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; import ch.njol.skript.structures.StructFunction; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.DefaultFunction; import org.skriptlang.skript.common.function.FunctionParser; import org.skriptlang.skript.common.function.FunctionReference; -import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; -import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import org.skriptlang.skript.common.function.Parameter; import org.skriptlang.skript.common.function.Signature.Modifier; import org.skriptlang.skript.lang.script.Script; @@ -23,7 +23,8 @@ */ public abstract class Functions { - private Functions() {} + private Functions() { + } /** * @deprecated Unused. @@ -70,7 +71,7 @@ public static DefaultFunction register(DefaultFunction function) { } globalFunctions.put(function.name(), javaNamespace); - FunctionRegistry.getRegistry().register(function); + FunctionRegistry.getRegistry().register(null, (Function) function); return function; } @@ -91,7 +92,7 @@ public static JavaFunction registerFunction(JavaFunction function) { } globalFunctions.put(function.getName(), javaNamespace); - FunctionRegistry.getRegistry().register(function); + FunctionRegistry.getRegistry().register(null, function); return function; } @@ -130,7 +131,7 @@ public static JavaFunction registerFunction(JavaFunction function) { if (function.getSignature().isLocal()) { FunctionRegistry.getRegistry().register(script.getConfig().getFileName(), function); } else { - FunctionRegistry.getRegistry().register(function); + FunctionRegistry.getRegistry().register(null, function); } return function; @@ -149,7 +150,7 @@ public static JavaFunction registerFunction(JavaFunction function) { */ @Deprecated(forRemoval = true, since = "INSERT VERSION") public static @Nullable Signature registerSignature(Signature signature) { - Retrieval> existing; + Retrieval> existing; Parameter[] parameters = signature.parameters().all(); if (parameters.length == 1 && !parameters[0].isSingle()) { @@ -196,7 +197,7 @@ public static JavaFunction registerFunction(JavaFunction function) { if (signature.isLocal()) { FunctionRegistry.getRegistry().register(signature.namespace(), signature); } else { - FunctionRegistry.getRegistry().register(signature); + FunctionRegistry.getRegistry().register(null, signature); } Skript.debug("Registered function signature: " + signature.getName()); @@ -228,7 +229,6 @@ public static JavaFunction registerFunction(JavaFunction function) { return function; } - /** * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getFunction(String, Class[])} instead. */ diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index 5a1441d9006..8fecc8e8e20 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -136,6 +136,10 @@ public Signature(String namespace, String name, org.skriptlang.skript.common.fun this(namespace, name, initParameters(parameters), returnType, false, contract); } + public Signature(String namespace, String name, org.skriptlang.skript.common.function.Parameter[] parameters, Class returnType, boolean local, boolean single, @Nullable Contract contract) { + this(namespace, name, initParameters(parameters), returnType, local, contract); + } + @Override public @NotNull String name() { return name; diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index 08ca5c67ea0..66a21666ba3 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -122,7 +122,7 @@ private static Signature registerSignature(Signature signature) { if (signature.isLocal()) { ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature.namespace(), signature); } else { - ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature); + ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(null, signature); } } catch (SkriptAPIException ex) { Skript.error(ex.getMessage()); @@ -163,7 +163,7 @@ private static void loadFunction(Script script, SectionNode node, Signature s if (function.getSignature().isLocal()) { FunctionRegistry.getRegistry().register(script.getConfig().getFileName(), function); } else { - FunctionRegistry.getRegistry().register(function); + FunctionRegistry.getRegistry().register(null, function); } } diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java index ea806f86a0d..6a449908ca7 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java @@ -61,6 +61,15 @@ public sealed interface DefaultFunction */ interface Builder { + /** + * Designates this function to be local. + * + * @param namespace The namespace for this function. + * @return This builder. + */ + @Contract("_ -> this") + Builder local(@NotNull String namespace); + /** * Sets this function builder's {@link ch.njol.skript.util.Contract}. * diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index ff315999c30..b2e766af720 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -29,6 +29,7 @@ final class DefaultFunctionImpl extends ch.njol.skript.lang.function.Function DefaultFunctionImpl( SkriptAddon source, String name, + String namespace, SequencedMap> parameters, Class returnType, boolean single, @Nullable ch.njol.skript.util.Contract contract, @@ -36,7 +37,8 @@ final class DefaultFunctionImpl extends ch.njol.skript.lang.function.Function String[] description, String[] since, String[] examples, String[] keywords, String[] requires ) { - super(new Signature<>(null, name, parameters.values().toArray(new Parameter[0]), returnType, single, contract)); + super(new Signature<>(namespace, name, parameters.values().toArray(new Parameter[0]), + returnType, namespace != null, single, contract)); Preconditions.checkNotNull(source, "source cannot be null"); Preconditions.checkNotNull(name, "name cannot be null"); @@ -188,6 +190,7 @@ static class BuilderImpl implements DefaultFunctionImpl.Builder { private final Class returnType; private final SequencedMap> parameters = new LinkedHashMap<>(); + private String namespace = null; private ch.njol.skript.util.Contract contract = null; private String[] description; @@ -206,6 +209,14 @@ static class BuilderImpl implements DefaultFunctionImpl.Builder { this.returnType = returnType; } + @Override + public Builder local(@NotNull String namespace) { + Preconditions.checkNotNull(namespace, "namespace cannot be null"); + + this.namespace = namespace; + return this; + } + @Override public Builder contract(@NotNull ch.njol.skript.util.Contract contract) { Preconditions.checkNotNull(contract, "contract cannot be null"); @@ -272,7 +283,7 @@ public Builder parameter(@NotNull String name, @NotNull Class type, Modifi public DefaultFunction build(@NotNull Function execute) { Preconditions.checkNotNull(execute, "execute cannot be null"); - return new DefaultFunctionImpl<>(source, name, parameters, + return new DefaultFunctionImpl<>(source, name, namespace, parameters, returnType, !returnType.isArray(), contract, execute, description, since, examples, keywords, requires); } @@ -293,10 +304,10 @@ private static void checkNotNull(@NotNull String[] strings, @NotNull String mess /** * A parameter for a {@link DefaultFunction}. * - * @param name The name. - * @param type The type's class. + * @param name The name. + * @param type The type's class. * @param modifiers The modifiers. - * @param The type. + * @param The type. */ record DefaultParameter(String name, Class type, Set modifiers) implements Parameter { diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java index 4179fbddeae..d72c57715cf 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java @@ -3,7 +3,6 @@ import ch.njol.skript.Skript; import ch.njol.skript.lang.*; import ch.njol.skript.lang.function.FunctionEvent; -import ch.njol.skript.lang.function.FunctionRegistry; import ch.njol.skript.lang.function.Functions; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.LiteralUtils; @@ -64,9 +63,16 @@ public FunctionReference(@Nullable String namespace, public boolean validate() { if (!validSignature) { Class[] parameters = Arrays.stream(cachedSignature.parameters().all()) - .map(Parameter::type) - .toArray(Class[]::new); - var result = FunctionRegistry.getRegistry().getSignature(namespace, name, parameters); + .map(Parameter::type) + .toArray(Class[]::new); + + Retrieval> result; + if (namespace == null) { + result = Skript.instance().registry(FunctionRegistry.class).getSignature(name, parameters); + } else { + result = Skript.instance().registry(FunctionRegistry.class).getSignature(namespace, name, parameters); + } + if (result.result() == RetrievalResult.EXACT) { //noinspection unchecked cachedSignature = (Signature) result.retrieved(); @@ -77,9 +83,9 @@ public boolean validate() { if (!printedInvalidSignatureWarning) { printedInvalidSignatureWarning = true; Skript.warning(String.format("The function '%s' from the script '%s' no longer exists." - + " Skript will continue to use the old function until this function is registered again." - + " Function call: %s", - name, namespace, toString(null, false))); + + " Skript will continue to use the old function until this function is registered again." + + " Function call: %s", + name, namespace, toString(null, false))); } } } @@ -253,7 +259,13 @@ public Function function() { .map(Parameter::type) .toArray(Class[]::new); - Retrieval> retrieval = FunctionRegistry.getRegistry().getFunction(namespace, name, parameters); + Retrieval> retrieval; + if (namespace == null) { + retrieval = Skript.instance().registry(FunctionRegistry.class).getFunction(name, parameters); + } else { + retrieval = Skript.instance().registry(FunctionRegistry.class).getFunction(namespace, name, parameters); + } + if (retrieval.result() == RetrievalResult.EXACT) { //noinspection unchecked cachedFunction = (Function) retrieval.retrieved(); diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java index fa09a082afb..d72d1bd23ab 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java @@ -6,7 +6,6 @@ import ch.njol.skript.lang.ExpressionList; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.SkriptParser; -import ch.njol.skript.lang.function.FunctionRegistry; import ch.njol.skript.lang.parser.ParserInstance; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.localization.ArgsMessage; @@ -118,16 +117,17 @@ public FunctionReference parseFunctionReference(String name, FunctionRefe } ParserInstance parser = ParserInstance.get(); + Set> options; String namespace; if (parser.isActive()) { namespace = parser.getCurrentScript().getConfig().getFileName(); + options = Skript.instance().registry(FunctionRegistry.class).getSignatures(namespace, name); } else { namespace = null; + options = Skript.instance().registry(FunctionRegistry.class).getSignatures(name); } // try to find a matching signature to get which types to parse args with - Set> options = FunctionRegistry.getRegistry().getSignatures(namespace, name); - if (options.isEmpty()) { doesNotExist(name, arguments, options); log.printError(); @@ -293,7 +293,8 @@ private Set> getExactReferences( int lastCheck = -1; // the index of the last unnamed argument slot that was filled int lastFilled = -1; - fill: for (int i = 0; i < arguments.length; i++) { + fill: + for (int i = 0; i < arguments.length; i++) { Argument argument = arguments[i]; if (argument.type() == ArgumentType.NAMED) { priorNames.add(argument.name()); @@ -482,8 +483,8 @@ private void ambiguousError(String name, Set> reference /** * Prints the error for when a function does not exist. * - * @param name The function name. - * @param arguments The passed arguments to the function call. + * @param name The function name. + * @param arguments The passed arguments to the function call. * @param possibleSignatures A set of signatures that may contain what the user intended to match. */ private void doesNotExist(String name, FunctionReference.Argument[] arguments, Set> possibleSignatures) { @@ -519,22 +520,22 @@ private void doesNotExist(String name, FunctionReference.Argument[] argu String possibleMatch = ""; var intended = possibleSignatures.stream() - .filter(signature -> { // filter for signatures that contain all known arguments - List> currentArgumentTypes = new ArrayList<>(argumentTypes); - Arrays.stream(signature.parameters().all()) - .map(parameter -> Utils.getComponentType(parameter.type())) - .forEach(type -> { - var iterator = currentArgumentTypes.iterator(); - while (iterator.hasNext()) { - if (type.isAssignableFrom(iterator.next())) { - iterator.remove(); - break; - } - } - }); - return currentArgumentTypes.isEmpty(); - }) - .min(Comparator.comparingInt(signature -> Math.abs(arguments.length - signature.parameters().maxCount()))); + .filter(signature -> { // filter for signatures that contain all known arguments + List> currentArgumentTypes = new ArrayList<>(argumentTypes); + Arrays.stream(signature.parameters().all()) + .map(parameter -> Utils.getComponentType(parameter.type())) + .forEach(type -> { + var iterator = currentArgumentTypes.iterator(); + while (iterator.hasNext()) { + if (type.isAssignableFrom(iterator.next())) { + iterator.remove(); + break; + } + } + }); + return currentArgumentTypes.isEmpty(); + }) + .min(Comparator.comparingInt(signature -> Math.abs(arguments.length - signature.parameters().maxCount()))); if (intended.isPresent()) { possibleMatch = " " + POTENTIAL_SIGNATURE.toString(intended.get().toString()); } @@ -624,10 +625,10 @@ private ArgumentParseResult parseFunctionArguments(FunctionReference.Argument parseExpression(Argument argument, ArgumentParseTarget targetData) { if (argument.value() == null) { @@ -640,7 +641,7 @@ private Expression parseExpression(Argument argument, ArgumentParseTa Expression expression = parser.parseExpression(targetData.type()); if (expression == null) { logHandler.printError(INVALID_ARGUMENT.toString( - argument.name(), Classes.getSuperClassInfo(targetData.type()).getName().getSingular(), argument.value() + argument.name(), Classes.getSuperClassInfo(targetData.type()).getName().getSingular(), argument.value() )); } return expression; diff --git a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java b/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java similarity index 86% rename from src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java rename to src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java index 2db41c41c72..133ccb21730 100644 --- a/src/test/java/ch/njol/skript/lang/function/FunctionRegistryTest.java +++ b/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java @@ -1,29 +1,28 @@ -package ch.njol.skript.lang.function; +package org.skriptlang.skript.common.function; import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; -import ch.njol.skript.lang.function.FunctionRegistry.FunctionIdentifier; +import ch.njol.skript.lang.function.Signature; +import ch.njol.skript.lang.function.SimpleJavaFunction; import ch.njol.skript.lang.util.SimpleLiteral; import ch.njol.skript.registrations.DefaultClasses; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import org.junit.Test; -import org.skriptlang.skript.common.function.DefaultFunction; -import org.skriptlang.skript.common.function.Function; import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; +import org.skriptlang.skript.common.function.FunctionRegistryImpl.FunctionIdentifier; import static org.junit.Assert.*; public class FunctionRegistryTest { - private static final FunctionRegistry registry = - (FunctionRegistry) org.skriptlang.skript.common.function.FunctionRegistry.empty(Skript.instance()); + private static final FunctionRegistry registry = FunctionRegistry.empty(Skript.instance()); private static final String FUNCTION_NAME = "testFunctionRegistry"; private static final String TEST_SCRIPT = "test"; private static final Function TEST_FUNCTION = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) - .build(_ -> true); + .build(b -> true); @Test public void testGetFunctionRetrieval() { @@ -107,14 +106,14 @@ public void testSimpleRegisterRemoveRegisterLocal() { assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(TEST_SCRIPT, TEST_FUNCTION); + registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - registry.remove(TEST_FUNCTION.signature()); + registry.remove(LOCAL_TEST_FUNCTION.signature()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); @@ -123,27 +122,27 @@ public void testSimpleRegisterRemoveRegisterLocal() { assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - registry.register(TEST_SCRIPT, TEST_FUNCTION); + registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); registry.register(TEST_FUNCTION); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); - assertEquals(TEST_FUNCTION.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); - assertEquals(TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME).retrieved()); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); - registry.remove(TEST_FUNCTION.signature()); + registry.remove(LOCAL_TEST_FUNCTION.signature()); registry.remove(TEST_FUNCTION.signature()); } private static final Function TEST_FUNCTION_B = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) .parameter("a", Boolean.class) - .build(_ -> true); + .build(b -> true); private static final Function TEST_FUNCTION_N = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) .parameter("a", Number.class) - .build(_ -> true); + .build(b -> true); @Test public void testMultipleRegistrations() { @@ -235,6 +234,16 @@ public void testRegisterRemoveRegisterGlobal() { registry.remove(TEST_FUNCTION_N.signature()); } + private static final Function LOCAL_TEST_FUNCTION_B = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .local(TEST_SCRIPT) + .parameter("a", Boolean.class) + .build(b -> true); + + private static final Function LOCAL_TEST_FUNCTION_N = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .local(TEST_SCRIPT) + .parameter("a", Number.class) + .build(b -> true); + @Test public void testRegisterRemoveRegisterLocal() { assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); @@ -244,15 +253,15 @@ public void testRegisterRemoveRegisterLocal() { assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - registry.register(TEST_SCRIPT, TEST_FUNCTION_B); + registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_B); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); - assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); - assertEquals(TEST_FUNCTION_B, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION_B.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION_B, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); - registry.remove(TEST_FUNCTION_B.signature()); + registry.remove(LOCAL_TEST_FUNCTION_B.signature()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); @@ -260,15 +269,15 @@ public void testRegisterRemoveRegisterLocal() { assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); - registry.register(TEST_SCRIPT, TEST_FUNCTION_N); + registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Number.class).result()); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).result()); - assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION_N.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - registry.remove(TEST_FUNCTION_N.signature()); + registry.remove(LOCAL_TEST_FUNCTION_N.signature()); assertSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).result()); assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Boolean.class).retrieved()); @@ -277,17 +286,17 @@ public void testRegisterRemoveRegisterLocal() { assertNull(registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); assertNull(registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - registry.register(TEST_SCRIPT, TEST_FUNCTION_N); + registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION_N); registry.register(TEST_FUNCTION_B); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).result()); - assertEquals(TEST_FUNCTION_N.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); - assertEquals(TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION_N.signature(), registry.getSignature(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); + assertEquals(LOCAL_TEST_FUNCTION_N, registry.getFunction(TEST_SCRIPT, FUNCTION_NAME, Number.class).retrieved()); assertNotSame(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME, Boolean.class).result()); assertEquals(TEST_FUNCTION_B.signature(), registry.getSignature(FUNCTION_NAME, Boolean.class).retrieved()); assertEquals(TEST_FUNCTION_B, registry.getFunction(FUNCTION_NAME, Boolean.class).retrieved()); - registry.remove(TEST_FUNCTION_N.signature()); + registry.remove(LOCAL_TEST_FUNCTION_N.signature()); registry.remove(TEST_FUNCTION_B.signature()); } @@ -349,12 +358,16 @@ public void testIdentifierSignatureOf() { assertEquals(FunctionIdentifier.of(function2.signature()), identifier); } + private static final Function LOCAL_TEST_FUNCTION = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) + .local(TEST_SCRIPT) + .build(b -> true); + // see https://github.com/SkriptLang/Skript/pull/8015 @Test public void testRemoveGlobalScriptFunctions8015() { // create empty TEST_SCRIPT namespace such that it is not null - registry.register(TEST_SCRIPT, TEST_FUNCTION); - registry.remove(TEST_FUNCTION.signature()); + registry.register(TEST_SCRIPT, LOCAL_TEST_FUNCTION); + registry.remove(LOCAL_TEST_FUNCTION.signature()); assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(TEST_SCRIPT, FUNCTION_NAME).result()); @@ -382,11 +395,11 @@ public void testRemoveGlobalScriptFunctions8015() { private static final Function TEST_FUNCTION_P = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) .parameter("a", Player.class) - .build(_ -> true); + .build(b -> true); private static final Function TEST_FUNCTION_OP = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) .parameter("a", OfflinePlayer.class) - .build(_ -> true); + .build(b -> true); @Test public void testGetExactSignature() { @@ -399,9 +412,9 @@ public void testGetExactSignature() { registry.register(TEST_FUNCTION_P); - assertSame(RetrievalResult.EXACT, registry.getExactSignature(null, FUNCTION_NAME, Player.class).result()); - assertEquals(TEST_FUNCTION_P.signature(), registry.getExactSignature(null, FUNCTION_NAME, Player.class).retrieved()); - assertNull(registry.getExactSignature(null, FUNCTION_NAME, OfflinePlayer.class).retrieved()); + assertSame(RetrievalResult.EXACT, registry.getExactSignature(FUNCTION_NAME, Player.class).result()); + assertEquals(TEST_FUNCTION_P.signature(), registry.getExactSignature(FUNCTION_NAME, Player.class).retrieved()); + assertNull(registry.getExactSignature(FUNCTION_NAME, OfflinePlayer.class).retrieved()); assertEquals(TEST_FUNCTION_P.signature(), registry.getSignature(FUNCTION_NAME, Player.class).retrieved()); assertEquals(TEST_FUNCTION_P.signature(), registry.getSignature(FUNCTION_NAME, OfflinePlayer.class).retrieved()); From 7d8725038f7dff7faa7bef7dba5223aa4e19881e Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:35:44 +0200 Subject: [PATCH 21/32] deprecate Functions --- .../njol/skript/lang/function/Function.java | 4 +- .../lang/function/FunctionRegistry.java | 38 ++++++++++++++++--- .../njol/skript/lang/function/Functions.java | 20 +++++----- .../skript/common/function/Function.java | 38 ++++++++++++++++++- .../common/function/FunctionReference.java | 2 + 5 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/Function.java b/src/main/java/ch/njol/skript/lang/function/Function.java index ca9ccca28b1..0103fc9aa6c 100644 --- a/src/main/java/ch/njol/skript/lang/function/Function.java +++ b/src/main/java/ch/njol/skript/lang/function/Function.java @@ -91,10 +91,10 @@ public Class type() { public final T @Nullable [] execute(Object[][] params) { FunctionEvent event = new FunctionEvent<>(this); - // Call function event only if requested by addon - // Functions may be called VERY often, so this might have performance impact if (Functions.callFunctionEvents) Bukkit.getPluginManager().callEvent(event); + org.skriptlang.skript.common.function.Function.eventRegistry().events(Function.CallEvent.class) + .forEach(e -> e.onCall(this)); // Parameters taken by the function. Parameters parameters = sign.parameters(); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index 74381b3fe9e..c0a117dfb5e 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -11,9 +11,9 @@ import java.util.stream.Collectors; /** - * A registry for functions. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry} instead. */ -@ApiStatus.Internal +@Deprecated(forRemoval = true, since = "INSERT VERSION") public final class FunctionRegistry implements Registry> { private static org.skriptlang.skript.common.function.FunctionRegistry newRegistry; @@ -26,10 +26,10 @@ public static void setNewRegistry(org.skriptlang.skript.common.function.Function private static FunctionRegistry registry; /** - * Gets the global function registry. - * - * @return The global function registry. + * @deprecated Use {@link org.skriptlang.skript.addon.SkriptAddon#registry(Class)} + * and {@link org.skriptlang.skript.common.function.FunctionRegistry} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static FunctionRegistry getRegistry() { if (registry == null) { registry = new FunctionRegistry(); @@ -37,6 +37,10 @@ public static FunctionRegistry getRegistry() { return registry; } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#elements()} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") @Override public @Unmodifiable @NotNull Collection> elements() { return newRegistry.elements().stream() @@ -44,6 +48,10 @@ public static FunctionRegistry getRegistry() { .collect(Collectors.toUnmodifiableSet()); } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#register(String, org.skriptlang.skript.common.function.Signature)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public void register(@Nullable String namespace, @NotNull Signature signature) { if (namespace == null) { newRegistry.register(signature); @@ -52,6 +60,10 @@ public void register(@Nullable String namespace, @NotNull Signature signature } } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#register(String, org.skriptlang.skript.common.function.Function)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public void register(@Nullable String namespace, @NotNull Function function) { if (namespace == null) { newRegistry.register(function); @@ -117,6 +129,10 @@ public record Retrieval( ) { } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getFunction(String, String, Class[])} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public @NotNull Retrieval> getFunction( @Nullable String namespace, @NotNull String name, @@ -133,6 +149,10 @@ public record Retrieval( return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Function) retrieval.retrieved(), retrieval.conflictingArgs()); } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getSignature(String, String, Class[])} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public Retrieval> getSignature( @Nullable String namespace, @NotNull String name, @@ -180,6 +200,10 @@ Retrieval> getExactSignature( return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Signature) retrieval.retrieved(), retrieval.conflictingArgs()); } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#getSignatures(String, String)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public @Unmodifiable @NotNull Set> getSignatures(@Nullable String namespace, @NotNull String name) { Set> signatures; @@ -194,6 +218,10 @@ Retrieval> getExactSignature( .collect(Collectors.toUnmodifiableSet()); } + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry#remove(org.skriptlang.skript.common.function.Signature)} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public void remove(@NotNull Signature signature) { newRegistry.remove(signature); } 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 1657677d709..f8ba81e7f41 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -19,8 +19,9 @@ import java.util.stream.Collectors; /** - * Static methods to work with functions. + * @deprecated Use {@link org.skriptlang.skript.common.function.FunctionRegistry} instead. */ +@Deprecated(forRemoval = true, since = "INSERT VERSION") public abstract class Functions { private Functions() { @@ -52,6 +53,10 @@ private Functions() { */ private static final Map globalFunctions = new HashMap<>(); + /** + * @deprecated Use {@link org.skriptlang.skript.common.function.Function#eventRegistry()} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static boolean callFunctionEvents = false; /** @@ -343,18 +348,11 @@ public static Collection> getFunctions() { return javaNamespace.getFunctions(); } + /** - * Normally, function calls do not cause actual Bukkit events to be - * called. If an addon requires such functionality, it should call this - * method. After doing so, the events will be called. Calling this method - * many times will not cause any additional changes. - *

- * Note that calling events is not free; performance might vary - * once you have enabled that. - * - * @param addon Addon instance. + * @deprecated Use {@link org.skriptlang.skript.common.function.Function#eventRegistry()} instead. */ - @SuppressWarnings({"null", "unused"}) + @Deprecated(forRemoval = true, since = "INSERT VERSION") public static void enableFunctionEvents(SkriptAddon addon) { if (addon == null) { throw new SkriptAPIException("enabling function events requires addon instance"); diff --git a/src/main/java/org/skriptlang/skript/common/function/Function.java b/src/main/java/org/skriptlang/skript/common/function/Function.java index 5266f57d2ad..edd4a8bcbe6 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Function.java +++ b/src/main/java/org/skriptlang/skript/common/function/Function.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.ApiStatus.NonExtendable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.util.event.EventRegistry; /** * Represents a function implementation. @@ -21,7 +22,7 @@ public interface Function { /** * Executes this function with the given parameters. * - * @param event The event that is associated with this function execution. + * @param event The event that is associated with this function execution. * @param arguments The arguments to execute the function with. * @return The return value. */ @@ -44,4 +45,39 @@ public interface Function { @Experimental @NotNull String @Nullable [] returnedKeys(); + // + + /** + * Used for listening to function events. + * + * @see #eventRegistry() + */ + interface Event extends org.skriptlang.skript.util.event.Event { + } + + /** + * Called when a function is called. + */ + @FunctionalInterface + interface CallEvent extends Event { + + /** + * The method that is called when this event triggers. + */ + void onCall(Function function); + + } + + /** + * @return An event registry for function events. + */ + static EventRegistry eventRegistry() { + return Data.EVENT_REGISTRY; + } + + class Data { + private static final EventRegistry EVENT_REGISTRY = new EventRegistry<>(); + } + // + } diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java index d72c57715cf..fe0e33938b0 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java @@ -195,6 +195,8 @@ public T execute(Event event) { if (Functions.callFunctionEvents) Bukkit.getPluginManager().callEvent(fnEvent); + org.skriptlang.skript.common.function.Function.eventRegistry().events(Function.CallEvent.class) + .forEach(e -> e.onCall(function)); return function.execute(fnEvent, new FunctionArgumentsImpl(args)); } From 4f6c75ac952716c0ee0399f157f625cc6b445981 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Sun, 16 Aug 2026 23:59:04 +0200 Subject: [PATCH 22/32] improve signature and parameter modifiers --- .../njol/skript/lang/function/Function.java | 4 +- .../lang/function/FunctionReference.java | 8 +- .../njol/skript/lang/function/Parameter.java | 22 +-- .../skript/lang/function/ScriptFunction.java | 3 +- .../njol/skript/lang/function/Signature.java | 6 +- .../skript/structures/StructFunction.java | 83 ++++++++--- .../common/function/DefaultFunctionImpl.java | 29 +++- .../function/FunctionReferenceParser.java | 4 +- .../common/function/FunctionRegistry.java | 41 +++++- .../common/function/FunctionRegistryImpl.java | 50 +++---- .../skript/common/function/Parameter.java | 138 ++++++++++++++---- .../skript/common/function/Parameters.java | 2 +- .../common/function/ScriptParameter.java | 4 +- .../skript/common/function/Signature.java | 27 ++-- 14 files changed, 299 insertions(+), 122 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/Function.java b/src/main/java/ch/njol/skript/lang/function/Function.java index 0103fc9aa6c..dd54ea41184 100644 --- a/src/main/java/ch/njol/skript/lang/function/Function.java +++ b/src/main/java/ch/njol/skript/lang/function/Function.java @@ -110,7 +110,7 @@ public Class type() { int i = 0; // Execute parameters or default value expressions for (org.skriptlang.skript.common.function.Parameter parameter : parameters.all()) { - Object[] parameterValue = parameter.hasModifier(Modifier.KEYED) ? convertToKeyed(parameterValues[i]) : parameterValues[i]; + Object[] parameterValue = parameter.hasModifier(Modifier.Keyed.class) ? convertToKeyed(parameterValues[i]) : parameterValues[i]; Expression defaultValueExpr; if (parameter instanceof Parameter script) { @@ -122,7 +122,7 @@ public Class type() { } // see https://github.com/SkriptLang/Skript/pull/8135 - if ((parameterValues[i] == null || parameterValues[i].length == 0) && parameter.hasModifier(Modifier.KEYED) && defaultValueExpr != null) { + if ((parameterValues[i] == null || parameterValues[i].length == 0) && parameter.hasModifier(Modifier.Keyed.class) && defaultValueExpr != null) { Object[] defaultValue = defaultValueExpr.getArray(event); if (defaultValue.length == 1) { parameterValue = KeyedValue.zip(defaultValue, null); diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 034282f6187..a135e4bd305 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -267,11 +267,11 @@ public boolean validateFunction(boolean first) { } // check ranged parameters - if (signatureParam.hasModifier(Modifier.RANGED) && exprParam instanceof Literal literalParam) { - RangedModifier range = signatureParam.getModifier(RangedModifier.class); + if (signatureParam.hasModifier(Modifier.Ranged.class) && exprParam instanceof Literal literalParam) { + Modifier.Ranged range = signatureParam.getModifier(Modifier.Ranged.class); if (!range.inRange(literalParam.getArray())) { Skript.error("The argument '" + signatureParam.name() +"' only accepts values between " - + Classes.toString(range.getMin()) + " and " + Classes.toString(range.getMax()) + ". " + + Classes.toString(range.min()) + " and " + Classes.toString(range.max()) + ". " + "Provided: " + literalParam.toString(null, Skript.debug())); return false; } @@ -409,7 +409,7 @@ private Object[] evaluateSingleListParameter(Parameter parameter, Expression< //noinspection unchecked,rawtypes return parameter.evaluate((Expression) arguments[0], event); - if (!parameter.hasModifier(Modifier.KEYED)) { + if (!parameter.hasModifier(Modifier.Keyed.class)) { List list = new ArrayList<>(); for (Expression argument : arguments) //noinspection unchecked,rawtypes diff --git a/src/main/java/ch/njol/skript/lang/function/Parameter.java b/src/main/java/ch/njol/skript/lang/function/Parameter.java index 5b4a10fd461..7916d6964a4 100644 --- a/src/main/java/ch/njol/skript/lang/function/Parameter.java +++ b/src/main/java/ch/njol/skript/lang/function/Parameter.java @@ -88,10 +88,10 @@ public Parameter(String name, ClassInfo type, boolean single, @Nullable Expre this.modifiers = new HashSet<>(); if (def != null) { - modifiers.add(Modifier.OPTIONAL); + modifiers.add(new Modifier.Optional()); } if (keyed) { - modifiers.add(Modifier.KEYED); + modifiers.add(new Modifier.Keyed()); } } @@ -110,10 +110,10 @@ public Parameter(String name, ClassInfo type, boolean single, @Nullable Expre this.modifiers = new HashSet<>(); if (optional) { - modifiers.add(Modifier.OPTIONAL); + modifiers.add(new Modifier.Optional()); } if (keyed) { - modifiers.add(Modifier.KEYED); + modifiers.add(new Modifier.Keyed()); } } @@ -131,7 +131,7 @@ public Parameter(String name, ClassInfo type, boolean single, @Nullable Expre this.def = def; this.single = single; this.modifiers = Set.of(modifiers); - this.keyed = this.modifiers.contains(Modifier.KEYED); + this.keyed = hasModifier(Modifier.Keyed.class); } /** @@ -139,7 +139,7 @@ public Parameter(String name, ClassInfo type, boolean single, @Nullable Expre * @return Whether this parameter is optional or not. */ public boolean isOptional() { - return modifiers.contains(Modifier.OPTIONAL); + return hasModifier(Modifier.Optional.class); } /** @@ -180,10 +180,10 @@ public ClassInfo getType() { Set modifiers = new HashSet<>(); if (d != null) { - modifiers.add(Modifier.OPTIONAL); + modifiers.add(new Modifier.Optional()); } if (!single) { - modifiers.add(Modifier.KEYED); + modifiers.add(new Modifier.Keyed()); } return new Parameter<>(name, type, single, d, modifiers.toArray(new Modifier[0])); @@ -299,9 +299,9 @@ public String toString() { // ns: numbers between 0 and 100 = 3 public String toString(boolean debug) { String result = name + ": " + Utils.toEnglishPlural(type.getCodeName(), !single); - if (this.hasModifier(Modifier.RANGED)) { - RangedModifier range = this.getModifier(RangedModifier.class); - result += " between " + Classes.toString(range.getMin()) + " and " + Classes.toString(range.getMax()); + if (hasModifier(Modifier.Ranged.class)) { + Modifier.Ranged range = this.getModifier(Modifier.Ranged.class); + result += " between " + Classes.toString(range.min()) + " and " + Classes.toString(range.max()); } result += (def != null ? " = " + def.toString(null, debug) : ""); return result; 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 9bbba5cc8bb..2d1c1449989 100644 --- a/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java +++ b/src/main/java/ch/njol/skript/lang/function/ScriptFunction.java @@ -7,7 +7,6 @@ import ch.njol.skript.util.Utils; import ch.njol.skript.variables.HintManager; import ch.njol.skript.variables.Variables; -import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.FunctionArguments; @@ -144,7 +143,7 @@ public boolean resetReturnValue() { } @Override - public final void returnValues(Event event, Expression value) { + public final void returnValues(org.bukkit.event.Event event, Expression value) { assert !returnValueSet.get(); returnValueSet.set(true); this.returnValues.set(value.getArray(event)); diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index 8fecc8e8e20..dc21b251f72 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -84,7 +84,7 @@ public Signature(@Nullable String script, String name, Parameter[] parameters this.parameters = initParameters(parameters); this.local = local; if (local) - modifiers.add(Modifier.LOCAL); + modifiers.add(new Modifier.Local()); this.returnType = returnType; this.single = single; if (returnType == null) { @@ -114,7 +114,7 @@ public Signature(@Nullable String script, String name, Parameters parameters, Cl this.parameters = parameters; this.local = local; if (local) - modifiers.add(Modifier.LOCAL); + modifiers.add(new Modifier.Local()); this.returns = returnType; if (returnType != null) { //noinspection unchecked @@ -289,7 +289,7 @@ public int getMinParameters() { int i = parameters.size() - 1; for (org.skriptlang.skript.common.function.Parameter parameter : Lists.reverse(params)) { - if (!parameter.hasModifier(org.skriptlang.skript.common.function.Parameter.Modifier.OPTIONAL)) { + if (!parameter.hasModifier(org.skriptlang.skript.common.function.Parameter.Modifier.Optional.class)) { return i + 1; } i--; diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index 66a21666ba3..ca94580fd61 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -10,12 +10,16 @@ import ch.njol.skript.doc.Since; import ch.njol.skript.lang.Literal; import ch.njol.skript.lang.SkriptParser.ParseResult; -import ch.njol.skript.lang.function.*; +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 org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.common.function.FunctionParser; -import org.skriptlang.skript.common.function.FunctionReference; +import org.skriptlang.skript.common.function.*; +import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; +import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; +import org.skriptlang.skript.common.function.Signature.Modifier; import org.skriptlang.skript.lang.entry.EntryContainer; import org.skriptlang.skript.lang.script.Script; import org.skriptlang.skript.lang.structure.Structure; @@ -107,7 +111,7 @@ public boolean preLoad() { // parse signature getParser().setCurrentEvent((local ? "local " : "") + "function", FunctionEvent.class); - signature = (Signature) FunctionParser.parse( + signature = FunctionParser.parse( getParser().getCurrentScript().getConfig().getFileName(), matcher.group("name"), matcher.group("args"), matcher.group("returns"), local ); @@ -118,16 +122,59 @@ public boolean preLoad() { } private static Signature registerSignature(Signature signature) { - try { - if (signature.isLocal()) { - ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(signature.namespace(), signature); + Retrieval> existing; + Parameter[] parameters = signature.parameters().all(); + FunctionRegistry registry = Skript.instance().registry(FunctionRegistry.class); + + if (parameters.length == 1 && !parameters[0].isSingle()) { + if (signature.namespace() != null) { + existing = registry.getExactSignature(signature.namespace(), signature.name(), parameters[0].type().arrayType()); } else { - ch.njol.skript.lang.function.FunctionRegistry.getRegistry().register(null, signature); + existing = registry.getExactSignature(signature.name()); } - } catch (SkriptAPIException ex) { - Skript.error(ex.getMessage()); + } else { + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + types[i] = parameters[i].type(); + } + + if (signature.namespace() != null) { + existing = registry.getExactSignature(signature.namespace(), signature.name(), types); + } else { + existing = registry.getExactSignature(signature.name(), types); + } + } + + // if this function has already been registered, only allow it if one function is local and one is global. + // if both are global or both are local, disallow. + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Modifier.Local.class) == signature.hasModifier(Modifier.Local.class)) { + StringBuilder error = new StringBuilder(); + + if (existing.retrieved().hasModifier(Modifier.Local.class)) { + error.append("Local function "); + } else { + error.append("Function "); + } + error.append("'%s' with the same argument types already exists".formatted(signature.name())); + if (existing.retrieved().namespace() != null) { + error.append(" in script '%s'.".formatted(existing.retrieved().namespace())); + } else { + error.append("."); + } + + Skript.error(error.toString()); + + return null; } + if (signature.namespace() != null) { + registry.register(signature.namespace(), signature); + } else { + registry.register(signature); + } + + Skript.debug("Registered function signature: " + signature.name()); + return signature; } @@ -148,22 +195,24 @@ public boolean load() { } private static void loadFunction(Script script, SectionNode node, Signature signature) { + FunctionRegistry registry = Skript.instance().registry(FunctionRegistry.class); + Function function; try { - function = new ScriptFunction<>(signature, node); + function = new ScriptFunction<>((ch.njol.skript.lang.function.Signature) signature, node); } catch (SkriptAPIException ex) { //noinspection ThrowableNotThrown Skript.exception(ex, "Error while trying to load a function"); // avoid getting a "function is already registered" error when the function implementation is not known yet - FunctionRegistry.getRegistry().remove(signature); + registry.remove(signature); return; } - if (function.getSignature().isLocal()) { - FunctionRegistry.getRegistry().register(script.getConfig().getFileName(), function); + if (function.signature().hasModifier(Modifier.Local.class)) { + registry.register(script.getConfig().getFileName(), function); } else { - FunctionRegistry.getRegistry().register(null, function); + registry.register(function); } } @@ -179,8 +228,8 @@ public boolean postLoad() { @Override public void unload() { assert signature != null; - FunctionRegistry.getRegistry().remove(signature); - signature.calls().forEach(FunctionReference::invalidate); + Skript.instance().registry(FunctionRegistry.class).remove(signature); +// signature.calls().forEach(FunctionReference::invalidate); VALIDATE_FUNCTIONS.set(true); } diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index b2e766af720..139a1cfbbf8 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -67,15 +67,15 @@ final class DefaultFunctionImpl extends ch.njol.skript.lang.function.Function Parameter parameter = arrayParams[i]; if (arg == null || arg.length == 0) { - if (parameter.hasModifier(Modifier.OPTIONAL)) { + if (parameter.hasModifier(Modifier.Optional.class)) { continue; } else { return null; } } - if (parameter.hasModifier(Modifier.RANGED)) { - RangedModifier range = parameter.getModifier(RangedModifier.class); + if (parameter.hasModifier(Modifier.Ranged.class)) { + Modifier.Ranged range = parameter.getModifier(Modifier.Ranged.class); if (!range.inRange(arg)) { return null; } @@ -123,12 +123,12 @@ public T execute(@NotNull FunctionEvent event, @NotNull FunctionArguments arg Parameter parameter = parameters.get(name); Object value = arguments.get(name); - if (value == null && !parameter.hasModifier(Modifier.OPTIONAL)) { + if (value == null && !parameter.hasModifier(Modifier.Optional.class)) { return null; } - if (parameter.hasModifier(Modifier.RANGED)) { - RangedModifier range = parameter.getModifier(RangedModifier.class); + if (parameter.hasModifier(Modifier.Ranged.class)) { + Modifier.Ranged range = parameter.getModifier(Modifier.Ranged.class); if (!range.inRange(value)) { return null; } @@ -313,13 +313,28 @@ record DefaultParameter(String name, Class type, Set modifiers) implements Parameter { DefaultParameter(String name, Class type, Modifier... modifiers) { - this(name, type, Set.of(modifiers)); + this(name, type, modernizeModifiers(modifiers)); } @Override public @NotNull String toString() { return toFormattedString(); } + + @SuppressWarnings("removal") + private static Set modernizeModifiers(Modifier[] modifiers) { + Set result = new LinkedHashSet<>(); + for (Modifier modifier : modifiers) { + if (modifier == Modifier.OPTIONAL) { + result.add(new Modifier.Optional()); + } else if (modifier == Modifier.KEYED) { + result.add(new Modifier.Keyed()); + } else if (modifier instanceof RangedModifier rangedModifier) { + result.add(new Modifier.Ranged<>(rangedModifier.getMax(), rangedModifier.getMax())); + } + } + return result; + } } } diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java index d72d1bd23ab..cda67c3b10f 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReferenceParser.java @@ -257,7 +257,7 @@ private Set> getExactReferences( Expression fallback; if (parameter instanceof ScriptParameter sp) { fallback = sp.defaultValue(); - } else if (parameter.hasModifier(Modifier.OPTIONAL)) { + } else if (parameter.hasModifier(Modifier.Optional.class)) { fallback = new EmptyExpression(); } else { fallback = null; @@ -397,7 +397,7 @@ private Set> getListReferences( Expression fallback; if (parameter instanceof ScriptParameter sp) { fallback = sp.defaultValue(); - } else if (parameter.hasModifier(Modifier.OPTIONAL)) { + } else if (parameter.hasModifier(Modifier.Optional.class)) { fallback = new EmptyExpression(); } else { fallback = null; diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java index 13db9e0b13a..ad3c1a7b3d1 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistry.java @@ -11,6 +11,8 @@ import org.skriptlang.skript.util.Registry; import org.skriptlang.skript.util.ViewProvider; +import java.util.Collection; +import java.util.SequencedCollection; import java.util.Set; /** @@ -139,8 +141,41 @@ enum RetrievalResult { record Retrieval( @NotNull RetrievalResult result, T retrieved, - Class[][] conflictingArgs + @NotNull Collection>> conflictingArgs ) { + + /** + * @return Whether this result errored. + */ + public boolean isError() { + return result != RetrievalResult.EXACT; + } + + /** + * @param The type to return. + * @return A retrieval without a registration. + */ + public static Retrieval notRegistered() { + return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, Set.of()); + } + + /** + * @param retrieved The retrieved object. + * @param The type to return. + * @return An exact retrieval. + */ + public static Retrieval exact(T retrieved) { + return new Retrieval<>(RetrievalResult.EXACT, retrieved, Set.of()); + } + + /** + * @param conflictingArgs The function arguments of all conflicting functions. + * @return An ambiguous retrieval. + */ + public static Retrieval ambiguous(Collection>> conflictingArgs) { + return new Retrieval<>(RetrievalResult.AMBIGUOUS, null, conflictingArgs); + } + } /** @@ -227,8 +262,8 @@ Retrieval> getExactSignature( * * @param namespace The namespace to get the function from. * Usually represents the path of the script this function is registered in. - * @param name The name of the function. - * @param args The types of the arguments of the function. + * @param name The name of the function. + * @param args The types of the arguments of the function. * @return The signature for the function with the given name and argument types, or null if no such function exists. */ Retrieval> getExactSignature( diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java index 2f3d4e6e84b..13111b10507 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java @@ -16,7 +16,6 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -144,7 +143,7 @@ public org.skriptlang.skript.common.function.FunctionRegistry unmodifiableView() @Override public void register(@NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - if (signature.hasModifier(Signature.Modifier.LOCAL)) { + if (signature.hasModifier(Signature.Modifier.Local.class)) { throw new IllegalArgumentException("Cannot register a local signature in the global namespace"); } register(GLOBAL_NAMESPACE, signature); @@ -153,7 +152,7 @@ public void register(@NotNull Signature signature) { @Override public void register(@NotNull String namespace, @NotNull Signature signature) { Preconditions.checkNotNull(signature, "signature cannot be null"); - if (!signature.hasModifier(Signature.Modifier.LOCAL)) { + if (!signature.hasModifier(Signature.Modifier.Local.class)) { throw new IllegalArgumentException("Cannot register a global signature in a local namespace"); } @@ -181,10 +180,10 @@ private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature // if this function has already been registered, only allow it if one function is local and one is global. // if both are global or both are local, disallow. - if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Signature.Modifier.LOCAL) == signature.hasModifier(Signature.Modifier.LOCAL)) { + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Signature.Modifier.Local.class) == signature.hasModifier(Signature.Modifier.Local.class)) { StringBuilder error = new StringBuilder(); - if (existing.retrieved().hasModifier(Signature.Modifier.LOCAL)) { + if (existing.retrieved().hasModifier(Signature.Modifier.Local.class)) { error.append("Local function "); } else { error.append("Function "); @@ -223,7 +222,7 @@ private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature @Override public void register(@NotNull Function function) { Preconditions.checkNotNull(function, "function cannot be null"); - if (function.signature().hasModifier(Signature.Modifier.LOCAL)) { + if (function.signature().hasModifier(Signature.Modifier.Local.class)) { throw new IllegalArgumentException("Cannot register a local function in the global namespace"); } register(GLOBAL_NAMESPACE, function); @@ -232,7 +231,7 @@ public void register(@NotNull Function function) { @Override public void register(@NotNull String namespace, @NotNull Function function) { Preconditions.checkNotNull(function, "function cannot be null"); - if (!function.signature().hasModifier(Signature.Modifier.LOCAL)) { + if (!function.signature().hasModifier(Signature.Modifier.Local.class)) { throw new IllegalArgumentException("Cannot register a global function in a local namespace"); } register(new NamespaceIdentifier(namespace), function); @@ -338,20 +337,18 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull Set existing = ns.identifiers.get(provided.name); if (existing == null) { Skript.debug("No functions named '%s' exist in the '%s' namespace", provided.name, namespace.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + return Retrieval.notRegistered(); } Set candidates = candidates(provided, existing, false); if (candidates.isEmpty()) { Skript.debug("Failed to find a function for '%s'", provided.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + return Retrieval.notRegistered(); } else if (candidates.size() == 1) { if (Skript.debug()) { Skript.debug("Matched function for '%s': %s", provided.name, candidates.stream().findAny().orElse(null)); } - return new Retrieval<>(RetrievalResult.EXACT, - ns.functions.get(candidates.stream().findAny().orElse(null)), - null); + return Retrieval.exact(ns.functions.get(candidates.stream().findAny().orElseThrow())); } else { if (Skript.debug()) { String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); @@ -359,11 +356,10 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull Skript.debug("Identifier: %s", provided); Skript.debug("Options: %s", options); } - return new Retrieval<>(RetrievalResult.AMBIGUOUS, - null, + return Retrieval.ambiguous( candidates.stream() - .map(FunctionIdentifier::args) - .toArray(Class[][]::new)); + .map(it -> List.of(it.args())) + .collect(Collectors.toSet())); } } @@ -453,21 +449,19 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp Namespace ns = namespaces.getOrDefault(namespace, new Namespace()); if (!ns.identifiers.containsKey(provided.name)) { Skript.debug("No signatures named '%s' exist in the '%s' namespace", provided.name, namespace.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + return Retrieval.notRegistered(); } Set candidates = candidates(provided, ns.identifiers.get(provided.name), exact); if (candidates.isEmpty()) { Skript.debug("Failed to find a signature for '%s'", provided.name); - return new Retrieval<>(RetrievalResult.NOT_REGISTERED, null, null); + return Retrieval.notRegistered(); } else if (candidates.size() == 1) { if (Skript.debug()) { Skript.debug("Matched signature for '%s': %s", provided.name, ns.signatures.get(candidates.stream().findAny().orElse(null))); } - return new Retrieval<>(RetrievalResult.EXACT, - ns.signatures.get(candidates.stream().findAny().orElse(null)), - null); + return Retrieval.exact(ns.signatures.get(candidates.stream().findAny().orElseThrow())); } else { if (Skript.debug()) { String options = candidates.stream().map(Record::toString).collect(Collectors.joining(", ")); @@ -475,11 +469,9 @@ private Retrieval> getSignature(@NotNull NamespaceIdentifier namesp Skript.debug("Identifier: %s", provided); Skript.debug("Options: %s", options); } - return new Retrieval<>(RetrievalResult.AMBIGUOUS, - null, - candidates.stream() - .map(FunctionIdentifier::args) - .toArray(Class[][]::new)); + return Retrieval.ambiguous(candidates.stream() + .map(it -> List.of(it.args())) + .collect(Collectors.toSet())); } } @@ -589,7 +581,7 @@ public void remove(@NotNull Signature signature) { FunctionIdentifier identifier = FunctionIdentifier.of(signature); Namespace namespace; - if (signature.hasModifier(Signature.Modifier.LOCAL)) { + if (signature.hasModifier(Signature.Modifier.Local.class)) { namespace = namespaces.get(new NamespaceIdentifier(signature.namespace())); } else { namespace = namespaces.get(GLOBAL_NAMESPACE); @@ -699,14 +691,14 @@ static FunctionIdentifier of(@NotNull Signature signature) { int optionalArgs = 0; for (int i = 0; i < signatureParams.length; i++) { Parameter param = signatureParams[i]; - if (param.hasModifier(Modifier.OPTIONAL)) { + if (param.hasModifier(Modifier.Optional.class)) { optionalArgs++; } parameters[i] = param.type(); } - return new FunctionIdentifier(signature.name(), signature.hasModifier(Signature.Modifier.LOCAL), + return new FunctionIdentifier(signature.name(), signature.hasModifier(Signature.Modifier.Local.class), parameters.length - optionalArgs, parameters); } diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameter.java b/src/main/java/org/skriptlang/skript/common/function/Parameter.java index 8c49ae879a3..906b6537275 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameter.java @@ -12,7 +12,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.common.function.DefaultFunction.Builder; -import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier; import org.skriptlang.skript.lang.converter.Converter; import org.skriptlang.skript.lang.converter.Converters; @@ -40,28 +39,39 @@ public interface Parameter { /** * @return All modifiers belonging to this parameter. */ - @Unmodifiable @NotNull Set modifiers(); + @Unmodifiable + @NotNull Set modifiers(); /** - * Returns whether this parameter has the specified modifier. - * @param modifier The modifier. - * @return True when {@link #modifiers()} contains the specified modifier, false if not. + * @deprecated Use {@link #hasModifier(Class)} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") default boolean hasModifier(Modifier modifier) { return modifiers().contains(modifier); } + /** + * Returns whether this signature has the specified modifier. + * + * @param modifier The modifier. + * @return True when {@link #modifiers()} contains the specified modifier, false if not. + */ + default boolean hasModifier(Class modifier) { + return modifiers().stream().anyMatch(modifier::isInstance); + } + /** * Gets a modifier of the specified type if present. + * * @param modifierClass The class of the modifier to retrieve * @return The modifier instance, or null if not present */ default M getModifier(Class modifierClass) { return modifiers().stream() - .filter(modifierClass::isInstance) - .map(modifierClass::cast) - .findFirst() - .orElse(null); + .filter(modifierClass::isInstance) + .map(modifierClass::cast) + .findFirst() + .orElse(null); } /** @@ -73,7 +83,7 @@ default M getModifier(Class modifierClass) { * otherwise, the returned array will contain only the values.

* * @param argument the argument passed to this parameter; or {@code null} to use the default value if present - * @param event the event in which to evaluate the expression + * @param event the event in which to evaluate the expression * @return an object array containing either value-only elements or {@code KeyedValue[]} when keyed * @throws IllegalStateException if the argument is {@code null} and this parameter does not have a default value */ @@ -88,7 +98,7 @@ default Object[] evaluate(@Nullable Expression argument, Event even for (int i = 0; i < values.length; i++) values[i] = Classes.clone(values[i]); - if (!hasModifier(Modifier.KEYED)) + if (!hasModifier(Modifier.Keyed.class)) return values; String[] keys = KeyProviderExpression.areKeysRecommended(argument) @@ -120,7 +130,7 @@ default String toFormattedString() { joiner.add("%s:".formatted(name())); - if (hasModifier(Modifier.OPTIONAL)) { + if (hasModifier(Modifier.Optional.class)) { joiner.add("optional"); } @@ -131,12 +141,12 @@ default String toFormattedString() { joiner.add(exact.getSingular()); } - if (hasModifier(Modifier.RANGED)) { - RangedModifier range = getModifier(RangedModifier.class); + if (hasModifier(Modifier.Ranged.class)) { + Modifier.Ranged range = getModifier(Modifier.Ranged.class); joiner.add("between") - .add(range.getMin().toString()) - .add("and") - .add(range.getMax().toString()); + .add(range.min().toString()) + .add("and") + .add(range.max().toString()); } return joiner.toString(); @@ -152,12 +162,18 @@ interface Modifier { * @return A new Modifier instance to be used as a custom flag. */ static Modifier of() { - return new Modifier() { }; + return new Modifier() {}; } /** * The modifier for parameters that are optional. */ + class Optional implements Modifier { } + + /** + * @deprecated Use {@link Optional} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") Modifier OPTIONAL = of(); /** @@ -166,33 +182,103 @@ static Modifier of() { * @see ch.njol.skript.lang.KeyProviderExpression * @see ch.njol.skript.lang.KeyReceiverExpression */ + class Keyed implements Modifier { } + + /** + * @deprecated Use {@link Keyed} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") Modifier KEYED = of(); /** * A modifier to use for checking if a parameter is ranged. - * Do NOT use for declaring a parameter to be ranged, use {@link Modifier#ranged(Comparable, Comparable)} - * Accessing the min and the max values can be done via {@link Parameter#getModifier(Class)}. */ - Modifier RANGED = new RangedModifier<>(0,0); // 0 and 0 are just dummy values. + record Ranged>(T min, T max) implements Modifier { + /** + * Inclusive range between min and max + * + * @param min min value + * @param max max value + */ + public Ranged { + Preconditions.checkState(min.compareTo(max) < 1, "Min value cannot be greater than max value!"); + } + + /** + * @return Min value of the range (inclusive) + */ + @Override + public T min() { + return min; + } + + /** + * @return Max value of the range (inclusive) + */ + @Override + public T max() { + return max; + } + + /** + * @param input The value to test. + * @return Whether input is between min and max. + */ + @SuppressWarnings("unchecked") + public boolean inRange(Object input) { + // convert to right type + if (!min.getClass().isInstance(input)) { + Converter converter = (Converter) Converters.getConverter(input.getClass(), min.getClass()); + if (converter == null) + return false; + input = converter.convert(input); + if (input == null) + return false; + } + // compare + return ((T) input).compareTo(min) > -1 && ((T) input).compareTo(max) < 1; + } + + /** + * @param inputs The values to test. + * @return Whether all the inputs are between min and max. + */ + public boolean inRange(Object @NotNull [] inputs) { + if (inputs.length == 0) + return false; + for (Object input : inputs) { + if (!inRange(input)) + return false; + } + return true; + } + } + + /** + * @deprecated Use {@link Ranged} instead. + */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") + Modifier RANGED = new RangedModifier<>(0, 0); // 0 and 0 are just dummy values. /** - * Creates a range modifier with inclusive min and max bounds. + * @deprecated Use {@link Ranged} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") static > RangedModifier ranged(T min, T max) { return new RangedModifier<>(min, max); } /** - * Modifier specifying valid range bounds for numeric parameters. - * Note that ALL instances will have the same hashCode and will be equal to {@link Modifier#RANGED}. - * Avoid comparing these objects or putting multiple into a HashSet or HashMap! + * @deprecated Use {@link Ranged} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") class RangedModifier> implements Modifier { private final T min; private final T max; /** * Inclusive range between min and max + * * @param min min value * @param max max value */ @@ -252,7 +338,7 @@ public boolean inRange(Object @NotNull [] inputs) { @Override public boolean equals(Object obj) { // equal to the RANGED singleton for hasModifier checks - return obj == Modifier.RANGED || ((obj instanceof RangedModifier range) && (this == Modifier.RANGED || range.max == this.max && range.min == this.min)); + return obj == Modifier.RANGED || ((obj instanceof RangedModifier range) && (this == Modifier.RANGED || range.max == this.max && range.min == this.min)); } @Override diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameters.java b/src/main/java/org/skriptlang/skript/common/function/Parameters.java index 25fbc5ab124..8a70b9dafac 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameters.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameters.java @@ -31,7 +31,7 @@ public Parameters(SequencedMap> parameters) { int j = size() - 1; for (Parameter parameter : Lists.reverse(new LinkedList<>(parameters.values()))) { - if (!parameter.hasModifier(Parameter.Modifier.OPTIONAL)) { + if (!parameter.hasModifier(Parameter.Modifier.Optional.class)) { this.minCount = j + 1; break; } diff --git a/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java b/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java index 6f4b586090f..175374997c3 100644 --- a/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java @@ -67,7 +67,7 @@ public static Parameter parse(@NotNull String name, @NotNull Class type, @ Set modifiers = new HashSet<>(); if (defaultValue != null) { - modifiers.add(Modifier.OPTIONAL); + modifiers.add(Modifier.Optional.class); } if (type.isArray()) { modifiers.add(Modifier.KEYED); @@ -94,7 +94,7 @@ public ScriptParameter(String name, Class type, Expression defaultValue, M */ public Object[] evaluate(@Nullable Expression argument, Event event) { if (argument == null) { - if (!hasModifier(Modifier.OPTIONAL)) { + if (!hasModifier(Modifier.Optional.class)) { throw new IllegalStateException("This parameter is required, but no argument was provided"); } else if (defaultValue == null) { throw new IllegalStateException("This parameter does not have a default value"); diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index 5056b9f75b5..d5ab54f736e 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import java.util.Optional; import java.util.Set; /** @@ -78,8 +79,18 @@ default boolean isSingle() { * @param modifier The modifier. * @return True when {@link #modifiers()} contains the specified modifier, false if not. */ - default boolean hasModifier(Modifier modifier) { - return modifiers().contains(modifier); + default boolean hasModifier(Class modifier) { + return modifiers().stream().anyMatch(modifier::isInstance); + } + + /** + * Returns whether this signature has the specified modifier. + */ + default Optional getModifier(Class modifier) { + return modifiers().stream() + .filter(modifier::isInstance) + .map(modifier::cast) + .findAny(); } /** @@ -87,17 +98,7 @@ default boolean hasModifier(Modifier modifier) { */ interface Modifier { - /** - * @return A new Modifier instance to be used as a custom flag. - */ - static Modifier of() { - return new Modifier() { }; - } - - /** - * Indicates the signature is local. - */ - Modifier LOCAL = of(); + class Local implements Modifier { } } From 8cfd7b4afb6bcaf817c5271327ef42191a4c603e Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:08:32 +0200 Subject: [PATCH 23/32] fix improvements for signature --- .../lang/function/FunctionReference.java | 6 +++--- .../skript/lang/function/FunctionRegistry.java | 18 +++++++++++++++--- .../njol/skript/lang/function/Functions.java | 4 ++-- .../common/function/DefaultFunctionImpl.java | 3 ++- .../common/function/ScriptParameter.java | 4 ++-- .../skript/common/function/Signature.java | 6 +++--- 6 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index a135e4bd305..591742354a2 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -517,10 +517,10 @@ static Object[][] consign(Object... arguments) { } - private void ambiguousError(Class[][] conflictingArgs) { + private void ambiguousError(Collection>> conflictingArgs) { List parts = new ArrayList<>(); - for (Class[] args : conflictingArgs) { - String argNames = Arrays.stream(args).map(arg -> { + for (SequencedCollection> args : conflictingArgs) { + String argNames = args.stream().map(arg -> { String name = Classes.getExactClassName(arg); if (name == null) { diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java index c0a117dfb5e..0da7ef916ec 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionRegistry.java @@ -146,7 +146,11 @@ public record Retrieval( retrieval = newRegistry.getFunction(namespace, name, args); } - return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Function) retrieval.retrieved(), retrieval.conflictingArgs()); + return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), + (Function) retrieval.retrieved(), + retrieval.conflictingArgs().stream() + .map(it -> it.toArray(Class[]::new)) + .toArray(Class[][]::new)); } /** @@ -166,7 +170,11 @@ public Retrieval> getSignature( retrieval = newRegistry.getSignature(namespace, name, args); } - return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Signature) retrieval.retrieved(), retrieval.conflictingArgs()); + return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), + (Signature) retrieval.retrieved(), + retrieval.conflictingArgs().stream() + .map(it -> it.toArray(Class[]::new)) + .toArray(Class[][]::new)); } /** @@ -197,7 +205,11 @@ Retrieval> getExactSignature( retrieval = newRegistry.getSignature(namespace, name, args); } - return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), (Signature) retrieval.retrieved(), retrieval.conflictingArgs()); + return new Retrieval<>(RetrievalResult.valueOf(retrieval.result().name()), + (Signature) retrieval.retrieved(), + retrieval.conflictingArgs().stream() + .map(it -> it.toArray(Class[]::new)) + .toArray(Class[][]::new)); } /** 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 f8ba81e7f41..451c8a3c581 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -171,10 +171,10 @@ public static JavaFunction registerFunction(JavaFunction function) { // if this function has already been registered, only allow it if one function is local and one is global. // if both are global or both are local, disallow. - if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Modifier.LOCAL) == signature.isLocal()) { + if (existing.result() == RetrievalResult.EXACT && existing.retrieved().hasModifier(Modifier.Local.class) == signature.isLocal()) { StringBuilder error = new StringBuilder(); - if (existing.retrieved().hasModifier(Modifier.LOCAL)) { + if (existing.retrieved().hasModifier(Modifier.Local.class)) { error.append("Local function "); } else { error.append("Function "); diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index 139a1cfbbf8..60f4aacd299 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -330,7 +330,8 @@ private static Set modernizeModifiers(Modifier[] modifiers) { } else if (modifier == Modifier.KEYED) { result.add(new Modifier.Keyed()); } else if (modifier instanceof RangedModifier rangedModifier) { - result.add(new Modifier.Ranged<>(rangedModifier.getMax(), rangedModifier.getMax())); + //noinspection rawtypes,unchecked + result.add(new Modifier.Ranged(rangedModifier.getMin(), rangedModifier.getMax())); } } return result; diff --git a/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java b/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java index 175374997c3..563f259fdda 100644 --- a/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/ScriptParameter.java @@ -67,10 +67,10 @@ public static Parameter parse(@NotNull String name, @NotNull Class type, @ Set modifiers = new HashSet<>(); if (defaultValue != null) { - modifiers.add(Modifier.Optional.class); + modifiers.add(new Modifier.Optional()); } if (type.isArray()) { - modifiers.add(Modifier.KEYED); + modifiers.add(new Modifier.Keyed()); } return new ScriptParameter<>(name, type, defaultValue, modifiers.toArray(new Modifier[0])); diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index d5ab54f736e..48d924c629a 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -8,7 +8,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -import java.util.Optional; import java.util.Set; /** @@ -86,11 +85,12 @@ default boolean hasModifier(Class modifier) { /** * Returns whether this signature has the specified modifier. */ - default Optional getModifier(Class modifier) { + default MT getModifier(Class modifier) { return modifiers().stream() .filter(modifier::isInstance) .map(modifier::cast) - .findAny(); + .findAny() + .orElse(null); } /** From 7877f8608f11b6c8a251bed598fffed881a5c35a Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:38:38 +0200 Subject: [PATCH 24/32] fix tests --- src/main/java/ch/njol/skript/Skript.java | 2 ++ .../lang/function/FunctionReference.java | 5 ++-- .../njol/skript/lang/function/Functions.java | 12 ++++++++ .../skript/structures/StructFunction.java | 28 +++++++++++++------ .../common/function/FunctionParser.java | 2 -- .../common/function/FunctionReference.java | 25 ++++++++++++++++- .../common/function/FunctionRegistryImpl.java | 25 ++++++++--------- .../skript/common/function/Parameter.java | 18 +----------- .../skript/common/function/Signature.java | 11 +++++--- 9 files changed, 78 insertions(+), 50 deletions(-) diff --git a/src/main/java/ch/njol/skript/Skript.java b/src/main/java/ch/njol/skript/Skript.java index 1cee840c141..441cdb9da47 100644 --- a/src/main/java/ch/njol/skript/Skript.java +++ b/src/main/java/ch/njol/skript/Skript.java @@ -20,6 +20,7 @@ import ch.njol.skript.registrations.Classes; import ch.njol.skript.registrations.EventValues; import ch.njol.skript.registrations.Feature; +import ch.njol.skript.structures.StructFunction; import ch.njol.skript.test.runner.*; import ch.njol.skript.timings.SkriptTimings; import ch.njol.skript.update.ReleaseManifest; @@ -484,6 +485,7 @@ public void onEnable() { FunctionRegistry functionRegistry = FunctionRegistry.empty(skript); skript.storeRegistry(FunctionRegistry.class, functionRegistry); ch.njol.skript.lang.function.FunctionRegistry.setNewRegistry(functionRegistry); + StructFunction.setRegistry(functionRegistry); // TODO this upcoming portion is a bad circular dependency // some modules depend on the config diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 591742354a2..947d88f835f 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -13,15 +13,14 @@ import ch.njol.util.StringUtils; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; +import org.skriptlang.skript.common.function.Function; import org.skriptlang.skript.common.function.FunctionReference.Argument; import org.skriptlang.skript.common.function.FunctionReference.ArgumentType; import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; import org.skriptlang.skript.common.function.Parameter; -import org.skriptlang.skript.common.function.Function; -import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.common.function.Parameter.Modifier; -import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier; +import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.util.Executable; 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 451c8a3c581..ba65e2fd1ce 100644 --- a/src/main/java/ch/njol/skript/lang/function/Functions.java +++ b/src/main/java/ch/njol/skript/lang/function/Functions.java @@ -7,6 +7,7 @@ import ch.njol.skript.lang.function.FunctionRegistry.Retrieval; import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult; import ch.njol.skript.structures.StructFunction; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.skriptlang.skript.common.function.DefaultFunction; import org.skriptlang.skript.common.function.FunctionParser; @@ -150,6 +151,17 @@ public static JavaFunction registerFunction(JavaFunction function) { return (Signature) FunctionParser.parse(script, name, args, returnType, local); } + @ApiStatus.Internal + public static void registerCompatibilitySignature(Signature signature) { + Namespace.Key namespaceKey = new Namespace.Key(Namespace.Origin.SCRIPT, signature.namespace()); + Namespace namespace = namespaces.computeIfAbsent(namespaceKey, k -> new Namespace()); + if (namespace.getSignature(signature.getName()) == null) { + namespace.addSignature(signature); + } + if (!signature.isLocal()) + globalFunctions.put(signature.getName(), namespace); + } + /** * @deprecated Handled by {@link StructFunction}. */ diff --git a/src/main/java/ch/njol/skript/structures/StructFunction.java b/src/main/java/ch/njol/skript/structures/StructFunction.java index ca94580fd61..5d84161d69a 100644 --- a/src/main/java/ch/njol/skript/structures/StructFunction.java +++ b/src/main/java/ch/njol/skript/structures/StructFunction.java @@ -15,10 +15,14 @@ import ch.njol.skript.lang.function.ScriptFunction; import ch.njol.skript.lang.parser.ParserInstance; import org.bukkit.event.Event; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; -import org.skriptlang.skript.common.function.*; +import org.skriptlang.skript.common.function.Function; +import org.skriptlang.skript.common.function.FunctionParser; import org.skriptlang.skript.common.function.FunctionRegistry.Retrieval; import org.skriptlang.skript.common.function.FunctionRegistry.RetrievalResult; +import org.skriptlang.skript.common.function.Parameter; +import org.skriptlang.skript.common.function.Signature; import org.skriptlang.skript.common.function.Signature.Modifier; import org.skriptlang.skript.lang.entry.EntryContainer; import org.skriptlang.skript.lang.script.Script; @@ -49,6 +53,13 @@ function getPoints(p: player) returns number: @Since("2.2, 2.7 (local functions)") public class StructFunction extends Structure { + private static org.skriptlang.skript.common.function.FunctionRegistry registry; + + @ApiStatus.Internal + public static void setRegistry(org.skriptlang.skript.common.function.FunctionRegistry registry) { + StructFunction.registry = registry; + } + public static final Priority PRIORITY = new Priority(400); /** @@ -124,10 +135,8 @@ public boolean preLoad() { private static Signature registerSignature(Signature signature) { Retrieval> existing; Parameter[] parameters = signature.parameters().all(); - FunctionRegistry registry = Skript.instance().registry(FunctionRegistry.class); - if (parameters.length == 1 && !parameters[0].isSingle()) { - if (signature.namespace() != null) { + if (signature.hasModifier(Modifier.Local.class)) { existing = registry.getExactSignature(signature.namespace(), signature.name(), parameters[0].type().arrayType()); } else { existing = registry.getExactSignature(signature.name()); @@ -138,7 +147,7 @@ private static Signature registerSignature(Signature signature) { types[i] = parameters[i].type(); } - if (signature.namespace() != null) { + if (signature.hasModifier(Modifier.Local.class)) { existing = registry.getExactSignature(signature.namespace(), signature.name(), types); } else { existing = registry.getExactSignature(signature.name(), types); @@ -167,7 +176,10 @@ private static Signature registerSignature(Signature signature) { return null; } - if (signature.namespace() != null) { + if (signature instanceof ch.njol.skript.lang.function.Signature oldSignature) + Functions.registerCompatibilitySignature(oldSignature); + + if (signature.hasModifier(Modifier.Local.class)) { registry.register(signature.namespace(), signature); } else { registry.register(signature); @@ -195,8 +207,6 @@ public boolean load() { } private static void loadFunction(Script script, SectionNode node, Signature signature) { - FunctionRegistry registry = Skript.instance().registry(FunctionRegistry.class); - Function function; try { function = new ScriptFunction<>((ch.njol.skript.lang.function.Signature) signature, node); @@ -228,7 +238,7 @@ public boolean postLoad() { @Override public void unload() { assert signature != null; - Skript.instance().registry(FunctionRegistry.class).remove(signature); + registry.remove(signature); // signature.calls().forEach(FunctionReference::invalidate); VALIDATE_FUNCTIONS.set(true); } diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java b/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java index 9436a801b10..6bdda06428b 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionParser.java @@ -5,7 +5,6 @@ import ch.njol.skript.classes.ClassInfo; import ch.njol.skript.lang.ParseContext; import ch.njol.skript.lang.SkriptParser; -import ch.njol.skript.lang.function.Functions; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Utils; import ch.njol.skript.util.Utils.PluralResult; @@ -32,7 +31,6 @@ public class FunctionParser { * @param returns The return type of the function, or null if the function should not return anything. * @param local If the signature of function is local. * @return Parsed signature or null if something went wrong. - * @see Functions#registerSignature(ch.njol.skript.lang.function.Signature) */ public static @Nullable Signature parse(String script, String name, String args, @Nullable String returns, boolean local) { Parameters parameters = parseParameters(args); diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java index fe0e33938b0..a429acfc1b1 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionReference.java @@ -39,6 +39,29 @@ public final class FunctionReference implements Debuggable { private record ArgInfo(Expression expression, Class type, Set modifiers) { + /** + * Returns whether this parameter has the specified modifier. + * + * @param modifier The modifier. + * @return True when {@link #modifiers()} contains the specified modifier, false if not. + */ + boolean hasModifier(Class modifier) { + return modifiers().stream().anyMatch(modifier::isInstance); + } + + /** + * Gets a modifier of the specified type if present. + * + * @param modifierClass The class of the modifier to retrieve + * @return The modifier instance, or null if not present + */ + M getModifier(Class modifierClass) { + return modifiers().stream() + .filter(modifierClass::isInstance) + .map(modifierClass::cast) + .findFirst() + .orElse(null); + } } public FunctionReference(@Nullable String namespace, @@ -174,7 +197,7 @@ public T execute(Event event) { SequencedMap args = new LinkedHashMap<>(); cachedArguments.forEach((k, v) -> { - if (v.modifiers().contains(Modifier.KEYED)) { + if (v.hasModifier(Modifier.Keyed.class)) { args.put(k, Classes.clone(evaluateKeyed(v.expression(), event))); return; } diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java index 13111b10507..dfbcca485cd 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java @@ -258,11 +258,6 @@ private void register(@NotNull NamespaceIdentifier namespace, @NotNull Function< } } - @Override - public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getFunction(@NotNull String name, @NotNull Class... args) { - return getFunction(null, name, args); - } - private static void alreadyRegisteredError(String name, FunctionIdentifier identifier, NamespaceIdentifier namespace) { throw new SkriptAPIException("Function '%s' with parameters %s is already registered in %s" .formatted(name, Arrays.toString(Arrays.stream(identifier.args).map(Class::getSimpleName).toArray()), @@ -300,24 +295,21 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull @Override public @NotNull Retrieval> getFunction( - @Nullable String namespace, + @NotNull String namespace, @NotNull String name, @NotNull Class... args ) { - Retrieval> attempt = null; - if (namespace != null) { - attempt = getFunction(new NamespaceIdentifier(namespace), - FunctionIdentifier.of(name, true, args)); - } - if (attempt == null || attempt.result() == RetrievalResult.NOT_REGISTERED) { + Retrieval> attempt = getFunction(new NamespaceIdentifier(namespace), + FunctionIdentifier.of(name, true, args)); + if (attempt.result() == RetrievalResult.NOT_REGISTERED) { attempt = getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } return attempt; } @Override - public org.skriptlang.skript.common.function.FunctionRegistry.@NotNull Retrieval> getSignature(@NotNull String name, @NotNull Class... args) { - return getSignature(null, name, args); + public @NotNull Retrieval> getFunction(@NotNull String name, @NotNull Class... args) { + return getFunction(GLOBAL_NAMESPACE, FunctionIdentifier.of(name, false, args)); } /** @@ -363,6 +355,11 @@ private boolean signatureExists(@NotNull NamespaceIdentifier namespace, @NotNull } } + @Override + public @NotNull Retrieval> getSignature(@NotNull String name, @NotNull Class... args) { + return getSignature(null, name, args); + } + @Override public @NotNull Retrieval> getSignature( @Nullable String namespace, diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameter.java b/src/main/java/org/skriptlang/skript/common/function/Parameter.java index 906b6537275..dd2bf038f50 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameter.java @@ -51,7 +51,7 @@ default boolean hasModifier(Modifier modifier) { } /** - * Returns whether this signature has the specified modifier. + * Returns whether this parameter has the specified modifier. * * @param modifier The modifier. * @return True when {@link #modifiers()} contains the specified modifier, false if not. @@ -204,22 +204,6 @@ record Ranged>(T min, T max) implements Modifier { Preconditions.checkState(min.compareTo(max) < 1, "Min value cannot be greater than max value!"); } - /** - * @return Min value of the range (inclusive) - */ - @Override - public T min() { - return min; - } - - /** - * @return Max value of the range (inclusive) - */ - @Override - public T max() { - return max; - } - /** * @param input The value to test. * @return Whether input is between min and max. diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index 48d924c629a..4899dd61b9d 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -83,12 +83,15 @@ default boolean hasModifier(Class modifier) { } /** - * Returns whether this signature has the specified modifier. + * Gets a modifier of the specified type if present. + * + * @param modifierClass The class of the modifier to retrieve + * @return The modifier instance, or null if not present */ - default MT getModifier(Class modifier) { + default M getModifier(Class modifierClass) { return modifiers().stream() - .filter(modifier::isInstance) - .map(modifier::cast) + .filter(modifierClass::isInstance) + .map(modifierClass::cast) .findAny() .orElse(null); } From 7e72acd044fdd3af06d77843999023369b8c5cb7 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:50:34 +0200 Subject: [PATCH 25/32] convert old modifiers --- .../skript/classes/data/DefaultFunctions.java | 20 +++++++++---------- .../common/function/DefaultFunctionImpl.java | 2 ++ .../common/function/FunctionRegistryImpl.java | 4 ++++ .../common/function/DefaultFunctionTest.java | 4 ++-- 4 files changed, 18 insertions(+), 12 deletions(-) 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 818b402af0c..981f44fda0c 100644 --- a/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java +++ b/src/main/java/ch/njol/skript/classes/data/DefaultFunctions.java @@ -365,7 +365,7 @@ Turns a number in a string using a specific base (decimal, hexadecimal, octal). ) .since("2.14") .parameter("n", Long[].class) - .parameter("base", Long.class, Modifier.ranged(2, 36)) + .parameter("base", Long.class, new Modifier.Ranged<>(2, 36)) .contract(new Contract() { @Override public boolean isSingle(Expression... arguments) { @@ -405,7 +405,7 @@ Turns a text version of a number in a specific base (decimal, hexadecimal, octal """) .since("2.14") .parameter("string value", String[].class) - .parameter("base", Long.class, Modifier.ranged(2, 36)) + .parameter("base", Long.class, new Modifier.Ranged<>(2, 36)) .contract(new Contract() { @Override public boolean isSingle(Expression... arguments) { @@ -482,9 +482,9 @@ set block at location({_x}, {_y}, {_z}, {_w}) to stone .parameter("x", Number.class) .parameter("y", Number.class) .parameter("z", Number.class) - .parameter("world", World.class, Modifier.OPTIONAL) - .parameter("yaw", Float.class, Modifier.OPTIONAL) - .parameter("pitch", Float.class, Modifier.OPTIONAL) + .parameter("world", World.class, new Modifier.Optional()) + .parameter("yaw", Float.class, new Modifier.Optional()) + .parameter("pitch", Float.class, new Modifier.Optional()) .build(args -> { World world = args.getOrDefault("world", Bukkit.getWorlds().get(0)); @@ -612,10 +612,10 @@ public Long[] executeSimple(Object[][] params) { "set the colour of a text display to rgb(10, 50, 100, 50)" ) .since("2.5, 2.10 (alpha)") - .parameter("red", Long.class, Modifier.ranged(0, 255)) - .parameter("green", Long.class, Modifier.ranged(0, 255)) - .parameter("blue", Long.class, Modifier.ranged(0, 255)) - .parameter("alpha", Long.class, Modifier.ranged(0, 255), Modifier.OPTIONAL) + .parameter("red", Long.class, new Modifier.Ranged<>(0, 255)) + .parameter("green", Long.class, new Modifier.Ranged<>(0, 255)) + .parameter("blue", Long.class, new Modifier.Ranged<>(0, 255)) + .parameter("alpha", Long.class, new Modifier.Ranged<>(0, 255), new Modifier.Optional()) .build(args -> ColorRGB.fromRGBA( args.get("red").intValue(), args.get("green").intValue(), @@ -635,7 +635,7 @@ public Long[] executeSimple(Object[][] params) { ) .since("2.8.0") .parameter("nameOrUUID", String.class) - .parameter("getExactPlayer", Boolean.class, Modifier.OPTIONAL) + .parameter("getExactPlayer", Boolean.class, new Modifier.Optional()) .build(args -> { String name = args.get("nameOrUUID"); boolean isExact = args.getOrDefault("getExactPlayer", false); diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index 60f4aacd299..95bb7129fa9 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -332,6 +332,8 @@ private static Set modernizeModifiers(Modifier[] modifiers) { } else if (modifier instanceof RangedModifier rangedModifier) { //noinspection rawtypes,unchecked result.add(new Modifier.Ranged(rangedModifier.getMin(), rangedModifier.getMax())); + } else { + result.add(modifier); } } return result; diff --git a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java index dfbcca485cd..49cdc511dfd 100644 --- a/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/FunctionRegistryImpl.java @@ -2,6 +2,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.SkriptAPIException; +import ch.njol.skript.lang.function.Functions; import ch.njol.skript.util.Utils; import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; @@ -200,6 +201,9 @@ private void register(@NotNull NamespaceIdentifier namespace, @NotNull Signature Skript.debug("Registering signature '%s'", signature.name()); + if (signature instanceof ch.njol.skript.lang.function.Signature oldSignature) + Functions.registerCompatibilitySignature(oldSignature); + Namespace ns = namespaces.computeIfAbsent(namespace, n -> new Namespace()); FunctionIdentifier identifier = FunctionIdentifier.of(signature); diff --git a/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java b/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java index d98b644ffed..f34b459bcb6 100644 --- a/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java +++ b/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java @@ -24,7 +24,7 @@ public void testStrings() { .description() .since() .keywords() - .parameter("x", String[].class, Modifier.OPTIONAL) + .parameter("x", String[].class, new Modifier.Optional()) .build(args -> { String[] xes = args.getOrDefault("x", new String[]{""}); @@ -51,7 +51,7 @@ public void testObjectArrays() { .description("x", "y") .since("1", "2") .keywords("x", "y") - .parameter("x", Object[].class, Modifier.OPTIONAL) + .parameter("x", Object[].class, new Modifier.Optional()) .parameter("y", Boolean.class) .build(args -> new Object[]{true, 1}); From 1294ee082e9cd1f564dbfaf63feb26d9b1349113 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Mon, 17 Aug 2026 23:06:54 +0200 Subject: [PATCH 26/32] fix junit --- src/main/java/ch/njol/skript/lang/function/Parameter.java | 8 ++++---- .../skript/common/function/DefaultFunctionTest.java | 7 ++----- .../skript/common/function/FunctionRegistryTest.java | 8 ++++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/Parameter.java b/src/main/java/ch/njol/skript/lang/function/Parameter.java index 7916d6964a4..5ec566f4e7a 100644 --- a/src/main/java/ch/njol/skript/lang/function/Parameter.java +++ b/src/main/java/ch/njol/skript/lang/function/Parameter.java @@ -22,6 +22,7 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * @deprecated Use {@link ScriptParameter} @@ -275,12 +276,11 @@ public boolean isSingleValue() { @Override public boolean equals(Object o) { - if (!(o instanceof Parameter parameter)) { - + if (!(o instanceof Parameter parameter)) return false; - } - return modifiers.equals(parameter.modifiers) + return modifiers.stream().map(Modifier::getClass).collect(Collectors.toSet()) + .equals(parameter.modifiers().stream().map(Modifier::getClass).collect(Collectors.toSet())) && single == parameter.single && name.equals(parameter.name) && type.equals(parameter.type) diff --git a/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java b/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java index f34b459bcb6..73d471b7c81 100644 --- a/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java +++ b/src/test/java/org/skriptlang/skript/common/function/DefaultFunctionTest.java @@ -2,14 +2,11 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.lang.function.FunctionEvent; import ch.njol.skript.lang.function.Signature; import ch.njol.skript.registrations.Classes; import ch.njol.util.StringUtils; import org.junit.Test; import org.skriptlang.skript.addon.SkriptAddon; -import org.skriptlang.skript.common.function.DefaultFunction; -import org.skriptlang.skript.common.function.Parameter; import org.skriptlang.skript.common.function.Parameter.Modifier; import static org.junit.Assert.*; @@ -26,9 +23,9 @@ public void testStrings() { .keywords() .parameter("x", String[].class, new Modifier.Optional()) .build(args -> { - String[] xes = args.getOrDefault("x", new String[]{""}); + String[] xs = args.getOrDefault("x", new String[]{""}); - return StringUtils.join(xes, ","); + return StringUtils.join(xs, ","); }); Signature signature = (Signature) built.signature(); diff --git a/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java b/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java index 133ccb21730..3aa6559b5f1 100644 --- a/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java +++ b/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java @@ -30,11 +30,11 @@ public void testGetFunctionRetrieval() { assertEquals(RetrievalResult.NOT_REGISTERED, registry.getSignature(FUNCTION_NAME).result()); assertNull(registry.getSignature(FUNCTION_NAME).retrieved()); - assertNull(registry.getSignature(FUNCTION_NAME).conflictingArgs()); + assertTrue(registry.getSignature(FUNCTION_NAME).conflictingArgs().isEmpty()); assertEquals(RetrievalResult.NOT_REGISTERED, registry.getFunction(FUNCTION_NAME).result()); assertNull(registry.getFunction(FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(FUNCTION_NAME).conflictingArgs()); + assertTrue(registry.getFunction(FUNCTION_NAME).conflictingArgs().isEmpty()); registry.register(TEST_FUNCTION); @@ -42,11 +42,11 @@ public void testGetFunctionRetrieval() { assertEquals(RetrievalResult.EXACT, registry.getSignature(FUNCTION_NAME).result()); assertEquals(TEST_FUNCTION.signature(), registry.getSignature(FUNCTION_NAME).retrieved()); - assertNull(registry.getSignature(FUNCTION_NAME).conflictingArgs()); + assertTrue(registry.getSignature(FUNCTION_NAME).conflictingArgs().isEmpty()); assertEquals(RetrievalResult.EXACT, registry.getFunction(FUNCTION_NAME).result()); assertEquals(TEST_FUNCTION, registry.getFunction(FUNCTION_NAME).retrieved()); - assertNull(registry.getFunction(FUNCTION_NAME).conflictingArgs()); + assertTrue(registry.getFunction(FUNCTION_NAME).conflictingArgs().isEmpty()); registry.remove(TEST_FUNCTION.signature()); } From 8585358d5f2eaf6e4f6f1e92585228fc2d3575ae Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:01:02 +0200 Subject: [PATCH 27/32] update priority --- .../org/skriptlang/skript/util/Priority.java | 27 ++++++++++++++++++ .../skriptlang/skript/util/PriorityTest.java | 28 ++++++++----------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/util/Priority.java b/src/main/java/org/skriptlang/skript/util/Priority.java index b0f49546a67..6edcce277ed 100644 --- a/src/main/java/org/skriptlang/skript/util/Priority.java +++ b/src/main/java/org/skriptlang/skript/util/Priority.java @@ -50,4 +50,31 @@ static Priority after(Priority priority) { */ @Unmodifiable Collection before(); + /** + * Checks whether this priority is before another priority. + * @param other A priority. + * @return True if the priority provided is before this priority, false if not. + */ + default boolean isBefore(Priority other) { + return compareTo(other) < 0; + } + + /** + * Checks whether this priority is equal to another priority. + * @param other A priority. + * @return True if the priority provided is equal to this priority, false if not. + */ + default boolean isEqual(Priority other) { + return compareTo(other) == 0; + } + + /** + * Checks whether this priority is after another priority. + * @param other A priority. + * @return True if the priority provided is after this priority, false if not. + */ + default boolean isAfter(Priority other) { + return compareTo(other) > 0; + } + } diff --git a/src/test/java/org/skriptlang/skript/util/PriorityTest.java b/src/test/java/org/skriptlang/skript/util/PriorityTest.java index 91ff270458d..6201bc809fd 100644 --- a/src/test/java/org/skriptlang/skript/util/PriorityTest.java +++ b/src/test/java/org/skriptlang/skript/util/PriorityTest.java @@ -24,8 +24,8 @@ public void testBefore() { assertTrue(before.before().contains(base)); assertTrue(before.after().isEmpty()); - assertTrue(before.compareTo(base) < 0); - assertTrue(base.compareTo(before) > 0); + assertTrue(before.isBefore(base)); + assertTrue(base.isAfter(before)); // Different instances, but functionally equal assertEquals(before, Priority.before(base)); @@ -38,8 +38,8 @@ public void testAfter() { assertTrue(after.before().isEmpty()); assertTrue(after.after().contains(base)); - assertTrue(after.compareTo(base) > 0); - assertTrue(base.compareTo(after) < 0); + assertTrue(after.isAfter(base)); + assertTrue(base.isBefore(after)); // Different instances, but functionally equal assertEquals(after, Priority.after(base)); @@ -51,10 +51,8 @@ public void testBoth() { Priority before = Priority.before(base); Priority after = Priority.after(base); - // 'before' should be before 'after' - assertTrue(before.compareTo(after) < 0); - // 'after' should be after 'before' - assertTrue(after.compareTo(before) > 0); + assertTrue(before.isBefore(after)); + assertTrue(after.isAfter(before)); } @Test @@ -63,17 +61,15 @@ public void testComplex() { Priority before = Priority.before(base); Priority afterBefore = Priority.after(before); - // 'afterBefore' should be before 'base' - assertTrue(afterBefore.compareTo(base) < 0); - // 'base' should be after 'afterBefore' - assertTrue(base.compareTo(afterBefore) > 0); + + assertTrue(afterBefore.isBefore(base)); + assertTrue(base.isAfter(afterBefore)); Priority after = Priority.after(base); Priority beforeAfter = Priority.before(after); - // 'beforeAfter' should be after 'base' - assertTrue(beforeAfter.compareTo(base) > 0); - // 'base' should be before 'beforeAfter' - assertTrue(base.compareTo(beforeAfter) < 0); + + assertTrue(beforeAfter.isAfter(base)); + assertTrue(base.isBefore(beforeAfter)); } } From 1e1126441abbf694c714198a4376930c908daf74 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:03:00 +0200 Subject: [PATCH 28/32] add priority for toFormattedString to parameter --- .../common/function/DefaultFunctionImpl.java | 6 +- .../skript/common/function/Parameter.java | 135 ++++++++++++++++-- .../skript/common/function/ParameterTest.java | 19 +++ 3 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 src/test/java/org/skriptlang/skript/common/function/ParameterTest.java diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index 95bb7129fa9..d27cf76a2bb 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -325,11 +325,7 @@ record DefaultParameter(String name, Class type, Set modifiers) private static Set modernizeModifiers(Modifier[] modifiers) { Set result = new LinkedHashSet<>(); for (Modifier modifier : modifiers) { - if (modifier == Modifier.OPTIONAL) { - result.add(new Modifier.Optional()); - } else if (modifier == Modifier.KEYED) { - result.add(new Modifier.Keyed()); - } else if (modifier instanceof RangedModifier rangedModifier) { + if (modifier instanceof RangedModifier rangedModifier) { //noinspection rawtypes,unchecked result.add(new Modifier.Ranged(rangedModifier.getMin(), rangedModifier.getMax())); } else { diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameter.java b/src/main/java/org/skriptlang/skript/common/function/Parameter.java index dd2bf038f50..28afa4c828e 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameter.java @@ -14,9 +14,11 @@ import org.skriptlang.skript.common.function.DefaultFunction.Builder; import org.skriptlang.skript.lang.converter.Converter; import org.skriptlang.skript.lang.converter.Converters; +import org.skriptlang.skript.util.Priority; import java.util.Set; import java.util.StringJoiner; +import java.util.stream.Collectors; /** * Represents a function parameter. @@ -130,8 +132,15 @@ default String toFormattedString() { joiner.add("%s:".formatted(name())); - if (hasModifier(Modifier.Optional.class)) { - joiner.add("optional"); + for (Modifier modifier : modifiers()) { + if (!modifier.toStringPriority().isBefore(Modifier.TYPE_PRIORITY)) { + continue; + } + String string = modifier.toFormattedString(); + if (string.isEmpty()) { + continue; + } + joiner.add(string); } Noun exact = Classes.getSuperClassInfo(type()).getName(); @@ -141,12 +150,15 @@ default String toFormattedString() { joiner.add(exact.getSingular()); } - if (hasModifier(Modifier.Ranged.class)) { - Modifier.Ranged range = getModifier(Modifier.Ranged.class); - joiner.add("between") - .add(range.min().toString()) - .add("and") - .add(range.max().toString()); + for (Modifier modifier : modifiers()) { + if (!modifier.toStringPriority().isAfter(Modifier.TYPE_PRIORITY)) { + continue; + } + String string = modifier.toFormattedString(); + if (string.isEmpty()) { + continue; + } + joiner.add(string); } return joiner.toString(); @@ -159,22 +171,75 @@ default String toFormattedString() { interface Modifier { /** - * @return A new Modifier instance to be used as a custom flag. + * The priority used for printing the type in a parameter's string representation. + */ + Priority TYPE_PRIORITY = Priority.base(); + + /** + * @return The modifier as a human-readable, formatted string. + */ + @NotNull String toFormattedString(); + + /** + * The priority used when converting this parameter to a string representation. + * + *

+ * Registering after {@link #TYPE_PRIORITY} will print after the type, + * e.g. {@code x: number optional}. + * Registering before {@link #TYPE_PRIORITY} will print before the type, + * e.g. {@code x: optional number}. + *

+ * + * @return The priority used. + */ + @NotNull Priority toStringPriority(); + + /** + * @deprecated Create a new class implementing {@link Modifier} instead. */ + @Deprecated(forRemoval = true, since = "INSERT VERSION") static Modifier of() { - return new Modifier() {}; + return new Modifier() { + + private static final Priority PRIORITY = Priority.after(TYPE_PRIORITY); + + @Override + public @NotNull String toFormattedString() { + return ""; + } + + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + + }; } /** * The modifier for parameters that are optional. */ - class Optional implements Modifier { } + class Optional implements Modifier { + + private static final Priority PRIORITY = Priority.before(TYPE_PRIORITY); + + @Override + public @NotNull String toFormattedString() { + return "optional"; + } + + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + + } /** * @deprecated Use {@link Optional} instead. */ @Deprecated(forRemoval = true, since = "INSERT VERSION") - Modifier OPTIONAL = of(); + Modifier OPTIONAL = new Modifier.Optional(); /** * The modifier for parameters that support optional keyed expressions. @@ -182,18 +247,35 @@ class Optional implements Modifier { } * @see ch.njol.skript.lang.KeyProviderExpression * @see ch.njol.skript.lang.KeyReceiverExpression */ - class Keyed implements Modifier { } + class Keyed implements Modifier { + + private static final Priority PRIORITY = Priority.before(TYPE_PRIORITY); + + @Override + public @NotNull String toFormattedString() { + return "keyed"; + } + + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + + } /** * @deprecated Use {@link Keyed} instead. */ @Deprecated(forRemoval = true, since = "INSERT VERSION") - Modifier KEYED = of(); + Modifier KEYED = new Modifier.Keyed(); /** * A modifier to use for checking if a parameter is ranged. */ record Ranged>(T min, T max) implements Modifier { + + private static final Priority PRIORITY = Priority.after(TYPE_PRIORITY); + /** * Inclusive range between min and max * @@ -236,6 +318,17 @@ public boolean inRange(Object @NotNull [] inputs) { } return true; } + + @Override + public @NotNull String toFormattedString() { + return "between %s and %s".formatted(min, max); + } + + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + } /** @@ -257,6 +350,9 @@ static > RangedModifier ranged(T min, T max) { */ @Deprecated(forRemoval = true, since = "INSERT VERSION") class RangedModifier> implements Modifier { + + private static final Priority PRIORITY = Priority.before(TYPE_PRIORITY); + private final T min; private final T max; @@ -335,7 +431,18 @@ public String toString() { return "RangedModifier(min=" + min + ", max=" + max + ")"; } + @Override + public @NotNull String toFormattedString() { + return "between %s and %s".formatted(min, max); + } + + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + } + } } diff --git a/src/test/java/org/skriptlang/skript/common/function/ParameterTest.java b/src/test/java/org/skriptlang/skript/common/function/ParameterTest.java new file mode 100644 index 00000000000..a2c577ed429 --- /dev/null +++ b/src/test/java/org/skriptlang/skript/common/function/ParameterTest.java @@ -0,0 +1,19 @@ +package org.skriptlang.skript.common.function; + +import org.junit.Test; +import org.skriptlang.skript.common.function.DefaultFunctionImpl.DefaultParameter; +import org.skriptlang.skript.common.function.Parameter.Modifier; + +import static org.junit.Assert.assertEquals; + +public class ParameterTest { + + @Test + public void testToFormattedString() { + DefaultParameter param = new DefaultParameter<>("x", Number.class, + new Modifier.Optional(), new Modifier.Keyed(), new Modifier.Ranged<>(0, 10)); + + assertEquals("x: optional keyed number between 0 and 10", param.toFormattedString()); + } + +} From b81bb17b11054b669d5585f08a128cb5fb595a02 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:03:10 +0200 Subject: [PATCH 29/32] add dynamic toString for signature --- .../ch/njol/skript/lang/function/Signature.java | 13 +++++++++++-- .../skript/common/function/Signature.java | 14 +++++++++++++- .../function/FunctionArgumentParserTest.java | 1 - 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index dc21b251f72..7e8d539c9b9 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -2,6 +2,7 @@ import ch.njol.skript.Skript; import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.localization.Noun; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Contract; import ch.njol.skript.util.Utils; @@ -12,7 +13,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.common.function.FunctionReference; +import org.skriptlang.skript.common.function.Parameter.Modifier; import org.skriptlang.skript.common.function.Parameters; +import org.skriptlang.skript.util.Priority; import java.util.*; @@ -311,8 +314,14 @@ public String toString() { public String toString(boolean includeReturnType, boolean debug) { StringBuilder signatureBuilder = new StringBuilder(); - if (local) - signatureBuilder.append("local "); + for (Modifier modifier : modifiers()) { + String string = modifier.toFormattedString(); + if (string == null || string.isEmpty()) { + continue; + } + signatureBuilder.append(string).append(" "); + } + signatureBuilder.append(name); signatureBuilder.append('(') diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index 4899dd61b9d..3da58b017f2 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -101,7 +101,19 @@ default M getModifier(Class modifierClass) { */ interface Modifier { - class Local implements Modifier { } + /** + * @return The modifier as a human-readable, formatted string. + */ + String toFormattedString(); + + class Local implements Modifier { + + @Override + public String toFormattedString() { + return "local"; + } + + } } diff --git a/src/test/java/org/skriptlang/skript/common/function/FunctionArgumentParserTest.java b/src/test/java/org/skriptlang/skript/common/function/FunctionArgumentParserTest.java index b4e6e824367..c5f7932441b 100644 --- a/src/test/java/org/skriptlang/skript/common/function/FunctionArgumentParserTest.java +++ b/src/test/java/org/skriptlang/skript/common/function/FunctionArgumentParserTest.java @@ -1,7 +1,6 @@ package org.skriptlang.skript.common.function; import org.junit.Test; -import org.skriptlang.skript.common.function.FunctionArgumentParser; import org.skriptlang.skript.common.function.FunctionReference.Argument; import org.skriptlang.skript.common.function.FunctionReference.ArgumentType; From c6b6239018f5386783472aee6f52bc80c07ad34a Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 18 Aug 2026 00:04:04 +0200 Subject: [PATCH 30/32] update imports --- .../org/skriptlang/skript/common/function/Parameter.java | 1 - .../org/skriptlang/skript/common/function/Parameters.java | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameter.java b/src/main/java/org/skriptlang/skript/common/function/Parameter.java index 28afa4c828e..70f3f95d299 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameter.java @@ -18,7 +18,6 @@ import java.util.Set; import java.util.StringJoiner; -import java.util.stream.Collectors; /** * Represents a function parameter. diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameters.java b/src/main/java/org/skriptlang/skript/common/function/Parameters.java index 8a70b9dafac..b349b64ea5f 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameters.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameters.java @@ -4,7 +4,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.SequencedMap; /** * Holding class for a parameters of a function. From 7038f19fba3dcfb208ae284435dbe27727b29214 Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 18 Aug 2026 01:13:01 +0200 Subject: [PATCH 31/32] add support for void DefaultFunctions --- .../lang/function/FunctionReference.java | 4 +- .../njol/skript/lang/function/Parameter.java | 10 +- .../njol/skript/lang/function/Signature.java | 117 +++++++---------- .../common/function/DefaultFunction.java | 15 ++- .../common/function/DefaultFunctionImpl.java | 59 +++++---- .../skript/common/function/Parameter.java | 41 +++--- .../skript/common/function/Signature.java | 124 ++++++++++++++++-- 7 files changed, 237 insertions(+), 133 deletions(-) diff --git a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java index 947d88f835f..3fb38ebe4cc 100644 --- a/src/main/java/ch/njol/skript/lang/function/FunctionReference.java +++ b/src/main/java/ch/njol/skript/lang/function/FunctionReference.java @@ -159,8 +159,7 @@ public boolean validateFunction(boolean first) { // Validate that return types are what caller expects they are Class[] expectedReturnTypes = this.returnTypes; if (expectedReturnTypes != null) { - Class candidateReturnType = sign.returnType(); - if (candidateReturnType == null) { + if (!sign.hasModifier(Signature.Modifier.Returns.class)) { if (first) { Skript.error("The function '" + stringified + "' doesn't return any value."); } else { @@ -171,6 +170,7 @@ public boolean validateFunction(boolean first) { return false; } + Class candidateReturnType = sign.getModifier(Signature.Modifier.Returns.class).type(); if (!Converters.converterExists(candidateReturnType, expectedReturnTypes)) { if (first) { Skript.error("The returned value of the function '" + stringified + "', " + candidateReturnType + ", is " + SkriptParser.notOfType(expectedReturnTypes) + "."); diff --git a/src/main/java/ch/njol/skript/lang/function/Parameter.java b/src/main/java/ch/njol/skript/lang/function/Parameter.java index 5ec566f4e7a..e6d2d3da4fc 100644 --- a/src/main/java/ch/njol/skript/lang/function/Parameter.java +++ b/src/main/java/ch/njol/skript/lang/function/Parameter.java @@ -315,7 +315,15 @@ public String toString(boolean debug) { @Override public @NotNull Class type() { //noinspection unchecked - return (Class) Signature.getReturns(single, type.getC()); + return (Class) getReturns(single, type.getC()); + } + + static Class getReturns(boolean single, Class cls) { + if (single) { + return cls; + } else { + return cls.arrayType(); + } } @Override diff --git a/src/main/java/ch/njol/skript/lang/function/Signature.java b/src/main/java/ch/njol/skript/lang/function/Signature.java index 7e8d539c9b9..272f46450f6 100644 --- a/src/main/java/ch/njol/skript/lang/function/Signature.java +++ b/src/main/java/ch/njol/skript/lang/function/Signature.java @@ -1,8 +1,6 @@ package ch.njol.skript.lang.function; -import ch.njol.skript.Skript; import ch.njol.skript.classes.ClassInfo; -import ch.njol.skript.localization.Noun; import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Contract; import ch.njol.skript.util.Utils; @@ -13,9 +11,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.common.function.FunctionReference; -import org.skriptlang.skript.common.function.Parameter.Modifier; import org.skriptlang.skript.common.function.Parameters; -import org.skriptlang.skript.util.Priority; +import org.skriptlang.skript.common.function.Signature.Modifier.Returns; import java.util.*; @@ -29,26 +26,14 @@ public class Signature implements org.skriptlang.skript.common.function.Signa */ final @Nullable String script; - /** - * Name of function this refers to. - */ - final String name; // Stored for hashCode - - /** - * Parameters taken by this function, in order. - */ + private final String name; private final Parameters parameters; - - /** - * Whether this function is only accessible in the script it was declared in - */ - final boolean local; + private final Set modifiers = new HashSet<>(); /** * The return type. */ final @Nullable ClassInfo returnType; - final Class returns; /** * Whether this function returns a single value, or multiple ones. @@ -67,58 +52,43 @@ public class Signature implements org.skriptlang.skript.common.function.Signa final @Nullable Contract contract; /** - * The class path for the origin of this signature. + * @deprecated Use {@link Signature#Signature(String, Set, SequencedMap, Contract)} instead. */ - @Nullable String originClassPath; - - private final Set modifiers = new HashSet<>(); - - static Class getReturns(boolean single, Class cls) { - if (single) { - return cls; - } else { - return cls.arrayType(); - } - } - + @Deprecated(since = "INSERT VERSION", forRemoval = true) public Signature(@Nullable String script, String name, Parameter[] parameters, boolean local, @Nullable ClassInfo returnType, boolean single, @Nullable Contract contract) { this.script = script; this.name = name; this.parameters = initParameters(parameters); - this.local = local; if (local) - modifiers.add(new Modifier.Local()); + modifiers.add(new Modifier.Local(script)); this.returnType = returnType; this.single = single; - if (returnType == null) { - this.returns = null; - } else { - this.returns = getReturns(single, returnType.getC()); - } this.contract = contract; this.calls = Collections.newSetFromMap(new WeakHashMap<>()); - this.originClassPath = ""; } + /** + * @deprecated Use {@link Signature#Signature(String, Set, SequencedMap, Contract)} instead. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public Signature(@Nullable String script, String name, Parameter[] parameters, boolean local, @Nullable ClassInfo returnType, boolean single, String stacktrace) { this(script, name, parameters, local, returnType, single, (Contract) null); - this.originClassPath = stacktrace; } + /** + * @deprecated Use {@link Signature#Signature(String, Set, SequencedMap, Contract)} instead. + */ + @Deprecated(since = "INSERT VERSION", forRemoval = true) public Signature(String script, String name, Parameter[] parameters, boolean local, ClassInfo returnType, boolean single, String stacktrace, @Nullable Contract contract) { this(script, name, parameters, local, returnType, single, contract); - - this.originClassPath = stacktrace; } public Signature(@Nullable String script, String name, Parameters parameters, Class returnType, boolean local, @Nullable Contract contract) { this.script = script; this.name = name; this.parameters = parameters; - this.local = local; if (local) - modifiers.add(new Modifier.Local()); - this.returns = returnType; + modifiers.add(new Modifier.Local(script)); if (returnType != null) { //noinspection unchecked this.returnType = (ClassInfo) Classes.getExactClassInfo(Utils.getComponentType(returnType)); @@ -139,8 +109,30 @@ public Signature(String namespace, String name, org.skriptlang.skript.common.fun this(namespace, name, initParameters(parameters), returnType, false, contract); } - public Signature(String namespace, String name, org.skriptlang.skript.common.function.Parameter[] parameters, Class returnType, boolean local, boolean single, @Nullable Contract contract) { - this(namespace, name, initParameters(parameters), returnType, local, contract); + public Signature(String name, Set modifiers, SequencedMap> parameters, @Nullable Contract contract) { + this.modifiers.addAll(modifiers); + + this.name = name; + this.parameters = new Parameters(parameters); + + if (hasModifier(Modifier.Local.class)) { + this.script = getModifier(Modifier.Local.class).namespace(); + } else { + this.script = null; + } + + if (hasModifier(Modifier.Returns.class)) { + Class type = getModifier(Returns.class).type(); + //noinspection unchecked + this.returnType = (ClassInfo) Classes.getExactClassInfo(Utils.getComponentType(type)); + this.single = !type.isArray(); + } else { + this.returnType = null; + this.single = true; + } + + this.contract = contract; + this.calls = Collections.newSetFromMap(new WeakHashMap<>()); } @Override @@ -190,12 +182,6 @@ public Parameter[] getParameters() { .toArray(Parameter[]::new); } - @Override - public @Nullable Class returnType() { - //noinspection unchecked - return (Class) returns; - } - @Override public @NotNull Parameters parameters() { return parameters; @@ -226,7 +212,7 @@ public String getName() { } public boolean isLocal() { - return local; + return hasModifier(Modifier.Local.class); } /** @@ -254,14 +240,6 @@ public boolean isSingle() { return Collections.unmodifiableSet(modifiers); } - /** - * @deprecated Unused. - */ - @Deprecated(forRemoval = true, since = "2.13") - public String getOriginClassPath() { - return originClassPath; - } - public @Nullable Contract getContract() { return contract; } @@ -308,19 +286,14 @@ public int hashCode() { @Override public String toString() { - return toString(true, Skript.debug()); + return toFormattedString(); } public String toString(boolean includeReturnType, boolean debug) { StringBuilder signatureBuilder = new StringBuilder(); - for (Modifier modifier : modifiers()) { - String string = modifier.toFormattedString(); - if (string == null || string.isEmpty()) { - continue; - } - signatureBuilder.append(string).append(" "); - } + if (isLocal()) + signatureBuilder.append("local "); signatureBuilder.append(name); @@ -328,10 +301,10 @@ public String toString(boolean includeReturnType, boolean debug) { .append(StringUtils.join(parameters.all(), ", ")) .append(')'); - if (includeReturnType && returns != null) { + if (includeReturnType && hasModifier(Modifier.Returns.class)) { signatureBuilder.append(" returns "); - signatureBuilder.append(Utils.toEnglishPlural(returnType.getCodeName(), returns.isArray())); + signatureBuilder.append(Utils.toEnglishPlural(returnType.getCodeName(), getModifier(Modifier.Returns.class).type().isArray())); } return signatureBuilder.toString(); diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java index 6a449908ca7..8fa615f5436 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java @@ -39,6 +39,7 @@ public sealed interface DefaultFunction /** * Creates a new builder for a function. * + * @param source The addon registering this function. * @param name The name of the function. * @param returnType The type of the function. * @param The return type. @@ -49,6 +50,18 @@ public sealed interface DefaultFunction return new DefaultFunctionImpl.BuilderImpl<>(source, name, returnType); } + /** + * Creates a new builder for a function. + * + * @param source The addon registering this function. + * @param name The name of the function. + * @return The builder for a function. + */ + @Contract("_, _ -> new") + static @NotNull Builder builder(@NotNull SkriptAddon source, @NotNull String name) { + return new DefaultFunctionImpl.BuilderImpl<>(source, name); + } + /** * @return The addon this function was registered for. */ @@ -68,7 +81,7 @@ interface Builder { * @return This builder. */ @Contract("_ -> this") - Builder local(@NotNull String namespace); + Builder modifiers(@NotNull Signature.Modifier @NotNull ... modifiers); /** * Sets this function builder's {@link ch.njol.skript.util.Contract}. diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index d27cf76a2bb..8fb0aaacb52 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -1,13 +1,11 @@ package org.skriptlang.skript.common.function; import ch.njol.skript.lang.function.FunctionEvent; -import ch.njol.skript.lang.function.Signature; import com.google.common.base.Preconditions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import org.skriptlang.skript.addon.SkriptAddon; -import org.skriptlang.skript.common.function.Parameter.Modifier; import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier; import java.lang.reflect.Array; @@ -29,21 +27,18 @@ final class DefaultFunctionImpl extends ch.njol.skript.lang.function.Function DefaultFunctionImpl( SkriptAddon source, String name, - String namespace, + Set modifiers, SequencedMap> parameters, - Class returnType, boolean single, @Nullable ch.njol.skript.util.Contract contract, Function execute, String[] description, String[] since, String[] examples, String[] keywords, String[] requires ) { - super(new Signature<>(namespace, name, parameters.values().toArray(new Parameter[0]), - returnType, namespace != null, single, contract)); + super(new ch.njol.skript.lang.function.Signature<>(name, modifiers, parameters, contract)); Preconditions.checkNotNull(source, "source cannot be null"); Preconditions.checkNotNull(name, "name cannot be null"); Preconditions.checkNotNull(parameters, "parameters cannot be null"); - Preconditions.checkNotNull(returnType, "return type cannot be null"); Preconditions.checkNotNull(execute, "execute cannot be null"); this.source = source; @@ -67,15 +62,15 @@ final class DefaultFunctionImpl extends ch.njol.skript.lang.function.Function Parameter parameter = arrayParams[i]; if (arg == null || arg.length == 0) { - if (parameter.hasModifier(Modifier.Optional.class)) { + if (parameter.hasModifier(Parameter.Modifier.Optional.class)) { continue; } else { return null; } } - if (parameter.hasModifier(Modifier.Ranged.class)) { - Modifier.Ranged range = parameter.getModifier(Modifier.Ranged.class); + if (parameter.hasModifier(Parameter.Modifier.Ranged.class)) { + Parameter.Modifier.Ranged range = parameter.getModifier(Parameter.Modifier.Ranged.class); if (!range.inRange(arg)) { return null; } @@ -123,12 +118,12 @@ public T execute(@NotNull FunctionEvent event, @NotNull FunctionArguments arg Parameter parameter = parameters.get(name); Object value = arguments.get(name); - if (value == null && !parameter.hasModifier(Modifier.Optional.class)) { + if (value == null && !parameter.hasModifier(Parameter.Modifier.Optional.class)) { return null; } - if (parameter.hasModifier(Modifier.Ranged.class)) { - Modifier.Ranged range = parameter.getModifier(Modifier.Ranged.class); + if (parameter.hasModifier(Parameter.Modifier.Ranged.class)) { + Parameter.Modifier.Ranged range = parameter.getModifier(Parameter.Modifier.Ranged.class); if (!range.inRange(value)) { return null; } @@ -179,7 +174,7 @@ public boolean resetReturnValue() { } @Override - public org.skriptlang.skript.common.function.@NotNull Signature signature() { + public @NotNull Signature signature() { return getSignature(); } @@ -187,10 +182,9 @@ static class BuilderImpl implements DefaultFunctionImpl.Builder { private final SkriptAddon source; private final String name; - private final Class returnType; private final SequencedMap> parameters = new LinkedHashMap<>(); + private final Set modifiers = new HashSet<>(); - private String namespace = null; private ch.njol.skript.util.Contract contract = null; private String[] description; @@ -206,14 +200,23 @@ static class BuilderImpl implements DefaultFunctionImpl.Builder { this.source = source; this.name = name; - this.returnType = returnType; + this.modifiers.add(new Signature.Modifier.Returns<>(returnType)); + } + + BuilderImpl(@NotNull SkriptAddon source, @NotNull String name) { + Preconditions.checkNotNull(source, "source cannot be null"); + Preconditions.checkNotNull(name, "name cannot be null"); + + this.source = source; + this.name = name; } @Override - public Builder local(@NotNull String namespace) { - Preconditions.checkNotNull(namespace, "namespace cannot be null"); + public Builder modifiers(Signature.Modifier @NotNull ... modifiers) { + Preconditions.checkNotNull(modifiers, "modifiers cannot be null"); + checkNotNull(modifiers, "modifiers contents cannot be null"); - this.namespace = namespace; + this.modifiers.addAll(Set.of(modifiers)); return this; } @@ -271,7 +274,7 @@ public Builder requires(@NotNull String @NotNull ... requires) { } @Override - public Builder parameter(@NotNull String name, @NotNull Class type, Modifier @NotNull ... modifiers) { + public Builder parameter(@NotNull String name, @NotNull Class type, Parameter.Modifier @NotNull ... modifiers) { Preconditions.checkNotNull(name, "name cannot be null"); Preconditions.checkNotNull(type, "type cannot be null"); @@ -283,19 +286,19 @@ public Builder parameter(@NotNull String name, @NotNull Class type, Modifi public DefaultFunction build(@NotNull Function execute) { Preconditions.checkNotNull(execute, "execute cannot be null"); - return new DefaultFunctionImpl<>(source, name, namespace, parameters, - returnType, !returnType.isArray(), contract, execute, + return new DefaultFunctionImpl<>(source, name, modifiers, parameters, + contract, execute, description, since, examples, keywords, requires); } /** - * Checks whether the elements in a {@link String} array are null. + * Checks whether the elements in an array are null. * - * @param strings The strings. + * @param objects The objects. */ - private static void checkNotNull(@NotNull String[] strings, @NotNull String message) { - for (String string : strings) { - Preconditions.checkNotNull(string, message); + private static void checkNotNull(@NotNull Object[] objects, @NotNull String message) { + for (Object object : objects) { + Preconditions.checkNotNull(object, message); } } diff --git a/src/main/java/org/skriptlang/skript/common/function/Parameter.java b/src/main/java/org/skriptlang/skript/common/function/Parameter.java index 70f3f95d299..70806c252ee 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Parameter.java +++ b/src/main/java/org/skriptlang/skript/common/function/Parameter.java @@ -16,6 +16,7 @@ import org.skriptlang.skript.lang.converter.Converters; import org.skriptlang.skript.util.Priority; +import java.util.NoSuchElementException; import java.util.Set; import java.util.StringJoiner; @@ -72,7 +73,22 @@ default M getModifier(Class modifierClass) { .filter(modifierClass::isInstance) .map(modifierClass::cast) .findFirst() - .orElse(null); + .orElseThrow(() -> new NoSuchElementException("No value present for modifier " + modifierClass.getSimpleName())); + } + + /** + * @return Whether this parameter is for single values. + */ + default boolean isSingle() { + return !type().isArray(); + } + + /** + * @deprecated Use {@link #isSingle()} instead. + */ + @Deprecated(forRemoval = true, since = "2.14") + default boolean single() { + return isSingle(); } /** @@ -108,25 +124,10 @@ default Object[] evaluate(@Nullable Expression argument, Event even return KeyedValue.zip(values, keys); } - /** - * @return Whether this parameter is for single values. - */ - default boolean isSingle() { - return !type().isArray(); - } - - /** - * @deprecated Use {@link #isSingle()} instead. - */ - @Deprecated(forRemoval = true, since = "2.14") - default boolean single() { - return isSingle(); - } - /** * @return A human-readable string representing this parameter. */ - default String toFormattedString() { + default @NotNull String toFormattedString() { StringJoiner joiner = new StringJoiner(" "); joiner.add("%s:".formatted(name())); @@ -180,7 +181,7 @@ interface Modifier { @NotNull String toFormattedString(); /** - * The priority used when converting this parameter to a string representation. + * The priority used when converting this modifier to a string representation. * *

* Registering after {@link #TYPE_PRIORITY} will print after the type, @@ -271,7 +272,7 @@ class Keyed implements Modifier { /** * A modifier to use for checking if a parameter is ranged. */ - record Ranged>(T min, T max) implements Modifier { + record Ranged>(@NotNull T min, @NotNull T max) implements Modifier { private static final Priority PRIORITY = Priority.after(TYPE_PRIORITY); @@ -282,6 +283,8 @@ record Ranged>(T min, T max) implements Modifier { * @param max max value */ public Ranged { + Preconditions.checkNotNull(min, "min cannot be null"); + Preconditions.checkNotNull(max, "max cannot be null"); Preconditions.checkState(min.compareTo(max) < 1, "Min value cannot be greater than max value!"); } diff --git a/src/main/java/org/skriptlang/skript/common/function/Signature.java b/src/main/java/org/skriptlang/skript/common/function/Signature.java index 3da58b017f2..ee463130fd6 100644 --- a/src/main/java/org/skriptlang/skript/common/function/Signature.java +++ b/src/main/java/org/skriptlang/skript/common/function/Signature.java @@ -1,14 +1,22 @@ package org.skriptlang.skript.common.function; +import ch.njol.skript.localization.Noun; +import ch.njol.skript.registrations.Classes; import ch.njol.skript.util.Contract; +import ch.njol.util.StringUtils; +import com.google.common.base.Preconditions; import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.ApiStatus.Internal; import org.jetbrains.annotations.ApiStatus.NonExtendable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; +import org.skriptlang.skript.common.function.Signature.Modifier.Returns; +import org.skriptlang.skript.util.Priority; +import java.util.NoSuchElementException; import java.util.Set; +import java.util.StringJoiner; /** * Represents a function signature. @@ -27,9 +35,17 @@ public interface Signature { @NotNull String name(); /** - * @return The type of this parameter. + * @deprecated Use {@link #hasModifier(Class)} and {@link #getModifier(Class)} + * with {@link Returns} instead. */ - @Nullable Class returnType(); + @Deprecated(forRemoval = true, since = "INSERT VERSION") + default @Nullable Class returnType() { + if (!hasModifier(Modifier.Returns.class)) + return null; + + //noinspection unchecked + return (Class) getModifier(Returns.class).type(); + } /** * @return An unmodifiable view of all the parameters that this signature has. @@ -59,11 +75,10 @@ public interface Signature { * @return Whether this signature returns single values. */ default boolean isSingle() { - Class returnType = returnType(); - if (returnType == null) { + if (!hasModifier(Modifier.Returns.class)) return false; - } - return !returnType.isArray(); + + return !getModifier(Returns.class).type().isArray(); } /** @@ -93,7 +108,41 @@ default M getModifier(Class modifierClass) { .filter(modifierClass::isInstance) .map(modifierClass::cast) .findAny() - .orElse(null); + .orElseThrow(() -> new NoSuchElementException("No value present for modifier " + modifierClass.getSimpleName())); + } + + /** + * @return A human-readable string representing this parameter. + */ + default @NotNull String toFormattedString() { + StringJoiner joiner = new StringJoiner(" "); + + for (Modifier modifier : modifiers()) { + if (!modifier.toStringPriority().isBefore(Modifier.FUNCTION_PRIORITY)) { + continue; + } + String string = modifier.toFormattedString(); + if (string.isEmpty()) { + continue; + } + joiner.add(string); + } + + joiner.add(name()); + joiner.add("(%s)".formatted(StringUtils.join(parameters().all(), ", "))); + + for (Modifier modifier : modifiers()) { + if (!modifier.toStringPriority().isAfter(Modifier.FUNCTION_PRIORITY)) { + continue; + } + String string = modifier.toFormattedString(); + if (string.isEmpty()) { + continue; + } + joiner.add(string); + } + + return joiner.toString(); } /** @@ -101,18 +150,73 @@ default M getModifier(Class modifierClass) { */ interface Modifier { + /** + * The priority used for printing the type in a signature's string representation. + */ + Priority FUNCTION_PRIORITY = Priority.base(); + /** * @return The modifier as a human-readable, formatted string. */ - String toFormattedString(); + @NotNull String toFormattedString(); - class Local implements Modifier { + /** + * The priority used when converting this modifier to a string representation. + * + *

+ * Registering after {@link #FUNCTION_PRIORITY} will print after the function, + * e.g. {@code function x() local}. + * Registering before {@link #FUNCTION_PRIORITY} will print before the function, + * e.g. {@code local function x()}. + *

+ * + * @return The priority used. + */ + @NotNull Priority toStringPriority(); + + record Local(@NotNull String namespace) implements Modifier { + + public Local { + Preconditions.checkNotNull(namespace, "namespace cannot be null"); + } + + private static final Priority PRIORITY = Priority.before(FUNCTION_PRIORITY); @Override - public String toFormattedString() { + public @NotNull String toFormattedString() { return "local"; } + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + + } + + record Returns(@NotNull Class type) implements Modifier { + + private static final Priority PRIORITY = Priority.after(FUNCTION_PRIORITY); + + public Returns { + Preconditions.checkNotNull(type, "type cannot be null"); + } + + @Override + public @NotNull String toFormattedString() { + Noun exact = Classes.getSuperClassInfo(type).getName(); + if (type.isArray()) { + return "returns " + exact.getPlural(); + } else { + return "returns " + exact.getSingular(); + } + } + + @Override + public @NotNull Priority toStringPriority() { + return PRIORITY; + } + } } From 15740d425b2405ce8823b58b1a792b38928a579f Mon Sep 17 00:00:00 2001 From: Efnilite <35348263+Efnilite@users.noreply.github.com> Date: Tue, 18 Aug 2026 01:27:17 +0200 Subject: [PATCH 32/32] add VoidBuilder --- .../common/function/DefaultFunction.java | 101 ++++++++++++- .../common/function/DefaultFunctionImpl.java | 135 +++++++++++++++--- .../common/function/FunctionRegistryTest.java | 14 +- 3 files changed, 222 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java index 8fa615f5436..958dd211f73 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunction.java @@ -6,6 +6,8 @@ import org.skriptlang.skript.addon.SkriptAddon; import org.skriptlang.skript.common.function.Parameter.Modifier; +import java.util.function.Consumer; + /** * A function that has been implemented in Java, instead of in Skript. *

@@ -58,8 +60,8 @@ public sealed interface DefaultFunction * @return The builder for a function. */ @Contract("_, _ -> new") - static @NotNull Builder builder(@NotNull SkriptAddon source, @NotNull String name) { - return new DefaultFunctionImpl.BuilderImpl<>(source, name); + static @NotNull VoidBuilder builder(@NotNull SkriptAddon source, @NotNull String name) { + return new DefaultFunctionImpl.VoidBuilderImpl<>(source, name); } /** @@ -75,9 +77,9 @@ public sealed interface DefaultFunction interface Builder { /** - * Designates this function to be local. + * Adds modifiers to this builder. * - * @param namespace The namespace for this function. + * @param modifiers The modifiers for this function. * @return This builder. */ @Contract("_ -> this") @@ -158,4 +160,95 @@ interface Builder { } + /** + * Represents a builder for {@link DefaultFunction DefaultFunctions} with no return value. + * + * @param The return type of the function. + */ + interface VoidBuilder { + + /** + * Adds modifiers to this builder. + * + * @param modifiers The modifiers for this function. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder modifiers(@NotNull Signature.Modifier @NotNull ... modifiers); + + /** + * Sets this function builder's {@link ch.njol.skript.util.Contract}. + * + * @param contract The contract. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder contract(@NotNull ch.njol.skript.util.Contract contract); + + /** + * Sets this function builder's description. + * + * @param description The description. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder description(@NotNull String @NotNull ... description); + + /** + * Sets this function builder's version history. + * + * @param since The version information. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder since(@NotNull String @NotNull ... since); + + /** + * Sets this function builder's examples. + * + * @param examples The examples. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder examples(@NotNull String @NotNull ... examples); + + /** + * Sets this function builder's keywords. + * + * @param keywords The keywords. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder keywords(@NotNull String @NotNull ... keywords); + + /** + * Sets this function builder's requires. + * + * @param requires The requirements. + * @return This builder. + */ + @Contract("_ -> this") + VoidBuilder requires(@NotNull String @NotNull ... requires); + + /** + * Adds a parameter to this function builder. + * + * @param name The parameter name. + * @param type The type of the parameter. + * @param modifiers The {@link Modifier}s to apply to this parameter. + * @return This builder. + */ + @Contract("_, _, _ -> this") + VoidBuilder parameter(@NotNull String name, @NotNull Class type, Modifier @NotNull ... modifiers); + + /** + * Completes this builder with the code to execute on call of this function. + * + * @param execute The code to execute. + * @return The final function. + */ + DefaultFunction build(@NotNull Consumer execute); + + } + } diff --git a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java index 8fb0aaacb52..9c73095dada 100644 --- a/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java +++ b/src/main/java/org/skriptlang/skript/common/function/DefaultFunctionImpl.java @@ -10,6 +10,7 @@ import java.lang.reflect.Array; import java.util.*; +import java.util.function.Consumer; import java.util.function.Function; final class DefaultFunctionImpl extends ch.njol.skript.lang.function.Function implements DefaultFunction { @@ -203,14 +204,6 @@ static class BuilderImpl implements DefaultFunctionImpl.Builder { this.modifiers.add(new Signature.Modifier.Returns<>(returnType)); } - BuilderImpl(@NotNull SkriptAddon source, @NotNull String name) { - Preconditions.checkNotNull(source, "source cannot be null"); - Preconditions.checkNotNull(name, "name cannot be null"); - - this.source = source; - this.name = name; - } - @Override public Builder modifiers(Signature.Modifier @NotNull ... modifiers) { Preconditions.checkNotNull(modifiers, "modifiers cannot be null"); @@ -291,15 +284,112 @@ public DefaultFunction build(@NotNull Function execute) description, since, examples, keywords, requires); } - /** - * Checks whether the elements in an array are null. - * - * @param objects The objects. - */ - private static void checkNotNull(@NotNull Object[] objects, @NotNull String message) { - for (Object object : objects) { - Preconditions.checkNotNull(object, message); - } + } + + static class VoidBuilderImpl implements DefaultFunctionImpl.VoidBuilder { + + private final SkriptAddon source; + private final String name; + private final SequencedMap> parameters = new LinkedHashMap<>(); + private final Set modifiers = new HashSet<>(); + + private ch.njol.skript.util.Contract contract = null; + + private String[] description; + private String[] since; + private String[] examples; + private String[] keywords; + private String[] requires; + + VoidBuilderImpl(@NotNull SkriptAddon source, @NotNull String name) { + Preconditions.checkNotNull(source, "source cannot be null"); + Preconditions.checkNotNull(name, "name cannot be null"); + + this.source = source; + this.name = name; + } + + @Override + public VoidBuilder modifiers(Signature.Modifier @NotNull ... modifiers) { + Preconditions.checkNotNull(modifiers, "modifiers cannot be null"); + checkNotNull(modifiers, "modifiers contents cannot be null"); + + this.modifiers.addAll(Set.of(modifiers)); + return this; + } + + @Override + public VoidBuilder contract(@NotNull ch.njol.skript.util.Contract contract) { + Preconditions.checkNotNull(contract, "contract cannot be null"); + + this.contract = contract; + return this; + } + + @Override + public VoidBuilder description(@NotNull String @NotNull ... description) { + Preconditions.checkNotNull(description, "description cannot be null"); + checkNotNull(description, "description contents cannot be null"); + + this.description = description; + return this; + } + + @Override + public VoidBuilder since(@NotNull String @NotNull ... since) { + Preconditions.checkNotNull(since, "since cannot be null"); + checkNotNull(since, "since contents cannot be null"); + + this.since = since; + return this; + } + + @Override + public VoidBuilder examples(@NotNull String @NotNull ... examples) { + Preconditions.checkNotNull(examples, "examples cannot be null"); + checkNotNull(examples, "examples contents cannot be null"); + + this.examples = examples; + return this; + } + + @Override + public VoidBuilder keywords(@NotNull String @NotNull ... keywords) { + Preconditions.checkNotNull(keywords, "keywords cannot be null"); + checkNotNull(keywords, "keywords contents cannot be null"); + + this.keywords = keywords; + return this; + } + + @Override + public VoidBuilder requires(@NotNull String @NotNull ... requires) { + Preconditions.checkNotNull(keywords, "requires cannot be null"); + checkNotNull(keywords, "requires contents cannot be null"); + + this.requires = requires; + return this; + } + + @Override + public VoidBuilder parameter(@NotNull String name, @NotNull Class type, Parameter.Modifier @NotNull ... modifiers) { + Preconditions.checkNotNull(name, "name cannot be null"); + Preconditions.checkNotNull(type, "type cannot be null"); + + parameters.put(name, new DefaultParameter<>(name, type, modifiers)); + return this; + } + + @Override + public DefaultFunction build(@NotNull Consumer execute) { + Preconditions.checkNotNull(execute, "execute cannot be null"); + + return new DefaultFunctionImpl<>(source, name, modifiers, parameters, contract, + (args) -> { + execute.accept(args); + return null; + }, + description, since, examples, keywords, requires); } } @@ -339,4 +429,15 @@ private static Set modernizeModifiers(Modifier[] modifiers) { } } + /** + * Checks whether the elements in an array are null. + * + * @param objects The objects. + */ + private static void checkNotNull(@NotNull Object[] objects, @NotNull String message) { + for (Object object : objects) { + Preconditions.checkNotNull(object, message); + } + } + } diff --git a/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java b/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java index 3aa6559b5f1..f3702f3ba69 100644 --- a/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java +++ b/src/test/java/org/skriptlang/skript/common/function/FunctionRegistryTest.java @@ -234,15 +234,15 @@ public void testRegisterRemoveRegisterGlobal() { registry.remove(TEST_FUNCTION_N.signature()); } - private static final Function LOCAL_TEST_FUNCTION_B = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) - .local(TEST_SCRIPT) + private static final Function LOCAL_TEST_FUNCTION_B = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME) + .modifiers(new org.skriptlang.skript.common.function.Signature.Modifier.Local(TEST_SCRIPT)) .parameter("a", Boolean.class) - .build(b -> true); + .build(b -> {}); - private static final Function LOCAL_TEST_FUNCTION_N = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) - .local(TEST_SCRIPT) + private static final Function LOCAL_TEST_FUNCTION_N = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME) + .modifiers(new org.skriptlang.skript.common.function.Signature.Modifier.Local(TEST_SCRIPT)) .parameter("a", Number.class) - .build(b -> true); + .build(b -> {}); @Test public void testRegisterRemoveRegisterLocal() { @@ -359,7 +359,7 @@ public void testIdentifierSignatureOf() { } private static final Function LOCAL_TEST_FUNCTION = DefaultFunction.builder(Skript.instance(), FUNCTION_NAME, Boolean.class) - .local(TEST_SCRIPT) + .modifiers(new org.skriptlang.skript.common.function.Signature.Modifier.Local(TEST_SCRIPT)) .build(b -> true); // see https://github.com/SkriptLang/Skript/pull/8015