Run schematic-file I/O in a task.

This commit is contained in:
wizjany 2019-05-12 16:01:22 -04:00
parent fab21c3eea
commit 7b170d8193
4 changed files with 100 additions and 157 deletions

View File

@ -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.

View File

@ -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<ClipboardHolder> {
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<Void> {
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;
}
}
}

View File

@ -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) {

View File

@ -1,114 +0,0 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<V> implements FutureCallback<V> {
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 <V> MessageFutureCallback<V> build() {
checkNotNull(exceptionConverter);
return new MessageFutureCallback<>(exceptionConverter, sender, success, failure);
}
}
public static <V> MessageFutureCallback<V> createRegionLoadCallback(ExceptionConverter exceptionConverter, Actor sender) {
return new Builder(sender)
.exceptionConverter(exceptionConverter)
.onSuccess("Successfully load the region data.")
.build();
}
public static <V> MessageFutureCallback<V> createRegionSaveCallback(ExceptionConverter exceptionConverter, Actor sender) {
return new Builder(sender)
.exceptionConverter(exceptionConverter)
.onSuccess("Successfully saved the region data.")
.build();
}
}