PageRenderTime 37ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java

https://gitlab.com/Skull3x/WorldEdit
Java | 350 lines | 271 code | 39 blank | 40 comment | 26 complexity | cd13a7536e72391cbfd4adc1ad506033 MD5 | raw file
  1. /*
  2. * WorldEdit, a Minecraft world manipulation toolkit
  3. * Copyright (C) sk89q <http://www.sk89q.com>
  4. * Copyright (C) WorldEdit team and contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  14. * for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldedit.extension.platform;
  20. import com.google.common.base.Joiner;
  21. import com.sk89q.minecraft.util.commands.CommandException;
  22. import com.sk89q.minecraft.util.commands.CommandLocals;
  23. import com.sk89q.minecraft.util.commands.CommandPermissionsException;
  24. import com.sk89q.minecraft.util.commands.WrappedCommandException;
  25. import com.sk89q.worldedit.EditSession;
  26. import com.sk89q.worldedit.LocalConfiguration;
  27. import com.sk89q.worldedit.LocalSession;
  28. import com.sk89q.worldedit.WorldEdit;
  29. import com.sk89q.worldedit.command.BiomeCommands;
  30. import com.sk89q.worldedit.command.BrushCommands;
  31. import com.sk89q.worldedit.command.ChunkCommands;
  32. import com.sk89q.worldedit.command.ClipboardCommands;
  33. import com.sk89q.worldedit.command.GeneralCommands;
  34. import com.sk89q.worldedit.command.GenerationCommands;
  35. import com.sk89q.worldedit.command.HistoryCommands;
  36. import com.sk89q.worldedit.command.NavigationCommands;
  37. import com.sk89q.worldedit.command.RegionCommands;
  38. import com.sk89q.worldedit.command.SchematicCommands;
  39. import com.sk89q.worldedit.command.ScriptingCommands;
  40. import com.sk89q.worldedit.command.SelectionCommands;
  41. import com.sk89q.worldedit.command.SnapshotCommands;
  42. import com.sk89q.worldedit.command.SnapshotUtilCommands;
  43. import com.sk89q.worldedit.command.SuperPickaxeCommands;
  44. import com.sk89q.worldedit.command.ToolCommands;
  45. import com.sk89q.worldedit.command.ToolUtilCommands;
  46. import com.sk89q.worldedit.command.UtilityCommands;
  47. import com.sk89q.worldedit.command.WorldEditCommands;
  48. import com.sk89q.worldedit.command.argument.ReplaceParser;
  49. import com.sk89q.worldedit.command.argument.TreeGeneratorParser;
  50. import com.sk89q.worldedit.command.composition.ApplyCommand;
  51. import com.sk89q.worldedit.command.composition.DeformCommand;
  52. import com.sk89q.worldedit.command.composition.PaintCommand;
  53. import com.sk89q.worldedit.command.composition.SelectionCommand;
  54. import com.sk89q.worldedit.command.composition.ShapedBrushCommand;
  55. import com.sk89q.worldedit.event.platform.CommandEvent;
  56. import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
  57. import com.sk89q.worldedit.function.factory.Deform;
  58. import com.sk89q.worldedit.function.factory.Deform.Mode;
  59. import com.sk89q.worldedit.internal.command.ActorAuthorizer;
  60. import com.sk89q.worldedit.internal.command.CommandLoggingHandler;
  61. import com.sk89q.worldedit.internal.command.UserCommandCompleter;
  62. import com.sk89q.worldedit.internal.command.WorldEditBinding;
  63. import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter;
  64. import com.sk89q.worldedit.session.request.Request;
  65. import com.sk89q.worldedit.util.command.Dispatcher;
  66. import com.sk89q.worldedit.util.command.InvalidUsageException;
  67. import com.sk89q.worldedit.util.command.composition.ProvidedValue;
  68. import com.sk89q.worldedit.util.command.fluent.CommandGraph;
  69. import com.sk89q.worldedit.util.command.parametric.ExceptionConverter;
  70. import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
  71. import com.sk89q.worldedit.util.command.parametric.ParametricBuilder;
  72. import com.sk89q.worldedit.util.eventbus.Subscribe;
  73. import com.sk89q.worldedit.util.formatting.ColorCodeBuilder;
  74. import com.sk89q.worldedit.util.formatting.component.CommandUsageBox;
  75. import com.sk89q.worldedit.util.logging.DynamicStreamHandler;
  76. import com.sk89q.worldedit.util.logging.LogFormat;
  77. import java.io.File;
  78. import java.io.IOException;
  79. import java.util.logging.FileHandler;
  80. import java.util.logging.Level;
  81. import java.util.logging.Logger;
  82. import java.util.regex.Pattern;
  83. import static com.google.common.base.Preconditions.checkNotNull;
  84. import static com.sk89q.worldedit.util.command.composition.LegacyCommandAdapter.adapt;
  85. /**
  86. * Handles the registration and invocation of commands.
  87. *
  88. * <p>This class is primarily for internal usage.</p>
  89. */
  90. public final class CommandManager {
  91. public static final Pattern COMMAND_CLEAN_PATTERN = Pattern.compile("^[/]+");
  92. private static final Logger log = Logger.getLogger(CommandManager.class.getCanonicalName());
  93. private static final Logger commandLog = Logger.getLogger(CommandManager.class.getCanonicalName() + ".CommandLog");
  94. private static final Pattern numberFormatExceptionPattern = Pattern.compile("^For input string: \"(.*)\"$");
  95. private final WorldEdit worldEdit;
  96. private final PlatformManager platformManager;
  97. private final Dispatcher dispatcher;
  98. private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler();
  99. private final ExceptionConverter exceptionConverter;
  100. /**
  101. * Create a new instance.
  102. *
  103. * @param worldEdit the WorldEdit instance
  104. */
  105. CommandManager(final WorldEdit worldEdit, PlatformManager platformManager) {
  106. checkNotNull(worldEdit);
  107. checkNotNull(platformManager);
  108. this.worldEdit = worldEdit;
  109. this.platformManager = platformManager;
  110. this.exceptionConverter = new WorldEditExceptionConverter(worldEdit);
  111. // Register this instance for command events
  112. worldEdit.getEventBus().register(this);
  113. // Setup the logger
  114. commandLog.addHandler(dynamicHandler);
  115. dynamicHandler.setFormatter(new LogFormat());
  116. // Set up the commands manager
  117. ParametricBuilder builder = new ParametricBuilder();
  118. builder.setAuthorizer(new ActorAuthorizer());
  119. builder.setDefaultCompleter(new UserCommandCompleter(platformManager));
  120. builder.addBinding(new WorldEditBinding(worldEdit));
  121. builder.addExceptionConverter(exceptionConverter);
  122. builder.addInvokeListener(new LegacyCommandsHandler());
  123. builder.addInvokeListener(new CommandLoggingHandler(worldEdit, commandLog));
  124. dispatcher = new CommandGraph()
  125. .builder(builder)
  126. .commands()
  127. .registerMethods(new BiomeCommands(worldEdit))
  128. .registerMethods(new ChunkCommands(worldEdit))
  129. .registerMethods(new ClipboardCommands(worldEdit))
  130. .registerMethods(new GeneralCommands(worldEdit))
  131. .registerMethods(new GenerationCommands(worldEdit))
  132. .registerMethods(new HistoryCommands(worldEdit))
  133. .registerMethods(new NavigationCommands(worldEdit))
  134. .registerMethods(new RegionCommands(worldEdit))
  135. .registerMethods(new ScriptingCommands(worldEdit))
  136. .registerMethods(new SelectionCommands(worldEdit))
  137. .registerMethods(new SnapshotUtilCommands(worldEdit))
  138. .registerMethods(new ToolUtilCommands(worldEdit))
  139. .registerMethods(new ToolCommands(worldEdit))
  140. .registerMethods(new UtilityCommands(worldEdit))
  141. .register(adapt(new SelectionCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within selection"), "worldedit.region.set")), "/set")
  142. .group("worldedit", "we")
  143. .describeAs("WorldEdit commands")
  144. .registerMethods(new WorldEditCommands(worldEdit))
  145. .parent()
  146. .group("schematic", "schem", "/schematic", "/schem")
  147. .describeAs("Schematic commands for saving/loading areas")
  148. .registerMethods(new SchematicCommands(worldEdit))
  149. .parent()
  150. .group("snapshot", "snap")
  151. .describeAs("Schematic commands for saving/loading areas")
  152. .registerMethods(new SnapshotCommands(worldEdit))
  153. .parent()
  154. .group("brush", "br")
  155. .describeAs("Brushing commands")
  156. .registerMethods(new BrushCommands(worldEdit))
  157. .register(adapt(new ShapedBrushCommand(new DeformCommand(), "worldedit.brush.deform")), "deform")
  158. .register(adapt(new ShapedBrushCommand(new ApplyCommand(new ReplaceParser(), "Set all blocks within region"), "worldedit.brush.set")), "set")
  159. .register(adapt(new ShapedBrushCommand(new PaintCommand(), "worldedit.brush.paint")), "paint")
  160. .register(adapt(new ShapedBrushCommand(new ApplyCommand(), "worldedit.brush.apply")), "apply")
  161. .register(adapt(new ShapedBrushCommand(new PaintCommand(new TreeGeneratorParser("treeType")), "worldedit.brush.forest")), "forest")
  162. .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y-=1", Mode.RAW_COORD), "Raise one block"), "worldedit.brush.raise")), "raise")
  163. .register(adapt(new ShapedBrushCommand(ProvidedValue.create(new Deform("y+=1", Mode.RAW_COORD), "Lower one block"), "worldedit.brush.lower")), "lower")
  164. .parent()
  165. .group("superpickaxe", "pickaxe", "sp")
  166. .describeAs("Super-pickaxe commands")
  167. .registerMethods(new SuperPickaxeCommands(worldEdit))
  168. .parent()
  169. .group("tool")
  170. .describeAs("Bind functions to held items")
  171. .registerMethods(new ToolCommands(worldEdit))
  172. .parent()
  173. .graph()
  174. .getDispatcher();
  175. }
  176. public ExceptionConverter getExceptionConverter() {
  177. return exceptionConverter;
  178. }
  179. void register(Platform platform) {
  180. log.log(Level.FINE, "Registering commands with " + platform.getClass().getCanonicalName());
  181. LocalConfiguration config = platform.getConfiguration();
  182. boolean logging = config.logCommands;
  183. String path = config.logFile;
  184. // Register log
  185. if (!logging || path.isEmpty()) {
  186. dynamicHandler.setHandler(null);
  187. commandLog.setLevel(Level.OFF);
  188. } else {
  189. File file = new File(config.getWorkingDirectory(), path);
  190. commandLog.setLevel(Level.ALL);
  191. log.log(Level.INFO, "Logging WorldEdit commands to " + file.getAbsolutePath());
  192. try {
  193. dynamicHandler.setHandler(new FileHandler(file.getAbsolutePath(), true));
  194. } catch (IOException e) {
  195. log.log(Level.WARNING, "Could not use command log file " + path + ": " + e.getMessage());
  196. }
  197. }
  198. platform.registerCommands(dispatcher);
  199. }
  200. void unregister() {
  201. dynamicHandler.setHandler(null);
  202. }
  203. public String[] commandDetection(String[] split) {
  204. // Quick script shortcut
  205. if (split[0].matches("^[^/].*\\.js$")) {
  206. String[] newSplit = new String[split.length + 1];
  207. System.arraycopy(split, 0, newSplit, 1, split.length);
  208. newSplit[0] = "cs";
  209. newSplit[1] = newSplit[1];
  210. split = newSplit;
  211. }
  212. String searchCmd = split[0].toLowerCase();
  213. // Try to detect the command
  214. if (!dispatcher.contains(searchCmd)) {
  215. if (worldEdit.getConfiguration().noDoubleSlash && dispatcher.contains("/" + searchCmd)) {
  216. split[0] = "/" + split[0];
  217. } else if (searchCmd.length() >= 2 && searchCmd.charAt(0) == '/' && dispatcher.contains(searchCmd.substring(1))) {
  218. split[0] = split[0].substring(1);
  219. }
  220. }
  221. return split;
  222. }
  223. @Subscribe
  224. public void handleCommand(CommandEvent event) {
  225. Request.reset();
  226. Actor actor = platformManager.createProxyActor(event.getActor());
  227. String[] split = commandDetection(event.getArguments().split(" "));
  228. // No command found!
  229. if (!dispatcher.contains(split[0])) {
  230. return;
  231. }
  232. LocalSession session = worldEdit.getSessionManager().get(actor);
  233. LocalConfiguration config = worldEdit.getConfiguration();
  234. CommandLocals locals = new CommandLocals();
  235. locals.put(Actor.class, actor);
  236. locals.put("arguments", event.getArguments());
  237. long start = System.currentTimeMillis();
  238. try {
  239. dispatcher.call(Joiner.on(" ").join(split), locals, new String[0]);
  240. } catch (CommandPermissionsException e) {
  241. actor.printError("You are not permitted to do that. Are you in the right mode?");
  242. } catch (InvalidUsageException e) {
  243. if (e.isFullHelpSuggested()) {
  244. actor.printRaw(ColorCodeBuilder.asColorCodes(new CommandUsageBox(e.getCommand(), e.getCommandUsed("/", ""), locals)));
  245. String message = e.getMessage();
  246. if (message != null) {
  247. actor.printError(message);
  248. }
  249. } else {
  250. String message = e.getMessage();
  251. actor.printError(message != null ? message : "The command was not used properly (no more help available).");
  252. actor.printError("Usage: " + e.getSimpleUsageString("/"));
  253. }
  254. } catch (WrappedCommandException e) {
  255. Throwable t = e.getCause();
  256. actor.printError("Please report this error: [See console]");
  257. actor.printRaw(t.getClass().getName() + ": " + t.getMessage());
  258. log.log(Level.SEVERE, "An unexpected error while handling a WorldEdit command", t);
  259. } catch (CommandException e) {
  260. String message = e.getMessage();
  261. if (message != null) {
  262. actor.printError(e.getMessage());
  263. } else {
  264. actor.printError("An unknown error has occurred! Please see console.");
  265. log.log(Level.SEVERE, "An unknown error occurred", e);
  266. }
  267. } finally {
  268. EditSession editSession = locals.get(EditSession.class);
  269. if (editSession != null) {
  270. session.remember(editSession);
  271. editSession.flushQueue();
  272. if (config.profile) {
  273. long time = System.currentTimeMillis() - start;
  274. int changed = editSession.getBlockChangeCount();
  275. if (time > 0) {
  276. double throughput = changed / (time / 1000.0);
  277. actor.printDebug((time / 1000.0) + "s elapsed (history: "
  278. + changed + " changed; "
  279. + Math.round(throughput) + " blocks/sec).");
  280. } else {
  281. actor.printDebug((time / 1000.0) + "s elapsed.");
  282. }
  283. }
  284. worldEdit.flushBlockBag(actor, editSession);
  285. }
  286. }
  287. event.setCancelled(true);
  288. }
  289. @Subscribe
  290. public void handleCommandSuggestion(CommandSuggestionEvent event) {
  291. try {
  292. CommandLocals locals = new CommandLocals();
  293. locals.put(Actor.class, event.getActor());
  294. locals.put("arguments", event.getArguments());
  295. event.setSuggestions(dispatcher.getSuggestions(event.getArguments(), locals));
  296. } catch (CommandException e) {
  297. event.getActor().printError(e.getMessage());
  298. }
  299. }
  300. /**
  301. * Get the command dispatcher instance.
  302. *
  303. * @return the command dispatcher
  304. */
  305. public Dispatcher getDispatcher() {
  306. return dispatcher;
  307. }
  308. public static Logger getLogger() {
  309. return commandLog;
  310. }
  311. }