Almost done with modular work

This commit is contained in:
Aikar
2017-06-03 00:11:43 -04:00
parent 0bf2621a91
commit 7bf24e9c13
27 changed files with 429 additions and 208 deletions
+4 -3
View File
@@ -30,12 +30,12 @@
<parent>
<groupId>co.aikar</groupId>
<artifactId>acf-parent</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>acf-bukkit</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<name>ACF (Bukkit)</name>
@@ -43,7 +43,8 @@
<dependency>
<groupId>co.aikar</groupId>
<artifactId>acf-core</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bukkit</groupId>
@@ -31,6 +31,7 @@ import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -61,7 +62,7 @@ public class ACFBukkitUtil {
return ChatColor.translateAlternateColorCodes('&', message);
}
public static void sendMsg(CommandIssuer player, String message) {
public static void sendMsg(CommandSender player, String message) {
message = color(message);
if (player == null) {
for (String msg : ACFPatterns.NEWLINE.split(message)) {
@@ -239,7 +240,7 @@ public class ACFBukkitUtil {
return loc1.getWorld() == loc2.getWorld() && loc1.distance(loc2) <= dist;
}
public static Player findPlayerSmart(CommandIssuer requester, String origName) {
public static Player findPlayerSmart(CommandSender requester, String origName) {
String name = ACFUtil.replace(origName, ":confirm", "");
if (name.length() < 3) {
requester.sendMessage("§cUsername too short, must be at least three characters");
@@ -0,0 +1,36 @@
/*
* 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 org.bukkit.command.CommandSender;
public class BukkitCommandCompletionContext extends CommandCompletionContext {
BukkitCommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) {
super(command, issuer, input, config, args);
}
public CommandSender getSender() {
return this.getIssuer().getIssuer();
}
}
@@ -27,21 +27,19 @@ import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@SuppressWarnings("WeakerAccess")
public class BukkitCommandCompletions extends CommandCompletions {
public class BukkitCommandCompletions extends CommandCompletions<CommandSender, BukkitCommandCompletionContext> {
public BukkitCommandCompletions(BukkitCommandManager manager) {
super(manager);
registerCompletion("mobs", (sender, config, input, c) -> {
@@ -74,7 +72,7 @@ public class BukkitCommandCompletions extends CommandCompletions {
Player senderPlayer = sender instanceof Player ? (Player) sender : null;
ArrayList<String> matchedPlayers = new ArrayList<String>();
for (Player player : sender.getServer().getOnlinePlayers()) {
for (Player player : Bukkit.getOnlinePlayers()) {
String name = player.getName();
if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, input)) {
matchedPlayers.add(name);
@@ -46,12 +46,12 @@ public class BukkitCommandContexts extends CommandContexts<BukkitCommandExecutio
registerContext(OnlinePlayer.class, (c) -> {
final String playercheck = c.popFirstArg();
Player player = ACFBukkitUtil.findPlayerSmart(c.getIssuer(), playercheck);
Player player = ACFBukkitUtil.findPlayerSmart(c.getSender(), playercheck);
if (player == null) {
if (c.hasAnnotation(Optional.class)) {
return null;
}
ACFBukkitUtil.sendMsg(c.getIssuer(), "&cCould not find a player by the name " + playercheck);
ACFBukkitUtil.sendMsg(c.getSender(), "&cCould not find a player by the name " + playercheck);
throw new InvalidCommandArgument(false);
}
return new OnlinePlayer(player);
@@ -62,17 +62,17 @@ public class BukkitCommandContexts extends CommandContexts<BukkitCommandExecutio
if (world != null) {
c.popFirstArg();
}
if (world == null && c.getIssuer() instanceof Player) {
world = ((Entity) c.getIssuer()).getWorld();
if (world == null && c.getSender() instanceof Player) {
world = ((Entity) c.getSender()).getWorld();
}
if (world == null) {
throw new InvalidCommandArgument("Invalid World");
}
return world;
});
registerSenderAwareContext(CommandSender.class, bukkitCommandExecutionContext -> bukkitCommandExecutionContext.getSender());
registerSenderAwareContext(CommandSender.class, BukkitCommandExecutionContext::getSender);
registerSenderAwareContext(Player.class, (c) -> {
Player player = c.getIssuer() instanceof Player ? (Player) c.getIssuer() : null;
Player player = c.getSender() instanceof Player ? (Player) c.getSender() : null;
if (player == null && !c.hasAnnotation(Optional.class)) {
throw new InvalidCommandArgument("Requires a player to run this command", false);
}
@@ -31,6 +31,7 @@ import org.bukkit.command.CommandException;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
@@ -39,7 +40,10 @@ import org.bukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@SuppressWarnings("WeakerAccess")
public class BukkitCommandManager extends CommandManager {
@@ -50,8 +54,8 @@ public class BukkitCommandManager extends CommandManager {
private final TimingManager timingManager;
protected Map<String, Command> knownCommands = new HashMap<>();
protected Map<String, BukkitRootCommand> registeredCommands = new HashMap<>();
protected CommandContexts contexts;
protected CommandCompletions completions;
protected BukkitCommandContexts contexts;
protected BukkitCommandCompletions completions;
public BukkitCommandManager(Plugin plugin) {
this.plugin = plugin;
@@ -87,7 +91,12 @@ public class BukkitCommandManager extends CommandManager {
}
@Override
public synchronized CommandContexts getCommandContexts() {
public boolean isCommandIssuer(Class<?> type) {
return CommandSender.class.isAssignableFrom(type);
}
@Override
public synchronized CommandContexts<BukkitCommandExecutionContext> getCommandContexts() {
if (this.contexts == null) {
this.contexts = new BukkitCommandContexts(this);
}
@@ -95,7 +104,7 @@ public class BukkitCommandManager extends CommandManager {
}
@Override
public synchronized CommandCompletions getCommandCompletions() {
public synchronized CommandCompletions<CommandSender, BukkitCommandCompletionContext> getCommandCompletions() {
if (this.completions == null) {
this.completions = new BukkitCommandCompletions(this);
}
@@ -155,7 +164,6 @@ public class BukkitCommandManager extends CommandManager {
}
}
@Override
public TimingManager getTimings() {
return timingManager;
}
@@ -166,8 +174,21 @@ public class BukkitCommandManager extends CommandManager {
}
@Override
public CommandExecutionContext<? extends CommandExecutionContext> createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs) {
return new BukkitCommandExecutionContext(command, parameter, sender, args, i, passedArgs);
public CommandIssuer getCommandIssuer(Object issuer) {
if (!(issuer instanceof CommandSender)) {
throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Command Issuer.");
}
return new BukkitCommandIssuer((CommandSender) issuer);
}
@Override
public <R extends CommandExecutionContext> R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs) {
return (R) new BukkitCommandExecutionContext(command, parameter, sender, args, i, passedArgs);
}
@Override
public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
return new RegisteredCommand(command, cmdName, method, prefSubCommand);
}
class ProxyCommandMap extends SimpleCommandMap {
@@ -0,0 +1,49 @@
/*
* 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.timings.lib.MCTiming;
import java.lang.reflect.Method;
public class BukkitRegisteredCommand extends RegisteredCommand<BukkitCommandExecutionContext> {
private final MCTiming timing;
BukkitRegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
super(scope, command, method, prefSubCommand);
this.timing = ((BukkitCommandManager) scope.manager).getTimings().of("Command: " + command);
}
@Override
public void preCommand() {
timing.startTiming();
super.preCommand();
}
@Override
public void postCommand() {
super.postCommand();
timing.stopTiming();
}
}
+1 -1
View File
@@ -17,7 +17,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: co.aikar:minecraft-timings:1.0.3" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:17.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:17.0" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
</component>
</module>
+2 -2
View File
@@ -30,12 +30,12 @@
<parent>
<groupId>co.aikar</groupId>
<artifactId>acf-parent</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>acf-core</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<name>ACF (Core)</name>
@@ -26,10 +26,10 @@ package co.aikar.commands;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Default;
import co.aikar.commands.annotation.PreCommand;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.UnknownHandler;
import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
import co.aikar.timings.lib.MCTiming;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -59,6 +59,7 @@ public abstract class BaseCommand {
public static final String UNKNOWN = "__unknown";
public static final String DEFAULT = "__default";
final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create();
Method preCommandHandler;
@SuppressWarnings("WeakerAccess")
private String execLabel;
@@ -73,6 +74,11 @@ public abstract class BaseCommand {
String usageMessage;
String permission;
public BaseCommand() {}
public BaseCommand(String cmd) {
this.commandName = cmd;
}
/**
* Gets the root command name that the user actually typed
* @return Name
@@ -98,7 +104,7 @@ public abstract class BaseCommand {
}
void onRegister(CommandManager manager) {
onRegister(manager, null);
onRegister(manager, this.commandName);
}
void onRegister(CommandManager manager, String cmd) {
this.manager = manager;
@@ -141,18 +147,21 @@ public abstract class BaseCommand {
ACFUtil.sneaky(new IllegalStateException("Multiple @Default commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
}
}
if (sub != null) {
sublist = sub;
} else if (commandAliases != null) {
sublist = commandAliases.value();
}
if (sublist != null) {
registerSubcommand(method, sublist);
}
//CommandIssuer.class, String.class, String[].class
UnknownHandler unknown = method.getAnnotation(UnknownHandler.class);
//CommandIssuer.class, String.class, String[].class
PreCommand preCommand = method.getAnnotation(PreCommand.class);
if (unknown != null) {
if (!foundUnknown) {
registerSubcommand(method, UNKNOWN);
@@ -160,6 +169,14 @@ public abstract class BaseCommand {
} else {
ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
}
} else if (preCommand != null) {
if (this.preCommandHandler == null) {
this.preCommandHandler = method;
} else {
ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName()));
}
} else if (sublist != null) {
registerSubcommand(method, sublist);
}
}
@@ -246,7 +263,7 @@ public abstract class BaseCommand {
final String[] aliasNames = cmdAlias != null ? ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(cmdAlias.value().toLowerCase())) : null;
String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " ";
RegisteredCommand cmd = new RegisteredCommand(this, cmdName, method, prefSubCommand);
RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand);
for (String subcmd : cmdList) {
subCommands.put(subcmd, cmd);
@@ -305,7 +322,7 @@ public abstract class BaseCommand {
origArgs = args;
if (args.length == 0) {
if (preCommand(sender, commandLabel, args)) {
if (checkPrecommand(sender, args)) {
return true;
}
executeSubcommand(DEFAULT, sender);
@@ -316,7 +333,7 @@ public abstract class BaseCommand {
if (cmd != null) {
execSubcommand = cmd.getCheckSub();
final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length);
if (preCommand(sender, commandLabel, execargs)) {
if (checkPrecommand(sender, execargs)) {
return true;
}
executeCommand(sender, execargs, cmd.cmd);
@@ -371,15 +388,13 @@ public abstract class BaseCommand {
private static void executeCommand(CommandIssuer sender, String[] args, RegisteredCommand cmd) {
if (cmd.hasPermission(sender)) {
List<String> sargs = Lists.newArrayList(args);
try (MCTiming timing = cmd.getTiming().startTiming()) {
cmd.invoke(sender, sargs);
}
cmd.invoke(sender, sargs);
} else {
sender.sendMessage("&cI'm sorry, but you do not have permission to perform this command.");
}
}
public boolean canExecute(CommandIssuer sender, RegisteredCommand cmd) {
public boolean canExecute(CommandIssuer sender, RegisteredCommand<?> cmd) {
return true;
}
@@ -416,12 +431,9 @@ public abstract class BaseCommand {
}
private List<String> completeCommand(CommandIssuer sender, RegisteredCommand cmd, String[] args, String commandLabel) {
if (args.length > cmd.requiredResolvers + cmd.optionalResolvers) {
if (args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0 || cmd.complete == null) {
return ImmutableList.of();
}
if (args.length == 0 || cmd.complete == null) {
return args.length < 2 ? null : ImmutableList.of();
}
String[] completions = ACFPatterns.SPACE.split(cmd.complete);
@@ -436,9 +448,6 @@ public abstract class BaseCommand {
.collect(Collectors.toList());
}
public void help(CommandIssuer sender, String[] args) {
sender.sendMessage("&cUnknown Command, please type &f/help");
}
private boolean executeSubcommand(String subcommand, CommandIssuer sender, String... args) {
final Set<RegisteredCommand> defs = subCommands.get(subcommand);
@@ -455,24 +464,44 @@ public abstract class BaseCommand {
return false;
}
public boolean preCommand(CommandIssuer sender, String commandLabel, String[] args) {
private boolean checkPrecommand(CommandIssuer sender, String[] args) {
if (this.preCommandHandler != null) {
try {
if (this.preCommandHandler.getParameterCount() == 1) {
return (boolean) preCommandHandler.invoke(this, new Object[]{ sender.getIssuer() });
} else {
return (boolean) preCommandHandler.invoke(this, sender.getIssuer(), args);
}
} catch (IllegalAccessException | InvocationTargetException e) {
ACFLog.exception(e);
}
}
return false;
}
public void help(Object sender, String[] args) {
help(manager.getCommandIssuer(sender), args);
}
public void help(CommandIssuer sender, String[] args) {
sender.sendMessage("&cUnknown Command, please type &f/help");
}
public void doHelp(Object sender, String... args) {
doHelp(manager.getCommandIssuer(sender), args);
}
public void doHelp(CommandIssuer sender, String... args) {
help(sender, args);
}
public void showSyntax(CommandIssuer sender, RegisteredCommand cmd) {
public void showSyntax(CommandIssuer sender, RegisteredCommand<?> cmd) {
sender.sendMessage("&cUsage: /" + cmd.command + " " + cmd.syntaxText);
}
public boolean testPermission(Object sender) {
return testPermission(manager.getCommandIssuer(sender));
}
public boolean testPermission(CommandIssuer sender) {
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) {
// TODO: Msg
return false;
}
return true;
return permission == null || permission.isEmpty() || sender.hasPermission(permission);
}
public String getName() {
@@ -0,0 +1,127 @@
/*
* 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 com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;
public class CommandCompletionContext {
private final RegisteredCommand command;
private final CommandIssuer 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) {
this.command = command;
this.issuer = issuer;
this.input = input;
if (config != null) {
String[] configs = ACFPatterns.COMMA.split(config);
for (String conf : configs) {
String[] confsplit = ACFPatterns.EQUALS.split(conf, 2);
this.configs.put(confsplit[0].toLowerCase(), confsplit.length > 1 ? confsplit[1] : null);
}
this.config = configs[0];
} else {
this.config = null;
}
this.args = Lists.newArrayList(args);
}
public Map<String, String> getConfigs() {
return configs;
}
public String getConfig(String key) {
return getConfig(key, null);
}
public String getConfig(String key, String def) {
return this.configs.getOrDefault(key.toLowerCase(), def);
}
public boolean hasConfig(String key) {
return this.configs.containsKey(key.toLowerCase());
}
public <T> T getContextValue(Class<? extends T> clazz) throws InvalidCommandArgument {
return getContextValue(clazz, null);
}
public <T> T getContextValue(Class<? extends T> clazz, Integer paramIdx) throws InvalidCommandArgument {
String name = null;
if (paramIdx != null) {
if (paramIdx >= command.parameters.length) {
throw new IllegalArgumentException("Param index is higher than number of parameters");
}
Parameter param = command.parameters[paramIdx];
Class<?> paramType = param.getType();
if (!clazz.isAssignableFrom(paramType)) {
throw new IllegalArgumentException(param.getName() +":" + paramType.getName() + " can not satisfy " + clazz.getName());
}
name = param.getName();
} else {
Parameter[] parameters = command.parameters;
for (int i = 0; i < parameters.length; i++) {
Parameter param = parameters[i];
if (clazz.isAssignableFrom(param.getType())) {
paramIdx = i;
name = param.getName();
break;
}
}
if (paramIdx == null) {
throw new IllegalStateException("Can not find any parameter that can satisfy " + clazz.getName());
}
}
//noinspection unchecked
Map<String, Object> resolved = command.resolveContexts(issuer, args, args.size());
if (resolved == null || paramIdx > resolved.size()) {
ACFLog.error("resolved: " + resolved + " paramIdx: " + paramIdx + " - size: " + (resolved != null ? resolved.size() : null ));
ACFUtil.sneaky(new CommandCompletionTextLookupException());
}
//noinspection unchecked
return (T) resolved.get(name);
}
public CommandIssuer getIssuer() {
return issuer;
}
public String getInput() {
return input;
}
public String getConfig() {
return config;
}
}
@@ -0,0 +1,27 @@
/*
* 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;
class CommandCompletionTextLookupException extends Throwable {
}
@@ -23,13 +23,10 @@
package co.aikar.commands;
import co.aikar.commands.annotation.Split;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -39,7 +36,7 @@ import java.util.stream.IntStream;
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
public class CommandCompletions {
public class CommandCompletions <I, C extends CommandCompletionContext> {
private final CommandManager manager;
private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
@@ -64,7 +61,7 @@ public class CommandCompletions {
registerCompletion("timeunits", (sender, config, input, c) -> ImmutableList.of("minutes", "hours", "days", "weeks", "months", "years"));
}
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler handler) {
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<I, C> handler) {
return this.completionMap.put("@" + id.toLowerCase(), handler);
}
@@ -121,105 +118,8 @@ public class CommandCompletions {
return allCompletions;
}
public interface CommandCompletionHandler {
Collection<String> getCompletions(CommandIssuer sender, String config, String input, CommandCompletionContext context) throws InvalidCommandArgument;
public interface CommandCompletionHandler <I, C extends CommandCompletionContext> {
Collection<String> getCompletions(I sender, String config, String input, C context) throws InvalidCommandArgument;
}
public class CommandCompletionContext {
private final RegisteredCommand command;
private final CommandIssuer sender;
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 sender, String input, String config, String[] args) {
this.command = command;
this.sender = sender;
this.input = input;
if (config != null) {
String[] configs = ACFPatterns.COMMA.split(config);
for (String conf : configs) {
String[] confsplit = ACFPatterns.EQUALS.split(conf, 2);
this.configs.put(confsplit[0].toLowerCase(), confsplit.length > 1 ? confsplit[1] : null);
}
this.config = configs[0];
} else {
this.config = null;
}
this.args = Lists.newArrayList(args);
}
public Map<String, String> getConfigs() {
return configs;
}
public String getConfig(String key) {
return getConfig(key, null);
}
public String getConfig(String key, String def) {
return this.configs.getOrDefault(key.toLowerCase(), def);
}
public boolean hasConfig(String key) {
return this.configs.containsKey(key.toLowerCase());
}
public <T> T getContextValue(Class<? extends T> clazz) throws InvalidCommandArgument {
return getContextValue(clazz, null);
}
public <T> T getContextValue(Class<? extends T> clazz, Integer paramIdx) throws InvalidCommandArgument {
String name = null;
if (paramIdx != null) {
if (paramIdx >= command.parameters.length) {
throw new IllegalArgumentException("Param index is higher than number of parameters");
}
Parameter param = command.parameters[paramIdx];
Class<?> paramType = param.getType();
if (!clazz.isAssignableFrom(paramType)) {
throw new IllegalArgumentException(param.getName() +":" + paramType.getName() + " can not satisfy " + clazz.getName());
}
name = param.getName();
} else {
Parameter[] parameters = command.parameters;
for (int i = 0; i < parameters.length; i++) {
Parameter param = parameters[i];
if (clazz.isAssignableFrom(param.getType())) {
paramIdx = i;
name = param.getName();
break;
}
}
if (paramIdx == null) {
throw new IllegalStateException("Can not find any parameter that can satisfy " + clazz.getName());
}
}
Map<String, Object> resolved = command.resolveContexts(sender, args, args.size());
if (resolved == null || paramIdx > resolved.size()) {
ACFLog.error("resolved: " + resolved + " paramIdx: " + paramIdx + " - size: " + (resolved != null ? resolved.size() : null ));
ACFUtil.sneaky(new CommandCompletionTextLookupException());
}
//noinspection unchecked
return (T) resolved.get(name);
}
public CommandIssuer getSender() {
return sender;
}
public String getInput() {
return input;
}
public String getConfig() {
return config;
}
}
private class CommandCompletionTextLookupException extends Throwable {
}
}
@@ -34,7 +34,7 @@ import java.util.List;
import java.util.Map;
@SuppressWarnings("WeakerAccess")
public class CommandContexts <R extends CommandExecutionContext> {
public class CommandContexts <R extends CommandExecutionContext<?>> {
protected final Map<Class<?>, ContextResolver<?, R>> contextMap = Maps.newHashMap();
private final CommandManager manager;
@@ -140,10 +140,10 @@ public class CommandContexts <R extends CommandExecutionContext> {
});
}
<T> void registerSenderAwareContext(Class<T> context, SenderAwareContextResolver<T, R> supplier) {
public <T> void registerSenderAwareContext(Class<T> context, SenderAwareContextResolver<T, R> supplier) {
contextMap.put(context, supplier);
}
<T> void registerContext(Class<T> context, ContextResolver<T, R> supplier) {
public <T> void registerContext(Class<T> context, ContextResolver<T, R> supplier) {
contextMap.put(context, supplier);
}
@@ -23,15 +23,14 @@
package co.aikar.commands;
import co.aikar.timings.lib.TimingManager;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings("WeakerAccess")
public abstract class CommandManager {
abstract class CommandManager {
protected Map<String, RootCommand> rootCommands = new HashMap<>();
protected CommandReplacements replacements = new CommandReplacements(this);
@@ -40,13 +39,13 @@ public abstract class CommandManager {
* Gets the command contexts manager
* @return Command Contexts
*/
public abstract CommandContexts getCommandContexts();
public abstract CommandContexts<?> getCommandContexts();
/**
* Gets the command completions manager
* @return Command Completions
*/
public abstract CommandCompletions getCommandCompletions();
public abstract CommandCompletions<?, ?> getCommandCompletions();
/**
* Lets you add custom string replacements that can be applied to annotation values,
@@ -69,13 +68,18 @@ public abstract class CommandManager {
*/
public abstract void registerCommand(BaseCommand command);
public abstract boolean hasRegisteredCommands();
public abstract TimingManager getTimings();
public abstract boolean isCommandIssuer(Class<?> type);
public abstract CommandIssuer getCommandIssuer(Object issuer);
public abstract RootCommand createRootCommand(String cmd);
public synchronized RootCommand obtainRootCommand(String cmd) {
return rootCommands.computeIfAbsent(cmd.toLowerCase(), this::createRootCommand);
}
public abstract CommandExecutionContext<? extends CommandExecutionContext> createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
public abstract <R extends CommandExecutionContext> R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs);
public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) {
return new RegisteredCommand(command, cmdName, method, prefSubCommand);
}
}
@@ -32,7 +32,6 @@ import co.aikar.commands.annotation.Syntax;
import co.aikar.commands.annotation.Values;
import co.aikar.commands.contexts.ContextResolver;
import co.aikar.commands.contexts.SenderAwareContextResolver;
import co.aikar.timings.lib.MCTiming;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -47,20 +46,19 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class RegisteredCommand {
public class RegisteredCommand <R extends CommandExecutionContext<? extends CommandExecutionContext>> {
final BaseCommand scope;
public final String command;
private final Method method;
final String prefSubCommand;
final Parameter[] parameters;
final ContextResolver<?, ?>[] resolvers;
final ContextResolver<?, R>[] resolvers;
final String syntaxText;
private final String permission;
final String complete;
final int requiredResolvers;
final int optionalResolvers;
private MCTiming timing;
RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
this.scope = scope;
@@ -75,6 +73,7 @@ public class RegisteredCommand {
CommandCompletion completionAnno = method.getAnnotation(CommandCompletion.class);
this.complete = completionAnno != null ? scope.manager.getCommandReplacements().replace(completionAnno.value()) : null;
this.parameters = method.getParameters();
//noinspection unchecked
this.resolvers = new ContextResolver[this.parameters.length];
final Syntax syntaxStr = method.getAnnotation(Syntax.class);
final CommandManager manager = scope.manager;
@@ -88,17 +87,19 @@ public class RegisteredCommand {
final Parameter parameter = parameters[i];
final Class<?> type = parameter.getType();
final ContextResolver<?, ?> resolver = commandContexts.getResolver(type);
//noinspection unchecked
final ContextResolver<?, R> resolver = commandContexts.getResolver(type);
if (resolver != null) {
resolvers[i] = resolver;
if (!CommandIssuer.class.isAssignableFrom(parameter.getType())) {
if (!scope.manager.isCommandIssuer(type)) {
String name = parameter.getName();
if (isOptionalResolver(resolver, parameter)) {
optionalResolvers++;
syntaxB.append('[').append(parameter.getName()).append("] ");
syntaxB.append('[').append(name).append("] ");
} else {
requiredResolvers++;
syntaxB.append('<').append(parameter.getName()).append("> ");
syntaxB.append('<').append(name).append("> ");
}
}
} else {
@@ -116,7 +117,7 @@ public class RegisteredCommand {
this.optionalResolvers = optionalResolvers;
}
static boolean isOptionalResolver(ContextResolver<?, ?> resolver, Parameter parameter) {
private boolean isOptionalResolver(ContextResolver<?, R> resolver, Parameter parameter) {
return resolver instanceof SenderAwareContextResolver
|| parameter.getAnnotation(Optional.class) != null
|| parameter.getAnnotation(Default.class) != null;
@@ -126,6 +127,7 @@ public class RegisteredCommand {
if (!scope.canExecute(sender, this)) {
return;
}
preCommand();
try {
Map<String, Object> passedArgs = resolveContexts(sender, args);
if (passedArgs == null) return;
@@ -134,7 +136,10 @@ public class RegisteredCommand {
} catch (Exception e) {
handleException(sender, args, e);
}
postCommand();
}
public void preCommand() {}
public void postCommand() {}
void handleException(CommandIssuer sender, List<String> args, Exception e) {
if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) {
@@ -169,8 +174,9 @@ public class RegisteredCommand {
final Parameter parameter = parameters[i];
final String parameterName = parameter.getName();
final Class<?> type = parameter.getType();
final ContextResolver<?, ?> resolver = resolvers[i];
CommandExecutionContext context = this.scope.manager.createCommandContext(this, parameter, sender, args, i, passedArgs);
//noinspection unchecked
final ContextResolver<?, R> resolver = resolvers[i];
R context = this.scope.manager.createCommandContext(this, parameter, sender, args, i, passedArgs);
if (!isOptionalResolver(resolver, parameter)) {
remainingRequired--;
}
@@ -212,13 +218,6 @@ public class RegisteredCommand {
return passedArgs;
}
MCTiming getTiming() {
if (this.timing == null) {
this.timing = scope.manager.getTimings().of("Command: " + command);
}
return this.timing;
}
boolean hasPermission(CommandIssuer check) {
return permission == null || !check.isPlayer() || check.hasPermission(permission);
}
@@ -0,0 +1,31 @@
/*
* 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.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface PreCommand {}
@@ -27,6 +27,6 @@ import co.aikar.commands.CommandExecutionContext;
import co.aikar.commands.InvalidCommandArgument;
@FunctionalInterface
public interface ContextResolver <C, T extends CommandExecutionContext> {
C getContext(CommandExecutionContext<T> c) throws InvalidCommandArgument;
public interface ContextResolver <T, C extends CommandExecutionContext<?>> {
T getContext(C c) throws InvalidCommandArgument;
}
@@ -25,4 +25,4 @@ package co.aikar.commands.contexts;
import co.aikar.commands.CommandExecutionContext;
public interface SenderAwareContextResolver<C, T extends CommandExecutionContext> extends ContextResolver <C, T> {}
public interface SenderAwareContextResolver<T, C extends CommandExecutionContext<?>> extends ContextResolver <T, C> {}
+2 -1
View File
@@ -112,7 +112,8 @@
<dependency>
<groupId>co.aikar</groupId>
<artifactId>acf-bukkit</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -24,14 +24,13 @@
package co.aikar.acfexample;
import co.aikar.commands.BukkitCommandManager;
import co.aikar.commands.CommandManager;
import com.google.common.collect.Lists;
import org.bukkit.plugin.java.JavaPlugin;
public final class ACFExample extends JavaPlugin {
private static ACFExample plugin;
private static CommandManager commandManager;
private static BukkitCommandManager commandManager;
@Override
public void onEnable() {
plugin = this;
@@ -54,7 +53,7 @@ public final class ACFExample extends JavaPlugin {
return plugin;
}
public static CommandManager getCommandManager() {
public static BukkitCommandManager getCommandManager() {
return commandManager;
}
}
@@ -27,14 +27,14 @@ import co.aikar.commands.BaseCommand;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.Subcommand;
import org.bukkit.command.CommandIssuer;
import org.bukkit.command.CommandSender;
@CommandAlias("acf")
public class SomeCommand_ExtraSubs extends BaseCommand {
@Subcommand("testsub test2")
@CommandCompletion("Foo2")
public void onTestSub2(CommandIssuer sender, String hi) {
public void onTestSub2(CommandSender sender, String hi) {
sender.sendMessage(hi);
}
}
@@ -23,6 +23,7 @@
package co.aikar.acfexample;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.contexts.ContextResolver;
@@ -37,7 +38,7 @@ public abstract class SomeObject {
return this.thisValue;
}
public static ContextResolver<SomeObject> getContextResolver() {
public static ContextResolver<SomeObject, BukkitCommandExecutionContext> getContextResolver() {
return (c) -> {
String first = c.popFirstArg();
if (first == null) {
+1 -1
View File
@@ -18,9 +18,9 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="acf-bukkit" />
<orderEntry type="module" module-name="acf-core" />
<orderEntry type="library" name="Maven: co.aikar:minecraft-timings:1.0.3" level="project" />
<orderEntry type="module" module-name="acf-bukkit" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.bukkit:bukkit:1.12-pre2-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
+4 -8
View File
@@ -30,25 +30,21 @@
<parent>
<groupId>co.aikar</groupId>
<artifactId>acf-parent</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>acf-paper</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<name>ACF (Paper)</name>
<dependencies>
<dependency>
<groupId>co.aikar</groupId>
<artifactId>acf-core</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
</dependency>
<dependency>
<groupId>co.aikar</groupId>
<artifactId>acf-bukkit</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bukkit</groupId>
@@ -23,6 +23,7 @@
package co.aikar.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
@SuppressWarnings("WeakerAccess")
@@ -34,7 +35,7 @@ public class PaperCommandManager extends BukkitCommandManager {
}
@Override
public synchronized CommandContexts getCommandContexts() {
public synchronized CommandContexts<BukkitCommandExecutionContext> getCommandContexts() {
if (this.contexts == null) {
this.contexts = new PaperCommandContexts(this);
}
@@ -42,7 +43,7 @@ public class PaperCommandManager extends BukkitCommandManager {
}
@Override
public synchronized CommandCompletions getCommandCompletions() {
public synchronized CommandCompletions<CommandSender, BukkitCommandCompletionContext> getCommandCompletions() {
if (this.completions == null) {
this.completions = new PaperCommandCompletions(this);
}
+1 -1
View File
@@ -25,7 +25,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>co.aikar</groupId>
<artifactId>acf-parent</artifactId>
<version><!--VERSION-->0.5.0<!--VERSION--></version>
<version><!--VERSION-->0.5.0-SNAPSHOT<!--VERSION--></version>
<packaging>pom</packaging>
<name>ACF (All)</name>
<url>https://acf.emc.gs</url>