001 package org.bukkit.command; 002 003 import java.util.ArrayList; 004 import java.util.Collections; 005 import java.util.List; 006 import java.util.Set; 007 008 import org.apache.commons.lang.Validate; 009 import org.bukkit.Bukkit; 010 import org.bukkit.ChatColor; 011 import org.bukkit.Server; 012 import org.bukkit.entity.Player; 013 import org.bukkit.entity.minecart.CommandMinecart; 014 import org.bukkit.permissions.Permissible; 015 import org.bukkit.plugin.PluginDescriptionFile; 016 import org.bukkit.util.StringUtil; 017 018 import com.google.common.collect.ImmutableList; 019 020 /** 021 * Represents a Command, which executes various tasks upon user input 022 */ 023 public abstract class Command { 024 private final String name; 025 private String nextLabel; 026 private String label; 027 private List<String> aliases; 028 private List<String> activeAliases; 029 private CommandMap commandMap = null; 030 protected String description = ""; 031 protected String usageMessage; 032 private String permission; 033 private String permissionMessage; 034 035 protected Command(String name) { 036 this(name, "", "/" + name, new ArrayList<String>()); 037 } 038 039 protected Command(String name, String description, String usageMessage, List<String> aliases) { 040 this.name = name; 041 this.nextLabel = name; 042 this.label = name; 043 this.description = description; 044 this.usageMessage = usageMessage; 045 this.aliases = aliases; 046 this.activeAliases = new ArrayList<String>(aliases); 047 } 048 049 /** 050 * Executes the command, returning its success 051 * 052 * @param sender Source object which is executing this command 053 * @param commandLabel The alias of the command used 054 * @param args All arguments passed to the command, split via ' ' 055 * @return true if the command was successful, otherwise false 056 */ 057 public abstract boolean execute(CommandSender sender, String commandLabel, String[] args); 058 059 /** 060 * @deprecated This method is not supported and returns null 061 */ 062 @Deprecated 063 public List<String> tabComplete(CommandSender sender, String[] args) { 064 return null; 065 } 066 067 /** 068 * Executed on tab completion for this command, returning a list of 069 * options the player can tab through. 070 * 071 * @param sender Source object which is executing this command 072 * @param alias the alias being used 073 * @param args All arguments passed to the command, split via ' ' 074 * @return a list of tab-completions for the specified arguments. This 075 * will never be null. List may be immutable. 076 * @throws IllegalArgumentException if sender, alias, or args is null 077 */ 078 public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { 079 Validate.notNull(sender, "Sender cannot be null"); 080 Validate.notNull(args, "Arguments cannot be null"); 081 Validate.notNull(alias, "Alias cannot be null"); 082 083 if (args.length == 0) { 084 return ImmutableList.of(); 085 } 086 087 String lastWord = args[args.length - 1]; 088 089 Player senderPlayer = sender instanceof Player ? (Player) sender : null; 090 091 ArrayList<String> matchedPlayers = new ArrayList<String>(); 092 for (Player player : sender.getServer().getOnlinePlayers()) { 093 String name = player.getName(); 094 if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) { 095 matchedPlayers.add(name); 096 } 097 } 098 099 Collections.sort(matchedPlayers, String.CASE_INSENSITIVE_ORDER); 100 return matchedPlayers; 101 } 102 103 /** 104 * Returns the name of this command 105 * 106 * @return Name of this command 107 */ 108 public String getName() { 109 return name; 110 } 111 112 /** 113 * Gets the permission required by users to be able to perform this 114 * command 115 * 116 * @return Permission name, or null if none 117 */ 118 public String getPermission() { 119 return permission; 120 } 121 122 /** 123 * Sets the permission required by users to be able to perform this 124 * command 125 * 126 * @param permission Permission name or null 127 */ 128 public void setPermission(String permission) { 129 this.permission = permission; 130 } 131 132 /** 133 * Tests the given {@link CommandSender} to see if they can perform this 134 * command. 135 * <p> 136 * If they do not have permission, they will be informed that they cannot 137 * do this. 138 * 139 * @param target User to test 140 * @return true if they can use it, otherwise false 141 */ 142 public boolean testPermission(CommandSender target) { 143 if (testPermissionSilent(target)) { 144 return true; 145 } 146 147 if (permissionMessage == null) { 148 target.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error."); 149 } else if (permissionMessage.length() != 0) { 150 for (String line : permissionMessage.replace("<permission>", permission).split("\n")) { 151 target.sendMessage(line); 152 } 153 } 154 155 return false; 156 } 157 158 /** 159 * Tests the given {@link CommandSender} to see if they can perform this 160 * command. 161 * <p> 162 * No error is sent to the sender. 163 * 164 * @param target User to test 165 * @return true if they can use it, otherwise false 166 */ 167 public boolean testPermissionSilent(CommandSender target) { 168 if ((permission == null) || (permission.length() == 0)) { 169 return true; 170 } 171 172 for (String p : permission.split(";")) { 173 if (target.hasPermission(p)) { 174 return true; 175 } 176 } 177 178 return false; 179 } 180 181 /** 182 * Returns the current label for this command 183 * 184 * @return Label of this command or null if not registered 185 */ 186 public String getLabel() { 187 return label; 188 } 189 190 /** 191 * Sets the label of this command. 192 * <p> 193 * If the command is currently registered the label change will only take 194 * effect after its been re-registered e.g. after a /reload 195 * 196 * @param name The command's name 197 * @return returns true if the name change happened instantly or false if 198 * it was scheduled for re-registration 199 */ 200 public boolean setLabel(String name) { 201 this.nextLabel = name; 202 if (!isRegistered()) { 203 this.label = name; 204 return true; 205 } 206 return false; 207 } 208 209 /** 210 * Registers this command to a CommandMap. 211 * Once called it only allows changes the registered CommandMap 212 * 213 * @param commandMap the CommandMap to register this command to 214 * @return true if the registration was successful (the current registered 215 * CommandMap was the passed CommandMap or null) false otherwise 216 */ 217 public boolean register(CommandMap commandMap) { 218 if (allowChangesFrom(commandMap)) { 219 this.commandMap = commandMap; 220 return true; 221 } 222 223 return false; 224 } 225 226 /** 227 * Unregisters this command from the passed CommandMap applying any 228 * outstanding changes 229 * 230 * @param commandMap the CommandMap to unregister 231 * @return true if the unregistration was successfull (the current 232 * registered CommandMap was the passed CommandMap or null) false 233 * otherwise 234 */ 235 public boolean unregister(CommandMap commandMap) { 236 if (allowChangesFrom(commandMap)) { 237 this.commandMap = null; 238 this.activeAliases = new ArrayList<String>(this.aliases); 239 this.label = this.nextLabel; 240 return true; 241 } 242 243 return false; 244 } 245 246 private boolean allowChangesFrom(CommandMap commandMap) { 247 return (null == this.commandMap || this.commandMap == commandMap); 248 } 249 250 /** 251 * Returns the current registered state of this command 252 * 253 * @return true if this command is currently registered false otherwise 254 */ 255 public boolean isRegistered() { 256 return (null != this.commandMap); 257 } 258 259 /** 260 * Returns a list of active aliases of this command 261 * 262 * @return List of aliases 263 */ 264 public List<String> getAliases() { 265 return activeAliases; 266 } 267 268 /** 269 * Returns a message to be displayed on a failed permission check for this 270 * command 271 * 272 * @return Permission check failed message 273 */ 274 public String getPermissionMessage() { 275 return permissionMessage; 276 } 277 278 /** 279 * Gets a brief description of this command 280 * 281 * @return Description of this command 282 */ 283 public String getDescription() { 284 return description; 285 } 286 287 /** 288 * Gets an example usage of this command 289 * 290 * @return One or more example usages 291 */ 292 public String getUsage() { 293 return usageMessage; 294 } 295 296 /** 297 * Sets the list of aliases to request on registration for this command. 298 * This is not effective outside of defining aliases in the {@link 299 * PluginDescriptionFile#getCommands()} (under the 300 * `<code>aliases</code>' node) is equivalent to this method. 301 * 302 * @param aliases aliases to register to this command 303 * @return this command object, for chaining 304 */ 305 public Command setAliases(List<String> aliases) { 306 this.aliases = aliases; 307 if (!isRegistered()) { 308 this.activeAliases = new ArrayList<String>(aliases); 309 } 310 return this; 311 } 312 313 /** 314 * Sets a brief description of this command. Defining a description in the 315 * {@link PluginDescriptionFile#getCommands()} (under the 316 * `<code>description</code>' node) is equivalent to this method. 317 * 318 * @param description new command description 319 * @return this command object, for chaining 320 */ 321 public Command setDescription(String description) { 322 this.description = description; 323 return this; 324 } 325 326 /** 327 * Sets the message sent when a permission check fails 328 * 329 * @param permissionMessage new permission message, null to indicate 330 * default message, or an empty string to indicate no message 331 * @return this command object, for chaining 332 */ 333 public Command setPermissionMessage(String permissionMessage) { 334 this.permissionMessage = permissionMessage; 335 return this; 336 } 337 338 /** 339 * Sets the example usage of this command 340 * 341 * @param usage new example usage 342 * @return this command object, for chaining 343 */ 344 public Command setUsage(String usage) { 345 this.usageMessage = usage; 346 return this; 347 } 348 349 public static void broadcastCommandMessage(CommandSender source, String message) { 350 broadcastCommandMessage(source, message, true); 351 } 352 353 public static void broadcastCommandMessage(CommandSender source, String message, boolean sendToSource) { 354 String result = source.getName() + ": " + message; 355 356 if (source instanceof BlockCommandSender) { 357 BlockCommandSender blockCommandSender = (BlockCommandSender) source; 358 359 if (blockCommandSender.getBlock().getWorld().getGameRuleValue("commandBlockOutput").equalsIgnoreCase("false")) { 360 Bukkit.getConsoleSender().sendMessage(result); 361 return; 362 } 363 } else if (source instanceof CommandMinecart) { 364 CommandMinecart commandMinecart = (CommandMinecart) source; 365 366 if (commandMinecart.getWorld().getGameRuleValue("commandBlockOutput").equalsIgnoreCase("false")) { 367 Bukkit.getConsoleSender().sendMessage(result); 368 return; 369 } 370 } 371 372 Set<Permissible> users = Bukkit.getPluginManager().getPermissionSubscriptions(Server.BROADCAST_CHANNEL_ADMINISTRATIVE); 373 String colored = ChatColor.GRAY + "" + ChatColor.ITALIC + "[" + result + ChatColor.GRAY + ChatColor.ITALIC + "]"; 374 375 if (sendToSource && !(source instanceof ConsoleCommandSender)) { 376 source.sendMessage(message); 377 } 378 379 for (Permissible user : users) { 380 if (user instanceof CommandSender) { 381 CommandSender target = (CommandSender) user; 382 383 if (target instanceof ConsoleCommandSender) { 384 target.sendMessage(result); 385 } else if (target != source) { 386 target.sendMessage(colored); 387 } 388 } 389 } 390 } 391 392 @Override 393 public String toString() { 394 return getClass().getName() + '(' + name + ')'; 395 } 396 }