Boom! NMS is gone! (Again) Will it come back?

Used reflection to get the command map from the server instead of using
the NMS call.

Also, more importantly, this commit enables CompositeCommands to
auto-register their top-level command in the constructor. No need to
separately obtain the command manager object. Yes, easy API. :-)
This commit is contained in:
Tastybento 2017-12-28 20:36:04 -08:00
parent 9b0992c30d
commit 452311fb69
11 changed files with 62 additions and 123 deletions

View File

@ -67,7 +67,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -21,8 +21,6 @@ import us.tastybento.bskyblock.listeners.PanelListener;
import us.tastybento.bskyblock.managers.AddonsManager;
import us.tastybento.bskyblock.managers.CommandsManager;
import us.tastybento.bskyblock.managers.LocalesManager;
import us.tastybento.bskyblock.util.Util;
import us.tastybento.bskyblock.util.nms.NMSAbstraction;
/**
* Main BSkyBlock class - provides an island minigame in the sky
@ -66,8 +64,8 @@ public class BSkyBlock extends JavaPlugin implements BSBModule {
// Set up commands
commandsManager = new CommandsManager();
commandsManager.registerCommand(this, new IslandCommand());
commandsManager.registerCommand(this, new AdminCommand());
new IslandCommand();
new AdminCommand();
// These items have to be loaded when the server has done 1 tick.
// Note Worlds are not loaded this early, so any Locations or World reference will be null
@ -206,16 +204,6 @@ public class BSkyBlock extends JavaPlugin implements BSBModule {
return plugin;
}
public NMSAbstraction getNMSHandler() {
NMSAbstraction nmsHandler = null;
try {
nmsHandler = Util.getNMSHandler();
} catch(Exception e) {
e.printStackTrace();
}
return nmsHandler;
}
public CommandsManager getCommandsManager() {
return commandsManager;
}

View File

@ -90,6 +90,28 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* @param string - aliases for this command
*/
public CompositeCommand(String label, String... string) {
super(label);
if (DEBUG)
Bukkit.getLogger().info("DEBUG: top level command registering..." + label);
this.setAliases(new ArrayList<>(Arrays.asList(string)));
this.parent = null;
setUsage("");
this.subCommandLevel = 0; // Top level
this.subCommands = new LinkedHashMap<>();
// Register command if it is not already registered
if (getPlugin().getCommand(label) == null) {
getPlugin().getCommandsManager().registerCommand(getPlugin(), this);
}
this.setup();
if (!this.getSubCommand("help").isPresent() && !label.equals("help"))
new DefaultHelpCommand(this);
}
/**
* Used only for testing....
*/
public CompositeCommand(BSkyBlock plugin, String label, String... string) {
super(label);
this.setAliases(new ArrayList<>(Arrays.asList(string)));
this.parent = null;

View File

@ -11,18 +11,19 @@ public class AdminCommand extends CompositeCommand {
public AdminCommand() {
super(Settings.ADMINCOMMAND, "bsb");
this.setPermission(Settings.PERMPREFIX + "admin.*");
this.setOnlyPlayer(false);
}
@Override
public void setup() {
this.setPermission(Settings.PERMPREFIX + "admin.*");
this.setOnlyPlayer(false);
this.setDescription("admin.help.description");
new AdminVersionCommand(this);
}
@Override
public boolean execute(User user, List<String> args) {
return true;
return this.getSubCommand("help").get().execute(user, args);
}
}

View File

@ -43,7 +43,6 @@ public class IslandCommand extends CompositeCommand {
@Override
public boolean execute(User user, List<String> args) {
user.sendLegacyMessage("You successfully did /is !");
if (!getPlugin().getIslands().hasIsland(user.getUniqueId())) {
return this.getSubCommand("create").get().execute(user, args);
}

View File

@ -1,20 +1,25 @@
package us.tastybento.bskyblock.managers;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.BSBModule;
public final class CommandsManager {
private static final boolean DEBUG = false;
private Map<BSBModule, List<Command>> commands = new LinkedHashMap<>();
public void registerCommand(BSBModule module, Command command) {
if (DEBUG)
Bukkit.getLogger().info("DEBUG: registering command for " + module.getIdentifier() + " - " + command.getLabel());
List<Command> cmds = new ArrayList<>();
if (commands.containsKey(module)) {
cmds = commands.get(module);
@ -22,17 +27,36 @@ public final class CommandsManager {
cmds.add(command);
commands.put(module, cmds);
BSkyBlock.getInstance().getNMSHandler().getServerCommandMap().register(command.getLabel(), command);
// Use reflection to obtain the commandMap method in Bukkit's server. It used to be visible, but isn't anymore.
try{
Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
commandMapField.setAccessible(true);
CommandMap commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
commandMap.register(command.getLabel(), command);
}
catch(Exception exception){
Bukkit.getLogger().severe("Bukkit server commandMap method is not there! This means no commands can be registered!");
}
}
public void unregisterCommand(Command command) {
// TODO - is this ever going to be used?
}
/**
* Get all of the commands for this
* @param module
* @return list of commands
*/
public List<Command> getCommands(BSBModule module) {
return commands.get(module);
}
/**
* Get the command with the label
* @param label
* @return the command or null if it is not there
*/
public Command getCommand(String label) {
for (List<Command> cmds : commands.values()) {
for (Command cmd : cmds) {

View File

@ -1,7 +1,6 @@
package us.tastybento.bskyblock.util;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@ -30,7 +29,6 @@ import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.commands.User;
import us.tastybento.bskyblock.config.Settings;
import us.tastybento.bskyblock.generators.IslandWorld;
import us.tastybento.bskyblock.util.nms.NMSAbstraction;
/**
* A set of utility methods
@ -42,7 +40,6 @@ public class Util {
private static BSkyBlock plugin = BSkyBlock.getInstance();
private static String serverVersion = null;
private static NMSAbstraction nmsHandler = null;
/**
* Returns the server version
@ -56,38 +53,6 @@ public class Util {
return serverVersion;
}
/**
* Checks what version the server is running and picks the appropriate NMS handler, or fallback
* @return NMSAbstraction class
* @throws ClassNotFoundException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
*/
public static NMSAbstraction getNMSHandler() throws ClassNotFoundException, IllegalArgumentException,
SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
String pluginPackageName = plugin.getClass().getPackage().getName();
String version = getServerVersion();
Class<?> clazz;
try {
clazz = Class.forName(pluginPackageName + ".util.nms." + version + ".NMSHandler");
} catch (Exception e) {
plugin.getLogger().info("No NMS Handler found for " + version + ", falling back to Bukkit API.");
clazz = Class.forName(pluginPackageName + ".util.nms.fallback.NMSHandler");
}
// Check if we have a NMSAbstraction implementing class at that location.
if (NMSAbstraction.class.isAssignableFrom(clazz)) {
if (nmsHandler == null) nmsHandler = (NMSAbstraction) clazz.getConstructor().newInstance();
return nmsHandler;
} else {
throw new IllegalStateException("Class " + clazz.getName() + " does not implement NMSAbstraction");
}
}
/**
* Converts a serialized location to a Location. Returns null if string is
* empty

View File

@ -1,21 +0,0 @@
package us.tastybento.bskyblock.util.nms;
import org.bukkit.command.CommandMap;
import org.bukkit.entity.Player;
public interface NMSAbstraction {
/**
* Send an action bar message to player
* @param player
* @param message
*/
void sendActionBar(Player player, String message);
/**
* Returns the active {@link org.bukkit.command.CommandMap} of the Server.
* It is used by the {@link us.tastybento.bskyblock.api.commands.CompositeCommand} to register itself.
* @return the active CommandMap of the server
*/
CommandMap getServerCommandMap();
}

View File

@ -1,19 +0,0 @@
package us.tastybento.bskyblock.util.nms.fallback;
import org.bukkit.command.CommandMap;
import org.bukkit.entity.Player;
import us.tastybento.bskyblock.util.nms.NMSAbstraction;
public class NMSHandler implements NMSAbstraction {
@Override
public void sendActionBar(Player player, String message) {
//TODO use /title command
}
@Override
public CommandMap getServerCommandMap() {
return null;
}
}

View File

@ -1,21 +0,0 @@
package us.tastybento.bskyblock.util.nms.v1_12_R1;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandMap;
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
import org.bukkit.entity.Player;
import us.tastybento.bskyblock.util.nms.NMSAbstraction;
public class NMSHandler implements NMSAbstraction {
@Override
public void sendActionBar(Player player, String message) {
}
@Override
public CommandMap getServerCommandMap() {
return ((CraftServer) Bukkit.getServer()).getCommandMap();
}
}

View File

@ -25,6 +25,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import us.tastybento.bskyblock.BSkyBlock;
import us.tastybento.bskyblock.api.commands.CompositeCommand;
import us.tastybento.bskyblock.api.commands.User;
import us.tastybento.bskyblock.api.events.IslandBaseEvent;
@ -36,11 +37,10 @@ public class TestBSkyBlock {
private final UUID playerUUID = UUID.randomUUID();
private static CommandSender sender;
private static Player player;
private static BSkyBlock plugin;
@BeforeClass
public static void setUp() {
//Plugin plugin = mock(Plugin.class);
//Mockito.doReturn(plugin).when(BSkyBlock.getPlugin());
//Mockito.when().thenReturn(plugin);
World world = mock(World.class);
@ -58,8 +58,9 @@ public class TestBSkyBlock {
player = mock(Player.class);
Mockito.when(player.hasPermission(Settings.PERMPREFIX + "default.permission")).thenReturn(true);
plugin = mock(BSkyBlock.class);
//Mockito.when(plugin.getServer()).thenReturn(server);
//Mockito.doReturn(Logger.getAnonymousLogger()).when(plugin.getLogger());
}
@Test
@ -140,7 +141,7 @@ public class TestBSkyBlock {
private class TestCommand extends CompositeCommand {
public TestCommand() {
super("test", "t", "tt");
super(plugin, "test", "t", "tt");
this.setParameters("test.params");
}
@ -243,7 +244,7 @@ public class TestBSkyBlock {
private class Test3ArgsCommand extends CompositeCommand {
public Test3ArgsCommand() {
super("args", "");
super(plugin, "args", "");
}
@Override