From 92469ec4fbc4286e0e1e50ede9b58e6f16a9d688 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 15 May 2025 21:15:30 +0200 Subject: [PATCH] fancydialogs: Setup cloud command manager --- plugins/fancydialogs/build.gradle.kts | 4 + .../fancydialogs/FancyDialogsPlugin.java | 6 + .../commands/CloudCommandManager.java | 166 ++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/commands/CloudCommandManager.java diff --git a/plugins/fancydialogs/build.gradle.kts b/plugins/fancydialogs/build.gradle.kts index ed7e8cd4..36e9bc46 100644 --- a/plugins/fancydialogs/build.gradle.kts +++ b/plugins/fancydialogs/build.gradle.kts @@ -55,6 +55,10 @@ dependencies { implementation("de.oliver.FancyAnalytics:logger:0.0.6") compileOnly("org.lushplugins:ChatColorHandler:5.1.3") + implementation("org.incendo:cloud-core:2.1.0-SNAPSHOT") + implementation("org.incendo:cloud-paper:2.0.0-SNAPSHOT") + implementation("org.incendo:cloud-annotations:2.1.0-SNAPSHOT") + annotationProcessor("org.incendo:cloud-annotations:2.1.0-SNAPSHOT") implementation("org.jetbrains:annotations:24.0.0") } diff --git a/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/FancyDialogsPlugin.java b/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/FancyDialogsPlugin.java index 105347e8..9c251c9f 100644 --- a/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/FancyDialogsPlugin.java +++ b/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/FancyDialogsPlugin.java @@ -1,6 +1,7 @@ package com.fancyinnovations.fancydialogs; import com.fancyinnovations.fancydialogs.api.Dialog; +import com.fancyinnovations.fancydialogs.commands.CloudCommandManager; import com.fancyinnovations.fancydialogs.config.FancyDialogsConfig; import com.fancyinnovations.fancydialogs.listener.PlayerJoinListener; import com.fancyinnovations.fancydialogs.registry.DialogRegistry; @@ -92,6 +93,11 @@ public class FancyDialogsPlugin extends JavaPlugin { registerListeners(); + new CloudCommandManager(this, false) + .registerArguments() + .registerExceptionHandlers() + .registerCommands(); + fancyLogger.info("Successfully enabled FancyDialogs version %s".formatted(getDescription().getVersion())); } diff --git a/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/commands/CloudCommandManager.java b/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/commands/CloudCommandManager.java new file mode 100644 index 00000000..b9f652a7 --- /dev/null +++ b/plugins/fancydialogs/src/main/java/com/fancyinnovations/fancydialogs/commands/CloudCommandManager.java @@ -0,0 +1,166 @@ +package com.fancyinnovations.fancydialogs.commands; + +import com.fancyinnovations.fancydialogs.FancyDialogsPlugin; +import de.oliver.fancylib.translations.Translator; +import io.leangen.geantyref.TypeToken; +import org.bukkit.command.CommandSender; +import org.incendo.cloud.annotations.AnnotationParser; +import org.incendo.cloud.bukkit.CloudBukkitCapabilities; +import org.incendo.cloud.bukkit.parser.WorldParser; +import org.incendo.cloud.bukkit.parser.location.LocationParser; +import org.incendo.cloud.exception.ArgumentParseException; +import org.incendo.cloud.exception.InvalidCommandSenderException; +import org.incendo.cloud.exception.NoPermissionException; +import org.incendo.cloud.exception.handling.ExceptionHandlerRegistration; +import org.incendo.cloud.exception.parsing.NumberParseException; +import org.incendo.cloud.exception.parsing.ParserException; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.LegacyPaperCommandManager; +import org.incendo.cloud.parser.standard.BooleanParser; +import org.incendo.cloud.parser.standard.EnumParser; +import org.jetbrains.annotations.NotNull; + +import static org.incendo.cloud.exception.handling.ExceptionHandler.unwrappingHandler; + +public class CloudCommandManager { + + private final @NotNull FancyDialogsPlugin plugin; + + private final @NotNull LegacyPaperCommandManager commandManager; + private final @NotNull AnnotationParser annotationParser; + + public CloudCommandManager(final @NotNull FancyDialogsPlugin plugin, final boolean isBrigadier) { + this.plugin = plugin; + // Creating instance of Cloud's LegacyPaperCommandManager, which is used for anything command-related. + this.commandManager = LegacyPaperCommandManager.createNative(plugin, ExecutionCoordinator.simpleCoordinator()); + // Registering Brigadier, if available. + if (isBrigadier && commandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) + commandManager.registerBrigadier(); + // Creating instance of AnnotationParser, which is used for parsing and registering commands. + this.annotationParser = new AnnotationParser<>(commandManager, CommandSender.class); + } + + /** + * Registers arguments (parsers and suggestion providers) to the {@link LegacyPaperCommandManager}. + */ + public @NotNull CloudCommandManager registerArguments() { +// annotationParser.parse(NpcArgument.INSTANCE); + return this; + } + + /** + * Registers exception handlers to the {@link LegacyPaperCommandManager}. + */ + public @NotNull CloudCommandManager registerExceptionHandlers() { + final Translator translator = plugin.getTranslator(); + // Unwrapping some causes of ArgumentParseException to be handled in standalone exception handlers. + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(NumberParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(BooleanParser.BooleanParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(EnumParser.EnumParseException.class)); + commandManager.exceptionController().registerHandler(ArgumentParseException.class, unwrappingHandler(WorldParser.WorldParseException.class)); + // Overriding some default handlers to send specialized messages. + commandManager.exceptionController().registerHandler(NoPermissionException.class, (exceptionContext) -> { + translator.translate("command_missing_permissions").send(exceptionContext.context().sender()); + }); + // DEV NOTE: No need to compare sender types until we decide to make a console-only command. Should get the job done for the time being. + commandManager.exceptionController().registerHandler(InvalidCommandSenderException.class, (exceptionContext) -> { + translator.translate("command_player_only").send(exceptionContext.context().sender()); + }); + commandManager.exceptionController().registerHandler(NumberParseException.class, (exceptionContext) -> { + translator.translate("command_invalid_number") + .replaceStripped("input", exceptionContext.exception().input()) + .replace("min", exceptionContext.exception().range().min().toString()) + .replace("max", exceptionContext.exception().range().max().toString()) + .send(exceptionContext.context().sender()); + }); + commandManager.exceptionController().registerHandler(BooleanParser.BooleanParseException.class, (exceptionContext) -> { + translator.translate("command_invalid_boolean") + .replaceStripped("input", exceptionContext.exception().input()) + .send(exceptionContext.context().sender()); + }); + commandManager.exceptionController().registerHandler(WorldParser.WorldParseException.class, (exceptionContext) -> { + translator.translate("command_invalid_world") + .replaceStripped("input", exceptionContext.exception().input()) + .send(exceptionContext.context().sender()); + }); + // DEV NOTE: Temporary solution until https://github.com/Incendo/cloud-minecraft/pull/70 is merged. + commandManager.exceptionController().register(ExceptionHandlerRegistration.builder(TypeToken.get(ArgumentParseException.class)) + .exceptionFilter(exception -> exception.getCause() instanceof ParserException parserException && parserException.argumentParserClass() == LocationParser.class) + .exceptionHandler(exceptionContext -> { + final ParserException exception = (ParserException) exceptionContext.exception().getCause(); + final String input = exception.captionVariables()[0].value(); // Should never throw. + translator.translate("command_invalid_location") + .replaceStripped("input", !input.isBlank() ? input : "N/A") // Under certain conditions, input is not passed to the exception. + .send(exceptionContext.context().sender()); + }).build() + ); +// commandManager.exceptionController().registerHandler(EnumParser.EnumParseException.class, (exceptionContext) -> { +// String translationKey = "command_invalid_enum_generic"; +// // Comparing exception enum class and choosing specialized messages. +// if (exceptionContext.exception().enumClass() == ListCMD.SortType.class) +// translationKey = "command_invalid_list_sort_type"; +// else if (exceptionContext.exception().enumClass() == NearbyCMD.SortType.class) +// translationKey = "command_invalid_nearby_sort_type"; +// else if (exceptionContext.exception().enumClass() == EntityType.class) +// translationKey = "command_invalid_entity_type"; +// else if (exceptionContext.exception().enumClass() == GlowingColor.class) +// translationKey = "command_invalid_glowing_color"; +// // Sending error message to the sender. In case no specialized message has been found, a generic one is used instead. +// translator.translate(translationKey) +// .replaceStripped("input", exceptionContext.exception().input()) +// .replace("enum", exceptionContext.exception().enumClass().getSimpleName().toLowerCase()) +// .send(exceptionContext.context().sender()); +// }); +// // ReplyingParseException is thrown from custom argument types and is handled there. +// commandManager.exceptionController().registerHandler(ReplyingParseException.class, context -> context.exception().runnable().run()); +// // InvalidSyntaxException is thrown when user specified syntax don't match any command. +// commandManager.exceptionController().registerHandler(InvalidSyntaxException.class, (exceptionContext) -> { +// // Creating a StringBuilder which is then appended with (known/existing) command literals. +// final StringBuilder translationKeyBuilder = new StringBuilder("command_syntax."); +// // Iterating over current command chain and appending literals, as described above. +// exceptionContext.exception().currentChain().stream() +// .filter(c -> c.type() == CommandComponent.ComponentType.LITERAL) +// .forEach(literal -> translationKeyBuilder.append(literal.name()).append(' ')); +// // Trimming input (last character ends up being blank) and replacing whitespaces with underscores, as that's how translations are defined inside the language file. +// final String translationKey = translationKeyBuilder.toString().trim().replace(' ', '_'); +// // Getting the message, it's not finished as there we need to handle fallback language etc. +// final @Nullable Message message = Optional.ofNullable(plugin.getTranslator().getSelectedLanguage().getMessage(translationKey)) +// .orElse(plugin.getTranslator().getFallbackLanguage().getMessage(translationKey)); +// // "Fall-backing" to generic syntax error, if no specialized syntax message has been defined in the language file. +// if (message == null) { +// plugin.getTranslator().translate("command_invalid_syntax_generic") +// .replace("syntax", exceptionContext.exception().correctSyntax()) +// .send(exceptionContext.context().sender()); +// return; +// } +// message.send(exceptionContext.context().sender()); +// }); + // Returning this instance of CloudCommandManager to keep "builder-like" flow. + return this; + } + + /** + * Registers plugin commands to the {@link LegacyPaperCommandManager}. + */ + public @NotNull CloudCommandManager registerCommands() { +// annotationParser.parse(AttributeCMD.INSTANCE); + + return this; + } + + /** + * Returns the internal {@link LegacyPaperCommandManager} associated with this instance of {@link CloudCommandManager}. + */ + public @NotNull LegacyPaperCommandManager getCommandManager() { + return commandManager; + } + + /** + * Returns the internal {@link AnnotationParser} associated with this instance of {@link CloudCommandManager}. + */ + public @NotNull AnnotationParser getAnnotationParser() { + return annotationParser; + } + + +}