From 3f96837cf12893f8deaa8f351f8e2d62472f2f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Mon, 21 Sep 2020 19:11:48 +0200 Subject: [PATCH] Add adventure based help menu --- .../commands/CommandHelpHandler.java | 52 +++- cloud-minecraft/cloud-bukkit-test/pom.xml | 17 ++ .../commands/BukkitTest.java | 17 +- .../cloud-minecraft-extras/pom.xml | 66 +++++ .../commands/AudienceProvider.java | 49 ++++ .../commands/MinecraftHelp.java | 266 ++++++++++++++++++ .../commands/package-info.java | 28 ++ pom.xml | 1 + 8 files changed, 480 insertions(+), 16 deletions(-) create mode 100644 cloud-minecraft/cloud-minecraft-extras/pom.xml create mode 100644 cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/AudienceProvider.java create mode 100644 cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/MinecraftHelp.java create mode 100644 cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/package-info.java diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/CommandHelpHandler.java b/cloud-core/src/main/java/com/intellectualsites/commands/CommandHelpHandler.java index da811738..910dbcf0 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/CommandHelpHandler.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/CommandHelpHandler.java @@ -30,8 +30,11 @@ import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Locale; +import java.util.Set; public final class CommandHelpHandler { @@ -142,31 +145,51 @@ public final class CommandHelpHandler { final String rootFragment = queryFragments[0]; /* Determine which command we are querying for */ - Command queryCommand = null; - String queryCommandName = ""; + final List> availableCommands = new LinkedList<>(); + final Set availableCommandLabels = new HashSet<>(); - outer: for (final VerboseHelpEntry entry : verboseEntries) { final Command command = entry.getCommand(); @SuppressWarnings("unchecked") final StaticArgument staticArgument = (StaticArgument) command.getArguments() .get(0); for (final String alias : staticArgument.getAliases()) { - if (alias.equalsIgnoreCase(rootFragment)) { - /* We found our command */ - queryCommand = command; - queryCommandName = staticArgument.getName(); - break outer; + if (alias.toLowerCase(Locale.ENGLISH).startsWith(rootFragment.toLowerCase(Locale.ENGLISH))) { + availableCommands.add(command); + availableCommandLabels.add(staticArgument.getName()); + break; } } + + if (rootFragment.equalsIgnoreCase(staticArgument.getName())) { + availableCommandLabels.clear(); + availableCommands.clear(); + availableCommandLabels.add(staticArgument.getName()); + availableCommands.add(command); + break; + } } /* No command found, return all possible commands */ - if (queryCommand == null) { - return new IndexHelpTopic<>(verboseEntries); + if (availableCommands.isEmpty()) { + return new IndexHelpTopic<>(Collections.emptyList()); + } else if (availableCommandLabels.size() > 1) { + final List> syntaxHints = new ArrayList<>(); + for (final Command command : availableCommands) { + final List> arguments = command.getArguments(); + final String description = command.getCommandMeta().getOrDefault("description", ""); + syntaxHints.add(new VerboseHelpEntry<>(command, + this.commandManager.getCommandSyntaxFormatter() + .apply(arguments, null), + description)); + } + syntaxHints.sort(Comparator.comparing(VerboseHelpEntry::getSyntaxString)); + return new IndexHelpTopic<>(syntaxHints); } /* Traverse command to find the most specific help topic */ - final CommandTree.Node> node = this.commandManager.getCommandTree().getNamedNode(queryCommandName); + final CommandTree.Node> node = this.commandManager.getCommandTree() + .getNamedNode(availableCommandLabels.iterator() + .next()); final List> traversedNodes = new LinkedList<>(); CommandTree.Node> head = node; @@ -196,7 +219,9 @@ public final class CommandHelpHandler { /* Attempt to parse the longest possible description for the children */ final List childSuggestions = new LinkedList<>(); for (final CommandTree.Node> child : head.getChildren()) { - childSuggestions.add(this.commandManager.getCommandSyntaxFormatter().apply(traversedNodes, child)); + final List> traversedNodesSub = new LinkedList<>(traversedNodes); + traversedNodesSub.add(child.getValue()); + childSuggestions.add(this.commandManager.getCommandSyntaxFormatter().apply(traversedNodesSub, child)); } return new MultiHelpTopic<>(currentDescription, childSuggestions); } @@ -250,7 +275,6 @@ public final class CommandHelpHandler { * * @return {@code true} if the topic is entry, else {@code false} */ - @Nonnull public boolean isEmpty() { return this.getEntries().isEmpty(); } @@ -295,7 +319,7 @@ public final class CommandHelpHandler { } } - + /** * Help topic with multiple semi-verbose command descriptions diff --git a/cloud-minecraft/cloud-bukkit-test/pom.xml b/cloud-minecraft/cloud-bukkit-test/pom.xml index 57abbfc8..239f1bdb 100644 --- a/cloud-minecraft/cloud-bukkit-test/pom.xml +++ b/cloud-minecraft/cloud-bukkit-test/pom.xml @@ -60,6 +60,13 @@ + + + sonatype + https://oss.sonatype.org/content/repositories/snapshots + + + com.intellectualsites @@ -82,5 +89,15 @@ commodore 1.9 + + com.intellectualsites + cloud-minecraft-extras + 0.1.0-SNAPSHOT + + + net.kyori + adventure-platform-bukkit + 4.0.0-SNAPSHOT + diff --git a/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java b/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java index 6cf01e75..a6c0bfb7 100644 --- a/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java +++ b/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java @@ -45,6 +45,7 @@ import com.intellectualsites.commands.execution.AsynchronousCommandExecutionCoor import com.intellectualsites.commands.execution.CommandExecutionCoordinator; import com.intellectualsites.commands.meta.SimpleCommandMeta; import com.intellectualsites.commands.paper.PaperCommandManager; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.GameMode; @@ -82,6 +83,9 @@ public final class BukkitTest extends JavaPlugin { Function.identity() ); + final BukkitAudiences bukkitAudiences = BukkitAudiences.create(this); + final MinecraftHelp minecraftHelp = new MinecraftHelp<>(bukkitAudiences::audience, mgr); + try { ((PaperCommandManager) mgr).registerBrigadier(); } catch (final Exception e) { @@ -176,7 +180,16 @@ public final class BukkitTest extends JavaPlugin { .argument(BooleanArgument.required("bool")) .argument(StringArgument.required("string")) .handler(c -> c.getSender().sendMessage("Executed the command")) - .build()); + .build()) + .command(mgr.commandBuilder("annotationass").handler(c -> c.getSender() + .sendMessage(ChatColor.YELLOW + "Du e en ananas!")).build()) + .command(mgr.commandBuilder("cloud") + .literal("help") + .argument(StringArgument.newBuilder("query").greedy() + .asOptionalWithDefault("") + .build(), "Help query") + .handler(c -> minecraftHelp.queryCommands(c.get("query").orElse(""), + c.getSender())).build()); } catch (final Exception e) { e.printStackTrace(); } @@ -191,7 +204,7 @@ public final class BukkitTest extends JavaPlugin { player.sendMessage(ChatColor.GOLD + "Your input was: " + ChatColor.AQUA + input + ChatColor.GREEN + " (" + number + ")"); } - @CommandMethod("cloud help") + @CommandMethod("cloud debug") private void doHelp() { final Set capabilities = this.mgr.queryCapabilities(); Bukkit.broadcastMessage(ChatColor.GOLD + "" + ChatColor.BOLD + "Capabilities"); diff --git a/cloud-minecraft/cloud-minecraft-extras/pom.xml b/cloud-minecraft/cloud-minecraft-extras/pom.xml new file mode 100644 index 00000000..f90400cc --- /dev/null +++ b/cloud-minecraft/cloud-minecraft-extras/pom.xml @@ -0,0 +1,66 @@ + + + + + + cloud + com.intellectualsites + 1.0-SNAPSHOT + ../../pom.xml + + 4.0.0 + + cloud-minecraft-extras + 0.1.0-SNAPSHOT + + + + sonatype + https://oss.sonatype.org/content/repositories/snapshots + + + + + + net.kyori + adventure-api + 4.0.0-SNAPSHOT + + + com.intellectualsites + cloud-core + 0.1.0-SNAPSHOT + + + net.kyori + adventure-text-minimessage + 4.0.0-SNAPSHOT + + + diff --git a/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/AudienceProvider.java b/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/AudienceProvider.java new file mode 100644 index 00000000..824da19d --- /dev/null +++ b/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/AudienceProvider.java @@ -0,0 +1,49 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands; + +import net.kyori.adventure.audience.Audience; + +import javax.annotation.Nonnull; +import java.util.function.Function; + +/** + * Function that maps the command sender type to an adventure {@link net.kyori.adventure.audience.Audience} + * + * @param Command sender type + */ +@FunctionalInterface +public interface AudienceProvider extends Function { + + /** + * Convert a command sender to an {@link Audience} + * + * @param sender Command sender + * @return Mapped audience + */ + @Override + @Nonnull + Audience apply(@Nonnull C sender); + +} diff --git a/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/MinecraftHelp.java b/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/MinecraftHelp.java new file mode 100644 index 00000000..89f10fdb --- /dev/null +++ b/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/MinecraftHelp.java @@ -0,0 +1,266 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands; + +import com.intellectualsites.commands.arguments.CommandArgument; +import com.intellectualsites.commands.arguments.StaticArgument; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.Template; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Opinionated extension of {@link CommandHelpHandler} for Minecraft + * + * @param Command sender type + */ +public final class MinecraftHelp { + + /* General help */ + public static final String MESSAGE_HELP_HEADER = "help_header"; + public static final String MESSAGE_HELP_FOOTER = "help_footer"; + /* Query specific */ + public static final String MESSAGE_QUERY_QUERY = "help_query_query"; + public static final String MESSAGE_QUERY_AVAILABLE_COMMANDS = "help_query_available_comments"; + public static final String MESSAGE_QUERY_COMMAND_SYNTAX = "help_query_command_syntax"; + public static final String MESSAGE_QUERY_COMMAND_SYNTAX_LAST = "help_query_command_syntax_last"; + public static final String MESSAGE_QUERY_LONGEST_PATH = "help_query_longest_path"; + public static final String MESSAGE_QUERY_SUGGESTION = "help_query_suggestion"; + public static final String MESSAGE_QUERY_VERBOSE_SYNTAX = "help_query_verbose_syntax"; + public static final String MESSAGE_QUERY_VERBOSE_DESCRIPTION = "help_query_verbose_description"; + public static final String MESSAGE_QUERY_VERBOSE_ARGS = "help_query_verbose_args"; + public static final String MESSAGE_QUERY_VERBOSE_OPTIONAL = "help_query_verbose_optional"; + public static final String MESSAGE_QUERY_VERBOSE_REQUIRED = "help_query_verbose_required"; + public static final String MESSAGE_QUERY_VERBOSE_LITERAL = "help_query_verbose_literal"; + + private final MiniMessage miniMessage = MiniMessage.builder().build(); + private final Map messageMap = new HashMap<>(); + + private final AudienceProvider audienceProvider; + private final CommandManager commandManager; + + /** + * Construct a new Minecraft help instance + * + * @param audienceProvider Provider that maps the command sender type to {@link Audience} + * @param commandManager Command manager instance + */ + public MinecraftHelp(@Nonnull final AudienceProvider audienceProvider, + @Nonnull final CommandManager commandManager) { + this.audienceProvider = audienceProvider; + this.commandManager = commandManager; + /* Default messages */ + this.messageMap.put(MESSAGE_HELP_HEADER, "------------ Help ------------"); + this.messageMap.put(MESSAGE_HELP_FOOTER, "----------------------------"); + this.messageMap.put(MESSAGE_QUERY_QUERY, "Showing search results for query: \"/\""); + this.messageMap.put(MESSAGE_QUERY_AVAILABLE_COMMANDS, "└─ Available Commands:"); + this.messageMap.put(MESSAGE_QUERY_COMMAND_SYNTAX, " ├─ " + + "\">/"); + this.messageMap.put(MESSAGE_QUERY_COMMAND_SYNTAX_LAST, " └─ " + + "\">/"); + this.messageMap.put(MESSAGE_QUERY_LONGEST_PATH, "└─ /"); + this.messageMap.put(MESSAGE_QUERY_SUGGESTION, " "); + this.messageMap.put(MESSAGE_QUERY_VERBOSE_SYNTAX, "└─ Command:" + + " /"); + this.messageMap.put(MESSAGE_QUERY_VERBOSE_DESCRIPTION, " ├─" + + " Description: "); + this.messageMap.put(MESSAGE_QUERY_VERBOSE_ARGS, " └─ Args:"); + this.messageMap.put(MESSAGE_QUERY_VERBOSE_OPTIONAL, " " + + "(Optional) - "); + this.messageMap.put(MESSAGE_QUERY_VERBOSE_REQUIRED, " " + + "- "); + this.messageMap.put(MESSAGE_QUERY_VERBOSE_LITERAL, ""); + } + + /** + * Get the command manager instance + * + * @return Command manager + */ + @Nonnull + public CommandManager getCommandManager() { + return this.commandManager; + } + + /** + * Get the audience provider that was used to create this instance + * + * @return Audience provider + */ + @Nonnull + public AudienceProvider getAudienceProvider() { + return this.audienceProvider; + } + + /** + * Map a command sender to an {@link Audience} + * + * @param sender Sender to map + * @return Mapped audience + */ + @Nonnull + public Audience getAudience(@Nonnull final C sender) { + return this.audienceProvider.apply(sender); + } + + /** + * Configure a message + * + * @param key Message key + * @param message Message + */ + public void setMessage(@Nonnull final String key, @Nonnull final String message) { + this.messageMap.put(key, message); + } + + /** + * Query commands and send the results to the recipient + * + * @param query Command query (without leading '/') + * @param recipient Recipient + */ + public void queryCommands(@Nonnull final String query, + @Nonnull final C recipient) { + final Audience audience = this.getAudience(recipient); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_HELP_HEADER))); + this.printTopic(recipient, query, this.commandManager.getCommandHelpHandler().queryHelp(query)); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_HELP_FOOTER))); + } + + private void printTopic(@Nonnull final C sender, + @Nonnull final String query, + @Nonnull final CommandHelpHandler.HelpTopic helpTopic) { + this.getAudience(sender).sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_QUERY), + Template.of("query", query))); + if (helpTopic instanceof CommandHelpHandler.IndexHelpTopic) { + this.printIndexHelpTopic(sender, (CommandHelpHandler.IndexHelpTopic) helpTopic); + } else if (helpTopic instanceof CommandHelpHandler.MultiHelpTopic) { + this.printMultiHelpTopic(sender, (CommandHelpHandler.MultiHelpTopic) helpTopic); + } else if (helpTopic instanceof CommandHelpHandler.VerboseHelpTopic) { + this.printVerboseHelpTopic(sender, (CommandHelpHandler.VerboseHelpTopic) helpTopic); + } else { + throw new IllegalArgumentException("Unknown help topic type"); + } + } + + private void printIndexHelpTopic(@Nonnull final C sender, @Nonnull final CommandHelpHandler.IndexHelpTopic helpTopic) { + final Audience audience = this.getAudience(sender); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_AVAILABLE_COMMANDS))); + final Iterator> iterator = helpTopic.getEntries().iterator(); + while (iterator.hasNext()) { + final CommandHelpHandler.VerboseHelpEntry entry = iterator.next(); + if (iterator.hasNext()) { + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_COMMAND_SYNTAX), + Template.of("command", entry.getSyntaxString()), + Template.of("description", entry.getDescription()))); + } else { + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_COMMAND_SYNTAX_LAST), + Template.of("command", entry.getSyntaxString()), + Template.of("description", entry.getDescription()))); + } + } + } + + private void printMultiHelpTopic(@Nonnull final C sender, @Nonnull final CommandHelpHandler.MultiHelpTopic helpTopic) { + final Audience audience = this.getAudience(sender); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_LONGEST_PATH), + Template.of("command", helpTopic.getLongestPath()))); + final int headerIndentation = helpTopic.getLongestPath().length(); + final Iterator iterator = helpTopic.getChildSuggestions().iterator(); + while (iterator.hasNext()) { + final String suggestion = iterator.next(); + + final StringBuilder indentation = new StringBuilder(); + for (int i = 0; i < headerIndentation; i++) { + indentation.append(" "); + } + + final String prefix; + if (iterator.hasNext()) { + prefix = "├─"; + } else { + prefix = "└─"; + } + + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_SUGGESTION), + Template.of("indentation", indentation.toString()), + Template.of("prefix", prefix), + Template.of("suggestion", suggestion))); + } + } + + private void printVerboseHelpTopic(@Nonnull final C sender, @Nonnull final CommandHelpHandler.VerboseHelpTopic helpTopic) { + final Audience audience = this.getAudience(sender); + final String command = this.commandManager.getCommandSyntaxFormatter() + .apply(helpTopic.getCommand().getArguments(), null); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_VERBOSE_SYNTAX), + Template.of("command", command))); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_VERBOSE_DESCRIPTION), + Template.of("description", helpTopic.getDescription()))); + audience.sendMessage(this.miniMessage.parse(this.messageMap.get(MESSAGE_QUERY_VERBOSE_ARGS))); + + final Iterator> iterator = helpTopic.getCommand().getArguments().iterator(); + /* Skip the first one because it's the command literal */ + iterator.next(); + + boolean hasLast; + + while (iterator.hasNext()) { + final CommandArgument argument = iterator.next(); + + String description = helpTopic.getCommand().getArgumentDescription(argument); + if (description.isEmpty()) { + description = "No description"; + } + + String syntax = this.commandManager.getCommandSyntaxFormatter() + .apply(Collections.singletonList(argument), null); + + if (argument instanceof StaticArgument) { + syntax = this.messageMap.get(MESSAGE_QUERY_VERBOSE_LITERAL).replace("", syntax); + } + + final String prefix = iterator.hasNext() ? "├─" : "└─"; + hasLast = !iterator.hasNext(); + + final String message; + if (argument.isRequired()) { + message = this.messageMap.get(MESSAGE_QUERY_VERBOSE_REQUIRED); + } else { + message = this.messageMap.get(MESSAGE_QUERY_VERBOSE_OPTIONAL); + } + + audience.sendMessage(this.miniMessage.parse(message, + Template.of("prefix", prefix), + Template.of("syntax", syntax), + Template.of("description", description))); + } + } + +} diff --git a/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/package-info.java b/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/package-info.java new file mode 100644 index 00000000..8e491fbc --- /dev/null +++ b/cloud-minecraft/cloud-minecraft-extras/src/main/java/com/intellectualsites/commands/package-info.java @@ -0,0 +1,28 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** + * Minecraft extras + */ +package com.intellectualsites.commands; diff --git a/pom.xml b/pom.xml index a7ca9884..c89a3b1f 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ cloud-minecraft/cloud-brigadier cloud-minecraft/cloud-bungee cloud-minecraft/cloud-velocity + cloud-minecraft/cloud-minecraft-extras pom 2020