diff --git a/docs/acf-bukkit/co/aikar/commands/BukkitCommandCompletions.html b/docs/acf-bukkit/co/aikar/commands/BukkitCommandCompletions.html index 3df24455..870e2f33 100644 --- a/docs/acf-bukkit/co/aikar/commands/BukkitCommandCompletions.html +++ b/docs/acf-bukkit/co/aikar/commands/BukkitCommandCompletions.html @@ -107,7 +107,7 @@
public class BukkitCommandCompletions +public class BukkitCommandCompletions extends co.aikar.commands.CommandCompletions<BukkitCommandCompletionContext>
public BukkitCommandCompletions(BukkitCommandManager manager)+
public BukkitCommandCompletions(BukkitCommandManager manager)
public class BukkitCommandManager +public class BukkitCommandManager extends co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>
addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocaleaddSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, hasPermission, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
protected final org.bukkit.plugin.Plugin plugin+
protected final org.bukkit.plugin.Plugin plugin
public final Integer mcMinorVersion+
public final Integer mcMinorVersion
public final Integer mcPatchVersion+
public final Integer mcPatchVersion
protected Map<String,org.bukkit.command.Command> knownCommands+
protected Map<String,org.bukkit.command.Command> knownCommands
protected Map<String,BukkitRootCommand> registeredCommands+
protected Map<String,BukkitRootCommand> registeredCommands
protected BukkitCommandContexts contexts+
protected BukkitCommandContexts contexts
protected BukkitCommandCompletions completions+
protected BukkitCommandCompletions completions
protected BukkitLocales locales+
protected BukkitLocales locales
protected boolean autoDetectFromClient+
protected boolean autoDetectFromClient
public BukkitCommandManager(org.bukkit.plugin.Plugin plugin)+
public BukkitCommandManager(org.bukkit.plugin.Plugin plugin)
public org.bukkit.plugin.Plugin getPlugin()+
public org.bukkit.plugin.Plugin getPlugin()
public boolean isCommandIssuer(Class<?> type)+
public boolean isCommandIssuer(Class<?> type)
isCommandIssuer in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public co.aikar.commands.CommandContexts<BukkitCommandExecutionContext> getCommandContexts()+
public co.aikar.commands.CommandContexts<BukkitCommandExecutionContext> getCommandContexts()
getCommandContexts in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public co.aikar.commands.CommandCompletions<BukkitCommandCompletionContext> getCommandCompletions()+
public co.aikar.commands.CommandCompletions<BukkitCommandCompletionContext> getCommandCompletions()
getCommandCompletions in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public BukkitLocales getLocales()+
public BukkitLocales getLocales()
getLocales in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public boolean hasRegisteredCommands()+
public boolean hasRegisteredCommands()
hasRegisteredCommands in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public void registerCommand(co.aikar.commands.BaseCommand command, +public void registerCommand(co.aikar.commands.BaseCommand command, boolean force)
public void registerCommand(co.aikar.commands.BaseCommand command)+
public void registerCommand(co.aikar.commands.BaseCommand command)
registerCommand in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public void unregisterCommand(co.aikar.commands.BaseCommand command)+
public void unregisterCommand(co.aikar.commands.BaseCommand command)
@Deprecated -public void unregisterCommand(BukkitRootCommand command)+public void unregisterCommand(BukkitRootCommand command)
public void unregisterCommands()+
public void unregisterCommands()
public Locale setPlayerLocale(org.bukkit.entity.Player player, +public Locale setPlayerLocale(org.bukkit.entity.Player player, Locale locale)
public co.aikar.timings.lib.TimingManager getTimings()+
public co.aikar.timings.lib.TimingManager getTimings()
public co.aikar.commands.RootCommand createRootCommand(String cmd)+
public co.aikar.commands.RootCommand createRootCommand(String cmd)
createRootCommand in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public Collection<co.aikar.commands.RootCommand> getRegisteredRootCommands()+
public Collection<co.aikar.commands.RootCommand> getRegisteredRootCommands()
getRegisteredRootCommands in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public BukkitCommandIssuer getCommandIssuer(Object issuer)+
public BukkitCommandIssuer getCommandIssuer(Object issuer)
getCommandIssuer in class co.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>public BukkitCommandExecutionContext createCommandContext(co.aikar.commands.RegisteredCommand command, +public BukkitCommandExecutionContext createCommandContext(co.aikar.commands.RegisteredCommand command, co.aikar.commands.CommandParameter parameter, co.aikar.commands.CommandIssuer sender, List<String> args, @@ -677,7 +677,7 @@ public void- diff --git a/docs/acf-core/co/aikar/commands/annotation/CommandPermission.html b/docs/acf-core/co/aikar/commands/annotation/CommandPermission.html index bf9c02a1..e972375a 100644 --- a/docs/acf-core/co/aikar/commands/annotation/CommandPermission.html +++ b/docs/acf-core/co/aikar/commands/annotation/CommandPermission.html @@ -93,10 +93,10 @@
createCompletionContext
-public BukkitCommandCompletionContext createCompletionContext(co.aikar.commands.RegisteredCommand command, +public BukkitCommandCompletionContext createCompletionContext(co.aikar.commands.RegisteredCommand command, co.aikar.commands.CommandIssuer sender, String input, String config, @@ -694,7 +694,7 @@ public void- @@ -259,7 +266,7 @@ extends
createRegisteredCommand
-public co.aikar.commands.RegisteredCommand createRegisteredCommand(co.aikar.commands.BaseCommand command, +public co.aikar.commands.RegisteredCommand createRegisteredCommand(co.aikar.commands.BaseCommand command, String cmdName, Method method, String prefSubCommand)@@ -710,7 +710,7 @@ public void- diff --git a/docs/acf-core/co/aikar/commands/CommandCompletions.html b/docs/acf-core/co/aikar/commands/CommandCompletions.html index 7d13aee3..ee4aa849 100644 --- a/docs/acf-core/co/aikar/commands/CommandCompletions.html +++ b/docs/acf-core/co/aikar/commands/CommandCompletions.html @@ -18,7 +18,7 @@ catch(err) { } //--> -var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10}; +var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10}; var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -214,6 +214,13 @@ extends Register a static list of command completions that will never change. +
createConditionContext
-public BukkitConditionContext createConditionContext(co.aikar.commands.CommandIssuer issuer, +public BukkitConditionContext createConditionContext(co.aikar.commands.CommandIssuer issuer, String config)
- Overrides:
@@ -724,7 +724,7 @@ public void- @@ -154,7 +154,7 @@ var activeTableTab = "activeTableTab";
log
-public void log(co.aikar.commands.LogLevel level, +public void log(co.aikar.commands.LogLevel level, String message, Throwable throwable)@@ -739,7 +739,7 @@ public void
- @@ -749,7 +749,7 @@ public void
usePerIssuerLocale
-public boolean usePerIssuerLocale(boolean usePerIssuerLocale, +public boolean usePerIssuerLocale(boolean usePerIssuerLocale, boolean autoDetectFromClient)- diff --git a/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html b/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html index c78c0e3f..885ec98b 100644 --- a/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html +++ b/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
getCommandPrefix
-public String getCommandPrefix(co.aikar.commands.CommandIssuer issuer)+public String getCommandPrefix(co.aikar.commands.CommandIssuer issuer)
- Overrides:
- @@ -762,7 +762,7 @@ public void
getCommandPrefixin classco.aikar.commands.CommandManager<org.bukkit.command.CommandSender,BukkitCommandIssuer,org.bukkit.ChatColor,BukkitMessageFormatter,BukkitCommandExecutionContext,BukkitConditionContext>handleUncaughtException
-protected boolean handleUncaughtException(co.aikar.commands.BaseCommand scope, +protected boolean handleUncaughtException(co.aikar.commands.BaseCommand scope, co.aikar.commands.RegisteredCommand registeredCommand, co.aikar.commands.CommandIssuer sender, List<String> args, diff --git a/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandCompletions.html b/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandCompletions.html index 53f4f463..d927f0e7 100644 --- a/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandCompletions.html +++ b/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandCompletions.html @@ -31,72 +31,76 @@ 023 024package co.aikar.commands; 025 -026import org.apache.commons.lang.Validate; -027import org.bukkit.Bukkit; -028import org.bukkit.ChatColor; -029import org.bukkit.DyeColor; -030import org.bukkit.World; -031import org.bukkit.command.CommandSender; -032import org.bukkit.entity.EntityType; -033import org.bukkit.entity.Player; -034import org.bukkit.util.StringUtil; -035 -036import java.util.ArrayList; -037import java.util.Arrays; -038import java.util.Set; -039import java.util.stream.Collectors; -040import java.util.stream.Stream; -041 -042@SuppressWarnings("WeakerAccess") -043public class BukkitCommandCompletions extends CommandCompletions<BukkitCommandCompletionContext> { -044 public BukkitCommandCompletions(BukkitCommandManager manager) { -045 super(manager); -046 registerAsyncCompletion("mobs", c -> { -047 final Stream<String> normal = Stream.of(EntityType.values()) -048 .map(entityType -> ACFUtil.simplifyString(entityType.getName())); -049 return normal.collect(Collectors.toList()); -050 }); -051 registerAsyncCompletion("chatcolors", c -> { -052 Stream<ChatColor> colors = Stream.of(ChatColor.values()); -053 if (c.hasConfig("colorsonly")) { -054 colors = colors.filter(color -> color.ordinal() <= 0xF); -055 } -056 String filter = c.getConfig("filter"); -057 if (filter != null) { -058 Set<String> filters = Arrays.stream(ACFPatterns.COLON.split(filter)) -059 .map(ACFUtil::simplifyString).collect(Collectors.toSet()); -060 -061 colors = colors.filter(color -> filters.contains(ACFUtil.simplifyString(color.name()))); -062 } -063 -064 return colors.map(color -> ACFUtil.simplifyString(color.name())).collect(Collectors.toList()); -065 }); -066 registerAsyncCompletion("dyecolors", c -> ACFUtil.enumNames(DyeColor.values())); -067 registerCompletion("worlds", c -> ( -068 Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList()) -069 )); -070 -071 registerCompletion("players", c -> { -072 CommandSender sender = c.getSender(); -073 Validate.notNull(sender, "Sender cannot be null"); -074 -075 Player senderPlayer = sender instanceof Player ? (Player) sender : null; -076 -077 ArrayList<String> matchedPlayers = new ArrayList<>(); -078 for (Player player : Bukkit.getOnlinePlayers()) { -079 String name = player.getName(); -080 if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, c.getInput())) { -081 matchedPlayers.add(name); -082 } -083 } -084 +026import co.aikar.commands.bukkit.contexts.OnlinePlayer; +027import org.apache.commons.lang.Validate; +028import org.bukkit.Bukkit; +029import org.bukkit.ChatColor; +030import org.bukkit.DyeColor; +031import org.bukkit.World; +032import org.bukkit.command.CommandSender; +033import org.bukkit.entity.EntityType; +034import org.bukkit.entity.Player; +035import org.bukkit.util.StringUtil; +036 +037import java.util.ArrayList; +038import java.util.Arrays; +039import java.util.Set; +040import java.util.stream.Collectors; +041import java.util.stream.Stream; +042 +043@SuppressWarnings("WeakerAccess") +044public class BukkitCommandCompletions extends CommandCompletions<BukkitCommandCompletionContext> { +045 public BukkitCommandCompletions(BukkitCommandManager manager) { +046 super(manager); +047 registerAsyncCompletion("mobs", c -> { +048 final Stream<String> normal = Stream.of(EntityType.values()) +049 .map(entityType -> ACFUtil.simplifyString(entityType.getName())); +050 return normal.collect(Collectors.toList()); +051 }); +052 registerAsyncCompletion("chatcolors", c -> { +053 Stream<ChatColor> colors = Stream.of(ChatColor.values()); +054 if (c.hasConfig("colorsonly")) { +055 colors = colors.filter(color -> color.ordinal() <= 0xF); +056 } +057 String filter = c.getConfig("filter"); +058 if (filter != null) { +059 Set<String> filters = Arrays.stream(ACFPatterns.COLON.split(filter)) +060 .map(ACFUtil::simplifyString).collect(Collectors.toSet()); +061 +062 colors = colors.filter(color -> filters.contains(ACFUtil.simplifyString(color.name()))); +063 } +064 +065 return colors.map(color -> ACFUtil.simplifyString(color.name())).collect(Collectors.toList()); +066 }); +067 registerAsyncCompletion("dyecolors", c -> ACFUtil.enumNames(DyeColor.values())); +068 registerCompletion("worlds", c -> ( +069 Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList()) +070 )); +071 +072 registerCompletion("players", c -> { +073 CommandSender sender = c.getSender(); +074 Validate.notNull(sender, "Sender cannot be null"); +075 +076 Player senderPlayer = sender instanceof Player ? (Player) sender : null; +077 +078 ArrayList<String> matchedPlayers = new ArrayList<>(); +079 for (Player player : Bukkit.getOnlinePlayers()) { +080 String name = player.getName(); +081 if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, c.getInput())) { +082 matchedPlayers.add(name); +083 } +084 } 085 -086 matchedPlayers.sort(String.CASE_INSENSITIVE_ORDER); -087 return matchedPlayers; -088 }); -089 } +086 +087 matchedPlayers.sort(String.CASE_INSENSITIVE_ORDER); +088 return matchedPlayers; +089 }); 090 -091} +091 setDefaultCompletion("players", OnlinePlayer.class, co.aikar.commands.contexts.OnlinePlayer.class, Player.class); +092 setDefaultCompletion("worlds", World.class); +093 } +094 +095} diff --git a/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandManager.html b/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandManager.html index e048a8d6..e900caba 100644 --- a/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandManager.html +++ b/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandManager.html @@ -43,360 +43,364 @@ 035import org.bukkit.command.CommandSender; 036import org.bukkit.command.PluginIdentifiableCommand; 037import org.bukkit.command.SimpleCommandMap; -038import org.bukkit.entity.Player; -039import org.bukkit.inventory.ItemFactory; -040import org.bukkit.plugin.Plugin; -041import org.bukkit.plugin.PluginManager; -042import org.bukkit.plugin.java.JavaPlugin; -043import org.bukkit.scheduler.BukkitScheduler; -044import org.bukkit.scheduler.BukkitTask; -045import org.bukkit.scoreboard.ScoreboardManager; -046import org.jetbrains.annotations.NotNull; -047 -048import java.lang.reflect.Field; -049import java.lang.reflect.Method; -050import java.util.Collection; -051import java.util.Collections; -052import java.util.HashMap; -053import java.util.List; -054import java.util.Locale; -055import java.util.Map; -056import java.util.Objects; -057import java.util.logging.Level; -058import java.util.logging.Logger; -059import java.util.regex.Matcher; -060import java.util.regex.Pattern; -061 -062@SuppressWarnings("WeakerAccess") -063public class BukkitCommandManager extends CommandManager< -064 CommandSender, -065 BukkitCommandIssuer, -066 ChatColor, -067 BukkitMessageFormatter, -068 BukkitCommandExecutionContext, -069 BukkitConditionContext -070 > { -071 -072 @SuppressWarnings("WeakerAccess") -073 protected final Plugin plugin; -074 private final CommandMap commandMap; -075 private final TimingManager timingManager; -076 private final BukkitTask localeTask; -077 private final Logger logger; -078 public final Integer mcMinorVersion; -079 public final Integer mcPatchVersion; -080 protected Map<String, Command> knownCommands = new HashMap<>(); -081 protected Map<String, BukkitRootCommand> registeredCommands = new HashMap<>(); -082 protected BukkitCommandContexts contexts; -083 protected BukkitCommandCompletions completions; -084 MCTiming commandTiming; -085 protected BukkitLocales locales; -086 private boolean cantReadLocale = false; -087 protected boolean autoDetectFromClient = true; -088 -089 @SuppressWarnings("JavaReflectionMemberAccess") -090 public BukkitCommandManager(Plugin plugin) { -091 this.plugin = plugin; -092 this.logger = Logger.getLogger(this.plugin.getName()); -093 this.timingManager = TimingManager.of(plugin); -094 this.commandTiming = this.timingManager.of("Commands"); -095 this.commandMap = hookCommandMap(); -096 this.formatters.put(MessageType.ERROR, defaultFormatter = new BukkitMessageFormatter(ChatColor.RED, ChatColor.YELLOW, ChatColor.RED)); -097 this.formatters.put(MessageType.SYNTAX, new BukkitMessageFormatter(ChatColor.YELLOW, ChatColor.GREEN, ChatColor.WHITE)); -098 this.formatters.put(MessageType.INFO, new BukkitMessageFormatter(ChatColor.BLUE, ChatColor.DARK_GREEN, ChatColor.GREEN)); -099 this.formatters.put(MessageType.HELP, new BukkitMessageFormatter(ChatColor.AQUA, ChatColor.GREEN, ChatColor.YELLOW)); -100 Pattern versionPattern = Pattern.compile("\\(MC: (\\d)\\.(\\d+)\\.?(\\d+?)?\\)"); -101 Matcher matcher = versionPattern.matcher(Bukkit.getVersion()); -102 if (matcher.find()) { -103 this.mcMinorVersion = ACFUtil.parseInt(matcher.toMatchResult().group(2), 0); -104 this.mcPatchVersion = ACFUtil.parseInt(matcher.toMatchResult().group(3), 0); -105 } else { -106 this.mcMinorVersion = -1; -107 this.mcPatchVersion = -1; -108 } -109 -110 Bukkit.getPluginManager().registerEvents(new ACFBukkitListener(this, plugin), plugin); -111 -112 getLocales(); // auto load locales -113 this.localeTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { -114 if (this.cantReadLocale || !this.autoDetectFromClient) { -115 return; -116 } -117 Bukkit.getOnlinePlayers().forEach(this::readPlayerLocale); -118 }, 5, 5); -119 -120 registerDependency(plugin.getClass(), plugin); -121 registerDependency(Plugin.class, plugin); -122 registerDependency(JavaPlugin.class, plugin); -123 registerDependency(PluginManager.class, Bukkit.getPluginManager()); -124 registerDependency(Server.class, Bukkit.getServer()); -125 registerDependency(BukkitScheduler.class, Bukkit.getScheduler()); -126 registerDependency(ScoreboardManager.class, Bukkit.getScoreboardManager()); -127 registerDependency(ItemFactory.class, Bukkit.getItemFactory()); -128 } -129 -130 @NotNull -131 private CommandMap hookCommandMap() { -132 CommandMap commandMap = null; -133 try { -134 Server server = Bukkit.getServer(); -135 Method getCommandMap = server.getClass().getDeclaredMethod("getCommandMap"); -136 getCommandMap.setAccessible(true); -137 commandMap = (CommandMap) getCommandMap.invoke(server); -138 if (!SimpleCommandMap.class.isAssignableFrom(commandMap.getClass())) { -139 this.log(LogLevel.ERROR, "ERROR: CommandMap has been hijacked! Offending command map is located at: " + commandMap.getClass().getName()); -140 this.log(LogLevel.ERROR, "We are going to try to hijack it back and resolve this, but you are now in dangerous territory."); -141 this.log(LogLevel.ERROR, "We can not guarantee things are going to work."); -142 Field cmField = server.getClass().getDeclaredField("commandMap"); -143 commandMap = new ProxyCommandMap(this, commandMap); -144 cmField.set(server, commandMap); -145 this.log(LogLevel.INFO, "Injected Proxy Command Map... good luck..."); -146 } -147 Field knownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands"); -148 knownCommands.setAccessible(true); -149 //noinspection unchecked -150 this.knownCommands = (Map<String, Command>) knownCommands.get(commandMap); -151 } catch (Exception e) { -152 this.log(LogLevel.ERROR, "Failed to get Command Map. ACF will not function."); -153 ACFUtil.sneaky(e); -154 } -155 return commandMap; -156 } -157 -158 public Plugin getPlugin() { -159 return this.plugin; +038import org.bukkit.configuration.file.FileConfiguration; +039import org.bukkit.entity.Player; +040import org.bukkit.inventory.ItemFactory; +041import org.bukkit.plugin.Plugin; +042import org.bukkit.plugin.PluginManager; +043import org.bukkit.plugin.java.JavaPlugin; +044import org.bukkit.scheduler.BukkitScheduler; +045import org.bukkit.scheduler.BukkitTask; +046import org.bukkit.scoreboard.ScoreboardManager; +047import org.jetbrains.annotations.NotNull; +048 +049import java.lang.reflect.Field; +050import java.lang.reflect.Method; +051import java.util.Collection; +052import java.util.Collections; +053import java.util.HashMap; +054import java.util.List; +055import java.util.Locale; +056import java.util.Map; +057import java.util.Objects; +058import java.util.logging.Level; +059import java.util.logging.Logger; +060import java.util.regex.Matcher; +061import java.util.regex.Pattern; +062 +063@SuppressWarnings("WeakerAccess") +064public class BukkitCommandManager extends CommandManager< +065 CommandSender, +066 BukkitCommandIssuer, +067 ChatColor, +068 BukkitMessageFormatter, +069 BukkitCommandExecutionContext, +070 BukkitConditionContext +071 > { +072 +073 @SuppressWarnings("WeakerAccess") +074 protected final Plugin plugin; +075 private final CommandMap commandMap; +076 private final TimingManager timingManager; +077 private final BukkitTask localeTask; +078 private final Logger logger; +079 public final Integer mcMinorVersion; +080 public final Integer mcPatchVersion; +081 protected Map<String, Command> knownCommands = new HashMap<>(); +082 protected Map<String, BukkitRootCommand> registeredCommands = new HashMap<>(); +083 protected BukkitCommandContexts contexts; +084 protected BukkitCommandCompletions completions; +085 MCTiming commandTiming; +086 protected BukkitLocales locales; +087 private boolean cantReadLocale = false; +088 protected boolean autoDetectFromClient = true; +089 +090 @SuppressWarnings("JavaReflectionMemberAccess") +091 public BukkitCommandManager(Plugin plugin) { +092 this.plugin = plugin; +093 this.logger = Logger.getLogger(this.plugin.getName()); +094 this.timingManager = TimingManager.of(plugin); +095 this.commandTiming = this.timingManager.of("Commands"); +096 this.commandMap = hookCommandMap(); +097 this.formatters.put(MessageType.ERROR, defaultFormatter = new BukkitMessageFormatter(ChatColor.RED, ChatColor.YELLOW, ChatColor.RED)); +098 this.formatters.put(MessageType.SYNTAX, new BukkitMessageFormatter(ChatColor.YELLOW, ChatColor.GREEN, ChatColor.WHITE)); +099 this.formatters.put(MessageType.INFO, new BukkitMessageFormatter(ChatColor.BLUE, ChatColor.DARK_GREEN, ChatColor.GREEN)); +100 this.formatters.put(MessageType.HELP, new BukkitMessageFormatter(ChatColor.AQUA, ChatColor.GREEN, ChatColor.YELLOW)); +101 Pattern versionPattern = Pattern.compile("\\(MC: (\\d)\\.(\\d+)\\.?(\\d+?)?\\)"); +102 Matcher matcher = versionPattern.matcher(Bukkit.getVersion()); +103 if (matcher.find()) { +104 this.mcMinorVersion = ACFUtil.parseInt(matcher.toMatchResult().group(2), 0); +105 this.mcPatchVersion = ACFUtil.parseInt(matcher.toMatchResult().group(3), 0); +106 } else { +107 this.mcMinorVersion = -1; +108 this.mcPatchVersion = -1; +109 } +110 +111 Bukkit.getPluginManager().registerEvents(new ACFBukkitListener(this, plugin), plugin); +112 +113 getLocales(); // auto load locales +114 this.localeTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { +115 if (this.cantReadLocale || !this.autoDetectFromClient) { +116 return; +117 } +118 Bukkit.getOnlinePlayers().forEach(this::readPlayerLocale); +119 }, 5, 5); +120 +121 registerDependency(plugin.getClass(), plugin); +122 registerDependency(Logger.class, plugin.getLogger()); +123 registerDependency(FileConfiguration.class, plugin.getConfig()); +124 registerDependency(FileConfiguration.class, "config", plugin.getConfig()); +125 registerDependency(Plugin.class, plugin); +126 registerDependency(JavaPlugin.class, plugin); +127 registerDependency(PluginManager.class, Bukkit.getPluginManager()); +128 registerDependency(Server.class, Bukkit.getServer()); +129 registerDependency(BukkitScheduler.class, Bukkit.getScheduler()); +130 registerDependency(ScoreboardManager.class, Bukkit.getScoreboardManager()); +131 registerDependency(ItemFactory.class, Bukkit.getItemFactory()); +132 } +133 +134 @NotNull +135 private CommandMap hookCommandMap() { +136 CommandMap commandMap = null; +137 try { +138 Server server = Bukkit.getServer(); +139 Method getCommandMap = server.getClass().getDeclaredMethod("getCommandMap"); +140 getCommandMap.setAccessible(true); +141 commandMap = (CommandMap) getCommandMap.invoke(server); +142 if (!SimpleCommandMap.class.isAssignableFrom(commandMap.getClass())) { +143 this.log(LogLevel.ERROR, "ERROR: CommandMap has been hijacked! Offending command map is located at: " + commandMap.getClass().getName()); +144 this.log(LogLevel.ERROR, "We are going to try to hijack it back and resolve this, but you are now in dangerous territory."); +145 this.log(LogLevel.ERROR, "We can not guarantee things are going to work."); +146 Field cmField = server.getClass().getDeclaredField("commandMap"); +147 commandMap = new ProxyCommandMap(this, commandMap); +148 cmField.set(server, commandMap); +149 this.log(LogLevel.INFO, "Injected Proxy Command Map... good luck..."); +150 } +151 Field knownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands"); +152 knownCommands.setAccessible(true); +153 //noinspection unchecked +154 this.knownCommands = (Map<String, Command>) knownCommands.get(commandMap); +155 } catch (Exception e) { +156 this.log(LogLevel.ERROR, "Failed to get Command Map. ACF will not function."); +157 ACFUtil.sneaky(e); +158 } +159 return commandMap; 160 } 161 -162 @Override -163 public boolean isCommandIssuer(Class<?> type) { -164 return CommandSender.class.isAssignableFrom(type); -165 } -166 -167 @Override -168 public synchronized CommandContexts<BukkitCommandExecutionContext> getCommandContexts() { -169 if (this.contexts == null) { -170 this.contexts = new BukkitCommandContexts(this); -171 } -172 return contexts; -173 } -174 -175 @Override -176 public synchronized CommandCompletions<BukkitCommandCompletionContext> getCommandCompletions() { -177 if (this.completions == null) { -178 this.completions = new BukkitCommandCompletions(this); -179 } -180 return completions; -181 } -182 -183 -184 @Override -185 public BukkitLocales getLocales() { -186 if (this.locales == null) { -187 this.locales = new BukkitLocales(this); -188 this.locales.loadLanguages(); -189 } -190 return locales; -191 } -192 -193 -194 @Override -195 public boolean hasRegisteredCommands() { -196 return !registeredCommands.isEmpty(); -197 } -198 -199 public void registerCommand(BaseCommand command, boolean force) { -200 final String plugin = this.plugin.getName().toLowerCase(); -201 command.onRegister(this); -202 for (Map.Entry<String, RootCommand> entry : command.registeredCommands.entrySet()) { -203 String commandName = entry.getKey().toLowerCase(); -204 BukkitRootCommand bukkitCommand = (BukkitRootCommand) entry.getValue(); -205 if (!bukkitCommand.isRegistered) { -206 Command oldCommand = commandMap.getCommand(commandName); -207 if (oldCommand instanceof PluginIdentifiableCommand && ((PluginIdentifiableCommand) oldCommand).getPlugin() == this.plugin) { -208 knownCommands.remove(commandName); -209 oldCommand.unregister(commandMap); -210 } else if (oldCommand != null && force) { -211 knownCommands.remove(commandName); -212 for (Map.Entry<String, Command> ce : knownCommands.entrySet()) { -213 String key = ce.getKey(); -214 Command value = ce.getValue(); -215 if (key.contains(":") && oldCommand.equals(value)) { -216 String[] split = ACFPatterns.COLON.split(key, 2); -217 if (split.length > 1) { -218 oldCommand.unregister(commandMap); -219 oldCommand.setLabel(split[0] + ":" + command.getName()); -220 oldCommand.register(commandMap); -221 } -222 } -223 } -224 } -225 commandMap.register(commandName, plugin, bukkitCommand); -226 } -227 bukkitCommand.isRegistered = true; -228 registeredCommands.put(commandName, bukkitCommand); -229 } -230 } -231 -232 @Override -233 public void registerCommand(BaseCommand command) { -234 registerCommand(command, false); -235 } -236 -237 public void unregisterCommand(BaseCommand command) { -238 for (RootCommand rootcommand : command.registeredCommands.values()) { -239 BukkitRootCommand bukkitCommand = (BukkitRootCommand) rootcommand; -240 bukkitCommand.getSubCommands().values().removeAll(command.subCommands.values()); -241 if (bukkitCommand.isRegistered && bukkitCommand.getSubCommands().isEmpty()) { -242 unregisterCommand(bukkitCommand); -243 bukkitCommand.isRegistered = false; -244 } -245 } -246 } -247 -248 /** -249 * @param command -250 * @deprecated Use unregisterCommand(BaseCommand) - this will be visibility reduced later. -251 */ -252 @Deprecated -253 public void unregisterCommand(BukkitRootCommand command) { -254 final String plugin = this.plugin.getName().toLowerCase(); -255 command.unregister(commandMap); -256 String key = command.getName(); -257 Command registered = knownCommands.get(key); -258 if (command.equals(registered)) { -259 knownCommands.remove(key); -260 } -261 knownCommands.remove(plugin + ":" + key); -262 } -263 -264 public void unregisterCommands() { -265 for (Map.Entry<String, BukkitRootCommand> entry : registeredCommands.entrySet()) { -266 unregisterCommand(entry.getValue()); -267 } -268 this.registeredCommands.clear(); -269 } -270 -271 -272 private Field getEntityField(Player player) throws NoSuchFieldException { -273 Class cls = player.getClass(); -274 while (cls != Object.class) { -275 if (cls.getName().endsWith("CraftEntity")) { -276 Field field = cls.getDeclaredField("entity"); -277 field.setAccessible(true); -278 return field; -279 } -280 cls = cls.getSuperclass(); -281 } -282 return null; -283 } -284 -285 public Locale setPlayerLocale(Player player, Locale locale) { -286 return this.setIssuerLocale(player, locale); +162 public Plugin getPlugin() { +163 return this.plugin; +164 } +165 +166 @Override +167 public boolean isCommandIssuer(Class<?> type) { +168 return CommandSender.class.isAssignableFrom(type); +169 } +170 +171 @Override +172 public synchronized CommandContexts<BukkitCommandExecutionContext> getCommandContexts() { +173 if (this.contexts == null) { +174 this.contexts = new BukkitCommandContexts(this); +175 } +176 return contexts; +177 } +178 +179 @Override +180 public synchronized CommandCompletions<BukkitCommandCompletionContext> getCommandCompletions() { +181 if (this.completions == null) { +182 this.completions = new BukkitCommandCompletions(this); +183 } +184 return completions; +185 } +186 +187 +188 @Override +189 public BukkitLocales getLocales() { +190 if (this.locales == null) { +191 this.locales = new BukkitLocales(this); +192 this.locales.loadLanguages(); +193 } +194 return locales; +195 } +196 +197 +198 @Override +199 public boolean hasRegisteredCommands() { +200 return !registeredCommands.isEmpty(); +201 } +202 +203 public void registerCommand(BaseCommand command, boolean force) { +204 final String plugin = this.plugin.getName().toLowerCase(); +205 command.onRegister(this); +206 for (Map.Entry<String, RootCommand> entry : command.registeredCommands.entrySet()) { +207 String commandName = entry.getKey().toLowerCase(); +208 BukkitRootCommand bukkitCommand = (BukkitRootCommand) entry.getValue(); +209 if (!bukkitCommand.isRegistered) { +210 Command oldCommand = commandMap.getCommand(commandName); +211 if (oldCommand instanceof PluginIdentifiableCommand && ((PluginIdentifiableCommand) oldCommand).getPlugin() == this.plugin) { +212 knownCommands.remove(commandName); +213 oldCommand.unregister(commandMap); +214 } else if (oldCommand != null && force) { +215 knownCommands.remove(commandName); +216 for (Map.Entry<String, Command> ce : knownCommands.entrySet()) { +217 String key = ce.getKey(); +218 Command value = ce.getValue(); +219 if (key.contains(":") && oldCommand.equals(value)) { +220 String[] split = ACFPatterns.COLON.split(key, 2); +221 if (split.length > 1) { +222 oldCommand.unregister(commandMap); +223 oldCommand.setLabel(split[0] + ":" + command.getName()); +224 oldCommand.register(commandMap); +225 } +226 } +227 } +228 } +229 commandMap.register(commandName, plugin, bukkitCommand); +230 } +231 bukkitCommand.isRegistered = true; +232 registeredCommands.put(commandName, bukkitCommand); +233 } +234 } +235 +236 @Override +237 public void registerCommand(BaseCommand command) { +238 registerCommand(command, false); +239 } +240 +241 public void unregisterCommand(BaseCommand command) { +242 for (RootCommand rootcommand : command.registeredCommands.values()) { +243 BukkitRootCommand bukkitCommand = (BukkitRootCommand) rootcommand; +244 bukkitCommand.getSubCommands().values().removeAll(command.subCommands.values()); +245 if (bukkitCommand.isRegistered && bukkitCommand.getSubCommands().isEmpty()) { +246 unregisterCommand(bukkitCommand); +247 bukkitCommand.isRegistered = false; +248 } +249 } +250 } +251 +252 /** +253 * @param command +254 * @deprecated Use unregisterCommand(BaseCommand) - this will be visibility reduced later. +255 */ +256 @Deprecated +257 public void unregisterCommand(BukkitRootCommand command) { +258 final String plugin = this.plugin.getName().toLowerCase(); +259 command.unregister(commandMap); +260 String key = command.getName(); +261 Command registered = knownCommands.get(key); +262 if (command.equals(registered)) { +263 knownCommands.remove(key); +264 } +265 knownCommands.remove(plugin + ":" + key); +266 } +267 +268 public void unregisterCommands() { +269 for (Map.Entry<String, BukkitRootCommand> entry : registeredCommands.entrySet()) { +270 unregisterCommand(entry.getValue()); +271 } +272 this.registeredCommands.clear(); +273 } +274 +275 +276 private Field getEntityField(Player player) throws NoSuchFieldException { +277 Class cls = player.getClass(); +278 while (cls != Object.class) { +279 if (cls.getName().endsWith("CraftEntity")) { +280 Field field = cls.getDeclaredField("entity"); +281 field.setAccessible(true); +282 return field; +283 } +284 cls = cls.getSuperclass(); +285 } +286 return null; 287 } 288 -289 void readPlayerLocale(Player player) { -290 if (!player.isOnline() || cantReadLocale) { -291 return; -292 } -293 try { -294 Field entityField = getEntityField(player); -295 if (entityField == null) { -296 return; -297 } -298 Object nmsPlayer = entityField.get(player); -299 if (nmsPlayer != null) { -300 Field localeField = nmsPlayer.getClass().getDeclaredField("locale"); -301 Object localeString = localeField.get(nmsPlayer); -302 if (localeString instanceof String) { -303 String[] split = ACFPatterns.UNDERSCORE.split((String) localeString); -304 Locale locale = split.length > 1 ? new Locale(split[0], split[1]) : new Locale(split[0]); -305 Locale prev = issuersLocale.put(player.getUniqueId(), locale); -306 if (!Objects.equals(locale, prev)) { -307 this.notifyLocaleChange(getCommandIssuer(player), prev, locale); -308 } -309 } -310 } -311 } catch (Exception e) { -312 cantReadLocale = true; -313 this.localeTask.cancel(); -314 this.log(LogLevel.INFO, "Can't read players locale, you will be unable to automatically detect players language. Only Bukkit 1.7+ is supported for this.", e); -315 } -316 } -317 -318 public TimingManager getTimings() { -319 return timingManager; +289 public Locale setPlayerLocale(Player player, Locale locale) { +290 return this.setIssuerLocale(player, locale); +291 } +292 +293 void readPlayerLocale(Player player) { +294 if (!player.isOnline() || cantReadLocale) { +295 return; +296 } +297 try { +298 Field entityField = getEntityField(player); +299 if (entityField == null) { +300 return; +301 } +302 Object nmsPlayer = entityField.get(player); +303 if (nmsPlayer != null) { +304 Field localeField = nmsPlayer.getClass().getDeclaredField("locale"); +305 Object localeString = localeField.get(nmsPlayer); +306 if (localeString instanceof String) { +307 String[] split = ACFPatterns.UNDERSCORE.split((String) localeString); +308 Locale locale = split.length > 1 ? new Locale(split[0], split[1]) : new Locale(split[0]); +309 Locale prev = issuersLocale.put(player.getUniqueId(), locale); +310 if (!Objects.equals(locale, prev)) { +311 this.notifyLocaleChange(getCommandIssuer(player), prev, locale); +312 } +313 } +314 } +315 } catch (Exception e) { +316 cantReadLocale = true; +317 this.localeTask.cancel(); +318 this.log(LogLevel.INFO, "Can't read players locale, you will be unable to automatically detect players language. Only Bukkit 1.7+ is supported for this.", e); +319 } 320 } 321 -322 @Override -323 public RootCommand createRootCommand(String cmd) { -324 return new BukkitRootCommand(this, cmd); -325 } -326 -327 @Override -328 public Collection<RootCommand> getRegisteredRootCommands() { -329 return Collections.unmodifiableCollection(registeredCommands.values()); -330 } -331 -332 @Override -333 public BukkitCommandIssuer getCommandIssuer(Object issuer) { -334 if (!(issuer instanceof CommandSender)) { -335 throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Command Issuer."); -336 } -337 return new BukkitCommandIssuer(this, (CommandSender) issuer); -338 } -339 -340 @Override -341 public BukkitCommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs) { -342 return new BukkitCommandExecutionContext(command, parameter, (BukkitCommandIssuer) sender, args, i, passedArgs); -343 } -344 -345 @Override -346 public BukkitCommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) { -347 return new BukkitCommandCompletionContext(command, (BukkitCommandIssuer) sender, input, config, args); -348 } -349 -350 @Override -351 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) { -352 return new BukkitRegisteredCommand(command, cmdName, method, prefSubCommand); -353 } -354 -355 @Override -356 public BukkitConditionContext createConditionContext(CommandIssuer issuer, String config) { -357 return new BukkitConditionContext((BukkitCommandIssuer) issuer, config); -358 } -359 -360 -361 @Override -362 public void log(LogLevel level, String message, Throwable throwable) { -363 Level logLevel = level == LogLevel.INFO ? Level.INFO : Level.SEVERE; -364 logger.log(logLevel, LogLevel.LOG_PREFIX + message); -365 if (throwable != null) { -366 for (String line : ACFPatterns.NEWLINE.split(ApacheCommonsExceptionUtil.getFullStackTrace(throwable))) { -367 logger.log(logLevel, LogLevel.LOG_PREFIX + line); -368 } -369 } -370 } -371 -372 public boolean usePerIssuerLocale(boolean usePerIssuerLocale, boolean autoDetectFromClient) { -373 boolean old = this.usePerIssuerLocale; -374 this.usePerIssuerLocale = usePerIssuerLocale; -375 this.autoDetectFromClient = autoDetectFromClient; -376 return old; -377 } -378 -379 @Override -380 public String getCommandPrefix(CommandIssuer issuer) { -381 return issuer.isPlayer() ? "/" : ""; -382 } -383 -384 @Override -385 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) { -386 if (t instanceof CommandException && t.getCause() != null && t.getMessage().startsWith("Unhandled exception")) { -387 t = t.getCause(); -388 } -389 return super.handleUncaughtException(scope, registeredCommand, sender, args, t); -390 } -391} +322 public TimingManager getTimings() { +323 return timingManager; +324 } +325 +326 @Override +327 public RootCommand createRootCommand(String cmd) { +328 return new BukkitRootCommand(this, cmd); +329 } +330 +331 @Override +332 public Collection<RootCommand> getRegisteredRootCommands() { +333 return Collections.unmodifiableCollection(registeredCommands.values()); +334 } +335 +336 @Override +337 public BukkitCommandIssuer getCommandIssuer(Object issuer) { +338 if (!(issuer instanceof CommandSender)) { +339 throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Command Issuer."); +340 } +341 return new BukkitCommandIssuer(this, (CommandSender) issuer); +342 } +343 +344 @Override +345 public BukkitCommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs) { +346 return new BukkitCommandExecutionContext(command, parameter, (BukkitCommandIssuer) sender, args, i, passedArgs); +347 } +348 +349 @Override +350 public BukkitCommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) { +351 return new BukkitCommandCompletionContext(command, (BukkitCommandIssuer) sender, input, config, args); +352 } +353 +354 @Override +355 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) { +356 return new BukkitRegisteredCommand(command, cmdName, method, prefSubCommand); +357 } +358 +359 @Override +360 public BukkitConditionContext createConditionContext(CommandIssuer issuer, String config) { +361 return new BukkitConditionContext((BukkitCommandIssuer) issuer, config); +362 } +363 +364 +365 @Override +366 public void log(LogLevel level, String message, Throwable throwable) { +367 Level logLevel = level == LogLevel.INFO ? Level.INFO : Level.SEVERE; +368 logger.log(logLevel, LogLevel.LOG_PREFIX + message); +369 if (throwable != null) { +370 for (String line : ACFPatterns.NEWLINE.split(ApacheCommonsExceptionUtil.getFullStackTrace(throwable))) { +371 logger.log(logLevel, LogLevel.LOG_PREFIX + line); +372 } +373 } +374 } +375 +376 public boolean usePerIssuerLocale(boolean usePerIssuerLocale, boolean autoDetectFromClient) { +377 boolean old = this.usePerIssuerLocale; +378 this.usePerIssuerLocale = usePerIssuerLocale; +379 this.autoDetectFromClient = autoDetectFromClient; +380 return old; +381 } +382 +383 @Override +384 public String getCommandPrefix(CommandIssuer issuer) { +385 return issuer.isPlayer() ? "/" : ""; +386 } +387 +388 @Override +389 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) { +390 if (t instanceof CommandException && t.getCause() != null && t.getMessage().startsWith("Unhandled exception")) { +391 t = t.getCause(); +392 } +393 return super.handleUncaughtException(scope, registeredCommand, sender, args, t); +394 } +395} diff --git a/docs/acf-bungee/co/aikar/commands/BungeeCommandCompletions.html b/docs/acf-bungee/co/aikar/commands/BungeeCommandCompletions.html index cd19c308..29028492 100644 --- a/docs/acf-bungee/co/aikar/commands/BungeeCommandCompletions.html +++ b/docs/acf-bungee/co/aikar/commands/BungeeCommandCompletions.html @@ -158,7 +158,7 @@ extends co.aikar.commands.CommandCompletions<- diff --git a/docs/acf-bungee/co/aikar/commands/BungeeCommandExecutionContext.html b/docs/acf-bungee/co/aikar/commands/BungeeCommandExecutionContext.html index cdd16efc..be86216d 100644 --- a/docs/acf-bungee/co/aikar/commands/BungeeCommandExecutionContext.html +++ b/docs/acf-bungee/co/aikar/commands/BungeeCommandExecutionContext.html @@ -162,7 +162,7 @@ extends co.aikar.commands.CommandExecutionContext<
- diff --git a/docs/acf-bungee/co/aikar/commands/BungeeCommandManager.html b/docs/acf-bungee/co/aikar/commands/BungeeCommandManager.html index fa0c3187..03f08bfe 100644 --- a/docs/acf-bungee/co/aikar/commands/BungeeCommandManager.html +++ b/docs/acf-bungee/co/aikar/commands/BungeeCommandManager.html @@ -293,7 +293,7 @@ extends co.aikar.commands.CommandManager<net.md_5.bungee.api.CommandSender,
+Methods inherited from class co.aikar.commands.CommandManager
-addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocaleaddSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
-public static interface CommandCompletions.AsyncCommandCompletionHandler<C extends CommandCompletionContext> +public static interface CommandCompletions.AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletions.CommandCompletionHandler<C>
-public static interface CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext>+public static interface CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext>
getCompletions
-Collection<String> getCompletions(C context) +Collection<String> getCompletions(C context) throws InvalidCommandArgument
- Throws:
diff --git a/docs/acf-core/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html b/docs/acf-core/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html index 33326a56..723551c5 100644 --- a/docs/acf-core/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html +++ b/docs/acf-core/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html @@ -125,7 +125,7 @@
-public static class CommandCompletions.SyncCompletionRequired +public static class CommandCompletions.SyncCompletionRequired extends RuntimeException+ + void+ setDefaultCompletion(String id, + Class... classes)+Registers a completion handler such as @players to default apply to all command parameters of the specified types+registerCompletion
-public CommandCompletions.CommandCompletionHandler registerCompletion(String id, +public CommandCompletions.CommandCompletionHandler registerCompletion(String id, CommandCompletions.CommandCompletionHandler<C> handler)Registr a completion handler to provide command completions based on the user input.@@ -276,7 +283,7 @@ extends
- @@ -543,7 +555,7 @@ not permitted.)
registerAsyncCompletion
-public CommandCompletions.CommandCompletionHandler registerAsyncCompletion(String id, +public CommandCompletions.CommandCompletionHandler registerAsyncCompletion(String id, CommandCompletions.AsyncCommandCompletionHandler<C> handler)Registr a completion handler to provide command completions based on the user input. This handler is declared to be safe to be executed asynchronously. @@ -301,7 +308,7 @@ extends- + + + +
registerStaticCompletion
-public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, +public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, String list)Register a static list of command completions that will never change. Like @CommandCompletion, values are | (PIPE) separated. @@ -321,7 +328,7 @@ extends- @@ -359,7 +363,7 @@ extends
registerStaticCompletion
-public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, +public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, String[] completions)Register a static list of command completions that will never change@@ -338,7 +345,7 @@ extends
- diff --git a/docs/acf-core/co/aikar/commands/CommandExecutionContext.html b/docs/acf-core/co/aikar/commands/CommandExecutionContext.html index 190d72f7..637071ff 100644 --- a/docs/acf-core/co/aikar/commands/CommandExecutionContext.html +++ b/docs/acf-core/co/aikar/commands/CommandExecutionContext.html @@ -18,7 +18,7 @@ catch(err) { } //--> -var methods = {"i0":10,"i1":42,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":42,"i25":10,"i26":10,"i27":10,"i28":10,"i29":10,"i30":10,"i31":10,"i32":10,"i33":10,"i34":10,"i35":10,"i36":10}; +var methods = {"i0":10,"i1":42,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":42,"i25":10,"i26":10,"i27":10,"i28":10,"i29":10,"i30":10,"i31":10,"i32":10,"i33":10,"i34":10,"i35":10,"i36":10,"i37":10}; var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
registerStaticCompletion
-public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, +public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier)Register a static list of command completions that will never change. The list is obtained from the supplier immediately as part of this method call.@@ -353,10 +360,10 @@ extends -+
registerStaticCompletion
-public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, +public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions)Register a static list of command completions that will never change@@ -367,6 +374,24 @@ extends + + +
+
- +
+setDefaultCompletion
+public void setDefaultCompletion(String id, + Class... classes)+Registers a completion handler such as @players to default apply to all command parameters of the specified types +++ This enables automatic completion support for parameters without manually defining it for custom objects
+
+- Parameters:
+- +
id-- +
classes-- @@ -266,51 +266,55 @@ extends +
-public class CommandExecutionContext<CEC extends CommandExecutionContext,I extends CommandIssuer> +public class CommandExecutionContext<CEC extends CommandExecutionContext,I extends CommandIssuer> extends Object+ Set<String>+ + getParameterPermissions()- Map<String,Object>getPassedArgs()+ - ObjectgetResolvedArg(Class<?>... classes)+ - ObjectgetResolvedArg(String arg)+ - <T> TgetResolvedArg(String key, Class<?>... classes)+ - <T extends Annotation>
booleanhasAnnotation(Class<T> cls)+ - booleanhasFlag(String flag)+ - booleanisLastArg()+ - booleanisOptional()+ - StringjoinArgs()+ - StringjoinArgs(String sep)+ - StringpopFirstArg()+ @@ -342,7 +346,7 @@ extends StringpopLastArg()issuer
-protected final I extends CommandIssuer issuer+protected final I extends CommandIssuer issuer- @@ -368,7 +372,7 @@ extends
popFirstArg
-public String popFirstArg()+public String popFirstArg()- @@ -377,7 +381,7 @@ extends
popLastArg
-public String popLastArg()+public String popLastArg()- @@ -386,7 +390,7 @@ extends
getFirstArg
-public String getFirstArg()+public String getFirstArg()- @@ -395,7 +399,7 @@ extends
getLastArg
-public String getLastArg()+public String getLastArg()- @@ -404,7 +408,7 @@ extends
isLastArg
-public boolean isLastArg()+public boolean isLastArg()- @@ -413,7 +417,7 @@ extends
getNumParams
-public int getNumParams()+public int getNumParams()- @@ -422,7 +426,7 @@ extends
canOverridePlayerContext
-public boolean canOverridePlayerContext()+public boolean canOverridePlayerContext()- @@ -431,7 +435,7 @@ extends
getResolvedArg
-public Object getResolvedArg(String arg)+public Object getResolvedArg(String arg)- @@ -440,17 +444,26 @@ extends
getResolvedArg
-public Object getResolvedArg(Class<?>... classes)+public Object getResolvedArg(Class<?>... classes)- + + + +
getResolvedArg
-public <T> T getResolvedArg(String key, +public <T> T getResolvedArg(String key, Class<?>... classes)+
- +
+getParameterPermissions
+public Set<String> getParameterPermissions()+@@ -459,7 +472,7 @@ extends
isOptional
-public boolean isOptional()+public boolean isOptional()- @@ -468,7 +481,7 @@ extends
hasFlag
-public boolean hasFlag(String flag)+public boolean hasFlag(String flag)- @@ -478,7 +491,7 @@ extends
getFlagValue
-public String getFlagValue(String flag, +public String getFlagValue(String flag, String def)- @@ -488,7 +501,7 @@ extends
getFlagValue
-public Integer getFlagValue(String flag, +public Integer getFlagValue(String flag, Integer def)- @@ -498,7 +511,7 @@ extends
getFlagValue
-public Long getFlagValue(String flag, +public Long getFlagValue(String flag, Long def)- @@ -508,7 +521,7 @@ extends
getFlagValue
-public Float getFlagValue(String flag, +public Float getFlagValue(String flag, Float def)- @@ -518,7 +531,7 @@ extends
getFlagValue
-public Double getFlagValue(String flag, +public Double getFlagValue(String flag, Double def)- @@ -528,7 +541,7 @@ extends
getIntFlagValue
-public Integer getIntFlagValue(String flag, +public Integer getIntFlagValue(String flag, Number def)- @@ -538,7 +551,7 @@ extends
getLongFlagValue
-public Long getLongFlagValue(String flag, +public Long getLongFlagValue(String flag, Number def)- @@ -548,7 +561,7 @@ extends
getFloatFlagValue
-public Float getFloatFlagValue(String flag, +public Float getFloatFlagValue(String flag, Number def)- @@ -558,7 +571,7 @@ extends
getDoubleFlagValue
-public Double getDoubleFlagValue(String flag, +public Double getDoubleFlagValue(String flag, Number def)- @@ -567,7 +580,7 @@ extends
getBooleanFlagValue
-public Boolean getBooleanFlagValue(String flag)+public Boolean getBooleanFlagValue(String flag)- @@ -577,7 +590,7 @@ extends
getBooleanFlagValue
-public Boolean getBooleanFlagValue(String flag, +public Boolean getBooleanFlagValue(String flag, Boolean def)- @@ -588,7 +601,7 @@ extends
getFlagValue
-public Double getFlagValue(String flag, +public Double getFlagValue(String flag, Number def)getAnnotation
@Deprecated -public <T extends Annotation> T getAnnotation(Class<T> cls)+public <T extends Annotation> T getAnnotation(Class<T> cls)Deprecated. UsegetAnnotationValue(Class)This method will not support annotation processors!! use getAnnotationValue or hasAnnotation@@ -599,7 +612,7 @@ public <T extends- @@ -608,7 +621,7 @@ public <T extends
getAnnotationValue
-public <T extends Annotation> String getAnnotationValue(Class<T> cls)+public <T extends Annotation> String getAnnotationValue(Class<T> cls)- @@ -618,7 +631,7 @@ public <T extends
getAnnotationValue
-public <T extends Annotation> String getAnnotationValue(Class<T> cls, +public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options)- @@ -627,7 +640,7 @@ public <T extends
hasAnnotation
-public <T extends Annotation> boolean hasAnnotation(Class<T> cls)+public <T extends Annotation> boolean hasAnnotation(Class<T> cls)- @@ -637,7 +650,7 @@ public <T extends
getCmd
-public RegisteredCommand getCmd()+public RegisteredCommand getCmd()getParam
@Deprecated -public Parameter getParam()+public Parameter getParam()Deprecated.@@ -647,7 +660,7 @@ public- @@ -656,7 +669,7 @@ public
getIssuer
-public I getIssuer()+public I getIssuer()- @@ -665,7 +678,7 @@ public
getArgs
-public List<String> getArgs()+public List<String> getArgs()- @@ -674,7 +687,7 @@ public
getIndex
-public int getIndex()+public int getIndex()- @@ -683,7 +696,7 @@ public
getPassedArgs
-public Map<String,Object> getPassedArgs()+public Map<String,Object> getPassedArgs()- @@ -692,7 +705,7 @@ public
getFlags
-public Map<String,String> getFlags()+public Map<String,String> getFlags()- @@ -701,7 +714,7 @@ public
joinArgs
-public String joinArgs()+public String joinArgs()- diff --git a/docs/acf-core/co/aikar/commands/CommandManager.html b/docs/acf-core/co/aikar/commands/CommandManager.html index c443cc5c..c53192ae 100644 --- a/docs/acf-core/co/aikar/commands/CommandManager.html +++ b/docs/acf-core/co/aikar/commands/CommandManager.html @@ -18,7 +18,7 @@ catch(err) { } //--> -var methods = {"i0":10,"i1":6,"i2":6,"i3":10,"i4":10,"i5":6,"i6":42,"i7":10,"i8":42,"i9":42,"i10":42,"i11":42,"i12":6,"i13":10,"i14":6,"i15":6,"i16":10,"i17":10,"i18":9,"i19":9,"i20":9,"i21":10,"i22":10,"i23":42,"i24":10,"i25":42,"i26":10,"i27":6,"i28":6,"i29":10,"i30":10,"i31":10,"i32":10,"i33":6,"i34":6,"i35":10,"i36":10,"i37":6,"i38":10,"i39":10,"i40":10,"i41":6,"i42":10,"i43":10,"i44":10,"i45":10,"i46":10,"i47":10,"i48":10,"i49":42,"i50":10,"i51":10,"i52":10,"i53":42,"i54":10,"i55":10,"i56":10}; +var methods = {"i0":10,"i1":6,"i2":6,"i3":10,"i4":10,"i5":6,"i6":42,"i7":10,"i8":42,"i9":42,"i10":42,"i11":42,"i12":6,"i13":10,"i14":6,"i15":6,"i16":10,"i17":10,"i18":9,"i19":9,"i20":9,"i21":10,"i22":10,"i23":42,"i24":10,"i25":42,"i26":10,"i27":6,"i28":6,"i29":10,"i30":10,"i31":10,"i32":10,"i33":10,"i34":6,"i35":6,"i36":10,"i37":10,"i38":6,"i39":10,"i40":10,"i41":10,"i42":6,"i43":10,"i44":10,"i45":10,"i46":10,"i47":10,"i48":10,"i49":10,"i50":42,"i51":10,"i52":10,"i53":10,"i54":42,"i55":10,"i56":10,"i57":10}; var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],2:["t2","Instance Methods"],4:["t3","Abstract Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -408,53 +408,58 @@ extends
joinArgs
-public String joinArgs(String sep)+public String joinArgs(String sep)+ boolean+ + hasPermission(CommandIssuer issuer, + Set<String> permissions)+ -booleanhasPermission(CommandIssuer issuer, String permission)+ - abstract booleanhasRegisteredCommands()+ - abstract booleanisCommandIssuer(Class<?> type)+ - booleanisLoggingUnhandledExceptions()+ - voidlog(co.aikar.commands.LogLevel level, String message)+ - abstract voidlog(co.aikar.commands.LogLevel level, String message, Throwable throwable)+ - voidnotifyLocaleChange(I issuer, Locale oldLocale, Locale newLocale)+ - RootCommandobtainRootCommand(@NotNull String cmd)+ - voidonLocaleChange(IssuerLocaleChangedCallback<I> onChange)+ - abstract voidregisterCommand(BaseCommand command)Registers a command with ACF+ - <T> voidregisterDependency(Class<? extends T> clazz, String key, @@ -464,7 +469,7 @@ extendsDependencywith the provided instance.+ - <T> voidregisterDependency(Class<? extends T> clazz, T instance)@@ -473,38 +478,38 @@ extendsDependencywith the provided instance.+ - voidsendMessage(CommandIssuer issuer, MessageType type, co.aikar.locales.MessageKeyProvider key, String... replacements)+ - voidsendMessage(IT issuerArg, MessageType type, co.aikar.locales.MessageKeyProvider key, String... replacements)+ - voidsetDefaultExceptionHandler(ExceptionHandler exceptionHandler)Sets the defaultExceptionHandlerthat is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.+ - voidsetDefaultExceptionHandler(ExceptionHandler exceptionHandler, boolean logExceptions)Sets the defaultExceptionHandlerthat is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered, and lets you control if ACF should also log the exception still.+ - voidsetDefaultFormatter(MF defaultFormatter)+ voidsetDefaultHelpPerPage(int defaultHelpPerPage)Deprecated. @@ -512,23 +517,23 @@ extends +- voidsetFormat(MessageType type, FT... colors)+ - voidsetFormat(MessageType type, int i, FT color)+ - MFsetFormat(MessageType type, MF formatter)+ voidsetHelpFormatter(CommandHelpFormatter helpFormatter)Deprecated. @@ -536,16 +541,16 @@ extends +- LocalesetIssuerLocale(IT issuer, Locale locale)+ - booleanusePerIssuerLocale(boolean setting)+ @@ -821,7 +826,7 @@ extends booleanusingPerIssuerLocale()- @@ -886,7 +891,7 @@ public
getCommandContexts
-public abstract CommandContexts<?> getCommandContexts()+public abstract CommandContexts<?> getCommandContexts()Gets the command contexts manager
- Returns:
@@ -835,7 +840,7 @@ extends- @@ -862,7 +867,7 @@ public
getCommandCompletions
-public abstract CommandCompletions<?> getCommandCompletions()+public abstract CommandCompletions<?> getCommandCompletions()Gets the command completions managergenerateCommandHelp
@Deprecated -public CommandHelp generateCommandHelp(CommandIssuer issuer, +public CommandHelp generateCommandHelp(CommandIssuer issuer, @NotNull @NotNull String command)Deprecated. Unstable API@@ -875,7 +880,7 @@ publicgenerateCommandHelp
@Deprecated -public CommandHelp generateCommandHelp()+public CommandHelp generateCommandHelp()Deprecated. Unstable APIgenerateCommandHelp
@Deprecated -public CommandHelp generateCommandHelp(CommandIssuer issuer, +public CommandHelp generateCommandHelp(CommandIssuer issuer, RootCommand rootCommand)Deprecated. Unstable API@@ -898,7 +903,7 @@ publicgetDefaultHelpPerPage
@Deprecated -public int getDefaultHelpPerPage()+public int getDefaultHelpPerPage()Deprecated. Unstable API@@ -909,7 +914,7 @@ public intsetDefaultHelpPerPage
@Deprecated -public void setDefaultHelpPerPage(int defaultHelpPerPage)+public void setDefaultHelpPerPage(int defaultHelpPerPage)Deprecated. Unstable API@@ -920,7 +925,7 @@ public voidsetHelpFormatter
@Deprecated -public void setHelpFormatter(CommandHelpFormatter helpFormatter)+public void setHelpFormatter(CommandHelpFormatter helpFormatter)Deprecated. Unstable API@@ -931,7 +936,7 @@ public voidgetHelpFormatter
@Deprecated -public CommandHelpFormatter getHelpFormatter()+public CommandHelpFormatter getHelpFormatter()Deprecated. Unstable API@@ -941,7 +946,7 @@ publicregisterCommand
-public abstract void registerCommand(BaseCommand command)+public abstract void registerCommand(BaseCommand command)Registers a command with ACF
- Parameters:
@@ -955,7 +960,7 @@ public- @@ -964,7 +969,7 @@ public
hasRegisteredCommands
-public abstract boolean hasRegisteredCommands()+public abstract boolean hasRegisteredCommands()- @@ -973,7 +978,7 @@ public
isCommandIssuer
-public abstract boolean isCommandIssuer(Class<?> type)+public abstract boolean isCommandIssuer(Class<?> type)- @@ -982,7 +987,7 @@ public
getCommandIssuer
-public abstract I getCommandIssuer(Object issuer)+public abstract I getCommandIssuer(Object issuer)- @@ -991,7 +996,7 @@ public
createRootCommand
-public abstract RootCommand createRootCommand(String cmd)+public abstract RootCommand createRootCommand(String cmd)getLocales
-public abstract Locales getLocales()+public abstract Locales getLocales()Returns a Locales Manager to add and modify language tables for your commands.
- Returns:
@@ -1004,7 +1009,7 @@ public- @@ -1013,7 +1018,7 @@ public
usingPerIssuerLocale
-public boolean usingPerIssuerLocale()+public boolean usingPerIssuerLocale()- @@ -1022,7 +1027,7 @@ public
usePerIssuerLocale
-public boolean usePerIssuerLocale(boolean setting)+public boolean usePerIssuerLocale(boolean setting)- @@ -1032,7 +1037,7 @@ public
createConditionContext
-public ConditionContext createConditionContext(CommandIssuer issuer, +public ConditionContext createConditionContext(CommandIssuer issuer, String config)createCommandContext
-public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, +public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, @@ -1046,7 +1051,7 @@ publiccreateCompletionContext
-public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, +public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, @@ -1059,7 +1064,7 @@ public- @@ -1070,7 +1075,7 @@ public
log
-public abstract void log(co.aikar.commands.LogLevel level, +public abstract void log(co.aikar.commands.LogLevel level, String message, Throwable throwable)- @@ -1080,12 +1085,12 @@ public
log
-public void log(co.aikar.commands.LogLevel level, +public void log(co.aikar.commands.LogLevel level, String message)- + + + +
getCommandReplacements
-public CommandReplacements getCommandReplacements()+public CommandReplacements getCommandReplacements()Lets you add custom string replacements that can be applied to annotation values, to reduce duplication/repetition of common values such as permission nodes and command prefixes. - +Any replacement registered starts with a % - +
So for ex @CommandPermission("%staff")
+
- +
+hasPermission
+public boolean hasPermission(CommandIssuer issuer, + Set<String> permissions)+@@ -1109,7 +1124,7 @@ public
hasPermission
-public boolean hasPermission(CommandIssuer issuer, +public boolean hasPermission(CommandIssuer issuer, String permission)- @@ -1119,7 +1134,7 @@ public
getRootCommand
-public RootCommand getRootCommand(@NotNull +public RootCommand getRootCommand(@NotNull @NotNull String cmd)- @@ -1129,7 +1144,7 @@ public
obtainRootCommand
-public RootCommand obtainRootCommand(@NotNull +public RootCommand obtainRootCommand(@NotNull @NotNull String cmd)- @@ -1138,7 +1153,7 @@ public
getRegisteredRootCommands
-public abstract Collection<RootCommand> getRegisteredRootCommands()+public abstract Collection<RootCommand> getRegisteredRootCommands()createRegisteredCommand
-public RegisteredCommand createRegisteredCommand(BaseCommand command, +public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand)@@ -1150,7 +1165,7 @@ publicsetDefaultExceptionHandler
-public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler)+public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler)Sets the defaultExceptionHandlerthat is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
- Parameters:
@@ -1164,7 +1179,7 @@ publicsetDefaultExceptionHandler
-public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler, +public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler, boolean logExceptions)Sets the defaultExceptionHandlerthat is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered, and lets you control if ACF should also log the exception still.@@ -1182,7 +1197,7 @@ public
- @@ -1191,7 +1206,7 @@ public
isLoggingUnhandledExceptions
-public boolean isLoggingUnhandledExceptions()+public boolean isLoggingUnhandledExceptions()getDefaultExceptionHandler
-public ExceptionHandler getDefaultExceptionHandler()+public ExceptionHandler getDefaultExceptionHandler()Gets the current default exception handler, might be null.
- Returns:
@@ -1205,7 +1220,7 @@ publichandleUncaughtException
-protected boolean handleUncaughtException(BaseCommand scope, +protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, @@ -1220,7 +1235,7 @@ publicsendMessage
-public void sendMessage(IT issuerArg, +public void sendMessage(IT issuerArg, MessageType type, co.aikar.locales.MessageKeyProvider key, String... replacements)@@ -1232,7 +1247,7 @@ publicsendMessage
-public void sendMessage(CommandIssuer issuer, +public void sendMessage(CommandIssuer issuer, MessageType type, co.aikar.locales.MessageKeyProvider key, String... replacements)@@ -1244,7 +1259,7 @@ publicformatMessage
-public String formatMessage(CommandIssuer issuer, +public String formatMessage(CommandIssuer issuer, MessageType type, co.aikar.locales.MessageKeyProvider key, String... replacements)@@ -1256,7 +1271,7 @@ public- @@ -1267,7 +1282,7 @@ public
onLocaleChange
-public void onLocaleChange(IssuerLocaleChangedCallback<I> onChange)+public void onLocaleChange(IssuerLocaleChangedCallback<I> onChange)- @@ -1280,7 +1295,7 @@ public
notifyLocaleChange
-public void notifyLocaleChange(I issuer, +public void notifyLocaleChange(I issuer, Locale oldLocale, Locale newLocale)- @@ -1290,7 +1305,7 @@ public
setIssuerLocale
-public Locale setIssuerLocale(IT issuer, +public Locale setIssuerLocale(IT issuer, Locale locale)- @@ -1299,7 +1314,7 @@ public
getIssuerLocale
-public Locale getIssuerLocale(CommandIssuer issuer)+public Locale getIssuerLocale(CommandIssuer issuer)getSupportedLanguages
-public Set<Locale> getSupportedLanguages()+public Set<Locale> getSupportedLanguages()Gets a list of all currently supported languages for this manager. These locales will be automatically loaded from@@ -1313,7 +1328,7 @@ public
addSupportedLanguage
-public void addSupportedLanguage(Locale locale)+public void addSupportedLanguage(Locale locale)Adds a new locale to the list of automatic Locales to load Message Bundles for. All bundles loaded under the previous supported languages will now automatically load for this new locale too.@@ -1330,7 +1345,7 @@ public
registerDependency
-public <T> void registerDependency(Class<? extends T> clazz, +public <T> void registerDependency(Class<? extends T> clazz, T instance)Registers an instance of a class to be registered as an injectable dependency.
The command manager will attempt to inject all fields in a command class that are annotated with @@ -1352,7 +1367,7 @@ publicregisterDependency
-public <T> void registerDependency(Class<? extends T> clazz, +public <T> void registerDependency(Class<? extends T> clazz, String key, T instance)Registers an instance of a class to be registered as an injectable dependency.
@@ -1375,7 +1390,7 @@ publicenableUnstableAPI
@Deprecated -public void enableUnstableAPI(String api)+public void enableUnstableAPI(String api)Deprecated. Use this with caution! If you enable and use Unstable API's, your next compile using ACF may require you to update your implementation to those unstable API's@@ -1386,7 +1401,7 @@ public void- diff --git a/docs/acf-core/co/aikar/commands/CommandOperationContext.html b/docs/acf-core/co/aikar/commands/CommandOperationContext.html index 76de0b12..2685c8ce 100644 --- a/docs/acf-core/co/aikar/commands/CommandOperationContext.html +++ b/docs/acf-core/co/aikar/commands/CommandOperationContext.html @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
getCommandPrefix
-public String getCommandPrefix(CommandIssuer issuer)+public String getCommandPrefix(CommandIssuer issuer)- @@ -210,7 +210,7 @@ extends
-public class CommandOperationContext<I extends CommandIssuer> +public class CommandOperationContext<I extends CommandIssuer> extends ObjectHolds information about the currently executing command on this thread- @@ -219,7 +219,7 @@ extends
getCommandManager
-public CommandManager getCommandManager()+public CommandManager getCommandManager()- @@ -228,7 +228,7 @@ extends
getCommandIssuer
-public I getCommandIssuer()+public I getCommandIssuer()- @@ -237,7 +237,7 @@ extends
getCommand
-public BaseCommand getCommand()+public BaseCommand getCommand()- @@ -246,7 +246,7 @@ extends
getCommandLabel
-public String getCommandLabel()+public String getCommandLabel()- @@ -255,7 +255,7 @@ extends
getArgs
-public String[] getArgs()+public String[] getArgs()- @@ -264,7 +264,7 @@ extends
isAsync
-public boolean isAsync()+public boolean isAsync()- @@ -273,7 +273,7 @@ extends
setRegisteredCommand
-public void setRegisteredCommand(RegisteredCommand registeredCommand)+public void setRegisteredCommand(RegisteredCommand registeredCommand)- @@ -283,7 +283,7 @@ extends
getRegisteredCommand
-public RegisteredCommand getRegisteredCommand()+public RegisteredCommand getRegisteredCommand()getAnnotation
@Deprecated -public <T extends Annotation> T getAnnotation(Class<T> anno)+public <T extends Annotation> T getAnnotation(Class<T> anno)Deprecated. UsegetAnnotationValue(Class)This method will not support annotation processors!! use getAnnotationValue or hasAnnotation@@ -294,7 +294,7 @@ public <T extends- @@ -303,7 +303,7 @@ public <T extends
getAnnotationValue
-public <T extends Annotation> String getAnnotationValue(Class<T> cls)+public <T extends Annotation> String getAnnotationValue(Class<T> cls)- @@ -313,7 +313,7 @@ public <T extends
getAnnotationValue
-public <T extends Annotation> String getAnnotationValue(Class<T> cls, +public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options)- diff --git a/docs/acf-core/co/aikar/commands/CommandParameter.html b/docs/acf-core/co/aikar/commands/CommandParameter.html index b46fb9b6..20048f0b 100644 --- a/docs/acf-core/co/aikar/commands/CommandParameter.html +++ b/docs/acf-core/co/aikar/commands/CommandParameter.html @@ -18,7 +18,7 @@ catch(err) { } //--> -var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":10,"i25":10,"i26":10,"i27":10,"i28":10}; +var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":10,"i25":10,"i26":10,"i27":10,"i28":10,"i29":10}; var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
hasAnnotation
-public boolean hasAnnotation(Class<? extends Annotation> anno)+public boolean hasAnnotation(Class<? extends Annotation> anno)- @@ -185,82 +185,86 @@ extends
-public class CommandParameter<CEC extends CommandExecutionContext<CEC,? extends CommandIssuer>> +public class CommandParameter<CEC extends CommandExecutionContext<CEC,? extends CommandIssuer>> extends ObjectgetParamIndex()+ ++ Set<String>+ getRequiredPermissions()- ContextResolver<?,CEC>getResolver()+ - StringgetSyntax()+ - Class<?>getType()+ - String[]getValues()+ - booleanisCommandIssuer()+ - booleanisOptional()+ - booleanisOptionalResolver()+ - booleanrequiresInput()+ - voidsetCanConsumeInput(boolean canConsumeInput)+ - voidsetCommandIssuer(boolean commandIssuer)+ - voidsetConditions(String conditions)+ - voidsetDefaultValue(String defaultValue)+ - voidsetDescription(String description)+ - voidsetFlags(Map<String,String> flags)+ - voidsetOptional(boolean optional)+ - voidsetOptionalResolver(boolean optionalResolver)+ - voidsetRequiresInput(boolean requiresInput)+ - voidsetResolver(ContextResolver<?,CEC> resolver)+ - voidsetSyntax(String syntax)+ @@ -292,7 +296,7 @@ extends voidsetValues(String[] values)- diff --git a/docs/acf-core/co/aikar/commands/MessageKeys.html b/docs/acf-core/co/aikar/commands/MessageKeys.html index fd7a7080..e02eb27e 100644 --- a/docs/acf-core/co/aikar/commands/MessageKeys.html +++ b/docs/acf-core/co/aikar/commands/MessageKeys.html @@ -199,15 +199,18 @@ implements co.aikar.locales.MessageKeyProvider
CommandParameter
-public CommandParameter(RegisteredCommand<CEC> command, +public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex, boolean isLast)@@ -312,7 +316,7 @@ extends- @@ -321,7 +325,7 @@ extends
getParameter
-public Parameter getParameter()+public Parameter getParameter()- @@ -330,7 +334,7 @@ extends
getType
-public Class<?> getType()+public Class<?> getType()- @@ -339,7 +343,7 @@ extends
getName
-public String getName()+public String getName()- @@ -348,7 +352,7 @@ extends
getManager
-public CommandManager getManager()+public CommandManager getManager()- @@ -357,7 +361,7 @@ extends
getParamIndex
-public int getParamIndex()+public int getParamIndex()- @@ -366,7 +370,7 @@ extends
getResolver
-public ContextResolver<?,CEC> getResolver()+public ContextResolver<?,CEC> getResolver()- @@ -375,7 +379,7 @@ extends
setResolver
-public void setResolver(ContextResolver<?,CEC> resolver)+public void setResolver(ContextResolver<?,CEC> resolver)- @@ -384,7 +388,7 @@ extends
isOptional
-public boolean isOptional()+public boolean isOptional()- @@ -393,7 +397,7 @@ extends
setOptional
-public void setOptional(boolean optional)+public void setOptional(boolean optional)- @@ -402,7 +406,7 @@ extends
getDescription
-public String getDescription()+public String getDescription()- @@ -411,7 +415,7 @@ extends
setDescription
-public void setDescription(String description)+public void setDescription(String description)- @@ -420,7 +424,7 @@ extends
getDefaultValue
-public String getDefaultValue()+public String getDefaultValue()- @@ -429,7 +433,7 @@ extends
setDefaultValue
-public void setDefaultValue(String defaultValue)+public void setDefaultValue(String defaultValue)- @@ -438,7 +442,7 @@ extends
isCommandIssuer
-public boolean isCommandIssuer()+public boolean isCommandIssuer()- @@ -447,7 +451,7 @@ extends
setCommandIssuer
-public void setCommandIssuer(boolean commandIssuer)+public void setCommandIssuer(boolean commandIssuer)- @@ -456,7 +460,7 @@ extends
getValues
-public String[] getValues()+public String[] getValues()- @@ -465,7 +469,7 @@ extends
setValues
-public void setValues(String[] values)+public void setValues(String[] values)- @@ -474,7 +478,7 @@ extends
getFlags
-public Map<String,String> getFlags()+public Map<String,String> getFlags()- @@ -483,7 +487,7 @@ extends
setFlags
-public void setFlags(Map<String,String> flags)+public void setFlags(Map<String,String> flags)- @@ -492,7 +496,7 @@ extends
canConsumeInput
-public boolean canConsumeInput()+public boolean canConsumeInput()- @@ -501,7 +505,7 @@ extends
setCanConsumeInput
-public void setCanConsumeInput(boolean canConsumeInput)+public void setCanConsumeInput(boolean canConsumeInput)- @@ -510,7 +514,7 @@ extends
setOptionalResolver
-public void setOptionalResolver(boolean optionalResolver)+public void setOptionalResolver(boolean optionalResolver)- @@ -519,7 +523,7 @@ extends
isOptionalResolver
-public boolean isOptionalResolver()+public boolean isOptionalResolver()- @@ -528,7 +532,7 @@ extends
requiresInput
-public boolean requiresInput()+public boolean requiresInput()- @@ -537,7 +541,7 @@ extends
setRequiresInput
-public void setRequiresInput(boolean requiresInput)+public void setRequiresInput(boolean requiresInput)- @@ -546,7 +550,7 @@ extends
getSyntax
-public String getSyntax()+public String getSyntax()- @@ -555,16 +559,25 @@ extends
setSyntax
-public void setSyntax(String syntax)+public void setSyntax(String syntax)- -
getConditions
-public String getConditions()+public String getConditions()+
+ + + +
- +
setConditions
-public void setConditions(String conditions)+public void setConditions(String conditions)++
- +
getRequiredPermissions
+public Set<String> getRequiredPermissions()PERMISSION_DENIED+ ++ PERMISSION_DENIED_PARAMETER- PLEASE_SPECIFY_AT_LEAST+ - PLEASE_SPECIFY_AT_MOST+ - PLEASE_SPECIFY_ONE_OF+ @@ -280,13 +283,22 @@ the order they are declared. UNKNOWN_COMMANDpublic static final MessageKeys PERMISSION_DENIED+
- +
+PERMISSION_DENIED_PARAMETER
+public static final MessageKeys PERMISSION_DENIED_PARAMETER+@@ -295,7 +307,7 @@ the order they are declared.
ERROR_GENERIC_LOGGED
-public static final MessageKeys ERROR_GENERIC_LOGGED+public static final MessageKeys ERROR_GENERIC_LOGGED@@ -304,7 +316,7 @@ the order they are declared.
UNKNOWN_COMMAND
-public static final MessageKeys UNKNOWN_COMMAND+public static final MessageKeys UNKNOWN_COMMAND@@ -313,7 +325,7 @@ the order they are declared.
INVALID_SYNTAX
-public static final MessageKeys INVALID_SYNTAX+public static final MessageKeys INVALID_SYNTAX@@ -322,7 +334,7 @@ the order they are declared.
ERROR_PREFIX
-public static final MessageKeys ERROR_PREFIX+public static final MessageKeys ERROR_PREFIX@@ -331,7 +343,7 @@ the order they are declared.
ERROR_PERFORMING_COMMAND
-public static final MessageKeys ERROR_PERFORMING_COMMAND+public static final MessageKeys ERROR_PERFORMING_COMMAND@@ -340,7 +352,7 @@ the order they are declared.
INFO_MESSAGE
-public static final MessageKeys INFO_MESSAGE+public static final MessageKeys INFO_MESSAGE@@ -349,7 +361,7 @@ the order they are declared.
PLEASE_SPECIFY_ONE_OF
-public static final MessageKeys PLEASE_SPECIFY_ONE_OF+public static final MessageKeys PLEASE_SPECIFY_ONE_OF@@ -358,7 +370,7 @@ the order they are declared.
MUST_BE_A_NUMBER
-public static final MessageKeys MUST_BE_A_NUMBER+public static final MessageKeys MUST_BE_A_NUMBER@@ -367,7 +379,7 @@ the order they are declared.
MUST_BE_MIN_LENGTH
-public static final MessageKeys MUST_BE_MIN_LENGTH+public static final MessageKeys MUST_BE_MIN_LENGTH@@ -376,7 +388,7 @@ the order they are declared.
MUST_BE_MAX_LENGTH
-public static final MessageKeys MUST_BE_MAX_LENGTH+public static final MessageKeys MUST_BE_MAX_LENGTH@@ -385,7 +397,7 @@ the order they are declared.
PLEASE_SPECIFY_AT_LEAST
-public static final MessageKeys PLEASE_SPECIFY_AT_LEAST+public static final MessageKeys PLEASE_SPECIFY_AT_LEAST@@ -394,7 +406,7 @@ the order they are declared.
PLEASE_SPECIFY_AT_MOST
-public static final MessageKeys PLEASE_SPECIFY_AT_MOST+public static final MessageKeys PLEASE_SPECIFY_AT_MOST@@ -403,7 +415,7 @@ the order they are declared.
NOT_ALLOWED_ON_CONSOLE
-public static final MessageKeys NOT_ALLOWED_ON_CONSOLE+public static final MessageKeys NOT_ALLOWED_ON_CONSOLE@@ -412,7 +424,7 @@ the order they are declared.
COULD_NOT_FIND_PLAYER
-public static final MessageKeys COULD_NOT_FIND_PLAYER+public static final MessageKeys COULD_NOT_FIND_PLAYER@@ -421,7 +433,7 @@ the order they are declared.
NO_COMMAND_MATCHED_SEARCH
-public static final MessageKeys NO_COMMAND_MATCHED_SEARCH+public static final MessageKeys NO_COMMAND_MATCHED_SEARCH@@ -430,7 +442,7 @@ the order they are declared.
HELP_PAGE_INFORMATION
-public static final MessageKeys HELP_PAGE_INFORMATION+public static final MessageKeys HELP_PAGE_INFORMATION@@ -439,7 +451,7 @@ the order they are declared.
HELP_NO_RESULTS
-public static final MessageKeys HELP_NO_RESULTS+public static final MessageKeys HELP_NO_RESULTS@@ -448,7 +460,7 @@ the order they are declared.
HELP_HEADER
-public static final MessageKeys HELP_HEADER+public static final MessageKeys HELP_HEADER@@ -457,7 +469,7 @@ the order they are declared.
HELP_FORMAT
-public static final MessageKeys HELP_FORMAT+public static final MessageKeys HELP_FORMAT@@ -466,7 +478,7 @@ the order they are declared.
HELP_DETAILED_HEADER
-public static final MessageKeys HELP_DETAILED_HEADER+public static final MessageKeys HELP_DETAILED_HEADER@@ -475,7 +487,7 @@ the order they are declared.
HELP_DETAILED_COMMAND_FORMAT
-public static final MessageKeys HELP_DETAILED_COMMAND_FORMAT+public static final MessageKeys HELP_DETAILED_COMMAND_FORMAT@@ -484,7 +496,7 @@ the order they are declared.
HELP_DETAILED_PARAMETER_FORMAT
-public static final MessageKeys HELP_DETAILED_PARAMETER_FORMAT+public static final MessageKeys HELP_DETAILED_PARAMETER_FORMAT
HELP_SEARCH_HEADER
-public static final MessageKeys HELP_SEARCH_HEADER+public static final MessageKeys HELP_SEARCH_HEADER
getMessageKey
-public co.aikar.locales.MessageKey getMessageKey()+public co.aikar.locales.MessageKey getMessageKey()
- Specified by:
- diff --git a/docs/acf-core/co/aikar/commands/RegisteredCommand.html b/docs/acf-core/co/aikar/commands/RegisteredCommand.html index 14c503ed..dd952ed5 100644 --- a/docs/acf-core/co/aikar/commands/RegisteredCommand.html +++ b/docs/acf-core/co/aikar/commands/RegisteredCommand.html @@ -265,7 +265,7 @@ extends
getMessageKeyin interfaceco.aikar.locales.MessageKeyProvidergetPermission
@Deprecated -public String getPermission()+public String getPermission()Deprecated.
- See Also:
@@ -279,7 +279,7 @@ public- @@ -288,7 +288,7 @@ public
getRequiredPermissions
-public Set<String> getRequiredPermissions()+public Set<String> getRequiredPermissions()- @@ -297,7 +297,7 @@ public
requiresPermission
-public boolean requiresPermission(String permission)+public boolean requiresPermission(String permission)- @@ -306,7 +306,7 @@ public
getPrefSubCommand
-public String getPrefSubCommand()+public String getPrefSubCommand()- @@ -315,7 +315,7 @@ public
getSyntaxText
-public String getSyntaxText()+public String getSyntaxText()- @@ -324,7 +324,7 @@ public
getHelpText
-public String getHelpText()+public String getHelpText()- @@ -333,7 +333,7 @@ public
isPrivate
-public boolean isPrivate()+public boolean isPrivate()- @@ -342,7 +342,7 @@ public
getCommand
-public String getCommand()+public String getCommand()- @@ -351,7 +351,7 @@ public
addSubcommand
-public void addSubcommand(String cmd)+public void addSubcommand(String cmd)- @@ -360,7 +360,7 @@ public
addSubcommands
-public void addSubcommands(Collection<String> cmd)+public void addSubcommands(Collection<String> cmd)getAnnotation
-public <T extends Annotation> T getAnnotation(Class<T> annotation)+public <T extends Annotation> T getAnnotation(Class<T> annotation)
@Retention(value=RUNTIME) - @Target(value={METHOD,TYPE}) + @Target(value={METHOD,TYPE,PARAMETER}) public @interface CommandPermissionSets the permission required to perform this command. - +Permission format will vary based on implementation platform
booleanhasPermission(CommandIssuer issuer,
+ Set<String> permissions) booleanhasPermission(CommandIssuer issuer,
String permission) voidhelp(CommandIssuer issuer,
String[] args) voidprintDetailedHelpCommand(CommandHelp help,
CommandIssuer issuer,
HelpEntry entry) voidprintDetailedHelpFooter(CommandHelp help,
CommandIssuer issuer,
HelpEntry entry) voidprintDetailedHelpHeader(CommandHelp help,
CommandIssuer issuer,
HelpEntry entry) voidprintDetailedParameter(CommandHelp help,
CommandIssuer issuer,
HelpEntry entry,
CommandParameter param) voidprintHelpCommand(CommandHelp help,
CommandIssuer issuer,
HelpEntry entry) voidprintHelpFooter(CommandHelp help,
CommandIssuer issuer) voidprintHelpHeader(CommandHelp help,
CommandIssuer issuer) voidprintSearchEntry(CommandHelp help,
CommandIssuer issuer,
HelpEntry page) voidprintSearchFooter(CommandHelp help,
CommandIssuer issuer) voidprintSearchHeader(CommandHelp help,
CommandIssuer issuer) voidsendMessage(CommandIssuer issuer,
MessageType type,
co.aikar.locales.MessageKeyProvider key,
String... replacements) voidshowHelp(CommandIssuer issuer) voidshowSyntax(CommandIssuer issuer,
RegisteredCommand<?> cmd) List<String>tabComplete(CommandIssuer issuer,
RootCommand rootCommand,
String[] args,
boolean isAsync) List<String>tabComplete(CommandIssuer issuer,
String commandLabel,
@@ -444,7 +449,7 @@
Gets tab completed data from the given command from the user.
List<String>tabComplete(CommandIssuer issuer,
String commandLabel,
diff --git a/docs/acf-core/index-all.html b/docs/acf-core/index-all.html
index 7d6b3672..aa646f97 100644
--- a/docs/acf-core/index-all.html
+++ b/docs/acf-core/index-all.html
@@ -799,6 +799,8 @@
-
Override this to control replacements
+- getParameterPermissions() - Method in class co.aikar.commands.CommandExecutionContext
+-
- getParameters() - Method in class co.aikar.commands.HelpEntry
-
- getParameterSyntax() - Method in class co.aikar.commands.HelpEntry
@@ -825,6 +827,8 @@
-
- getRequiredPermissions() - Method in class co.aikar.commands.BaseCommand
-
+- getRequiredPermissions() - Method in class co.aikar.commands.CommandParameter
+-
- getRequiredPermissions() - Method in class co.aikar.commands.ForwardingCommand
-
- getRequiredPermissions() - Method in class co.aikar.commands.RegisteredCommand
@@ -952,6 +956,8 @@
-
Has permission node
+- hasPermission(CommandIssuer, Set<String>) - Method in class co.aikar.commands.CommandManager
+-
- hasPermission(CommandIssuer, String) - Method in class co.aikar.commands.CommandManager
-
- hasPermission(Object) - Method in class co.aikar.commands.ForwardingCommand
@@ -1713,6 +1719,10 @@
-
- setContextFlags(Class<?>, String) - Method in class co.aikar.commands.BaseCommand
-
+- setDefaultCompletion(String, Class...) - Method in class co.aikar.commands.CommandCompletions
+-
+Registers a completion handler such as @players to default apply to all command parameters of the specified types
+
- setDefaultExceptionHandler(ExceptionHandler) - Method in class co.aikar.commands.CommandManager
-
Sets the default
ExceptionHandler that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
diff --git a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
index 2471cbd8..b3fe8a64 100644
--- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html
@@ -688,7 +688,7 @@
680 * @return All results to complete the command.
681 */
682 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
-683 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
+683 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0) {
684 return Collections.emptyList();
685 }
686
@@ -796,7 +796,7 @@
788 }
789
790 public boolean hasPermission(CommandIssuer issuer) {
-791 return getRequiredPermissions().isEmpty() || getRequiredPermissions().stream().allMatch(permission -> manager.hasPermission(issuer, permission));
+791 return this.manager.hasPermission(issuer, getRequiredPermissions());
792 }
793
794 public Set<String> getRequiredPermissions() {
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html
index a2053fdb..616f583a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html
@@ -47,207 +47,253 @@
039
040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
041public class CommandCompletions<C extends CommandCompletionContext> {
-042 private final CommandManager manager;
-043 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
-044 private Map<Class, String> defaultCompletions = new HashMap<>();
-045
-046 public CommandCompletions(CommandManager manager) {
-047 this.manager = manager;
-048 registerAsyncCompletion("nothing", c -> Collections.emptyList());
-049 registerAsyncCompletion("range", (c) -> {
-050 String config = c.getConfig();
-051 if (config == null) {
-052 return Collections.emptyList();
-053 }
-054 final String[] ranges = ACFPatterns.DASH.split(config);
-055 int start;
-056 int end;
-057 if (ranges.length != 2) {
-058 start = 0;
-059 end = ACFUtil.parseInt(ranges[0], 0);
-060 } else {
-061 start = ACFUtil.parseInt(ranges[0], 0);
-062 end = ACFUtil.parseInt(ranges[1], 0);
-063 }
-064 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
-065 });
-066 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
-067 registerAsyncCompletion("timeunits", (c) -> timeunits);
-068 }
-069
-070 /**
-071 * Registr a completion handler to provide command completions based on the user input.
-072 *
-073 * @param id
-074 * @param handler
-075 * @return
-076 */
-077 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
-078 return this.completionMap.put("@" + id.toLowerCase(), handler);
-079 }
-080
-081 /**
-082 * Registr a completion handler to provide command completions based on the user input.
-083 * This handler is declared to be safe to be executed asynchronously.
-084 * <p>
-085 * Not all platforms support this, so if the platform does not support asynchronous execution,
-086 * your handler will be executed on the main thread.
-087 * <p>
-088 * Use this anytime your handler does not need to access state that is not considered thread safe.
+042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__";
+043 private final CommandManager manager;
+044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support
+045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
+046 private Map<Class, String> defaultCompletions = new HashMap<>();
+047
+048 public CommandCompletions(CommandManager manager) {
+049 this.manager = manager;
+050 registerAsyncCompletion("nothing", c -> Collections.emptyList());
+051 registerAsyncCompletion("range", (c) -> {
+052 String config = c.getConfig();
+053 if (config == null) {
+054 return Collections.emptyList();
+055 }
+056 final String[] ranges = ACFPatterns.DASH.split(config);
+057 int start;
+058 int end;
+059 if (ranges.length != 2) {
+060 start = 0;
+061 end = ACFUtil.parseInt(ranges[0], 0);
+062 } else {
+063 start = ACFUtil.parseInt(ranges[0], 0);
+064 end = ACFUtil.parseInt(ranges[1], 0);
+065 }
+066 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
+067 });
+068 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
+069 registerAsyncCompletion("timeunits", (c) -> timeunits);
+070 }
+071
+072 /**
+073 * Registr a completion handler to provide command completions based on the user input.
+074 *
+075 * @param id
+076 * @param handler
+077 * @return
+078 */
+079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
+080 return this.completionMap.put(prepareCompletionId(id), handler);
+081 }
+082
+083 /**
+084 * Registr a completion handler to provide command completions based on the user input.
+085 * This handler is declared to be safe to be executed asynchronously.
+086 * <p>
+087 * Not all platforms support this, so if the platform does not support asynchronous execution,
+088 * your handler will be executed on the main thread.
089 * <p>
-090 * Use context.isAsync() to determine if you are async or not.
-091 *
-092 * @param id
-093 * @param handler
-094 * @return
-095 */
-096 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
-097 return this.completionMap.put("@" + id.toLowerCase(), handler);
-098 }
-099
-100 /**
-101 * Register a static list of command completions that will never change.
-102 * Like @CommandCompletion, values are | (PIPE) separated.
-103 * <p>
-104 * Example: foo|bar|baz
-105 *
-106 * @param id
-107 * @param list
-108 * @return
-109 */
-110 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
-111 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
-112 }
-113
-114 /**
-115 * Register a static list of command completions that will never change
-116 *
-117 * @param id
-118 * @param completions
-119 * @return
-120 */
-121 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
-122 return registerStaticCompletion(id, Arrays.asList(completions));
-123 }
-124
-125 /**
-126 * Register a static list of command completions that will never change. The list is obtained from the supplier
-127 * immediately as part of this method call.
-128 *
-129 * @param id
-130 * @param supplier
-131 * @return
-132 */
-133 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
-134 return registerStaticCompletion(id, supplier.get());
-135 }
-136
-137 /**
-138 * Register a static list of command completions that will never change
-139 *
-140 * @param id
-141 * @param completions
-142 * @return
-143 */
-144 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
-145 return registerAsyncCompletion(id, x -> completions);
-146 }
-147
-148 /**
-149 * @param id
-150 * @param classes
-151 * @return
-152 * @deprecated Feature Not done yet
-153 */
-154 CommandCompletionHandler setDefaultCompletion(String id, Class... classes) {
-155 // get completion with specified id
-156 id = id.toLowerCase();
-157 CommandCompletionHandler completion = completionMap.get(id);
-158
-159 if (completion == null) {
-160 // Throw something because no completion with specified id
-161 throw new IllegalStateException("Completion not registered for " + id);
-162 }
-163
-164 for (Class clazz : classes) {
-165 defaultCompletions.put(clazz, id);
+090 * Use this anytime your handler does not need to access state that is not considered thread safe.
+091 * <p>
+092 * Use context.isAsync() to determine if you are async or not.
+093 *
+094 * @param id
+095 * @param handler
+096 * @return
+097 */
+098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
+099 return this.completionMap.put(prepareCompletionId(id), handler);
+100 }
+101
+102 /**
+103 * Register a static list of command completions that will never change.
+104 * Like @CommandCompletion, values are | (PIPE) separated.
+105 * <p>
+106 * Example: foo|bar|baz
+107 *
+108 * @param id
+109 * @param list
+110 * @return
+111 */
+112 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
+113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
+114 }
+115
+116 /**
+117 * Register a static list of command completions that will never change
+118 *
+119 * @param id
+120 * @param completions
+121 * @return
+122 */
+123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
+124 return registerStaticCompletion(id, Arrays.asList(completions));
+125 }
+126
+127 /**
+128 * Register a static list of command completions that will never change. The list is obtained from the supplier
+129 * immediately as part of this method call.
+130 *
+131 * @param id
+132 * @param supplier
+133 * @return
+134 */
+135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
+136 return registerStaticCompletion(id, supplier.get());
+137 }
+138
+139 /**
+140 * Register a static list of command completions that will never change
+141 *
+142 * @param id
+143 * @param completions
+144 * @return
+145 */
+146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
+147 return registerAsyncCompletion(id, x -> completions);
+148 }
+149
+150 /**
+151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types
+152 * <p>
+153 * This enables automatic completion support for parameters without manually defining it for custom objects
+154 *
+155 * @param id
+156 * @param classes
+157 */
+158 public void setDefaultCompletion(String id, Class... classes) {
+159 // get completion with specified id
+160 id = prepareCompletionId(id);
+161 CommandCompletionHandler completion = completionMap.get(id);
+162
+163 if (completion == null) {
+164 // Throw something because no completion with specified id
+165 throw new IllegalStateException("Completion not registered for " + id);
166 }
167
-168 return completion;
-169 }
-170
-171 @NotNull
-172 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
-173 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
-174 final int argIndex = args.length - 1;
-175
-176 String input = args[argIndex];
+168 for (Class clazz : classes) {
+169 defaultCompletions.put(clazz, id);
+170 }
+171 }
+172
+173 @NotNull
+174 private static String prepareCompletionId(String id) {
+175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase();
+176 }
177
-178 String completion = argIndex < completions.length ? completions[argIndex] : null;
-179 if (completion == null && completions.length > 0) {
-180 completion = completions[completions.length - 1];
-181 }
-182 if (completion == null) {
-183 return Collections.singletonList(input);
-184 }
-185
-186 return getCompletionValues(cmd, sender, completion, args, isAsync);
-187 }
-188
-189 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
-190 completion = manager.getCommandReplacements().replace(completion);
-191
-192 List<String> allCompletions = new ArrayList<>();
-193 String input = args.length > 0 ? args[args.length - 1] : "";
-194
-195 for (String value : ACFPatterns.PIPE.split(completion)) {
-196 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
-197 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
-198 if (handler != null) {
-199 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
-200 ACFUtil.sneaky(new SyncCompletionRequired());
-201 return null;
-202 }
-203 String config = complete.length == 1 ? null : complete[1];
-204 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
-205
-206 try {
-207 //noinspection unchecked
-208 Collection<String> completions = handler.getCompletions(context);
-209 if (completions != null) {
-210 allCompletions.addAll(completions);
-211 continue;
-212 }
-213 //noinspection ConstantIfStatement,ConstantConditions
-214 if (false) { // Hack to fool compiler. since its sneakily thrown.
-215 throw new CommandCompletionTextLookupException();
-216 }
-217 } catch (CommandCompletionTextLookupException ignored) {
-218 // This should only happen if some other feedback error occured.
-219 } catch (Exception e) {
-220 command.handleException(sender, Arrays.asList(args), e);
+178 @NotNull
+179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
+180 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
+181 final int argIndex = args.length - 1;
+182
+183 String input = args[argIndex];
+184
+185 String completion = argIndex < completions.length ? completions[argIndex] : null;
+186 if (completion == null || "*".equals(completion)) {
+187 completion = findDefaultCompletion(cmd, args);
+188 }
+189
+190 if (completion == null && completions.length > 0) {
+191 String last = completions[completions.length - 1];
+192 if (last.startsWith("repeat@")) {
+193 completion = last;
+194 }
+195 }
+196
+197 if (completion == null) {
+198 return Collections.singletonList(input);
+199 }
+200
+201 return getCompletionValues(cmd, sender, completion, args, isAsync);
+202 }
+203
+204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) {
+205 int i = 0;
+206 for (CommandParameter param : cmd.parameters) {
+207 if (param.canConsumeInput() && ++i == args.length) {
+208 Class type = param.getType();
+209 while (type != null) {
+210 String completion = this.defaultCompletions.get(type);
+211 if (completion != null) {
+212 return completion;
+213 }
+214 type = type.getSuperclass();
+215 }
+216 if (param.getType().isEnum()) {
+217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext();
+218 //noinspection unchecked
+219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType());
+220 return DEFAULT_ENUM_ID;
221 }
-222 // Something went wrong in lookup, fall back to input
-223 return Collections.singletonList(input);
-224 } else {
-225 // Plaintext value
-226 allCompletions.add(value);
-227 }
-228 }
-229 return allCompletions;
-230 }
-231
-232 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
-233 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
-234 }
-235
-236 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
-237 }
-238
-239 public static class SyncCompletionRequired extends RuntimeException {
-240 }
-241
-242}
+222 break;
+223 }
+224 }
+225 return null;
+226 }
+227
+228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
+229 if (DEFAULT_ENUM_ID.equals(completion)) {
+230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext();
+231 return ctx.enumCompletionValues;
+232 }
+233 if (completion.startsWith("repeat@")) {
+234 completion = completion.substring(6);
+235 }
+236 completion = manager.getCommandReplacements().replace(completion);
+237
+238 List<String> allCompletions = new ArrayList<>();
+239 String input = args.length > 0 ? args[args.length - 1] : "";
+240
+241 for (String value : ACFPatterns.PIPE.split(completion)) {
+242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
+243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
+244 if (handler != null) {
+245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
+246 ACFUtil.sneaky(new SyncCompletionRequired());
+247 return null;
+248 }
+249 String config = complete.length == 1 ? null : complete[1];
+250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
+251
+252 try {
+253 //noinspection unchecked
+254 Collection<String> completions = handler.getCompletions(context);
+255 if (completions != null) {
+256 allCompletions.addAll(completions);
+257 continue;
+258 }
+259 //noinspection ConstantIfStatement,ConstantConditions
+260 if (false) { // Hack to fool compiler. since its sneakily thrown.
+261 throw new CommandCompletionTextLookupException();
+262 }
+263 } catch (CommandCompletionTextLookupException ignored) {
+264 // This should only happen if some other feedback error occured.
+265 } catch (Exception e) {
+266 command.handleException(sender, Arrays.asList(args), e);
+267 }
+268 // Something went wrong in lookup, fall back to input
+269 return Collections.singletonList(input);
+270 } else {
+271 // Plaintext value
+272 allCompletions.add(value);
+273 }
+274 }
+275 return allCompletions;
+276 }
+277
+278 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
+279 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
+280 }
+281
+282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
+283 }
+284
+285 public static class SyncCompletionRequired extends RuntimeException {
+286 }
+287
+288}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html
index a2053fdb..616f583a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html
@@ -47,207 +47,253 @@
039
040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
041public class CommandCompletions<C extends CommandCompletionContext> {
-042 private final CommandManager manager;
-043 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
-044 private Map<Class, String> defaultCompletions = new HashMap<>();
-045
-046 public CommandCompletions(CommandManager manager) {
-047 this.manager = manager;
-048 registerAsyncCompletion("nothing", c -> Collections.emptyList());
-049 registerAsyncCompletion("range", (c) -> {
-050 String config = c.getConfig();
-051 if (config == null) {
-052 return Collections.emptyList();
-053 }
-054 final String[] ranges = ACFPatterns.DASH.split(config);
-055 int start;
-056 int end;
-057 if (ranges.length != 2) {
-058 start = 0;
-059 end = ACFUtil.parseInt(ranges[0], 0);
-060 } else {
-061 start = ACFUtil.parseInt(ranges[0], 0);
-062 end = ACFUtil.parseInt(ranges[1], 0);
-063 }
-064 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
-065 });
-066 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
-067 registerAsyncCompletion("timeunits", (c) -> timeunits);
-068 }
-069
-070 /**
-071 * Registr a completion handler to provide command completions based on the user input.
-072 *
-073 * @param id
-074 * @param handler
-075 * @return
-076 */
-077 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
-078 return this.completionMap.put("@" + id.toLowerCase(), handler);
-079 }
-080
-081 /**
-082 * Registr a completion handler to provide command completions based on the user input.
-083 * This handler is declared to be safe to be executed asynchronously.
-084 * <p>
-085 * Not all platforms support this, so if the platform does not support asynchronous execution,
-086 * your handler will be executed on the main thread.
-087 * <p>
-088 * Use this anytime your handler does not need to access state that is not considered thread safe.
+042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__";
+043 private final CommandManager manager;
+044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support
+045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
+046 private Map<Class, String> defaultCompletions = new HashMap<>();
+047
+048 public CommandCompletions(CommandManager manager) {
+049 this.manager = manager;
+050 registerAsyncCompletion("nothing", c -> Collections.emptyList());
+051 registerAsyncCompletion("range", (c) -> {
+052 String config = c.getConfig();
+053 if (config == null) {
+054 return Collections.emptyList();
+055 }
+056 final String[] ranges = ACFPatterns.DASH.split(config);
+057 int start;
+058 int end;
+059 if (ranges.length != 2) {
+060 start = 0;
+061 end = ACFUtil.parseInt(ranges[0], 0);
+062 } else {
+063 start = ACFUtil.parseInt(ranges[0], 0);
+064 end = ACFUtil.parseInt(ranges[1], 0);
+065 }
+066 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
+067 });
+068 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
+069 registerAsyncCompletion("timeunits", (c) -> timeunits);
+070 }
+071
+072 /**
+073 * Registr a completion handler to provide command completions based on the user input.
+074 *
+075 * @param id
+076 * @param handler
+077 * @return
+078 */
+079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
+080 return this.completionMap.put(prepareCompletionId(id), handler);
+081 }
+082
+083 /**
+084 * Registr a completion handler to provide command completions based on the user input.
+085 * This handler is declared to be safe to be executed asynchronously.
+086 * <p>
+087 * Not all platforms support this, so if the platform does not support asynchronous execution,
+088 * your handler will be executed on the main thread.
089 * <p>
-090 * Use context.isAsync() to determine if you are async or not.
-091 *
-092 * @param id
-093 * @param handler
-094 * @return
-095 */
-096 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
-097 return this.completionMap.put("@" + id.toLowerCase(), handler);
-098 }
-099
-100 /**
-101 * Register a static list of command completions that will never change.
-102 * Like @CommandCompletion, values are | (PIPE) separated.
-103 * <p>
-104 * Example: foo|bar|baz
-105 *
-106 * @param id
-107 * @param list
-108 * @return
-109 */
-110 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
-111 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
-112 }
-113
-114 /**
-115 * Register a static list of command completions that will never change
-116 *
-117 * @param id
-118 * @param completions
-119 * @return
-120 */
-121 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
-122 return registerStaticCompletion(id, Arrays.asList(completions));
-123 }
-124
-125 /**
-126 * Register a static list of command completions that will never change. The list is obtained from the supplier
-127 * immediately as part of this method call.
-128 *
-129 * @param id
-130 * @param supplier
-131 * @return
-132 */
-133 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
-134 return registerStaticCompletion(id, supplier.get());
-135 }
-136
-137 /**
-138 * Register a static list of command completions that will never change
-139 *
-140 * @param id
-141 * @param completions
-142 * @return
-143 */
-144 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
-145 return registerAsyncCompletion(id, x -> completions);
-146 }
-147
-148 /**
-149 * @param id
-150 * @param classes
-151 * @return
-152 * @deprecated Feature Not done yet
-153 */
-154 CommandCompletionHandler setDefaultCompletion(String id, Class... classes) {
-155 // get completion with specified id
-156 id = id.toLowerCase();
-157 CommandCompletionHandler completion = completionMap.get(id);
-158
-159 if (completion == null) {
-160 // Throw something because no completion with specified id
-161 throw new IllegalStateException("Completion not registered for " + id);
-162 }
-163
-164 for (Class clazz : classes) {
-165 defaultCompletions.put(clazz, id);
+090 * Use this anytime your handler does not need to access state that is not considered thread safe.
+091 * <p>
+092 * Use context.isAsync() to determine if you are async or not.
+093 *
+094 * @param id
+095 * @param handler
+096 * @return
+097 */
+098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
+099 return this.completionMap.put(prepareCompletionId(id), handler);
+100 }
+101
+102 /**
+103 * Register a static list of command completions that will never change.
+104 * Like @CommandCompletion, values are | (PIPE) separated.
+105 * <p>
+106 * Example: foo|bar|baz
+107 *
+108 * @param id
+109 * @param list
+110 * @return
+111 */
+112 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
+113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
+114 }
+115
+116 /**
+117 * Register a static list of command completions that will never change
+118 *
+119 * @param id
+120 * @param completions
+121 * @return
+122 */
+123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
+124 return registerStaticCompletion(id, Arrays.asList(completions));
+125 }
+126
+127 /**
+128 * Register a static list of command completions that will never change. The list is obtained from the supplier
+129 * immediately as part of this method call.
+130 *
+131 * @param id
+132 * @param supplier
+133 * @return
+134 */
+135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
+136 return registerStaticCompletion(id, supplier.get());
+137 }
+138
+139 /**
+140 * Register a static list of command completions that will never change
+141 *
+142 * @param id
+143 * @param completions
+144 * @return
+145 */
+146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
+147 return registerAsyncCompletion(id, x -> completions);
+148 }
+149
+150 /**
+151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types
+152 * <p>
+153 * This enables automatic completion support for parameters without manually defining it for custom objects
+154 *
+155 * @param id
+156 * @param classes
+157 */
+158 public void setDefaultCompletion(String id, Class... classes) {
+159 // get completion with specified id
+160 id = prepareCompletionId(id);
+161 CommandCompletionHandler completion = completionMap.get(id);
+162
+163 if (completion == null) {
+164 // Throw something because no completion with specified id
+165 throw new IllegalStateException("Completion not registered for " + id);
166 }
167
-168 return completion;
-169 }
-170
-171 @NotNull
-172 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
-173 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
-174 final int argIndex = args.length - 1;
-175
-176 String input = args[argIndex];
+168 for (Class clazz : classes) {
+169 defaultCompletions.put(clazz, id);
+170 }
+171 }
+172
+173 @NotNull
+174 private static String prepareCompletionId(String id) {
+175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase();
+176 }
177
-178 String completion = argIndex < completions.length ? completions[argIndex] : null;
-179 if (completion == null && completions.length > 0) {
-180 completion = completions[completions.length - 1];
-181 }
-182 if (completion == null) {
-183 return Collections.singletonList(input);
-184 }
-185
-186 return getCompletionValues(cmd, sender, completion, args, isAsync);
-187 }
-188
-189 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
-190 completion = manager.getCommandReplacements().replace(completion);
-191
-192 List<String> allCompletions = new ArrayList<>();
-193 String input = args.length > 0 ? args[args.length - 1] : "";
-194
-195 for (String value : ACFPatterns.PIPE.split(completion)) {
-196 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
-197 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
-198 if (handler != null) {
-199 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
-200 ACFUtil.sneaky(new SyncCompletionRequired());
-201 return null;
-202 }
-203 String config = complete.length == 1 ? null : complete[1];
-204 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
-205
-206 try {
-207 //noinspection unchecked
-208 Collection<String> completions = handler.getCompletions(context);
-209 if (completions != null) {
-210 allCompletions.addAll(completions);
-211 continue;
-212 }
-213 //noinspection ConstantIfStatement,ConstantConditions
-214 if (false) { // Hack to fool compiler. since its sneakily thrown.
-215 throw new CommandCompletionTextLookupException();
-216 }
-217 } catch (CommandCompletionTextLookupException ignored) {
-218 // This should only happen if some other feedback error occured.
-219 } catch (Exception e) {
-220 command.handleException(sender, Arrays.asList(args), e);
+178 @NotNull
+179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
+180 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
+181 final int argIndex = args.length - 1;
+182
+183 String input = args[argIndex];
+184
+185 String completion = argIndex < completions.length ? completions[argIndex] : null;
+186 if (completion == null || "*".equals(completion)) {
+187 completion = findDefaultCompletion(cmd, args);
+188 }
+189
+190 if (completion == null && completions.length > 0) {
+191 String last = completions[completions.length - 1];
+192 if (last.startsWith("repeat@")) {
+193 completion = last;
+194 }
+195 }
+196
+197 if (completion == null) {
+198 return Collections.singletonList(input);
+199 }
+200
+201 return getCompletionValues(cmd, sender, completion, args, isAsync);
+202 }
+203
+204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) {
+205 int i = 0;
+206 for (CommandParameter param : cmd.parameters) {
+207 if (param.canConsumeInput() && ++i == args.length) {
+208 Class type = param.getType();
+209 while (type != null) {
+210 String completion = this.defaultCompletions.get(type);
+211 if (completion != null) {
+212 return completion;
+213 }
+214 type = type.getSuperclass();
+215 }
+216 if (param.getType().isEnum()) {
+217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext();
+218 //noinspection unchecked
+219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType());
+220 return DEFAULT_ENUM_ID;
221 }
-222 // Something went wrong in lookup, fall back to input
-223 return Collections.singletonList(input);
-224 } else {
-225 // Plaintext value
-226 allCompletions.add(value);
-227 }
-228 }
-229 return allCompletions;
-230 }
-231
-232 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
-233 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
-234 }
-235
-236 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
-237 }
-238
-239 public static class SyncCompletionRequired extends RuntimeException {
-240 }
-241
-242}
+222 break;
+223 }
+224 }
+225 return null;
+226 }
+227
+228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
+229 if (DEFAULT_ENUM_ID.equals(completion)) {
+230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext();
+231 return ctx.enumCompletionValues;
+232 }
+233 if (completion.startsWith("repeat@")) {
+234 completion = completion.substring(6);
+235 }
+236 completion = manager.getCommandReplacements().replace(completion);
+237
+238 List<String> allCompletions = new ArrayList<>();
+239 String input = args.length > 0 ? args[args.length - 1] : "";
+240
+241 for (String value : ACFPatterns.PIPE.split(completion)) {
+242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
+243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
+244 if (handler != null) {
+245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
+246 ACFUtil.sneaky(new SyncCompletionRequired());
+247 return null;
+248 }
+249 String config = complete.length == 1 ? null : complete[1];
+250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
+251
+252 try {
+253 //noinspection unchecked
+254 Collection<String> completions = handler.getCompletions(context);
+255 if (completions != null) {
+256 allCompletions.addAll(completions);
+257 continue;
+258 }
+259 //noinspection ConstantIfStatement,ConstantConditions
+260 if (false) { // Hack to fool compiler. since its sneakily thrown.
+261 throw new CommandCompletionTextLookupException();
+262 }
+263 } catch (CommandCompletionTextLookupException ignored) {
+264 // This should only happen if some other feedback error occured.
+265 } catch (Exception e) {
+266 command.handleException(sender, Arrays.asList(args), e);
+267 }
+268 // Something went wrong in lookup, fall back to input
+269 return Collections.singletonList(input);
+270 } else {
+271 // Plaintext value
+272 allCompletions.add(value);
+273 }
+274 }
+275 return allCompletions;
+276 }
+277
+278 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
+279 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
+280 }
+281
+282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
+283 }
+284
+285 public static class SyncCompletionRequired extends RuntimeException {
+286 }
+287
+288}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html
index a2053fdb..616f583a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html
@@ -47,207 +47,253 @@
039
040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
041public class CommandCompletions<C extends CommandCompletionContext> {
-042 private final CommandManager manager;
-043 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
-044 private Map<Class, String> defaultCompletions = new HashMap<>();
-045
-046 public CommandCompletions(CommandManager manager) {
-047 this.manager = manager;
-048 registerAsyncCompletion("nothing", c -> Collections.emptyList());
-049 registerAsyncCompletion("range", (c) -> {
-050 String config = c.getConfig();
-051 if (config == null) {
-052 return Collections.emptyList();
-053 }
-054 final String[] ranges = ACFPatterns.DASH.split(config);
-055 int start;
-056 int end;
-057 if (ranges.length != 2) {
-058 start = 0;
-059 end = ACFUtil.parseInt(ranges[0], 0);
-060 } else {
-061 start = ACFUtil.parseInt(ranges[0], 0);
-062 end = ACFUtil.parseInt(ranges[1], 0);
-063 }
-064 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
-065 });
-066 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
-067 registerAsyncCompletion("timeunits", (c) -> timeunits);
-068 }
-069
-070 /**
-071 * Registr a completion handler to provide command completions based on the user input.
-072 *
-073 * @param id
-074 * @param handler
-075 * @return
-076 */
-077 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
-078 return this.completionMap.put("@" + id.toLowerCase(), handler);
-079 }
-080
-081 /**
-082 * Registr a completion handler to provide command completions based on the user input.
-083 * This handler is declared to be safe to be executed asynchronously.
-084 * <p>
-085 * Not all platforms support this, so if the platform does not support asynchronous execution,
-086 * your handler will be executed on the main thread.
-087 * <p>
-088 * Use this anytime your handler does not need to access state that is not considered thread safe.
+042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__";
+043 private final CommandManager manager;
+044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support
+045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
+046 private Map<Class, String> defaultCompletions = new HashMap<>();
+047
+048 public CommandCompletions(CommandManager manager) {
+049 this.manager = manager;
+050 registerAsyncCompletion("nothing", c -> Collections.emptyList());
+051 registerAsyncCompletion("range", (c) -> {
+052 String config = c.getConfig();
+053 if (config == null) {
+054 return Collections.emptyList();
+055 }
+056 final String[] ranges = ACFPatterns.DASH.split(config);
+057 int start;
+058 int end;
+059 if (ranges.length != 2) {
+060 start = 0;
+061 end = ACFUtil.parseInt(ranges[0], 0);
+062 } else {
+063 start = ACFUtil.parseInt(ranges[0], 0);
+064 end = ACFUtil.parseInt(ranges[1], 0);
+065 }
+066 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
+067 });
+068 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
+069 registerAsyncCompletion("timeunits", (c) -> timeunits);
+070 }
+071
+072 /**
+073 * Registr a completion handler to provide command completions based on the user input.
+074 *
+075 * @param id
+076 * @param handler
+077 * @return
+078 */
+079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
+080 return this.completionMap.put(prepareCompletionId(id), handler);
+081 }
+082
+083 /**
+084 * Registr a completion handler to provide command completions based on the user input.
+085 * This handler is declared to be safe to be executed asynchronously.
+086 * <p>
+087 * Not all platforms support this, so if the platform does not support asynchronous execution,
+088 * your handler will be executed on the main thread.
089 * <p>
-090 * Use context.isAsync() to determine if you are async or not.
-091 *
-092 * @param id
-093 * @param handler
-094 * @return
-095 */
-096 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
-097 return this.completionMap.put("@" + id.toLowerCase(), handler);
-098 }
-099
-100 /**
-101 * Register a static list of command completions that will never change.
-102 * Like @CommandCompletion, values are | (PIPE) separated.
-103 * <p>
-104 * Example: foo|bar|baz
-105 *
-106 * @param id
-107 * @param list
-108 * @return
-109 */
-110 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
-111 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
-112 }
-113
-114 /**
-115 * Register a static list of command completions that will never change
-116 *
-117 * @param id
-118 * @param completions
-119 * @return
-120 */
-121 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
-122 return registerStaticCompletion(id, Arrays.asList(completions));
-123 }
-124
-125 /**
-126 * Register a static list of command completions that will never change. The list is obtained from the supplier
-127 * immediately as part of this method call.
-128 *
-129 * @param id
-130 * @param supplier
-131 * @return
-132 */
-133 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
-134 return registerStaticCompletion(id, supplier.get());
-135 }
-136
-137 /**
-138 * Register a static list of command completions that will never change
-139 *
-140 * @param id
-141 * @param completions
-142 * @return
-143 */
-144 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
-145 return registerAsyncCompletion(id, x -> completions);
-146 }
-147
-148 /**
-149 * @param id
-150 * @param classes
-151 * @return
-152 * @deprecated Feature Not done yet
-153 */
-154 CommandCompletionHandler setDefaultCompletion(String id, Class... classes) {
-155 // get completion with specified id
-156 id = id.toLowerCase();
-157 CommandCompletionHandler completion = completionMap.get(id);
-158
-159 if (completion == null) {
-160 // Throw something because no completion with specified id
-161 throw new IllegalStateException("Completion not registered for " + id);
-162 }
-163
-164 for (Class clazz : classes) {
-165 defaultCompletions.put(clazz, id);
+090 * Use this anytime your handler does not need to access state that is not considered thread safe.
+091 * <p>
+092 * Use context.isAsync() to determine if you are async or not.
+093 *
+094 * @param id
+095 * @param handler
+096 * @return
+097 */
+098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
+099 return this.completionMap.put(prepareCompletionId(id), handler);
+100 }
+101
+102 /**
+103 * Register a static list of command completions that will never change.
+104 * Like @CommandCompletion, values are | (PIPE) separated.
+105 * <p>
+106 * Example: foo|bar|baz
+107 *
+108 * @param id
+109 * @param list
+110 * @return
+111 */
+112 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
+113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
+114 }
+115
+116 /**
+117 * Register a static list of command completions that will never change
+118 *
+119 * @param id
+120 * @param completions
+121 * @return
+122 */
+123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
+124 return registerStaticCompletion(id, Arrays.asList(completions));
+125 }
+126
+127 /**
+128 * Register a static list of command completions that will never change. The list is obtained from the supplier
+129 * immediately as part of this method call.
+130 *
+131 * @param id
+132 * @param supplier
+133 * @return
+134 */
+135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
+136 return registerStaticCompletion(id, supplier.get());
+137 }
+138
+139 /**
+140 * Register a static list of command completions that will never change
+141 *
+142 * @param id
+143 * @param completions
+144 * @return
+145 */
+146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
+147 return registerAsyncCompletion(id, x -> completions);
+148 }
+149
+150 /**
+151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types
+152 * <p>
+153 * This enables automatic completion support for parameters without manually defining it for custom objects
+154 *
+155 * @param id
+156 * @param classes
+157 */
+158 public void setDefaultCompletion(String id, Class... classes) {
+159 // get completion with specified id
+160 id = prepareCompletionId(id);
+161 CommandCompletionHandler completion = completionMap.get(id);
+162
+163 if (completion == null) {
+164 // Throw something because no completion with specified id
+165 throw new IllegalStateException("Completion not registered for " + id);
166 }
167
-168 return completion;
-169 }
-170
-171 @NotNull
-172 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
-173 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
-174 final int argIndex = args.length - 1;
-175
-176 String input = args[argIndex];
+168 for (Class clazz : classes) {
+169 defaultCompletions.put(clazz, id);
+170 }
+171 }
+172
+173 @NotNull
+174 private static String prepareCompletionId(String id) {
+175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase();
+176 }
177
-178 String completion = argIndex < completions.length ? completions[argIndex] : null;
-179 if (completion == null && completions.length > 0) {
-180 completion = completions[completions.length - 1];
-181 }
-182 if (completion == null) {
-183 return Collections.singletonList(input);
-184 }
-185
-186 return getCompletionValues(cmd, sender, completion, args, isAsync);
-187 }
-188
-189 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
-190 completion = manager.getCommandReplacements().replace(completion);
-191
-192 List<String> allCompletions = new ArrayList<>();
-193 String input = args.length > 0 ? args[args.length - 1] : "";
-194
-195 for (String value : ACFPatterns.PIPE.split(completion)) {
-196 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
-197 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
-198 if (handler != null) {
-199 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
-200 ACFUtil.sneaky(new SyncCompletionRequired());
-201 return null;
-202 }
-203 String config = complete.length == 1 ? null : complete[1];
-204 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
-205
-206 try {
-207 //noinspection unchecked
-208 Collection<String> completions = handler.getCompletions(context);
-209 if (completions != null) {
-210 allCompletions.addAll(completions);
-211 continue;
-212 }
-213 //noinspection ConstantIfStatement,ConstantConditions
-214 if (false) { // Hack to fool compiler. since its sneakily thrown.
-215 throw new CommandCompletionTextLookupException();
-216 }
-217 } catch (CommandCompletionTextLookupException ignored) {
-218 // This should only happen if some other feedback error occured.
-219 } catch (Exception e) {
-220 command.handleException(sender, Arrays.asList(args), e);
+178 @NotNull
+179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
+180 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
+181 final int argIndex = args.length - 1;
+182
+183 String input = args[argIndex];
+184
+185 String completion = argIndex < completions.length ? completions[argIndex] : null;
+186 if (completion == null || "*".equals(completion)) {
+187 completion = findDefaultCompletion(cmd, args);
+188 }
+189
+190 if (completion == null && completions.length > 0) {
+191 String last = completions[completions.length - 1];
+192 if (last.startsWith("repeat@")) {
+193 completion = last;
+194 }
+195 }
+196
+197 if (completion == null) {
+198 return Collections.singletonList(input);
+199 }
+200
+201 return getCompletionValues(cmd, sender, completion, args, isAsync);
+202 }
+203
+204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) {
+205 int i = 0;
+206 for (CommandParameter param : cmd.parameters) {
+207 if (param.canConsumeInput() && ++i == args.length) {
+208 Class type = param.getType();
+209 while (type != null) {
+210 String completion = this.defaultCompletions.get(type);
+211 if (completion != null) {
+212 return completion;
+213 }
+214 type = type.getSuperclass();
+215 }
+216 if (param.getType().isEnum()) {
+217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext();
+218 //noinspection unchecked
+219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType());
+220 return DEFAULT_ENUM_ID;
221 }
-222 // Something went wrong in lookup, fall back to input
-223 return Collections.singletonList(input);
-224 } else {
-225 // Plaintext value
-226 allCompletions.add(value);
-227 }
-228 }
-229 return allCompletions;
-230 }
-231
-232 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
-233 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
-234 }
-235
-236 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
-237 }
-238
-239 public static class SyncCompletionRequired extends RuntimeException {
-240 }
-241
-242}
+222 break;
+223 }
+224 }
+225 return null;
+226 }
+227
+228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
+229 if (DEFAULT_ENUM_ID.equals(completion)) {
+230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext();
+231 return ctx.enumCompletionValues;
+232 }
+233 if (completion.startsWith("repeat@")) {
+234 completion = completion.substring(6);
+235 }
+236 completion = manager.getCommandReplacements().replace(completion);
+237
+238 List<String> allCompletions = new ArrayList<>();
+239 String input = args.length > 0 ? args[args.length - 1] : "";
+240
+241 for (String value : ACFPatterns.PIPE.split(completion)) {
+242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
+243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
+244 if (handler != null) {
+245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
+246 ACFUtil.sneaky(new SyncCompletionRequired());
+247 return null;
+248 }
+249 String config = complete.length == 1 ? null : complete[1];
+250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
+251
+252 try {
+253 //noinspection unchecked
+254 Collection<String> completions = handler.getCompletions(context);
+255 if (completions != null) {
+256 allCompletions.addAll(completions);
+257 continue;
+258 }
+259 //noinspection ConstantIfStatement,ConstantConditions
+260 if (false) { // Hack to fool compiler. since its sneakily thrown.
+261 throw new CommandCompletionTextLookupException();
+262 }
+263 } catch (CommandCompletionTextLookupException ignored) {
+264 // This should only happen if some other feedback error occured.
+265 } catch (Exception e) {
+266 command.handleException(sender, Arrays.asList(args), e);
+267 }
+268 // Something went wrong in lookup, fall back to input
+269 return Collections.singletonList(input);
+270 } else {
+271 // Plaintext value
+272 allCompletions.add(value);
+273 }
+274 }
+275 return allCompletions;
+276 }
+277
+278 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
+279 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
+280 }
+281
+282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
+283 }
+284
+285 public static class SyncCompletionRequired extends RuntimeException {
+286 }
+287
+288}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html
index a2053fdb..616f583a 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html
@@ -47,207 +47,253 @@
039
040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
041public class CommandCompletions<C extends CommandCompletionContext> {
-042 private final CommandManager manager;
-043 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
-044 private Map<Class, String> defaultCompletions = new HashMap<>();
-045
-046 public CommandCompletions(CommandManager manager) {
-047 this.manager = manager;
-048 registerAsyncCompletion("nothing", c -> Collections.emptyList());
-049 registerAsyncCompletion("range", (c) -> {
-050 String config = c.getConfig();
-051 if (config == null) {
-052 return Collections.emptyList();
-053 }
-054 final String[] ranges = ACFPatterns.DASH.split(config);
-055 int start;
-056 int end;
-057 if (ranges.length != 2) {
-058 start = 0;
-059 end = ACFUtil.parseInt(ranges[0], 0);
-060 } else {
-061 start = ACFUtil.parseInt(ranges[0], 0);
-062 end = ACFUtil.parseInt(ranges[1], 0);
-063 }
-064 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
-065 });
-066 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
-067 registerAsyncCompletion("timeunits", (c) -> timeunits);
-068 }
-069
-070 /**
-071 * Registr a completion handler to provide command completions based on the user input.
-072 *
-073 * @param id
-074 * @param handler
-075 * @return
-076 */
-077 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
-078 return this.completionMap.put("@" + id.toLowerCase(), handler);
-079 }
-080
-081 /**
-082 * Registr a completion handler to provide command completions based on the user input.
-083 * This handler is declared to be safe to be executed asynchronously.
-084 * <p>
-085 * Not all platforms support this, so if the platform does not support asynchronous execution,
-086 * your handler will be executed on the main thread.
-087 * <p>
-088 * Use this anytime your handler does not need to access state that is not considered thread safe.
+042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__";
+043 private final CommandManager manager;
+044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support
+045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
+046 private Map<Class, String> defaultCompletions = new HashMap<>();
+047
+048 public CommandCompletions(CommandManager manager) {
+049 this.manager = manager;
+050 registerAsyncCompletion("nothing", c -> Collections.emptyList());
+051 registerAsyncCompletion("range", (c) -> {
+052 String config = c.getConfig();
+053 if (config == null) {
+054 return Collections.emptyList();
+055 }
+056 final String[] ranges = ACFPatterns.DASH.split(config);
+057 int start;
+058 int end;
+059 if (ranges.length != 2) {
+060 start = 0;
+061 end = ACFUtil.parseInt(ranges[0], 0);
+062 } else {
+063 start = ACFUtil.parseInt(ranges[0], 0);
+064 end = ACFUtil.parseInt(ranges[1], 0);
+065 }
+066 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
+067 });
+068 List<String> timeunits = Arrays.asList("minutes", "hours", "days", "weeks", "months", "years");
+069 registerAsyncCompletion("timeunits", (c) -> timeunits);
+070 }
+071
+072 /**
+073 * Registr a completion handler to provide command completions based on the user input.
+074 *
+075 * @param id
+076 * @param handler
+077 * @return
+078 */
+079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
+080 return this.completionMap.put(prepareCompletionId(id), handler);
+081 }
+082
+083 /**
+084 * Registr a completion handler to provide command completions based on the user input.
+085 * This handler is declared to be safe to be executed asynchronously.
+086 * <p>
+087 * Not all platforms support this, so if the platform does not support asynchronous execution,
+088 * your handler will be executed on the main thread.
089 * <p>
-090 * Use context.isAsync() to determine if you are async or not.
-091 *
-092 * @param id
-093 * @param handler
-094 * @return
-095 */
-096 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
-097 return this.completionMap.put("@" + id.toLowerCase(), handler);
-098 }
-099
-100 /**
-101 * Register a static list of command completions that will never change.
-102 * Like @CommandCompletion, values are | (PIPE) separated.
-103 * <p>
-104 * Example: foo|bar|baz
-105 *
-106 * @param id
-107 * @param list
-108 * @return
-109 */
-110 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
-111 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
-112 }
-113
-114 /**
-115 * Register a static list of command completions that will never change
-116 *
-117 * @param id
-118 * @param completions
-119 * @return
-120 */
-121 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
-122 return registerStaticCompletion(id, Arrays.asList(completions));
-123 }
-124
-125 /**
-126 * Register a static list of command completions that will never change. The list is obtained from the supplier
-127 * immediately as part of this method call.
-128 *
-129 * @param id
-130 * @param supplier
-131 * @return
-132 */
-133 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
-134 return registerStaticCompletion(id, supplier.get());
-135 }
-136
-137 /**
-138 * Register a static list of command completions that will never change
-139 *
-140 * @param id
-141 * @param completions
-142 * @return
-143 */
-144 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
-145 return registerAsyncCompletion(id, x -> completions);
-146 }
-147
-148 /**
-149 * @param id
-150 * @param classes
-151 * @return
-152 * @deprecated Feature Not done yet
-153 */
-154 CommandCompletionHandler setDefaultCompletion(String id, Class... classes) {
-155 // get completion with specified id
-156 id = id.toLowerCase();
-157 CommandCompletionHandler completion = completionMap.get(id);
-158
-159 if (completion == null) {
-160 // Throw something because no completion with specified id
-161 throw new IllegalStateException("Completion not registered for " + id);
-162 }
-163
-164 for (Class clazz : classes) {
-165 defaultCompletions.put(clazz, id);
+090 * Use this anytime your handler does not need to access state that is not considered thread safe.
+091 * <p>
+092 * Use context.isAsync() to determine if you are async or not.
+093 *
+094 * @param id
+095 * @param handler
+096 * @return
+097 */
+098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
+099 return this.completionMap.put(prepareCompletionId(id), handler);
+100 }
+101
+102 /**
+103 * Register a static list of command completions that will never change.
+104 * Like @CommandCompletion, values are | (PIPE) separated.
+105 * <p>
+106 * Example: foo|bar|baz
+107 *
+108 * @param id
+109 * @param list
+110 * @return
+111 */
+112 public CommandCompletionHandler registerStaticCompletion(String id, String list) {
+113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list));
+114 }
+115
+116 /**
+117 * Register a static list of command completions that will never change
+118 *
+119 * @param id
+120 * @param completions
+121 * @return
+122 */
+123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) {
+124 return registerStaticCompletion(id, Arrays.asList(completions));
+125 }
+126
+127 /**
+128 * Register a static list of command completions that will never change. The list is obtained from the supplier
+129 * immediately as part of this method call.
+130 *
+131 * @param id
+132 * @param supplier
+133 * @return
+134 */
+135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) {
+136 return registerStaticCompletion(id, supplier.get());
+137 }
+138
+139 /**
+140 * Register a static list of command completions that will never change
+141 *
+142 * @param id
+143 * @param completions
+144 * @return
+145 */
+146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) {
+147 return registerAsyncCompletion(id, x -> completions);
+148 }
+149
+150 /**
+151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types
+152 * <p>
+153 * This enables automatic completion support for parameters without manually defining it for custom objects
+154 *
+155 * @param id
+156 * @param classes
+157 */
+158 public void setDefaultCompletion(String id, Class... classes) {
+159 // get completion with specified id
+160 id = prepareCompletionId(id);
+161 CommandCompletionHandler completion = completionMap.get(id);
+162
+163 if (completion == null) {
+164 // Throw something because no completion with specified id
+165 throw new IllegalStateException("Completion not registered for " + id);
166 }
167
-168 return completion;
-169 }
-170
-171 @NotNull
-172 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
-173 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
-174 final int argIndex = args.length - 1;
-175
-176 String input = args[argIndex];
+168 for (Class clazz : classes) {
+169 defaultCompletions.put(clazz, id);
+170 }
+171 }
+172
+173 @NotNull
+174 private static String prepareCompletionId(String id) {
+175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase();
+176 }
177
-178 String completion = argIndex < completions.length ? completions[argIndex] : null;
-179 if (completion == null && completions.length > 0) {
-180 completion = completions[completions.length - 1];
-181 }
-182 if (completion == null) {
-183 return Collections.singletonList(input);
-184 }
-185
-186 return getCompletionValues(cmd, sender, completion, args, isAsync);
-187 }
-188
-189 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
-190 completion = manager.getCommandReplacements().replace(completion);
-191
-192 List<String> allCompletions = new ArrayList<>();
-193 String input = args.length > 0 ? args[args.length - 1] : "";
-194
-195 for (String value : ACFPatterns.PIPE.split(completion)) {
-196 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
-197 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
-198 if (handler != null) {
-199 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
-200 ACFUtil.sneaky(new SyncCompletionRequired());
-201 return null;
-202 }
-203 String config = complete.length == 1 ? null : complete[1];
-204 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
-205
-206 try {
-207 //noinspection unchecked
-208 Collection<String> completions = handler.getCompletions(context);
-209 if (completions != null) {
-210 allCompletions.addAll(completions);
-211 continue;
-212 }
-213 //noinspection ConstantIfStatement,ConstantConditions
-214 if (false) { // Hack to fool compiler. since its sneakily thrown.
-215 throw new CommandCompletionTextLookupException();
-216 }
-217 } catch (CommandCompletionTextLookupException ignored) {
-218 // This should only happen if some other feedback error occured.
-219 } catch (Exception e) {
-220 command.handleException(sender, Arrays.asList(args), e);
+178 @NotNull
+179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) {
+180 String[] completions = ACFPatterns.SPACE.split(cmd.complete);
+181 final int argIndex = args.length - 1;
+182
+183 String input = args[argIndex];
+184
+185 String completion = argIndex < completions.length ? completions[argIndex] : null;
+186 if (completion == null || "*".equals(completion)) {
+187 completion = findDefaultCompletion(cmd, args);
+188 }
+189
+190 if (completion == null && completions.length > 0) {
+191 String last = completions[completions.length - 1];
+192 if (last.startsWith("repeat@")) {
+193 completion = last;
+194 }
+195 }
+196
+197 if (completion == null) {
+198 return Collections.singletonList(input);
+199 }
+200
+201 return getCompletionValues(cmd, sender, completion, args, isAsync);
+202 }
+203
+204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) {
+205 int i = 0;
+206 for (CommandParameter param : cmd.parameters) {
+207 if (param.canConsumeInput() && ++i == args.length) {
+208 Class type = param.getType();
+209 while (type != null) {
+210 String completion = this.defaultCompletions.get(type);
+211 if (completion != null) {
+212 return completion;
+213 }
+214 type = type.getSuperclass();
+215 }
+216 if (param.getType().isEnum()) {
+217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext();
+218 //noinspection unchecked
+219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType());
+220 return DEFAULT_ENUM_ID;
221 }
-222 // Something went wrong in lookup, fall back to input
-223 return Collections.singletonList(input);
-224 } else {
-225 // Plaintext value
-226 allCompletions.add(value);
-227 }
-228 }
-229 return allCompletions;
-230 }
-231
-232 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
-233 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
-234 }
-235
-236 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
-237 }
-238
-239 public static class SyncCompletionRequired extends RuntimeException {
-240 }
-241
-242}
+222 break;
+223 }
+224 }
+225 return null;
+226 }
+227
+228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
+229 if (DEFAULT_ENUM_ID.equals(completion)) {
+230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext();
+231 return ctx.enumCompletionValues;
+232 }
+233 if (completion.startsWith("repeat@")) {
+234 completion = completion.substring(6);
+235 }
+236 completion = manager.getCommandReplacements().replace(completion);
+237
+238 List<String> allCompletions = new ArrayList<>();
+239 String input = args.length > 0 ? args[args.length - 1] : "";
+240
+241 for (String value : ACFPatterns.PIPE.split(completion)) {
+242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
+243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
+244 if (handler != null) {
+245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
+246 ACFUtil.sneaky(new SyncCompletionRequired());
+247 return null;
+248 }
+249 String config = complete.length == 1 ? null : complete[1];
+250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
+251
+252 try {
+253 //noinspection unchecked
+254 Collection<String> completions = handler.getCompletions(context);
+255 if (completions != null) {
+256 allCompletions.addAll(completions);
+257 continue;
+258 }
+259 //noinspection ConstantIfStatement,ConstantConditions
+260 if (false) { // Hack to fool compiler. since its sneakily thrown.
+261 throw new CommandCompletionTextLookupException();
+262 }
+263 } catch (CommandCompletionTextLookupException ignored) {
+264 // This should only happen if some other feedback error occured.
+265 } catch (Exception e) {
+266 command.handleException(sender, Arrays.asList(args), e);
+267 }
+268 // Something went wrong in lookup, fall back to input
+269 return Collections.singletonList(input);
+270 } else {
+271 // Plaintext value
+272 allCompletions.add(value);
+273 }
+274 }
+275 return allCompletions;
+276 }
+277
+278 public interface CommandCompletionHandler<C extends CommandCompletionContext> {
+279 Collection<String> getCompletions(C context) throws InvalidCommandArgument;
+280 }
+281
+282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> {
+283 }
+284
+285 public static class SyncCompletionRequired extends RuntimeException {
+286 }
+287
+288}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html b/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
index 2f8d0dd4..2c13c8bf 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
@@ -213,7 +213,7 @@
205 Enum<?> match = ACFUtil.simpleMatch(enumCls, first);
206 if (match == null) {
207 List<String> names = ACFUtil.enumNames(enumCls);
-208 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names));
+208 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names, ", "));
209 }
210 return match;
211 });
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html b/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html
index b00fbd79..5acbca05 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html
@@ -35,80 +35,80 @@
027import java.lang.reflect.Parameter;
028import java.util.List;
029import java.util.Map;
-030
-031@SuppressWarnings({"WeakerAccess"})
-032public class CommandExecutionContext <CEC extends CommandExecutionContext, I extends CommandIssuer> {
-033 private final RegisteredCommand cmd;
-034 private final CommandParameter param;
-035 protected final I issuer;
-036 private final List<String> args;
-037 private final int index;
-038 private final Map<String, Object> passedArgs;
-039 private final Map<String, String> flags;
-040 private final CommandManager manager;
-041
-042 CommandExecutionContext(RegisteredCommand cmd, CommandParameter param, I sender, List<String> args,
-043 int index, Map<String, Object> passedArgs) {
-044 this.cmd = cmd;
-045 this.manager = cmd.scope.manager;
-046 this.param = param;
-047 this.issuer = sender;
-048 this.args = args;
-049 this.index = index;
-050 this.passedArgs = passedArgs;
-051 this.flags = param.getFlags();
-052
-053 }
-054
-055 public String popFirstArg() {
-056 return !args.isEmpty() ? args.remove(0) : null;
-057 }
-058
-059 public String popLastArg() {
-060 return !args.isEmpty() ? args.remove(args.size() - 1) : null;
-061 }
-062
-063 public String getFirstArg() {
-064 return !args.isEmpty() ? args.get(0) : null;
-065 }
-066
-067 public String getLastArg() {
-068 return !args.isEmpty() ? args.get(args.size() - 1) : null;
-069 }
-070
-071 public boolean isLastArg() {
-072 return cmd.parameters.length -1 == index;
-073 }
-074
-075 public int getNumParams() {
-076 return cmd.parameters.length;
-077 }
-078
-079 public boolean canOverridePlayerContext() {
-080 return cmd.requiredResolvers >= args.size();
-081 }
-082
-083 public Object getResolvedArg(String arg) {
-084 return passedArgs.get(arg);
-085 }
-086
-087 public Object getResolvedArg(Class<?>... classes) {
-088 for (Class<?> clazz : classes) {
-089 for (Object passedArg : passedArgs.values()) {
-090 if (clazz.isInstance(passedArg)) {
-091 return passedArg;
-092 }
-093 }
-094 }
-095
-096 return null;
-097 }
-098
-099 public <T> T getResolvedArg(String key, Class<?>... classes) {
-100 final Object o = passedArgs.get(key);
-101 for (Class<?> clazz : classes) {
-102 if (clazz.isInstance(o)) {
-103 //noinspection unchecked
+030import java.util.Set;
+031
+032@SuppressWarnings({"WeakerAccess", "unchecked"})
+033public class CommandExecutionContext<CEC extends CommandExecutionContext, I extends CommandIssuer> {
+034 private final RegisteredCommand cmd;
+035 private final CommandParameter param;
+036 protected final I issuer;
+037 private final List<String> args;
+038 private final int index;
+039 private final Map<String, Object> passedArgs;
+040 private final Map<String, String> flags;
+041 private final CommandManager manager;
+042
+043 CommandExecutionContext(RegisteredCommand cmd, CommandParameter param, I sender, List<String> args,
+044 int index, Map<String, Object> passedArgs) {
+045 this.cmd = cmd;
+046 this.manager = cmd.scope.manager;
+047 this.param = param;
+048 this.issuer = sender;
+049 this.args = args;
+050 this.index = index;
+051 this.passedArgs = passedArgs;
+052 this.flags = param.getFlags();
+053
+054 }
+055
+056 public String popFirstArg() {
+057 return !args.isEmpty() ? args.remove(0) : null;
+058 }
+059
+060 public String popLastArg() {
+061 return !args.isEmpty() ? args.remove(args.size() - 1) : null;
+062 }
+063
+064 public String getFirstArg() {
+065 return !args.isEmpty() ? args.get(0) : null;
+066 }
+067
+068 public String getLastArg() {
+069 return !args.isEmpty() ? args.get(args.size() - 1) : null;
+070 }
+071
+072 public boolean isLastArg() {
+073 return cmd.parameters.length - 1 == index;
+074 }
+075
+076 public int getNumParams() {
+077 return cmd.parameters.length;
+078 }
+079
+080 public boolean canOverridePlayerContext() {
+081 return cmd.requiredResolvers >= args.size();
+082 }
+083
+084 public Object getResolvedArg(String arg) {
+085 return passedArgs.get(arg);
+086 }
+087
+088 public Object getResolvedArg(Class<?>... classes) {
+089 for (Class<?> clazz : classes) {
+090 for (Object passedArg : passedArgs.values()) {
+091 if (clazz.isInstance(passedArg)) {
+092 return passedArg;
+093 }
+094 }
+095 }
+096
+097 return null;
+098 }
+099
+100 public <T> T getResolvedArg(String key, Class<?>... classes) {
+101 final Object o = passedArgs.get(key);
+102 for (Class<?> clazz : classes) {
+103 if (clazz.isInstance(o)) {
104 return (T) o;
105 }
106 }
@@ -116,128 +116,134 @@
108 return null;
109 }
110
-111 public boolean isOptional() {
-112 return param.isOptional();
+111 public Set<String> getParameterPermissions() {
+112 return param.getRequiredPermissions();
113 }
114
-115 public boolean hasFlag(String flag) {
-116 return flags.containsKey(flag);
+115 public boolean isOptional() {
+116 return param.isOptional();
117 }
118
-119 public String getFlagValue(String flag, String def) {
-120 return flags.getOrDefault(flag, def);
+119 public boolean hasFlag(String flag) {
+120 return flags.containsKey(flag);
121 }
122
-123 public Integer getFlagValue(String flag, Integer def) {
-124 return ACFUtil.parseInt(this.flags.get(flag), def);
+123 public String getFlagValue(String flag, String def) {
+124 return flags.getOrDefault(flag, def);
125 }
126
-127 public Long getFlagValue(String flag, Long def) {
-128 return ACFUtil.parseLong(this.flags.get(flag), def);
+127 public Integer getFlagValue(String flag, Integer def) {
+128 return ACFUtil.parseInt(this.flags.get(flag), def);
129 }
130
-131 public Float getFlagValue(String flag, Float def) {
-132 return ACFUtil.parseFloat(this.flags.get(flag), def);
+131 public Long getFlagValue(String flag, Long def) {
+132 return ACFUtil.parseLong(this.flags.get(flag), def);
133 }
134
-135 public Double getFlagValue(String flag, Double def) {
-136 return ACFUtil.parseDouble(this.flags.get(flag), def);
+135 public Float getFlagValue(String flag, Float def) {
+136 return ACFUtil.parseFloat(this.flags.get(flag), def);
137 }
138
-139 public Integer getIntFlagValue(String flag, Number def) {
-140 return ACFUtil.parseInt(this.flags.get(flag), def != null ? def.intValue() : null);
+139 public Double getFlagValue(String flag, Double def) {
+140 return ACFUtil.parseDouble(this.flags.get(flag), def);
141 }
142
-143 public Long getLongFlagValue(String flag, Number def) {
-144 return ACFUtil.parseLong(this.flags.get(flag), def != null ? def.longValue() : null);
+143 public Integer getIntFlagValue(String flag, Number def) {
+144 return ACFUtil.parseInt(this.flags.get(flag), def != null ? def.intValue() : null);
145 }
146
-147 public Float getFloatFlagValue(String flag, Number def) {
-148 return ACFUtil.parseFloat(this.flags.get(flag), def != null ? def.floatValue() : null);
+147 public Long getLongFlagValue(String flag, Number def) {
+148 return ACFUtil.parseLong(this.flags.get(flag), def != null ? def.longValue() : null);
149 }
150
-151 public Double getDoubleFlagValue(String flag, Number def) {
-152 return ACFUtil.parseDouble(this.flags.get(flag), def != null ? def.doubleValue() : null);
+151 public Float getFloatFlagValue(String flag, Number def) {
+152 return ACFUtil.parseFloat(this.flags.get(flag), def != null ? def.floatValue() : null);
153 }
154
-155 public Boolean getBooleanFlagValue(String flag) {
-156 return getBooleanFlagValue(flag, false);
+155 public Double getDoubleFlagValue(String flag, Number def) {
+156 return ACFUtil.parseDouble(this.flags.get(flag), def != null ? def.doubleValue() : null);
157 }
158
-159 public Boolean getBooleanFlagValue(String flag, Boolean def) {
-160 String val = this.flags.get(flag);
-161 if (val == null) {
-162 return def;
-163 }
-164 return ACFUtil.isTruthy(val);
-165 }
-166
-167 public Double getFlagValue(String flag, Number def) {
-168 return ACFUtil.parseDouble(this.flags.get(flag), def != null ? def.doubleValue() : null);
+159 public Boolean getBooleanFlagValue(String flag) {
+160 return getBooleanFlagValue(flag, false);
+161 }
+162
+163 public Boolean getBooleanFlagValue(String flag, Boolean def) {
+164 String val = this.flags.get(flag);
+165 if (val == null) {
+166 return def;
+167 }
+168 return ACFUtil.isTruthy(val);
169 }
170
-171 /**
-172 * This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
-173 * @deprecated Use {@link #getAnnotationValue(Class)}
-174 */
-175 @Deprecated
-176 public <T extends Annotation> T getAnnotation(Class<T> cls) {
-177 return param.getParameter().getAnnotation(cls);
-178 }
-179
-180 public <T extends Annotation> String getAnnotationValue(Class<T> cls) {
-181 return manager.getAnnotations().getAnnotationValue(param.getParameter(), cls);
-182 }
-183
-184 public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options) {
-185 return manager.getAnnotations().getAnnotationValue(param.getParameter(), cls, options);
-186 }
-187
-188 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) {
-189 return manager.getAnnotations().hasAnnotation(param.getParameter(), cls);
-190 }
-191
-192 public RegisteredCommand getCmd() {
-193 return this.cmd;
-194 }
-195
-196 @UnstableAPI
-197 CommandParameter getCommandParameter() {
-198 return this.param;
+171 public Double getFlagValue(String flag, Number def) {
+172 return ACFUtil.parseDouble(this.flags.get(flag), def != null ? def.doubleValue() : null);
+173 }
+174
+175 /**
+176 * This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
+177 *
+178 * @deprecated Use {@link #getAnnotationValue(Class)}
+179 */
+180 @Deprecated
+181 public <T extends Annotation> T getAnnotation(Class<T> cls) {
+182 return param.getParameter().getAnnotation(cls);
+183 }
+184
+185 public <T extends Annotation> String getAnnotationValue(Class<T> cls) {
+186 return manager.getAnnotations().getAnnotationValue(param.getParameter(), cls);
+187 }
+188
+189 public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options) {
+190 return manager.getAnnotations().getAnnotationValue(param.getParameter(), cls, options);
+191 }
+192
+193 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) {
+194 return manager.getAnnotations().hasAnnotation(param.getParameter(), cls);
+195 }
+196
+197 public RegisteredCommand getCmd() {
+198 return this.cmd;
199 }
200
-201 @Deprecated
-202 public Parameter getParam() {
-203 return this.param.getParameter();
+201 @UnstableAPI
+202 CommandParameter getCommandParameter() {
+203 return this.param;
204 }
205
-206 public I getIssuer() {
-207 return this.issuer;
-208 }
-209
-210 public List<String> getArgs() {
-211 return this.args;
-212 }
-213
-214 public int getIndex() {
-215 return this.index;
-216 }
-217
-218 public Map<String, Object> getPassedArgs() {
-219 return this.passedArgs;
-220 }
-221
-222 public Map<String, String> getFlags() {
-223 return this.flags;
-224 }
-225
-226 public String joinArgs() {
-227 return ACFUtil.join(args, " ");
-228 }
-229 public String joinArgs(String sep) {
-230 return ACFUtil.join(args, sep);
-231 }
-232}
+206 @Deprecated
+207 public Parameter getParam() {
+208 return this.param.getParameter();
+209 }
+210
+211 public I getIssuer() {
+212 return this.issuer;
+213 }
+214
+215 public List<String> getArgs() {
+216 return this.args;
+217 }
+218
+219 public int getIndex() {
+220 return this.index;
+221 }
+222
+223 public Map<String, Object> getPassedArgs() {
+224 return this.passedArgs;
+225 }
+226
+227 public Map<String, String> getFlags() {
+228 return this.flags;
+229 }
+230
+231 public String joinArgs() {
+232 return ACFUtil.join(args, " ");
+233 }
+234
+235 public String joinArgs(String sep) {
+236 return ACFUtil.join(args, sep);
+237 }
+238}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandManager.html b/docs/acf-core/src-html/co/aikar/commands/CommandManager.html
index 6cd98236..3c7c8d80 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandManager.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandManager.html
@@ -56,14 +56,14 @@
048
049
050@SuppressWarnings("WeakerAccess")
-051public abstract class CommandManager <
+051public abstract class CommandManager<
052 IT,
053 I extends CommandIssuer,
054 FT,
055 MF extends MessageFormatter<FT>,
056 CEC extends CommandExecutionContext<CEC, I>,
057 CC extends ConditionContext<I>
-058 > {
+058 > {
059
060 /**
061 * This is a stack incase a command calls a command
@@ -121,7 +121,7 @@
113 public void setFormat(MessageType type, FT... colors) {
114 MF format = getFormat(type);
115 for (int i = 1; i <= colors.length; i++) {
-116 format.setColor(i, colors[i-1]);
+116 format.setColor(i, colors[i - 1]);
117 }
118 }
119
@@ -144,402 +144,452 @@
136
137 /**
138 * Gets the command contexts manager
-139 * @return Command Contexts
-140 */
-141 public abstract CommandContexts<?> getCommandContexts();
-142
-143 /**
-144 * Gets the command completions manager
-145 * @return Command Completions
-146 */
-147 public abstract CommandCompletions<?> getCommandCompletions();
-148
-149 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-150 public CommandHelp generateCommandHelp(@NotNull String command) {
-151 verifyUnstableAPI("help");
-152 CommandOperationContext context = getCurrentCommandOperationContext();
-153 if (context == null) {
-154 throw new IllegalStateException("This method can only be called as part of a command execution.");
-155 }
-156 return generateCommandHelp(context.getCommandIssuer(), command);
-157 }
-158
-159 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-160 public CommandHelp generateCommandHelp(CommandIssuer issuer, @NotNull String command) {
-161 verifyUnstableAPI("help");
-162 return generateCommandHelp(issuer, obtainRootCommand(command));
+139 *
+140 * @return Command Contexts
+141 */
+142 public abstract CommandContexts<?> getCommandContexts();
+143
+144 /**
+145 * Gets the command completions manager
+146 *
+147 * @return Command Completions
+148 */
+149 public abstract CommandCompletions<?> getCommandCompletions();
+150
+151 /**
+152 * @deprecated Unstable API
+153 */
+154 @Deprecated
+155 @UnstableAPI
+156 public CommandHelp generateCommandHelp(@NotNull String command) {
+157 verifyUnstableAPI("help");
+158 CommandOperationContext context = getCurrentCommandOperationContext();
+159 if (context == null) {
+160 throw new IllegalStateException("This method can only be called as part of a command execution.");
+161 }
+162 return generateCommandHelp(context.getCommandIssuer(), command);
163 }
164
-165 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-166 public CommandHelp generateCommandHelp() {
-167 verifyUnstableAPI("help");
-168 CommandOperationContext context = getCurrentCommandOperationContext();
-169 if (context == null) {
-170 throw new IllegalStateException("This method can only be called as part of a command execution.");
-171 }
-172 String commandLabel = context.getCommandLabel();
-173 return generateCommandHelp(context.getCommandIssuer(), this.obtainRootCommand(commandLabel));
-174 }
-175
-176 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-177 public CommandHelp generateCommandHelp(CommandIssuer issuer, RootCommand rootCommand) {
-178 verifyUnstableAPI("help");
-179 return new CommandHelp(this, rootCommand, issuer);
-180 }
-181
-182 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-183 public int getDefaultHelpPerPage() {
-184 verifyUnstableAPI("help");
-185 return defaultHelpPerPage;
-186 }
-187
-188 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-189 public void setDefaultHelpPerPage(int defaultHelpPerPage) {
-190 verifyUnstableAPI("help");
-191 this.defaultHelpPerPage = defaultHelpPerPage;
-192 }
-193 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-194 public void setHelpFormatter(CommandHelpFormatter helpFormatter) {
-195 this.helpFormatter = helpFormatter;
-196 }
-197
-198 /** @deprecated Unstable API */ @Deprecated @UnstableAPI
-199 public CommandHelpFormatter getHelpFormatter() {
-200 return helpFormatter;
-201 }
-202
-203 CommandRouter getRouter() {
-204 return router;
-205 }
-206
-207 /**
-208 * Registers a command with ACF
-209 *
-210 * @param command The command to register
-211 * @return boolean
+165 /**
+166 * @deprecated Unstable API
+167 */
+168 @Deprecated
+169 @UnstableAPI
+170 public CommandHelp generateCommandHelp(CommandIssuer issuer, @NotNull String command) {
+171 verifyUnstableAPI("help");
+172 return generateCommandHelp(issuer, obtainRootCommand(command));
+173 }
+174
+175 /**
+176 * @deprecated Unstable API
+177 */
+178 @Deprecated
+179 @UnstableAPI
+180 public CommandHelp generateCommandHelp() {
+181 verifyUnstableAPI("help");
+182 CommandOperationContext context = getCurrentCommandOperationContext();
+183 if (context == null) {
+184 throw new IllegalStateException("This method can only be called as part of a command execution.");
+185 }
+186 String commandLabel = context.getCommandLabel();
+187 return generateCommandHelp(context.getCommandIssuer(), this.obtainRootCommand(commandLabel));
+188 }
+189
+190 /**
+191 * @deprecated Unstable API
+192 */
+193 @Deprecated
+194 @UnstableAPI
+195 public CommandHelp generateCommandHelp(CommandIssuer issuer, RootCommand rootCommand) {
+196 verifyUnstableAPI("help");
+197 return new CommandHelp(this, rootCommand, issuer);
+198 }
+199
+200 /**
+201 * @deprecated Unstable API
+202 */
+203 @Deprecated
+204 @UnstableAPI
+205 public int getDefaultHelpPerPage() {
+206 verifyUnstableAPI("help");
+207 return defaultHelpPerPage;
+208 }
+209
+210 /**
+211 * @deprecated Unstable API
212 */
-213 public abstract void registerCommand(BaseCommand command);
-214 public abstract boolean hasRegisteredCommands();
-215 public abstract boolean isCommandIssuer(Class<?> type);
-216
-217 // TODO: Change this to IT if we make a breaking change
-218 public abstract I getCommandIssuer(Object issuer);
+213 @Deprecated
+214 @UnstableAPI
+215 public void setDefaultHelpPerPage(int defaultHelpPerPage) {
+216 verifyUnstableAPI("help");
+217 this.defaultHelpPerPage = defaultHelpPerPage;
+218 }
219
-220 public abstract RootCommand createRootCommand(String cmd);
-221
-222 /**
-223 * Returns a Locales Manager to add and modify language tables for your commands.
-224 * @return
-225 */
-226 public abstract Locales getLocales();
-227
-228 public boolean usingPerIssuerLocale() {
-229 return usePerIssuerLocale;
-230 }
-231
-232 public boolean usePerIssuerLocale(boolean setting) {
-233 boolean old = usePerIssuerLocale;
-234 usePerIssuerLocale = setting;
-235 return old;
+220 /**
+221 * @deprecated Unstable API
+222 */
+223 @Deprecated
+224 @UnstableAPI
+225 public void setHelpFormatter(CommandHelpFormatter helpFormatter) {
+226 this.helpFormatter = helpFormatter;
+227 }
+228
+229 /**
+230 * @deprecated Unstable API
+231 */
+232 @Deprecated
+233 @UnstableAPI
+234 public CommandHelpFormatter getHelpFormatter() {
+235 return helpFormatter;
236 }
237
-238 public ConditionContext createConditionContext(CommandIssuer issuer, String config) {
-239 //noinspection unchecked
-240 return new ConditionContext(issuer, config);
-241 }
-242
-243 public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
-244
-245 public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args);
-246
-247 public abstract void log(final LogLevel level, final String message, final Throwable throwable);
-248
-249 public void log(final LogLevel level, final String message) {
-250 log(level, message, null);
-251 }
-252
-253 /**
-254 * Lets you add custom string replacements that can be applied to annotation values,
-255 * to reduce duplication/repetition of common values such as permission nodes and command prefixes.
-256 *
-257 * Any replacement registered starts with a %
-258 *
-259 * So for ex @CommandPermission("%staff")
-260 * @return Replacements Manager
-261 */
-262 public CommandReplacements getCommandReplacements() {
-263 return replacements;
-264 }
+238 CommandRouter getRouter() {
+239 return router;
+240 }
+241
+242 /**
+243 * Registers a command with ACF
+244 *
+245 * @param command The command to register
+246 * @return boolean
+247 */
+248 public abstract void registerCommand(BaseCommand command);
+249
+250 public abstract boolean hasRegisteredCommands();
+251
+252 public abstract boolean isCommandIssuer(Class<?> type);
+253
+254 // TODO: Change this to IT if we make a breaking change
+255 public abstract I getCommandIssuer(Object issuer);
+256
+257 public abstract RootCommand createRootCommand(String cmd);
+258
+259 /**
+260 * Returns a Locales Manager to add and modify language tables for your commands.
+261 *
+262 * @return
+263 */
+264 public abstract Locales getLocales();
265
-266 public boolean hasPermission(CommandIssuer issuer, String permission) {
-267 if (permission == null || permission.isEmpty()) {
-268 return true;
-269 }
-270 for (String perm : ACFPatterns.COMMA.split(permission)) {
-271 if (!perm.isEmpty() && !issuer.hasPermission(perm)) {
-272 return false;
-273 }
-274 }
-275 return true;
-276 }
-277
-278 public synchronized RootCommand getRootCommand(@NotNull String cmd) {
-279 return rootCommands.get(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0]);
-280 }
-281
-282 public synchronized RootCommand obtainRootCommand(@NotNull String cmd) {
-283 return rootCommands.computeIfAbsent(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0], this::createRootCommand);
-284 }
-285
-286 public abstract Collection<RootCommand> getRegisteredRootCommands();
-287
-288 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
-289 return new RegisteredCommand(command, cmdName, method, prefSubCommand);
-290 }
-291
-292 /**
-293 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
-294 *
-295 * @param exceptionHandler the handler that should handle uncaught exceptions. May not be null if logExceptions is false
-296 */
-297 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler) {
-298 if (exceptionHandler == null && !this.logUnhandledExceptions) {
-299 throw new IllegalArgumentException("You may not disable the default exception handler and have logging of unhandled exceptions disabled");
-300 }
-301 defaultExceptionHandler = exceptionHandler;
-302 }
-303
-304 /**
-305 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered, and lets you control if ACF should also log the exception still.
-306 * <p>
-307 * If you disable logging, you need to log it yourself in your handler.
-308 *
-309 * @param exceptionHandler the handler that should handle uncaught exceptions. May not be null if logExceptions is false
-310 * @param logExceptions Whether or not to log exceptions.
-311 */
-312 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler, boolean logExceptions) {
-313 if (exceptionHandler == null && !logExceptions) {
-314 throw new IllegalArgumentException("You may not disable the default exception handler and have logging of unhandled exceptions disabled");
-315 }
-316 this.logUnhandledExceptions = logExceptions;
-317 this.defaultExceptionHandler = exceptionHandler;
-318 }
-319
-320 public boolean isLoggingUnhandledExceptions() {
-321 return this.logUnhandledExceptions;
-322 }
-323
-324 /**
-325 * Gets the current default exception handler, might be null.
-326 *
-327 * @return the default exception handler
-328 */
-329 public ExceptionHandler getDefaultExceptionHandler() {
-330 return defaultExceptionHandler;
-331 }
-332
-333 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) {
-334 if (t instanceof InvocationTargetException && t.getCause() != null) {
-335 t = t.getCause();
-336 }
-337 boolean result = false;
-338 if (scope.getExceptionHandler() != null) {
-339 result = scope.getExceptionHandler().execute(scope, registeredCommand, sender, args, t);
-340 } else if (defaultExceptionHandler != null) {
-341 result = defaultExceptionHandler.execute(scope, registeredCommand, sender, args, t);
-342 }
-343 return result;
-344 }
-345
-346 public void sendMessage(IT issuerArg, MessageType type, MessageKeyProvider key, String... replacements) {
-347 sendMessage(getCommandIssuer(issuerArg), type, key, replacements);
-348 }
-349
-350 public void sendMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
-351 String message = formatMessage(issuer, type, key, replacements);
-352
-353 for (String msg : ACFPatterns.NEWLINE.split(message)) {
-354 issuer.sendMessageInternal(ACFUtil.rtrim(msg));
-355 }
-356 }
-357
-358 public String formatMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
-359 String message = getLocales().getMessage(issuer, key.getMessageKey());
-360 if (replacements.length > 0) {
-361 message = ACFUtil.replaceStrings(message, replacements);
-362 }
-363
-364 message = getCommandReplacements().replace(message);
-365 message = getLocales().replaceI18NStrings(message);
-366
-367 MessageFormatter formatter = formatters.getOrDefault(type, defaultFormatter);
-368 if (formatter != null) {
-369 message = formatter.format(message);
-370 }
-371 return message;
-372 }
-373
-374 public void onLocaleChange(IssuerLocaleChangedCallback<I> onChange) {
-375 localeChangedCallbacks.add(onChange);
-376 }
-377
-378 public void notifyLocaleChange(I issuer, Locale oldLocale, Locale newLocale) {
-379 localeChangedCallbacks.forEach(cb -> {
-380 try {
-381 cb.onIssuerLocaleChange(issuer, oldLocale, newLocale);
-382 } catch (Exception e) {
-383 this.log(LogLevel.ERROR, "Error in notifyLocaleChange", e);
-384 }
-385 });
-386 }
-387
-388 public Locale setIssuerLocale(IT issuer, Locale locale) {
-389 I commandIssuer = getCommandIssuer(issuer);
-390
-391 Locale old = issuersLocale.put(commandIssuer.getUniqueId(), locale);
-392 if (!Objects.equals(old, locale)) {
-393 this.notifyLocaleChange(commandIssuer, old, locale);
-394 }
-395
-396 return old;
-397 }
-398
-399 public Locale getIssuerLocale(CommandIssuer issuer) {
-400 if (usingPerIssuerLocale() && issuer != null) {
-401 Locale locale = issuersLocale.get(issuer.getUniqueId());
-402 if (locale != null) {
-403 return locale;
-404 }
-405 }
-406
-407 return getLocales().getDefaultLocale();
-408 }
-409
-410 CommandOperationContext<I> createCommandOperationContext(BaseCommand command, CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
-411 //noinspection unchecked
-412 return new CommandOperationContext<>(
-413 this,
-414 (I) issuer,
-415 command,
-416 commandLabel,
-417 args,
-418 isAsync
-419 );
+266 public boolean usingPerIssuerLocale() {
+267 return usePerIssuerLocale;
+268 }
+269
+270 public boolean usePerIssuerLocale(boolean setting) {
+271 boolean old = usePerIssuerLocale;
+272 usePerIssuerLocale = setting;
+273 return old;
+274 }
+275
+276 public ConditionContext createConditionContext(CommandIssuer issuer, String config) {
+277 //noinspection unchecked
+278 return new ConditionContext(issuer, config);
+279 }
+280
+281 public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
+282
+283 public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args);
+284
+285 public abstract void log(final LogLevel level, final String message, final Throwable throwable);
+286
+287 public void log(final LogLevel level, final String message) {
+288 log(level, message, null);
+289 }
+290
+291 /**
+292 * Lets you add custom string replacements that can be applied to annotation values,
+293 * to reduce duplication/repetition of common values such as permission nodes and command prefixes.
+294 * <p>
+295 * Any replacement registered starts with a %
+296 * <p>
+297 * So for ex @CommandPermission("%staff")
+298 *
+299 * @return Replacements Manager
+300 */
+301 public CommandReplacements getCommandReplacements() {
+302 return replacements;
+303 }
+304
+305 public boolean hasPermission(CommandIssuer issuer, Set<String> permissions) {
+306 for (String permission : permissions) {
+307 if (!hasPermission(issuer, permission)) {
+308 return false;
+309 }
+310 }
+311 return true;
+312 }
+313
+314 public boolean hasPermission(CommandIssuer issuer, String permission) {
+315 if (permission == null || permission.isEmpty()) {
+316 return true;
+317 }
+318 for (String perm : ACFPatterns.COMMA.split(permission)) {
+319 if (!perm.isEmpty() && !issuer.hasPermission(perm)) {
+320 return false;
+321 }
+322 }
+323 return true;
+324 }
+325
+326 public synchronized RootCommand getRootCommand(@NotNull String cmd) {
+327 return rootCommands.get(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0]);
+328 }
+329
+330 public synchronized RootCommand obtainRootCommand(@NotNull String cmd) {
+331 return rootCommands.computeIfAbsent(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0], this::createRootCommand);
+332 }
+333
+334 public abstract Collection<RootCommand> getRegisteredRootCommands();
+335
+336 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
+337 return new RegisteredCommand(command, cmdName, method, prefSubCommand);
+338 }
+339
+340 /**
+341 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.
+342 *
+343 * @param exceptionHandler the handler that should handle uncaught exceptions. May not be null if logExceptions is false
+344 */
+345 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler) {
+346 if (exceptionHandler == null && !this.logUnhandledExceptions) {
+347 throw new IllegalArgumentException("You may not disable the default exception handler and have logging of unhandled exceptions disabled");
+348 }
+349 defaultExceptionHandler = exceptionHandler;
+350 }
+351
+352 /**
+353 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered, and lets you control if ACF should also log the exception still.
+354 * <p>
+355 * If you disable logging, you need to log it yourself in your handler.
+356 *
+357 * @param exceptionHandler the handler that should handle uncaught exceptions. May not be null if logExceptions is false
+358 * @param logExceptions Whether or not to log exceptions.
+359 */
+360 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler, boolean logExceptions) {
+361 if (exceptionHandler == null && !logExceptions) {
+362 throw new IllegalArgumentException("You may not disable the default exception handler and have logging of unhandled exceptions disabled");
+363 }
+364 this.logUnhandledExceptions = logExceptions;
+365 this.defaultExceptionHandler = exceptionHandler;
+366 }
+367
+368 public boolean isLoggingUnhandledExceptions() {
+369 return this.logUnhandledExceptions;
+370 }
+371
+372 /**
+373 * Gets the current default exception handler, might be null.
+374 *
+375 * @return the default exception handler
+376 */
+377 public ExceptionHandler getDefaultExceptionHandler() {
+378 return defaultExceptionHandler;
+379 }
+380
+381 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) {
+382 if (t instanceof InvocationTargetException && t.getCause() != null) {
+383 t = t.getCause();
+384 }
+385 boolean result = false;
+386 if (scope.getExceptionHandler() != null) {
+387 result = scope.getExceptionHandler().execute(scope, registeredCommand, sender, args, t);
+388 } else if (defaultExceptionHandler != null) {
+389 result = defaultExceptionHandler.execute(scope, registeredCommand, sender, args, t);
+390 }
+391 return result;
+392 }
+393
+394 public void sendMessage(IT issuerArg, MessageType type, MessageKeyProvider key, String... replacements) {
+395 sendMessage(getCommandIssuer(issuerArg), type, key, replacements);
+396 }
+397
+398 public void sendMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
+399 String message = formatMessage(issuer, type, key, replacements);
+400
+401 for (String msg : ACFPatterns.NEWLINE.split(message)) {
+402 issuer.sendMessageInternal(ACFUtil.rtrim(msg));
+403 }
+404 }
+405
+406 public String formatMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) {
+407 String message = getLocales().getMessage(issuer, key.getMessageKey());
+408 if (replacements.length > 0) {
+409 message = ACFUtil.replaceStrings(message, replacements);
+410 }
+411
+412 message = getCommandReplacements().replace(message);
+413 message = getLocales().replaceI18NStrings(message);
+414
+415 MessageFormatter formatter = formatters.getOrDefault(type, defaultFormatter);
+416 if (formatter != null) {
+417 message = formatter.format(message);
+418 }
+419 return message;
420 }
421
-422 /**
-423 * Gets a list of all currently supported languages for this manager.
-424 * These locales will be automatically loaded from
-425 * @return
-426 */
-427 public Set<Locale> getSupportedLanguages() {
-428 return supportedLanguages;
-429 }
-430
-431 /**
-432 * Adds a new locale to the list of automatic Locales to load Message Bundles for.
-433 * All bundles loaded under the previous supported languages will now automatically load for this new locale too.
-434 *
-435 * @param locale
-436 */
-437 public void addSupportedLanguage(Locale locale) {
-438 supportedLanguages.add(locale);
-439 getLocales().loadMissingBundles();
-440 }
-441
-442 /**
-443 * Registers an instance of a class to be registered as an injectable dependency.<br>
-444 * The command manager will attempt to inject all fields in a command class that are annotated with
-445 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
-446 *
-447 * @param clazz the class the injector should look for when injecting
-448 * @param instance the instance of the class that should be injected
-449 * @throws IllegalStateException when there is already an instance for the provided class registered
-450 */
-451 public <T> void registerDependency(Class<? extends T> clazz, T instance){
-452 registerDependency(clazz, clazz.getName(), instance);
-453 }
+422 public void onLocaleChange(IssuerLocaleChangedCallback<I> onChange) {
+423 localeChangedCallbacks.add(onChange);
+424 }
+425
+426 public void notifyLocaleChange(I issuer, Locale oldLocale, Locale newLocale) {
+427 localeChangedCallbacks.forEach(cb -> {
+428 try {
+429 cb.onIssuerLocaleChange(issuer, oldLocale, newLocale);
+430 } catch (Exception e) {
+431 this.log(LogLevel.ERROR, "Error in notifyLocaleChange", e);
+432 }
+433 });
+434 }
+435
+436 public Locale setIssuerLocale(IT issuer, Locale locale) {
+437 I commandIssuer = getCommandIssuer(issuer);
+438
+439 Locale old = issuersLocale.put(commandIssuer.getUniqueId(), locale);
+440 if (!Objects.equals(old, locale)) {
+441 this.notifyLocaleChange(commandIssuer, old, locale);
+442 }
+443
+444 return old;
+445 }
+446
+447 public Locale getIssuerLocale(CommandIssuer issuer) {
+448 if (usingPerIssuerLocale() && issuer != null) {
+449 Locale locale = issuersLocale.get(issuer.getUniqueId());
+450 if (locale != null) {
+451 return locale;
+452 }
+453 }
454
-455 /**
-456 * Registers an instance of a class to be registered as an injectable dependency.<br>
-457 * The command manager will attempt to inject all fields in a command class that are annotated with
-458 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
-459 *
-460 * @param clazz the class the injector should look for when injecting
-461 * @param key the key which needs to be present if that
-462 * @param instance the instance of the class that should be injected
-463 * @throws IllegalStateException when there is already an instance for the provided class registered
-464 */
-465 public <T> void registerDependency(Class<? extends T> clazz, String key, T instance){
-466 if (dependencies.containsKey(clazz, key)) {
-467 throw new IllegalStateException("There is already an instance of " + clazz.getName() + " with the key " + key + " registered!");
-468 }
+455 return getLocales().getDefaultLocale();
+456 }
+457
+458 CommandOperationContext<I> createCommandOperationContext(BaseCommand command, CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
+459 //noinspection unchecked
+460 return new CommandOperationContext<>(
+461 this,
+462 (I) issuer,
+463 command,
+464 commandLabel,
+465 args,
+466 isAsync
+467 );
+468 }
469
-470 dependencies.put(clazz, key, instance);
-471 }
-472
-473 /**
-474 * Attempts to inject instances of classes registered with {@link CommandManager#registerDependency(Class, Object)}
-475 * into all fields of the class and its superclasses that are marked with {@link Dependency}.
-476 *
-477 * @param baseCommand the instance which fields should be filled
-478 */
-479 void injectDependencies(BaseCommand baseCommand) {
-480 Class clazz = baseCommand.getClass();
-481 do {
-482 for (Field field : clazz.getDeclaredFields()) {
-483 if (annotations.hasAnnotation(field, Dependency.class)) {
-484 String dependency = annotations.getAnnotationValue(field, Dependency.class);
-485 String key = (key = dependency).isEmpty() ? field.getType().getName() : key;
-486 Object object = dependencies.row(field.getType()).get(key);
-487 if (object == null) {
-488 throw new UnresolvedDependencyException("Could not find a registered instance of " +
-489 field.getType().getName() + " with key " + key + " for field " + field.getName() +
-490 " in class " + baseCommand.getClass().getName());
-491 }
-492
-493 try {
-494 boolean accessible = field.isAccessible();
-495 if (!accessible) {
-496 field.setAccessible(true);
-497 }
-498 field.set(baseCommand, object);
-499 field.setAccessible(accessible);
-500 } catch (IllegalAccessException e) {
-501 e.printStackTrace(); //TODO should we print our own exception here to make a more descriptive error?
-502 }
-503 }
-504 }
-505 clazz = clazz.getSuperclass();
-506 } while (!clazz.equals(BaseCommand.class));
-507 }
-508
-509 /**
-510 * @deprecated Use this with caution! If you enable and use Unstable API's, your next compile using ACF
-511 * may require you to update your implementation to those unstable API's
-512 */
-513 @Deprecated
-514 public void enableUnstableAPI(String api) {
-515 unstableAPIs.add(api);
-516 }
-517 void verifyUnstableAPI(String api) {
-518 if (!unstableAPIs.contains(api)) {
-519 throw new IllegalStateException("Using an unstable API that has not been enabled ( " + api + "). See https://acfunstable.emc.gs");
-520 }
-521 }
-522
-523 boolean hasUnstableAPI(String api) {
-524 return unstableAPIs.contains(api);
-525 }
-526
-527 Annotations getAnnotations() {
-528 return annotations;
-529 }
-530
-531 public String getCommandPrefix(CommandIssuer issuer) {
-532 return "";
-533 }
-534}
+470 /**
+471 * Gets a list of all currently supported languages for this manager.
+472 * These locales will be automatically loaded from
+473 *
+474 * @return
+475 */
+476 public Set<Locale> getSupportedLanguages() {
+477 return supportedLanguages;
+478 }
+479
+480 /**
+481 * Adds a new locale to the list of automatic Locales to load Message Bundles for.
+482 * All bundles loaded under the previous supported languages will now automatically load for this new locale too.
+483 *
+484 * @param locale
+485 */
+486 public void addSupportedLanguage(Locale locale) {
+487 supportedLanguages.add(locale);
+488 getLocales().loadMissingBundles();
+489 }
+490
+491 /**
+492 * Registers an instance of a class to be registered as an injectable dependency.<br>
+493 * The command manager will attempt to inject all fields in a command class that are annotated with
+494 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
+495 *
+496 * @param clazz the class the injector should look for when injecting
+497 * @param instance the instance of the class that should be injected
+498 * @throws IllegalStateException when there is already an instance for the provided class registered
+499 */
+500 public <T> void registerDependency(Class<? extends T> clazz, T instance) {
+501 registerDependency(clazz, clazz.getName(), instance);
+502 }
+503
+504 /**
+505 * Registers an instance of a class to be registered as an injectable dependency.<br>
+506 * The command manager will attempt to inject all fields in a command class that are annotated with
+507 * {@link co.aikar.commands.annotation.Dependency} with the provided instance.
+508 *
+509 * @param clazz the class the injector should look for when injecting
+510 * @param key the key which needs to be present if that
+511 * @param instance the instance of the class that should be injected
+512 * @throws IllegalStateException when there is already an instance for the provided class registered
+513 */
+514 public <T> void registerDependency(Class<? extends T> clazz, String key, T instance) {
+515 if (dependencies.containsKey(clazz, key)) {
+516 throw new IllegalStateException("There is already an instance of " + clazz.getName() + " with the key " + key + " registered!");
+517 }
+518
+519 dependencies.put(clazz, key, instance);
+520 }
+521
+522 /**
+523 * Attempts to inject instances of classes registered with {@link CommandManager#registerDependency(Class, Object)}
+524 * into all fields of the class and its superclasses that are marked with {@link Dependency}.
+525 *
+526 * @param baseCommand the instance which fields should be filled
+527 */
+528 void injectDependencies(BaseCommand baseCommand) {
+529 Class clazz = baseCommand.getClass();
+530 do {
+531 for (Field field : clazz.getDeclaredFields()) {
+532 if (annotations.hasAnnotation(field, Dependency.class)) {
+533 String dependency = annotations.getAnnotationValue(field, Dependency.class);
+534 String key = (key = dependency).isEmpty() ? field.getType().getName() : key;
+535 Object object = dependencies.row(field.getType()).get(key);
+536 if (object == null) {
+537 throw new UnresolvedDependencyException("Could not find a registered instance of " +
+538 field.getType().getName() + " with key " + key + " for field " + field.getName() +
+539 " in class " + baseCommand.getClass().getName());
+540 }
+541
+542 try {
+543 boolean accessible = field.isAccessible();
+544 if (!accessible) {
+545 field.setAccessible(true);
+546 }
+547 field.set(baseCommand, object);
+548 field.setAccessible(accessible);
+549 } catch (IllegalAccessException e) {
+550 e.printStackTrace(); //TODO should we print our own exception here to make a more descriptive error?
+551 }
+552 }
+553 }
+554 clazz = clazz.getSuperclass();
+555 } while (!clazz.equals(BaseCommand.class));
+556 }
+557
+558 /**
+559 * @deprecated Use this with caution! If you enable and use Unstable API's, your next compile using ACF
+560 * may require you to update your implementation to those unstable API's
+561 */
+562 @Deprecated
+563 public void enableUnstableAPI(String api) {
+564 unstableAPIs.add(api);
+565 }
+566
+567 void verifyUnstableAPI(String api) {
+568 if (!unstableAPIs.contains(api)) {
+569 throw new IllegalStateException("Using an unstable API that has not been enabled ( " + api + "). See https://acfunstable.emc.gs");
+570 }
+571 }
+572
+573 boolean hasUnstableAPI(String api) {
+574 return unstableAPIs.contains(api);
+575 }
+576
+577 Annotations getAnnotations() {
+578 return annotations;
+579 }
+580
+581 public String getCommandPrefix(CommandIssuer issuer) {
+582 return "";
+583 }
+584}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html b/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html
index 5852da15..3440a71d 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandOperationContext.html
@@ -32,83 +32,86 @@
024package co.aikar.commands;
025
026import java.lang.annotation.Annotation;
-027
-028/**
-029 * Holds information about the currently executing command on this thread
-030 */
-031public class CommandOperationContext <I extends CommandIssuer> {
-032
-033 private final CommandManager manager;
-034 private final I issuer;
-035 private final BaseCommand command;
-036 private final String commandLabel;
-037 private final String[] args;
-038 private final boolean isAsync;
-039 private RegisteredCommand registeredCommand;
-040
-041 CommandOperationContext(CommandManager manager, I issuer, BaseCommand command, String commandLabel, String[] args, boolean isAsync) {
-042 this.manager = manager;
-043 this.issuer = issuer;
-044 this.command = command;
-045 this.commandLabel = commandLabel;
-046 this.args = args;
-047 this.isAsync = isAsync;
-048 }
-049
-050 public CommandManager getCommandManager() {
-051 return manager;
-052 }
-053
-054 public I getCommandIssuer() {
-055 return issuer;
-056 }
-057
-058 public BaseCommand getCommand() {
-059 return command;
-060 }
-061
-062 public String getCommandLabel() {
-063 return commandLabel;
-064 }
-065
-066 public String[] getArgs() {
-067 return args;
-068 }
-069
-070 public boolean isAsync() {
-071 return isAsync;
-072 }
-073
-074 public void setRegisteredCommand(RegisteredCommand registeredCommand) {
-075 this.registeredCommand = registeredCommand;
-076 }
-077
-078 public RegisteredCommand getRegisteredCommand() {
-079 return registeredCommand;
-080 }
-081
-082 /**
-083 * This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
-084 * @deprecated Use {@link #getAnnotationValue(Class)}
-085 */
-086 @Deprecated
-087 public <T extends Annotation> T getAnnotation(Class<T> anno) {
-088 return registeredCommand.method.getAnnotation(anno);
-089 }
-090
-091 public <T extends Annotation> String getAnnotationValue(Class<T> cls) {
-092 return manager.getAnnotations().getAnnotationValue(registeredCommand.method, cls);
-093 }
-094
-095 public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options) {
-096 return manager.getAnnotations().getAnnotationValue(registeredCommand.method, cls, options);
-097 }
-098
-099 public boolean hasAnnotation(Class<? extends Annotation> anno) {
-100 return getAnnotation(anno) != null;
-101 }
-102
-103}
+027import java.util.List;
+028
+029/**
+030 * Holds information about the currently executing command on this thread
+031 */
+032public class CommandOperationContext<I extends CommandIssuer> {
+033
+034 private final CommandManager manager;
+035 private final I issuer;
+036 private final BaseCommand command;
+037 private final String commandLabel;
+038 private final String[] args;
+039 private final boolean isAsync;
+040 private RegisteredCommand registeredCommand;
+041 List<String> enumCompletionValues;
+042
+043 CommandOperationContext(CommandManager manager, I issuer, BaseCommand command, String commandLabel, String[] args, boolean isAsync) {
+044 this.manager = manager;
+045 this.issuer = issuer;
+046 this.command = command;
+047 this.commandLabel = commandLabel;
+048 this.args = args;
+049 this.isAsync = isAsync;
+050 }
+051
+052 public CommandManager getCommandManager() {
+053 return manager;
+054 }
+055
+056 public I getCommandIssuer() {
+057 return issuer;
+058 }
+059
+060 public BaseCommand getCommand() {
+061 return command;
+062 }
+063
+064 public String getCommandLabel() {
+065 return commandLabel;
+066 }
+067
+068 public String[] getArgs() {
+069 return args;
+070 }
+071
+072 public boolean isAsync() {
+073 return isAsync;
+074 }
+075
+076 public void setRegisteredCommand(RegisteredCommand registeredCommand) {
+077 this.registeredCommand = registeredCommand;
+078 }
+079
+080 public RegisteredCommand getRegisteredCommand() {
+081 return registeredCommand;
+082 }
+083
+084 /**
+085 * This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
+086 *
+087 * @deprecated Use {@link #getAnnotationValue(Class)}
+088 */
+089 @Deprecated
+090 public <T extends Annotation> T getAnnotation(Class<T> anno) {
+091 return registeredCommand.method.getAnnotation(anno);
+092 }
+093
+094 public <T extends Annotation> String getAnnotationValue(Class<T> cls) {
+095 return manager.getAnnotations().getAnnotationValue(registeredCommand.method, cls);
+096 }
+097
+098 public <T extends Annotation> String getAnnotationValue(Class<T> cls, int options) {
+099 return manager.getAnnotations().getAnnotationValue(registeredCommand.method, cls, options);
+100 }
+101
+102 public boolean hasAnnotation(Class<? extends Annotation> anno) {
+103 return getAnnotation(anno) != null;
+104 }
+105
+106}
diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html b/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html
index 362a6689..795614ab 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandParameter.html
@@ -31,240 +31,259 @@
023
024package co.aikar.commands;
025
-026import co.aikar.commands.annotation.Conditions;
-027import co.aikar.commands.annotation.Default;
-028import co.aikar.commands.annotation.Description;
-029import co.aikar.commands.annotation.Flags;
-030import co.aikar.commands.annotation.Optional;
-031import co.aikar.commands.annotation.Single;
-032import co.aikar.commands.annotation.Syntax;
-033import co.aikar.commands.annotation.Values;
-034import co.aikar.commands.contexts.ContextResolver;
-035import co.aikar.commands.contexts.IssuerAwareContextResolver;
-036import co.aikar.commands.contexts.IssuerOnlyContextResolver;
-037import co.aikar.commands.contexts.OptionalContextResolver;
-038
-039import java.lang.reflect.Parameter;
-040import java.util.HashMap;
-041import java.util.Map;
-042
-043public class CommandParameter <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
-044 private final Parameter parameter;
-045 private final Class<?> type;
-046 private final String name;
-047 private final CommandManager manager;
-048 private final int paramIndex;
-049
-050 private ContextResolver<?, CEC> resolver;
-051 private boolean optional;
-052 private String description;
-053 private String defaultValue;
-054 private String syntax;
-055 private String conditions;
-056 private boolean requiresInput;
-057 private boolean commandIssuer;
-058 private String[] values;
-059 private Map<String, String> flags;
-060 private boolean canConsumeInput;
-061 private boolean optionalResolver;
-062 boolean consumesRest;
-063
-064 public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex, boolean isLast) {
-065 this.parameter = param;
-066 this.type = param.getType();
-067 this.name = param.getName(); // do we care for an annotation to supply name?
-068 this.manager = command.manager;
-069 this.paramIndex = paramIndex;
-070 Annotations annotations = manager.getAnnotations();
-071
-072 this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0));
-073 this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
-074 this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-075
-076 //noinspection unchecked
-077 this.resolver = manager.getCommandContexts().getResolver(type);
-078 if (this.resolver == null) {
-079 ACFUtil.sneaky(new InvalidCommandContextException(
-080 "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
-081 ));
-082 }
-083
-084 this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null || (isLast && type == String[].class);
-085 this.optionalResolver = isOptionalResolver(resolver);
-086 this.requiresInput = !this.optional && !this.optionalResolver;
-087 //noinspection unchecked
-088 this.commandIssuer = paramIndex == 0 && manager.isCommandIssuer(type);
-089 this.canConsumeInput = !this.commandIssuer && !(resolver instanceof IssuerOnlyContextResolver);
-090 this.consumesRest = (type == String.class && !annotations.hasAnnotation(param, Single.class)) || (isLast && type == String[].class);
-091
-092 this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-093
-094 this.syntax = null;
-095 if (!commandIssuer) {
-096 this.syntax = annotations.getAnnotationValue(param, Syntax.class);
-097 if (syntax == null) {
-098 if (!requiresInput && canConsumeInput) {
-099 this.syntax = "[" + name + "]";
-100 } else if (requiresInput) {
-101 this.syntax = "<" + name + ">";
-102 }
-103 }
-104 }
-105
-106 this.flags = new HashMap<>();
-107 String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-108 if (flags != null) {
-109 parseFlags(flags);
-110 }
-111 inheritContextFlags(command.scope);
-112 }
-113
-114 private void inheritContextFlags(BaseCommand scope) {
-115 if (!scope.contextFlags.isEmpty()) {
-116 Class<?> pCls = this.type;
-117 do {
-118 parseFlags(scope.contextFlags.get(pCls));
-119 } while ((pCls = pCls.getSuperclass()) != null);
-120 }
-121 if (scope.parentCommand != null) {
-122 inheritContextFlags(scope.parentCommand);
-123 }
-124 }
-125
-126 private void parseFlags(String flags) {
-127 if (flags != null) {
-128 for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
-129 String[] v = ACFPatterns.EQUALS.split(s, 2);
-130 if (!this.flags.containsKey(v[0])) {
-131 this.flags.put(v[0], v.length > 1 ? v[1] : null);
-132 }
-133 }
-134 }
-135 }
-136
-137 private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
-138 return resolver instanceof IssuerAwareContextResolver
-139 || resolver instanceof IssuerOnlyContextResolver
-140 || resolver instanceof OptionalContextResolver;
-141 }
-142
-143
-144 public Parameter getParameter() {
-145 return parameter;
-146 }
-147
-148 public Class<?> getType() {
-149 return type;
+026import co.aikar.commands.annotation.CommandPermission;
+027import co.aikar.commands.annotation.Conditions;
+028import co.aikar.commands.annotation.Default;
+029import co.aikar.commands.annotation.Description;
+030import co.aikar.commands.annotation.Flags;
+031import co.aikar.commands.annotation.Optional;
+032import co.aikar.commands.annotation.Single;
+033import co.aikar.commands.annotation.Syntax;
+034import co.aikar.commands.annotation.Values;
+035import co.aikar.commands.contexts.ContextResolver;
+036import co.aikar.commands.contexts.IssuerAwareContextResolver;
+037import co.aikar.commands.contexts.IssuerOnlyContextResolver;
+038import co.aikar.commands.contexts.OptionalContextResolver;
+039
+040import java.lang.reflect.Parameter;
+041import java.util.Arrays;
+042import java.util.HashMap;
+043import java.util.HashSet;
+044import java.util.Map;
+045import java.util.Set;
+046
+047public class CommandParameter<CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
+048 private final Parameter parameter;
+049 private final Class<?> type;
+050 private final String name;
+051 private final CommandManager manager;
+052 private final int paramIndex;
+053
+054 private ContextResolver<?, CEC> resolver;
+055 private boolean optional;
+056 private Set<String> permissions = new HashSet<>();
+057 private String permission;
+058 private String description;
+059 private String defaultValue;
+060 private String syntax;
+061 private String conditions;
+062 private boolean requiresInput;
+063 private boolean commandIssuer;
+064 private String[] values;
+065 private Map<String, String> flags;
+066 private boolean canConsumeInput;
+067 private boolean optionalResolver;
+068 boolean consumesRest;
+069
+070 public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex, boolean isLast) {
+071 this.parameter = param;
+072 this.type = param.getType();
+073 this.name = param.getName(); // do we care for an annotation to supply name?
+074 this.manager = command.manager;
+075 this.paramIndex = paramIndex;
+076 Annotations annotations = manager.getAnnotations();
+077
+078 this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0));
+079 this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
+080 this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+081
+082 //noinspection unchecked
+083 this.resolver = manager.getCommandContexts().getResolver(type);
+084 if (this.resolver == null) {
+085 ACFUtil.sneaky(new InvalidCommandContextException(
+086 "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver"
+087 ));
+088 }
+089
+090 this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null || (isLast && type == String[].class);
+091 this.permission = annotations.getAnnotationValue(param, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+092 this.optionalResolver = isOptionalResolver(resolver);
+093 this.requiresInput = !this.optional && !this.optionalResolver;
+094 //noinspection unchecked
+095 this.commandIssuer = paramIndex == 0 && manager.isCommandIssuer(type);
+096 this.canConsumeInput = !this.commandIssuer && !(resolver instanceof IssuerOnlyContextResolver);
+097 this.consumesRest = (type == String.class && !annotations.hasAnnotation(param, Single.class)) || (isLast && type == String[].class);
+098
+099 this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+100
+101 this.syntax = null;
+102 if (!commandIssuer) {
+103 this.syntax = annotations.getAnnotationValue(param, Syntax.class);
+104 if (syntax == null) {
+105 if (!requiresInput && canConsumeInput) {
+106 this.syntax = "[" + name + "]";
+107 } else if (requiresInput) {
+108 this.syntax = "<" + name + ">";
+109 }
+110 }
+111 }
+112
+113 this.flags = new HashMap<>();
+114 String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
+115 if (flags != null) {
+116 parseFlags(flags);
+117 }
+118 inheritContextFlags(command.scope);
+119 this.computePermissions();
+120 }
+121
+122 private void inheritContextFlags(BaseCommand scope) {
+123 if (!scope.contextFlags.isEmpty()) {
+124 Class<?> pCls = this.type;
+125 do {
+126 parseFlags(scope.contextFlags.get(pCls));
+127 } while ((pCls = pCls.getSuperclass()) != null);
+128 }
+129 if (scope.parentCommand != null) {
+130 inheritContextFlags(scope.parentCommand);
+131 }
+132 }
+133
+134 private void parseFlags(String flags) {
+135 if (flags != null) {
+136 for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) {
+137 String[] v = ACFPatterns.EQUALS.split(s, 2);
+138 if (!this.flags.containsKey(v[0])) {
+139 this.flags.put(v[0], v.length > 1 ? v[1] : null);
+140 }
+141 }
+142 }
+143 }
+144
+145 private void computePermissions() {
+146 this.permissions.clear();
+147 if (this.permission != null && !this.permission.isEmpty()) {
+148 this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
+149 }
150 }
151
-152 public String getName() {
-153 return name;
-154 }
-155
-156 public CommandManager getManager() {
-157 return manager;
-158 }
-159
-160 public int getParamIndex() {
-161 return paramIndex;
-162 }
-163
-164 public ContextResolver<?, CEC> getResolver() {
-165 return resolver;
-166 }
-167
-168 public void setResolver(ContextResolver<?, CEC> resolver) {
-169 this.resolver = resolver;
-170 }
-171
-172 public boolean isOptional() {
-173 return optional;
-174 }
-175
-176 public void setOptional(boolean optional) {
-177 this.optional = optional;
-178 }
-179
-180 public String getDescription() {
-181 return description;
-182 }
-183
-184 public void setDescription(String description) {
-185 this.description = description;
-186 }
-187
-188 public String getDefaultValue() {
-189 return defaultValue;
-190 }
-191
-192 public void setDefaultValue(String defaultValue) {
-193 this.defaultValue = defaultValue;
-194 }
-195
-196 public boolean isCommandIssuer() {
-197 return commandIssuer;
-198 }
-199
-200 public void setCommandIssuer(boolean commandIssuer) {
-201 this.commandIssuer = commandIssuer;
-202 }
-203
-204 public String[] getValues() {
-205 return values;
-206 }
-207
-208 public void setValues(String[] values) {
-209 this.values = values;
-210 }
-211
-212 public Map<String, String> getFlags() {
-213 return flags;
-214 }
-215
-216 public void setFlags(Map<String, String> flags) {
-217 this.flags = flags;
-218 }
-219
-220 public boolean canConsumeInput() {
-221 return canConsumeInput;
-222 }
-223
-224 public void setCanConsumeInput(boolean canConsumeInput) {
-225 this.canConsumeInput = canConsumeInput;
-226 }
-227
-228 public void setOptionalResolver(boolean optionalResolver) {
-229 this.optionalResolver = optionalResolver;
-230 }
-231
-232 public boolean isOptionalResolver() {
-233 return optionalResolver;
-234 }
-235
-236 public boolean requiresInput() {
-237 return requiresInput;
-238 }
-239
-240 public void setRequiresInput(boolean requiresInput) {
-241 this.requiresInput = requiresInput;
-242 }
-243
-244 public String getSyntax() {
-245 return syntax;
-246 }
-247
-248 public void setSyntax(String syntax) {
-249 this.syntax = syntax;
-250 }
-251
-252 public String getConditions() {
-253 return conditions;
-254 }
-255
-256 public void setConditions(String conditions) {
-257 this.conditions = conditions;
-258 }
-259}
+152 private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) {
+153 return resolver instanceof IssuerAwareContextResolver
+154 || resolver instanceof IssuerOnlyContextResolver
+155 || resolver instanceof OptionalContextResolver;
+156 }
+157
+158
+159 public Parameter getParameter() {
+160 return parameter;
+161 }
+162
+163 public Class<?> getType() {
+164 return type;
+165 }
+166
+167 public String getName() {
+168 return name;
+169 }
+170
+171 public CommandManager getManager() {
+172 return manager;
+173 }
+174
+175 public int getParamIndex() {
+176 return paramIndex;
+177 }
+178
+179 public ContextResolver<?, CEC> getResolver() {
+180 return resolver;
+181 }
+182
+183 public void setResolver(ContextResolver<?, CEC> resolver) {
+184 this.resolver = resolver;
+185 }
+186
+187 public boolean isOptional() {
+188 return optional;
+189 }
+190
+191 public void setOptional(boolean optional) {
+192 this.optional = optional;
+193 }
+194
+195 public String getDescription() {
+196 return description;
+197 }
+198
+199 public void setDescription(String description) {
+200 this.description = description;
+201 }
+202
+203 public String getDefaultValue() {
+204 return defaultValue;
+205 }
+206
+207 public void setDefaultValue(String defaultValue) {
+208 this.defaultValue = defaultValue;
+209 }
+210
+211 public boolean isCommandIssuer() {
+212 return commandIssuer;
+213 }
+214
+215 public void setCommandIssuer(boolean commandIssuer) {
+216 this.commandIssuer = commandIssuer;
+217 }
+218
+219 public String[] getValues() {
+220 return values;
+221 }
+222
+223 public void setValues(String[] values) {
+224 this.values = values;
+225 }
+226
+227 public Map<String, String> getFlags() {
+228 return flags;
+229 }
+230
+231 public void setFlags(Map<String, String> flags) {
+232 this.flags = flags;
+233 }
+234
+235 public boolean canConsumeInput() {
+236 return canConsumeInput;
+237 }
+238
+239 public void setCanConsumeInput(boolean canConsumeInput) {
+240 this.canConsumeInput = canConsumeInput;
+241 }
+242
+243 public void setOptionalResolver(boolean optionalResolver) {
+244 this.optionalResolver = optionalResolver;
+245 }
+246
+247 public boolean isOptionalResolver() {
+248 return optionalResolver;
+249 }
+250
+251 public boolean requiresInput() {
+252 return requiresInput;
+253 }
+254
+255 public void setRequiresInput(boolean requiresInput) {
+256 this.requiresInput = requiresInput;
+257 }
+258
+259 public String getSyntax() {
+260 return syntax;
+261 }
+262
+263 public void setSyntax(String syntax) {
+264 this.syntax = syntax;
+265 }
+266
+267 public String getConditions() {
+268 return conditions;
+269 }
+270
+271 public void setConditions(String conditions) {
+272 this.conditions = conditions;
+273 }
+274
+275 public Set<String> getRequiredPermissions() {
+276 return permissions;
+277 }
+278}
diff --git a/docs/acf-core/src-html/co/aikar/commands/MessageKeys.html b/docs/acf-core/src-html/co/aikar/commands/MessageKeys.html
index 5dc53266..b337aad8 100644
--- a/docs/acf-core/src-html/co/aikar/commands/MessageKeys.html
+++ b/docs/acf-core/src-html/co/aikar/commands/MessageKeys.html
@@ -40,36 +40,38 @@
032@SuppressWarnings("WeakerAccess")
033public enum MessageKeys implements MessageKeyProvider {
034 PERMISSION_DENIED,
-035 ERROR_GENERIC_LOGGED,
-036 UNKNOWN_COMMAND,
-037 INVALID_SYNTAX,
-038 ERROR_PREFIX,
-039 ERROR_PERFORMING_COMMAND,
-040 INFO_MESSAGE,
-041 PLEASE_SPECIFY_ONE_OF,
-042 MUST_BE_A_NUMBER,
-043 MUST_BE_MIN_LENGTH,
-044 MUST_BE_MAX_LENGTH,
-045 PLEASE_SPECIFY_AT_LEAST,
-046 PLEASE_SPECIFY_AT_MOST,
-047 NOT_ALLOWED_ON_CONSOLE,
-048 COULD_NOT_FIND_PLAYER,
-049 NO_COMMAND_MATCHED_SEARCH,
-050 HELP_PAGE_INFORMATION,
-051 HELP_NO_RESULTS,
-052 HELP_HEADER,
-053 HELP_FORMAT,
-054 HELP_DETAILED_HEADER,
-055 HELP_DETAILED_COMMAND_FORMAT,
-056 HELP_DETAILED_PARAMETER_FORMAT,
-057 HELP_SEARCH_HEADER,
-058 ;
-059
-060 private final MessageKey key = MessageKey.of("acf-core." + this.name().toLowerCase());
-061 public MessageKey getMessageKey() {
-062 return key;
-063 }
-064}
+035 PERMISSION_DENIED_PARAMETER,
+036 ERROR_GENERIC_LOGGED,
+037 UNKNOWN_COMMAND,
+038 INVALID_SYNTAX,
+039 ERROR_PREFIX,
+040 ERROR_PERFORMING_COMMAND,
+041 INFO_MESSAGE,
+042 PLEASE_SPECIFY_ONE_OF,
+043 MUST_BE_A_NUMBER,
+044 MUST_BE_MIN_LENGTH,
+045 MUST_BE_MAX_LENGTH,
+046 PLEASE_SPECIFY_AT_LEAST,
+047 PLEASE_SPECIFY_AT_MOST,
+048 NOT_ALLOWED_ON_CONSOLE,
+049 COULD_NOT_FIND_PLAYER,
+050 NO_COMMAND_MATCHED_SEARCH,
+051 HELP_PAGE_INFORMATION,
+052 HELP_NO_RESULTS,
+053 HELP_HEADER,
+054 HELP_FORMAT,
+055 HELP_DETAILED_HEADER,
+056 HELP_DETAILED_COMMAND_FORMAT,
+057 HELP_DETAILED_PARAMETER_FORMAT,
+058 HELP_SEARCH_HEADER,
+059 ;
+060
+061 private final MessageKey key = MessageKey.of("acf-core." + this.name().toLowerCase());
+062
+063 public MessageKey getMessageKey() {
+064 return key;
+065 }
+066}
diff --git a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
index a049087d..20a17f21 100644
--- a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
@@ -96,7 +96,7 @@
088 this.prefSubCommand = prefSubCommand;
089
090 this.permission = annotations.getAnnotationValue(method, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
-091 this.complete = annotations.getAnnotationValue(method, CommandCompletion.class);
+091 this.complete = annotations.getAnnotationValue(method, CommandCompletion.class, Annotations.DEFAULT_EMPTY); // no replacements as it should be per-issuer
092 this.helpText = annotations.getAnnotationValue(method, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
093 this.conditions = annotations.getAnnotationValue(method, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
094 this.helpSearchTags = annotations.getAnnotationValue(method, HelpSearchTags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
@@ -234,117 +234,132 @@
226 if (requiresInput && remainingRequired > 0) {
227 remainingRequired--;
228 }
-229 if (args.isEmpty() && !(isLast && type == String[].class)) {
-230 if (allowOptional && parameter.getDefaultValue() != null) {
-231 args.add(parameter.getDefaultValue());
-232 } else if (allowOptional && parameter.isOptional()) {
-233 Object value = parameter.isOptionalResolver() ? resolver.getContext(context) : null;
-234 if (value == null && parameter.getClass().isPrimitive()) {
-235 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
-236 }
-237 //noinspection unchecked
-238 this.manager.conditions.validateConditions(context, value);
-239 passedArgs.put(parameterName, value);
-240 //noinspection UnnecessaryContinue
-241 continue;
-242 } else if (requiresInput) {
-243 scope.showSyntax(sender, this);
-244 return null;
-245 }
-246 }
-247 if (parameter.getValues() != null) {
-248 String arg = !args.isEmpty() ? args.get(0) : "";
-249
-250 Set<String> possible = new HashSet<>();
-251 CommandCompletions commandCompletions = this.manager.getCommandCompletions();
-252 for (String s : parameter.getValues()) {
-253 //noinspection unchecked
-254 List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
-255 if (!check.isEmpty()) {
-256 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
-257 } else {
-258 possible.add(s.toLowerCase());
-259 }
-260 }
+229
+230 Set<String> parameterPermissions = parameter.getRequiredPermissions();
+231 if (args.isEmpty() && !(isLast && type == String[].class)) {
+232 if (allowOptional && parameter.getDefaultValue() != null) {
+233 args.add(parameter.getDefaultValue());
+234 } else if (allowOptional && parameter.isOptional()) {
+235 if (!this.manager.hasPermission(sender, parameterPermissions)) {
+236 sender.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED_PARAMETER, "{param}", parameterName);
+237 throw new InvalidCommandArgument(false);
+238 }
+239 Object value = parameter.isOptionalResolver() ? resolver.getContext(context) : null;
+240
+241 if (value == null && parameter.getClass().isPrimitive()) {
+242 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
+243 }
+244 //noinspection unchecked
+245 this.manager.conditions.validateConditions(context, value);
+246 passedArgs.put(parameterName, value);
+247 continue;
+248 } else if (requiresInput) {
+249 scope.showSyntax(sender, this);
+250 return null;
+251 }
+252 } else {
+253 if (!this.manager.hasPermission(sender, parameterPermissions)) {
+254 sender.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED_PARAMETER, "{param}", parameterName);
+255 throw new InvalidCommandArgument(false);
+256 }
+257 }
+258
+259 if (parameter.getValues() != null) {
+260 String arg = !args.isEmpty() ? args.get(0) : "";
261
-262 if (!possible.contains(arg.toLowerCase())) {
-263 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
-264 "{valid}", ACFUtil.join(possible, ", "));
-265 }
-266 }
-267 Object paramValue = resolver.getContext(context);
-268 //noinspection unchecked
-269 this.manager.conditions.validateConditions(context, paramValue);
-270 passedArgs.put(parameterName, paramValue);
-271 }
-272 return passedArgs;
-273 }
-274
-275 boolean hasPermission(CommandIssuer issuer) {
-276 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
-277 }
-278
-279
-280 /**
-281 * @see #getRequiredPermissions()
-282 * @deprecated
-283 */
-284 @Deprecated
-285 public String getPermission() {
-286 if (this.permission == null || this.permission.isEmpty()) {
-287 return null;
-288 }
-289 return ACFPatterns.COMMA.split(this.permission)[0];
-290 }
-291
-292 private void computePermissions() {
-293 this.permissions.clear();
-294 this.permissions.addAll(this.scope.getRequiredPermissions());
-295 if (this.permission != null && !this.permission.isEmpty()) {
-296 this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
-297 }
-298 }
-299
-300 public Set<String> getRequiredPermissions() {
-301 return this.permissions;
-302 }
-303
-304 public boolean requiresPermission(String permission) {
-305 return getRequiredPermissions().contains(permission);
-306 }
-307
-308 public String getPrefSubCommand() {
-309 return prefSubCommand;
-310 }
-311
-312 public String getSyntaxText() {
-313 return syntaxText;
-314 }
-315
-316 public String getHelpText() {
-317 return helpText != null ? helpText : "";
-318 }
-319
-320 public boolean isPrivate() {
-321 return isPrivate;
-322 }
-323
-324 public String getCommand() {
-325 return command;
-326 }
-327
-328 public void addSubcommand(String cmd) {
-329 this.registeredSubcommands.add(cmd);
-330 }
-331
-332 public void addSubcommands(Collection<String> cmd) {
-333 this.registeredSubcommands.addAll(cmd);
-334 }
-335
-336 public <T extends Annotation> T getAnnotation(Class<T> annotation) {
-337 return method.getAnnotation(annotation);
-338 }
-339}
+262 Set<String> possible = new HashSet<>();
+263 CommandCompletions commandCompletions = this.manager.getCommandCompletions();
+264 for (String s : parameter.getValues()) {
+265 if ("*".equals(s) || "@completions".equals(s)) {
+266 s = commandCompletions.findDefaultCompletion(this, origArgs);
+267 }
+268 //noinspection unchecked
+269 List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
+270 if (!check.isEmpty()) {
+271 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
+272 } else {
+273 possible.add(s.toLowerCase());
+274 }
+275 }
+276 if (!possible.contains(arg.toLowerCase())) {
+277 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
+278 "{valid}", ACFUtil.join(possible, ", "));
+279 }
+280 }
+281
+282 Object paramValue = resolver.getContext(context);
+283
+284 //noinspection unchecked
+285 this.manager.conditions.validateConditions(context, paramValue);
+286 passedArgs.put(parameterName, paramValue);
+287 }
+288 return passedArgs;
+289 }
+290
+291 boolean hasPermission(CommandIssuer issuer) {
+292 return this.manager.hasPermission(issuer, getRequiredPermissions());
+293 }
+294
+295 /**
+296 * @see #getRequiredPermissions()
+297 * @deprecated
+298 */
+299 @Deprecated
+300 public String getPermission() {
+301 if (this.permission == null || this.permission.isEmpty()) {
+302 return null;
+303 }
+304 return ACFPatterns.COMMA.split(this.permission)[0];
+305 }
+306
+307 private void computePermissions() {
+308 this.permissions.clear();
+309 this.permissions.addAll(this.scope.getRequiredPermissions());
+310 if (this.permission != null && !this.permission.isEmpty()) {
+311 this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission)));
+312 }
+313 }
+314
+315 public Set<String> getRequiredPermissions() {
+316 return this.permissions;
+317 }
+318
+319 public boolean requiresPermission(String permission) {
+320 return getRequiredPermissions().contains(permission);
+321 }
+322
+323 public String getPrefSubCommand() {
+324 return prefSubCommand;
+325 }
+326
+327 public String getSyntaxText() {
+328 return syntaxText;
+329 }
+330
+331 public String getHelpText() {
+332 return helpText != null ? helpText : "";
+333 }
+334
+335 public boolean isPrivate() {
+336 return isPrivate;
+337 }
+338
+339 public String getCommand() {
+340 return command;
+341 }
+342
+343 public void addSubcommand(String cmd) {
+344 this.registeredSubcommands.add(cmd);
+345 }
+346
+347 public void addSubcommands(Collection<String> cmd) {
+348 this.registeredSubcommands.addAll(cmd);
+349 }
+350
+351 public <T extends Annotation> T getAnnotation(Class<T> annotation) {
+352 return method.getAnnotation(annotation);
+353 }
+354}
diff --git a/docs/acf-core/src-html/co/aikar/commands/annotation/CommandPermission.html b/docs/acf-core/src-html/co/aikar/commands/annotation/CommandPermission.html
index ed90fee8..a4f456c7 100644
--- a/docs/acf-core/src-html/co/aikar/commands/annotation/CommandPermission.html
+++ b/docs/acf-core/src-html/co/aikar/commands/annotation/CommandPermission.html
@@ -38,11 +38,11 @@
030
031/**
032 * Sets the permission required to perform this command.
-033 *
+033 * <p>
034 * Permission format will vary based on implementation platform
035 */
036@Retention(RetentionPolicy.RUNTIME)
-037@Target({ElementType.METHOD, ElementType.TYPE})
+037@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
038public @interface CommandPermission {
039 String value();
040}
diff --git a/docs/acf-jda/co/aikar/commands/JDACommandCompletions.html b/docs/acf-jda/co/aikar/commands/JDACommandCompletions.html
index a9ffd403..dab8c4cf 100644
--- a/docs/acf-jda/co/aikar/commands/JDACommandCompletions.html
+++ b/docs/acf-jda/co/aikar/commands/JDACommandCompletions.html
@@ -181,7 +181,7 @@ extends co.aikar.commands.CommandCompletions<co.aikar.commands.CommandComplet
Methods inherited from class co.aikar.commands.CommandCompletions
-registerStaticCompletion, registerStaticCompletion, registerStaticCompletion, registerStaticCompletion
+registerStaticCompletion, registerStaticCompletion, registerStaticCompletion, registerStaticCompletion, setDefaultCompletion
-
diff --git a/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html b/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html
index d76d4ea0..54841594 100644
--- a/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html
+++ b/docs/acf-jda/co/aikar/commands/JDACommandExecutionContext.html
@@ -141,7 +141,7 @@ extends co.aikar.commands.CommandExecutionContext<
-
diff --git a/docs/acf-jda/co/aikar/commands/JDACommandManager.html b/docs/acf-jda/co/aikar/commands/JDACommandManager.html
index 5edd7a59..06aa5e5c 100644
--- a/docs/acf-jda/co/aikar/commands/JDACommandManager.html
+++ b/docs/acf-jda/co/aikar/commands/JDACommandManager.html
@@ -309,7 +309,7 @@ extends co.aikar.commands.CommandManager<net.dv8tion.jda.core.events.message.
Methods inherited from class co.aikar.commands.CommandManager
-addSupportedLanguage, createConditionContext, createRegisteredCommand, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
+addSupportedLanguage, createConditionContext, createRegisteredCommand, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
-
diff --git a/docs/acf-paper/co/aikar/commands/PaperCommandCompletions.html b/docs/acf-paper/co/aikar/commands/PaperCommandCompletions.html
index 795c7681..ecf3569b 100644
--- a/docs/acf-paper/co/aikar/commands/PaperCommandCompletions.html
+++ b/docs/acf-paper/co/aikar/commands/PaperCommandCompletions.html
@@ -162,7 +162,7 @@ extends co.aikar.commands.BukkitCommandCompletions
Methods inherited from class co.aikar.commands.CommandCompletions
-registerAsyncCompletion, registerCompletion, registerStaticCompletion, registerStaticCompletion, registerStaticCompletion, registerStaticCompletion
+registerAsyncCompletion, registerCompletion, registerStaticCompletion, registerStaticCompletion, registerStaticCompletion, registerStaticCompletion, setDefaultCompletion
-
diff --git a/docs/acf-paper/co/aikar/commands/PaperCommandManager.html b/docs/acf-paper/co/aikar/commands/PaperCommandManager.html
index 1d64225e..21d016af 100644
--- a/docs/acf-paper/co/aikar/commands/PaperCommandManager.html
+++ b/docs/acf-paper/co/aikar/commands/PaperCommandManager.html
@@ -197,7 +197,7 @@ extends co.aikar.commands.BukkitCommandManager
Methods inherited from class co.aikar.commands.CommandManager
-addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
+addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, hasPermission, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
-
diff --git a/docs/acf-sponge/co/aikar/commands/SpongeCommandCompletions.html b/docs/acf-sponge/co/aikar/commands/SpongeCommandCompletions.html
index a158ff32..38974ddd 100644
--- a/docs/acf-sponge/co/aikar/commands/SpongeCommandCompletions.html
+++ b/docs/acf-sponge/co/aikar/commands/SpongeCommandCompletions.html
@@ -158,7 +158,7 @@ extends co.aikar.commands.CommandCompletions<
-
diff --git a/docs/acf-sponge/co/aikar/commands/SpongeCommandExecutionContext.html b/docs/acf-sponge/co/aikar/commands/SpongeCommandExecutionContext.html
index dfc16356..09c60dd2 100644
--- a/docs/acf-sponge/co/aikar/commands/SpongeCommandExecutionContext.html
+++ b/docs/acf-sponge/co/aikar/commands/SpongeCommandExecutionContext.html
@@ -162,7 +162,7 @@ extends co.aikar.commands.CommandExecutionContext<
-
diff --git a/docs/acf-sponge/co/aikar/commands/SpongeCommandManager.html b/docs/acf-sponge/co/aikar/commands/SpongeCommandManager.html
index c05b85e1..9b4583b0 100644
--- a/docs/acf-sponge/co/aikar/commands/SpongeCommandManager.html
+++ b/docs/acf-sponge/co/aikar/commands/SpongeCommandManager.html
@@ -281,7 +281,7 @@ extends co.aikar.commands.CommandManager<org.spongepowered.api.command.Comman
Methods inherited from class co.aikar.commands.CommandManager
-addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
+addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
-
diff --git a/docs/acf-velocity/co/aikar/commands/VelocityCommandCompletions.html b/docs/acf-velocity/co/aikar/commands/VelocityCommandCompletions.html
index 72081303..7418b411 100644
--- a/docs/acf-velocity/co/aikar/commands/VelocityCommandCompletions.html
+++ b/docs/acf-velocity/co/aikar/commands/VelocityCommandCompletions.html
@@ -159,7 +159,7 @@ extends co.aikar.commands.CommandCompletions<
-
diff --git a/docs/acf-velocity/co/aikar/commands/VelocityCommandExecutionContext.html b/docs/acf-velocity/co/aikar/commands/VelocityCommandExecutionContext.html
index c98d603a..a79d0017 100644
--- a/docs/acf-velocity/co/aikar/commands/VelocityCommandExecutionContext.html
+++ b/docs/acf-velocity/co/aikar/commands/VelocityCommandExecutionContext.html
@@ -162,7 +162,7 @@ extends co.aikar.commands.CommandExecutionContext<
-
diff --git a/docs/acf-velocity/co/aikar/commands/VelocityCommandManager.html b/docs/acf-velocity/co/aikar/commands/VelocityCommandManager.html
index e6611382..4c961939 100644
--- a/docs/acf-velocity/co/aikar/commands/VelocityCommandManager.html
+++ b/docs/acf-velocity/co/aikar/commands/VelocityCommandManager.html
@@ -302,7 +302,7 @@ extends co.aikar.commands.CommandManager<com.velocitypowered.api.command.Comm
Methods inherited from class co.aikar.commands.CommandManager
-addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale
+addSupportedLanguage, enableUnstableAPI, formatMessage, generateCommandHelp, generateCommandHelp, generateCommandHelp, generateCommandHelp, getCommandConditions, getCommandReplacements, getCurrentCommandIssuer, getCurrentCommandManager, getCurrentCommandOperationContext, getDefaultExceptionHandler, getDefaultFormatter, getDefaultHelpPerPage, getFormat, getHelpFormatter, getIssuerLocale, getRootCommand, getSupportedLanguages, handleUncaughtException, hasPermission, hasPermission, isLoggingUnhandledExceptions, log, notifyLocaleChange, obtainRootCommand, onLocaleChange, registerDependency, registerDependency, sendMessage, sendMessage, setDefaultExceptionHandler, setDefaultExceptionHandler, setDefaultFormatter, setDefaultHelpPerPage, setFormat, setFormat, setFormat, setHelpFormatter, setIssuerLocale, usePerIssuerLocale, usingPerIssuerLocale