diff --git a/README.md b/README.md index 0c748c5..86bab03 100644 --- a/README.md +++ b/README.md @@ -28,16 +28,17 @@ Currently the default value is not configurable, as this was made for a specific ### Regular Commands -| Command | Description | -|---------|-------------| -| `/clan` | Base command. | -| `/clan create ` | Creates a clan if it doesn't already exist, the name is allowed, and you are not in a clan. | -| `/clan info ` | Shows info about a certain clan. If blank, attempts to use your clan. | -| `/clan invites` | Views your current clan invites in a neat list to accept/decline. | -| `/clan accept ` | Alternative to clicking in chat to accept an invite. | -| `/clan decline ` | Alternative to clicking in chat to decline an invite. | -| `/clan join ` | Joins a clan if it is open to joins. | -| `/clan leave` | Leaves your current clan. If you are the leader with no other members, disbands the clan. | +| Command | Description | +|----------------------------|---------------------------------------------------------------------------------------------| +| `/clan` | Base command. | +| `/clan help [pageName]` | Shows a help menu page (defaults to this page if pageName isn't chosen). | +| `/clan create ` | Creates a clan if it doesn't already exist, the name is allowed, and you are not in a clan. | +| `/clan info ` | Shows info about a certain clan. If blank, attempts to use your clan. | +| `/clan invites` | Views your current clan invites in a neat list to accept/decline. | +| `/clan accept ` | Alternative to clicking in chat to accept an invite. | +| `/clan decline ` | Alternative to clicking in chat to decline an invite. | +| `/clan join ` | Joins a clan if it is open to joins. | +| `/clan leave` | Leaves your current clan. If you are the leader with no other members, disbands the clan. | ### Clan Officer Commands - Restricted to Officers and Above @@ -52,10 +53,9 @@ Currently the default value is not configurable, as this was made for a specific |------------------------------------------------|---------------------------------------------------------| | `/clan promote ` | Promotes a player to Officer rank. | | `/clan demote ` | Demotes a player to Member rank. | -| `/clan disband` | Sends a confirmation message for deletion. | -| `/clan disband confirm` | Deletes your clan directly. | +| `/clan disband [confirm]` | Deletes a clan (if confirmed). | | `/clan set color ` | Changes the color code of your clan. | -| `/clan set access ` | Changes your clan’s access state. | +| `/clan set access ` | Changes your clan’s access state. | | `/clan set name ` | Changes the name of your clan if allowed and available. | | `/clan transfer ` | Transfers clan ownership to a member. | diff --git a/src/main/java/mnfu/clantag/ClanManager.java b/src/main/java/mnfu/clantag/ClanManager.java index 851bc5f..dc36e0f 100644 --- a/src/main/java/mnfu/clantag/ClanManager.java +++ b/src/main/java/mnfu/clantag/ClanManager.java @@ -226,6 +226,10 @@ public Collection getAllClans() { return clans.values(); } + public Collection getAllClansCanonicalNames() { + return clans.keySet(); + } + public boolean load() { if (!file.exists()) return false; diff --git a/src/main/java/mnfu/clantag/ClanTag.java b/src/main/java/mnfu/clantag/ClanTag.java index 86f778e..ec698de 100644 --- a/src/main/java/mnfu/clantag/ClanTag.java +++ b/src/main/java/mnfu/clantag/ClanTag.java @@ -76,6 +76,8 @@ public void onInitialize() { CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { var baseCommand = CommandManager.literal("clan"); + HelpCommand helpCommandClass = new HelpCommand(); + var helpCommand = new HelpCommand().build(); var adminCommand = new AdminCommand(clanManager).build(); var infoCommand = new InfoCommand(clanManager).build(); @@ -96,6 +98,7 @@ public void onInitialize() { var demoteCommand = new DemoteCommand(clanManager).build(); dispatcher.register(baseCommand + .then(helpCommand) .then(adminCommand) .then(setCommand) .then(infoCommand) @@ -111,10 +114,7 @@ public void onInitialize() { .then(transferCommand) .then(promoteCommand) .then(demoteCommand) - .executes(context -> { - context.getSource().sendFeedback(()->Text.literal("Command help message not implemented, WIP!"), false); - return 1; - }) + .executes(helpCommandClass::executeGeneral) ); }); } diff --git a/src/main/java/mnfu/clantag/commands/AdminCommand.java b/src/main/java/mnfu/clantag/commands/AdminCommand.java index cbb0270..929351c 100644 --- a/src/main/java/mnfu/clantag/commands/AdminCommand.java +++ b/src/main/java/mnfu/clantag/commands/AdminCommand.java @@ -52,8 +52,8 @@ public LiteralArgumentBuilder build() { }) .then(CommandManager.argument("clanName", StringArgumentType.greedyString()) .suggests((context, builder) -> { - for (Clan c : clanManager.getAllClans()) { - builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) @@ -82,8 +82,8 @@ public LiteralArgumentBuilder build() { }) .then(CommandManager.argument("clanName", StringArgumentType.greedyString()) .suggests((context, builder) -> { - for (Clan c : clanManager.getAllClans()) { - builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) @@ -112,8 +112,8 @@ public LiteralArgumentBuilder build() { }) .then(CommandManager.argument("clanName", StringArgumentType.greedyString()) .suggests((context, builder) -> { - for (Clan c : clanManager.getAllClans()) { - builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) @@ -134,8 +134,8 @@ public LiteralArgumentBuilder build() { .requires(source -> hasPermission(source, "clantag.admin.rename")) .then(CommandManager.argument("clanName", StringArgumentType.string()) .suggests((context, builder) -> { - for (Clan c : clanManager.getAllClans()) { - builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) @@ -158,8 +158,8 @@ public LiteralArgumentBuilder build() { .requires(source -> hasPermission(source, "clantag.admin.delete")) .then(CommandManager.argument("clanName", StringArgumentType.greedyString()) .suggests((context, builder) -> { - for (Clan c : clanManager.getAllClans()) { - builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) diff --git a/src/main/java/mnfu/clantag/commands/HelpCommand.java b/src/main/java/mnfu/clantag/commands/HelpCommand.java new file mode 100644 index 0000000..18a7742 --- /dev/null +++ b/src/main/java/mnfu/clantag/commands/HelpCommand.java @@ -0,0 +1,148 @@ +package mnfu.clantag.commands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class HelpCommand { + + private final Map cooldowns = new HashMap<>(); + private static final long COOLDOWN_MS = 150; // 0.15 second cooldown + private static final long CLEANUP_MS = 60000; // 60 second cleanup + + private boolean cooldown(ServerCommandSource source) { + ServerPlayerEntity player = source.getPlayer(); + if (player == null) return false; + UUID playerId = player.getUuid(); + long now = System.currentTimeMillis(); + + cooldowns.entrySet().removeIf(entry -> now - entry.getValue() > CLEANUP_MS); + + Long lastClick = cooldowns.get(playerId); + if (lastClick != null && now - lastClick < COOLDOWN_MS) { + source.sendError(Text.literal("Please wait before clicking again")); + return true; + } + + cooldowns.put(playerId, now); + return false; + } + + public LiteralArgumentBuilder build() { + return CommandManager.literal("help") + .executes(this::executeGeneral) // default to "general commands" page + .then(CommandManager.literal("general").executes(this::executeGeneral)) + .then(CommandManager.literal("manage").executes(this::executeManage)) + .then(CommandManager.literal("admin").executes(this::executeAdmin)); + } + + // HELP PAGE FOR GENERAL COMMANDS + public int executeGeneral(CommandContext context) { + if (cooldown(context.getSource())) return 0; + MutableText message = Text.empty(); + + message.append(Text.literal("[ General Commands ]").formatted(Formatting.WHITE)).append("\n"); + message.append(Text.literal("/clan help [pageName]").formatted(Formatting.YELLOW)).append(" - Shows a help menu page (defaults to this page if [pageName] isn't chosen)").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan create ").formatted(Formatting.YELLOW)).append(" - Creates a clan if it doesn't already exist").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan info ").formatted(Formatting.YELLOW)).append(" - Shows info about a clan, and if is blank, it shows your clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan invites").formatted(Formatting.YELLOW)).append(" - Displays your current clan invites to accept/decline").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan accept ").formatted(Formatting.YELLOW)).append(" - Accepts a clan invite").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan decline ").formatted(Formatting.YELLOW)).append(" - Declines a clan invite").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan join ").formatted(Formatting.YELLOW)).append(" - Joins a clan if it is open").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan leave").formatted(Formatting.YELLOW)).append(" - Leaves your current clan. If you are the last remaining member, it disbands the clan as well").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("[Management Help Page]") + .styled(style -> style + .withColor(Formatting.GOLD) + .withClickEvent(new ClickEvent.RunCommand("/clan help manage")) + .withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to view management commands")))) + ); + message.append(Text.literal(" | ").formatted(Formatting.GRAY)); + message.append(Text.literal("[Admin Help Page]") + .styled(style -> style + .withColor(Formatting.GOLD) + .withClickEvent(new ClickEvent.RunCommand("/clan help admin")) + .withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to view admin commands")))) + ); + message.append("\n"); + context.getSource().sendMessage(message); + return 1; + } + + // HELP PAGE FOR MANAGEMENT COMMANDS + private int executeManage(CommandContext context) { + if (cooldown(context.getSource())) return 0; + MutableText message = Text.empty(); + + message.append(Text.literal("[ Management Commands ]").formatted(Formatting.WHITE)).append("\n"); + message.append(Text.literal("/clan invite ").formatted(Formatting.YELLOW)).append(" - Invites to your clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan kick ").formatted(Formatting.YELLOW)).append(" - Kicks from your clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan promote ").formatted(Formatting.YELLOW)).append(" - Promotes a player in the clan rank hierarchy").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan demote ").formatted(Formatting.YELLOW)).append(" - Demotes a player in the clan rank hierarchy").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan disband [confirm]").formatted(Formatting.YELLOW)).append(" - Facilitates the deletion of your clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan set color ").formatted(Formatting.YELLOW)).append(" - Sets your clan color (supports \"WHITE\" or #FFFFFF or FFFFFF formats)").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan set access ").formatted(Formatting.YELLOW)).append(" - Sets your clan access to open or invite only").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan set name ").formatted(Formatting.YELLOW)).append(" - Sets your clan name to if it is available").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan transfer ").formatted(Formatting.YELLOW)).append(" - Transfers clan ownership to ").formatted(Formatting.GRAY).append("\n"); + + message.append(Text.literal("[General Help Page]") + .styled(style -> style + .withColor(Formatting.GOLD) + .withClickEvent(new ClickEvent.RunCommand("/clan help general")) + .withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to view general commands")))) + ); + message.append(Text.literal(" | ").formatted(Formatting.GRAY)); + message.append(Text.literal("[Admin Help Page]") + .styled(style -> style + .withColor(Formatting.GOLD) + .withClickEvent(new ClickEvent.RunCommand("/clan help admin")) + .withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to view admin commands")))) + ); + message.append("\n"); + context.getSource().sendMessage(message); + return 1; + } + + // HELP PAGE FOR ADMIN COMMANDS + private int executeAdmin(CommandContext context) { + if (cooldown(context.getSource())) return 0; + MutableText message = Text.empty(); + + message.append(Text.literal("[ Admin Commands ]").formatted(Formatting.WHITE)).append("\n"); + message.append(Text.literal("/clan admin add ").formatted(Formatting.YELLOW)).append(" - Adds to a clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan admin remove ").formatted(Formatting.YELLOW)).append(" - Removes from a clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan admin delete ").formatted(Formatting.YELLOW)).append(" - Deletes a clan").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan admin rename <\"clanName\"> ").formatted(Formatting.YELLOW)).append(" - Renames a clan (Overrides length limitations)").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan admin transfer ").formatted(Formatting.YELLOW)).append(" - Transfers clan ownership to ").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan admin reload").formatted(Formatting.YELLOW)).append(" - Reloads clans.json from disk").formatted(Formatting.GRAY).append("\n"); + message.append(Text.literal("/clan admin cache clear").formatted(Formatting.YELLOW)).append(" - Clears the MojangAPI Cache").formatted(Formatting.GRAY).append("\n"); + + message.append(Text.literal("[General Help Page]") + .styled(style -> style + .withColor(Formatting.GOLD) + .withClickEvent(new ClickEvent.RunCommand("/clan help general")) + .withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to view general commands")))) + ); + message.append(Text.literal(" | ").formatted(Formatting.GRAY)); + message.append(Text.literal("[Management Help Page]") + .styled(style -> style + .withColor(Formatting.GOLD) + .withClickEvent(new ClickEvent.RunCommand("/clan help manage")) + .withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to view management commands")))) + ); + message.append("\n"); + context.getSource().sendMessage(message); + return 1; + } + +} diff --git a/src/main/java/mnfu/clantag/commands/InfoCommand.java b/src/main/java/mnfu/clantag/commands/InfoCommand.java index 458a9ac..eb481b3 100644 --- a/src/main/java/mnfu/clantag/commands/InfoCommand.java +++ b/src/main/java/mnfu/clantag/commands/InfoCommand.java @@ -14,7 +14,6 @@ import net.minecraft.text.TextColor; import net.minecraft.util.Formatting; -import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.UUID; @@ -34,9 +33,8 @@ public LiteralArgumentBuilder build() { .executes(this::executeForSelf) .then(CommandManager.argument("clanName", StringArgumentType.greedyString()) .suggests((context, builder) -> { - Collection clans = clanManager.getAllClans(); - for (Clan c : clans) { - builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) diff --git a/src/main/java/mnfu/clantag/commands/JoinCommand.java b/src/main/java/mnfu/clantag/commands/JoinCommand.java index 85c3f41..7b61d6c 100644 --- a/src/main/java/mnfu/clantag/commands/JoinCommand.java +++ b/src/main/java/mnfu/clantag/commands/JoinCommand.java @@ -28,8 +28,8 @@ public LiteralArgumentBuilder build() { return CommandManager.literal("join") .then(CommandManager.argument("clanName", StringArgumentType.greedyString()) .suggests((context, builder) -> { - for (Clan c : clanManager.getAllClans()) { - if (!c.isClosed()) builder.suggest(c.name()); + for (String canonicalName : clanManager.getAllClansCanonicalNames()) { + builder.suggest(canonicalName); } return builder.buildFuture(); }) diff --git a/src/main/java/mnfu/clantag/commands/SetCommand.java b/src/main/java/mnfu/clantag/commands/SetCommand.java index 82da0a5..20d16e5 100644 --- a/src/main/java/mnfu/clantag/commands/SetCommand.java +++ b/src/main/java/mnfu/clantag/commands/SetCommand.java @@ -100,6 +100,8 @@ private int executeColor(CommandContext context) { newColor = "#" + Integer.toHexString(formatting.getColorValue()).toUpperCase(Locale.ROOT); } else if (newColor.matches("(?i)^#?[0-9a-f]{1,6}$")) { newColor = "#" + newColor.replaceFirst("^#", "").toUpperCase(Locale.ROOT); + } else if ("reset".equalsIgnoreCase(newColor)) { + newColor = "#" + Integer.toHexString(Formatting.WHITE.getColorValue()).toUpperCase(Locale.ROOT); // Reset to white } else { context.getSource().sendError(Text.literal(newColor + " is not a valid hex color or minecraft color.")); return 0;