From ef6abe3aabab7a84ff6612a1886be8ca672d3ffc Mon Sep 17 00:00:00 2001 From: Koneiii <64519859+koneiii@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:43:12 +0200 Subject: [PATCH 1/2] feat: help automatic --- .../fr/communaywen/core/AywenCraftPlugin.java | 10 + .../core/commands/TeamCommand.java | 16 + .../utils/command/InteractiveHelpMenu.java | 283 ++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 src/main/java/fr/communaywen/core/utils/command/InteractiveHelpMenu.java diff --git a/src/main/java/fr/communaywen/core/AywenCraftPlugin.java b/src/main/java/fr/communaywen/core/AywenCraftPlugin.java index 695a77aa..a6cc30d3 100644 --- a/src/main/java/fr/communaywen/core/AywenCraftPlugin.java +++ b/src/main/java/fr/communaywen/core/AywenCraftPlugin.java @@ -12,6 +12,7 @@ import fr.communaywen.core.economy.EconomyManager; import dev.xernas.menulib.MenuLib; +import fr.communaywen.core.utils.command.InteractiveHelpMenu; import fr.communaywen.core.utils.database.DatabaseManager; import fr.communaywen.core.staff.freeze.FreezeCommand; import fr.communaywen.core.staff.freeze.FreezeListener; @@ -46,6 +47,7 @@ public final class AywenCraftPlugin extends JavaPlugin { public LuckPerms api; private BukkitAudiences adventure; + private InteractiveHelpMenu interactiveHelpMenu; private BukkitCommandHandler handler; @@ -111,6 +113,8 @@ public void onEnable() { /* COMMANDS */ this.handler = BukkitCommandHandler.create(this); + this.interactiveHelpMenu = InteractiveHelpMenu.create(); + this.handler.accept(interactiveHelpMenu); this.handler.register(new SpawnCommand(this), new VersionCommand(this), new RulesCommand(bookConfig), new TeamCommand()); @@ -195,6 +199,10 @@ public DatabaseManager getDatabaseManager() { return databaseManager; } + public InteractiveHelpMenu getInteractiveHelpMenu() { + return interactiveHelpMenu; + } + public static AywenCraftPlugin getInstance() { return instance; } @@ -212,4 +220,6 @@ public static AywenCraftPlugin getInstance() { return category.formatPermission(suffix); } + + } diff --git a/src/main/java/fr/communaywen/core/commands/TeamCommand.java b/src/main/java/fr/communaywen/core/commands/TeamCommand.java index d8faffc9..717ca68e 100644 --- a/src/main/java/fr/communaywen/core/commands/TeamCommand.java +++ b/src/main/java/fr/communaywen/core/commands/TeamCommand.java @@ -9,12 +9,16 @@ import fr.communaywen.core.teams.utils.MethodState; import fr.communaywen.core.teams.utils.TeamUtils; import fr.communaywen.core.utils.CommandUtils; +import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import revxrsal.commands.annotation.*; +import revxrsal.commands.bukkit.BukkitCommandActor; import revxrsal.commands.bukkit.annotation.CommandPermission; +import revxrsal.commands.command.ExecutableCommand; +import revxrsal.commands.help.CommandHelp; import java.util.ArrayList; import java.util.List; @@ -28,6 +32,18 @@ public class TeamCommand { TeamManager teamManager = AywenCraftPlugin.getInstance().getTeamManager(); + @DefaultFor("~") + public void sendHelp(BukkitCommandActor actor, ExecutableCommand command) { + String helpCommandPath = command.getPath().toRealString() + " help"; + actor.getCommandHandler().dispatch(actor, helpCommandPath); + } + + @Subcommand("help") + @Description("Afficher l'aide") + public void sendHelp(BukkitCommandActor sender, CommandHelp help, ExecutableCommand thisHelpCommand, @Default("1") @Range(min = 1) int page) { + Audience audience = AywenCraftPlugin.getInstance().getAdventure().sender(sender.getSender()); + AywenCraftPlugin.getInstance().getInteractiveHelpMenu().sendInteractiveMenu(audience, help, page, thisHelpCommand, "§b§lGUILD"); + } @Subcommand("menu") @Description("Menu de la team") diff --git a/src/main/java/fr/communaywen/core/utils/command/InteractiveHelpMenu.java b/src/main/java/fr/communaywen/core/utils/command/InteractiveHelpMenu.java new file mode 100644 index 00000000..7d81c1f0 --- /dev/null +++ b/src/main/java/fr/communaywen/core/utils/command/InteractiveHelpMenu.java @@ -0,0 +1,283 @@ +package fr.communaywen.core.utils.command; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import revxrsal.commands.CommandHandler; +import revxrsal.commands.CommandHandlerVisitor; +import revxrsal.commands.command.CommandActor; +import revxrsal.commands.command.ExecutableCommand; +import revxrsal.commands.help.CommandHelp; +import revxrsal.commands.help.CommandHelpWriter; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + +/** + * A utility for creating interactive, paginated help menus. + *

+ * Note: This requires adventure APIs to work. + *

+ *

Usage

+ *
    + *
  1. Create a singleton instance of {@link InteractiveHelpMenu} either + * using {@link InteractiveHelpMenu#builder()} or {@link InteractiveHelpMenu#create()} + *
    {@code
    + *     public static final InteractiveHelpMenu HELP_MENU = InteractiveHelpMenu.create();
    + *     }
    + *
  2. + *
  3. Register your singleton instance to the {@link CommandHandler} + * using {@link CommandHandler#accept(CommandHandlerVisitor)} + *
    {@code
    + *     commandHandler.accept(HELP_MENU);
    + *     }
    + *
  4. + *
  5. Create the commands that you would like to display the help menu in. Send the help + * menu using {@link InteractiveHelpMenu#sendInteractiveMenu(Audience, CommandHelp, int, ExecutableCommand)}. + * All the parameters should be parameters in the help method, and Lamp will resolve them automatically + * to the appropriate values. + *
    {@code
    + * @Command("myplugin")
    + * public class PluginCommands {
    + *
    + *     // Add this method if you want /myplugin to display the help menu
    + *     @DefaultFor("~")
    + *     public void sendHelp(BukkitCommandActor actor, ExecutableCommand command) {
    + *          String helpCommandPath = command.getPath().toRealString() + " help";
    + *          actor.getCommandHandler().dispatch(actor, helpCommandPath);
    + *     }
    + *
    + *     @Subcommand("help")
    + *     // add other annotations here such as @Usage and @Description...
    + *     public void sendHelp(
    + *             Audience sender,
    + *             CommandHelp help,
    + *             ExecutableCommand thisHelpCommand,
    + *             @Default("1") @Range(min = 1) int page
    + *     ) {
    + *         HELP_MENU.sendInteractiveMenu(sender, help, page, thisHelpCommand);
    + *     }
    + * }
    + * }
    + *
  6. + *
+ *

+ * Note that the help menu lists all the subcommands (excluding the help command) in the + * same category as the help command. For example: + *

+ * This is why {@link #sendInteractiveMenu(Audience, CommandHelp, int, ExecutableCommand)} requires + * an {@link ExecutableCommand}, as it uses it to resolve sibling commands that should be + * listed in the help menu. + */ +public final class InteractiveHelpMenu implements CommandHelpWriter, CommandHandlerVisitor { + + /** + * The default number of entries in a single help page + */ + private static final int DEFAULT_PAGE_SIZE = 8; + + /** + * The slash command color + */ + private final String slashCommandColor; + + /** + * The command name color + */ + private final String commandNameColor; + + /** + * The parameters/command usage color + */ + private final String parametersColor; + + /** + * The hover tooltip label color + */ + private final String tooltipLabelsColor; + + /** + * The page size + */ + private final int pageSize; + + private InteractiveHelpMenu(String slashCommandColor, String commandNameColor, String parametersColor, String tooltipLabelsColor, int pageSize) { + this.slashCommandColor = slashCommandColor; + this.commandNameColor = commandNameColor; + this.parametersColor = parametersColor; + this.tooltipLabelsColor = tooltipLabelsColor; + this.pageSize = pageSize; + } + + /** + * Creates a new {@link Builder} + * + * @return A new builder + */ + public static @NotNull Builder builder() { + return new Builder(); + } + + /** + * Creates a new {@link InteractiveHelpMenu} with default settings + * + * @return A new {@link InteractiveHelpMenu} + */ + public static @NotNull InteractiveHelpMenu create() { + return new Builder().build(); + } + + @Override + public @Nullable Component generate(@NotNull ExecutableCommand command, @NotNull CommandActor actor) { + if (command.isSecret() || !command.hasPermission(actor)) + return null; + boolean hasParameters = !command.getValueParameters().isEmpty(); + List tooltip = createTooltip(command); + + StringJoiner desc = new StringJoiner(" "); + desc.add(slashCommandColor + "/" + commandNameColor + command.getPath().getParent()); + if (command.getPath().size() > 1) + desc.add(String.join(" ", command.getPath().getSubcommandPath())); + if (!command.getValueParameters().isEmpty()) + desc.add(parametersColor + command.getUsage()); + return lg(desc.toString() + " §f» §e" + + (command.getDescription() == null || command.getDescription().isEmpty() ? "§cAucune description" : command.getDescription())).style(Style.style() + .hoverEvent(HoverEvent.showText(lg(String.join("\n", tooltip)))) + .clickEvent(ClickEvent.suggestCommand('/' + command.getPath().toRealString() + (hasParameters ? " " : "")))); + } + + private List createTooltip(ExecutableCommand command) { + List tooltip = new ArrayList<>(); + if (command.getDescription() != null) + tooltip.add(tooltipLabelsColor + "&lDescription&f: " + command.getDescription()); + if (!command.getValueParameters().isEmpty()) /* check if command has any parameters */ + tooltip.add(tooltipLabelsColor + "&lParamètres&f: " + command.getUsage()); + if (!tooltip.isEmpty()) + tooltip.add(""); + tooltip.add("Cliquez pour le mettre dans votre chat !"); + return tooltip; + } + + public void sendInteractiveMenu( + @NotNull Audience target, + @NotNull CommandHelp allEntries, + int page, + @NotNull ExecutableCommand helpCommand, @NotNull String prefix + ) { + int pageCount = allEntries.getPageSize(pageSize); + int index = coerce(page, 1, pageCount); + sendTopBar(target, helpCommand, prefix); + CommandHelp helpPage = allEntries.paginate(index, pageSize); + for (Component component : helpPage) { + target.sendMessage(component); + } + sendBottomBar(target, index, pageCount, page, helpCommand); + } + + private static void sendTopBar(Audience audience, ExecutableCommand command, String prefix) { + Component bar = Component.text(prefix + " §8» §fListe des commandes") + .appendNewline(); + audience.sendMessage(bar); + } + + private static void sendBottomBar(Audience audience, int index, int pageSize, int currentPage, ExecutableCommand command) { + String previous = "/" + command.getPath().toRealString() + " " + (currentPage - 1); + String next = "/" + command.getPath().toRealString() + " " + (currentPage + 1); + Component bar = Component.text() + .appendNewline() + .append(currentPage <= 1 ? lg("§8[§7§m« Page précédente §8]") + .hoverEvent(HoverEvent.showText(lg("&cCeci est la première page"))) + : lg("§8[§e« Page précédente §8]").clickEvent(ClickEvent.runCommand(previous)) + .hoverEvent(HoverEvent.showText(lg("&aPage précédente")))) + .append(lg("&r &8■ &r")) + .append( + currentPage >= pageSize ? lg("§8[§7§mPage suivante »§8]") + .hoverEvent(HoverEvent.showText(lg("&cCeci est la dernière page"))) + : lg("§8[§ePage suivante »§8]").clickEvent(ClickEvent.runCommand(next)) + .hoverEvent(HoverEvent.showText(lg("&aPage suivante"))) + ) + .build(); + +// Component bar = Component.text() +// .append(lg("&7&m-------&a (")) +// .append(lg((index == pageSize ? "&2" : "&e") + index + "&7/&2" + pageSize)) +// .append(lg("&a) &7&m-------")) +// .build(); + audience.sendMessage(bar); + } + + @Override + public String toString() { + return "InteractiveHelpMenu(" + + "slashCommandColor='" + slashCommandColor + '\'' + + ", commandNameColor='" + commandNameColor + '\'' + + ", parametersColor='" + parametersColor + '\'' + + ", tooltipLabelsColor='" + tooltipLabelsColor + '\'' + + ", pageSize=" + pageSize + + ')'; + } + /* Utility methods */ + + private static int coerce(int value, int min, int max) { + return value < min ? min : Math.min(value, max); + } + + private static Component lg(String m) { + return LegacyComponentSerializer.legacyAmpersand().deserialize(m); + } + + @Override + public void visit(@NotNull CommandHandler handler) { + handler.setHelpWriter(this); + } + + public static class Builder { + private String slashCommandColor = "&7"; + private String commandNameColor = "&e"; + private String parametersColor = "&6"; + private String tooltipLabelsColor = "&e"; + private int pageSize = DEFAULT_PAGE_SIZE; + + public Builder slashCommandColor(String slashCommandColor) { + this.slashCommandColor = slashCommandColor; + return this; + } + + public Builder commandNameColor(String commandNameColor) { + this.commandNameColor = commandNameColor; + return this; + } + + public Builder parametersColor(String parametersColor) { + this.parametersColor = parametersColor; + return this; + } + + public Builder tooltipLabelsColor(String tooltipLabelsColor) { + this.tooltipLabelsColor = tooltipLabelsColor; + return this; + } + + public Builder pageSize(int pageSize) { + this.pageSize = pageSize; + return this; + } + + public InteractiveHelpMenu build() { + return new InteractiveHelpMenu(this.slashCommandColor, this.commandNameColor, this.parametersColor, this.tooltipLabelsColor, this.pageSize); + } + + public String toString() { + return "InteractiveHelpMenu.Builder(slashCommandColor=" + this.slashCommandColor + ", commandNameColor=" + this.commandNameColor + ", parametersColor=" + this.parametersColor + ", tooltipLabelsColor=" + this.tooltipLabelsColor + ", pageSize=" + this.pageSize + ")"; + } + } +} \ No newline at end of file From e34b7827c692908bf4dfec88cf986803f05b76da Mon Sep 17 00:00:00 2001 From: Koneiii <64519859+koneiii@users.noreply.github.com> Date: Sun, 7 Jul 2024 16:43:54 +0200 Subject: [PATCH 2/2] feat: help automatic --- src/main/java/fr/communaywen/core/commands/TeamCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/communaywen/core/commands/TeamCommand.java b/src/main/java/fr/communaywen/core/commands/TeamCommand.java index 717ca68e..f7d7caf1 100644 --- a/src/main/java/fr/communaywen/core/commands/TeamCommand.java +++ b/src/main/java/fr/communaywen/core/commands/TeamCommand.java @@ -42,7 +42,7 @@ public void sendHelp(BukkitCommandActor actor, ExecutableCommand command) { @Description("Afficher l'aide") public void sendHelp(BukkitCommandActor sender, CommandHelp help, ExecutableCommand thisHelpCommand, @Default("1") @Range(min = 1) int page) { Audience audience = AywenCraftPlugin.getInstance().getAdventure().sender(sender.getSender()); - AywenCraftPlugin.getInstance().getInteractiveHelpMenu().sendInteractiveMenu(audience, help, page, thisHelpCommand, "§b§lGUILD"); + AywenCraftPlugin.getInstance().getInteractiveHelpMenu().sendInteractiveMenu(audience, help, page, thisHelpCommand, "§b§lTEAM"); } @Subcommand("menu")