001 package org.bukkit.command; 002 003 import static org.bukkit.util.Java15Compat.Arrays_copyOfRange; 004 005 import java.util.ArrayList; 006 import java.util.Collection; 007 import java.util.Collections; 008 import java.util.HashMap; 009 import java.util.Iterator; 010 import java.util.List; 011 import java.util.Map; 012 import java.util.regex.Pattern; 013 014 import org.apache.commons.lang.Validate; 015 import org.bukkit.Server; 016 import org.bukkit.command.defaults.*; 017 import org.bukkit.entity.Player; 018 import org.bukkit.util.StringUtil; 019 020 public class SimpleCommandMap implements CommandMap { 021 private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL); 022 protected final Map<String, Command> knownCommands = new HashMap<String, Command>(); 023 private final Server server; 024 025 public SimpleCommandMap(final Server server) { 026 this.server = server; 027 setDefaultCommands(); 028 } 029 030 private void setDefaultCommands() { 031 register("bukkit", new SaveCommand()); 032 register("bukkit", new SaveOnCommand()); 033 register("bukkit", new SaveOffCommand()); 034 register("bukkit", new StopCommand()); 035 register("bukkit", new VersionCommand("version")); 036 register("bukkit", new ReloadCommand("reload")); 037 register("bukkit", new PluginsCommand("plugins")); 038 register("bukkit", new TimingsCommand("timings")); 039 } 040 041 public void setFallbackCommands() { 042 register("bukkit", new ListCommand()); 043 register("bukkit", new OpCommand()); 044 register("bukkit", new DeopCommand()); 045 register("bukkit", new BanIpCommand()); 046 register("bukkit", new PardonIpCommand()); 047 register("bukkit", new BanCommand()); 048 register("bukkit", new PardonCommand()); 049 register("bukkit", new KickCommand()); 050 register("bukkit", new TeleportCommand()); 051 register("bukkit", new GiveCommand()); 052 register("bukkit", new TimeCommand()); 053 register("bukkit", new SayCommand()); 054 register("bukkit", new WhitelistCommand()); 055 register("bukkit", new TellCommand()); 056 register("bukkit", new MeCommand()); 057 register("bukkit", new KillCommand()); 058 register("bukkit", new GameModeCommand()); 059 register("bukkit", new HelpCommand()); 060 register("bukkit", new ExpCommand()); 061 register("bukkit", new ToggleDownfallCommand()); 062 register("bukkit", new BanListCommand()); 063 register("bukkit", new DefaultGameModeCommand()); 064 register("bukkit", new SeedCommand()); 065 register("bukkit", new DifficultyCommand()); 066 register("bukkit", new WeatherCommand()); 067 register("bukkit", new SpawnpointCommand()); 068 register("bukkit", new ClearCommand()); 069 register("bukkit", new GameRuleCommand()); 070 register("bukkit", new EnchantCommand()); 071 register("bukkit", new TestForCommand()); 072 register("bukkit", new EffectCommand()); 073 register("bukkit", new ScoreboardCommand()); 074 register("bukkit", new PlaySoundCommand()); 075 register("bukkit", new SpreadPlayersCommand()); 076 register("bukkit", new SetWorldSpawnCommand()); 077 register("bukkit", new SetIdleTimeoutCommand()); 078 register("bukkit", new AchievementCommand()); 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 public void registerAll(String fallbackPrefix, List<Command> commands) { 085 if (commands != null) { 086 for (Command c : commands) { 087 register(fallbackPrefix, c); 088 } 089 } 090 } 091 092 /** 093 * {@inheritDoc} 094 */ 095 public boolean register(String fallbackPrefix, Command command) { 096 return register(command.getName(), fallbackPrefix, command); 097 } 098 099 /** 100 * {@inheritDoc} 101 */ 102 public boolean register(String label, String fallbackPrefix, Command command) { 103 label = label.toLowerCase().trim(); 104 fallbackPrefix = fallbackPrefix.toLowerCase().trim(); 105 boolean registered = register(label, command, false, fallbackPrefix); 106 107 Iterator<String> iterator = command.getAliases().iterator(); 108 while (iterator.hasNext()) { 109 if (!register(iterator.next(), command, true, fallbackPrefix)) { 110 iterator.remove(); 111 } 112 } 113 114 // If we failed to register under the real name, we need to set the command label to the direct address 115 if (!registered) { 116 command.setLabel(fallbackPrefix + ":" + label); 117 } 118 119 // Register to us so further updates of the commands label and aliases are postponed until its reregistered 120 command.register(this); 121 122 return registered; 123 } 124 125 /** 126 * Registers a command with the given name is possible. Also uses 127 * fallbackPrefix to create a unique name. 128 * 129 * @param label the name of the command, without the '/'-prefix. 130 * @param command the command to register 131 * @param isAlias whether the command is an alias 132 * @param fallbackPrefix a prefix which is prepended to the command for a 133 * unique address 134 * @return true if command was registered, false otherwise. 135 */ 136 private synchronized boolean register(String label, Command command, boolean isAlias, String fallbackPrefix) { 137 knownCommands.put(fallbackPrefix + ":" + label, command); 138 if ((command instanceof VanillaCommand || isAlias) && knownCommands.containsKey(label)) { 139 // Request is for an alias/fallback command and it conflicts with 140 // a existing command or previous alias ignore it 141 // Note: This will mean it gets removed from the commands list of active aliases 142 return false; 143 } 144 145 boolean registered = true; 146 147 // If the command exists but is an alias we overwrite it, otherwise we return 148 Command conflict = knownCommands.get(label); 149 if (conflict != null && conflict.getLabel().equals(label)) { 150 return false; 151 } 152 153 if (!isAlias) { 154 command.setLabel(label); 155 } 156 knownCommands.put(label, command); 157 158 return registered; 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 public boolean dispatch(CommandSender sender, String commandLine) throws CommandException { 165 String[] args = PATTERN_ON_SPACE.split(commandLine); 166 167 if (args.length == 0) { 168 return false; 169 } 170 171 String sentCommandLabel = args[0].toLowerCase(); 172 Command target = getCommand(sentCommandLabel); 173 174 if (target == null) { 175 return false; 176 } 177 178 try { 179 // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) 180 target.execute(sender, sentCommandLabel, Arrays_copyOfRange(args, 1, args.length)); 181 } catch (CommandException ex) { 182 throw ex; 183 } catch (Throwable ex) { 184 throw new CommandException("Unhandled exception executing '" + commandLine + "' in " + target, ex); 185 } 186 187 // return true as command was handled 188 return true; 189 } 190 191 public synchronized void clearCommands() { 192 for (Map.Entry<String, Command> entry : knownCommands.entrySet()) { 193 entry.getValue().unregister(this); 194 } 195 knownCommands.clear(); 196 setDefaultCommands(); 197 } 198 199 public Command getCommand(String name) { 200 Command target = knownCommands.get(name.toLowerCase()); 201 return target; 202 } 203 204 public List<String> tabComplete(CommandSender sender, String cmdLine) { 205 Validate.notNull(sender, "Sender cannot be null"); 206 Validate.notNull(cmdLine, "Command line cannot null"); 207 208 int spaceIndex = cmdLine.indexOf(' '); 209 210 if (spaceIndex == -1) { 211 ArrayList<String> completions = new ArrayList<String>(); 212 Map<String, Command> knownCommands = this.knownCommands; 213 214 final String prefix = (sender instanceof Player ? "/" : ""); 215 216 for (Map.Entry<String, Command> commandEntry : knownCommands.entrySet()) { 217 Command command = commandEntry.getValue(); 218 219 if (!command.testPermissionSilent(sender)) { 220 continue; 221 } 222 223 String name = commandEntry.getKey(); // Use the alias, not command name 224 225 if (StringUtil.startsWithIgnoreCase(name, cmdLine)) { 226 completions.add(prefix + name); 227 } 228 } 229 230 Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); 231 return completions; 232 } 233 234 String commandName = cmdLine.substring(0, spaceIndex); 235 Command target = getCommand(commandName); 236 237 if (target == null) { 238 return null; 239 } 240 241 if (!target.testPermissionSilent(sender)) { 242 return null; 243 } 244 245 String argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length()); 246 String[] args = PATTERN_ON_SPACE.split(argLine, -1); 247 248 try { 249 return target.tabComplete(sender, commandName, args); 250 } catch (CommandException ex) { 251 throw ex; 252 } catch (Throwable ex) { 253 throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex); 254 } 255 } 256 257 public Collection<Command> getCommands() { 258 return Collections.unmodifiableCollection(knownCommands.values()); 259 } 260 261 public void registerServerAliases() { 262 Map<String, String[]> values = server.getCommandAliases(); 263 264 for (String alias : values.keySet()) { 265 if (alias.contains(":") || alias.contains(" ")) { 266 server.getLogger().warning("Could not register alias " + alias + " because it contains illegal characters"); 267 continue; 268 } 269 270 String[] commandStrings = values.get(alias); 271 List<String> targets = new ArrayList<String>(); 272 StringBuilder bad = new StringBuilder(); 273 274 for (String commandString : commandStrings) { 275 String[] commandArgs = commandString.split(" "); 276 Command command = getCommand(commandArgs[0]); 277 278 if (command == null) { 279 if (bad.length() > 0) { 280 bad.append(", "); 281 } 282 bad.append(commandString); 283 } else { 284 targets.add(commandString); 285 } 286 } 287 288 if (bad.length() > 0) { 289 server.getLogger().warning("Could not register alias " + alias + " because it contains commands that do not exist: " + bad); 290 continue; 291 } 292 293 // We register these as commands so they have absolute priority. 294 if (targets.size() > 0) { 295 knownCommands.put(alias.toLowerCase(), new FormattedCommandAlias(alias.toLowerCase(), targets.toArray(new String[targets.size()]))); 296 } else { 297 knownCommands.remove(alias.toLowerCase()); 298 } 299 } 300 } 301 }