mirror of
https://github.com/EngineHub/WorldEdit.git
synced 2025-03-13 13:57:48 +08:00
Schematic Share system (#1591)
* Very WIP in-game schematic sharing system * Add support for paste meta, and send that data when possible * Add ability to specify the name of the shared schematic
This commit is contained in:
parent
df71f3ae7d
commit
303f5a76b2
@ -54,6 +54,8 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
import com.sk89q.worldedit.util.io.Closer;
|
||||
import com.sk89q.worldedit.util.io.file.FilenameException;
|
||||
import com.sk89q.worldedit.util.io.file.MorePaths;
|
||||
import com.sk89q.worldedit.util.paste.EngineHubPaste;
|
||||
import com.sk89q.worldedit.util.paste.PasteMetadata;
|
||||
import org.enginehub.piston.annotation.Command;
|
||||
import org.enginehub.piston.annotation.CommandContainer;
|
||||
import org.enginehub.piston.annotation.param.Arg;
|
||||
@ -66,14 +68,19 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -146,7 +153,7 @@ public class SchematicCommands {
|
||||
|
||||
@Command(
|
||||
name = "save",
|
||||
desc = "Save a schematic into your clipboard"
|
||||
desc = "Save your clipboard into a schematic file"
|
||||
)
|
||||
@CommandPermissions({ "worldedit.clipboard.save", "worldedit.schematic.save" })
|
||||
public void save(Actor actor, LocalSession session,
|
||||
@ -205,6 +212,39 @@ public class SchematicCommands {
|
||||
.buildAndExec(worldEdit.getExecutorService());
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "share",
|
||||
desc = "Share your clipboard as a schematic online"
|
||||
)
|
||||
@CommandPermissions({ "worldedit.clipboard.share", "worldedit.schematic.share" })
|
||||
public void share(Actor actor, LocalSession session,
|
||||
@Arg(desc = "Schematic name. Defaults to name-millis", def = "")
|
||||
String schematicName,
|
||||
@Arg(desc = "Format name.", def = "sponge")
|
||||
String formatName) throws WorldEditException {
|
||||
if (worldEdit.getPlatformManager().queryCapability(Capability.GAME_HOOKS).getDataVersion() == -1) {
|
||||
actor.printError(TranslatableComponent.of("worldedit.schematic.unsupported-minecraft-version"));
|
||||
return;
|
||||
}
|
||||
|
||||
ClipboardFormat format = ClipboardFormats.findByAlias(formatName);
|
||||
if (format == null) {
|
||||
actor.printError(TranslatableComponent.of("worldedit.schematic.unknown-format", TextComponent.of(formatName)));
|
||||
return;
|
||||
}
|
||||
|
||||
ClipboardHolder holder = session.getClipboard();
|
||||
|
||||
SchematicShareTask task = new SchematicShareTask(actor, format, holder, schematicName);
|
||||
AsyncCommandBuilder.wrap(task, actor)
|
||||
.registerWithSupervisor(worldEdit.getSupervisor(), "Sharing schematic")
|
||||
.setDelayMessage(TranslatableComponent.of("worldedit.schematic.save.saving"))
|
||||
.setWorkingMessage(TranslatableComponent.of("worldedit.schematic.save.still-saving"))
|
||||
.onSuccess("Shared", (url -> actor.printInfo(TextComponent.of(url.toExternalForm() + ".schem").clickEvent(ClickEvent.openUrl(url.toExternalForm() + ".schem")))))
|
||||
.onFailure("Failed to share schematic", worldEdit.getPlatformManager().getPlatformCommandManager().getExceptionConverter())
|
||||
.buildAndExec(worldEdit.getExecutorService());
|
||||
}
|
||||
|
||||
@Command(
|
||||
name = "delete",
|
||||
aliases = {"d"},
|
||||
@ -326,23 +366,18 @@ public class SchematicCommands {
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicSaveTask implements Callable<Void> {
|
||||
private final Actor actor;
|
||||
private final File file;
|
||||
private abstract static class SchematicOutputTask<T> implements Callable<T> {
|
||||
protected final Actor actor;
|
||||
private final ClipboardFormat format;
|
||||
private final ClipboardHolder holder;
|
||||
private final boolean overwrite;
|
||||
|
||||
SchematicSaveTask(Actor actor, File file, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) {
|
||||
SchematicOutputTask(Actor actor, ClipboardFormat format, ClipboardHolder holder) {
|
||||
this.actor = actor;
|
||||
this.file = file;
|
||||
this.format = format;
|
||||
this.holder = holder;
|
||||
this.overwrite = overwrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
protected void writeToOutputStream(OutputStream outputStream) throws Exception {
|
||||
Clipboard clipboard = holder.getClipboard();
|
||||
Transform transform = holder.getTransform();
|
||||
Clipboard target;
|
||||
@ -358,11 +393,28 @@ public class SchematicCommands {
|
||||
}
|
||||
|
||||
try (Closer closer = Closer.create()) {
|
||||
FileOutputStream fos = closer.register(new FileOutputStream(file));
|
||||
BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
|
||||
OutputStream stream = closer.register(outputStream);
|
||||
BufferedOutputStream bos = closer.register(new BufferedOutputStream(stream));
|
||||
ClipboardWriter writer = closer.register(format.getWriter(bos));
|
||||
writer.write(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicSaveTask extends SchematicOutputTask<Void> {
|
||||
private final File file;
|
||||
private final boolean overwrite;
|
||||
|
||||
SchematicSaveTask(Actor actor, File file, ClipboardFormat format, ClipboardHolder holder, boolean overwrite) {
|
||||
super(actor, format, holder);
|
||||
this.file = file;
|
||||
this.overwrite = overwrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
try {
|
||||
writeToOutputStream(new FileOutputStream(file));
|
||||
log.info(actor.getName() + " saved " + file.getCanonicalPath() + (overwrite ? " (overwriting previous file)" : ""));
|
||||
} catch (IOException e) {
|
||||
file.delete();
|
||||
@ -372,6 +424,32 @@ public class SchematicCommands {
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicShareTask extends SchematicOutputTask<URL> {
|
||||
private final String name;
|
||||
|
||||
SchematicShareTask(Actor actor, ClipboardFormat format, ClipboardHolder holder, String name) {
|
||||
super(actor, format, holder);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL call() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
writeToOutputStream(baos);
|
||||
} catch (Exception e) {
|
||||
throw new CommandException(TextComponent.of(e.getMessage()), e, ImmutableList.of());
|
||||
}
|
||||
|
||||
EngineHubPaste pasteService = new EngineHubPaste();
|
||||
PasteMetadata metadata = new PasteMetadata();
|
||||
metadata.author = this.actor.getName();
|
||||
metadata.extension = "schem";
|
||||
metadata.name = name == null ? actor.getName() + "-" + System.currentTimeMillis() : name;
|
||||
return pasteService.paste(new String(Base64.getEncoder().encode(baos.toByteArray()), StandardCharsets.UTF_8), metadata).call();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SchematicListTask implements Callable<Component> {
|
||||
private final Comparator<Path> pathComparator;
|
||||
private final int page;
|
||||
|
@ -40,6 +40,7 @@ import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
|
||||
import com.sk89q.worldedit.util.paste.ActorCallbackPaste;
|
||||
import com.sk89q.worldedit.util.paste.PasteMetadata;
|
||||
import com.sk89q.worldedit.util.report.ConfigReport;
|
||||
import com.sk89q.worldedit.util.report.ReportList;
|
||||
import com.sk89q.worldedit.util.report.SystemInfoReport;
|
||||
@ -137,7 +138,10 @@ public class WorldEditCommands {
|
||||
|
||||
if (pastebin) {
|
||||
actor.checkPermission("worldedit.report.pastebin");
|
||||
ActorCallbackPaste.pastebin(we.getSupervisor(), actor, result, TranslatableComponent.builder("worldedit.report.callback"));
|
||||
PasteMetadata metadata = new PasteMetadata();
|
||||
metadata.author = actor.getName();
|
||||
metadata.extension = "report";
|
||||
ActorCallbackPaste.pastebin(we.getSupervisor(), actor, result, metadata, TranslatableComponent.builder("worldedit.report.callback"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,4 +77,26 @@ public final class ActorCallbackPaste {
|
||||
.buildAndExec(Pasters.getExecutor());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 pasteMetadata The paste metadata
|
||||
* @param successMessage The message builder, given the URL as an arg
|
||||
*/
|
||||
public static void pastebin(Supervisor supervisor, final Actor sender, String content, PasteMetadata pasteMetadata, final TranslatableComponent.Builder successMessage) {
|
||||
Callable<URL> task = paster.paste(content, pasteMetadata);
|
||||
|
||||
AsyncCommandBuilder.wrap(task, sender)
|
||||
.registerWithSupervisor(supervisor, "Submitting content to a pastebin service.")
|
||||
.setDelayMessage(TranslatableComponent.of("worldedit.pastebin.uploading"))
|
||||
.onSuccess((String) null, url -> sender.printInfo(successMessage.args(TextComponent.of(url.toString())).build()))
|
||||
.onFailure("Failed to submit paste", null)
|
||||
.buildAndExec(Pasters.getExecutor());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,22 +33,37 @@ public class EngineHubPaste implements Paster {
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
@Override
|
||||
public Callable<URL> paste(String content) {
|
||||
return new PasteTask(content);
|
||||
public Callable<URL> paste(String content, PasteMetadata metadata) {
|
||||
return new PasteTask(content, metadata);
|
||||
}
|
||||
|
||||
private static final class PasteTask implements Callable<URL> {
|
||||
private final String content;
|
||||
private final PasteMetadata metadata;
|
||||
|
||||
private PasteTask(String content) {
|
||||
private PasteTask(String content, PasteMetadata metadata) {
|
||||
this.content = content;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL call() throws IOException, InterruptedException {
|
||||
URL initialUrl = HttpRequest.url("https://paste.enginehub.org/signed_paste");
|
||||
|
||||
SignedPasteResponse response = GSON.fromJson(HttpRequest.get(initialUrl)
|
||||
HttpRequest requestBuilder = HttpRequest.get(initialUrl);
|
||||
|
||||
requestBuilder.header("x-paste-meta-from", "EngineHub");
|
||||
if (metadata.name != null) {
|
||||
requestBuilder.header("x-paste-meta-name", metadata.name);
|
||||
}
|
||||
if (metadata.author != null) {
|
||||
requestBuilder.header("x-paste-meta-author", metadata.author);
|
||||
}
|
||||
if (metadata.extension != null) {
|
||||
requestBuilder.header("x-paste-meta-extension", metadata.extension);
|
||||
}
|
||||
|
||||
SignedPasteResponse response = GSON.fromJson(requestBuilder
|
||||
.execute()
|
||||
.expectResponseCode(200)
|
||||
.returnContent()
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.util.paste;
|
||||
|
||||
public class PasteMetadata {
|
||||
public String name;
|
||||
public String extension;
|
||||
public String author;
|
||||
}
|
@ -24,6 +24,10 @@ import java.util.concurrent.Callable;
|
||||
|
||||
public interface Paster {
|
||||
|
||||
Callable<URL> paste(String content);
|
||||
default Callable<URL> paste(String content) {
|
||||
return paste(content, new PasteMetadata());
|
||||
}
|
||||
|
||||
Callable<URL> paste(String content, PasteMetadata metadata);
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user