Merge pull request #5 from Protonull/fix-commands

Fix and optimise ACF extensions
This commit is contained in:
RedDevel2
2024-02-19 21:57:55 +01:00
committed by GitHub
2 changed files with 122 additions and 16 deletions

View File

@@ -0,0 +1,106 @@
package vg.civcraft.mc.civmodcore.commands;
import co.aikar.commands.CommandCompletions;
import co.aikar.commands.CommandContexts;
import co.aikar.commands.InvalidCommandArgument;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.command.ConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
import vg.civcraft.mc.civmodcore.inventory.items.ItemUtils;
/**
* This is a separate helper class that allows plugins to use ACF without needing to commit to {@link CommandManager}
* to get all the additional features it provides. It also allows some degree of optimisation since not all plugins
* need their own item-material completions (like CivChat2).
*/
public final class CommandHelpers {
/**
* ACF has an automatic help generator, but it's currently considered an unstable API.
* <a href="https://github.com/aikar/commands/wiki/Command-Help">Read more</a>.
*
* <pre><code>
* // Somewhere in your plugin enable process
* CommandHelpers.enableCommandHelp(this.commandManager);
*
* // Example Command
* &#64;CommandAlias("example")
* public class ExampleCommand extends BaseCommand {
* &#64;Default
* public void showHelp(final &#64;NotNull Player sender) {
* throw new ShowCommandHelp(); // Sends a help page to the player
* }
* }
* </code></pre>
*/
@SuppressWarnings("deprecation")
public static void enableCommandHelp(
final @NotNull co.aikar.commands.CommandManager<?, ?, ?, ?, ?, ?> manager
) {
manager.enableUnstableAPI("help");
}
// ============================================================
// Completions
// ============================================================
/**
* ACF already has the "@nothing" completion, but that seems less intuitive than "@none", so this adds "@none".
*/
public static void registerNoneCompletion(
final @NotNull CommandCompletions<?> completions
) {
completions.registerStaticCompletion(
"none",
List.of()
);
}
/**
* Completion for all Bukkit materials.
*/
public static void registerMaterialsCompletion(
final @NotNull CommandCompletions<?> completions
) {
completions.registerStaticCompletion(
"materials",
Arrays.stream(Material.values())
.map(Enum::name)
.toList()
);
}
/**
* Completion for all Bukkit materials that are items, excluding Air.
*/
public static void registerItemMaterialsCompletion(
final @NotNull CommandCompletions<?> completions
) {
completions.registerStaticCompletion(
"itemMaterials",
Arrays.stream(Material.values())
.filter(ItemUtils::isValidItemMaterial)
.map(Enum::name)
.toList()
);
}
// ============================================================
// Contexts
// ============================================================
/**
* Registers a context that requires the command-sender to be the console.
*/
public static void registerConsoleSenderContext(
final @NotNull CommandContexts<?> contexts
) {
contexts.registerIssuerAwareContext(ConsoleCommandSender.class, (context) -> {
if (context.getIssuer().getIssuer() instanceof final ConsoleCommandSender console) {
return console;
}
throw new InvalidCommandArgument("Command can only be called from console!", false);
});
}
}

View File

@@ -8,36 +8,34 @@ import co.aikar.commands.CommandCompletions;
import co.aikar.commands.CommandCompletions.CommandCompletionHandler;
import co.aikar.commands.CommandContexts;
import com.google.common.base.Strings;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.plugin.Plugin;
import vg.civcraft.mc.civmodcore.inventory.items.ItemUtils;
import vg.civcraft.mc.civmodcore.utilities.CivLogger;
/**
* Command registration class wrapper around {@link BukkitCommandManager}.
*/
public class CommandManager extends BukkitCommandManager {
// allMaterials and itemMaterials won't change over a run, so autocomplete lists can be prebuilt globally.
private final static List<String> allMaterials = Arrays.stream(Material.values()).map(Enum::name).toList();
private final static List<String> itemMaterials = Arrays.stream(Material.values()).filter(ItemUtils::isValidItemMaterial).map(Enum::name).toList();
// Track players to offer quick completion where necessary.
private final Set<String> autocompletePlayerNames = new ConcurrentSkipListSet<>();
@@ -76,9 +74,9 @@ public class CommandManager extends BukkitCommandManager {
}
}, plugin);
registerCommands();
registerCompletions(getCommandCompletions());
registerContexts(getCommandContexts());
registerCompletions(getCommandCompletions());
registerCommands();
}
/**
@@ -97,13 +95,14 @@ public class CommandManager extends BukkitCommandManager {
* {@link #getCommandCompletions()}.
*/
public void registerCompletions(@Nonnull final CommandCompletions<BukkitCommandCompletionContext> completions) {
completions.registerCompletion("none", (context) -> Collections.emptyList());
CommandHelpers.registerNoneCompletion(completions);
CommandHelpers.registerMaterialsCompletion(completions);
CommandHelpers.registerItemMaterialsCompletion(completions);
// Completion lists are copied so outer code can modify the lists without breaking our inner contracts,
// namely that all players should be searchable by completion.
// Using Collections.immutableList is an alternative, but both variants aren't expensive.
completions.registerAsyncCompletion("allplayers", (context) -> new ArrayList<>(autocompletePlayerNames));
completions.registerAsyncCompletion("materials", (context) -> new ArrayList<>(allMaterials));
completions.registerAsyncCompletion("itemMaterials", (context) -> new ArrayList<>(itemMaterials));
}
/**
@@ -114,6 +113,7 @@ public class CommandManager extends BukkitCommandManager {
* {@link #getCommandContexts()}.
*/
public void registerContexts(@Nonnull final CommandContexts<BukkitCommandExecutionContext> contexts) {
CommandHelpers.registerConsoleSenderContext(contexts);
}
/**