Integrate WE Schematic Share system (#2619)

* Integrate WE Schematic Share system

(cherry picked from commit 303f5a76b2df70d63480f2126c9ef4b228eb3c59)

* disable feature for now

---------

Co-authored-by: Madeline Miller <mnmiller1@me.com>
This commit is contained in:
Hannes Greule 2024-05-12 10:41:48 +02:00 committed by GitHub
parent 991d93d8ce
commit 9bc09c6a4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 181 additions and 13 deletions

View File

@ -26,6 +26,7 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStructure;
import com.fastasyncworldedit.core.util.MainUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
@ -60,15 +61,19 @@ 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 org.apache.logging.log4j.Logger;
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;
import org.enginehub.piston.annotation.param.ArgFlag;
import org.enginehub.piston.annotation.param.Switch;
import org.enginehub.piston.exception.CommandException;
import org.enginehub.piston.exception.StopExecutionException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -79,9 +84,12 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -536,6 +544,42 @@ 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 (true) {
throw new UnsupportedOperationException("This feature is currently not implemented");
}
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 = "formats",
aliases = {"listformats", "f"},
@ -804,14 +848,48 @@ public class SchematicCommands {
}
private static class SchematicSaveTask implements Callable<Void> {
private abstract static class SchematicOutputTask<T> implements Callable<T> {
protected final Actor actor;
protected final ClipboardFormat format;
protected final ClipboardHolder holder;
private final Actor actor;
private final ClipboardFormat format;
private final ClipboardHolder holder;
SchematicOutputTask(
Actor actor,
ClipboardFormat format,
ClipboardHolder holder
) {
this.actor = actor;
this.format = format;
this.holder = holder;
}
protected void writeToOutputStream(OutputStream outputStream) 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()) {
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 File file;
private final boolean overwrite;
private final File rootDir;
private File file;
SchematicSaveTask(
Actor actor,
@ -821,11 +899,9 @@ public class SchematicCommands {
ClipboardHolder holder,
boolean overwrite
) {
this.actor = actor;
super(actor, format, holder);
this.file = file;
this.rootDir = rootDir;
this.format = format;
this.holder = holder;
this.overwrite = overwrite;
}
@ -984,7 +1060,32 @@ public class SchematicCommands {
//FAWE end
return null;
}
}
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> {

View File

@ -83,4 +83,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());
}
}

View File

@ -33,23 +33,38 @@ 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()

View File

@ -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;
}

View File

@ -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);
}