Merge pull request 'Fix Schematics Readers' (#72) from Schematics/1.21 into main

Reviewed-on: https://steamwar.de/devlabs/SteamWar/SteamWar/pulls/72
Reviewed-by: Lixfel <lixfel@steamwar.de>
This commit is contained in:
Lixfel
2025-01-01 12:42:01 +01:00
19 changed files with 205 additions and 132 deletions
@@ -28,12 +28,15 @@ import java.sql.PreparedStatement;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@AllArgsConstructor @AllArgsConstructor
@Getter
public class NodeData { public class NodeData {
static { static {
new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
SqlTypeMapper.ordinalEnumMapper(SchematicFormat.class);
} }
private static final Table<NodeData> table = new Table<>(NodeData.class); private static final Table<NodeData> table = new Table<>(NodeData.class);
@@ -48,19 +51,18 @@ public class NodeData {
throw new IllegalArgumentException("Node is a directory"); throw new IllegalArgumentException("Node is a directory");
return get.select(rs -> { return get.select(rs -> {
if(rs.next()) { if(rs.next()) {
return new NodeData(node.getId(), rs.getBoolean("NodeFormat")); return new NodeData(node.getId(), SchematicFormat.values()[rs.getInt("NodeFormat")]);
} else { } else {
return new NodeData(node.getId(), false); return new NodeData(node.getId(), SchematicFormat.MCEDIT);
} }
}, node); }, node);
} }
@Getter
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int nodeId; private final int nodeId;
@Field @Field
private boolean nodeFormat; private SchematicFormat nodeFormat;
public InputStream schemData() throws IOException { public InputStream schemData() throws IOException {
try { try {
@@ -81,12 +83,18 @@ public class NodeData {
} }
} }
public void saveFromStream(InputStream blob, boolean newFormat) { public void saveFromStream(InputStream blob, SchematicFormat newFormat) {
updateDatabase.update(nodeId, newFormat, blob); updateDatabase.update(nodeId, newFormat, blob);
nodeFormat = newFormat; nodeFormat = newFormat;
} }
public boolean getNodeFormat() { @AllArgsConstructor
return nodeFormat; @Getter
public enum SchematicFormat {
MCEDIT(".schematic"),
SPONGE_V2(".schem"),
SPONGE_V3(".schem");
private final String fileEnding;
} }
} }
@@ -375,11 +375,10 @@ public class SchematicNode {
return nodeType == null; return nodeType == null;
} }
@Deprecated public String getFileEnding() {
public boolean getSchemFormat() {
if(isDir()) if(isDir())
throw new SecurityException("Node is Directory"); throw new SecurityException("Node is Directory");
return NodeData.get(this).getNodeFormat(); return NodeData.get(this).getNodeFormat().getFileEnding();
} }
public int getRank() { public int getRank() {
@@ -42,6 +42,7 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.sql.NodeData;
import de.steamwar.sql.SchematicData; import de.steamwar.sql.SchematicData;
import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicNode;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
@@ -144,6 +145,6 @@ public class WorldeditWrapper14 implements WorldeditWrapper {
throw new SecurityException(e); throw new SecurityException(e);
} }
new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), true); new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), NodeData.SchematicFormat.SPONGE_V2);
} }
} }
@@ -39,6 +39,7 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.sql.NodeData;
import de.steamwar.sql.SchematicData; import de.steamwar.sql.SchematicData;
import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicNode;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
@@ -142,6 +143,6 @@ public class WorldeditWrapper8 implements WorldeditWrapper {
throw new SecurityException(e); throw new SecurityException(e);
} }
new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), false); new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), NodeData.SchematicFormat.MCEDIT);
} }
} }
@@ -22,6 +22,7 @@ package de.steamwar.fightsystem.record;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import de.steamwar.core.TrickyTrialsWrapper; import de.steamwar.core.TrickyTrialsWrapper;
import de.steamwar.core.WorldEditWrapper;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RPlayer; import de.steamwar.entity.RPlayer;
@@ -523,7 +524,7 @@ public class PacketProcessor implements Listener {
public void close() { public void close() {
// FAWE 1.12 calls close... // FAWE 1.12 calls close...
} }
}, Core.getVersion() > 12); }, WorldEditWrapper.impl.getNativeFormat());
execSync(() -> team.pasteSchem(schemId, clipboard)); execSync(() -> team.pasteSchem(schemId, clipboard));
} }
@@ -245,7 +245,7 @@ public class SchematicCommandUtils {
SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_ELO", player, node.getElo(Season.getSeason())); SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_ELO", player, node.getElo(Season.getSeason()));
} }
SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_FORMAT", player, node.getSchemFormat() ? ".schem" : ".schematic"); SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_FORMAT", player, node.getFileEnding());
CheckedSchematic.getLastDeclinedOfNode(node.getId()).stream().findFirst().ifPresent(checkedSchematic -> SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_STATUS", player, checkedSchematic.getEndTime(), checkedSchematic.getDeclineReason())); CheckedSchematic.getLastDeclinedOfNode(node.getId()).stream().findFirst().ifPresent(checkedSchematic -> SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_STATUS", player, checkedSchematic.getEndTime(), checkedSchematic.getDeclineReason()));
} else { } else {
SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_TYPE", player, SchematicSystem.MESSAGE.parse("UTIL_INFO_TYPE_DIR", player)); SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_TYPE", player, SchematicSystem.MESSAGE.parse("UTIL_INFO_TYPE_DIR", player));
@@ -22,7 +22,6 @@ package de.steamwar.core;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.sk89q.jnbt.*; import com.sk89q.jnbt.*;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
@@ -45,36 +44,29 @@ import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.registry.LegacyMapper; import com.sk89q.worldedit.world.registry.LegacyMapper;
import de.steamwar.sql.NoClipboardException; import de.steamwar.sql.NoClipboardException;
import de.steamwar.sql.NodeData;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
public class WorldEditWrapper14 implements WorldEditWrapper { public class WorldEditWrapper14 implements WorldEditWrapper {
private static final ClipboardFormat SCHEMATIC = BuiltInClipboardFormat.MCEDIT_SCHEMATIC;
private static final ClipboardFormat SCHEM = BuiltInClipboardFormat.SPONGE_SCHEMATIC;
@Override @Override
public InputStream getPlayerClipboard(Player player, boolean schemFormat) { public InputStream getPlayerClipboard(Player player) {
return WorldEditWrapper.getPlayerClipboard(player, schemFormat, (outputStream, clipboard, clipboardHolder) -> { return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) -> {
if(schemFormat){ ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(outputStream);
ClipboardWriter writer = SCHEM.getWriter(outputStream); writer.write(clipboard);
writer.write(clipboard); writer.close();
writer.close();
}else{
SCHEMATIC.getWriter(outputStream).write(clipboard);
}
}); });
} }
@Override @Override
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
Clipboard clipboard = null; Clipboard clipboard = null;
try { try {
clipboard = getClipboard(is, schemFormat); clipboard = getClipboard(is, schemFormat);
@@ -90,12 +82,17 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
} }
@Override @Override
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
try { try {
if(schemFormat){
return new SpongeSchematicReader(new NBTInputStream(is)).read(); switch (schemFormat) {
}else{ case SPONGE_V2:
return new MCEditSchematicReader(new NBTInputStream(is)).read(); case SPONGE_V3:
return new SpongeSchematicReader(new NBTInputStream(is)).read();
case MCEDIT:
return new MCEditSchematicReader(new NBTInputStream(is)).read();
default:
throw new IOException("This schematic format is currently not supported");
} }
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new NoClipboardException(); throw new NoClipboardException();
@@ -124,6 +121,11 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
return new org.bukkit.util.Vector(v.getX(), v.getY(), v.getZ()); return new org.bukkit.util.Vector(v.getX(), v.getY(), v.getZ());
} }
@Override
public NodeData.SchematicFormat getNativeFormat() {
return NodeData.SchematicFormat.SPONGE_V2;
}
private static class MCEditSchematicReader extends NBTSchematicReader { private static class MCEditSchematicReader extends NBTSchematicReader {
private final NBTInputStream inputStream; private final NBTInputStream inputStream;
@@ -387,7 +389,7 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
inputStream.close(); inputStream.close();
} }
} }
private static class SpongeSchematicReader extends NBTSchematicReader { public static class SpongeSchematicReader extends NBTSchematicReader {
private final NBTInputStream inputStream; private final NBTInputStream inputStream;
private DataFixer fixer = null; private DataFixer fixer = null;
@@ -418,7 +420,7 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1- dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1-
fixer = platform.getDataFixer(); fixer = platform.getDataFixer();
return readVersion1(schematicTag); return readVersion1(schematicTag);
} else if (schematicVersion == 2) { } else if (schematicVersion == 2 || schematicVersion == 3) {
dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue();
if (dataVersion < liveDataVersion) { if (dataVersion < liveDataVersion) {
fixer = platform.getDataFixer(); fixer = platform.getDataFixer();
@@ -447,14 +449,18 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
private CompoundTag getBaseTag() throws IOException { private CompoundTag getBaseTag() throws IOException {
NamedTag rootTag = inputStream.readNamedTag(); NamedTag rootTag = inputStream.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
throw new IOException("Tag 'Schematic' does not exist or is not first");
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check // Check
Map<String, Tag> schematic = schematicTag.getValue(); Map<String, Tag> schematic = schematicTag.getValue();
if (schematic.size() == 1) {
schematicTag = requireTag(schematic, "Schematic", CompoundTag.class);
schematic = schematicTag.getValue();
} else if (!rootTag.getName().equals("Schematic")) {
throw new IOException("Tag 'Schematic' does not exist or is not first");
}
schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue(); schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue();
return schematicTag; return schematicTag;
} }
@@ -499,12 +505,16 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE)); region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE));
} }
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class); Map<String, Tag> blockContainer = null;
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue(); boolean v3Mode = false;
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) {
throw new IOException("Block palette size does not match expected size."); if (schematicVersion == 3) {
blockContainer = requireTag(schematic, "Blocks", CompoundTag.class).getValue();
v3Mode = true;
} }
Map<String, Tag> paletteObject = requireTag(v3Mode ? blockContainer: schematic, "Palette", CompoundTag.class).getValue();
Map<Integer, BlockState> palette = new HashMap<>(); Map<Integer, BlockState> palette = new HashMap<>();
ParserContext parserContext = new ParserContext(); ParserContext parserContext = new ParserContext();
@@ -526,12 +536,12 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
palette.put(id, state); palette.put(id, state);
} }
byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue(); byte[] blocks = requireTag(v3Mode ? blockContainer: schematic, v3Mode ? "Data" : "BlockData", ByteArrayTag.class).getValue();
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>(); Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class); ListTag tileEntities = getTag(v3Mode ? blockContainer: schematic, "BlockEntities", ListTag.class);
if (tileEntities == null) { if (tileEntities == null) {
tileEntities = getTag(schematic, "TileEntities", ListTag.class); tileEntities = getTag(v3Mode ? blockContainer: schematic, "TileEntities", ListTag.class);
} }
if (tileEntities != null) { if (tileEntities != null) {
List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream() List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream()
@@ -23,6 +23,7 @@ import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*; import com.sk89q.worldedit.extent.clipboard.io.*;
import de.steamwar.sql.NoClipboardException; import de.steamwar.sql.NoClipboardException;
import de.steamwar.sql.NodeData;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -31,11 +32,19 @@ public class WorldEditWrapper18 extends WorldEditWrapper14 {
@Override @Override
@SuppressWarnings("removal") @SuppressWarnings("removal")
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
NBTInputStream nbtStream = new NBTInputStream(is); NBTInputStream nbtStream = new NBTInputStream(is);
//Use FAWE reader due to FAWE capability of reading corrupt FAWE schems //Use FAWE reader due to FAWE capability of reading corrupt FAWE schems
try { try {
return (schemFormat ? new SpongeSchematicReader(nbtStream) : new MCEditSchematicReader(nbtStream)).read(); switch (schemFormat) {
case MCEDIT:
return new MCEditSchematicReader(nbtStream).read();
case SPONGE_V2:
case SPONGE_V3:
return new WorldEditWrapper14.SpongeSchematicReader(nbtStream).read();
default:
throw new IllegalArgumentException("Unsupported schematic format");
}
} catch (NullPointerException e) { } catch (NullPointerException e) {
throw new NoClipboardException(); throw new NoClipboardException();
} }
+8 -12
View File
@@ -21,7 +21,13 @@ plugins {
steamwar.java steamwar.java
} }
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
dependencies { dependencies {
compileOnly(project(":CommonCore", "default"))
compileOnly(project(":SpigotCore:SpigotCore_Main", "default")) compileOnly(project(":SpigotCore:SpigotCore_Main", "default"))
compileOnly(project(":SpigotCore:SpigotCore_18", "default")) compileOnly(project(":SpigotCore:SpigotCore_18", "default"))
compileOnly(project(":SpigotCore:SpigotCore_14", "default")) compileOnly(project(":SpigotCore:SpigotCore_14", "default"))
@@ -29,16 +35,6 @@ dependencies {
compileOnly(libs.fawe21) compileOnly(libs.fawe21)
compileOnly(libs.paperapi21) { compileOnly(libs.paperapi21)
attributes { compileOnly(libs.nms21)
// Very Hacky, but it works
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
compileOnly(libs.nms21) {
attributes {
// Very Hacky, but it works
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
} }
@@ -20,33 +20,30 @@
package de.steamwar.core; package de.steamwar.core;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2; import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV3;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader; import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV1Reader; import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.sql.NodeData;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.enginehub.linbus.stream.LinBinaryIO; import org.enginehub.linbus.stream.LinBinaryIO;
import org.enginehub.linbus.stream.LinStream;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinRootEntry;
import org.enginehub.linbus.tree.LinTagType;
import java.io.*; import java.io.*;
public class WorldEditWrapper21 implements WorldEditWrapper { public class WorldEditWrapper21 implements WorldEditWrapper {
@Override @Override
public InputStream getPlayerClipboard(Player player, boolean schemFormat) { public InputStream getPlayerClipboard(Player player) {
return WorldEditWrapper.getPlayerClipboard(player, schemFormat, (outputStream, clipboard, clipboardHolder) -> { return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) -> {
ClipboardWriter writer = BuiltInClipboardFormat.FAST_V3.getWriter(outputStream); ClipboardWriter writer = BuiltInClipboardFormat.FAST_V3.getWriter(outputStream);
writer.write(clipboard); writer.write(clipboard);
writer.close(); writer.close();
@@ -54,7 +51,7 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
} }
@Override @Override
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
Clipboard clipboard = null; Clipboard clipboard = null;
try { try {
clipboard = getClipboard(is, schemFormat); clipboard = getClipboard(is, schemFormat);
@@ -70,35 +67,12 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
} }
@Override @Override
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
if (!schemFormat) { return switch (schemFormat) {
return new MCEditSchematicReader(new NBTInputStream(is)).read(); case MCEDIT -> new MCEditSchematicReader(new NBTInputStream(is)).read();
} else { case SPONGE_V2 -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(is))).read();
BufferedInputStream bis = new BufferedInputStream(is); case SPONGE_V3 -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(is))).read();
};
bis.mark(Integer.MAX_VALUE);
LinStream linStream = LinBinaryIO.read(new DataInputStream(bis));
LinCompoundTag entry = LinRootEntry.readFrom(linStream).value();
if (entry.value().size() == 1) {
entry = entry.getTag("Schematic", LinTagType.compoundTag());
}
bis.reset();
switch (entry.getTag("Version", LinTagType.intTag()).valueAsInt()) {
case 1:
return new SpongeSchematicV1Reader(entry.linStream()).read();
case 2:
return new FastSchematicReaderV2(new NBTInputStream(bis)).read();
case 3:
return new FastSchematicReaderV3(bis).read();
default:
throw new IOException("Unknown schematic version");
}
}
} }
@Override @Override
@@ -122,4 +96,9 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
v = transform.apply(v); v = transform.apply(v);
return new org.bukkit.util.Vector(v.x(), v.y(), v.z()); return new org.bukkit.util.Vector(v.x(), v.y(), v.z());
} }
@Override
public NodeData.SchematicFormat getNativeFormat() {
return NodeData.SchematicFormat.SPONGE_V3;
}
} }
@@ -37,6 +37,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData; import com.sk89q.worldedit.world.registry.WorldData;
import de.steamwar.sql.NodeData;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.io.IOException; import java.io.IOException;
@@ -50,13 +51,13 @@ import java.util.stream.Collectors;
public class WorldEditWrapper8 implements WorldEditWrapper { public class WorldEditWrapper8 implements WorldEditWrapper {
@Override @Override
public InputStream getPlayerClipboard(Player player, boolean schemFormat) { public InputStream getPlayerClipboard(Player player) {
return WorldEditWrapper.getPlayerClipboard(player, schemFormat, (outputStream, clipboard, clipboardHolder) -> return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) ->
ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData())); ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData()));
} }
@Override @Override
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) { public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
WorldData world = new BukkitWorld(player.getWorld()).getWorldData(); WorldData world = new BukkitWorld(player.getWorld()).getWorldData();
Clipboard clipboard; Clipboard clipboard;
try { try {
@@ -70,11 +71,16 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
} }
@Override @Override
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException { public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
if(schemFormat) switch (schemFormat) {
return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData()); case MCEDIT:
else return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData()); case SPONGE_V2:
case SPONGE_V3:
return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
default:
throw new IllegalArgumentException("Unsupported schematic format");
}
} }
@Override @Override
@@ -99,6 +105,11 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
return new org.bukkit.util.Vector(v.getX(), v.getY(), v.getZ()); return new org.bukkit.util.Vector(v.getX(), v.getY(), v.getZ());
} }
@Override
public NodeData.SchematicFormat getNativeFormat() {
return NodeData.SchematicFormat.MCEDIT;
}
private static class SpongeSchematicReader implements ClipboardReader { private static class SpongeSchematicReader implements ClipboardReader {
private final NBTInputStream inputStream; private final NBTInputStream inputStream;
@@ -137,6 +148,13 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
IDConverter8 ids = new IDConverter8(); IDConverter8 ids = new IDConverter8();
Map<String, Tag> schematic = schematicTag.getValue(); Map<String, Tag> schematic = schematicTag.getValue();
boolean v3Mode = false;
if (schematic.size() == 1) {
schematic = (requireTag(schematic, "Schematic", CompoundTag.class)).getValue();
v3Mode = true;
}
int width = (requireTag(schematic, "Width", ShortTag.class)).getValue(); int width = (requireTag(schematic, "Width", ShortTag.class)).getValue();
int height = (requireTag(schematic, "Height", ShortTag.class)).getValue(); int height = (requireTag(schematic, "Height", ShortTag.class)).getValue();
int length = (requireTag(schematic, "Length", ShortTag.class)).getValue(); int length = (requireTag(schematic, "Length", ShortTag.class)).getValue();
@@ -167,10 +185,13 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE)); region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE));
} }
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class); Map<String, Tag> blockContainer = null;
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue();
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) if (v3Mode) {
throw new IOException("Block palette size does not match expected size."); blockContainer = getTag(schematic, "Blocks", CompoundTag.class).getValue();
}
Map<String, Tag> paletteObject = requireTag(v3Mode ? blockContainer : schematic, "Palette", CompoundTag.class).getValue();
Map<Integer, BaseBlock> palette = new HashMap<>(); Map<Integer, BaseBlock> palette = new HashMap<>();
ParserContext parserContext = new ParserContext(); ParserContext parserContext = new ParserContext();
@@ -182,11 +203,11 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
palette.put(requireTag(paletteObject, palettePart, IntTag.class).getValue(), new BaseBlock(blockID.getBlockId(), blockID.getDataId())); palette.put(requireTag(paletteObject, palettePart, IntTag.class).getValue(), new BaseBlock(blockID.getBlockId(), blockID.getDataId()));
} }
byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue(); byte[] blocks = requireTag(v3Mode ? blockContainer : schematic, v3Mode ? "Data" : "BlockData", ByteArrayTag.class).getValue();
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<>(); Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class); ListTag tileEntities = getTag(v3Mode ? blockContainer : schematic, "BlockEntities", ListTag.class);
if (tileEntities == null) { if (tileEntities == null) {
tileEntities = getTag(schematic, "TileEntities", ListTag.class); tileEntities = getTag(v3Mode ? blockContainer : schematic, "TileEntities", ListTag.class);
} }
if (tileEntities != null) { if (tileEntities != null) {
@@ -26,6 +26,7 @@ import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.sql.NoClipboardException; import de.steamwar.sql.NoClipboardException;
import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import de.steamwar.sql.NodeData;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
@@ -36,20 +37,22 @@ import java.util.logging.Level;
public interface WorldEditWrapper { public interface WorldEditWrapper {
WorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance()); WorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
InputStream getPlayerClipboard(Player player, boolean schemFormat); InputStream getPlayerClipboard(Player player);
void setPlayerClipboard(Player player, InputStream is, boolean schemFormat); void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat);
Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException; Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException;
Vector getOrigin(Clipboard clipboard); Vector getOrigin(Clipboard clipboard);
Vector getMinimum(Region region); Vector getMinimum(Region region);
Vector getMaximum(Region region); Vector getMaximum(Region region);
Vector applyTransform(Vector vector, Transform transform); Vector applyTransform(Vector vector, Transform transform);
NodeData.SchematicFormat getNativeFormat();
static WorldEditPlugin getWorldEditPlugin() { static WorldEditPlugin getWorldEditPlugin() {
return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"); return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
} }
public static InputStream getPlayerClipboard(Player player, boolean schemFormat, SchematicWriter consumer) { static InputStream getPlayerClipboard(Player player, SchematicWriter consumer) {
ClipboardHolder clipboardHolder; ClipboardHolder clipboardHolder;
try { try {
clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard(); clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard();
@@ -85,7 +88,7 @@ public interface WorldEditWrapper {
return inputStream; return inputStream;
} }
public static interface SchematicWriter { interface SchematicWriter {
void write(OutputStream outputStream, Clipboard clipboard, ClipboardHolder holder) throws IOException; void write(OutputStream outputStream, Clipboard clipboard, ClipboardHolder holder) throws IOException;
} }
} }
@@ -36,7 +36,7 @@ import java.util.zip.GZIPInputStream;
public class SchematicData { public class SchematicData {
public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) { public static Clipboard clipboardFromStream(InputStream is, NodeData.SchematicFormat schemFormat) {
try { try {
return WorldEditWrapper.impl.getClipboard(is, schemFormat); return WorldEditWrapper.impl.getClipboard(is, schemFormat);
} catch (IOException e) { } catch (IOException e) {
@@ -61,15 +61,11 @@ public class SchematicData {
} }
public void saveFromPlayer(Player player) throws IOException, NoClipboardException { public void saveFromPlayer(Player player) throws IOException, NoClipboardException {
saveFromPlayer(player, Core.getVersion() > 12); data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player), WorldEditWrapper.impl.getNativeFormat());
}
public void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException {
data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player, newFormat), newFormat);
} }
@Deprecated @Deprecated
public void saveFromBytes(byte[] bytes, boolean newFormat) { public void saveFromBytes(byte[] bytes, NodeData.SchematicFormat newFormat) {
data.saveFromStream(new ByteArrayInputStream(bytes), newFormat); data.saveFromStream(new ByteArrayInputStream(bytes), newFormat);
} }
} }
+6 -1
View File
@@ -40,5 +40,10 @@ dependencies {
implementation(project(":SpigotCore:SpigotCore_18")) implementation(project(":SpigotCore:SpigotCore_18"))
implementation(project(":SpigotCore:SpigotCore_19")) implementation(project(":SpigotCore:SpigotCore_19"))
implementation(project(":SpigotCore:SpigotCore_20")) implementation(project(":SpigotCore:SpigotCore_20"))
implementation(project(":SpigotCore:SpigotCore_21")) implementation(project(":SpigotCore:SpigotCore_21")) {
attributes {
// Very Hacky, but it works
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
} }
+2
View File
@@ -63,4 +63,6 @@ dependencies {
implementation(libs.msgpack) implementation(libs.msgpack)
implementation(libs.apolloprotos) implementation(libs.apolloprotos)
implementation(libs.nbt)
} }
@@ -25,10 +25,13 @@ import de.steamwar.sql.NodeData;
import de.steamwar.sql.Punishment; import de.steamwar.sql.Punishment;
import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import dev.dewy.nbt.Nbt;
import dev.dewy.nbt.tags.collection.CompoundTag;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
@@ -38,6 +41,8 @@ import java.util.logging.Level;
public class DiscordSchemUpload extends ListenerAdapter { public class DiscordSchemUpload extends ListenerAdapter {
private static final Nbt NBT = new Nbt();
private static final List<String> SCHEM_FILE_ENDINGS = Arrays.asList(".schem", ".schematic"); private static final List<String> SCHEM_FILE_ENDINGS = Arrays.asList(".schem", ".schematic");
@Override @Override
@@ -79,7 +84,17 @@ public class DiscordSchemUpload extends ListenerAdapter {
node = SchematicNode.createSchematic(user.getId(), name, null); node = SchematicNode.createSchematic(user.getId(), name, null);
try (InputStream in = attachment.getProxy().download().get()) { try (InputStream in = attachment.getProxy().download().get()) {
NodeData.get(node).saveFromStream(in, fileName.substring(dot).equalsIgnoreCase(".schem")); CompoundTag tags = NBT.fromStream(new DataInputStream(in));
NodeData.SchematicFormat version = NodeData.SchematicFormat.SPONGE_V2;
if (tags.size() == 1) {
version = NodeData.SchematicFormat.SPONGE_V3;
} else if (tags.contains("Materials")) {
version = NodeData.SchematicFormat.MCEDIT;
}
NodeData.get(node).saveFromStream(in, version);
sender.system("DC_SCHEMUPLOAD_SUCCESS", name); sender.system("DC_SCHEMUPLOAD_SUCCESS", name);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
+1
View File
@@ -53,4 +53,5 @@ dependencies {
implementation(libs.yamlconfig) implementation(libs.yamlconfig)
implementation(libs.kotlinxSerializationCbor) implementation(libs.kotlinxSerializationCbor)
implementation(libs.ktorRateLimit) implementation(libs.ktorRateLimit)
implementation(libs.nbt)
} }
@@ -22,10 +22,13 @@ package de.steamwar.routes
import de.steamwar.plugins.SWAuthPrincipal import de.steamwar.plugins.SWAuthPrincipal
import de.steamwar.plugins.SWPermissionCheck import de.steamwar.plugins.SWPermissionCheck
import de.steamwar.sql.NodeData import de.steamwar.sql.NodeData
import de.steamwar.sql.NodeData.SchematicFormat
import de.steamwar.sql.NodeDownload import de.steamwar.sql.NodeDownload
import de.steamwar.sql.NodeMember import de.steamwar.sql.NodeMember
import de.steamwar.sql.SWException import de.steamwar.sql.SWException
import de.steamwar.sql.SchematicNode import de.steamwar.sql.SchematicNode
import dev.dewy.nbt.Nbt
import dev.dewy.nbt.tags.collection.CompoundTag
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
@@ -33,6 +36,7 @@ import io.ktor.server.request.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import java.security.MessageDigest import java.security.MessageDigest
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
@@ -71,6 +75,8 @@ data class SchematicCode(val id: Int, val code: String, val expires: Long)
@Serializable @Serializable
data class UploadSchematic(val name: String, val content: String) data class UploadSchematic(val name: String, val content: String)
val nbt = Nbt()
fun Route.configureSchematic() { fun Route.configureSchematic() {
route("/download/{code}") { route("/download/{code}") {
get { get {
@@ -88,7 +94,7 @@ fun Route.configureSchematic() {
return@get return@get
} }
call.response.header("Content-Disposition", "attachment; filename=\"${node.name}.${if (data.nodeFormat) "schem" else "schematic"}\"") call.response.header("Content-Disposition", "attachment; filename=\"${node.name}${data.nodeFormat.fileEnding}\"")
call.respondBytes(data.schemData().readAllBytes(), contentType = ContentType.Application.OctetStream, status = HttpStatusCode.OK) call.respondBytes(data.schemData().readAllBytes(), contentType = ContentType.Application.OctetStream, status = HttpStatusCode.OK)
} }
get("/info") { get("/info") {
@@ -112,16 +118,34 @@ fun Route.configureSchematic() {
val user = call.principal<SWAuthPrincipal>()!!.user val user = call.principal<SWAuthPrincipal>()!!.user
val content = Base64.getDecoder().decode(file.content)
var node = SchematicNode.getSchematicNode(user.id, schemName, 0) var node = SchematicNode.getSchematicNode(user.id, schemName, 0)
if (node == null) { if (node == null) {
node = SchematicNode.createSchematic(user.id, schemName, 0) node = SchematicNode.createSchematic(user.id, schemName, 0)
} }
val data = NodeData(node.id, false) try {
data.saveFromStream(content.inputStream(), schemType == "schem") val content = Base64.getDecoder().decode(file.content)
call.respond(ResponseSchematic(node)) var schem = nbt.fromByteArray(content)
if (schem.size() == 1) schem = schem.first() as CompoundTag
val version = schem.let {
if (it.contains("Materials"))
return@let SchematicFormat.MCEDIT
else if (it.contains("Blocks"))
return@let SchematicFormat.SPONGE_V3
else
return@let SchematicFormat.SPONGE_V2
}
val data = NodeData(node.id, version)
data.saveFromStream(content.inputStream(), version)
call.respond(ResponseSchematic(node))
} catch (e: Exception) {
call.respond(HttpStatusCode.BadRequest)
}
} }
} }
} }
+2
View File
@@ -164,6 +164,8 @@ dependencyResolutionManagement {
library("yamlconfig", "org.bspfsystems:yamlconfiguration:1.3.0") library("yamlconfig", "org.bspfsystems:yamlconfiguration:1.3.0")
library("kotlinxSerializationCbor", "org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.4.1") library("kotlinxSerializationCbor", "org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.4.1")
library("ktorRateLimit", "io.ktor:ktor-server-rate-limit:$ktorVersion") library("ktorRateLimit", "io.ktor:ktor-server-rate-limit:$ktorVersion")
library("nbt", "dev.dewy:nbt:1.5.1")
} }
} }
} }