From 4328be282c3d4c6362c2ac4b941853c0613dba55 Mon Sep 17 00:00:00 2001 From: zml2008 Date: Fri, 9 Mar 2012 23:11:51 -0800 Subject: [PATCH] Register command permissions, integrate with the Bukkit help API Help API support requires a fix in Bukkit to work fully Allow annotation-free registering of commands with other plugins --- .../com/sk89q/bukkit/util/CommandInfo.java | 65 ++++++++++ .../bukkit/util/CommandRegistration.java | 22 ++-- .../util/CommandsManagerRegistration.java | 22 +++- .../bukkit/util/DynamicPluginCommand.java | 23 +++- .../util/DynamicPluginCommandHelpTopic.java | 112 ++++++++++++++++++ .../util/FallbackRegistrationListener.java | 6 +- .../com/sk89q/worldedit/ServerInterface.java | 16 ++- .../java/com/sk89q/worldedit/WorldEdit.java | 92 +++++++------- .../bukkit/BukkitServerInterface.java | 24 +++- 9 files changed, 310 insertions(+), 72 deletions(-) create mode 100644 src/main/java/com/sk89q/bukkit/util/CommandInfo.java create mode 100644 src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java diff --git a/src/main/java/com/sk89q/bukkit/util/CommandInfo.java b/src/main/java/com/sk89q/bukkit/util/CommandInfo.java new file mode 100644 index 000000000..6bcdd756e --- /dev/null +++ b/src/main/java/com/sk89q/bukkit/util/CommandInfo.java @@ -0,0 +1,65 @@ +/* + * WorldEdit + * Copyright (C) 2012 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.bukkit.util; + +/** + * @author zml2008 + */ +public class CommandInfo { + private final String[] aliases; + private final Object registeredWith; + private final String usage, desc; + private final String[] permissions; + + public CommandInfo(String usage, String desc, String[] aliases, Object registeredWith) { + this(usage, desc, aliases, registeredWith, null); + } + + public CommandInfo(String usage, String desc, String[] aliases, Object registeredWith, String[] permissions) { + this.usage = usage; + this.desc = desc; + this.aliases = aliases; + this.permissions = permissions; + this.registeredWith = registeredWith; + } + + public String[] getAliases() { + return aliases; + } + + public String getName() { + return aliases[0]; + } + + public String getUsage() { + return usage; + } + + public String getDesc() { + return desc; + } + + public String[] getPermissions() { + return permissions; + } + + public Object getRegisteredWith() { + return registeredWith; + } +} diff --git a/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java b/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java index 25e96b115..d5c58b987 100644 --- a/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java +++ b/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.sk89q.minecraft.util.commands.Command; import com.sk89q.util.ReflectionUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandExecutor; @@ -37,8 +36,13 @@ import org.bukkit.plugin.Plugin; * @author zml2008 */ public class CommandRegistration { - private final Plugin plugin; - private final CommandExecutor executor; + + static { + Bukkit.getServer().getHelpMap().registerHelpTopicFactory(DynamicPluginCommand.class, new DynamicPluginCommandHelpTopic.Factory()); + } + + protected final Plugin plugin; + protected final CommandExecutor executor; private CommandMap fallbackCommands; public CommandRegistration(Plugin plugin) { @@ -49,15 +53,17 @@ public class CommandRegistration { this.plugin = plugin; this.executor = executor; } - - public boolean registerAll(List registered) { + + public boolean register(List registered) { CommandMap commandMap = getCommandMap(); if (registered == null || commandMap == null) { return false; } - for (Command command : registered) { - commandMap.register(plugin.getDescription().getName(), - new DynamicPluginCommand(command.aliases(), command.desc(), "/" + command.aliases()[0] + " " + command.usage(), executor)); + for (CommandInfo command : registered) { + DynamicPluginCommand cmd = new DynamicPluginCommand(command.getAliases(), + command.getDesc(), "/" + command.getAliases()[0] + " " + command.getUsage(), executor, command.getRegisteredWith()); + cmd.setPermissions(command.getPermissions()); + commandMap.register(plugin.getDescription().getName(), cmd); } return true; } diff --git a/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java b/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java index 210cf5c76..e3fa20b01 100644 --- a/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java +++ b/src/main/java/com/sk89q/bukkit/util/CommandsManagerRegistration.java @@ -20,10 +20,14 @@ package com.sk89q.bukkit.util; import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandsManager; import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandMap; import org.bukkit.plugin.Plugin; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.List; /** @@ -43,7 +47,21 @@ public class CommandsManagerRegistration extends CommandRegistration { } public boolean register(Class clazz) { - List registered = commands.registerAndReturn(clazz); - return registerAll(registered); + return registerAll(commands.registerAndReturn(clazz)); + } + + public boolean registerAll(List registered) { + List toRegister = new ArrayList(); + for (Command command : registered) { + String[] permissions = null; + Method cmdMethod = commands.getMethods().get(null).get(command.aliases()[0]); + if (cmdMethod != null && cmdMethod.isAnnotationPresent(CommandPermissions.class)) { + permissions = cmdMethod.getAnnotation(CommandPermissions.class).value(); + } + + toRegister.add(new CommandInfo(command.usage(), command.desc(), command.aliases(), commands, permissions)); + } + + return register(toRegister); } } diff --git a/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java b/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java index 1a66e31a2..d8b62df9b 100644 --- a/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java +++ b/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java @@ -19,6 +19,7 @@ package com.sk89q.bukkit.util; +import com.sk89q.util.StringUtil; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import java.util.Arrays; @@ -29,18 +30,36 @@ import java.util.Arrays; public class DynamicPluginCommand extends org.bukkit.command.Command { protected final CommandExecutor owner; + protected final Object registeredWith; + protected String[] permissions = new String[0]; - public DynamicPluginCommand(String[] aliases, String desc, String usage, CommandExecutor owner) { + public DynamicPluginCommand(String[] aliases, String desc, String usage, CommandExecutor owner, Object registeredWith) { super(aliases[0], desc, usage, Arrays.asList(aliases)); this.owner = owner; + this.registeredWith = registeredWith; } @Override public boolean execute(CommandSender sender, String label, String[] args) { return owner.onCommand(sender, this, label, args); } - + public Object getOwner() { return owner; } + + public Object getRegisteredWith() { + return registeredWith; + } + + public void setPermissions(String[] permissions) { + this.permissions = permissions; + if (permissions != null) { + super.setPermission(StringUtil.joinString(permissions, ";")); + } + } + + public String[] getPermissions() { + return permissions; + } } diff --git a/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java b/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java new file mode 100644 index 000000000..936a47879 --- /dev/null +++ b/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommandHelpTopic.java @@ -0,0 +1,112 @@ +/* + * WorldEdit + * Copyright (C) 2012 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.bukkit.util; + +import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.wepif.PermissionsResolverManager; +import com.sk89q.wepif.WEPIFRuntimeException; +import org.bukkit.OfflinePlayer; +import org.bukkit.command.CommandSender; +import org.bukkit.help.HelpTopic; +import org.bukkit.help.HelpTopicFactory; + +import java.util.Map; + +/** + * @author zml2008 + */ +public class DynamicPluginCommandHelpTopic extends HelpTopic { + private final DynamicPluginCommand cmd; + + public DynamicPluginCommandHelpTopic(DynamicPluginCommand cmd) { + this.cmd = cmd; + this.name = "/" + cmd.getName(); + + if (cmd.getRegisteredWith() instanceof CommandsManager) { + Map helpText = ((CommandsManager) cmd.getRegisteredWith()).getHelpMessages(); + final String lookupName = cmd.getName().replaceAll("/", ""); + if (helpText.containsKey(lookupName)) { + this.fullText = helpText.get(lookupName); + } + helpText = ((CommandsManager) cmd.getRegisteredWith()).getCommands(); + if (helpText.containsKey(cmd.getName())) { + final String shortText = helpText.get(cmd.getName()); + if (this.fullText == null) { + this.fullText = this.name + " " + shortText; + } + this.shortText = shortText; + } + } else { + this.shortText = cmd.getDescription(); + } + } + + @Override + @SuppressWarnings("unchecked") + public boolean canSee(CommandSender player) { + if (cmd.getPermissions() != null && cmd.getPermissions().length > 0) { + if (cmd.getRegisteredWith() instanceof CommandsManager) { + try { + for (String perm : cmd.getPermissions()) { + if (((CommandsManager) cmd.getRegisteredWith()).hasPermission(player, perm)) { + return true; + } + } + } catch (Throwable t) { + // Doesn't take the CommandSender (Hooray for compile-time generics!), we have other methods at our disposal + } + } + if (player instanceof OfflinePlayer) { + try { + for (String perm : cmd.getPermissions()) { + if (PermissionsResolverManager.getInstance().hasPermission((OfflinePlayer) player, perm)) { + return true; + } + } + } catch (WEPIFRuntimeException e) { + // PermissionsResolverManager not initialized, eat it + } + } + for (String perm : cmd.getPermissions()) { + if (player.hasPermission(perm)) { + return true; + } + } + return false; + } + return true; + } + + @Override + public String getFullText(CommandSender forWho) { + if (this.fullText == null || this.fullText.length() == 0) { + return getShortText(); + } else { + return this.fullText; + } + } + + public static class Factory implements HelpTopicFactory { + + @Override + public HelpTopic createTopic(DynamicPluginCommand command) { + return new DynamicPluginCommandHelpTopic(command); + } + } +} diff --git a/src/main/java/com/sk89q/bukkit/util/FallbackRegistrationListener.java b/src/main/java/com/sk89q/bukkit/util/FallbackRegistrationListener.java index 6009457be..6123e02be 100644 --- a/src/main/java/com/sk89q/bukkit/util/FallbackRegistrationListener.java +++ b/src/main/java/com/sk89q/bukkit/util/FallbackRegistrationListener.java @@ -35,12 +35,8 @@ public class FallbackRegistrationListener implements Listener { this.commandRegistration = commandRegistration; } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { - if (event.isCancelled()) { - return; - } - if (commandRegistration.dispatch(event.getPlayer(), event.getMessage())) { event.setCancelled(true); } diff --git a/src/main/java/com/sk89q/worldedit/ServerInterface.java b/src/main/java/com/sk89q/worldedit/ServerInterface.java index 0bcbbef0d..8baaee076 100644 --- a/src/main/java/com/sk89q/worldedit/ServerInterface.java +++ b/src/main/java/com/sk89q/worldedit/ServerInterface.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit; import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandsManager; import java.util.Collections; import java.util.List; @@ -32,16 +33,16 @@ public abstract class ServerInterface { /** * Resolves an item name to its ID. * - * @param name - * @return + * @param name The name to look up + * @return The id that corresponds to the name, or -1 if no such ID exists */ public abstract int resolveItem(String name); /** * Checks if a mob type is valid. * - * @param type - * @return + * @param type The mob type name to check + * @return Whether the name is a valid mod bype */ public abstract boolean isValidMobType(String type); @@ -66,8 +67,13 @@ public abstract class ServerInterface { public List getWorlds() { return Collections.emptyList(); } - + + @Deprecated public void onCommandRegistration(List commands) { // Do nothing :) } + + public void onCommandRegistration(List commands, CommandsManager manager) { + onCommandRegistration(commands); + } } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index 2e228977b..d11ae2685 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -102,7 +102,7 @@ public class WorldEdit { /** * Construct an instance of the plugin - * + * * @param server * @param config */ @@ -182,22 +182,22 @@ public class WorldEdit { super.invokeMethod(parent, args, player, method, instance, methodArgs, level); } }; - + commands.setInjector(new SimpleInjector(this)); - server.onCommandRegistration(commands.registerAndReturn(ChunkCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(ClipboardCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(GeneralCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(GenerationCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(HistoryCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(NavigationCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(RegionCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(ScriptingCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(SelectionCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(SnapshotUtilCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(ToolUtilCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(ToolCommands.class)); - server.onCommandRegistration(commands.registerAndReturn(UtilityCommands.class)); + server.onCommandRegistration(commands.registerAndReturn(ChunkCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(ClipboardCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(GeneralCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(GenerationCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(HistoryCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(NavigationCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(RegionCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(ScriptingCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(SelectionCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(SnapshotUtilCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(ToolUtilCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(ToolCommands.class), commands); + server.onCommandRegistration(commands.registerAndReturn(UtilityCommands.class), commands); } /** @@ -262,7 +262,7 @@ public class WorldEdit { /** * Returns true if the player has a session. - * + * * @param player * @return */ @@ -498,8 +498,8 @@ public class WorldEdit { * @param player * @param patternString * @return pattern - * @throws UnknownItemException - * @throws DisallowedItemException + * @throws UnknownItemException + * @throws DisallowedItemException */ public Pattern getBlockPattern(LocalPlayer player, String patternString) throws UnknownItemException, DisallowedItemException { @@ -648,8 +648,8 @@ public class WorldEdit { * @param list * @param allBlocksAllowed * @return set - * @throws UnknownItemException - * @throws DisallowedItemException + * @throws UnknownItemException + * @throws DisallowedItemException */ public Set getBlockIDs(LocalPlayer player, String list, boolean allBlocksAllowed) @@ -668,8 +668,8 @@ public class WorldEdit { * has valid characters and has an extension. It also prevents directory * traversal exploits by checking the root directory and the file directory. * On success, a java.io.File object will be returned. - * - * @param player + * + * @param player * @param dir sub-directory to look in * @param filename filename (user-submitted) * @param defaultExt append an extension if missing one, null to not use @@ -688,8 +688,8 @@ public class WorldEdit { * has valid characters and has an extension. It also prevents directory * traversal exploits by checking the root directory and the file directory. * On success, a java.io.File object will be returned. - * - * @param player + * + * @param player * @param dir sub-directory to look in * @param filename filename (user-submitted) * @param defaultExt append an extension if missing one, null to not use @@ -705,7 +705,7 @@ public class WorldEdit { /** * Get a safe path to a file. - * + * * @param player * @param dir * @param filename @@ -775,7 +775,7 @@ public class WorldEdit { /** * Get a file relative to the defined working directory. If the specified * path is absolute, then the working directory is not used. - * + * * @param path * @return */ @@ -790,7 +790,7 @@ public class WorldEdit { /** * Modulus, divisor-style. - * + * * @param a * @param n * @return @@ -802,11 +802,11 @@ public class WorldEdit { /** * Get the direction vector for a player's direction. May return * null if a direction could not be found. - * + * * @param player - * @param dirStr + * @param dirStr * @return - * @throws UnknownDirectionException + * @throws UnknownDirectionException */ public Vector getDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException { @@ -897,11 +897,11 @@ public class WorldEdit { /** * Get diagonal direction vector for a player's direction. May return * null if a direction could not be found. - * + * * @param player - * @param dirStr + * @param dirStr * @return - * @throws UnknownDirectionException + * @throws UnknownDirectionException */ public Vector getDiagonalDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException { @@ -913,9 +913,9 @@ public class WorldEdit { * Get the flip direction for a player's direction. * * @param player - * @param dirStr + * @param dirStr * @return - * @throws UnknownDirectionException + * @throws UnknownDirectionException */ public FlipDirection getFlipDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException { @@ -941,7 +941,7 @@ public class WorldEdit { /** * Remove a session. - * + * * @param player */ public void removeSession(LocalPlayer player) { @@ -961,7 +961,7 @@ public class WorldEdit { /** * Flush a block bag's changes to a player. - * + * * @param player * @param editSession */ @@ -1064,9 +1064,9 @@ public class WorldEdit { /** * Called on arm swing. - * + * * @param player - * @return + * @return */ public boolean handleArmSwing(LocalPlayer player) { if (player.getItemInHand() == config.navigationWand) { @@ -1103,9 +1103,9 @@ public class WorldEdit { /** * Called on right click (not on a block). - * + * * @param player - * @return + * @return */ public boolean handleRightClick(LocalPlayer player) { if (player.getItemInHand() == config.navigationWand) { @@ -1327,7 +1327,7 @@ public class WorldEdit { return true; } - + public String[] commandDetection(String[] split) { split[0] = split[0].substring(1); @@ -1355,11 +1355,11 @@ public class WorldEdit { /** * Executes a WorldEdit script. - * + * * @param player * @param f * @param args - * @throws WorldEditException + * @throws WorldEditException */ public void runScript(LocalPlayer player, File f, String[] args) throws WorldEditException { @@ -1444,7 +1444,7 @@ public class WorldEdit { /** * Get Worldedit's configuration. - * + * * @return */ public LocalConfiguration getConfiguration() { @@ -1453,7 +1453,7 @@ public class WorldEdit { /** * Get the server interface. - * + * * @return */ public ServerInterface getServer() { diff --git a/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index 47e6d44ab..6ec0d1e05 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -19,12 +19,17 @@ package com.sk89q.worldedit.bukkit; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import com.sk89q.bukkit.util.CommandInfo; import com.sk89q.bukkit.util.CommandRegistration; import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.minecraft.util.commands.CommandsManager; +import com.sk89q.worldedit.LocalPlayer; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; @@ -78,12 +83,23 @@ public class BukkitServerInterface extends ServerInterface { return ret; } - + @Override - public void onCommandRegistration(List commands) { - dynamicCommands.registerAll(commands); + public void onCommandRegistration(List commands, CommandsManager manager) { + List toRegister = new ArrayList(); + for (Command command : commands) { + String[] permissions = null; + Method cmdMethod = manager.getMethods().get(null).get(command.aliases()[0]); + if (cmdMethod != null && cmdMethod.isAnnotationPresent(CommandPermissions.class)) { + permissions = cmdMethod.getAnnotation(CommandPermissions.class).value(); + } + + toRegister.add(new CommandInfo(command.usage(), command.desc(), command.aliases(), manager, permissions)); + } + + dynamicCommands.register(toRegister); } - + public void unregisterCommands() { dynamicCommands.unregisterCommands(); }