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 }