Skip to content

Commit

Permalink
Added Command#hidden (hide commands from help and subcommand tab comp…
Browse files Browse the repository at this point in the history
…letion), refactored usage message generation, added Command#usage to customize usage, bumped to 2.1.5
  • Loading branch information
vaperion committed Mar 7, 2022
1 parent d7d8baa commit 00f4c09
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 87 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Maven
<dependency>
<groupId>com.github.vaperion</groupId>
<artifactId>blade</artifactId>
<version>2.1.4</version>
<version>2.1.5</version>
<scope>compile</scope>
</dependency>
</dependencies>
Expand All @@ -40,7 +40,7 @@ allprojects {
}
dependencies {
implementation 'com.github.vaperion:blade:2.1.4'
implementation 'com.github.vaperion:blade:2.1.5'
}
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>me.vaperion</groupId>
<artifactId>blade</artifactId>
<version>2.1.4</version>
<version>2.1.5</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down
22 changes: 21 additions & 1 deletion src/main/java/me/vaperion/blade/annotation/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,33 @@
*/
boolean quoted() default false;

/**
* This method indicates whether this command should be hidden from or not.
* <p>Hidden commands do not show up in the generated help message, and cannot be tab completed.</p>
* <p>Players will see the default unknown command message instead of the no permission message when executed without permission.</p>
*/
boolean hidden() default false;

/**
* This is the description of the command that is shown when you hover over the usage message.
*/
String description() default "";

/**
* This data will get appended to the end of the usage message.
* This is the usage message that is shown when an invalid number of arguments is given.
* <p>If this is not set, the usage message will automatically be generated.</p>
*/
String usage() default "";

/**
* This is the alias that should be displayed in the generated help message.
* <p>If this is not set, the first alias in {@link Command#value()} will be used.</p>
*/
String usageAlias() default "";

