Move the pasting system over to WorldEdit, and add a /we report command. Currently only reports system status and the config. Need to add a platform reporter system.

This commit is contained in:
Matthew Miller 2018-12-18 19:28:55 +10:00
parent 8f236afae9
commit 5eb9b779d7
30 changed files with 2441 additions and 419 deletions

View File

@ -14,6 +14,7 @@
<allow pkg="gnu.trove"/> <allow pkg="gnu.trove"/>
<allow pkg="com.google.gson"/> <allow pkg="com.google.gson"/>
<allow pkg="net.royawesome.jlibnoise"/> <allow pkg="net.royawesome.jlibnoise"/>
<allow pkg="org.json.simple" />
<subpackage name="util.yaml"> <subpackage name="util.yaml">
<allow pkg="org.yaml.snakeyaml"/> <allow pkg="org.yaml.snakeyaml"/>

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.bukkit;
import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.worldedit.util.YAMLConfiguration; import com.sk89q.worldedit.util.YAMLConfiguration;
import com.sk89q.worldedit.util.report.Unreported;
import java.io.File; import java.io.File;
@ -30,7 +31,7 @@ import java.io.File;
public class BukkitConfiguration extends YAMLConfiguration { public class BukkitConfiguration extends YAMLConfiguration {
public boolean noOpPermissions = false; public boolean noOpPermissions = false;
private final WorldEditPlugin plugin; @Unreported private final WorldEditPlugin plugin;
public BukkitConfiguration(YAMLProcessor config, WorldEditPlugin plugin) { public BukkitConfiguration(YAMLProcessor config, WorldEditPlugin plugin) {
super(config, plugin.getLogger()); super(config, plugin.getLogger());

View File

@ -11,6 +11,7 @@ dependencies {
compile 'com.thoughtworks.paranamer:paranamer:2.6' compile 'com.thoughtworks.paranamer:paranamer:2.6'
compile 'com.google.code.gson:gson:2.8.0' compile 'com.google.code.gson:gson:2.8.0'
compile 'com.sk89q.lib:jlibnoise:1.0.0' compile 'com.sk89q.lib:jlibnoise:1.0.0'
compile 'com.googlecode.json-simple:json-simple:1.1.1'
//compile 'net.sf.trove4j:trove4j:3.0.3' //compile 'net.sf.trove4j:trove4j:3.0.3'
testCompile 'org.mockito:mockito-core:1.9.0-rc1' testCompile 'org.mockito:mockito-core:1.9.0-rc1'
} }

View File

@ -54,6 +54,8 @@ import com.sk89q.worldedit.util.io.file.FilenameException;
import com.sk89q.worldedit.util.io.file.FilenameResolutionException; import com.sk89q.worldedit.util.io.file.FilenameResolutionException;
import com.sk89q.worldedit.util.io.file.InvalidFilenameException; import com.sk89q.worldedit.util.io.file.InvalidFilenameException;
import com.sk89q.worldedit.util.logging.WorldEditPrefixHandler; import com.sk89q.worldedit.util.logging.WorldEditPrefixHandler;
import com.sk89q.worldedit.util.task.SimpleSupervisor;
import com.sk89q.worldedit.util.task.Supervisor;
import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.registry.BundledBlockData; import com.sk89q.worldedit.world.registry.BundledBlockData;
@ -99,6 +101,7 @@ public class WorldEdit {
private final PlatformManager platformManager = new PlatformManager(this); private final PlatformManager platformManager = new PlatformManager(this);
private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus); private final EditSessionFactory editSessionFactory = new EditSessionFactory.EditSessionFactoryImpl(eventBus);
private final SessionManager sessions = new SessionManager(this); private final SessionManager sessions = new SessionManager(this);
private final Supervisor supervisor = new SimpleSupervisor();
private final BlockFactory blockFactory = new BlockFactory(this); private final BlockFactory blockFactory = new BlockFactory(this);
private final ItemFactory itemFactory = new ItemFactory(this); private final ItemFactory itemFactory = new ItemFactory(this);
@ -148,6 +151,15 @@ public class WorldEdit {
return eventBus; return eventBus;
} }
/**
* Get the supervisor.
*
* @return the supervisor
*/
public Supervisor getSupervisor() {
return supervisor;
}
/** /**
* Get the block factory from which new {@link BlockStateHolder}s can be * Get the block factory from which new {@link BlockStateHolder}s can be
* constructed. * constructed.

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.command; package com.sk89q.worldedit.command;
import com.google.common.io.Files;
import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.minecraft.util.commands.CommandPermissions;
@ -32,7 +33,14 @@ import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.extension.platform.PlatformManager;
import com.sk89q.worldedit.util.paste.ActorCallbackPaste;
import com.sk89q.worldedit.util.report.ConfigReport;
import com.sk89q.worldedit.util.report.ReportList;
import com.sk89q.worldedit.util.report.SystemInfoReport;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
@ -86,6 +94,28 @@ public class WorldEditCommands {
actor.print("Configuration reloaded!"); actor.print("Configuration reloaded!");
} }
@Command(aliases = {"report"}, desc = "Writes a report on WorldEdit", flags = "p", max = 0)
@CommandPermissions({"worldedit.report"})
public void report(Actor actor, CommandContext args) throws WorldEditException {
ReportList report = new ReportList("Report");
report.add(new SystemInfoReport());
report.add(new ConfigReport());
String result = report.toString();
try {
File dest = new File(we.getWorkingDirectoryFile(we.getConfiguration().saveDir), "report.txt");
Files.write(result, dest, Charset.forName("UTF-8"));
actor.print("WorldEdit report written to " + dest.getAbsolutePath());
} catch (IOException e) {
actor.printError("Failed to write report: " + e.getMessage());
}
if (args.hasFlag('p')) {
actor.checkPermission("worldedit.report.pastebin");
ActorCallbackPaste.pastebin(we.getSupervisor(), actor, result, "WorldEdit report: %s.report");
}
}
@Command( @Command(
aliases = { "cui" }, aliases = { "cui" },
usage = "", usage = "",

View File

@ -0,0 +1,135 @@
/*
* 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.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.task.FutureForwardingTask;
import com.sk89q.worldedit.util.task.Supervisor;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nullable;
public class AsyncCommandHelper {
private final ListenableFuture<?> future;
private final Supervisor supervisor;
private final Actor sender;
@Nullable
private Object[] formatArgs;
private AsyncCommandHelper(ListenableFuture<?> future, Supervisor supervisor, Actor sender) {
checkNotNull(future);
checkNotNull(supervisor);
checkNotNull(sender);
this.future = future;
this.supervisor = supervisor;
this.sender = sender;
}
public AsyncCommandHelper formatUsing(Object... args) {
this.formatArgs = args;
return this;
}
private String format(String message) {
if (formatArgs != null) {
return String.format(message, formatArgs);
} else {
return message;
}
}
public AsyncCommandHelper registerWithSupervisor(String description) {
supervisor.monitor(
FutureForwardingTask.create(
future, format(description), sender));
return this;
}
public AsyncCommandHelper sendMessageAfterDelay(String message) {
FutureProgressListener.addProgressListener(future, sender, format(message));
return this;
}
public AsyncCommandHelper thenRespondWith(String success, String failure) {
// Send a response message
Futures.addCallback(
future,
new MessageFutureCallback.Builder(sender)
.onSuccess(format(success))
.onFailure(format(failure))
.build());
return this;
}
public AsyncCommandHelper thenTellErrorsOnly(String failure) {
// Send a response message
Futures.addCallback(
future,
new MessageFutureCallback.Builder(sender)
.onFailure(format(failure))
.build());
return this;
}
public AsyncCommandHelper forRegionDataLoad(World world, boolean silent) {
checkNotNull(world);
formatUsing(world.getName());
registerWithSupervisor("Loading region data for '%s'");
if (silent) {
thenTellErrorsOnly("Failed to load regions '%s'");
} else {
sendMessageAfterDelay("(Please wait... loading the region data for '%s')");
thenRespondWith(
"Loaded region data for '%s'",
"Failed to load regions '%s'");
}
return this;
}
public AsyncCommandHelper forRegionDataSave(World world, boolean silent) {
checkNotNull(world);
formatUsing(world.getName());
registerWithSupervisor("Saving region data for '%s'");
if (silent) {
thenTellErrorsOnly("Failed to save regions '%s'");
} else {
sendMessageAfterDelay("(Please wait... saving the region data for '%s')");
thenRespondWith(
"Saved region data for '%s'",
"Failed to load regions '%s'");
}
return this;
}
public static AsyncCommandHelper wrap(ListenableFuture<?> future, Supervisor supervisor, Actor sender) {
return new AsyncCommandHelper(future, supervisor, sender);
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.sk89q.worldedit.extension.platform.Actor;
import java.util.Timer;
public class FutureProgressListener implements Runnable {
private static final Timer timer = new Timer();
private static final int MESSAGE_DELAY = 1000;
private final MessageTimerTask task;
public FutureProgressListener(Actor sender, String message) {
checkNotNull(sender);
checkNotNull(message);
task = new MessageTimerTask(sender, message);
timer.schedule(task, MESSAGE_DELAY);
}
@Override
public void run() {
task.cancel();
}
public static void addProgressListener(ListenableFuture<?> future, Actor sender, String message) {
future.addListener(new FutureProgressListener(sender, message), MoreExecutors.directExecutor());
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.minecraft.util.commands.CommandException;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.command.parametric.ExceptionConverter;
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();
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.sk89q.worldedit.extension.platform.Actor;
import java.util.TimerTask;
public class MessageTimerTask extends TimerTask {
private final Actor sender;
private final String message;
MessageTimerTask(Actor sender, String message) {
checkNotNull(sender);
checkNotNull(message);
this.sender = sender;
this.message = message;
}
@Override
public void run() {
sender.printDebug(message);
}
}

View File

@ -24,6 +24,7 @@ package com.sk89q.worldedit.util;
import com.sk89q.util.StringUtil; import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.util.report.Unreported;
import com.sk89q.worldedit.world.registry.LegacyMapper; import com.sk89q.worldedit.world.registry.LegacyMapper;
import com.sk89q.worldedit.world.snapshot.SnapshotRepository; import com.sk89q.worldedit.world.snapshot.SnapshotRepository;
@ -47,10 +48,10 @@ import java.util.logging.Logger;
*/ */
public class PropertiesConfiguration extends LocalConfiguration { public class PropertiesConfiguration extends LocalConfiguration {
private static final Logger log = Logger.getLogger(PropertiesConfiguration.class.getCanonicalName()); @Unreported private static final Logger log = Logger.getLogger(PropertiesConfiguration.class.getCanonicalName());
protected Properties properties; @Unreported protected Properties properties;
protected File path; @Unreported protected File path;
/** /**
* Construct the object. The configuration isn't loaded yet. * Construct the object. The configuration isn't loaded yet.

View File

@ -24,6 +24,7 @@ import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.session.SessionManager;
import com.sk89q.worldedit.util.report.Unreported;
import com.sk89q.worldedit.world.snapshot.SnapshotRepository; import com.sk89q.worldedit.world.snapshot.SnapshotRepository;
import java.io.IOException; import java.io.IOException;
@ -37,8 +38,8 @@ import java.util.logging.Logger;
*/ */
public class YAMLConfiguration extends LocalConfiguration { public class YAMLConfiguration extends LocalConfiguration {
protected final YAMLProcessor config; @Unreported protected final YAMLProcessor config;
protected final Logger logger; @Unreported protected final Logger logger;
public YAMLConfiguration(YAMLProcessor config, Logger logger) { public YAMLConfiguration(YAMLProcessor config, Logger logger) {
this.config = config; this.config = config;

View File

@ -0,0 +1,490 @@
/*
* 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.util.net;
import com.sk89q.worldedit.util.io.Closer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpRequest implements Closeable {
private static final int CONNECT_TIMEOUT = 1000 * 5;
private static final int READ_TIMEOUT = 1000 * 5;
private static final int READ_BUFFER_SIZE = 1024 * 8;
private final Map<String, String> headers = new HashMap<>();
private final String method;
private final URL url;
private String contentType;
private byte[] body;
private HttpURLConnection conn;
private InputStream inputStream;
private long contentLength = -1;
private long readBytes = 0;
/**
* Create a new HTTP request.
*
* @param method the method
* @param url the URL
*/
private HttpRequest(String method, URL url) {
this.method = method;
this.url = url;
}
/**
* Submit data.
*
* @return this object
*/
public HttpRequest body(String data) {
body = data.getBytes();
return this;
}
/**
* Submit form data.
*
* @param form the form
* @return this object
*/
public HttpRequest bodyForm(Form form) {
contentType = "application/x-www-form-urlencoded";
body = form.toString().getBytes();
return this;
}
/**
* Add a header.
*
* @param key the header key
* @param value the header value
* @return this object
*/
public HttpRequest header(String key, String value) {
if (key.equalsIgnoreCase("Content-Type")) {
contentType = value;
} else {
headers.put(key, value);
}
return this;
}
/**
* Execute the request.
* <p/>
* After execution, {@link #close()} should be called.
*
* @return this object
* @throws java.io.IOException on I/O error
*/
public HttpRequest execute() throws IOException {
boolean successful = false;
try {
if (conn != null) {
throw new IllegalArgumentException("Connection already executed");
}
conn = (HttpURLConnection) reformat(url).openConnection();
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Java)");
if (body != null) {
conn.setRequestProperty("Content-Type", contentType);
conn.setRequestProperty("Content-Length", Integer.toString(body.length));
conn.setDoInput(true);
}
for (Map.Entry<String, String> entry : headers.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
conn.setRequestMethod(method);
conn.setUseCaches(false);
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.connect();
if (body != null) {
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.write(body);
out.flush();
out.close();
}
inputStream = conn.getResponseCode() == HttpURLConnection.HTTP_OK ?
conn.getInputStream() : conn.getErrorStream();
successful = true;
} finally {
if (!successful) {
close();
}
}
return this;
}
/**
* Require that the response code is one of the given response codes.
*
* @param codes a list of codes
* @return this object
* @throws java.io.IOException if there is an I/O error or the response code is not expected
*/
public HttpRequest expectResponseCode(int... codes) throws IOException {
int responseCode = getResponseCode();
for (int code : codes) {
if (code == responseCode) {
return this;
}
}
close();
throw new IOException("Did not get expected response code, got " + responseCode + " for " + url);
}
/**
* Get the response code.
*
* @return the response code
* @throws java.io.IOException on I/O error
*/
public int getResponseCode() throws IOException {
if (conn == null) {
throw new IllegalArgumentException("No connection has been made");
}
return conn.getResponseCode();
}
/**
* Get the input stream.
*
* @return the input stream
*/
public InputStream getInputStream() {
return inputStream;
}
/**
* Buffer the returned response.
*
* @return the buffered response
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public BufferedResponse returnContent() throws IOException, InterruptedException {
if (inputStream == null) {
throw new IllegalArgumentException("No input stream available");
}
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b = 0;
while ((b = inputStream.read()) != -1) {
bos.write(b);
}
return new BufferedResponse(bos.toByteArray());
} finally {
close();
}
}
/**
* Save the result to a file.
*
* @param file the file
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public HttpRequest saveContent(File file) throws IOException, InterruptedException {
Closer closer = Closer.create();
try {
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
saveContent(bos);
} finally {
closer.close();
}
return this;
}
/**
* Save the result to an output stream.
*
* @param out the output stream
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public HttpRequest saveContent(OutputStream out) throws IOException, InterruptedException {
BufferedInputStream bis;
try {
String field = conn.getHeaderField("Content-Length");
if (field != null) {
long len = Long.parseLong(field);
if (len >= 0) { // Let's just not deal with really big numbers
contentLength = len;
}
}
} catch (NumberFormatException ignored) {
}
try {
bis = new BufferedInputStream(inputStream);
byte[] data = new byte[READ_BUFFER_SIZE];
int len = 0;
while ((len = bis.read(data, 0, READ_BUFFER_SIZE)) >= 0) {
out.write(data, 0, len);
readBytes += len;
}
} finally {
close();
}
return this;
}
@Override
public void close() throws IOException {
if (conn != null) conn.disconnect();
}
/**
* Perform a GET request.
*
* @param url the URL
* @return a new request object
*/
public static HttpRequest get(URL url) {
return request("GET", url);
}
/**
* Perform a POST request.
*
* @param url the URL
* @return a new request object
*/
public static HttpRequest post(URL url) {
return request("POST", url);
}
/**
* Perform a request.
*
* @param method the method
* @param url the URL
* @return a new request object
*/
public static HttpRequest request(String method, URL url) {
return new HttpRequest(method, url);
}
/**
* Create a new {@link java.net.URL} and throw a {@link RuntimeException} if the URL
* is not valid.
*
* @param url the url
* @return a URL object
* @throws RuntimeException if the URL is invalid
*/
public static URL url(String url) {
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
/**
* URL may contain spaces and other nasties that will cause a failure.
*
* @param existing the existing URL to transform
* @return the new URL, or old one if there was a failure
*/
private static URL reformat(URL existing) {
try {
URL url = new URL(existing.toString());
URI uri = new URI(
url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(),
url.getPath(), url.getQuery(), url.getRef());
url = uri.toURL();
return url;
} catch (MalformedURLException e) {
return existing;
} catch (URISyntaxException e) {
return existing;
}
}
/**
* Used with {@link #bodyForm(Form)}.
*/
public final static class Form {
public final List<String> elements = new ArrayList<>();
private Form() {
}
/**
* Add a key/value to the form.
*
* @param key the key
* @param value the value
* @return this object
*/
public Form add(String key, String value) {
try {
elements.add(URLEncoder.encode(key, "UTF-8") +
"=" + URLEncoder.encode(value, "UTF-8"));
return this;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (String element : elements) {
if (first) {
first = false;
} else {
builder.append("&");
}
builder.append(element);
}
return builder.toString();
}
/**
* Create a new form.
*
* @return a new form
*/
public static Form create() {
return new Form();
}
}
/**
* Used to buffer the response in memory.
*/
public class BufferedResponse {
private final byte[] data;
private BufferedResponse(byte[] data) {
this.data = data;
}
/**
* Return the result as bytes.
*
* @return the data
*/
public byte[] asBytes() {
return data;
}
/**
* Return the result as a string.
*
* @param encoding the encoding
* @return the string
* @throws java.io.IOException on I/O error
*/
public String asString(String encoding) throws IOException {
return new String(data, encoding);
}
/**
* Save the result to a file.
*
* @param file the file
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public BufferedResponse saveContent(File file) throws IOException, InterruptedException {
Closer closer = Closer.create();
file.getParentFile().mkdirs();
try {
FileOutputStream fos = closer.register(new FileOutputStream(file));
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
saveContent(bos);
} finally {
closer.close();
}
return this;
}
/**
* Save the result to an output stream.
*
* @param out the output stream
* @return this object
* @throws java.io.IOException on I/O error
* @throws InterruptedException on interruption
*/
public BufferedResponse saveContent(OutputStream out) throws IOException, InterruptedException {
out.write(data);
return this;
}
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.util.paste;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldedit.command.util.AsyncCommandHelper;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.util.task.Supervisor;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ActorCallbackPaste {
private static final Logger LOGGER = Logger.getLogger(ActorCallbackPaste.class.getSimpleName());
private ActorCallbackPaste() {
}
/**
* Submit data to a pastebin service and inform the sender of
* success or failure.
*
* @param supervisor The supervisor instance
* @param sender The sender
* @param content The content
* @param successMessage The message, formatted with {@link String#format(String, Object...)} on success
*/
public static void pastebin(Supervisor supervisor, final Actor sender, String content, final String successMessage) {
ListenableFuture<URL> future = new EngineHubPaste().paste(content);
AsyncCommandHelper.wrap(future, supervisor, sender)
.registerWithSupervisor("Submitting content to a pastebin service...")
.sendMessageAfterDelay("(Please wait... sending output to pastebin...)");
Futures.addCallback(future, new FutureCallback<URL>() {
@Override
public void onSuccess(URL url) {
sender.print(String.format(successMessage, url));
}
@Override
public void onFailure(Throwable throwable) {
LOGGER.log(Level.WARNING, "Failed to submit pastebin", throwable);
sender.printError("Failed to submit to a pastebin. Please see console for the error.");
}
});
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.util.paste;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldedit.util.net.HttpRequest;
import org.json.simple.JSONValue;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EngineHubPaste implements Paster {
private static final Pattern URL_PATTERN = Pattern.compile("https?://.+$");
@Override
public ListenableFuture<URL> paste(String content) {
return Pasters.getExecutor().submit(new PasteTask(content));
}
private final class PasteTask implements Callable<URL> {
private final String content;
private PasteTask(String content) {
this.content = content;
}
@Override
public URL call() throws IOException, InterruptedException {
HttpRequest.Form form = HttpRequest.Form.create();
form.add("content", content);
form.add("from", "worldguard");
URL url = HttpRequest.url("http://paste.enginehub.org/paste");
String result = HttpRequest.post(url)
.bodyForm(form)
.execute()
.expectResponseCode(200)
.returnContent()
.asString("UTF-8").trim();
Object object = JSONValue.parse(result);
if (object instanceof Map) {
@SuppressWarnings("unchecked")
String urlString = String.valueOf(((Map<Object, Object>) object).get("url"));
Matcher m = URL_PATTERN.matcher(urlString);
if (m.matches()) {
return new URL(urlString);
}
}
throw new IOException("Failed to save paste; instead, got: " + result);
}
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.util.paste;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldedit.util.net.HttpRequest;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Pastebin implements Paster {
private static final Pattern URL_PATTERN = Pattern.compile("https?://pastebin.com/([^/]+)$");
private boolean mungingLinks = true;
public boolean isMungingLinks() {
return mungingLinks;
}
public void setMungingLinks(boolean mungingLinks) {
this.mungingLinks = mungingLinks;
}
@Override
public ListenableFuture<URL> paste(String content) {
if (mungingLinks) {
content = content.replaceAll("http://", "http_//");
}
return Pasters.getExecutor().submit(new PasteTask(content));
}
private final class PasteTask implements Callable<URL> {
private final String content;
private PasteTask(String content) {
this.content = content;
}
@Override
public URL call() throws IOException, InterruptedException {
HttpRequest.Form form = HttpRequest.Form.create();
form.add("api_option", "paste");
form.add("api_dev_key", "4867eae74c6990dbdef07c543cf8f805");
form.add("api_paste_code", content);
form.add("api_paste_private", "0");
form.add("api_paste_name", "");
form.add("api_paste_expire_date", "1W");
form.add("api_paste_format", "text");
form.add("api_user_key", "");
URL url = HttpRequest.url("http://pastebin.com/api/api_post.php");
String result = HttpRequest.post(url)
.bodyForm(form)
.execute()
.expectResponseCode(200)
.returnContent()
.asString("UTF-8").trim();
Matcher m = URL_PATTERN.matcher(result);
if (m.matches()) {
return new URL("http://pastebin.com/raw.php?i=" + m.group(1));
} else if (result.matches("^https?://.+")) {
return new URL(result);
} else {
throw new IOException("Failed to save paste; instead, got: " + result);
}
}
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.util.paste;
import com.google.common.util.concurrent.ListenableFuture;
import java.net.URL;
public interface Paster {
ListenableFuture<URL> paste(String content);
}

View File

@ -0,0 +1,44 @@
/*
* 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.util.paste;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
final class Pasters {
private static final ListeningExecutorService executor =
MoreExecutors.listeningDecorator(
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
2L, TimeUnit.SECONDS,
new SynchronousQueue<>()));
private Pasters() {
}
static ListeningExecutorService getExecutor() {
return executor;
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.util.report;
import com.sk89q.worldedit.WorldEdit;
public class ConfigReport extends DataReport {
public ConfigReport() {
super("WorldEdit Configuration");
append("Configuration", new HierarchyObjectReport("Configuration", WorldEdit.getInstance().getConfiguration()));
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.util.report;
public class HierarchyObjectReport extends ShallowObjectReport {
public HierarchyObjectReport(String title, Object object) {
super(title, object);
Class<?> type = object.getClass();
while ((type = type.getSuperclass()) != null) {
scanClass(object, type);
}
}
}

View File

@ -34,8 +34,10 @@ public class ShallowObjectReport extends DataReport {
super(title); super(title);
checkNotNull(object, "object"); checkNotNull(object, "object");
Class<?> type = object.getClass(); scanClass(object, object.getClass());
}
void scanClass(Object object, Class<?> type) {
for (Field field : type.getDeclaredFields()) { for (Field field : type.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) { if (Modifier.isStatic(field.getModifiers())) {
continue; continue;
@ -54,5 +56,4 @@ public class ShallowObjectReport extends DataReport {
} }
} }
} }
} }

View File

@ -0,0 +1,74 @@
/*
* 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.util.task;
import com.google.common.util.concurrent.AbstractFuture;
import javax.annotation.Nullable;
import java.util.Date;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An abstract task that stores a name and owner.
*
* @param <V> the type returned
*/
public abstract class AbstractTask<V> extends AbstractFuture<V> implements Task<V> {
private final UUID uniqueId = UUID.randomUUID();
private final String name;
private final Object owner;
private final Date creationDate = new Date();
/**
* Create a new instance.
*
* @param name the name
* @param owner the owner
*/
protected AbstractTask(String name, @Nullable Object owner) {
checkNotNull(name);
this.name = name;
this.owner = owner;
}
@Override
public UUID getUniqueId() {
return uniqueId;
}
@Override
public String getName() {
return name;
}
@Nullable
@Override
public Object getOwner() {
return owner;
}
@Override
public Date getCreationDate() {
return creationDate;
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.util.task;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldedit.util.task.progress.Progress;
import javax.annotation.Nullable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A task that wraps a {@code ListenableFuture}.
*
* <p>{@link Task.State#SCHEDULED} is never returned because it is not possible
* to test whether the future has "started," so {@link Task.State#RUNNING} is
* returned in its place.</p>
*
* <p>Use {@link #create(ListenableFuture, String, Object)} to create a new
* instance.</p>
*
* @param <V> the type returned
*/
public class FutureForwardingTask<V> extends AbstractTask<V> {
private final ListenableFuture<V> future;
private FutureForwardingTask(ListenableFuture<V> future, String name, @Nullable Object owner) {
super(name, owner);
checkNotNull(future);
this.future = future;
}
@Override
public void addListener(Runnable listener, Executor executor) {
future.addListener(listener, executor);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return future.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return future.isCancelled();
}
@Override
public boolean isDone() {
return future.isDone();
}
@Override
public V get() throws InterruptedException, ExecutionException {
return future.get();
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return future.get(timeout, unit);
}
@Override
public State getState() {
if (isCancelled()) {
return State.CANCELLED;
} else if (isDone()) {
try {
get();
return State.SUCCEEDED;
} catch (InterruptedException e) {
return State.CANCELLED;
} catch (ExecutionException e) {
return State.FAILED;
}
} else {
return State.RUNNING;
}
}
@Override
public Progress getProgress() {
return Progress.indeterminate();
}
/**
* Create a new instance.
*
* @param future the future
* @param name the name of the task
* @param owner the owner of the task, or {@code null}
* @param <V> the type returned by the future
* @return a new instance
*/
public static <V> FutureForwardingTask<V> create(ListenableFuture<V> future, String name, @Nullable Object owner) {
return new FutureForwardingTask<>(future, name, owner);
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.util.task;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An implementation of a {@code Supervisor}.
*/
public class SimpleSupervisor implements Supervisor {
private final List<Task<?>> monitored = new ArrayList<>();
private final Object lock = new Object();
@Override
public List<Task<?>> getTasks() {
synchronized (lock) {
return new ArrayList<>(monitored);
}
}
@Override
public void monitor(final Task<?> task) {
checkNotNull(task);
synchronized (lock) {
monitored.add(task);
}
task.addListener(() -> {
synchronized (lock) {
monitored.remove(task);
}
}, MoreExecutors.directExecutor());
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.util.task;
import java.util.List;
/**
* Manages running tasks and informs users of their progress, but does not
* execute the task.
*/
public interface Supervisor {
/**
* Get a list of running or queued tasks.
*
* @return a list of tasks
*/
List<Task<?>> getTasks();
/**
* Monitor the given task.
*
* @param task the task
*/
void monitor(Task<?> task);
}

View File

@ -0,0 +1,97 @@
/*
* 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.util.task;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.worldedit.util.task.progress.ProgressObservable;
import javax.annotation.Nullable;
import java.util.Date;
import java.util.UUID;
/**
* A task is a job that can be scheduled, run, or cancelled. Tasks can report
* on their own status. Tasks have owners.
*/
public interface Task<V> extends ListenableFuture<V>, ProgressObservable {
/**
* Get the unique ID of this task.
*
* @return this task's unique ID
*/
UUID getUniqueId();
/**
* Get the name of the task so it can be printed to the user.
*
* @return the name of the task
*/
String getName();
/**
* Get the owner of the task.
*
* @return an owner object, if one is known or valid, otherwise {@code null}
*/
@Nullable
Object getOwner();
/**
* Get the state of the task.
*
* @return the state of the task
*/
State getState();
/**
* Get the time at which the task was created.
*
* @return a date
*/
Date getCreationDate();
/**
* Represents the state of a task.
*/
enum State {
/**
* The task has been scheduled to run but is not running yet.
*/
SCHEDULED,
/**
* The task has been cancelled and may be stopped or will stop.
*/
CANCELLED,
/**
* The task is currently running.
*/
RUNNING,
/**
* The task has failed.
*/
FAILED,
/**
* The task has succeeded.
*/
SUCCEEDED
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.util.task;
import java.util.Comparator;
/**
* Compares task states according to the order of the {@link Task.State}
* enumeration.
*/
public class TaskStateComparator implements Comparator<Task<?>> {
@Override
public int compare(com.sk89q.worldedit.util.task.Task<?> o1, Task<?> o2) {
int ordinal1 = o1.getState().ordinal();
int ordinal2 = o2.getState().ordinal();
if (ordinal1 < ordinal2) {
return -1;
} else if (ordinal1 > ordinal2) {
return 1;
} else {
return 0;
}
}
}

View File

@ -0,0 +1,183 @@
/*
* 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.util.task.progress;
import java.util.Arrays;
import java.util.Collection;
/**
* A progress object describes the progress of an operation, specifying
* either a percentage of completion or a status of indeterminacy.
*
* <p>Progress objects are immutable.</p>
*
* <p>To create a new instance, use one of the static constructors
* on this class.</p>
*/
public abstract class Progress {
/**
* Create a new instance.
*/
private Progress() {
}
/**
* Return whether the current progress is indeterminate.
*
* @return true if indeterminate
*/
public abstract boolean isIndeterminate();
/**
* Get the progress percentage.
*
* <p>If {@link #isIndeterminate()} returns {@code true}, the behavior
* of this method is undefined.</p>
*
* @return a number in the range [0, 1]
*/
public abstract double getProgress();
/**
* Get a static progress object that is indeterminate.
*
* @return a progress object
*/
public static Progress indeterminate() {
return INDETERMINATE;
}
/**
* Get a static progress object that is complete.
*
* @return a progress object
*/
public static Progress completed() {
return COMPLETED;
}
/**
* Create a new progress object with the given percentage.
*
* @param value the percentage, which will be clamped to [0, 1]
* @return a progress object
*/
public static Progress of(double value) {
if (value < 0) {
value = 0;
} else if (value > 1) {
value = 1;
}
final double finalValue = value;
return new Progress() {
@Override
public boolean isIndeterminate() {
return false;
}
@Override
public double getProgress() {
return finalValue;
}
};
}
/**
* Create a new progress object with progress split equally between the
* given progress objects.
*
* @param objects an array of progress objects
* @return a new progress value
*/
public static Progress split(Progress... objects) {
return split(Arrays.asList(objects));
}
/**
* Create a new progress object with progress split equally between the
* given progress objects.
*
* @param progress a collection of progress objects
* @return a new progress value
*/
public static Progress split(Collection<Progress> progress) {
int count = 0;
double total = 0;
for (Progress p : progress) {
if (p.isIndeterminate()) {
return indeterminate();
}
total += p.getProgress();
}
return of(total / count);
}
/**
* Create a new progress object with progress split equally between the
* given {@link ProgressObservable}s.
*
* @param observables an array of observables
* @return a new progress value
*/
public static Progress splitObservables(ProgressObservable... observables) {
return splitObservables(Arrays.asList(observables));
}
/**
* Create a new progress object with progress split equally between the
* given {@link ProgressObservable}s.
*
* @param observables a collection of observables
* @return a new progress value
*/
public static Progress splitObservables(Collection<? extends ProgressObservable> observables) {
int count = 0;
double total = 0;
for (ProgressObservable observable : observables) {
Progress p = observable.getProgress();
if (p.isIndeterminate()) {
return indeterminate();
}
total += p.getProgress();
}
return of(total / count);
}
private static final Progress COMPLETED = of(1);
private static final Progress INDETERMINATE = new Progress() {
@Override
public boolean isIndeterminate() {
return true;
}
@Override
public double getProgress() {
return 0;
}
};
}

View File

@ -0,0 +1,100 @@
/*
* 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.util.task.progress;
import java.util.Iterator;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An iterator that keeps track of how many entries have been visited and
* calculates a "percent completed" using a provided total count.
*
* <p>The returned progress percentage will always be between 0 or 1
* (inclusive). If the iterator returns more entries than the total count,
* then 100% will be returned for the progress.</p>
*
* @param <V> the type
*/
public class ProgressIterator<V> implements Iterator<V>, ProgressObservable {
private final Iterator<V> iterator;
private final int count;
private int visited = 0;
/**
* Create a new instance.
*
* @param iterator the iterator
* @param count the count
*/
private ProgressIterator(Iterator<V> iterator, int count) {
checkNotNull(iterator);
this.iterator = iterator;
this.count = count;
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public V next() {
V value = iterator.next();
visited++;
return value;
}
@Override
public void remove() {
iterator.remove();
}
@Override
public Progress getProgress() {
return Progress.of(count > 0 ? Math.min(1, Math.max(0, (visited / (double) count))) : 1);
}
/**
* Create a new instance.
*
* @param iterator the iterator
* @param count the number of objects
* @param <V> the type
* @return an instance
*/
public static <V> ProgressIterator<V> create(Iterator<V> iterator, int count) {
return new ProgressIterator<V>(iterator, count);
}
/**
* Create a new instance from a list.
*
* @param list a list
* @param <V> the type
* @return an instance
*/
public static <V> ProgressIterator<V> create(List<V> list) {
return create(list.iterator(), list.size());
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.util.task.progress;
/**
* An object that is able to report on its progress.
*/
public interface ProgressObservable {
/**
* Get the current percentage of completion.
*
* @return a progress object
*/
Progress getProgress();
}

View File

@ -23,6 +23,7 @@ import com.google.common.reflect.TypeToken;
import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.session.SessionManager;
import com.sk89q.worldedit.util.report.Unreported;
import com.sk89q.worldedit.world.registry.LegacyMapper; import com.sk89q.worldedit.world.registry.LegacyMapper;
import com.sk89q.worldedit.world.snapshot.SnapshotRepository; import com.sk89q.worldedit.world.snapshot.SnapshotRepository;
import ninja.leaping.configurate.ConfigurationOptions; import ninja.leaping.configurate.ConfigurationOptions;
@ -36,10 +37,10 @@ import java.util.HashSet;
public class ConfigurateConfiguration extends LocalConfiguration { public class ConfigurateConfiguration extends LocalConfiguration {
protected final ConfigurationLoader<CommentedConfigurationNode> config; @Unreported protected final ConfigurationLoader<CommentedConfigurationNode> config;
protected final Logger logger; @Unreported protected final Logger logger;
protected CommentedConfigurationNode node; @Unreported protected CommentedConfigurationNode node;
public ConfigurateConfiguration(ConfigurationLoader<CommentedConfigurationNode> config, Logger logger) { public ConfigurateConfiguration(ConfigurationLoader<CommentedConfigurationNode> config, Logger logger) {
this.config = config; this.config = config;