Command Conditions and MANY other code changes for Generics <3🌮

This completes and fully enables a new feature called "Conditions"

We already had some forms of conditions built into @Flags such as
on the Player for itemheld.

However, letting end users add additional restrictions to existing context
handlers such as players is not possible without redefining the context.

That's not friendly nor scalable.

Flags will now be primarily only for controlling how to resolve a context,
and then Conditions will then be the way to validate the context and trigger
a failure if the condition is not met.

Conditions can be placed on Command Class, Methods, or individual Parameters.
This commit is contained in:
Aikar
2018-01-08 23:30:27 -05:00
parent 01f47211bb
commit 40eaa9ea07
29 changed files with 255 additions and 291 deletions
@@ -26,8 +26,8 @@ package co.aikar.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class BukkitCommandCompletionContext extends CommandCompletionContext {
BukkitCommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) {
public class BukkitCommandCompletionContext extends CommandCompletionContext<BukkitCommandIssuer> {
BukkitCommandCompletionContext(RegisteredCommand command, BukkitCommandIssuer issuer, String input, String config, String[] args) {
super(command, issuer, input, config, args);
}
@@ -40,7 +40,6 @@ public class BukkitCommandCompletionContext extends CommandCompletionContext {
* @return
*/
public Player getPlayer() {
CommandSender issuer = this.issuer.getIssuer();
return issuer instanceof Player ? (Player) issuer : null;
return this.issuer.getPlayer();
}
}
@@ -45,7 +45,6 @@ public class BukkitCommandExecutionContext extends CommandExecutionContext<Bukki
* @return
*/
public Player getPlayer() {
CommandSender issuer = this.issuer.getIssuer();
return issuer instanceof Player ? (Player) issuer : null;
return this.issuer.getPlayer();
}
}
@@ -43,9 +43,13 @@ public class BukkitCommandIssuer implements CommandIssuer {
}
@Override
public <T> T getIssuer() {
public CommandSender getIssuer() {
//noinspection unchecked
return (T) sender;
return sender;
}
public Player getPlayer() {
return isPlayer() ? (Player) sender : null;
}
@Override
@@ -23,7 +23,6 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil;
import co.aikar.timings.lib.MCTiming;
import co.aikar.timings.lib.TimingManager;
@@ -59,9 +58,7 @@ public class BukkitCommandManager extends CommandManager<
ChatColor,
BukkitMessageFormatter,
BukkitCommandExecutionContext,
BukkitCommandCompletionContext,
BukkitConditionContext,
BukkitParameterConditionContext<?>
BukkitConditionContext
> {
@SuppressWarnings("WeakerAccess")
@@ -304,7 +301,7 @@ public class BukkitCommandManager extends CommandManager<
@Override
public BukkitCommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) {
return new BukkitCommandCompletionContext(command, sender, input, config, args);
return new BukkitCommandCompletionContext(command, (BukkitCommandIssuer) sender, input, config, args);
}
@Override
@@ -313,14 +310,10 @@ public class BukkitCommandManager extends CommandManager<
}
@Override
public BukkitConditionContext createConditionContext(CommandOperationContext context, Conditions conditions) {
return new BukkitConditionContext(context.getRegisteredCommand(), (BukkitCommandIssuer) context.getCommandIssuer(), conditions);
public BukkitConditionContext createConditionContext(CommandIssuer issuer, String config) {
return new BukkitConditionContext((BukkitCommandIssuer) issuer, config);
}
@Override
public <P> BukkitParameterConditionContext createConditionContext(CommandOperationContext context, BukkitCommandExecutionContext execContext, Conditions conditions) {
return new BukkitParameterConditionContext<P>(context.getRegisteredCommand(), (BukkitCommandIssuer) context.getCommandIssuer(), execContext, conditions);
}
@Override
public void log(LogLevel level, String message, Throwable throwable) {
@@ -23,10 +23,19 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class BukkitConditionContext extends ConditionContext<BukkitCommandIssuer> {
BukkitConditionContext(RegisteredCommand cmd, BukkitCommandIssuer issuer, Conditions condAnno) {
super(cmd, issuer, condAnno);
BukkitConditionContext(BukkitCommandIssuer issuer, String config) {
super(issuer, config);
}
public CommandSender getSender() {
return getIssuer().getIssuer();
}
public Player getPlayer() {
return getIssuer().getPlayer();
}
}
@@ -24,14 +24,19 @@
package co.aikar.commands;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class BungeeCommandCompletionContext extends CommandCompletionContext {
public class BungeeCommandCompletionContext extends CommandCompletionContext<BungeeCommandIssuer> {
BungeeCommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) {
BungeeCommandCompletionContext(RegisteredCommand command, BungeeCommandIssuer issuer, String input, String config, String[] args) {
super(command, issuer, input, config, args);
}
public CommandSender getSender() {
return this.getIssuer().getIssuer();
}
public ProxiedPlayer getPlayer() {
return this.issuer.getPlayer();
}
}
@@ -24,6 +24,7 @@
package co.aikar.commands;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.lang.reflect.Parameter;
import java.util.List;
@@ -38,4 +39,8 @@ public class BungeeCommandExecutionContext extends CommandExecutionContext<Bunge
public CommandSender getSender() {
return this.issuer.getIssuer();
}
public ProxiedPlayer getPlayer() {
return this.issuer.getPlayer();
}
}
@@ -28,7 +28,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Objects;
public class BungeeCommandIssuer implements CommandIssuer{
public class BungeeCommandIssuer implements CommandIssuer {
private final BungeeCommandManager manager;
private final CommandSender sender;
@@ -39,9 +39,12 @@ public class BungeeCommandIssuer implements CommandIssuer{
@Override
public <T> T getIssuer() {
//noinspection unchecked
return (T) sender;
public CommandSender getIssuer() {
return sender;
}
public ProxiedPlayer getPlayer() {
return isPlayer() ? (ProxiedPlayer) sender : null;
}
@Override
@@ -23,7 +23,6 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
@@ -43,9 +42,7 @@ public class BungeeCommandManager extends CommandManager<
ChatColor,
BungeeMessageFormatter,
BungeeCommandExecutionContext,
BungeeCommandCompletionContext,
BungeeConditionContext,
BungeeParameterConditionContext<?>
BungeeConditionContext
> {
protected final Plugin plugin;
@@ -160,26 +157,19 @@ public class BungeeCommandManager extends CommandManager<
@Override
public CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) {
return new BungeeCommandCompletionContext(command, sender, input, config, args);
return new BungeeCommandCompletionContext(command, (BungeeCommandIssuer) sender, input, config, args);
}
@Override
public BungeeConditionContext createConditionContext(CommandOperationContext context, Conditions conditions) {
return new BungeeConditionContext(context.getRegisteredCommand(), (BungeeCommandIssuer) context.getCommandIssuer(), conditions);
}
@Override
public <P> BungeeParameterConditionContext createConditionContext(CommandOperationContext context, BungeeCommandExecutionContext execContext, Conditions conditions) {
return new BungeeParameterConditionContext<P>(context.getRegisteredCommand(), (BungeeCommandIssuer) context.getCommandIssuer(), execContext, conditions);
}
@Override
public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
return new RegisteredCommand(command, cmdName, method, prefSubCommand);
}
@Override
public BungeeConditionContext createConditionContext(CommandIssuer issuer, String config) {
return new BungeeConditionContext((BungeeCommandIssuer) issuer, config);
}
@Override
public void log(LogLevel level, String message, Throwable throwable) {
Logger logger = this.plugin.getLogger();
@@ -1,9 +1,19 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class BungeeConditionContext extends ConditionContext <BungeeCommandIssuer> {
BungeeConditionContext(RegisteredCommand cmd, BungeeCommandIssuer issuer, Conditions condAnno) {
super(cmd, issuer, condAnno);
BungeeConditionContext(BungeeCommandIssuer issuer, String config) {
super(issuer, config);
}
public CommandSender getSender() {
return getIssuer().getIssuer();
}
public ProxiedPlayer getPlayer() {
return getIssuer().getPlayer();
}
}
@@ -1,9 +0,0 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
public class BungeeParameterConditionContext <P> extends ParameterConditionContext<P, BungeeCommandExecutionContext, BungeeCommandIssuer> {
BungeeParameterConditionContext(RegisteredCommand cmd, BungeeCommandIssuer issuer, BungeeCommandExecutionContext execContext, Conditions conditions) {
super(cmd, issuer, execContext, conditions);
}
}
@@ -71,7 +71,7 @@ public abstract class BaseCommand {
private String execSubcommand;
@SuppressWarnings("WeakerAccess")
private String[] origArgs;
CommandManager<?, ?, ?, ?, ?, ?, ?, ?> manager = null;
CommandManager<?, ?, ?, ?, ?, ?> manager = null;
BaseCommand parentCommand;
Map<String, RootCommand> registeredCommands = new HashMap<>();
String description;
@@ -443,7 +443,7 @@ public abstract class BaseCommand {
return;
}
List<String> sargs = Lists.newArrayList(args);
cmd.invoke(issuer, sargs);
cmd.invoke(issuer, sargs, commandOperationContext);
} else {
issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED);
}
@@ -30,15 +30,15 @@ import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;
public class CommandCompletionContext {
public class CommandCompletionContext <I extends CommandIssuer> {
private final RegisteredCommand command;
protected final CommandIssuer issuer;
protected final I issuer;
private final String input;
private final String config;
private final Map<String, String> configs = Maps.newHashMap();
private final List<String> args;
CommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) {
CommandCompletionContext(RegisteredCommand command, I issuer, String input, String config, String[] args) {
this.command = command;
this.issuer = issuer;
this.input = input;
@@ -27,88 +27,92 @@ import co.aikar.commands.annotation.Conditions;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
@SuppressWarnings("BooleanMethodIsAlwaysInverted") // No IDEA, you are wrong
public class CommandConditions <
I extends CommandIssuer,
M extends CommandManager,
CC extends ConditionContext,
CEC extends CommandExecutionContext<CEC, I>,
PCC extends ParameterConditionContext<?, CEC, I>
CC extends ConditionContext<I>
> {
M manager;
Map<String, Condition<CC>> conditions = Maps.newHashMap();
Table<Class<?>, String, ParameterCondition<PCC>> paramConditions = HashBasedTable.create();
CommandConditions(M manager) {
private CommandManager manager;
private Map<String, Condition<I>> conditions = Maps.newHashMap();
private Table<Class<?>, String, ParameterCondition<?, ?, ?>> paramConditions = HashBasedTable.create();
CommandConditions(CommandManager manager) {
this.manager = manager;
}
Condition<CC> addCondition(String id, Condition<CC> handler) {
public Condition<I> addCondition(@NotNull String id, @NotNull Condition<I> handler) {
return this.conditions.put(id.toLowerCase(), handler);
}
<P> ParameterCondition addCondition(Class<P> clazz, String id, ParameterCondition<PCC> handler) {
public <P> ParameterCondition addCondition(Class<P> clazz, @NotNull String id,
@NotNull ParameterCondition<P, CEC, I> handler) {
return this.paramConditions.put(clazz, id.toLowerCase(), handler);
}
boolean validateConditions(CommandOperationContext context, CEC cec) {
void validateConditions(CEC cec, Object value) throws InvalidCommandArgument {
Conditions conditions = cec.getParam().getAnnotation(Conditions.class);
return conditions == null || validateConditions(conditions, context, cec);
validateConditions(conditions, cec, value);
}
boolean validateConditions(CommandOperationContext context) {
void validateConditions(CommandOperationContext context) throws InvalidCommandArgument {
RegisteredCommand cmd = context.getRegisteredCommand();
Conditions conditions = cmd.method.getAnnotation(Conditions.class);
if (conditions != null) {
if (!validateConditions(conditions, context)) {
return false;
}
}
return validateConditions(cmd.scope, context);
validateConditions(conditions, context);
validateConditions(cmd.scope, context);
}
private boolean validateConditions(BaseCommand scope, CommandOperationContext operationContext) {
private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument {
Conditions conditions = scope.getClass().getAnnotation(Conditions.class);
//noinspection SimplifiableIfStatement
if (!validateConditions(conditions, operationContext)) {
return false;
validateConditions(conditions, operationContext);
if (scope.parentCommand != null) {
validateConditions(scope.parentCommand, operationContext);
}
return scope.parentCommand == null || validateConditions(scope.parentCommand, operationContext);
}
private boolean validateConditions(Conditions condAnno, CommandOperationContext context) {
private void validateConditions(Conditions condAnno, CommandOperationContext context) throws InvalidCommandArgument {
if (condAnno == null) {
return true;
return;
}
//noinspection unchecked
CC conditionContext = (CC) this.manager.createConditionContext(context, condAnno);
String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
for (String cond : ACFPatterns.PIPE.split(conditions)) {
String[] split = ACFPatterns.EQUALS.split(cond, 2);
Condition<CC> condition = this.conditions.get(split[0].toLowerCase());
String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
CommandIssuer issuer = context.getCommandIssuer();
for (String cond : ACFPatterns.PIPE.split(conditions)) {
String[] split = ACFPatterns.COLON.split(cond, 2);
String id = split[0].toLowerCase();
Condition<I> condition = this.conditions.get(id);
if (condition == null) {
RegisteredCommand cmd = context.getRegisteredCommand();
this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName());
continue;
}
String config = split.length == 2 ? split[1] : null;
//noinspection unchecked
CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
//noinspection unchecked
if (!condition.validateCondition(conditionContext)) {
return false;
return;
}
}
return true;
}
private boolean validateConditions(Conditions condAnno, CommandOperationContext context, CEC execContext) {
private void validateConditions(Conditions condAnno, CEC execContext, Object value) throws InvalidCommandArgument {
if (condAnno == null) {
return true;
return;
}
//noinspection unchecked
ParameterConditionContext conditionContext = this.manager.createConditionContext(context, execContext, condAnno);
String conditions = this.manager.getCommandReplacements().replace(condAnno.value());
I issuer = execContext.getIssuer();
for (String cond : ACFPatterns.PIPE.split(conditions)) {
String[] split = ACFPatterns.EQUALS.split(cond, 2);
String[] split = ACFPatterns.COLON.split(cond, 2);
ParameterCondition condition;
Class<?> cls = execContext.getParam().getClass();
Class<?> cls = execContext.getParam().getType();
String id = split[0].toLowerCase();
do {
condition = this.paramConditions.get(cls, id);
@@ -119,19 +123,28 @@ public class CommandConditions <
}
} while (cls != null);
if (condition == null) {
RegisteredCommand cmd = execContext.getCmd();
this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName());
continue;
}
String config = split.length == 2 ? split[1] : null;
//noinspection unchecked
if (condition != null && !condition.validateCondition(conditionContext)) {
return false;
CC conditionContext = (CC) this.manager.createConditionContext(issuer, config);
//noinspection unchecked
if (!condition.validateCondition(conditionContext, execContext, value)) {
return;
}
}
return true;
}
interface Condition <CC extends ConditionContext> {
boolean validateCondition(CC context);
public interface Condition <I extends CommandIssuer> {
boolean validateCondition(ConditionContext<I> context) throws InvalidCommandArgument;
}
interface ParameterCondition <PCC extends ParameterConditionContext> {
boolean validateCondition(PCC context);
public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> {
boolean validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument;
}
}
@@ -47,9 +47,7 @@ public abstract class CommandManager <
FT,
MF extends MessageFormatter<FT>,
CEC extends CommandExecutionContext<CEC, I>,
CCC extends CommandCompletionContext,
CC extends ConditionContext<I>,
PCC extends ParameterConditionContext<?, CEC, I>
CC extends ConditionContext<I>
> {
/**
@@ -65,6 +63,7 @@ public abstract class CommandManager <
});
protected Map<String, RootCommand> rootCommands = new HashMap<>();
protected final CommandReplacements replacements = new CommandReplacements(this);
protected final CommandConditions<I, CEC, CC> conditions = new CommandConditions<>(this);
protected ExceptionHandler defaultExceptionHandler = null;
protected boolean usePerIssuerLocale = false;
@@ -118,6 +117,10 @@ public abstract class CommandManager <
this.defaultFormatter = defaultFormatter;
}
public CommandConditions<I, CEC, CC> getCommandConditions() {
return conditions;
}
/**
* Gets the command contexts manager
* @return Command Contexts
@@ -205,14 +208,10 @@ public abstract class CommandManager <
usePerIssuerLocale = setting;
return old;
}
public ConditionContext createConditionContext(CommandOperationContext context, Conditions conditions) {
//noinspection unchecked
return new ConditionContext<>(context.getRegisteredCommand(), context.getCommandIssuer(), conditions);
}
public <P> ParameterConditionContext createConditionContext(CommandOperationContext context, CEC execContext, Conditions conditions) {
public ConditionContext createConditionContext(CommandIssuer issuer, String config) {
//noinspection unchecked
return new ParameterConditionContext<P, CEC, I>(context.getRegisteredCommand(), (I) context.getCommandIssuer(), execContext, conditions);
return new ConditionContext(issuer, config);
}
public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
@@ -391,4 +390,8 @@ public abstract class CommandManager <
throw new IllegalStateException("Using an unstable API that has not been enabled ( " + api + "). See https://acfunstable.emc.gs");
}
}
boolean hasUnstableAPI(String api) {
return unstableAPIs.contains(api);
}
}
@@ -23,42 +23,45 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import com.google.common.collect.Maps;
import java.util.Map;
public class ConditionContext <I extends CommandIssuer> {
private final RegisteredCommand cmd;
private final I issuer;
private final Conditions condAnno;
private final Map<String, String> flags;
private final String config;
private final Map<String, String> configs;
ConditionContext(RegisteredCommand cmd, I issuer, Conditions condAnno) {
this.cmd = cmd;
ConditionContext(I issuer, String config) {
this.issuer = issuer;
this.condAnno = condAnno;
this.flags = Maps.newHashMap();
for (String s : ACFPatterns.COMMA.split(cmd.scope.manager.getCommandReplacements().replace(condAnno.value()))) {
String[] v = ACFPatterns.EQUALS.split(s, 2);
this.flags.put(v[0], v.length > 1 ? v[1] : null);
this.config = config;
this.configs = Maps.newHashMap();
if (config != null) {
for (String s : ACFPatterns.COMMA.split(config)) {
String[] v = ACFPatterns.EQUALS.split(s, 2);
this.configs.put(v[0], v.length > 1 ? v[1] : null);
}
}
}
public I getIssuer() {
return issuer;
}
public boolean hasFlag(String flag) {
return flags.containsKey(flag);
public String getConfig() {
return this.config;
}
public boolean hasConfig(String flag) {
return configs.containsKey(flag);
}
public String getFlagValue(String flag, String def) {
return flags.getOrDefault(flag, def);
public String getConfigValue(String flag, String def) {
return configs.getOrDefault(flag, def);
}
public Integer getFlagValue(String flag, Integer def) {
return ACFUtil.parseInt(this.flags.get(flag), def);
public Integer getConfigValue(String flag, Integer def) {
return ACFUtil.parseInt(this.configs.get(flag), def);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
* Copyright (c) 2016-2018 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -23,10 +23,23 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
public class BukkitParameterConditionContext <P> extends ParameterConditionContext<P, BukkitCommandExecutionContext, BukkitCommandIssuer> {
BukkitParameterConditionContext(RegisteredCommand cmd, BukkitCommandIssuer issuer, BukkitCommandExecutionContext execContext, Conditions conditions) {
super(cmd, issuer, execContext, conditions);
public class ConditionFailedException extends InvalidCommandArgument {
public ConditionFailedException() {
super(false);
}
public ConditionFailedException(MessageKeyProvider key, String... replacements) {
super(key, false, replacements);
}
public ConditionFailedException(MessageKey key, String... replacements) {
super(key, false, replacements);
}
public ConditionFailedException(String message) {
super(message, false);
}
}
@@ -53,19 +53,10 @@ public class InvalidCommandArgument extends Exception {
this.replacements = replacements;
}
/**
* Please move to a MessageKey
* @deprecated
*/
@Deprecated
public InvalidCommandArgument(String message) {
this(message, true);
}
/**
* Please move to a MessageKey
* @deprecated
*/
@Deprecated
public InvalidCommandArgument(String message, boolean showSyntax) {
super(message, null, false, false);
this.showSyntax = showSyntax;
@@ -34,6 +34,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
@SuppressWarnings("WeakerAccess")
@@ -72,7 +73,7 @@ public class Locales {
public static final Locale WELSH = new Locale("cy");
private final CommandManager<?, ?, ?, ?, ?, ?, ?, ?> manager;
private final CommandManager manager;
private final LocaleManager<CommandIssuer> localeManager;
private final SetMultimap<String, Locale> loadedBundles = HashMultimap.create();
@@ -97,7 +98,9 @@ public class Locales {
* Looks for all previously loaded bundles, and if any new Supported Languages have been added, load them.
*/
public void loadMissingBundles() {
for (Locale locale : manager.getSupportedLanguages()) {
//noinspection unchecked
Set<Locale> supportedLanguages = manager.getSupportedLanguages();
for (Locale locale : supportedLanguages) {
for (String bundleName : Sets.newHashSet(loadedBundles.keys())) {
addMessageBundle(bundleName, locale);
}
@@ -106,7 +109,9 @@ public class Locales {
public void addMessageBundles(String... bundleNames) {
for (String bundleName : bundleNames) {
for (Locale locale : manager.getSupportedLanguages()) {
//noinspection unchecked
Set<Locale> supportedLanguages = manager.getSupportedLanguages();
for (Locale locale : supportedLanguages) {
addMessageBundle(bundleName, locale);
}
}
@@ -1,97 +0,0 @@
/*
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
public class ParameterConditionContext <P, CEC extends CommandExecutionContext, I extends CommandIssuer> extends ConditionContext<I> {
private final CEC execContext;
ParameterConditionContext(RegisteredCommand cmd, I issuer, CEC execContext, Conditions conditions) {
super(cmd, issuer, conditions);
this.execContext = execContext;
}
public boolean isLastArg() {
return execContext.isLastArg();
}
public int getNumParams() {
return execContext.getNumParams();
}
public boolean canOverridePlayerContext() {
return execContext.canOverridePlayerContext();
}
public Object getResolvedArg(String arg) {
return execContext.getResolvedArg(arg);
}
public Object getResolvedArg(Class[] classes) {
return execContext.getResolvedArg(classes);
}
public Object getResolvedArg(String key, Class[] classes) {
return execContext.getResolvedArg(key, classes);
}
public boolean isOptional() {
return execContext.isOptional();
}
public Annotation getAnnotation(Class cls) {
return execContext.getAnnotation(cls);
}
public boolean hasAnnotation(Class cls) {
return execContext.hasAnnotation(cls);
}
public List<String> getArgs() {
return execContext.getArgs();
}
public int getIndex() {
return execContext.getIndex();
}
public Map<String, Object> getPassedArgs() {
return execContext.getPassedArgs();
}
public String joinArgs() {
return execContext.joinArgs();
}
public String joinArgs(String sep) {
return execContext.joinArgs(sep);
}
}
@@ -52,7 +52,7 @@ import java.util.Set;
import java.util.stream.Collectors;
@SuppressWarnings("WeakerAccess")
public class RegisteredCommand <CEC extends CommandExecutionContext<? extends CommandExecutionContext, ? extends CommandIssuer>> {
public class RegisteredCommand <CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> {
final BaseCommand scope;
final String command;
final Method method;
@@ -67,7 +67,7 @@ public class RegisteredCommand <CEC extends CommandExecutionContext<? extends Co
final int requiredResolvers;
final int optionalResolvers;
final List<String> registeredSubcommands = new ArrayList<>();
private final CommandManager<?, ?, ?, ?, ?, ?, ?, ?> manager;
private final CommandManager manager;
RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
this.scope = scope;
@@ -89,6 +89,7 @@ public class RegisteredCommand <CEC extends CommandExecutionContext<? extends Co
//noinspection unchecked
this.resolvers = new ContextResolver[this.parameters.length];
final Syntax syntaxStr = method.getAnnotation(Syntax.class);
//noinspection unchecked
final CommandContexts commandContexts = this.manager.getCommandContexts();
int requiredResolvers = 0;
@@ -140,12 +141,13 @@ public class RegisteredCommand <CEC extends CommandExecutionContext<? extends Co
|| resolver instanceof OptionalContextResolver;
}
void invoke(CommandIssuer sender, List<String> args) {
void invoke(CommandIssuer sender, List<String> args, CommandOperationContext context) {
if (!scope.canExecute(sender, this)) {
return;
}
preCommand();
try {
this.manager.conditions.validateConditions(context);
Map<String, Object> passedArgs = resolveContexts(sender, args);
if (passedArgs == null) return;
@@ -220,6 +222,8 @@ public class RegisteredCommand <CEC extends CommandExecutionContext<? extends Co
if (value == null && parameter.getClass().isPrimitive()) {
throw new IllegalStateException("Parameter " + parameter.getName() + " is primitive and does not support Optional.");
}
//noinspection unchecked
this.manager.conditions.validateConditions(context, value);
passedArgs.put(parameterName, value);
//noinspection UnnecessaryContinue
continue;
@@ -235,6 +239,7 @@ public class RegisteredCommand <CEC extends CommandExecutionContext<? extends Co
final String[] split = ACFPatterns.PIPE.split(scope.manager.getCommandReplacements().replace(values.value()));
Set<String> possible = Sets.newHashSet();
for (String s : split) {
//noinspection unchecked
List<String> check = this.manager.getCommandCompletions().getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
if (!check.isEmpty()) {
possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
@@ -248,7 +253,10 @@ public class RegisteredCommand <CEC extends CommandExecutionContext<? extends Co
"{valid}", ACFUtil.join(possible, ", "));
}
}
passedArgs.put(parameterName, resolver.getContext(context));
Object paramValue = resolver.getContext(context);
//noinspection unchecked
this.manager.conditions.validateConditions(context, paramValue);
passedArgs.put(parameterName, paramValue);
}
return passedArgs;
}
@@ -26,9 +26,6 @@ package co.aikar.commands.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @deprecated This isn't ready yet
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Conditions {
String value();
@@ -24,6 +24,7 @@
package co.aikar.acfexample;
import co.aikar.commands.BukkitCommandManager;
import co.aikar.commands.ConditionFailedException;
import co.aikar.commands.MessageKeys;
import co.aikar.commands.MessageType;
import com.google.common.collect.Lists;
@@ -64,17 +65,31 @@ public final class ACFExample extends JavaPlugin {
Lists.newArrayList("foo", "bar", "baz")
));
// 5: Register your commands - This first command demonstrates adding an exception handler to that command
// 5: Register Command Conditions
commandManager.getCommandConditions().addCondition(SomeObject.class, "limits", (c, exec, value) -> {
if (value == null) {
return true;
}
if (c.hasConfig("min") && c.getConfigValue("min", 0) > value.getValue()) {
throw new ConditionFailedException("Min value must be " + c.getConfigValue("min", 0));
}
if (c.hasConfig("max") && c.getConfigValue("max", 3) < value.getValue()) {
throw new ConditionFailedException("Max value must be " + c.getConfigValue("max", 3));
}
return true;
});
// 6: Register your commands - This first command demonstrates adding an exception handler to that command
commandManager.registerCommand(new SomeCommand().setExceptionHandler((command, registeredCommand, sender, args, t) -> {
sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_GENERIC_LOGGED);
return true; // mark as handeled, default message will not be send to sender
}));
// 5: Register an additional command. This one happens to share the same CommandAlias as the previous command
// 7: Register an additional command. This one happens to share the same CommandAlias as the previous command
// This means it simply registers additional sub commands under the same command, but organized into separate
// Classes (Maybe different permission sets)
commandManager.registerCommand(new SomeCommand_ExtraSubs());
// 6: Register default exception handler for any command that doesn't supply its own
// 8: Register default exception handler for any command that doesn't supply its own
commandManager.setDefaultExceptionHandler((command, registeredCommand, sender, args, t) -> {
getLogger().warning("Error occured while executing command " + command.getName());
return false; // mark as unhandeled, sender will see default message
@@ -24,15 +24,20 @@
package co.aikar.commands;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
@SuppressWarnings("WeakerAccess")
public class SpongeCommandCompletionContext extends CommandCompletionContext {
public class SpongeCommandCompletionContext extends CommandCompletionContext<SpongeCommandIssuer> {
SpongeCommandCompletionContext(final RegisteredCommand command, final CommandIssuer issuer, final String input, final String config, final String[] args) {
SpongeCommandCompletionContext(final RegisteredCommand command, final SpongeCommandIssuer issuer, final String input, final String config, final String[] args) {
super(command, issuer, input, config, args);
}
public CommandSource getSource() {
return this.issuer.getIssuer();
}
public Player getPlayer() {
return this.issuer.getPlayer();
}
}
@@ -24,6 +24,7 @@
package co.aikar.commands;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import java.lang.reflect.Parameter;
import java.util.List;
@@ -39,4 +40,8 @@ public class SpongeCommandExecutionContext extends CommandExecutionContext<Spong
public CommandSource getSource() {
return this.issuer.getIssuer();
}
public Player getPlayer() {
return this.issuer.getPlayer();
}
}
@@ -45,9 +45,12 @@ public class SpongeCommandIssuer implements CommandIssuer {
}
@Override
public <T> T getIssuer() {
//noinspection unchecked
return (T) this.source;
public CommandSource getIssuer() {
return this.source;
}
public Player getPlayer() {
return isPlayer() ? (Player) source : null;
}
@Override
@@ -23,7 +23,6 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
@@ -47,9 +46,7 @@ public class SpongeCommandManager extends CommandManager<
TextColor,
SpongeMessageFormatter,
SpongeCommandExecutionContext,
SpongeCommandCompletionContext,
SpongeConditionContext,
SpongeParameterConditionContext<?>
SpongeConditionContext
> {
protected final PluginContainer plugin;
@@ -150,7 +147,7 @@ public class SpongeCommandManager extends CommandManager<
@Override
public CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) {
return new SpongeCommandCompletionContext(command, sender, input, config, args);
return new SpongeCommandCompletionContext(command, (SpongeCommandIssuer) sender, input, config, args);
}
@Override
@@ -192,15 +189,9 @@ public class SpongeCommandManager extends CommandManager<
);
}
@Override
public SpongeConditionContext createConditionContext(CommandOperationContext context, Conditions conditions) {
return new SpongeConditionContext(context.getRegisteredCommand(), (SpongeCommandIssuer) context.getCommandIssuer(), conditions);
}
@Override
public <P> SpongeParameterConditionContext createConditionContext(CommandOperationContext context, SpongeCommandExecutionContext execContext, Conditions conditions) {
return new SpongeParameterConditionContext<P>(context.getRegisteredCommand(), (SpongeCommandIssuer) context.getCommandIssuer(), execContext, conditions);
public SpongeConditionContext createConditionContext(CommandIssuer issuer, String config) {
return new SpongeConditionContext((SpongeCommandIssuer) issuer, config);
}
}
@@ -1,9 +1,19 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
public class SpongeConditionContext extends ConditionContext <SpongeCommandIssuer> {
SpongeConditionContext(RegisteredCommand cmd, SpongeCommandIssuer issuer, Conditions condAnno) {
super(cmd, issuer, condAnno);
SpongeConditionContext(SpongeCommandIssuer issuer, String config) {
super(issuer, config);
}
public CommandSource getSource() {
return getIssuer().getIssuer();
}
public Player getPlayer() {
return getIssuer().getPlayer();
}
}
@@ -1,9 +0,0 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Conditions;
public class SpongeParameterConditionContext <P> extends ParameterConditionContext<P, SpongeCommandExecutionContext, SpongeCommandIssuer> {
SpongeParameterConditionContext(RegisteredCommand cmd, SpongeCommandIssuer issuer, SpongeCommandExecutionContext execContext, Conditions conditions) {
super(cmd, issuer, execContext, conditions);
}
}