/**
* This data will get appended to the end of the generated usage message.
* <p>If a custom usage message is set, this will be ignored.</p>
*/
String extraUsageData() default "";
}
10 changes: 8 additions & 2 deletions src/main/java/me/vaperion/blade/command/BladeCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import me.vaperion.blade.argument.BladeProvider;
import me.vaperion.blade.context.BladeContext;
import me.vaperion.blade.service.BladeCommandService;
import me.vaperion.blade.utils.LoadedValue;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Method;
Expand All @@ -22,9 +23,9 @@ public class BladeCommand {
private final Object instance;
private final Method method;
private final String[] aliases, realAliases;
private final String description, extraUsageData;
private final String description, customUsage, extraUsageData, usageAlias;
private final String permission, permissionMessage;
private final boolean async, quoted;
private final boolean async, quoted, hidden;

private final boolean contextBased;

Expand All @@ -34,6 +35,8 @@ public class BladeCommand {
private final List<BladeParameter> parameters = new LinkedList<>();
private final List<BladeProvider<?>> providers = new LinkedList<>(), parameterProviders = new LinkedList<>(), flagProviders = new LinkedList<>();

private final LoadedValue<UsageMessage> usageMessage = new LoadedValue<>();

public BladeCommand(BladeCommandService commandService, Object instance, Method method, String[] aliases, Command command, Permission permission) {
this.commandService = commandService;

Expand All @@ -42,9 +45,12 @@ public BladeCommand(BladeCommandService commandService, Object instance, Method
this.aliases = aliases;
this.realAliases = Arrays.stream(aliases).map(String::toLowerCase).map(s -> s.split(" ")[0]).distinct().toArray(String[]::new);
this.description = command.description();
this.customUsage = command.usage();
this.extraUsageData = command.extraUsageData();
this.usageAlias = command.usageAlias();
this.async = command.async();
this.quoted = command.quoted();
this.hidden = command.hidden();

this.permission = permission == null ? "" : permission.value().trim();
this.permissionMessage = permission == null ? "" : "".equals(permission.message()) ? commandService.getDefaultPermissionMessage() : permission.message();
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/me/vaperion/blade/command/UsageMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package me.vaperion.blade.command;

import me.vaperion.blade.context.BladeContext;
import org.jetbrains.annotations.NotNull;

public interface UsageMessage {
void sendTo(@NotNull BladeContext context);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package me.vaperion.blade.command.impl;

import me.vaperion.blade.annotation.Flag;
import me.vaperion.blade.command.BladeCommand;
import me.vaperion.blade.command.BladeParameter.CommandParameter;
import me.vaperion.blade.command.BladeParameter.FlagParameter;
import me.vaperion.blade.command.UsageMessage;
import me.vaperion.blade.context.BladeContext;
import me.vaperion.blade.utils.MessageBuilder;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;

public class BukkitUsageMessage implements UsageMessage {

private final MessageBuilder messageBuilder;

public BukkitUsageMessage(BladeCommand command) {
this.messageBuilder = new MessageBuilder("Usage: /").color(ChatColor.RED)
.hoverWithColor(ChatColor.GRAY, command.getDescription())
.append(command.getUsageAlias().isEmpty() ? command.getAliases()[0] : command.getUsageAlias());

if (!command.getCustomUsage().isEmpty()) {
this.messageBuilder.append(" ").append(command.getCustomUsage());
return;
}

// Add flag parameters
boolean first = true;
for (FlagParameter flagParameter : command.getFlagParameters()) {
Flag flag = flagParameter.getFlag();

if (first) {
this.messageBuilder.append(" (").reset().color(ChatColor.RED).hoverWithColor(ChatColor.GRAY, command.getDescription());
first = false;
} else {
this.messageBuilder.append(" | ").reset().color(ChatColor.RED).hoverWithColor(ChatColor.GRAY, command.getDescription());
}

this.messageBuilder
.append("-" + flag.value() + (flagParameter.isBooleanFlag() ? "" : " <" + flagParameter.getName() + ">"))
.color(ChatColor.AQUA)
.hoverWithColor(ChatColor.GRAY, flag.description());
}
if (!first) this.messageBuilder.append(")").reset().color(ChatColor.RED).hoverWithColor(ChatColor.GRAY, command.getDescription());

// Add real parameters
for (CommandParameter commandParameter : command.getCommandParameters()) {
this.messageBuilder.append(" ");

this.messageBuilder.append(commandParameter.isOptional() ? "(" : "<");
this.messageBuilder.append(commandParameter.getName());
if (commandParameter.isCombined()) this.messageBuilder.append("...");
this.messageBuilder.append(commandParameter.isOptional() ? ")" : ">");
}

// Add extra usage
if (!command.getExtraUsageData().isEmpty()) {
this.messageBuilder.append(" ").append(command.getExtraUsageData().trim());
}
}

@Override
public void sendTo(@NotNull BladeContext context) {
messageBuilder.sendTo((CommandSender) context.sender().getBackingSender());
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package me.vaperion.blade.container.impl;

import lombok.Getter;
import me.vaperion.blade.annotation.Flag;
import me.vaperion.blade.command.BladeCommand;
import me.vaperion.blade.command.BladeParameter;
import me.vaperion.blade.command.impl.BukkitUsageMessage;
import me.vaperion.blade.container.CommandContainer;
import me.vaperion.blade.container.ContainerCreator;
import me.vaperion.blade.context.BladeContext;
import me.vaperion.blade.context.impl.BukkitSender;
import me.vaperion.blade.exception.BladeExitMessage;
import me.vaperion.blade.exception.BladeUsageMessage;
import me.vaperion.blade.service.BladeCommandService;
import me.vaperion.blade.utils.MessageBuilder;
import me.vaperion.blade.utils.Tuple;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
Expand All @@ -31,11 +29,26 @@
@Getter
public class BukkitCommandContainer extends Command implements CommandContainer {

private static final Field COMMAND_MAP, KNOWN_COMMANDS;
public static final ContainerCreator<BukkitCommandContainer> CREATOR = BukkitCommandContainer::new;
private static final Field COMMAND_MAP, KNOWN_COMMANDS;
private static final String UNKNOWN_COMMAND_MESSAGE;

static {
Field mapField = null, commandsField = null;
Class<?> spigotConfigClass = null;
Field mapField = null, commandsField = null, unknownCommandField = null;
String unknownCommandMessage = ChatColor.WHITE + "Unknown command. Type \"/help\" for help.";

try {
spigotConfigClass = Class.forName("org.spigotmc.SpigotConfig");

unknownCommandField = spigotConfigClass.getDeclaredField("unknownCommandMessage");
unknownCommandField.setAccessible(true);

unknownCommandMessage = ChatColor.WHITE + (String) unknownCommandField.get(null);
} catch (Exception ex) {
System.err.println("Failed to grab unknown command message from SpigotConfig.");
ex.printStackTrace();
}

try {
mapField = SimplePluginManager.class.getDeclaredField("commandMap");
Expand All @@ -55,6 +68,7 @@ public class BukkitCommandContainer extends Command implements CommandContainer

COMMAND_MAP = mapField;
KNOWN_COMMANDS = commandsField;
UNKNOWN_COMMAND_MESSAGE = unknownCommandMessage;
}

private final BladeCommandService commandService;
Expand Down Expand Up @@ -114,66 +128,9 @@ private String getSenderType(@NotNull Class<?> clazz) {
}
}

private void sendUsageMessage(@NotNull CommandSender sender, @NotNull String alias, @Nullable BladeCommand command) {
private void sendUsageMessage(@NotNull BladeContext context, @Nullable BladeCommand command) {
if (command == null) return;
boolean hasDesc = command.getDescription() != null && !command.getDescription().trim().isEmpty();

MessageBuilder builder = new MessageBuilder(ChatColor.RED + "Usage: /").append(ChatColor.RED + alias);
if (hasDesc) builder.hover(Collections.singletonList(ChatColor.GRAY + command.getDescription()));

Optional.of(command.getFlagParameters())
.ifPresent(flagParameters -> {
if (!flagParameters.isEmpty()) {
builder.append(" ").append(ChatColor.RED + "(").reset();
if (hasDesc)
builder.hover(Collections.singletonList(ChatColor.GRAY + command.getDescription().trim()));

int i = 0;
for (BladeParameter.FlagParameter flagParameter : flagParameters) {
builder.append(i++ == 0 ? "" : (ChatColor.GRAY + " | ")).reset();
if (hasDesc)
builder.hover(Collections.singletonList(ChatColor.GRAY + command.getDescription().trim()));

Flag flag = flagParameter.getFlag();

builder.append(ChatColor.AQUA + "-" + flag.value());
if (!flagParameter.isBooleanFlag())
builder.append(ChatColor.AQUA + " <" + flagParameter.getName() + ">");
if (!flag.description().trim().isEmpty())
builder.hover(Collections.singletonList(ChatColor.YELLOW + flag.description().trim()));
}

builder.append(ChatColor.RED + ")").reset();
if (hasDesc)
builder.hover(Collections.singletonList(ChatColor.GRAY + command.getDescription().trim()));
}
});

Optional.of(command.getCommandParameters())
.ifPresent(commandParameters -> {
if (!commandParameters.isEmpty()) {
builder.append(" ");
if (hasDesc)
builder.hover(Collections.singletonList(ChatColor.GRAY + command.getDescription().trim()));

int i = 0;
for (BladeParameter.CommandParameter commandParameter : commandParameters) {
builder.append(i++ == 0 ? "" : " ");

builder.append(ChatColor.RED + (commandParameter.isOptional() ? "(" : "<"));
builder.append(ChatColor.RED + commandParameter.getName());
builder.append(ChatColor.RED + (commandParameter.isOptional() ? ")" : ">"));
}
}
});

if (command.getExtraUsageData() != null && !command.getExtraUsageData().trim().isEmpty()) {
builder.append(" ");
builder.append(ChatColor.RED + command.getExtraUsageData());
if (hasDesc) builder.hover(Collections.singletonList(ChatColor.GRAY + command.getDescription().trim()));
}

builder.sendTo(sender);
command.getUsageMessage().ensureGetOrLoad(() -> new BukkitUsageMessage(command)).sendTo(context);
}

private boolean hasPermission(@NotNull CommandSender sender, String[] args) throws BladeExitMessage {
Expand All @@ -188,7 +145,7 @@ private Tuple<Boolean, String> checkPermission(@NotNull BladeContext context, @N

return new Tuple<>(
commandService.getPermissionTester().testPermission(context, command),
command.getPermissionMessage());
command.isHidden() ? UNKNOWN_COMMAND_MESSAGE : command.getPermissionMessage());
}

private String[] joinAliasToArgs(String alias, String[] args) {
Expand All @@ -209,11 +166,10 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String alias, @No
BladeCommand command = null;
String resolvedAlias = alias;

try {
String[] joined = joinAliasToArgs(alias, args);

BladeContext context = new BladeContext(commandService, new BukkitSender(sender), alias, args);
String[] joined = joinAliasToArgs(alias, args);
BladeContext context = new BladeContext(commandService, new BukkitSender(sender), alias, args);

try {
Tuple<BladeCommand, String> resolved = resolveCommand(joined);
if (resolved == null) {
List<BladeCommand> availableCommands = commandService.getAllBladeCommands()
Expand Down Expand Up @@ -254,13 +210,13 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String alias, @No
finalCommand.getMethod().setAccessible(true);
finalCommand.getMethod().invoke(finalCommand.getInstance(), parsed.toArray(new Object[0]));
} catch (BladeUsageMessage ex) {
sendUsageMessage(sender, finalResolvedAlias, finalCommand);
sendUsageMessage(context, finalCommand);
} catch (BladeExitMessage ex) {
sender.sendMessage(ChatColor.RED + ex.getMessage());
} catch (InvocationTargetException ex) {
if (ex.getTargetException() != null) {
if (ex.getTargetException() instanceof BladeUsageMessage) {
sendUsageMessage(sender, finalResolvedAlias, finalCommand);
sendUsageMessage(context, finalCommand);
return;
} else if (ex.getTargetException() instanceof BladeExitMessage) {
sender.sendMessage(ChatColor.RED + ex.getTargetException().getMessage());
Expand Down Expand Up @@ -296,7 +252,7 @@ public boolean execute(@NotNull CommandSender sender, @NotNull String alias, @No

return true;
} catch (BladeUsageMessage ex) {
sendUsageMessage(sender, resolvedAlias, command);
sendUsageMessage(context, command);
} catch (BladeExitMessage ex) {
sender.sendMessage(ChatColor.RED + ex.getMessage());
} catch (Throwable t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class DefaultHelpGenerator implements HelpGenerator {
@NotNull
@Override
public List<String> generate(@NotNull BladeContext context, @NotNull List<BladeCommand> commands) {
commands = commands.stream().distinct().collect(Collectors.toList());
commands = commands.stream().distinct().filter(c -> !c.isHidden()).collect(Collectors.toList());
List<String> lines = new ArrayList<>();

if (commands.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public void suggestSubCommand(@NotNull List<String> suggestions, @NotNull String
if (currentWordIndex == 0) return;

for (BladeCommand bladeCommand : commandsWithBase) {
if (bladeCommand.isHidden()) continue;
if (!permissionFunction.apply(bladeCommand)) continue;

for (String alias : bladeCommand.getAliases()) {
Expand Down
Loading

0 comments on commit 00f4c09

Please sign in to comment.