Refactor stuff away from static and exposed Command Contexts - Fixes #2

This commit is contained in:
Aikar
2017-04-18 20:38:30 -04:00
parent 22989101e7
commit d4d2c490ca
9 changed files with 135 additions and 86 deletions
-4
View File
@@ -23,10 +23,6 @@
package co.aikar.commands;
import co.aikar.commands.managers.BukkitCommandManager;
import co.aikar.commands.managers.CommandManager;
import co.aikar.commands.managers.PaperCommandManager;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
/**
@@ -44,6 +44,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -61,6 +62,8 @@ public abstract class BaseCommand extends Command {
protected String execSubcommand;
@SuppressWarnings("WeakerAccess")
protected String[] origArgs;
CommandManager manager = null;
Map<String, Command> registeredCommands = new HashMap<>();
public BaseCommand(Plugin plugin) {
this(plugin, null);
@@ -139,8 +142,8 @@ public abstract class BaseCommand extends Command {
register(getName(), this);
}
private boolean register(String name, Command cmd) {
return Bukkit.getServer().getCommandMap().register(name.toLowerCase(), plugin.getName().toLowerCase(), cmd);
private void register(String name, Command cmd) {
this.registeredCommands.put(name.toLowerCase(), cmd);
}
private void registerSubcommand(Method method, String subCommand) {
@@ -0,0 +1,74 @@
/*
* 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.Optional;
import co.aikar.commands.contexts.OnlinePlayer;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
public class BukkitCommandContexts extends CommandContexts {
BukkitCommandContexts(CommandManager manager) {
super(manager);
registerContext(OnlinePlayer.class, (c) -> {
final String playercheck = c.popFirstArg();
Player player = CommandUtil.findPlayerSmart(c.getSender(), playercheck);
if (player == null) {
CommandUtil.sendMsg(c.getSender(), "&cCould not find a player by the name " + playercheck);
throw new InvalidCommandArgument(false);
}
return new OnlinePlayer(player);
});
registerSenderAwareContext(World.class, (c) -> {
String firstArg = c.getFirstArg();
World world = firstArg != null ? Bukkit.getWorld(firstArg) : null;
if (world != null) {
c.popFirstArg();
}
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, CommandExecutionContext::getSender);
registerSenderAwareContext(Player.class, (c) -> {
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);
}
if (player != null && c.hasFlag("itemheld") && !CommandUtil.isValidItem(player.getInventory().getItemInMainHand())) {
throw new InvalidCommandArgument("You must be holding an item in your main hand.", false);
}
return player;
});
}
}
@@ -21,28 +21,41 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands.managers;
package co.aikar.commands;
import co.aikar.commands.BaseCommand;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.plugin.Plugin;
import java.util.Map;
public class BukkitCommandManager implements CommandManager {
@SuppressWarnings("WeakerAccess")
protected final Plugin plugin;
private CommandContexts contexts;
public BukkitCommandManager(Plugin plugin) {
this.plugin = plugin;
this.contexts = new CommandContexts(this);
}
@Override
public CommandContexts getCommandContexts() {
return contexts;
}
@Override
public boolean register(BaseCommand command) {
return Bukkit.getServer().getCommandMap().register(this.plugin.getDescription().getName(), command);
}
command.manager = this;
final String plugin = this.plugin.getName().toLowerCase();
boolean allSuccess = true;
for (Map.Entry<String, Command> entry : command.registeredCommands.entrySet()) {
if (!Bukkit.getServer().getCommandMap().register(entry.getKey().toLowerCase(), plugin, entry.getValue())) {
allSuccess = false;
}
}
@Override
public boolean register(BaseCommand command, String commandName) {
return Bukkit.getServer().getCommandMap().register(commandName, this.plugin.getDescription().getName(), command);
return allSuccess;
}
}
@@ -32,23 +32,22 @@ import co.aikar.commands.contexts.OnlinePlayer;
import co.aikar.commands.contexts.SenderAwareContextResolver;
import com.google.common.collect.Maps;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Map;
public final class CommandContexts {
private static final Map<Class<?>, ContextResolver<?>> contextMap = Maps.newHashMap();
@SuppressWarnings("WeakerAccess")
public class CommandContexts {
private final CommandManager manager;
private final Map<Class<?>, ContextResolver<?>> contextMap = Maps.newHashMap();
private CommandContexts() {}
public static void initialize() {
CommandContexts(CommandManager manager) {
this.manager = manager;
registerContext(Integer.class, (c) -> {
try {
return CommandUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).intValue();
@@ -118,40 +117,6 @@ public final class CommandContexts {
return result;
});
registerContext(OnlinePlayer.class, (c) -> {
final String playercheck = c.popFirstArg();
Player player = CommandUtil.findPlayerSmart(c.getSender(), playercheck);
if (player == null) {
CommandUtil.sendMsg(c.getSender(), "&cCould not find a player by the name " + playercheck);
throw new InvalidCommandArgument(false);
}
return new OnlinePlayer(player);
});
registerSenderAwareContext(World.class, (c) -> {
String firstArg = c.getFirstArg();
World world = firstArg != null ? Bukkit.getWorld(firstArg) : null;
if (world != null) {
c.popFirstArg();
}
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, CommandExecutionContext::getSender);
registerSenderAwareContext(Player.class, (c) -> {
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);
}
if (player != null && c.hasFlag("itemheld") && !isValidItem(player.getInventory().getItemInMainHand())) {
throw new InvalidCommandArgument("You must be holding an item in your main hand.", false);
}
return player;
});
registerContext(Enum.class, (c) -> {
final String first = c.popFirstArg();
Class<? extends Enum<?>> enumCls = (Class<? extends Enum<?>>) c.getParam().getType();
@@ -164,14 +129,14 @@ public final class CommandContexts {
});
}
public static <T> void registerSenderAwareContext(Class<T> context, SenderAwareContextResolver<T> supplier) {
public <T> void registerSenderAwareContext(Class<T> context, SenderAwareContextResolver<T> supplier) {
contextMap.put(context, supplier);
}
public static <T> void registerContext(Class<T> context, ContextResolver<T> supplier) {
public <T> void registerContext(Class<T> context, ContextResolver<T> supplier) {
contextMap.put(context, supplier);
}
public static ContextResolver<?> getResolver(Class<?> type) {
public ContextResolver<?> getResolver(Class<?> type) {
Class<?> rootType = type;
do {
if (type == Object.class) {
@@ -187,7 +152,4 @@ public final class CommandContexts {
CommandLog.exception(new InvalidConfigurationException("No context resolver defined for " + rootType.getName()));
return null;
}
private static boolean isValidItem(ItemStack item) {
return item != null && item.getType() != Material.AIR && item.getAmount() > 0;
}
}
@@ -21,12 +21,16 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands.managers;
import co.aikar.commands.BaseCommand;
package co.aikar.commands;
public interface CommandManager {
/**
* Gets the command contexts registered
* @return Command Contexts
*/
CommandContexts getCommandContexts();
/**
* Registers a command with ACF
*
@@ -34,13 +38,4 @@ public interface CommandManager {
* @return boolean
*/
boolean register(BaseCommand command);
/**
* Registers a command with ACF
*
* @param command The command to register
* @param commandName Specific command name to register as
* @return boolean
*/
boolean register(BaseCommand command, String commandName);
}
@@ -34,6 +34,7 @@ import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -880,6 +881,11 @@ final class CommandUtil {
}
return timingProvider.newTiming(cmd, command);
}
static boolean isValidItem(ItemStack item) {
return item != null && item.getType() != Material.AIR && item.getAmount() > 0;
}
private enum TimingType {
SPIGOT() {
@Override
@@ -21,7 +21,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package co.aikar.commands.managers;
package co.aikar.commands;
import org.bukkit.plugin.Plugin;
@@ -48,18 +48,18 @@ import java.util.Set;
import java.util.stream.Collectors;
public class RegisteredCommand {
public final BaseCommand scope;
private final BaseCommand scope;
public final String command;
public final Method method;
public final String prefSubCommand;
public final Parameter[] parameters;
public final ContextResolver<?>[] resolvers;
public final String syntax;
private final Method method;
final String prefSubCommand;
final Parameter[] parameters;
final ContextResolver<?>[] resolvers;
final String syntax;
public final CommandPermission perm;
public final CommandCompletion complete;
public final int nonSenderAwareResolvers;
public final int optionalResolvers;
private final CommandPermission perm;
final CommandCompletion complete;
final int nonSenderAwareResolvers;
final int optionalResolvers;
CommandTiming timing;
RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
@@ -83,7 +83,7 @@ public class RegisteredCommand {
for (int i = 0; i < parameters.length; i++) {
final Parameter parameter = parameters[i];
final Class<?> type = parameter.getType();
final ContextResolver<?> resolver = CommandContexts.getResolver(type);
final ContextResolver<?> resolver = scope.manager.getCommandContexts().getResolver(type);
if (resolver != null) {
resolvers[i] = resolver;
@@ -117,7 +117,7 @@ public class RegisteredCommand {
this.optionalResolvers = optionalResolvers;
}
public void invoke(CommandSender sender, List<String> args) {
void invoke(CommandSender sender, List<String> args) {
if (!scope.canExecute(sender, this)) {
return;
}
@@ -191,7 +191,7 @@ public class RegisteredCommand {
}
}
public boolean hasPermission(CommandSender check) {
boolean hasPermission(CommandSender check) {
return perm == null || !(check instanceof Player) || check.hasPermission(perm.value());
}
}