Maybe, Fix Schematics Readers
This commit is contained in:
@@ -28,12 +28,15 @@ import java.sql.PreparedStatement;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class NodeData {
|
||||
|
||||
static {
|
||||
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<>(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);
|
||||
@@ -48,21 +51,24 @@ public class NodeData {
|
||||
throw new IllegalArgumentException("Node is a directory");
|
||||
return get.select(rs -> {
|
||||
if(rs.next()) {
|
||||
return new NodeData(node.getId(), rs.getBoolean("NodeFormat"));
|
||||
return new NodeData(node.getId(), SchematicFormat.values()[rs.getInt("NodeFormat")]);
|
||||
} else {
|
||||
return new NodeData(node.getId(), false);
|
||||
return new NodeData(node.getId(), SchematicFormat.MCEDIT);
|
||||
}
|
||||
}, node);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int nodeId;
|
||||
|
||||
@Field
|
||||
private boolean nodeFormat;
|
||||
private SchematicFormat nodeFormat;
|
||||
|
||||
public InputStream schemData() throws IOException {
|
||||
return new GZIPInputStream(schemDataRaw());
|
||||
}
|
||||
|
||||
public InputStream schemDataRaw() throws IOException {
|
||||
try {
|
||||
return selSchemData.select(rs -> {
|
||||
rs.next();
|
||||
@@ -81,12 +87,18 @@ public class NodeData {
|
||||
}
|
||||
}
|
||||
|
||||
public void saveFromStream(InputStream blob, boolean newFormat) {
|
||||
public void saveFromStream(InputStream blob, SchematicFormat newFormat) {
|
||||
updateDatabase.update(nodeId, newFormat, blob);
|
||||
nodeFormat = newFormat;
|
||||
}
|
||||
|
||||
public boolean getNodeFormat() {
|
||||
return nodeFormat;
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum SchematicFormat {
|
||||
MCEDIT(".schematic"),
|
||||
V2(".schem"),
|
||||
V3(".schem");
|
||||
|
||||
private final String fileEnding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,11 +375,10 @@ public class SchematicNode {
|
||||
return nodeType == null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean getSchemFormat() {
|
||||
public String getFileEnding() {
|
||||
if(isDir())
|
||||
throw new SecurityException("Node is Directory");
|
||||
return NodeData.get(this).getNodeFormat();
|
||||
return NodeData.get(this).getNodeFormat().getFileEnding();
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.FightSystem;
|
||||
import de.steamwar.sql.NodeData;
|
||||
import de.steamwar.sql.SchematicData;
|
||||
import de.steamwar.sql.SchematicNode;
|
||||
import org.bukkit.DyeColor;
|
||||
@@ -144,6 +145,6 @@ public class WorldeditWrapper14 implements WorldeditWrapper {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
|
||||
new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), true);
|
||||
new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), NodeData.SchematicFormat.V2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.sk89q.worldedit.session.ClipboardHolder;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.FightSystem;
|
||||
import de.steamwar.sql.NodeData;
|
||||
import de.steamwar.sql.SchematicData;
|
||||
import de.steamwar.sql.SchematicNode;
|
||||
import org.bukkit.DyeColor;
|
||||
@@ -142,6 +143,6 @@ public class WorldeditWrapper8 implements WorldeditWrapper {
|
||||
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 de.steamwar.core.Core;
|
||||
import de.steamwar.core.TrickyTrialsWrapper;
|
||||
import de.steamwar.core.WorldEditWrapper;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RPlayer;
|
||||
@@ -519,7 +520,7 @@ public class PacketProcessor implements Listener {
|
||||
public void close() {
|
||||
// FAWE 1.12 calls close...
|
||||
}
|
||||
}, Core.getVersion() > 12);
|
||||
}, WorldEditWrapper.getNativeFormat());
|
||||
|
||||
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_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()));
|
||||
} else {
|
||||
SchematicSystem.MESSAGE.sendPrefixless("UTIL_INFO_TYPE", player, SchematicSystem.MESSAGE.parse("UTIL_INFO_TYPE_DIR", player));
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.registry.LegacyMapper;
|
||||
import de.steamwar.sql.NoClipboardException;
|
||||
import de.steamwar.sql.NodeData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
@@ -57,24 +58,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class WorldEditWrapper14 implements WorldEditWrapper {
|
||||
|
||||
private static final ClipboardFormat SCHEMATIC = BuiltInClipboardFormat.MCEDIT_SCHEMATIC;
|
||||
private static final ClipboardFormat SCHEM = BuiltInClipboardFormat.SPONGE_SCHEMATIC;
|
||||
|
||||
@Override
|
||||
public InputStream getPlayerClipboard(Player player, boolean schemFormat) {
|
||||
return WorldEditWrapper.getPlayerClipboard(player, schemFormat, (outputStream, clipboard, clipboardHolder) -> {
|
||||
if(schemFormat){
|
||||
ClipboardWriter writer = SCHEM.getWriter(outputStream);
|
||||
writer.write(clipboard);
|
||||
writer.close();
|
||||
}else{
|
||||
SCHEMATIC.getWriter(outputStream).write(clipboard);
|
||||
}
|
||||
public InputStream getPlayerClipboard(Player player) {
|
||||
return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) -> {
|
||||
ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(outputStream);
|
||||
writer.write(clipboard);
|
||||
writer.close();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) {
|
||||
public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
|
||||
Clipboard clipboard = null;
|
||||
try {
|
||||
clipboard = getClipboard(is, schemFormat);
|
||||
@@ -90,12 +84,16 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
|
||||
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
|
||||
try {
|
||||
if(schemFormat){
|
||||
return new SpongeSchematicReader(new NBTInputStream(is)).read();
|
||||
}else{
|
||||
return new MCEditSchematicReader(new NBTInputStream(is)).read();
|
||||
|
||||
switch (schemFormat) {
|
||||
case V2:
|
||||
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) {
|
||||
throw new NoClipboardException();
|
||||
@@ -455,6 +453,11 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
|
||||
// Check
|
||||
Map<String, Tag> schematic = schematicTag.getValue();
|
||||
|
||||
if (schematic.size() == 1) {
|
||||
schematicTag = requireTag(schematic, "Schematic", CompoundTag.class);
|
||||
schematic = schematicTag.getValue();
|
||||
}
|
||||
|
||||
schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue();
|
||||
return schematicTag;
|
||||
}
|
||||
@@ -499,12 +502,16 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
|
||||
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(BlockVector3.ONE));
|
||||
}
|
||||
|
||||
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class);
|
||||
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue();
|
||||
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue()) {
|
||||
throw new IOException("Block palette size does not match expected size.");
|
||||
Map<String, Tag> blockContainer = null;
|
||||
boolean v3Mode = false;
|
||||
|
||||
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<>();
|
||||
|
||||
ParserContext parserContext = new ParserContext();
|
||||
@@ -526,12 +533,12 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
|
||||
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<>();
|
||||
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class);
|
||||
ListTag tileEntities = getTag(v3Mode ? blockContainer: schematic, "BlockEntities", ListTag.class);
|
||||
if (tileEntities == null) {
|
||||
tileEntities = getTag(schematic, "TileEntities", ListTag.class);
|
||||
tileEntities = getTag(v3Mode ? blockContainer: schematic, "TileEntities", ListTag.class);
|
||||
}
|
||||
if (tileEntities != null) {
|
||||
List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream()
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
|
||||
package de.steamwar.core;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReader;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.*;
|
||||
import de.steamwar.sql.NoClipboardException;
|
||||
import de.steamwar.sql.NodeData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -31,11 +33,18 @@ public class WorldEditWrapper18 extends WorldEditWrapper14 {
|
||||
|
||||
@Override
|
||||
@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);
|
||||
//Use FAWE reader due to FAWE capability of reading corrupt FAWE schems
|
||||
try {
|
||||
return (schemFormat ? new SpongeSchematicReader(nbtStream) : new MCEditSchematicReader(nbtStream)).read();
|
||||
switch (schemFormat) {
|
||||
case MCEDIT:
|
||||
return new MCEditSchematicReader(nbtStream).read();
|
||||
case V2:
|
||||
return new SpongeSchematicReader(nbtStream).read();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported schematic format");
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
throw new NoClipboardException();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,13 @@ plugins {
|
||||
steamwar.java
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":CommonCore", "default"))
|
||||
compileOnly(project(":SpigotCore:SpigotCore_Main", "default"))
|
||||
compileOnly(project(":SpigotCore:SpigotCore_18", "default"))
|
||||
compileOnly(project(":SpigotCore:SpigotCore_14", "default"))
|
||||
@@ -29,16 +35,6 @@ dependencies {
|
||||
|
||||
compileOnly(libs.fawe21)
|
||||
|
||||
compileOnly(libs.paperapi21) {
|
||||
attributes {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
compileOnly(libs.paperapi21)
|
||||
compileOnly(libs.nms21)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,857 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.core;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.clipboard.LinearClipboard;
|
||||
import com.fastasyncworldedit.core.extent.clipboard.SimpleClipboard;
|
||||
import com.fastasyncworldedit.core.internal.io.ResettableFileInputStream;
|
||||
import com.fastasyncworldedit.core.internal.io.VarIntStreamIterator;
|
||||
import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||
import com.fastasyncworldedit.core.util.IOUtil;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.NBTConstants;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.NBTOutputStream;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.sponge.ReaderUtil;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.sponge.VersionedDataFixer;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.concurrency.LazyReference;
|
||||
import com.sk89q.worldedit.world.DataFixer;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedOutputStream;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.enginehub.linbus.tree.LinCompoundTag;
|
||||
import org.enginehub.linbus.tree.LinIntArrayTag;
|
||||
import org.enginehub.linbus.tree.LinTagType;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* ClipboardReader for the Sponge Schematic Format v3.
|
||||
* Not necessarily much faster than {@link com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader}, but uses a
|
||||
* stream based approach to keep the memory overhead minimal (especially in larger schematics)
|
||||
*
|
||||
* @since 2.11.1
|
||||
*/
|
||||
@SuppressWarnings("removal") // JNBT
|
||||
public class FaWeSchematicReaderV3 implements ClipboardReader {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
private static final byte CACHE_IDENTIFIER_END = 0x00;
|
||||
private static final byte CACHE_IDENTIFIER_BLOCK = 0x01;
|
||||
private static final byte CACHE_IDENTIFIER_BIOMES = 0x02;
|
||||
private static final byte CACHE_IDENTIFIER_ENTITIES = 0x03;
|
||||
private static final byte CACHE_IDENTIFIER_BLOCK_TILE_ENTITIES = 0x04;
|
||||
|
||||
private final InputStream parentStream;
|
||||
private final MutableBlockVector3 dimensions = MutableBlockVector3.at(0, 0, 0);
|
||||
private final Set<Byte> remainingTags;
|
||||
|
||||
private DataInputStream dataInputStream;
|
||||
private NBTInputStream nbtInputStream;
|
||||
|
||||
private VersionedDataFixer dataFixer;
|
||||
private BlockVector3 offset;
|
||||
private BlockVector3 origin = BlockVector3.ZERO;
|
||||
private BlockState[] blockPalette;
|
||||
private BiomeType[] biomePalette;
|
||||
private int dataVersion = -1;
|
||||
|
||||
// Only used if the InputStream is not file based (and therefor does not support resets based on FileChannels)
|
||||
// and the file is unordered
|
||||
// Data and Palette cache is separated, as the data requires a fully populated palette - and the order is not guaranteed
|
||||
private byte[] dataCache;
|
||||
private byte[] paletteCache;
|
||||
private OutputStream dataCacheWriter;
|
||||
private OutputStream paletteCacheWriter;
|
||||
|
||||
|
||||
public FaWeSchematicReaderV3(@NonNull InputStream stream) {
|
||||
Objects.requireNonNull(stream, "stream");
|
||||
if (stream instanceof ResettableFileInputStream) {
|
||||
stream.mark(Integer.MAX_VALUE);
|
||||
this.remainingTags = new HashSet<>();
|
||||
} else if (stream instanceof FileInputStream fileInputStream) {
|
||||
stream = new ResettableFileInputStream(fileInputStream);
|
||||
stream.mark(Integer.MAX_VALUE);
|
||||
this.remainingTags = new HashSet<>();
|
||||
} else if (stream instanceof FastBufferedInputStream || stream instanceof BufferedInputStream) {
|
||||
this.remainingTags = null;
|
||||
} else {
|
||||
stream = new FastBufferedInputStream(stream);
|
||||
this.remainingTags = null;
|
||||
}
|
||||
this.parentStream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard read(final UUID uuid, final Function<BlockVector3, Clipboard> createOutput) throws IOException {
|
||||
Clipboard clipboard = null;
|
||||
|
||||
this.setSubStreams();
|
||||
skipHeader(this.dataInputStream);
|
||||
|
||||
byte type;
|
||||
String tag;
|
||||
while ((type = dataInputStream.readByte()) != NBTConstants.TYPE_END) {
|
||||
tag = this.dataInputStream.readUTF();
|
||||
switch (tag) {
|
||||
case "DataVersion" -> {
|
||||
final Platform platform =
|
||||
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING);
|
||||
this.dataVersion = this.dataInputStream.readInt();
|
||||
this.dataFixer = ReaderUtil.getVersionedDataFixer(this.dataVersion, platform, platform.getDataVersion());
|
||||
}
|
||||
case "Metadata" -> {
|
||||
LinCompoundTag metadataCompoundTag =
|
||||
(LinCompoundTag) this.nbtInputStream.readTagPayload(NBTConstants.TYPE_COMPOUND, 0).toLinTag();
|
||||
|
||||
LinCompoundTag worldEditTag = metadataCompoundTag.findTag("WorldEdit", LinTagType.compoundTag());
|
||||
if (worldEditTag != null) { // allowed to be optional
|
||||
LinIntArrayTag originTag = worldEditTag.findTag("Origin", LinTagType.intArrayTag());
|
||||
if (originTag != null) { // allowed to be optional
|
||||
int[] parts = originTag.value();
|
||||
|
||||
if (parts.length != 3) {
|
||||
throw new IOException("`Metadata > WorldEdit > Origin` int array length is invalid.");
|
||||
}
|
||||
|
||||
this.origin = BlockVector3.at(parts[0], parts[1], parts[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
case "Offset" -> {
|
||||
this.dataInputStream.skipNBytes(4); // Array Length field (4 byte int)
|
||||
this.offset = BlockVector3.at(
|
||||
this.dataInputStream.readInt(),
|
||||
this.dataInputStream.readInt(),
|
||||
this.dataInputStream.readInt()
|
||||
);
|
||||
}
|
||||
case "Width" -> this.dimensions.mutX(this.dataInputStream.readShort() & 0xFFFF);
|
||||
case "Height" -> this.dimensions.mutY(this.dataInputStream.readShort() & 0xFFFF);
|
||||
case "Length" -> this.dimensions.mutZ(this.dataInputStream.readShort() & 0xFFFF);
|
||||
case "Blocks" -> readBlocks(clipboard);
|
||||
case "Biomes" -> readBiomes(clipboard);
|
||||
case "Entities" -> readEntities(clipboard);
|
||||
default -> this.nbtInputStream.readTagPayloadLazy(type, 0);
|
||||
}
|
||||
if (clipboard == null && this.areDimensionsAvailable()) {
|
||||
clipboard = createOutput.apply(this.dimensions);
|
||||
}
|
||||
}
|
||||
|
||||
if (clipboard == null) {
|
||||
throw new IOException("Invalid schematic - missing dimensions");
|
||||
}
|
||||
if (dataFixer == null) {
|
||||
throw new IOException("Invalid schematic - missing DataVersion");
|
||||
}
|
||||
|
||||
if (this.supportsReset() && !remainingTags.isEmpty()) {
|
||||
readRemainingDataReset(clipboard);
|
||||
} else if (this.dataCacheWriter != null || this.paletteCacheWriter != null) {
|
||||
readRemainingDataCache(clipboard);
|
||||
}
|
||||
|
||||
clipboard.setOrigin(this.offset.multiply(-1));
|
||||
if (clipboard instanceof SimpleClipboard simpleClipboard && !this.offset.equals(BlockVector3.ZERO)) {
|
||||
clipboard = new BlockArrayClipboard(simpleClipboard, this.offset.add(this.origin));
|
||||
}
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads all locally cached data (due to reset not being available) and applies them to the clipboard.
|
||||
* <p>
|
||||
* Firstly, closes all cache writers (which adds the END identifier to each and fills the cache byte arrays on this instance)
|
||||
* If required, creates all missing palettes first (as needed by all remaining data).
|
||||
* At last writes all missing data (block states, tile entities, biomes, entities).
|
||||
*
|
||||
* @param clipboard The clipboard to write into.
|
||||
* @throws IOException on I/O error.
|
||||
*/
|
||||
private void readRemainingDataCache(Clipboard clipboard) throws IOException {
|
||||
byte identifier;
|
||||
if (this.paletteCacheWriter != null) {
|
||||
this.paletteCacheWriter.close();
|
||||
}
|
||||
if (this.dataCacheWriter != null) {
|
||||
this.dataCacheWriter.close();
|
||||
}
|
||||
if (this.paletteCache != null) {
|
||||
try (final DataInputStream cacheStream = new DataInputStream(new FastBufferedInputStream(
|
||||
new LZ4BlockInputStream(new FastBufferedInputStream(new ByteArrayInputStream(this.paletteCache)))))) {
|
||||
while ((identifier = cacheStream.readByte()) != CACHE_IDENTIFIER_END) {
|
||||
if (identifier == CACHE_IDENTIFIER_BLOCK) {
|
||||
this.readPaletteMap(cacheStream, this.provideBlockPaletteInitializer());
|
||||
continue;
|
||||
}
|
||||
if (identifier == CACHE_IDENTIFIER_BIOMES) {
|
||||
this.readPaletteMap(cacheStream, this.provideBiomePaletteInitializer());
|
||||
continue;
|
||||
}
|
||||
throw new IOException("invalid cache state - got identifier: 0x" + identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
try (final DataInputStream cacheStream = new DataInputStream(new FastBufferedInputStream(
|
||||
new LZ4BlockInputStream(new FastBufferedInputStream(new ByteArrayInputStream(this.dataCache)))));
|
||||
final NBTInputStream cacheNbtIn = new NBTInputStream(cacheStream)) {
|
||||
while ((identifier = cacheStream.readByte()) != CACHE_IDENTIFIER_END) {
|
||||
switch (identifier) {
|
||||
case CACHE_IDENTIFIER_BLOCK -> this.readPaletteData(cacheStream, this.getBlockWriter(clipboard));
|
||||
case CACHE_IDENTIFIER_BIOMES -> this.readPaletteData(cacheStream, this.getBiomeWriter(clipboard));
|
||||
case CACHE_IDENTIFIER_ENTITIES -> {
|
||||
cacheStream.skipNBytes(1); // list child type (TAG_Compound)
|
||||
this.readEntityContainers(
|
||||
cacheStream,
|
||||
cacheNbtIn,
|
||||
DataFixer.FixTypes.ENTITY,
|
||||
this.provideEntityTransformer(clipboard)
|
||||
);
|
||||
}
|
||||
case CACHE_IDENTIFIER_BLOCK_TILE_ENTITIES -> {
|
||||
cacheStream.skipNBytes(1); // list child type (TAG_Compound)
|
||||
this.readEntityContainers(
|
||||
cacheStream,
|
||||
cacheNbtIn,
|
||||
DataFixer.FixTypes.BLOCK_ENTITY,
|
||||
this.provideTileEntityTransformer(clipboard)
|
||||
);
|
||||
}
|
||||
default -> throw new IOException("invalid cache state - got identifier: 0x" + identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the main stream of this clipboard and reads all remaining data that could not be read or fixed yet.
|
||||
* Might need two iterations if the DataVersion tag is after the Blocks tag while the Palette inside the Blocks tag is not
|
||||
* at the first position.
|
||||
*
|
||||
* @param clipboard The clipboard to write into.
|
||||
* @throws IOException on I/O error.
|
||||
*/
|
||||
private void readRemainingDataReset(Clipboard clipboard) throws IOException {
|
||||
byte type;
|
||||
String tag;
|
||||
outer:
|
||||
while (!this.remainingTags.isEmpty()) {
|
||||
this.reset();
|
||||
skipHeader(this.dataInputStream);
|
||||
while ((type = dataInputStream.readByte()) != NBTConstants.TYPE_END) {
|
||||
tag = dataInputStream.readUTF();
|
||||
byte b = tag.equals("Blocks") ? CACHE_IDENTIFIER_BLOCK :
|
||||
tag.equals("Biomes") ? CACHE_IDENTIFIER_BIOMES :
|
||||
tag.equals("Entities") ? CACHE_IDENTIFIER_ENTITIES :
|
||||
CACHE_IDENTIFIER_END;
|
||||
if (!this.remainingTags.remove(b)) {
|
||||
this.nbtInputStream.readTagPayloadLazy(type, 0);
|
||||
continue;
|
||||
}
|
||||
switch (tag) {
|
||||
case "Blocks" -> readBlocks(clipboard);
|
||||
case "Biomes" -> readBiomes(clipboard);
|
||||
case "Entities" -> readEntities(clipboard);
|
||||
default -> this.nbtInputStream.readTagPayloadLazy(type, 0); // Should never happen, but just in case
|
||||
}
|
||||
if (this.remainingTags.isEmpty()) {
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Requires {@link #read()}, {@link #read(UUID)} or {@link #read(UUID, Function)} to be called before.
|
||||
*/
|
||||
@Override
|
||||
public OptionalInt getDataVersion() {
|
||||
return this.dataVersion > -1 ? OptionalInt.of(this.dataVersion) : OptionalInt.empty();
|
||||
}
|
||||
|
||||
private void readBlocks(Clipboard target) throws IOException {
|
||||
this.blockPalette = new BlockState[BlockTypesCache.states.length];
|
||||
readPalette(
|
||||
target != null,
|
||||
CACHE_IDENTIFIER_BLOCK,
|
||||
() -> this.blockPalette[0] != null,
|
||||
this.provideBlockPaletteInitializer(),
|
||||
this.getBlockWriter(target),
|
||||
(type, tag) -> {
|
||||
if (!tag.equals("BlockEntities")) {
|
||||
try {
|
||||
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to skip additional tag", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.readTileEntities(target);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Failed to read tile entities", e);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void readBiomes(Clipboard target) throws IOException {
|
||||
this.biomePalette = new BiomeType[BiomeType.REGISTRY.size()];
|
||||
readPalette(
|
||||
target != null,
|
||||
CACHE_IDENTIFIER_BIOMES,
|
||||
() -> this.biomePalette[0] != null,
|
||||
this.provideBiomePaletteInitializer(),
|
||||
this.getBiomeWriter(target),
|
||||
(type, tag) -> {
|
||||
try {
|
||||
this.nbtInputStream.readTagPayloadLazy(type, 0);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to skip additional tag in biome container: {}", tag, e);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void readEntities(@Nullable Clipboard target) throws IOException {
|
||||
if (target == null || this.dataFixer == null) {
|
||||
if (supportsReset()) {
|
||||
this.remainingTags.add(CACHE_IDENTIFIER_ENTITIES);
|
||||
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
|
||||
return;
|
||||
}
|
||||
// Easier than streaming for now
|
||||
final NBTOutputStream cacheStream = new NBTOutputStream(this.getDataCacheWriter());
|
||||
cacheStream.writeByte(CACHE_IDENTIFIER_ENTITIES);
|
||||
cacheStream.writeTagPayload(this.nbtInputStream.readTagPayload(NBTConstants.TYPE_LIST, 0));
|
||||
return;
|
||||
}
|
||||
if (this.dataInputStream.read() != NBTConstants.TYPE_COMPOUND) {
|
||||
throw new IOException("Expected a compound block for entity");
|
||||
}
|
||||
this.readEntityContainers(
|
||||
this.dataInputStream, this.nbtInputStream, DataFixer.FixTypes.ENTITY, this.provideEntityTransformer(target)
|
||||
);
|
||||
}
|
||||
|
||||
private void readTileEntities(Clipboard target) throws IOException {
|
||||
if (target == null || this.dataFixer == null) {
|
||||
if (supportsReset()) {
|
||||
this.remainingTags.add(CACHE_IDENTIFIER_BLOCK); // use block identifier, as this method will be called by
|
||||
// readBlocks again
|
||||
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
|
||||
return;
|
||||
}
|
||||
// Easier than streaming for now
|
||||
final NBTOutputStream cacheStream = new NBTOutputStream(this.getDataCacheWriter());
|
||||
cacheStream.writeByte(CACHE_IDENTIFIER_BLOCK_TILE_ENTITIES);
|
||||
cacheStream.writeTagPayload(this.nbtInputStream.readTagPayload(NBTConstants.TYPE_LIST, 0));
|
||||
return;
|
||||
}
|
||||
if (this.dataInputStream.read() != NBTConstants.TYPE_COMPOUND) {
|
||||
throw new IOException("Expected a compound block for tile entity");
|
||||
}
|
||||
this.readEntityContainers(
|
||||
this.dataInputStream,
|
||||
this.nbtInputStream,
|
||||
DataFixer.FixTypes.BLOCK_ENTITY,
|
||||
this.provideTileEntityTransformer(target)
|
||||
);
|
||||
}
|
||||
|
||||
private void readEntityContainers(
|
||||
DataInputStream stream,
|
||||
NBTInputStream nbtStream,
|
||||
DataFixer.FixType<LinCompoundTag> fixType,
|
||||
EntityTransformer transformer
|
||||
) throws IOException {
|
||||
double x, y, z;
|
||||
LinCompoundTag tag;
|
||||
String id;
|
||||
byte type;
|
||||
int count = stream.readInt();
|
||||
while (count-- > 0) {
|
||||
x = -1;
|
||||
y = -1;
|
||||
z = -1;
|
||||
tag = null;
|
||||
id = null;
|
||||
while ((type = stream.readByte()) != NBTConstants.TYPE_END) {
|
||||
switch (type) {
|
||||
// Depending on the type of entity container (tile vs "normal") the pos consists of either doubles or ints
|
||||
case NBTConstants.TYPE_INT_ARRAY -> {
|
||||
if (!stream.readUTF().equals("Pos")) {
|
||||
throw new IOException("Expected INT_ARRAY tag to be Pos");
|
||||
}
|
||||
stream.skipNBytes(4); // count of following ints - for pos = 3
|
||||
x = stream.readInt();
|
||||
y = stream.readInt();
|
||||
z = stream.readInt();
|
||||
}
|
||||
case NBTConstants.TYPE_LIST -> {
|
||||
if (!stream.readUTF().equals("Pos")) {
|
||||
throw new IOException("Expected LIST tag to be Pos");
|
||||
}
|
||||
if (stream.readByte() != NBTConstants.TYPE_DOUBLE) {
|
||||
throw new IOException("Expected LIST Pos tag to contain DOUBLE");
|
||||
}
|
||||
stream.skipNBytes(4); // count of following doubles - for pos = 3
|
||||
x = stream.readDouble();
|
||||
y = stream.readDouble();
|
||||
z = stream.readDouble();
|
||||
}
|
||||
case NBTConstants.TYPE_STRING -> {
|
||||
if (!stream.readUTF().equals("Id")) {
|
||||
throw new IOException("Expected STRING tag to be Id");
|
||||
}
|
||||
id = stream.readUTF();
|
||||
}
|
||||
case NBTConstants.TYPE_COMPOUND -> {
|
||||
if (!stream.readUTF().equals("Data")) {
|
||||
throw new IOException("Expected COMPOUND tag to be Data");
|
||||
}
|
||||
if (!(nbtStream.readTagPayload(NBTConstants.TYPE_COMPOUND, 0).toLinTag() instanceof LinCompoundTag lin)) {
|
||||
throw new IOException("Data tag could not be read into LinCompoundTag");
|
||||
}
|
||||
tag = lin;
|
||||
}
|
||||
default -> throw new IOException("Unexpected tag in compound: " + type);
|
||||
}
|
||||
}
|
||||
if (id == null) {
|
||||
throw new IOException("Missing Id tag in compound");
|
||||
}
|
||||
if (x < 0 || y < 0 || z < 0) {
|
||||
throw new IOException("Missing position for entity " + id);
|
||||
}
|
||||
if (tag == null) {
|
||||
transformer.transform(x, y, z, id, LinCompoundTag.of(Map.of()));
|
||||
continue;
|
||||
}
|
||||
tag = this.dataFixer.fixUp(fixType, tag);
|
||||
if (tag == null) {
|
||||
LOGGER.warn("Failed to fix-up entity for {} @ {},{},{} - skipping", id, x, y, z);
|
||||
continue;
|
||||
}
|
||||
transformer.transform(x, y, z, id, tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Palette` tag is required first, as that contains the information of the actual palette size.
|
||||
* Keeping the whole Data block in memory - which *could* be compressed - is just not it
|
||||
*
|
||||
* @param paletteInitializer Invoked for each 'Palette' entry using the actual palette value (e.g. block state) + index
|
||||
* @param paletteDataApplier Invoked for each 'Data' entry using the data index and the palette index at the data index
|
||||
*/
|
||||
private void readPalette(
|
||||
boolean hasClipboard,
|
||||
byte paletteType,
|
||||
BooleanSupplier paletteAlreadyInitialized,
|
||||
PaletteInitializer paletteInitializer,
|
||||
PaletteDataApplier paletteDataApplier,
|
||||
AdditionalTagConsumer additionalTag
|
||||
) throws IOException {
|
||||
boolean hasPalette = paletteAlreadyInitialized.getAsBoolean();
|
||||
byte type;
|
||||
String tag;
|
||||
while ((type = this.dataInputStream.readByte()) != NBTConstants.TYPE_END) {
|
||||
tag = this.dataInputStream.readUTF();
|
||||
if (tag.equals("Palette")) {
|
||||
if (hasPalette) {
|
||||
// Skip palette, as already exists
|
||||
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_COMPOUND, 0);
|
||||
continue;
|
||||
}
|
||||
if (!this.readPaletteMap(this.dataInputStream, paletteInitializer)) {
|
||||
if (this.supportsReset()) {
|
||||
// Couldn't read - skip palette for now
|
||||
this.remainingTags.add(paletteType);
|
||||
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_COMPOUND, 0);
|
||||
continue;
|
||||
}
|
||||
// Reset not possible, write into cache
|
||||
final NBTOutputStream cacheWriter = new NBTOutputStream(this.getPaletteCacheWriter());
|
||||
cacheWriter.write(paletteType);
|
||||
cacheWriter.writeTagPayload(this.nbtInputStream.readTagPayload(NBTConstants.TYPE_COMPOUND, 0));
|
||||
continue;
|
||||
}
|
||||
hasPalette = true;
|
||||
continue;
|
||||
}
|
||||
if (tag.equals("Data")) {
|
||||
// No palette or dimensions are yet available
|
||||
if (!hasPalette || this.dataFixer == null || !hasClipboard) {
|
||||
if (this.supportsReset()) {
|
||||
this.remainingTags.add(paletteType);
|
||||
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_BYTE_ARRAY, 0);
|
||||
continue;
|
||||
}
|
||||
// Reset not possible, write into cache
|
||||
int byteLen = this.dataInputStream.readInt();
|
||||
final DataOutputStream cacheWriter = new DataOutputStream(this.getDataCacheWriter());
|
||||
cacheWriter.write(paletteType);
|
||||
cacheWriter.writeInt(byteLen);
|
||||
IOUtil.copy(this.dataInputStream, cacheWriter, byteLen);
|
||||
continue;
|
||||
}
|
||||
this.readPaletteData(this.dataInputStream, paletteDataApplier);
|
||||
continue;
|
||||
}
|
||||
additionalTag.accept(type, tag);
|
||||
}
|
||||
}
|
||||
|
||||
private void readPaletteData(DataInputStream stream, PaletteDataApplier applier) throws IOException {
|
||||
int length = stream.readInt();
|
||||
// Write data into clipboard
|
||||
int i = 0;
|
||||
if (needsVarIntReading(length)) {
|
||||
for (var iter = new VarIntStreamIterator(stream, length); iter.hasNext(); i++) {
|
||||
applier.apply(i, (char) iter.nextInt());
|
||||
}
|
||||
return;
|
||||
}
|
||||
while (i < length) {
|
||||
applier.apply(i++, (char) stream.readUnsignedByte());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the CompoundTag containing the palette mapping ({@code index: value}) and passes each entry to the
|
||||
* {@link PaletteInitializer}.
|
||||
* <p>
|
||||
* This method expects that the identifier ({@link NBTConstants#TYPE_COMPOUND}) is already consumed from the stream.
|
||||
*
|
||||
* @param stream The stream to read the data from.
|
||||
* @param initializer The initializer called for each entry with its index and backed value.
|
||||
* @return {@code true} if the mapping could be read, {@code false} otherwise (e.g. DataFixer is not yet available).
|
||||
* @throws IOException on I/O error.
|
||||
*/
|
||||
private boolean readPaletteMap(DataInputStream stream, PaletteInitializer initializer) throws IOException {
|
||||
if (this.dataFixer == null) {
|
||||
return false;
|
||||
}
|
||||
while (stream.readByte() != NBTConstants.TYPE_END) {
|
||||
String value = stream.readUTF();
|
||||
char index = (char) stream.readInt();
|
||||
initializer.initialize(index, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void indexToPosition(int index, PositionConsumer supplier) {
|
||||
int y = index / (dimensions.x() * dimensions.z());
|
||||
int remainder = index - (y * dimensions.x() * dimensions.z());
|
||||
int z = remainder / dimensions.x();
|
||||
int x = remainder - z * dimensions.x();
|
||||
supplier.accept(x, y, z);
|
||||
}
|
||||
|
||||
private PaletteDataApplier getBlockWriter(Clipboard target) {
|
||||
if (target instanceof LinearClipboard linearClipboard) {
|
||||
return (index, ordinal) -> linearClipboard.setBlock(index, this.blockPalette[ordinal]);
|
||||
}
|
||||
return (index, ordinal) -> indexToPosition(index, (x, y, z) -> target.setBlock(x, y, z, this.blockPalette[ordinal]));
|
||||
}
|
||||
|
||||
private PaletteDataApplier getBiomeWriter(Clipboard target) {
|
||||
return (index, ordinal) -> indexToPosition(index, (x, y, z) -> target.setBiome(x, y, z, this.biomePalette[ordinal]));
|
||||
}
|
||||
|
||||
private PaletteInitializer provideBlockPaletteInitializer() {
|
||||
return (index, value) -> {
|
||||
if (this.dataFixer == null) {
|
||||
throw new IllegalStateException("Can't read block palette map if DataFixer is not yet available");
|
||||
}
|
||||
value = dataFixer.fixUp(DataFixer.FixTypes.BLOCK_STATE, value);
|
||||
try {
|
||||
this.blockPalette[index] = BlockState.get(value);
|
||||
} catch (InputParseException e) {
|
||||
LOGGER.warn("Invalid BlockState in palette: {}. Block will be replaced with air.", value);
|
||||
this.blockPalette[index] = BlockTypes.AIR.getDefaultState();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PaletteInitializer provideBiomePaletteInitializer() {
|
||||
return (index, value) -> {
|
||||
if (this.dataFixer == null) {
|
||||
throw new IllegalStateException("Can't read biome palette map if DataFixer is not yet available");
|
||||
}
|
||||
value = dataFixer.fixUp(DataFixer.FixTypes.BIOME, value);
|
||||
BiomeType biomeType = BiomeTypes.get(value);
|
||||
if (biomeType == null) {
|
||||
biomeType = BiomeTypes.PLAINS;
|
||||
LOGGER.warn("Invalid biome type in palette: {}. Biome will be replaced with plains.", value);
|
||||
}
|
||||
this.biomePalette[index] = biomeType;
|
||||
};
|
||||
}
|
||||
|
||||
private EntityTransformer provideEntityTransformer(Clipboard clipboard) {
|
||||
return (x, y, z, id, tag) -> {
|
||||
EntityType type = EntityType.REGISTRY.get(id);
|
||||
if (type == null) {
|
||||
LOGGER.warn("Invalid entity id: {} - skipping", id);
|
||||
return;
|
||||
}
|
||||
clipboard.createEntity(
|
||||
new Location(clipboard, Location.at(x, y, z).add(clipboard.getMinimumPoint().toVector3())),
|
||||
new BaseEntity(type, LazyReference.computed(tag))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
private EntityTransformer provideTileEntityTransformer(Clipboard clipboard) {
|
||||
return (x, y, z, id, tag) -> clipboard.tile(
|
||||
MathMan.roundInt(x + clipboard.getMinimumPoint().x()),
|
||||
MathMan.roundInt(y + clipboard.getMinimumPoint().y()),
|
||||
MathMan.roundInt(z + clipboard.getMinimumPoint().z()),
|
||||
FaweCompoundTag.of(tag)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if {@code Width}, {@code Length} and {@code Height} are already read from the stream
|
||||
*/
|
||||
private boolean areDimensionsAvailable() {
|
||||
return this.dimensions.x() != 0 && this.dimensions.y() != 0 && this.dimensions.z() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this reader instance and all underlying resources.
|
||||
*
|
||||
* @throws IOException on I/O error.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
parentStream.close(); // closes all underlying resources implicitly
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the main stream to the previously marked position ({@code 0}), if supported (see {@link #supportsReset()}).
|
||||
* If the stream is reset, the sub streams (for DataInput and NBT) are re-created to respect the new position.
|
||||
*
|
||||
* @throws IOException on I/O error.
|
||||
*/
|
||||
private void reset() throws IOException {
|
||||
if (this.supportsReset()) {
|
||||
this.parentStream.reset();
|
||||
this.parentStream.mark(Integer.MAX_VALUE);
|
||||
this.setSubStreams();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if the stream used while instantiating the reader supports resets (without memory overhead).
|
||||
*/
|
||||
private boolean supportsReset() {
|
||||
return this.remainingTags != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the DataInput- and NBT-InputStreams (e.g. when the marker of the backed stream updated).
|
||||
*
|
||||
* @throws IOException on I/O error.
|
||||
*/
|
||||
private void setSubStreams() throws IOException {
|
||||
this.dataInputStream = new DataInputStream(this.parentStream);
|
||||
this.nbtInputStream = new NBTInputStream(this.parentStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cache writer for non-palette data, if none exists yet.
|
||||
* Returns either the already created or new one.
|
||||
*
|
||||
* @return the output stream for non-palette cache data.
|
||||
*/
|
||||
private OutputStream getDataCacheWriter() {
|
||||
if (this.dataCacheWriter == null) {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(512);
|
||||
this.dataCacheWriter = new FastBufferedOutputStream(new LZ4BlockOutputStream(byteArrayOutputStream)) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.write(CACHE_IDENTIFIER_END);
|
||||
super.close();
|
||||
FaWeSchematicReaderV3.this.dataCache = byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
};
|
||||
}
|
||||
return this.dataCacheWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cache writer for palette data, if none exists yet.
|
||||
* Returns either the already created or new one.
|
||||
*
|
||||
* @return the output stream for palette cache data.
|
||||
*/
|
||||
private OutputStream getPaletteCacheWriter() {
|
||||
if (this.paletteCacheWriter == null) {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(256);
|
||||
this.paletteCacheWriter = new FastBufferedOutputStream(new LZ4BlockOutputStream(byteArrayOutputStream)) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.write(CACHE_IDENTIFIER_END);
|
||||
super.close();
|
||||
FaWeSchematicReaderV3.this.paletteCache = byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
};
|
||||
}
|
||||
return this.paletteCacheWriter;
|
||||
}
|
||||
|
||||
private boolean needsVarIntReading(int byteArrayLength) {
|
||||
return byteArrayLength > this.dimensions.x() * this.dimensions.y() * this.dimensions.z();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips the schematic header including the root compound (empty name) and the root's child compound ("Schematic")
|
||||
*
|
||||
* @param dataInputStream The stream containing the schematic data to skip
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
private static void skipHeader(DataInputStream dataInputStream) throws IOException {
|
||||
dataInputStream.skipNBytes(1 + 2); // 1 Byte = TAG_Compound, 2 Bytes = Short (Length of tag name = "")
|
||||
dataInputStream.skipNBytes(1 + 2 + 9); // as above + 9 bytes = "Schematic"
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@FunctionalInterface
|
||||
private interface PositionConsumer {
|
||||
|
||||
/**
|
||||
* Called with block location coordinates.
|
||||
*
|
||||
* @param x the x coordinate.
|
||||
* @param y the y coordinate.
|
||||
* @param z the z coordinate.
|
||||
*/
|
||||
void accept(int x, int y, int z);
|
||||
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@FunctionalInterface
|
||||
private interface EntityTransformer {
|
||||
|
||||
/**
|
||||
* Called for each entity from the Schematics {@code Entities} compound list.
|
||||
*
|
||||
* @param x the relative x coordinate of the entity.
|
||||
* @param y the relative y coordinate of the entity.
|
||||
* @param z the relative z coordinate of the entity.
|
||||
* @param id the entity id as a resource location (e.g. {@code minecraft:sheep}).
|
||||
* @param tag the - already fixed, if required - nbt data of the entity.
|
||||
*/
|
||||
void transform(double x, double y, double z, String id, LinCompoundTag tag);
|
||||
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@FunctionalInterface
|
||||
private interface PaletteInitializer {
|
||||
|
||||
/**
|
||||
* Called for each palette entry (the mapping part, not data).
|
||||
*
|
||||
* @param index the index of the entry, as used in the Data byte array.
|
||||
* @param value the value for this entry (either biome type as resource location or the block state as a string).
|
||||
*/
|
||||
void initialize(char index, String value);
|
||||
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@FunctionalInterface
|
||||
private interface PaletteDataApplier {
|
||||
|
||||
/**
|
||||
* Called for each palette data entry (not the mapping part, but the var-int byte array).
|
||||
*
|
||||
* @param index The index of this data entry (due to var-int behaviour not necessarily the index in the data byte array).
|
||||
* @param ordinal The ordinal of this entry as defined in the palette mapping.
|
||||
*/
|
||||
void apply(int index, char ordinal);
|
||||
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@FunctionalInterface
|
||||
private interface AdditionalTagConsumer {
|
||||
|
||||
/**
|
||||
* Called for each unknown nbt tag.
|
||||
*
|
||||
* @param type The type of the tag (as defined by the constants in {@link NBTConstants}).
|
||||
* @param name The name of the tag.
|
||||
*/
|
||||
void accept(byte type, String name);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,33 +20,27 @@
|
||||
package de.steamwar.core;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
|
||||
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV3;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
|
||||
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV1Reader;
|
||||
import com.sk89q.worldedit.math.Vector3;
|
||||
import com.sk89q.worldedit.math.transform.Transform;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||
import de.steamwar.sql.NodeData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
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.*;
|
||||
|
||||
public class WorldEditWrapper21 implements WorldEditWrapper {
|
||||
|
||||
@Override
|
||||
public InputStream getPlayerClipboard(Player player, boolean schemFormat) {
|
||||
return WorldEditWrapper.getPlayerClipboard(player, schemFormat, (outputStream, clipboard, clipboardHolder) -> {
|
||||
public InputStream getPlayerClipboard(Player player) {
|
||||
return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) -> {
|
||||
ClipboardWriter writer = BuiltInClipboardFormat.FAST_V3.getWriter(outputStream);
|
||||
writer.write(clipboard);
|
||||
writer.close();
|
||||
@@ -54,7 +48,7 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) {
|
||||
public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
|
||||
Clipboard clipboard = null;
|
||||
try {
|
||||
clipboard = getClipboard(is, schemFormat);
|
||||
@@ -70,35 +64,12 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
|
||||
if (!schemFormat) {
|
||||
return new MCEditSchematicReader(new NBTInputStream(is)).read();
|
||||
} else {
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
|
||||
return switch (schemFormat) {
|
||||
case MCEDIT -> new MCEditSchematicReader(new NBTInputStream(is)).read();
|
||||
case V2 -> new FastSchematicReaderV2(new NBTInputStream(is)).read();
|
||||
case V3 -> new FaWeSchematicReaderV3(is).read();
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||
import com.sk89q.worldedit.world.registry.WorldData;
|
||||
import de.steamwar.sql.NodeData;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -50,13 +51,13 @@ import java.util.stream.Collectors;
|
||||
public class WorldEditWrapper8 implements WorldEditWrapper {
|
||||
|
||||
@Override
|
||||
public InputStream getPlayerClipboard(Player player, boolean schemFormat) {
|
||||
return WorldEditWrapper.getPlayerClipboard(player, schemFormat, (outputStream, clipboard, clipboardHolder) ->
|
||||
public InputStream getPlayerClipboard(Player player) {
|
||||
return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) ->
|
||||
ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData()));
|
||||
}
|
||||
|
||||
@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();
|
||||
Clipboard clipboard;
|
||||
try {
|
||||
@@ -70,11 +71,16 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
|
||||
if(schemFormat)
|
||||
return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
|
||||
else
|
||||
return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
|
||||
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
|
||||
switch (schemFormat) {
|
||||
case MCEDIT:
|
||||
return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
|
||||
case V2:
|
||||
case V3:
|
||||
return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported schematic format");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,6 +143,13 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
|
||||
IDConverter8 ids = new IDConverter8();
|
||||
|
||||
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 height = (requireTag(schematic, "Height", ShortTag.class)).getValue();
|
||||
int length = (requireTag(schematic, "Length", ShortTag.class)).getValue();
|
||||
@@ -167,10 +180,13 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
|
||||
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE));
|
||||
}
|
||||
|
||||
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class);
|
||||
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue();
|
||||
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue())
|
||||
throw new IOException("Block palette size does not match expected size.");
|
||||
Map<String, Tag> blockContainer = null;
|
||||
|
||||
if (v3Mode) {
|
||||
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<>();
|
||||
ParserContext parserContext = new ParserContext();
|
||||
@@ -182,11 +198,11 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
|
||||
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<>();
|
||||
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class);
|
||||
ListTag tileEntities = getTag(v3Mode ? blockContainer : schematic, "BlockEntities", ListTag.class);
|
||||
if (tileEntities == null) {
|
||||
tileEntities = getTag(schematic, "TileEntities", ListTag.class);
|
||||
tileEntities = getTag(v3Mode ? blockContainer : schematic, "TileEntities", ListTag.class);
|
||||
}
|
||||
|
||||
if (tileEntities != null) {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ErrorHandler extends Handler {
|
||||
return;
|
||||
|
||||
try {
|
||||
SWException.log(message, stacktrace);
|
||||
//SWException.log(message, stacktrace);
|
||||
} catch (SecurityException e) {
|
||||
Core.getInstance().getLogger().log(Level.INFO, "Could not log error in database", e);
|
||||
}
|
||||
|
||||
@@ -26,19 +26,23 @@ import com.sk89q.worldedit.session.ClipboardHolder;
|
||||
import de.steamwar.sql.NoClipboardException;
|
||||
import com.sk89q.worldedit.math.transform.Transform;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import de.steamwar.sql.NodeData;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public interface WorldEditWrapper {
|
||||
WorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
|
||||
|
||||
InputStream getPlayerClipboard(Player player, boolean schemFormat);
|
||||
void setPlayerClipboard(Player player, InputStream is, boolean schemFormat);
|
||||
Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException;
|
||||
InputStream getPlayerClipboard(Player player);
|
||||
void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat);
|
||||
Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException;
|
||||
|
||||
Vector getOrigin(Clipboard clipboard);
|
||||
Vector getMinimum(Region region);
|
||||
@@ -49,7 +53,7 @@ public interface WorldEditWrapper {
|
||||
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;
|
||||
try {
|
||||
clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard();
|
||||
@@ -85,7 +89,17 @@ public interface WorldEditWrapper {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
public static interface SchematicWriter {
|
||||
static NodeData.SchematicFormat getNativeFormat() {
|
||||
if (Core.getVersion() <= 12) {
|
||||
return NodeData.SchematicFormat.MCEDIT;
|
||||
} else if (Core.getVersion() <= 20) {
|
||||
return NodeData.SchematicFormat.V2;
|
||||
} else {
|
||||
return NodeData.SchematicFormat.V3;
|
||||
}
|
||||
}
|
||||
|
||||
interface SchematicWriter {
|
||||
void write(OutputStream outputStream, Clipboard clipboard, ClipboardHolder holder) throws IOException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class SchematicData {
|
||||
|
||||
public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) {
|
||||
public static Clipboard clipboardFromStream(InputStream is, NodeData.SchematicFormat schemFormat) {
|
||||
try {
|
||||
return WorldEditWrapper.impl.getClipboard(is, schemFormat);
|
||||
} catch (IOException e) {
|
||||
@@ -61,15 +61,11 @@ public class SchematicData {
|
||||
}
|
||||
|
||||
public void saveFromPlayer(Player player) throws IOException, NoClipboardException {
|
||||
saveFromPlayer(player, Core.getVersion() > 12);
|
||||
}
|
||||
|
||||
public void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException {
|
||||
data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player, newFormat), newFormat);
|
||||
data.saveFromStream(WorldEditWrapper.impl.getPlayerClipboard(player), WorldEditWrapper.getNativeFormat());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void saveFromBytes(byte[] bytes, boolean newFormat) {
|
||||
public void saveFromBytes(byte[] bytes, NodeData.SchematicFormat newFormat) {
|
||||
data.saveFromStream(new ByteArrayInputStream(bytes), newFormat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,5 +40,10 @@ dependencies {
|
||||
implementation(project(":SpigotCore:SpigotCore_18"))
|
||||
implementation(project(":SpigotCore:SpigotCore_19"))
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public class DiscordSchemUpload extends ListenerAdapter {
|
||||
node = SchematicNode.createSchematic(user.getId(), name, null);
|
||||
|
||||
try (InputStream in = attachment.getProxy().download().get()) {
|
||||
NodeData.get(node).saveFromStream(in, fileName.substring(dot).equalsIgnoreCase(".schem"));
|
||||
NodeData.get(node).saveFromStream(in, fileName.substring(dot).equalsIgnoreCase(".schem") ? NodeData.SchematicFormat.V2 : NodeData.SchematicFormat.MCEDIT);
|
||||
sender.system("DC_SCHEMUPLOAD_SUCCESS", name);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
||||
@@ -88,7 +88,7 @@ fun Route.configureSchematic() {
|
||||
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)
|
||||
}
|
||||
get("/info") {
|
||||
@@ -118,8 +118,8 @@ fun Route.configureSchematic() {
|
||||
node = SchematicNode.createSchematic(user.id, schemName, 0)
|
||||
}
|
||||
|
||||
val data = NodeData(node.id, false)
|
||||
data.saveFromStream(content.inputStream(), schemType == "schem")
|
||||
val data = NodeData(node.id, NodeData.SchematicFormat.V2)
|
||||
data.saveFromStream(content.inputStream(), NodeData.SchematicFormat.V2)
|
||||
|
||||
call.respond(ResponseSchematic(node))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user