From 7b170d81939f8e86ae9b67093ab77c4cd210fc2a Mon Sep 17 00:00:00 2001 From: wizjany Date: Sun, 12 May 2019 16:01:22 -0400 Subject: [PATCH] Run schematic-file I/O in a task. --- .../java/com/sk89q/worldedit/WorldEdit.java | 15 ++- .../worldedit/command/SchematicCommands.java | 126 ++++++++++++------ .../worldedit/command/WorldEditCommands.java | 2 +- .../command/util/MessageFutureCallback.java | 114 ---------------- 4 files changed, 100 insertions(+), 157 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java index 38c7cbd26..c9e926222 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -21,6 +21,8 @@ package com.sk89q.worldedit; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.BlockInteractEvent; @@ -44,6 +46,7 @@ import com.sk89q.worldedit.scripting.RhinoCraftScriptEngine; import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.concurrency.EvenMoreExecutors; import com.sk89q.worldedit.util.eventbus.EventBus; import com.sk89q.worldedit.util.io.file.FileSelectionAbortedException; import com.sk89q.worldedit.util.io.file.FilenameException; @@ -102,6 +105,7 @@ public final class WorldEdit { private final PlatformManager platformManager = new PlatformManager(this); private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus); private final SessionManager sessions = new SessionManager(this); + private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20));; private final Supervisor supervisor = new SimpleSupervisor(); private final BlockFactory blockFactory = new BlockFactory(this); @@ -152,7 +156,7 @@ public final class WorldEdit { } /** - * Get the supervisor. + * Get the supervisor. Internal, not for API use. * * @return the supervisor */ @@ -160,6 +164,15 @@ public final class WorldEdit { return supervisor; } + /** + * Get the executor service. Internal, not for API use. + * + * @return the executor service + */ + public ListeningExecutorService getExecutorService() { + return executorService; + } + /** * Get the block factory from which new {@link BlockStateHolder}s can be * constructed. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 829af8a9c..fee171716 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.command.util.AsyncCommandBuilder; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.entity.Player; @@ -60,6 +61,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.Callable; import static com.google.common.base.Preconditions.checkNotNull; @@ -113,20 +115,13 @@ public class SchematicCommands { return; } - try (Closer closer = Closer.create()) { - FileInputStream fis = closer.register(new FileInputStream(f)); - BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); - ClipboardReader reader = closer.register(format.getReader(bis)); - - Clipboard clipboard = reader.read(); - session.setClipboard(new ClipboardHolder(clipboard)); - - log.info(player.getName() + " loaded " + f.getCanonicalPath()); - player.print(filename + " loaded. Paste it with //paste"); - } catch (IOException e) { - player.printError("Schematic could not read or it does not exist: " + e.getMessage()); - log.warn("Failed to load schematic: " + e.getMessage()); - } + SchematicLoadTask task = new SchematicLoadTask(player, f, format); + AsyncCommandBuilder.wrap(task, player) + .registerWithSupervisor(worldEdit.getSupervisor(), "Loading schematic " + filename) + .sendMessageAfterDelay("(Please wait... loading schematic.)") + .onSuccess(filename + " loaded. Paste it with //paste", session::setClipboard) + .onFailure("Failed to load schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter()) + .buildAndExec(worldEdit.getExecutorService()); } @Command( @@ -165,42 +160,24 @@ public class SchematicCommands { } } - ClipboardHolder holder = session.getClipboard(); - Clipboard clipboard = holder.getClipboard(); - Transform transform = holder.getTransform(); - Clipboard target; - - // If we have a transform, bake it into the copy - if (!transform.isIdentity()) { - FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform); - target = new BlockArrayClipboard(result.getTransformedRegion()); - target.setOrigin(clipboard.getOrigin()); - Operations.completeLegacy(result.copyTo(target)); - } else { - target = clipboard; - } - // Create parent directories File parent = f.getParentFile(); if (parent != null && !parent.exists()) { if (!parent.mkdirs()) { throw new StopExecutionException(TextComponent.of( - "Could not create folder for schematics!")); + "Could not create folder for schematics!")); } } - try (Closer closer = Closer.create()) { - FileOutputStream fos = closer.register(new FileOutputStream(f)); - BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); - ClipboardWriter writer = closer.register(format.getWriter(bos)); - writer.write(target); + ClipboardHolder holder = session.getClipboard(); - log.info(player.getName() + " saved " + f.getCanonicalPath() + (overwrite ? " (overwriting previous file)" : "")); - player.print(filename + " saved" + (overwrite ? " (overwriting previous file)." : ".")); - } catch (IOException e) { - player.printError("Schematic could not written: " + e.getMessage()); - log.warn("Failed to write a saved clipboard", e); - } + SchematicSaveTask task = new SchematicSaveTask(player, f, format, holder, overwrite); + AsyncCommandBuilder.wrap(task, player) + .registerWithSupervisor(worldEdit.getSupervisor(), "Saving schematic " + filename) + .sendMessageAfterDelay("(Please wait... saving schematic.)") + .onSuccess(filename + " saved" + (overwrite ? " (overwriting previous file)." : "."), null) + .onFailure("Failed to load schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter()) + .buildAndExec(worldEdit.getExecutorService()); } @Command( @@ -329,4 +306,71 @@ public class SchematicCommands { return fileList; } + private static class SchematicLoadTask implements Callable { + private final Player player; + private final File file; + private final ClipboardFormat format; + + SchematicLoadTask(Player player, File file, ClipboardFormat format) { + this.player = player; + this.file = file; + this.format = format; + } + + @Override + public ClipboardHolder call() throws Exception { + try (Closer closer = Closer.create()) { + FileInputStream fis = closer.register(new FileInputStream(file)); + BufferedInputStream bis = closer.register(new BufferedInputStream(fis)); + ClipboardReader reader = closer.register(format.getReader(bis)); + + Clipboard clipboard = reader.read(); + log.info(player.getName() + " loaded " + file.getCanonicalPath()); + return new ClipboardHolder(clipboard); + } + } + } + + private static class SchematicSaveTask implements Callable { + private final Player player; + private final File file; + private final ClipboardFormat format; + private final ClipboardHolder holder; + private final boolean overwrite; + + SchematicSaveTask(Player player, File file, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) { + this.player = player; + this.file = file; + this.format = format; + this.holder = holder; + this.overwrite = overwrite; + } + + @Override + public Void call() throws Exception { + Clipboard clipboard = holder.getClipboard(); + Transform transform = holder.getTransform(); + Clipboard target; + + // If we have a transform, bake it into the copy + if (transform.isIdentity()) { + target = clipboard; + } else { + FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform); + target = new BlockArrayClipboard(result.getTransformedRegion()); + target.setOrigin(clipboard.getOrigin()); + Operations.completeLegacy(result.copyTo(target)); + } + + try (Closer closer = Closer.create()) { + FileOutputStream fos = closer.register(new FileOutputStream(file)); + BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos)); + ClipboardWriter writer = closer.register(format.getWriter(bos)); + writer.write(target); + + log.info(player.getName() + " saved " + file.getCanonicalPath() + (overwrite ? " (overwriting previous file)" : "")); + } + return null; + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index dee15226e..154f57f52 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -110,7 +110,7 @@ public class WorldEditCommands { String result = report.toString(); try { - File dest = new File(we.getWorkingDirectoryFile(we.getConfiguration().saveDir), "report.txt"); + File dest = new File(we.getConfiguration().getWorkingDirectory(), "report.txt"); Files.write(result, dest, Charset.forName("UTF-8")); actor.print("WorldEdit report written to " + dest.getAbsolutePath()); } catch (IOException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java deleted file mode 100644 index 2a330259e..000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/MessageFutureCallback.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser 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 Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.command.util; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.util.concurrent.FutureCallback; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.internal.command.exception.ExceptionConverter; -import org.enginehub.piston.exception.CommandException; - -import javax.annotation.Nullable; - -public class MessageFutureCallback implements FutureCallback { - - private final ExceptionConverter exceptionConverter; - private final Actor sender; - @Nullable - private final String success; - @Nullable - private final String failure; - - private MessageFutureCallback(ExceptionConverter exceptionConverter, Actor sender, @Nullable String success, @Nullable String failure) { - this.exceptionConverter = exceptionConverter; - this.sender = sender; - this.success = success; - this.failure = failure; - } - - @Override - public void onSuccess(@Nullable V v) { - if (success != null) { - sender.print(success); - } - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - try { - exceptionConverter.convert(throwable); - } catch (CommandException e) { - String failure = this.failure != null ? this.failure : "An error occurred"; - String message = e.getMessage() != null ? e.getMessage() : "An unknown error occurred. Please see the console!"; - sender.printError(failure + ": " + message); - } - } - - public static class Builder { - private final Actor sender; - @Nullable - private String success; - @Nullable - private String failure; - private ExceptionConverter exceptionConverter; - - public Builder(Actor sender) { - checkNotNull(sender); - - this.sender = sender; - } - - public Builder exceptionConverter(ExceptionConverter exceptionConverter) { - this.exceptionConverter = exceptionConverter; - return this; - } - - public Builder onSuccess(@Nullable String message) { - this.success = message; - return this; - } - - public Builder onFailure(@Nullable String message) { - this.failure = message; - return this; - } - - public MessageFutureCallback build() { - checkNotNull(exceptionConverter); - return new MessageFutureCallback<>(exceptionConverter, sender, success, failure); - } - } - - public static MessageFutureCallback createRegionLoadCallback(ExceptionConverter exceptionConverter, Actor sender) { - return new Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess("Successfully load the region data.") - .build(); - } - - public static MessageFutureCallback createRegionSaveCallback(ExceptionConverter exceptionConverter, Actor sender) { - return new Builder(sender) - .exceptionConverter(exceptionConverter) - .onSuccess("Successfully saved the region data.") - .build(); - } - -}