diff --git a/docs/acf-bukkit/co/aikar/commands/BukkitCommandContexts.html b/docs/acf-bukkit/co/aikar/commands/BukkitCommandContexts.html
index 4c317c1b..8fa83e6f 100644
--- a/docs/acf-bukkit/co/aikar/commands/BukkitCommandContexts.html
+++ b/docs/acf-bukkit/co/aikar/commands/BukkitCommandContexts.html
@@ -107,7 +107,7 @@
-public class BukkitCommandContexts
+public class BukkitCommandContexts
extends co.aikar.commands.CommandContexts<BukkitCommandExecutionContext>
@@ -187,7 +187,7 @@ extends co.aikar.commands.CommandContexts<
BukkitCommandContexts
-public BukkitCommandContexts(BukkitCommandManager manager)
+public BukkitCommandContexts(BukkitCommandManager manager)
diff --git a/docs/acf-bukkit/co/aikar/commands/MinecraftMessageKeys.html b/docs/acf-bukkit/co/aikar/commands/MinecraftMessageKeys.html
index a2be9e6f..fdf186fe 100644
--- a/docs/acf-bukkit/co/aikar/commands/MinecraftMessageKeys.html
+++ b/docs/acf-bukkit/co/aikar/commands/MinecraftMessageKeys.html
@@ -159,15 +159,18 @@ implements co.aikar.locales.MessageKeyProvider
NO_PLAYER_FOUND |
+NO_PLAYER_FOUND_OFFLINE |
+
+
NO_PLAYER_FOUND_SERVER |
-
+
PLAYER_IS_VANISHED_CONFIRM |
-
+
USERNAME_TOO_SHORT |
-
+
YOU_MUST_BE_HOLDING_ITEM |
@@ -294,13 +297,22 @@ the order they are declared.
public static final MinecraftMessageKeys NO_PLAYER_FOUND_SERVER
+
+
+
+
@@ -309,7 +321,7 @@ the order they are declared.
@@ -318,7 +330,7 @@ the order they are declared.
@@ -327,7 +339,7 @@ the order they are declared.
@@ -386,7 +398,7 @@ not permitted.)
-
getMessageKey
-public co.aikar.locales.MessageKey getMessageKey()
+public co.aikar.locales.MessageKey getMessageKey()
- Specified by:
getMessageKey in interface co.aikar.locales.MessageKeyProvider
diff --git a/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandContexts.html b/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandContexts.html
index 81d6d7b4..9beb4a27 100644
--- a/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandContexts.html
+++ b/docs/acf-bukkit/src-html/co/aikar/commands/BukkitCommandContexts.html
@@ -36,193 +36,228 @@
028import org.bukkit.Bukkit;
029import org.bukkit.ChatColor;
030import org.bukkit.Location;
-031import org.bukkit.World;
-032import org.bukkit.command.BlockCommandSender;
-033import org.bukkit.command.CommandSender;
-034import org.bukkit.entity.Entity;
-035import org.bukkit.entity.Player;
-036import org.bukkit.inventory.PlayerInventory;
-037import org.jetbrains.annotations.Nullable;
-038
-039import java.util.HashSet;
-040import java.util.Set;
-041import java.util.regex.Matcher;
-042import java.util.regex.Pattern;
-043import java.util.stream.Collectors;
-044import java.util.stream.Stream;
-045
-046@SuppressWarnings("WeakerAccess")
-047public class BukkitCommandContexts extends CommandContexts<BukkitCommandExecutionContext> {
-048
-049 public BukkitCommandContexts(BukkitCommandManager manager) {
-050 super(manager);
-051
-052 registerContext(OnlinePlayer.class, c -> getOnlinePlayer(c.getIssuer(), c.popFirstArg(), c.hasAnnotation(Optional.class)));
-053 registerContext(OnlinePlayer[].class, (c) -> {
-054 BukkitCommandIssuer issuer = c.getIssuer();
-055 final String search = c.popFirstArg();
-056 boolean allowMissing = c.hasFlag("allowmissing");
-057 Set<OnlinePlayer> players = new HashSet<>();
-058 Pattern split = ACFPatterns.COMMA;
-059 String splitter = c.getFlagValue("splitter", (String) null);
-060 if (splitter != null) {
-061 split = Pattern.compile(Pattern.quote(splitter));
-062 }
-063 for (String lookup : split.split(search)) {
-064 OnlinePlayer player = getOnlinePlayer(issuer, lookup, allowMissing);
-065 if (player != null) {
-066 players.add(player);
-067 }
-068 }
-069 if (players.isEmpty() && !c.hasFlag("allowempty")) {
-070 issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER,
-071 "{search}", search);
-072
-073 throw new InvalidCommandArgument(false);
-074 }
-075 return players.toArray(new OnlinePlayer[players.size()]);
-076 });
-077 registerIssuerAwareContext(World.class, (c) -> {
-078 String firstArg = c.getFirstArg();
-079 World world = firstArg != null ? Bukkit.getWorld(firstArg) : null;
-080 if (world != null) {
-081 c.popFirstArg();
-082 }
-083 if (world == null && c.getSender() instanceof Player) {
-084 world = ((Entity) c.getSender()).getWorld();
-085 }
-086 if (world == null) {
-087 throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
-088 }
-089 return world;
-090 });
-091 registerIssuerAwareContext(CommandSender.class, BukkitCommandExecutionContext::getSender);
-092 registerIssuerAwareContext(Player.class, (c) -> {
-093 Player player = c.getSender() instanceof Player ? (Player) c.getSender() : null;
-094 if (player == null && !c.hasAnnotation(Optional.class)) {
-095 throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
-096 }
-097 PlayerInventory inventory = player != null ? player.getInventory() : null;
-098 if (inventory != null && c.hasFlag("itemheld") && !ACFBukkitUtil.isValidItem(inventory.getItem(inventory.getHeldItemSlot()))) {
-099 throw new InvalidCommandArgument(MinecraftMessageKeys.YOU_MUST_BE_HOLDING_ITEM, false);
-100 }
-101 return player;
-102 });
-103 registerContext(ChatColor.class, c -> {
-104 String first = c.popFirstArg();
-105 Stream<ChatColor> colors = Stream.of(ChatColor.values());
-106 if (c.hasFlag("colorsonly")) {
-107 colors = colors.filter(color -> color.ordinal() <= 0xF);
-108 }
-109 String filter = c.getFlagValue("filter", (String) null);
-110 if (filter != null) {
-111 filter = ACFUtil.simplifyString(filter);
-112 String finalFilter = filter;
-113 colors = colors.filter(color -> finalFilter.equals(ACFUtil.simplifyString(color.name())));
-114 }
-115
-116 ChatColor match = ACFUtil.simpleMatch(ChatColor.class, first);
-117 if (match == null) {
-118 String valid = colors
-119 .map(color -> "<c2>" + ACFUtil.simplifyString(color.name()) + "</c2>")
-120 .collect(Collectors.joining("<c1>,</c1> "));
-121
-122 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid);
+031import org.bukkit.OfflinePlayer;
+032import org.bukkit.World;
+033import org.bukkit.command.BlockCommandSender;
+034import org.bukkit.command.CommandSender;
+035import org.bukkit.entity.Entity;
+036import org.bukkit.entity.Player;
+037import org.bukkit.inventory.PlayerInventory;
+038import org.jetbrains.annotations.Nullable;
+039
+040import java.util.HashSet;
+041import java.util.Set;
+042import java.util.UUID;
+043import java.util.regex.Matcher;
+044import java.util.regex.Pattern;
+045import java.util.stream.Collectors;
+046import java.util.stream.Stream;
+047
+048@SuppressWarnings("WeakerAccess")
+049public class BukkitCommandContexts extends CommandContexts<BukkitCommandExecutionContext> {
+050
+051 public BukkitCommandContexts(BukkitCommandManager manager) {
+052 super(manager);
+053
+054 registerContext(OnlinePlayer.class, c -> getOnlinePlayer(c.getIssuer(), c.popFirstArg(), c.hasAnnotation(Optional.class)));
+055 registerContext(OnlinePlayer[].class, (c) -> {
+056 BukkitCommandIssuer issuer = c.getIssuer();
+057 final String search = c.popFirstArg();
+058 boolean allowMissing = c.hasFlag("allowmissing");
+059 Set<OnlinePlayer> players = new HashSet<>();
+060 Pattern split = ACFPatterns.COMMA;
+061 String splitter = c.getFlagValue("splitter", (String) null);
+062 if (splitter != null) {
+063 split = Pattern.compile(Pattern.quote(splitter));
+064 }
+065 for (String lookup : split.split(search)) {
+066 OnlinePlayer player = getOnlinePlayer(issuer, lookup, allowMissing);
+067 if (player != null) {
+068 players.add(player);
+069 }
+070 }
+071 if (players.isEmpty() && !c.hasFlag("allowempty")) {
+072 issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER,
+073 "{search}", search);
+074
+075 throw new InvalidCommandArgument(false);
+076 }
+077 return players.toArray(new OnlinePlayer[players.size()]);
+078 });
+079 registerIssuerAwareContext(World.class, (c) -> {
+080 String firstArg = c.getFirstArg();
+081 World world = firstArg != null ? Bukkit.getWorld(firstArg) : null;
+082 if (world != null) {
+083 c.popFirstArg();
+084 }
+085 if (world == null && c.getSender() instanceof Player) {
+086 world = ((Entity) c.getSender()).getWorld();
+087 }
+088 if (world == null) {
+089 throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
+090 }
+091 return world;
+092 });
+093 registerIssuerAwareContext(CommandSender.class, BukkitCommandExecutionContext::getSender);
+094 registerIssuerAwareContext(Player.class, (c) -> {
+095 boolean isOptional = c.hasAnnotation(Optional.class);
+096 CommandSender sender = c.getSender();
+097 boolean isPlayerSender = sender instanceof Player;
+098 if (!c.hasFlag("other")) {
+099 Player player = isPlayerSender ? (Player) sender : null;
+100 if (player == null && !isOptional) {
+101 throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
+102 }
+103 PlayerInventory inventory = player != null ? player.getInventory() : null;
+104 if (inventory != null && c.hasFlag("itemheld") && !ACFBukkitUtil.isValidItem(inventory.getItem(inventory.getHeldItemSlot()))) {
+105 throw new InvalidCommandArgument(MinecraftMessageKeys.YOU_MUST_BE_HOLDING_ITEM, false);
+106 }
+107 return player;
+108 } else {
+109 String arg = c.popFirstArg();
+110 if (arg == null && isOptional) {
+111 if (c.hasFlag("defaultself")) {
+112 if (isPlayerSender) {
+113 return (Player) sender;
+114 } else {
+115 throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
+116 }
+117 } else {
+118 return null;
+119 }
+120 }
+121 OnlinePlayer onlinePlayer = getOnlinePlayer(c.getIssuer(), arg, isOptional);
+122 return onlinePlayer != null ? onlinePlayer.getPlayer() : null;
123 }
-124 return match;
-125 });
-126 registerContext(Location.class, c -> {
-127 String input = c.popFirstArg();
-128 CommandSender sender = c.getSender();
-129 String[] split = ACFPatterns.COLON.split(input, 2);
-130 if (split.length == 0) {
-131 throw new InvalidCommandArgument(true);
-132 }
-133 if (split.length < 2 && !(sender instanceof Player) && !(sender instanceof BlockCommandSender)) {
-134 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_WORLD);
+124 });
+125 registerContext(OfflinePlayer.class, c -> {
+126 String name = c.popFirstArg();
+127 UUID uuid = null;
+128 if (c.hasFlag("uuid")) {
+129 uuid = UUID.fromString(name);
+130 }
+131 OfflinePlayer offlinePlayer = uuid != null ? Bukkit.getOfflinePlayer(uuid) : Bukkit.getOfflinePlayer(name);
+132 if (offlinePlayer == null) {
+133 throw new InvalidCommandArgument(MinecraftMessageKeys.NO_PLAYER_FOUND_OFFLINE,
+134 "{search}", name);
135 }
-136 final String world;
-137 final String rest;
-138 Location sourceLoc = null;
-139 if (split.length == 2) {
-140 world = split[0];
-141 rest = split[1];
-142 } else if (sender instanceof Player) {
-143 sourceLoc = ((Player) sender).getLocation();
-144 world = sourceLoc.getWorld().getName();
-145 rest = split[0];
-146 } else if (sender instanceof BlockCommandSender) {
-147 sourceLoc = ((BlockCommandSender) sender).getBlock().getLocation();
-148 world = sourceLoc.getWorld().getName();
-149 rest = split[0];
-150 } else {
-151 throw new InvalidCommandArgument(true);
-152 }
-153
-154 boolean rel = rest.startsWith("~");
-155 split = ACFPatterns.COMMA.split(rel ? rest.substring(1) : rest);
-156 if (split.length < 3) {
-157 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ);
+136 return offlinePlayer;
+137 });
+138 registerContext(ChatColor.class, c -> {
+139 String first = c.popFirstArg();
+140 Stream<ChatColor> colors = Stream.of(ChatColor.values());
+141 if (c.hasFlag("colorsonly")) {
+142 colors = colors.filter(color -> color.ordinal() <= 0xF);
+143 }
+144 String filter = c.getFlagValue("filter", (String) null);
+145 if (filter != null) {
+146 filter = ACFUtil.simplifyString(filter);
+147 String finalFilter = filter;
+148 colors = colors.filter(color -> finalFilter.equals(ACFUtil.simplifyString(color.name())));
+149 }
+150
+151 ChatColor match = ACFUtil.simpleMatch(ChatColor.class, first);
+152 if (match == null) {
+153 String valid = colors
+154 .map(color -> "<c2>" + ACFUtil.simplifyString(color.name()) + "</c2>")
+155 .collect(Collectors.joining("<c1>,</c1> "));
+156
+157 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid);
158 }
-159
-160 Double x = ACFUtil.parseDouble(split[0]);
-161 Double y = ACFUtil.parseDouble(split[1]);
-162 Double z = ACFUtil.parseDouble(split[2]);
-163
-164 if (sourceLoc != null && rel) {
-165 x += sourceLoc.getX();
-166 y += sourceLoc.getY();
-167 z += sourceLoc.getZ();
-168 } else if (rel) {
-169 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_CONSOLE_NOT_RELATIVE);
+159 return match;
+160 });
+161 registerContext(Location.class, c -> {
+162 String input = c.popFirstArg();
+163 CommandSender sender = c.getSender();
+164 String[] split = ACFPatterns.COLON.split(input, 2);
+165 if (split.length == 0) {
+166 throw new InvalidCommandArgument(true);
+167 }
+168 if (split.length < 2 && !(sender instanceof Player) && !(sender instanceof BlockCommandSender)) {
+169 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_WORLD);
170 }
-171
-172 if (x == null || y == null || z == null) {
-173 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ);
-174 }
-175
-176 World worldObj = Bukkit.getWorld(world);
-177 if (worldObj == null) {
-178 throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
-179 }
-180
-181 if (split.length >= 5) {
-182 Float yaw = ACFUtil.parseFloat(split[3]);
-183 Float pitch = ACFUtil.parseFloat(split[4]);
-184
-185 if (pitch == null || yaw == null) {
-186 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ);
-187 }
-188 return new Location(worldObj, x, y, z, yaw, pitch);
-189 } else {
-190 return new Location(worldObj, x, y, z);
-191 }
-192 });
-193 Pattern versionPattern = Pattern.compile("\\(MC: (\\d)\\.(\\d+)\\.?.*?\\)");
-194 Matcher matcher = versionPattern.matcher(Bukkit.getVersion());
-195 if (matcher.find()) {
-196 int mcMajorVersion = ACFUtil.parseInt(matcher.toMatchResult().group(1), 0);
-197 int mcMinorVersion = ACFUtil.parseInt(matcher.toMatchResult().group(2), 0);
-198 manager.log(LogLevel.INFO, "Minecraft Version: " + mcMajorVersion + "." + mcMinorVersion);
-199 if (mcMajorVersion >= 1 && mcMinorVersion >= 12) {
-200 BukkitCommandContexts_1_12.register(this);
-201 }
-202 }
-203 }
-204
-205 @Nullable
-206 OnlinePlayer getOnlinePlayer(BukkitCommandIssuer issuer, String lookup, boolean allowMissing) throws InvalidCommandArgument {
-207 Player player = ACFBukkitUtil.findPlayerSmart(issuer, lookup);
-208 //noinspection Duplicates
-209 if (player == null) {
-210 if (allowMissing) {
-211 return null;
-212 }
-213 throw new InvalidCommandArgument(false);
-214 }
-215 return new OnlinePlayer(player);
-216 }
-217}
+171 final String world;
+172 final String rest;
+173 Location sourceLoc = null;
+174 if (split.length == 2) {
+175 world = split[0];
+176 rest = split[1];
+177 } else if (sender instanceof Player) {
+178 sourceLoc = ((Player) sender).getLocation();
+179 world = sourceLoc.getWorld().getName();
+180 rest = split[0];
+181 } else if (sender instanceof BlockCommandSender) {
+182 sourceLoc = ((BlockCommandSender) sender).getBlock().getLocation();
+183 world = sourceLoc.getWorld().getName();
+184 rest = split[0];
+185 } else {
+186 throw new InvalidCommandArgument(true);
+187 }
+188
+189 boolean rel = rest.startsWith("~");
+190 split = ACFPatterns.COMMA.split(rel ? rest.substring(1) : rest);
+191 if (split.length < 3) {
+192 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ);
+193 }
+194
+195 Double x = ACFUtil.parseDouble(split[0]);
+196 Double y = ACFUtil.parseDouble(split[1]);
+197 Double z = ACFUtil.parseDouble(split[2]);
+198
+199 if (sourceLoc != null && rel) {
+200 x += sourceLoc.getX();
+201 y += sourceLoc.getY();
+202 z += sourceLoc.getZ();
+203 } else if (rel) {
+204 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_CONSOLE_NOT_RELATIVE);
+205 }
+206
+207 if (x == null || y == null || z == null) {
+208 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ);
+209 }
+210
+211 World worldObj = Bukkit.getWorld(world);
+212 if (worldObj == null) {
+213 throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
+214 }
+215
+216 if (split.length >= 5) {
+217 Float yaw = ACFUtil.parseFloat(split[3]);
+218 Float pitch = ACFUtil.parseFloat(split[4]);
+219
+220 if (pitch == null || yaw == null) {
+221 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ);
+222 }
+223 return new Location(worldObj, x, y, z, yaw, pitch);
+224 } else {
+225 return new Location(worldObj, x, y, z);
+226 }
+227 });
+228 Pattern versionPattern = Pattern.compile("\\(MC: (\\d)\\.(\\d+)\\.?.*?\\)");
+229 Matcher matcher = versionPattern.matcher(Bukkit.getVersion());
+230 if (matcher.find()) {
+231 int mcMajorVersion = ACFUtil.parseInt(matcher.toMatchResult().group(1), 0);
+232 int mcMinorVersion = ACFUtil.parseInt(matcher.toMatchResult().group(2), 0);
+233 manager.log(LogLevel.INFO, "Minecraft Version: " + mcMajorVersion + "." + mcMinorVersion);
+234 if (mcMajorVersion >= 1 && mcMinorVersion >= 12) {
+235 BukkitCommandContexts_1_12.register(this);
+236 }
+237 }
+238 }
+239
+240 @Nullable
+241 OnlinePlayer getOnlinePlayer(BukkitCommandIssuer issuer, String lookup, boolean allowMissing) throws InvalidCommandArgument {
+242 Player player = ACFBukkitUtil.findPlayerSmart(issuer, lookup);
+243 //noinspection Duplicates
+244 if (player == null) {
+245 if (allowMissing) {
+246 return null;
+247 }
+248 throw new InvalidCommandArgument(false);
+249 }
+250 return new OnlinePlayer(player);
+251 }
+252}
diff --git a/docs/acf-bukkit/src-html/co/aikar/commands/MinecraftMessageKeys.html b/docs/acf-bukkit/src-html/co/aikar/commands/MinecraftMessageKeys.html
index ce37abbb..0ee6b52d 100644
--- a/docs/acf-bukkit/src-html/co/aikar/commands/MinecraftMessageKeys.html
+++ b/docs/acf-bukkit/src-html/co/aikar/commands/MinecraftMessageKeys.html
@@ -42,16 +42,17 @@
034 IS_NOT_A_VALID_NAME,
035 MULTIPLE_PLAYERS_MATCH,
036 NO_PLAYER_FOUND_SERVER,
-037 NO_PLAYER_FOUND,
-038 LOCATION_PLEASE_SPECIFY_WORLD,
-039 LOCATION_PLEASE_SPECIFY_XYZ,
-040 LOCATION_CONSOLE_NOT_RELATIVE;
-041
-042 private final MessageKey key = MessageKey.of("acf-minecraft." + this.name().toLowerCase());
-043 public MessageKey getMessageKey() {
-044 return key;
-045 }
-046}
+037 NO_PLAYER_FOUND_OFFLINE,
+038 NO_PLAYER_FOUND,
+039 LOCATION_PLEASE_SPECIFY_WORLD,
+040 LOCATION_PLEASE_SPECIFY_XYZ,
+041 LOCATION_CONSOLE_NOT_RELATIVE;
+042
+043 private final MessageKey key = MessageKey.of("acf-minecraft." + this.name().toLowerCase());
+044 public MessageKey getMessageKey() {
+045 return key;
+046 }
+047}
diff --git a/docs/acf-core/co/aikar/commands/CommandContexts.html b/docs/acf-core/co/aikar/commands/CommandContexts.html
index e1d480a0..7f525e88 100644
--- a/docs/acf-core/co/aikar/commands/CommandContexts.html
+++ b/docs/acf-core/co/aikar/commands/CommandContexts.html
@@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
-
-public class CommandContexts<R extends CommandExecutionContext<?,? extends CommandIssuer>>
+public class CommandContexts<R extends CommandExecutionContext<?,? extends CommandIssuer>>
extends Object
@@ -222,7 +222,7 @@ extends
contextMap
-protected final Map<Class<?>,ContextResolver<?,R extends CommandExecutionContext<?,? extends CommandIssuer>>> contextMap
+protected final Map<Class<?>,ContextResolver<?,R extends CommandExecutionContext<?,? extends CommandIssuer>>> contextMap
@@ -231,7 +231,7 @@ extends
manager
-protected final CommandManager manager
+protected final CommandManager manager
@@ -249,7 +249,7 @@ extends
registerSenderAwareContext
@Deprecated
-public <T> void registerSenderAwareContext(Class<T> context,
+public <T> void registerSenderAwareContext(Class<T> context,
IssuerAwareContextResolver<T,R> supplier)
Deprecated.
@@ -265,7 +265,7 @@ public <T> void
registerIssuerAwareContext
-public <T> void registerIssuerAwareContext(Class<T> context,
+public <T> void registerIssuerAwareContext(Class<T> context,
IssuerAwareContextResolver<T,R> supplier)
public static final MessageKeys MUST_BE_MAX_LENGTH
+
+
+
+
@@ -364,7 +376,7 @@ the order they are declared.
@@ -373,7 +385,7 @@ the order they are declared.
@@ -382,7 +394,7 @@ the order they are declared.
@@ -391,7 +403,7 @@ the order they are declared.
@@ -400,7 +412,7 @@ the order they are declared.
@@ -459,7 +471,7 @@ not permitted.)
@@ -240,7 +240,7 @@ public
requiresPermission
-public boolean requiresPermission(String permission)
+public boolean requiresPermission(String permission)
@@ -249,7 +249,7 @@ public
getPrefSubCommand
-public String getPrefSubCommand()
+public String getPrefSubCommand()
@@ -258,7 +258,7 @@ public
getSyntaxText
-public String getSyntaxText()
+public String getSyntaxText()
@@ -267,7 +267,7 @@ public
getCommand
-public String getCommand()
+public String getCommand()
@@ -276,7 +276,7 @@ public
addSubcommand
-public void addSubcommand(String cmd)
+public void addSubcommand(String cmd)
@@ -285,7 +285,7 @@ public
addSubcommands
-public void addSubcommands(Collection<String> cmd)
+public void addSubcommands(Collection<String> cmd)
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 6f6e758c..3031c16c 100644
--- a/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
+++ b/docs/acf-core/src-html/co/aikar/commands/CommandContexts.html
@@ -39,213 +39,266 @@
031import co.aikar.commands.contexts.IssuerOnlyContextResolver;
032import co.aikar.commands.contexts.OptionalContextResolver;
033import com.google.common.collect.Maps;
-034
-035import java.util.List;
-036import java.util.Map;
-037
-038@SuppressWarnings("WeakerAccess")
-039public class CommandContexts <R extends CommandExecutionContext<?, ? extends CommandIssuer>> {
-040 protected final Map<Class<?>, ContextResolver<?, R>> contextMap = Maps.newHashMap();
-041 protected final CommandManager manager;
-042
-043 CommandContexts(CommandManager manager) {
-044 this.manager = manager;
-045 registerContext(Integer.class, (c) -> {
-046 try {
-047 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).intValue();
-048 } catch (NumberFormatException e) {
-049 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
-050 }
-051 });
-052 registerContext(Long.class, (c) -> {
-053 try {
-054 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).longValue();
-055 } catch (NumberFormatException e) {
-056 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
-057 }
-058
+034import org.jetbrains.annotations.NotNull;
+035
+036import java.util.List;
+037import java.util.Map;
+038
+039@SuppressWarnings("WeakerAccess")
+040public class CommandContexts <R extends CommandExecutionContext<?, ? extends CommandIssuer>> {
+041 protected final Map<Class<?>, ContextResolver<?, R>> contextMap = Maps.newHashMap();
+042 protected final CommandManager manager;
+043
+044 CommandContexts(CommandManager manager) {
+045 this.manager = manager;
+046 registerContext(Short.class, (c) -> {
+047 try {
+048 return parseAndValudateNumber(c, Short.MAX_VALUE).shortValue();
+049 } catch (NumberFormatException e) {
+050 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+051 }
+052 });
+053 registerContext(short.class, (c) -> {
+054 try {
+055 return parseAndValudateNumber(c, Short.MAX_VALUE).shortValue();
+056 } catch (NumberFormatException e) {
+057 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+058 }
059 });
-060 registerContext(Float.class, (c) -> {
+060 registerContext(Integer.class, (c) -> {
061 try {
-062 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).floatValue();
+062 return parseAndValudateNumber(c, Integer.MAX_VALUE).intValue();
063 } catch (NumberFormatException e) {
064 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
065 }
066 });
-067 registerContext(Double.class, (c) -> {
+067 registerContext(int.class, (c) -> {
068 try {
-069 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).doubleValue();
+069 return parseAndValudateNumber(c, Integer.MAX_VALUE).intValue();
070 } catch (NumberFormatException e) {
071 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
072 }
073 });
-074 registerContext(Number.class, (c) -> {
+074 registerContext(Long.class, (c) -> {
075 try {
-076 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes"));
+076 return parseAndValudateNumber(c, Long.MAX_VALUE).longValue();
077 } catch (NumberFormatException e) {
078 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
079 }
080 });
-081 registerContext(Boolean.class, (c) -> {
-082 String test = c.popFirstArg();
-083 if (test == null) {
-084 return null;
-085 }
-086 return ACFUtil.isTruthy(test);
+081 registerContext(long.class, (c) -> {
+082 try {
+083 return parseAndValudateNumber(c, Long.MAX_VALUE).longValue();
+084 } catch (NumberFormatException e) {
+085 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+086 }
087 });
-088 registerContext(String.class, (c) -> {
-089 final Values values = c.getParam().getAnnotation(Values.class);
-090 if (values != null) {
-091 return c.popFirstArg();
-092 }
-093 String ret = (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) ?
-094 ACFUtil.join(c.getArgs())
-095 :
-096 c.popFirstArg();
-097
-098 Integer minLen = c.getFlagValue("minlen", (Integer) null);
-099 Integer maxLen = c.getFlagValue("maxlen", (Integer) null);
-100 if (minLen != null) {
-101 if (ret.length() < minLen) {
-102 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MIN_LENGTH, "{min}", String.valueOf(minLen));
-103 }
-104 }
-105 if (maxLen != null) {
-106 if (ret.length() > maxLen) {
-107 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MAX_LENGTH, "{max}", String.valueOf(maxLen));
-108 }
-109 }
-110
-111 return ret;
-112 });
-113 registerContext(String[].class, (c) -> {
-114 String val;
-115 // Go home IDEA, you're drunk
-116 //noinspection unchecked
-117 List<String> args = c.getArgs();
-118 if (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) {
-119 val = ACFUtil.join(args);
-120 } else {
-121 val = c.popFirstArg();
-122 }
-123 Split split = c.getParam().getAnnotation(Split.class);
-124 if (split != null) {
-125 if (val.isEmpty()) {
-126 throw new InvalidCommandArgument();
-127 }
-128 return ACFPatterns.getPattern(split.value()).split(val);
-129 } else if (!c.isLastArg()) {
-130 ACFUtil.sneaky(new IllegalStateException("Weird Command signature... String[] should be last or @Split"));
-131 }
-132
-133 String[] result = args.toArray(new String[args.size()]);
-134 args.clear();
-135 return result;
-136 });
-137
-138 registerContext(Enum.class, (c) -> {
-139 final String first = c.popFirstArg();
-140 //noinspection unchecked
-141 Class<? extends Enum<?>> enumCls = (Class<? extends Enum<?>>) c.getParam().getType();
-142 Enum<?> match = ACFUtil.simpleMatch(enumCls, first);
-143 if (match == null) {
-144 List<String> names = ACFUtil.enumNames(enumCls);
-145 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names));
-146 }
-147 return match;
-148 });
-149 registerOptionalContext(CommandHelp.class, (c) -> {
-150 String first = c.getFirstArg();
-151 String last = c.getLastArg();
-152 int page = 1;
-153 List<String> search = null;
-154 if (last != null && ACFUtil.isInteger(last)) {
-155 c.popLastArg();
-156 page = ACFUtil.parseInt(last);
-157 if (!c.getArgs().isEmpty()) {
-158 search = c.getArgs();
-159 }
-160 } else if (first != null && ACFUtil.isInteger(first)) {
-161 c.popFirstArg();
-162 page = ACFUtil.parseInt(first);
-163 if (!c.getArgs().isEmpty()) {
-164 search = c.getArgs();
-165 }
-166 } else if (!c.getArgs().isEmpty()) {
-167 search = c.getArgs();
-168 }
-169 CommandHelp commandHelp = manager.generateCommandHelp();
-170 commandHelp.setPage(page);
-171 Integer perPage = c.getFlagValue("perpage", (Integer) null);
-172 if (perPage != null) {
-173 commandHelp.setPerPage(perPage);
-174 }
-175 commandHelp.setSearch(search);
-176 return commandHelp;
-177 });
-178 }
-179
-180 /**
-181 * @deprecated Please switch to {@link #registerIssuerAwareContext(Class, IssuerAwareContextResolver)}
-182 * as the core wants to use the platform agnostic term of "Issuer" instead of Sender
-183 * @see #registerIssuerAwareContext(Class, IssuerAwareContextResolver)
-184 */
-185 @Deprecated
-186 public <T> void registerSenderAwareContext(Class<T> context, IssuerAwareContextResolver<T, R> supplier) {
-187 contextMap.put(context, supplier);
-188 }
-189
-190 /**
-191 * Registers a context resolver that may conditionally consume input, falling back to using the context of the
-192 * issuer to potentially fulfill this context.
-193 * You may call {@link CommandExecutionContext#getFirstArg()} and then conditionally call {@link CommandExecutionContext#popFirstArg()}
-194 * if you want to consume that input.
-195 */
-196 public <T> void registerIssuerAwareContext(Class<T> context, IssuerAwareContextResolver<T, R> supplier) {
-197 contextMap.put(context, supplier);
-198 }
-199
-200 /**
-201 * Registers a context resolver that will never consume input. It will always satisfy its context based on the
-202 * issuer of the command, so it will not appear in syntax strings.
-203 */
-204 public <T> void registerIssuerOnlyContext(Class<T> context, IssuerOnlyContextResolver<T, R> supplier) {
-205 contextMap.put(context, supplier);
-206 }
-207
-208 /**
-209 * Registers a context that can safely accept a null input from the command issuer to resolve. This resolver should always
-210 * call {@link CommandExecutionContext#popFirstArg()}
-211 */
-212 public <T> void registerOptionalContext(Class<T> context, OptionalContextResolver<T, R> supplier) {
-213 contextMap.put(context, supplier);
-214 }
-215
-216 /**
-217 * Registers a context that requires input from the command issuer to resolve. This resolver should always
-218 * call {@link CommandExecutionContext#popFirstArg()}
-219 */
-220 public <T> void registerContext(Class<T> context, ContextResolver<T, R> supplier) {
-221 contextMap.put(context, supplier);
+088 registerContext(Float.class, (c) -> {
+089 try {
+090 return parseAndValudateNumber(c, Float.MAX_VALUE).floatValue();
+091 } catch (NumberFormatException e) {
+092 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+093 }
+094 });
+095 registerContext(float.class, (c) -> {
+096 try {
+097 return parseAndValudateNumber(c, Float.MAX_VALUE).floatValue();
+098 } catch (NumberFormatException e) {
+099 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+100 }
+101 });
+102 registerContext(Double.class, (c) -> {
+103 try {
+104 return parseAndValudateNumber(c, Double.MAX_VALUE).doubleValue();
+105 } catch (NumberFormatException e) {
+106 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+107 }
+108 });
+109 registerContext(double.class, (c) -> {
+110 try {
+111 return parseAndValudateNumber(c, Double.MAX_VALUE).doubleValue();
+112 } catch (NumberFormatException e) {
+113 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+114 }
+115 });
+116 registerContext(Number.class, (c) -> {
+117 try {
+118 return parseAndValudateNumber(c, Double.MAX_VALUE);
+119 } catch (NumberFormatException e) {
+120 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER);
+121 }
+122 });
+123 registerContext(Boolean.class, (c) -> ACFUtil.isTruthy(c.popFirstArg()));
+124 registerContext(boolean.class, (c) -> ACFUtil.isTruthy(c.popFirstArg()));
+125 registerContext(char.class, c -> {
+126 String s = c.popFirstArg();
+127 if (s.length() > 1) {
+128 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MAX_LENGTH, "{max}", String.valueOf(1));
+129 }
+130 return s.charAt(0);
+131 });
+132 registerContext(String.class, (c) -> {
+133 final Values values = c.getParam().getAnnotation(Values.class);
+134 if (values != null) {
+135 return c.popFirstArg();
+136 }
+137 String ret = (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) ?
+138 ACFUtil.join(c.getArgs())
+139 :
+140 c.popFirstArg();
+141
+142 Integer minLen = c.getFlagValue("minlen", (Integer) null);
+143 Integer maxLen = c.getFlagValue("maxlen", (Integer) null);
+144 if (minLen != null) {
+145 if (ret.length() < minLen) {
+146 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MIN_LENGTH, "{min}", String.valueOf(minLen));
+147 }
+148 }
+149 if (maxLen != null) {
+150 if (ret.length() > maxLen) {
+151 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MAX_LENGTH, "{max}", String.valueOf(maxLen));
+152 }
+153 }
+154
+155 return ret;
+156 });
+157 registerContext(String[].class, (c) -> {
+158 String val;
+159 // Go home IDEA, you're drunk
+160 //noinspection unchecked
+161 List<String> args = c.getArgs();
+162 if (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) {
+163 val = ACFUtil.join(args);
+164 } else {
+165 val = c.popFirstArg();
+166 }
+167 Split split = c.getParam().getAnnotation(Split.class);
+168 if (split != null) {
+169 if (val.isEmpty()) {
+170 throw new InvalidCommandArgument();
+171 }
+172 return ACFPatterns.getPattern(split.value()).split(val);
+173 } else if (!c.isLastArg()) {
+174 ACFUtil.sneaky(new IllegalStateException("Weird Command signature... String[] should be last or @Split"));
+175 }
+176
+177 String[] result = args.toArray(new String[args.size()]);
+178 args.clear();
+179 return result;
+180 });
+181
+182 registerContext(Enum.class, (c) -> {
+183 final String first = c.popFirstArg();
+184 //noinspection unchecked
+185 Class<? extends Enum<?>> enumCls = (Class<? extends Enum<?>>) c.getParam().getType();
+186 Enum<?> match = ACFUtil.simpleMatch(enumCls, first);
+187 if (match == null) {
+188 List<String> names = ACFUtil.enumNames(enumCls);
+189 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names));
+190 }
+191 return match;
+192 });
+193 registerOptionalContext(CommandHelp.class, (c) -> {
+194 String first = c.getFirstArg();
+195 String last = c.getLastArg();
+196 int page = 1;
+197 List<String> search = null;
+198 if (last != null && ACFUtil.isInteger(last)) {
+199 c.popLastArg();
+200 page = ACFUtil.parseInt(last);
+201 if (!c.getArgs().isEmpty()) {
+202 search = c.getArgs();
+203 }
+204 } else if (first != null && ACFUtil.isInteger(first)) {
+205 c.popFirstArg();
+206 page = ACFUtil.parseInt(first);
+207 if (!c.getArgs().isEmpty()) {
+208 search = c.getArgs();
+209 }
+210 } else if (!c.getArgs().isEmpty()) {
+211 search = c.getArgs();
+212 }
+213 CommandHelp commandHelp = manager.generateCommandHelp();
+214 commandHelp.setPage(page);
+215 Integer perPage = c.getFlagValue("perpage", (Integer) null);
+216 if (perPage != null) {
+217 commandHelp.setPerPage(perPage);
+218 }
+219 commandHelp.setSearch(search);
+220 return commandHelp;
+221 });
222 }
223
-224 public ContextResolver<?, R> getResolver(Class<?> type) {
-225 Class<?> rootType = type;
-226 do {
-227 if (type == Object.class) {
-228 break;
-229 }
-230
-231 final ContextResolver<?, R> resolver = contextMap.get(type);
-232 if (resolver != null) {
-233 return resolver;
-234 }
-235 } while ((type = type.getSuperclass()) != null);
-236
-237 this.manager.log(LogLevel.ERROR, "Could not find context resolver", new IllegalStateException("No context resolver defined for " + rootType.getName()));
-238 return null;
-239 }
-240}
+224 @NotNull
+225 private Number parseAndValudateNumber(R c, Number maxValue) throws InvalidCommandArgument {
+226 Number val = ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes"));
+227 if (maxValue != null && val.doubleValue() > maxValue.doubleValue()) {
+228 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_AT_MOST, "{max}", String.valueOf(maxValue));
+229 }
+230 return val;
+231 }
+232
+233 /**
+234 * @deprecated Please switch to {@link #registerIssuerAwareContext(Class, IssuerAwareContextResolver)}
+235 * as the core wants to use the platform agnostic term of "Issuer" instead of Sender
+236 * @see #registerIssuerAwareContext(Class, IssuerAwareContextResolver)
+237 */
+238 @Deprecated
+239 public <T> void registerSenderAwareContext(Class<T> context, IssuerAwareContextResolver<T, R> supplier) {
+240 contextMap.put(context, supplier);
+241 }
+242
+243 /**
+244 * Registers a context resolver that may conditionally consume input, falling back to using the context of the
+245 * issuer to potentially fulfill this context.
+246 * You may call {@link CommandExecutionContext#getFirstArg()} and then conditionally call {@link CommandExecutionContext#popFirstArg()}
+247 * if you want to consume that input.
+248 */
+249 public <T> void registerIssuerAwareContext(Class<T> context, IssuerAwareContextResolver<T, R> supplier) {
+250 contextMap.put(context, supplier);
+251 }
+252
+253 /**
+254 * Registers a context resolver that will never consume input. It will always satisfy its context based on the
+255 * issuer of the command, so it will not appear in syntax strings.
+256 */
+257 public <T> void registerIssuerOnlyContext(Class<T> context, IssuerOnlyContextResolver<T, R> supplier) {
+258 contextMap.put(context, supplier);
+259 }
+260
+261 /**
+262 * Registers a context that can safely accept a null input from the command issuer to resolve. This resolver should always
+263 * call {@link CommandExecutionContext#popFirstArg()}
+264 */
+265 public <T> void registerOptionalContext(Class<T> context, OptionalContextResolver<T, R> supplier) {
+266 contextMap.put(context, supplier);
+267 }
+268
+269 /**
+270 * Registers a context that requires input from the command issuer to resolve. This resolver should always
+271 * call {@link CommandExecutionContext#popFirstArg()}
+272 */
+273 public <T> void registerContext(Class<T> context, ContextResolver<T, R> supplier) {
+274 contextMap.put(context, supplier);
+275 }
+276
+277 public ContextResolver<?, R> getResolver(Class<?> type) {
+278 Class<?> rootType = type;
+279 do {
+280 if (type == Object.class) {
+281 break;
+282 }
+283
+284 final ContextResolver<?, R> resolver = contextMap.get(type);
+285 if (resolver != null) {
+286 return resolver;
+287 }
+288 } while ((type = type.getSuperclass()) != null);
+289
+290 this.manager.log(LogLevel.ERROR, "Could not find context resolver", new IllegalStateException("No context resolver defined for " + rootType.getName()));
+291 return null;
+292 }
+293}
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 4d987662..448634da 100644
--- a/docs/acf-core/src-html/co/aikar/commands/MessageKeys.html
+++ b/docs/acf-core/src-html/co/aikar/commands/MessageKeys.html
@@ -50,19 +50,20 @@
042 MUST_BE_A_NUMBER,
043 MUST_BE_MIN_LENGTH,
044 MUST_BE_MAX_LENGTH,
-045 NOT_ALLOWED_ON_CONSOLE,
-046 COULD_NOT_FIND_PLAYER,
-047 HELP_FORMAT,
-048 NO_COMMAND_MATCHED_SEARCH,
-049 HELP_PAGE_INFORMATION,
-050 HELP_NO_RESULTS
-051 ;
-052
-053 private final MessageKey key = MessageKey.of("acf-core." + this.name().toLowerCase());
-054 public MessageKey getMessageKey() {
-055 return key;
-056 }
-057}
+045 PLEASE_SPECIFY_AT_MOST,
+046 NOT_ALLOWED_ON_CONSOLE,
+047 COULD_NOT_FIND_PLAYER,
+048 HELP_FORMAT,
+049 NO_COMMAND_MATCHED_SEARCH,
+050 HELP_PAGE_INFORMATION,
+051 HELP_NO_RESULTS
+052 ;
+053
+054 private final MessageKey key = MessageKey.of("acf-core." + this.name().toLowerCase());
+055 public MessageKey getMessageKey() {
+056 return key;
+057 }
+058}
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 50347a8a..afaed805 100644
--- a/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
+++ b/docs/acf-core/src-html/co/aikar/commands/RegisteredCommand.html
@@ -224,86 +224,90 @@
216 if (allowOptional && def != null) {
217 args.add(scope.manager.getCommandReplacements().replace(def.value()));
218 } else if (allowOptional && opt != null) {
-219 passedArgs.put(parameterName, isOptionalResolver(resolver) ? resolver.getContext(context) : null);
-220 //noinspection UnnecessaryContinue
-221 continue;
-222 } else if (!isOptionalResolver) {
-223 scope.showSyntax(sender, this);
-224 return null;
-225 }
-226 }
-227 final Values values = parameter.getAnnotation(Values.class);
-228 if (values != null) {
-229 String arg = !args.isEmpty() ? args.get(0) : "";
-230
-231 final String[] split = ACFPatterns.PIPE.split(scope.manager.getCommandReplacements().replace(values.value()));
-232 Set<String> possible = Sets.newHashSet();
-233 for (String s : split) {
-234 List<String> check = this.manager.getCommandCompletions().getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
-235 if (!check.isEmpty()) {
-236 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
-237 } else {
-238 possible.add(s.toLowerCase());
-239 }
-240 }
-241
-242 if (!possible.contains(arg.toLowerCase())) {
-243 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
-244 "{valid}", ACFUtil.join(possible, ", "));
-245 }
-246 }
-247 passedArgs.put(parameterName, resolver.getContext(context));
-248 }
-249 return passedArgs;
-250 }
-251
-252 boolean hasPermission(CommandIssuer issuer) {
-253 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
+219 Object value = isOptionalResolver(resolver) ? resolver.getContext(context) : null;
+220 if (value == null && parameter.getClass().isPrimitive()) {
+221 throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
+222 }
+223 passedArgs.put(parameterName, value);
+224 //noinspection UnnecessaryContinue
+225 continue;
+226 } else if (!isOptionalResolver) {
+227 scope.showSyntax(sender, this);
+228 return null;
+229 }
+230 }
+231 final Values values = parameter.getAnnotation(Values.class);
+232 if (values != null) {
+233 String arg = !args.isEmpty() ? args.get(0) : "";
+234
+235 final String[] split = ACFPatterns.PIPE.split(scope.manager.getCommandReplacements().replace(values.value()));
+236 Set<String> possible = Sets.newHashSet();
+237 for (String s : split) {
+238 List<String> check = this.manager.getCommandCompletions().getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
+239 if (!check.isEmpty()) {
+240 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
+241 } else {
+242 possible.add(s.toLowerCase());
+243 }
+244 }
+245
+246 if (!possible.contains(arg.toLowerCase())) {
+247 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF,
+248 "{valid}", ACFUtil.join(possible, ", "));
+249 }
+250 }
+251 passedArgs.put(parameterName, resolver.getContext(context));
+252 }
+253 return passedArgs;
254 }
255
-256
-257 /**
-258 * @see #getRequiredPermissions()
-259 * @deprecated
-260 */
-261 @Deprecated
-262 public String getPermission() {
-263 if (this.permission == null || this.permission.isEmpty()) {
-264 return null;
-265 }
-266 return ACFPatterns.COMMA.split(this.permission)[0];
-267 }
-268
-269 public Set<String> getRequiredPermissions() {
-270 if (this.permission == null || this.permission.isEmpty()) {
-271 return ImmutableSet.of();
-272 }
-273 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
-274 }
-275
-276 public boolean requiresPermission(String permission) {
-277 return getRequiredPermissions().contains(permission) || scope.requiresPermission(permission);
+256 boolean hasPermission(CommandIssuer issuer) {
+257 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer);
+258 }
+259
+260
+261 /**
+262 * @see #getRequiredPermissions()
+263 * @deprecated
+264 */
+265 @Deprecated
+266 public String getPermission() {
+267 if (this.permission == null || this.permission.isEmpty()) {
+268 return null;
+269 }
+270 return ACFPatterns.COMMA.split(this.permission)[0];
+271 }
+272
+273 public Set<String> getRequiredPermissions() {
+274 if (this.permission == null || this.permission.isEmpty()) {
+275 return ImmutableSet.of();
+276 }
+277 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission));
278 }
279
-280 public String getPrefSubCommand() {
-281 return prefSubCommand;
+280 public boolean requiresPermission(String permission) {
+281 return getRequiredPermissions().contains(permission) || scope.requiresPermission(permission);
282 }
283
-284 public String getSyntaxText() {
-285 return syntaxText;
+284 public String getPrefSubCommand() {
+285 return prefSubCommand;
286 }
287
-288 public String getCommand() {
-289 return command;
+288 public String getSyntaxText() {
+289 return syntaxText;
290 }
291
-292 public void addSubcommand(String cmd) {
-293 this.registeredSubcommands.add(cmd);
+292 public String getCommand() {
+293 return command;
294 }
-295 public void addSubcommands(Collection<String> cmd) {
-296 this.registeredSubcommands.addAll(cmd);
-297 }
-298}
+295
+296 public void addSubcommand(String cmd) {
+297 this.registeredSubcommands.add(cmd);
+298 }
+299 public void addSubcommands(Collection<String> cmd) {
+300 this.registeredSubcommands.addAll(cmd);
+301 }
+302}