diff --git a/buildSrc/src/main/kotlin/litecommands-repositories.gradle.kts b/buildSrc/src/main/kotlin/litecommands-repositories.gradle.kts index 8f1d769d7..03e27cfb2 100644 --- a/buildSrc/src/main/kotlin/litecommands-repositories.gradle.kts +++ b/buildSrc/src/main/kotlin/litecommands-repositories.gradle.kts @@ -7,7 +7,7 @@ repositories { maven("https://maven.fabricmc.net/") // fabric maven("https://repo.dmulloy2.net/repository/public/") // protocol lib - maven("https://papermc.io/repo/repository/maven-public/") // paper, adventure, velocity + maven("https://repo.papermc.io/repository/maven-public/") // paper, adventure, velocity maven("https://repo.opencollab.dev/maven-snapshots") // nukkit maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // spigot maven("https://jitpack.io/") // minestom diff --git a/examples/bukkit/build.gradle.kts b/examples/bukkit/build.gradle.kts index 6ebcc8f95..b6521f273 100644 --- a/examples/bukkit/build.gradle.kts +++ b/examples/bukkit/build.gradle.kts @@ -45,7 +45,7 @@ tasks.shadowJar { tasks.withType { options.compilerArgs.add("-parameters") - options.release = 17 + options.release = 21 } sourceSets.test { @@ -54,6 +54,6 @@ sourceSets.test { } tasks.runServer { - minecraftVersion("1.21") + minecraftVersion("1.21.4") allJvmArgs = listOf("-DPaper.IgnoreJavaVersion=true") } diff --git a/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/ExamplePlugin.java b/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/ExamplePlugin.java index db7fc6c7b..d92c7de54 100644 --- a/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/ExamplePlugin.java +++ b/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/ExamplePlugin.java @@ -1,6 +1,7 @@ package dev.rollczi.example.bukkit; import dev.rollczi.example.bukkit.argument.GameModeArgument; +import dev.rollczi.example.bukkit.command.CatCommand; import dev.rollczi.example.bukkit.command.ConvertCommand; import dev.rollczi.example.bukkit.command.FlyCommand; import dev.rollczi.example.bukkit.command.GameModeCommand; @@ -67,6 +68,7 @@ public void onEnable() { new NumberCommand(), new CurrencyCommand(currencyService), new CurrencyBalanceCommand(currencyService), + new CatCommand(), new UserCommand() ) diff --git a/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/CatCommand.java b/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/CatCommand.java new file mode 100644 index 000000000..50973c743 --- /dev/null +++ b/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/CatCommand.java @@ -0,0 +1,20 @@ +package dev.rollczi.example.bukkit.command; + +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import org.bukkit.Location; +import org.bukkit.entity.Cat; +import org.bukkit.entity.EntityType; + +@Command(name = "cat") +public class CatCommand { + + @Execute + void executeCat(@Context Location currentLocation, @Arg Cat.Type type) { + Cat cat = (Cat) currentLocation.getWorld().spawnEntity(currentLocation, EntityType.CAT); + cat.setCatType(type); + } + +} diff --git a/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/GiveCommand.java b/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/GiveCommand.java index 2d51c6d82..0bb01a43c 100644 --- a/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/GiveCommand.java +++ b/examples/bukkit/src/main/java/dev/rollczi/example/bukkit/command/GiveCommand.java @@ -21,7 +21,7 @@ public void execute( @Arg("item") Material item, @OptionalArg("amount") Integer amount ) { - if (amount != null) { + if (amount == null) { amount = 1; } diff --git a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitFactory.java b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitFactory.java index 22623ba8f..45bf1708c 100644 --- a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitFactory.java +++ b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitFactory.java @@ -4,6 +4,8 @@ import dev.rollczi.litecommands.LiteCommandsFactory; import dev.rollczi.litecommands.bukkit.argument.LocationArgument; import dev.rollczi.litecommands.bukkit.argument.OfflinePlayerArgument; +import dev.rollczi.litecommands.bukkit.argument.OldEnumAccessor; +import dev.rollczi.litecommands.bukkit.argument.OldEnumArgument; import dev.rollczi.litecommands.bukkit.argument.PlayerArgument; import dev.rollczi.litecommands.bukkit.argument.WorldArgument; import dev.rollczi.litecommands.bukkit.context.LocationContext; @@ -11,6 +13,7 @@ import dev.rollczi.litecommands.bukkit.context.WorldContext; import dev.rollczi.litecommands.bukkit.util.BukkitFallbackPrefixUtil; import dev.rollczi.litecommands.message.MessageRegistry; +import dev.rollczi.litecommands.reflect.type.TypeRange; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.Server; @@ -75,6 +78,11 @@ public static upwards = (TypeRange) TypeRange.upwards(OldEnumAccessor.getTypeOrThrow()); + builder.advanced().argument(upwards, new OldEnumArgument()); + } }); } diff --git a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitSettings.java b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitSettings.java index eabf62e68..a39a790a9 100644 --- a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitSettings.java +++ b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/LiteBukkitSettings.java @@ -11,10 +11,11 @@ public class LiteBukkitSettings implements PlatformSettings { private BukkitCommandsRegistry commandsRegistry; private TabComplete tabCompleter; - public LiteBukkitSettings() { + public LiteBukkitSettings(BukkitCommandsRegistry commandsRegistry) { + this.commandsRegistry = commandsRegistry; } - LiteBukkitSettings(Server server) { + public LiteBukkitSettings(Server server) { this.commandsRegistry = BukkitCommandsRegistryImpl.create(server); } diff --git a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/argument/OldEnumAccessor.java b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/argument/OldEnumAccessor.java new file mode 100644 index 000000000..5e7743004 --- /dev/null +++ b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/argument/OldEnumAccessor.java @@ -0,0 +1,79 @@ +package dev.rollczi.litecommands.bukkit.argument; + +import dev.rollczi.litecommands.reflect.LiteCommandsReflectException; +import dev.rollczi.litecommands.reflect.ReflectUtil; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +public class OldEnumAccessor { + + private static final Map, Method> VALUE_OF_METHODS = new HashMap<>(); + private static final Map, Method> NAME_METHODS = new HashMap<>(); + private static final Map, Method> VALUES_METHODS = new HashMap<>(); + + public static boolean isAvailable() { + return getType().isPresent(); + } + + public static Class getTypeOrThrow() { + return getType().orElseThrow(() -> new IllegalStateException("OldEnum is not available")); + } + + public static Optional> getType() { + try { + return Optional.of(Class.forName("org.bukkit.util.OldEnum")); + } catch (ClassNotFoundException classNotFoundException) { + return Optional.empty(); + } + } + + public static Object invokeValueOf(Class type, String source) { + if (!isInstanceOfOldEnum(type)) { + throw new IllegalArgumentException("Type is not an instance of OldEnum"); + } + + Method valueOfMethod = VALUE_OF_METHODS.computeIfAbsent(type, key -> ReflectUtil.getMethod(type, "valueOf", String.class)); + try { + return ReflectUtil.invokeStaticMethod(valueOfMethod, source); + } catch (LiteCommandsReflectException exception) { + throw exception.toRuntimeException(); + } + } + + public static String invokeName(Object source) { + Class type = source.getClass(); + if (!isInstanceOfOldEnum(type)) { + throw new IllegalArgumentException("Type is not an instance of OldEnum"); + } + + Method nameMethod = NAME_METHODS.computeIfAbsent(type, key -> ReflectUtil.getMethod(type, "name")); + try { + return ReflectUtil.invokeMethod(nameMethod, source); + } catch (LiteCommandsReflectException exception) { + throw exception.toRuntimeException(); + } + } + + public static Object[] invokeValues(Class type) { + if (!isInstanceOfOldEnum(type)) { + throw new IllegalArgumentException("Type is not an instance of OldEnum"); + } + + Method valuesMethod = VALUES_METHODS.computeIfAbsent(type, key -> ReflectUtil.getMethod(type, "values")); + try { + return ReflectUtil.invokeStaticMethod(valuesMethod); + } catch (LiteCommandsReflectException exception) { + throw exception.toRuntimeException(); + } + } + + private static @NotNull Boolean isInstanceOfOldEnum(Class type) { + return getType() + .map(oldEnum -> oldEnum.isAssignableFrom(type)) + .orElse(false); + } + +} diff --git a/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/argument/OldEnumArgument.java b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/argument/OldEnumArgument.java new file mode 100644 index 000000000..fac26c6e0 --- /dev/null +++ b/litecommands-bukkit/src/dev/rollczi/litecommands/bukkit/argument/OldEnumArgument.java @@ -0,0 +1,50 @@ +package dev.rollczi.litecommands.bukkit.argument; + +import dev.rollczi.litecommands.argument.Argument; +import dev.rollczi.litecommands.argument.parser.ParseResult; +import dev.rollczi.litecommands.argument.resolver.ArgumentResolver; +import dev.rollczi.litecommands.invalidusage.InvalidUsage; +import dev.rollczi.litecommands.invocation.Invocation; +import dev.rollczi.litecommands.suggestion.SuggestionContext; +import dev.rollczi.litecommands.suggestion.SuggestionResult; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.bukkit.command.CommandSender; + +public class OldEnumArgument extends ArgumentResolver { + + private final Map, SuggestionResult> cachedOldEnumSuggestions = new HashMap<>(); + + @Override + public boolean canParse(Argument argument) { + return OldEnumAccessor.getType().map(type -> type.isAssignableFrom(argument.getType().getRawType())) + .orElseThrow(() -> new IllegalStateException("OldEnumArgument can't be used without on old bukkit version")); + } + + @Override + protected ParseResult parse(Invocation invocation, Argument context, String argument) { + try { + return ParseResult.success(OldEnumAccessor.invokeValueOf(context.getType().getRawType(), argument)); + } catch (IllegalArgumentException ignored) { + return ParseResult.failure(InvalidUsage.Cause.INVALID_ARGUMENT); + } + } + + @Override + public SuggestionResult suggest(Invocation invocation, Argument argument, SuggestionContext context) { + Class oldEnumClass = argument.getType().getRawType(); + + return cachedOldEnumSuggestions.computeIfAbsent(oldEnumClass, key -> { + Object[] oldEnums = OldEnumAccessor.invokeValues(oldEnumClass); + if (oldEnums.length == 0) { + return SuggestionResult.empty(); + } + + return Arrays.stream(oldEnums) + .map(oldEnum -> OldEnumAccessor.invokeName(oldEnum)) + .collect(SuggestionResult.collector()); + }); + } + +} diff --git a/litecommands-core/src/dev/rollczi/litecommands/LiteCommandsException.java b/litecommands-core/src/dev/rollczi/litecommands/LiteCommandsException.java index 70e36a17f..9d4f224d7 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/LiteCommandsException.java +++ b/litecommands-core/src/dev/rollczi/litecommands/LiteCommandsException.java @@ -24,4 +24,11 @@ public LiteCommandsException(String message, List exception exceptions.forEach(exception -> addSuppressed(exception)); } + public RuntimeException toRuntimeException() { + if (getCause() instanceof Exception) { + return (RuntimeException) getCause(); + } + return this; + } + } diff --git a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParseResult.java b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParseResult.java index 189f9bd70..d1e61d633 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParseResult.java +++ b/litecommands-core/src/dev/rollczi/litecommands/argument/parser/ParseResult.java @@ -12,6 +12,7 @@ import java.util.function.Function; import java.util.function.Supplier; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.UnknownNullability; public interface ParseResult extends RequirementFutureResult { @@ -71,7 +72,7 @@ static ParseAsyncResult completableFuture(CompletableFuture } @ApiStatus.Experimental - static ParseAsyncResult completableFuture(CompletableFuture future, Function> mapper) { + static ParseAsyncResult completableFuture(CompletableFuture future, Function<@UnknownNullability T, ? extends ParseResult> mapper) { return new ParseAsyncResult<>(future.thenApply(mapper)); } diff --git a/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java b/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java index 0b00f3b58..5e5df487b 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java +++ b/litecommands-core/src/dev/rollczi/litecommands/reflect/IterableSuperClassResolver.java @@ -24,11 +24,14 @@ public Iterator> iterator() { private class TypeIterator implements Iterator> { private Class next; + private Class lastSuperclass; private final Queue> interfaces = new LinkedList<>(); private final Set> visitedInterfaces = new HashSet<>(); private TypeIterator() { this.next = baseType; + this.lastSuperclass = baseType; + this.interfaces.addAll(ReflectIndex.getInterfaces(baseType)); } @Override @@ -60,10 +63,11 @@ public Class next() { return nextToReturn; } - Class superclass = nextToReturn.getSuperclass(); + Class superclass = this.lastSuperclass.getSuperclass(); if (superclass != null) { this.next = superclass; + this.lastSuperclass = superclass; interfaces.addAll(ReflectIndex.getInterfaces(superclass)); return nextToReturn; } diff --git a/litecommands-core/src/dev/rollczi/litecommands/reflect/ReflectUtil.java b/litecommands-core/src/dev/rollczi/litecommands/reflect/ReflectUtil.java index 83915cfa6..265277a6a 100644 --- a/litecommands-core/src/dev/rollczi/litecommands/reflect/ReflectUtil.java +++ b/litecommands-core/src/dev/rollczi/litecommands/reflect/ReflectUtil.java @@ -4,6 +4,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -147,8 +148,11 @@ public static T invokeMethod(Method method, Object instance, Object... param try { return (T) method.invoke(instance, params); } - catch (Exception exception) { - throw new LiteCommandsReflectException("Unable to invoke method " + method.getName() + " in " + instance.getClass(), exception); + catch (InvocationTargetException invocationTargetException) { + throw new LiteCommandsReflectException("Unable to invoke method " + method.getName() + " in " + instance.getClass(), invocationTargetException.getCause()); + } + catch (IllegalAccessException exception) { + throw new LiteCommandsReflectException("Cannot access method " + method.getName() + " in " + instance.getClass(), exception); } }