Fix fast schematic reader/writer

- Have both sponge and fast r/w available but default to "fast"
 - Correctly "offset" entities in the schematic, and add a legacy mode for loading old FAWE schematics with entities required.
 - Lazily reading means it's read in order of appearance in the inputstream so we need to read schematic version first (skip past everything) and then reset the stream. Fixes #740
 - Add an FAWEVersion to the metadata
 - Correctly actually return a BlockArrayClipboard when required. Fixes #454
This commit is contained in:
dordsor21 2021-01-01 15:01:35 +00:00
parent 82f640d132
commit fbfe3221d7
No known key found for this signature in database
GPG Key ID: 1E53E88969FFCF0B
8 changed files with 181 additions and 57 deletions

View File

@ -200,7 +200,7 @@ public class StreamDelegate {
Object raw = is.readTagPayloadRaw(type, depth); Object raw = is.readTagPayloadRaw(type, depth);
valueReader.apply(0, raw); valueReader.apply(0, raw);
} else { } else {
is.readTagPaylodLazy(type, depth + 1, this); is.readTagPayloadLazy(type, depth + 1, this);
} }
} }

View File

@ -61,6 +61,14 @@ public final class NBTInputStream implements Closeable {
this.is = dis; this.is = dis;
} }
public void mark(int mark) {
is.mark(mark);
}
public void reset() throws IOException {
is.reset();
}
/** /**
* Reads an NBT tag from the stream. * Reads an NBT tag from the stream.
* *
@ -99,7 +107,7 @@ public final class NBTInputStream implements Closeable {
if (child != null) { if (child != null) {
child.acceptRoot(this, type, 0); child.acceptRoot(this, type, 0);
} else { } else {
readTagPaylodLazy(type, 0); readTagPayloadLazy(type, 0);
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -119,7 +127,7 @@ public final class NBTInputStream implements Closeable {
private byte[] buf; private byte[] buf;
public void readTagPaylodLazy(int type, int depth) throws IOException { public void readTagPayloadLazy(int type, int depth) throws IOException {
switch (type) { switch (type) {
case NBTConstants.TYPE_END: case NBTConstants.TYPE_END:
return; return;
@ -152,7 +160,7 @@ public final class NBTInputStream implements Closeable {
int childType = is.readByte(); int childType = is.readByte();
length = is.readInt(); length = is.readInt();
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
readTagPaylodLazy(childType, depth + 1); readTagPayloadLazy(childType, depth + 1);
} }
return; return;
} }
@ -165,7 +173,7 @@ public final class NBTInputStream implements Closeable {
return; return;
} }
is.skipBytes(is.readShort() & 0xFFFF); is.skipBytes(is.readShort() & 0xFFFF);
readTagPaylodLazy(childType, depth + 1); readTagPayloadLazy(childType, depth + 1);
} }
} }
case NBTConstants.TYPE_INT_ARRAY: { case NBTConstants.TYPE_INT_ARRAY: {
@ -181,7 +189,7 @@ public final class NBTInputStream implements Closeable {
} }
} }
public void readTagPaylodLazy(int type, int depth, StreamDelegate scope) throws IOException { public void readTagPayloadLazy(int type, int depth, StreamDelegate scope) throws IOException {
switch (type) { switch (type) {
case NBTConstants.TYPE_END: case NBTConstants.TYPE_END:
return; return;
@ -293,11 +301,11 @@ public final class NBTInputStream implements Closeable {
child = scope.get0(); child = scope.get0();
if (child == null) { if (child == null) {
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
readTagPaylodLazy(childType, depth + 1); readTagPayloadLazy(childType, depth + 1);
} }
} else { } else {
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
readTagPaylodLazy(childType, depth + 1, child); readTagPayloadLazy(childType, depth + 1, child);
} }
} }
return; return;
@ -330,9 +338,9 @@ public final class NBTInputStream implements Closeable {
} }
StreamDelegate child = scope.get(is); StreamDelegate child = scope.get(is);
if (child == null) { if (child == null) {
readTagPaylodLazy(childType, depth + 1); readTagPayloadLazy(childType, depth + 1);
} else { } else {
readTagPaylodLazy(childType, depth + 1, child); readTagPayloadLazy(childType, depth + 1, child);
} }
} }
} }

View File

@ -147,7 +147,7 @@ public class SchematicCommands {
@Deprecated @Deprecated
@CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.load.web", "worldedit.schematic.load.asset"}) @CommandPermissions({"worldedit.clipboard.load", "worldedit.schematic.load", "worldedit.schematic.load.web", "worldedit.schematic.load.asset"})
public void loadall(Player player, LocalSession session, public void loadall(Player player, LocalSession session,
@Arg(desc = "Format name.", def = "schematic") @Arg(desc = "Format name.", def = "fast")
String formatName, String formatName,
@Arg(desc = "File name.") @Arg(desc = "File name.")
String filename, String filename,
@ -223,7 +223,7 @@ public class SchematicCommands {
public void load(Actor actor, LocalSession session, public void load(Actor actor, LocalSession session,
@Arg(desc = "File name.") @Arg(desc = "File name.")
String filename, String filename,
@Arg(desc = "Format name.", def = "sponge") @Arg(desc = "Format name.", def = "fast")
String formatName) throws FilenameException { String formatName) throws FilenameException {
LocalConfiguration config = worldEdit.getConfiguration(); LocalConfiguration config = worldEdit.getConfiguration();
@ -323,7 +323,7 @@ public class SchematicCommands {
public void save(Actor actor, LocalSession session, public void save(Actor actor, LocalSession session,
@Arg(desc = "File name.") @Arg(desc = "File name.")
String filename, String filename,
@Arg(desc = "Format name.", def = "sponge") @Arg(desc = "Format name.", def = "fast")
String formatName, String formatName,
@Switch(name = 'f', desc = "Overwrite an existing file.") @Switch(name = 'f', desc = "Overwrite an existing file.")
boolean allowOverwrite, boolean allowOverwrite,

View File

@ -24,8 +24,11 @@ import com.boydti.fawe.object.io.ResettableFileInputStream;
import com.boydti.fawe.object.schematic.MinecraftStructure; import com.boydti.fawe.object.schematic.MinecraftStructure;
import com.boydti.fawe.object.schematic.PNGWriter; import com.boydti.fawe.object.schematic.PNGWriter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.Tag;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -35,6 +38,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
@ -44,6 +48,44 @@ import java.util.zip.GZIPOutputStream;
*/ */
public enum BuiltInClipboardFormat implements ClipboardFormat { public enum BuiltInClipboardFormat implements ClipboardFormat {
FAST("fast", "fawe") {
@Override
public String getPrimaryFileExtension() {
return "schem";
}
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
if (inputStream instanceof FileInputStream) {
inputStream = new ResettableFileInputStream((FileInputStream) inputStream);
}
BufferedInputStream buffered = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered)));
return new FastSchematicReader(nbtStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
OutputStream gzip;
if (outputStream instanceof PGZIPOutputStream || outputStream instanceof GZIPOutputStream) {
gzip = outputStream;
} else {
outputStream = new BufferedOutputStream(outputStream);
gzip = new PGZIPOutputStream(outputStream);
}
NBTOutputStream nbtStream = new NBTOutputStream(new BufferedOutputStream(gzip));
return new FastSchematicWriter(nbtStream);
}
@Override
public boolean isFormat(File file) {
String name = file.getName().toLowerCase(Locale.ROOT);
return name.endsWith(".schem") || name.endsWith(".sponge");
}
},
/** /**
* The Schematic format used by MCEdit. * The Schematic format used by MCEdit.
*/ */
@ -77,8 +119,49 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
return name.endsWith(".schematic") || name.endsWith(".mcedit") || name.endsWith(".mce"); return name.endsWith(".schematic") || name.endsWith(".mcedit") || name.endsWith(".mce");
} }
}, },
SPONGE_SCHEMATIC("sponge", "schem") { SPONGE_SCHEMATIC("sponge", "schem") {
@Override
public String getPrimaryFileExtension() {
return "schem";
}
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream));
return new SpongeSchematicReader(nbtStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream));
return new SpongeSchematicWriter(nbtStream);
}
@Override
public boolean isFormat(File file) {
try (NBTInputStream str = new NBTInputStream(new GZIPInputStream(new FileInputStream(file)))) {
NamedTag rootTag = str.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
return false;
}
CompoundTag schematicTag = (CompoundTag) rootTag.getTag();
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Version")) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
},
BROKENENTITY("brokenentity", "legacyentity", "le", "be", "brokenentities", "legacyentities") {
@Override @Override
public String getPrimaryFileExtension() { public String getPrimaryFileExtension() {
return "schem"; return "schem";
@ -91,7 +174,9 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
} }
BufferedInputStream buffered = new BufferedInputStream(inputStream); BufferedInputStream buffered = new BufferedInputStream(inputStream);
NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered))); NBTInputStream nbtStream = new NBTInputStream(new BufferedInputStream(new GZIPInputStream(buffered)));
return new FastSchematicReader(nbtStream); FastSchematicReader reader = new FastSchematicReader(nbtStream);
reader.setBrokenEntities(true);
return reader;
} }
@Override @Override
@ -104,13 +189,14 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
gzip = new PGZIPOutputStream(outputStream); gzip = new PGZIPOutputStream(outputStream);
} }
NBTOutputStream nbtStream = new NBTOutputStream(new BufferedOutputStream(gzip)); NBTOutputStream nbtStream = new NBTOutputStream(new BufferedOutputStream(gzip));
return new FastSchematicWriter(nbtStream); FastSchematicWriter writer = new FastSchematicWriter(nbtStream);
writer.setBrokenEntities(true);
return writer;
} }
@Override @Override
public boolean isFormat(File file) { public boolean isFormat(File file) {
String name = file.getName().toLowerCase(Locale.ROOT); return false;
return name.endsWith(".schem") || name.endsWith(".sponge");
} }
}, },

View File

@ -30,10 +30,12 @@ import com.boydti.fawe.object.io.FastByteArraysInputStream;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NamedTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag; import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.InputParseException;
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
@ -45,6 +47,7 @@ import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState; 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.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.entity.EntityTypes;
@ -58,6 +61,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.OptionalInt;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function; import java.util.function.Function;
@ -70,9 +74,10 @@ public class FastSchematicReader extends NBTSchematicReader {
private static final Logger log = LoggerFactory.getLogger(FastSchematicReader.class); private static final Logger log = LoggerFactory.getLogger(FastSchematicReader.class);
private final NBTInputStream inputStream; private final NBTInputStream inputStream;
private DataFixer fixer = null; private DataFixer fixer;
private int dataVersion = -1; private int dataVersion = -1;
private int version = -1; private int version = -1;
private int faweWritten = -1;
private FastByteArrayOutputStream blocksOut; private FastByteArrayOutputStream blocksOut;
private FaweOutputStream blocks; private FaweOutputStream blocks;
@ -92,6 +97,7 @@ public class FastSchematicReader extends NBTSchematicReader {
private char[] palette; private char[] palette;
private char[] biomePalette; private char[] biomePalette;
private BlockVector3 min = BlockVector3.ZERO; private BlockVector3 min = BlockVector3.ZERO;
private boolean brokenEntities = false;
/** /**
@ -105,6 +111,10 @@ public class FastSchematicReader extends NBTSchematicReader {
this.fixer = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer(); this.fixer = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer();
} }
public void setBrokenEntities(boolean brokenEntities) {
this.brokenEntities = brokenEntities;
}
private String fix(String palettePart) { private String fix(String palettePart) {
if (fixer == null || dataVersion == -1) { if (fixer == null || dataVersion == -1) {
return palettePart; return palettePart;
@ -133,7 +143,7 @@ public class FastSchematicReader extends NBTSchematicReader {
return fixer.fixUp(DataFixer.FixTypes.BIOME, biomePalettePart, dataVersion); return fixer.fixUp(DataFixer.FixTypes.BIOME, biomePalettePart, dataVersion);
} }
public StreamDelegate createDelegate() { public StreamDelegate createVersionDelegate() {
StreamDelegate root = new StreamDelegate(); StreamDelegate root = new StreamDelegate();
StreamDelegate schematic = root.add("Schematic"); StreamDelegate schematic = root.add("Schematic");
schematic.add("DataVersion").withInt((i, v) -> dataVersion = v); schematic.add("DataVersion").withInt((i, v) -> dataVersion = v);
@ -143,6 +153,12 @@ public class FastSchematicReader extends NBTSchematicReader {
dataVersion = Constants.DATA_VERSION_MC_1_13_2; dataVersion = Constants.DATA_VERSION_MC_1_13_2;
} }
}); });
return root;
}
public StreamDelegate createDelegate() {
StreamDelegate root = new StreamDelegate();
StreamDelegate schematic = root.add("Schematic");
schematic.add("Width").withInt((i, v) -> width = v); schematic.add("Width").withInt((i, v) -> width = v);
schematic.add("Height").withInt((i, v) -> height = v); schematic.add("Height").withInt((i, v) -> height = v);
schematic.add("Length").withInt((i, v) -> length = v); schematic.add("Length").withInt((i, v) -> length = v);
@ -152,17 +168,19 @@ public class FastSchematicReader extends NBTSchematicReader {
metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v); metadata.add("WEOffsetX").withInt((i, v) -> offsetX = v);
metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v); metadata.add("WEOffsetY").withInt((i, v) -> offsetY = v);
metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v); metadata.add("WEOffsetZ").withInt((i, v) -> offsetZ = v);
metadata.add("FAWEVersion").withInt((i, v) -> faweWritten = v);
StreamDelegate paletteDelegate = schematic.add("Palette"); StreamDelegate paletteDelegate = schematic.add("Palette");
paletteDelegate.withValue((ValueReader<Map<String, Object>>) (ignore, v) -> { paletteDelegate.withValue((ValueReader<Map<String, Object>>) (ignore, v) -> {
palette = new char[v.size()]; palette = new char[v.size()];
for (Entry<String, Object> entry : v.entrySet()) { for (Entry<String, Object> entry : v.entrySet()) {
BlockState state = null; BlockState state;
try {
String palettePart = fix(entry.getKey()); String palettePart = fix(entry.getKey());
try {
state = BlockState.get(palettePart); state = BlockState.get(palettePart);
} catch (InputParseException e) { } catch (InputParseException ignored) {
e.printStackTrace(); log.warn("Invalid BlockState in palette: " + palettePart + ". Block will be replaced with air.");
state = BlockTypes.AIR.getDefaultState();
} }
int index = (int) entry.getValue(); int index = (int) entry.getValue();
palette[index] = (char) state.getOrdinal(); palette[index] = (char) state.getOrdinal();
@ -224,10 +242,14 @@ public class FastSchematicReader extends NBTSchematicReader {
@Override @Override
public Clipboard read(UUID uuid, Function<BlockVector3, Clipboard> createOutput) throws IOException { public Clipboard read(UUID uuid, Function<BlockVector3, Clipboard> createOutput) throws IOException {
StreamDelegate root = createDelegate(); StreamDelegate root = createDelegate();
StreamDelegate versions = createVersionDelegate();
inputStream.mark(Integer.MAX_VALUE);
inputStream.readNamedTagLazy(versions);
inputStream.reset();
inputStream.readNamedTagLazy(root); inputStream.readNamedTagLazy(root);
if (version != 1 && version != 2) { if (version != 1 && version != 2) {
throw new IOException("This schematic version is currently not supported"); throw new IOException("This schematic version is currently not supported (" + version + ")");
} }
if (blocks != null) { if (blocks != null) {
@ -240,9 +262,11 @@ public class FastSchematicReader extends NBTSchematicReader {
biomes = null; biomes = null;
BlockVector3 dimensions = BlockVector3.at(width, height, length); BlockVector3 dimensions = BlockVector3.at(width, height, length);
BlockVector3 origin = BlockVector3.ZERO; BlockVector3 origin;
if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) { if (offsetX != Integer.MIN_VALUE && offsetY != Integer.MIN_VALUE && offsetZ != Integer.MIN_VALUE) {
origin = BlockVector3.at(-offsetX, -offsetY, -offsetZ); origin = BlockVector3.at(-offsetX, -offsetY, -offsetZ);
} else {
origin = BlockVector3.ZERO;
} }
Clipboard clipboard = createOutput.apply(dimensions); Clipboard clipboard = createOutput.apply(dimensions);
@ -352,7 +376,7 @@ public class FastSchematicReader extends NBTSchematicReader {
if (id == null) { if (id == null) {
id = (StringTag) value.get("id"); id = (StringTag) value.get("id");
if (id == null) { if (id == null) {
return null; continue;
} }
} }
value.put("id", id); value.put("id", id);
@ -363,7 +387,26 @@ public class FastSchematicReader extends NBTSchematicReader {
ent = fixEntity(ent); ent = fixEntity(ent);
BaseEntity state = new BaseEntity(type, ent); BaseEntity state = new BaseEntity(type, ent);
Location loc = ent.getEntityLocation(clipboard); Location loc = ent.getEntityLocation(clipboard);
if (brokenEntities) {
clipboard.createEntity(loc, state); clipboard.createEntity(loc, state);
continue;
}
if (faweWritten == -1) {
int locX = loc.getBlockX();
int locY = loc.getBlockY();
int locZ = loc.getBlockZ();
BlockVector3 max = min.add(dimensions).subtract(BlockVector3.ONE);
if (locX < min.getX() || locY < min.getY() || locZ < min.getZ()
|| locX > max.getX() || locY > max.getY() || locZ > max.getZ()) {
for (Entity e : clipboard.getEntities()) {
clipboard.removeEntity(e);
}
log.error("Detected schematic entity outside clipboard region. FAWE will not load entities. "
+ "Please try loading the schematic with the format \"legacyentity\"");
break;
}
}
clipboard.createEntity(loc.setPosition(loc.subtract(min.toVector3())), state);
} else { } else {
log.debug("Invalid entity: " + id); log.debug("Invalid entity: " + id);
} }
@ -372,7 +415,7 @@ public class FastSchematicReader extends NBTSchematicReader {
clipboard.setOrigin(origin); clipboard.setOrigin(origin);
if (!min.equals(BlockVector3.ZERO)) { if (!min.equals(BlockVector3.ZERO)) {
new BlockArrayClipboard(clipboard, min); clipboard = new BlockArrayClipboard(clipboard, min);
} }
return clipboard; return clipboard;

View File

@ -19,10 +19,10 @@
package com.sk89q.worldedit.extent.clipboard.io; package com.sk89q.worldedit.extent.clipboard.io;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.jnbt.streamer.IntValueReader; import com.boydti.fawe.jnbt.streamer.IntValueReader;
import com.boydti.fawe.object.FaweOutputStream; import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.util.IOUtil; import com.boydti.fawe.util.IOUtil;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntArrayTag; import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
@ -39,6 +39,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.visitor.Order; import com.sk89q.worldedit.function.visitor.Order;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
@ -58,8 +59,6 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -72,6 +71,7 @@ public class FastSchematicWriter implements ClipboardWriter {
private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE; private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE;
private final NBTOutputStream outputStream; private final NBTOutputStream outputStream;
private boolean brokenEntities = false;
/** /**
* Create a new schematic writer. * Create a new schematic writer.
@ -83,6 +83,10 @@ public class FastSchematicWriter implements ClipboardWriter {
this.outputStream = outputStream; this.outputStream = outputStream;
} }
public void setBrokenEntities(boolean brokenEntities) {
this.brokenEntities = brokenEntities;
}
@Override @Override
public void write(Clipboard clipboard) throws IOException { public void write(Clipboard clipboard) throws IOException {
// For now always write the latest version. Maybe provide support for earlier if more appear. // For now always write the latest version. Maybe provide support for earlier if more appear.
@ -132,6 +136,7 @@ public class FastSchematicWriter implements ClipboardWriter {
out1.writeNamedTag("WEOffsetX", offset.getBlockX()); out1.writeNamedTag("WEOffsetX", offset.getBlockX());
out1.writeNamedTag("WEOffsetY", offset.getBlockY()); out1.writeNamedTag("WEOffsetY", offset.getBlockY());
out1.writeNamedTag("WEOffsetZ", offset.getBlockZ()); out1.writeNamedTag("WEOffsetZ", offset.getBlockZ());
out1.writeNamedTag("FAWEVersion", Fawe.get().getVersion().build);
}); });
ByteArrayOutputStream blocksCompressed = new ByteArrayOutputStream(); ByteArrayOutputStream blocksCompressed = new ByteArrayOutputStream();
@ -240,8 +245,12 @@ public class FastSchematicWriter implements ClipboardWriter {
// Store our location data, overwriting any // Store our location data, overwriting any
values.remove("id"); values.remove("id");
Location loc = entity.getLocation();
if (!brokenEntities) {
loc = loc.setPosition(loc.add(min.toVector3()));
}
values.put("Id", new StringTag(state.getType().getId())); values.put("Id", new StringTag(state.getType().getId()));
values.put("Pos", writeVector(entity.getLocation())); values.put("Pos", writeVector(loc));
values.put("Rotation", writeRotation(entity.getLocation())); values.put("Rotation", writeRotation(entity.getLocation()));
CompoundTag entityTag = new CompoundTag(values); CompoundTag entityTag = new CompoundTag(values);
@ -311,30 +320,6 @@ public class FastSchematicWriter implements ClipboardWriter {
} }
} }
private void writeEntities(Clipboard clipboard, NBTOutputStream schematic) throws IOException {
List<CompoundTag> entities = clipboard.getEntities().stream().map(e -> {
BaseEntity state = e.getState();
if (state == null) {
return null;
}
Map<String, Tag> values = Maps.newHashMap();
CompoundTag rawData = state.getNbtData();
if (rawData != null) {
values.putAll(rawData.getValue());
}
values.remove("id");
values.put("Id", new StringTag(state.getType().getId()));
values.put("Pos", writeVector(e.getLocation().toVector()));
values.put("Rotation", writeRotation(e.getLocation()));
return new CompoundTag(values);
}).filter(Objects::nonNull).collect(Collectors.toList());
if (entities.isEmpty()) {
return;
}
schematic.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities));
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
outputStream.close(); outputStream.close();

View File

@ -378,7 +378,7 @@ public class SchematicReader implements ClipboardReader {
BlockVector3 min = BlockVector3.at(originX, originY, originZ); BlockVector3 min = BlockVector3.at(originX, originY, originZ);
if (!min.equals(BlockVector3.ZERO)) { if (!min.equals(BlockVector3.ZERO)) {
new BlockArrayClipboard(clipboard, min); clipboard = new BlockArrayClipboard(clipboard, min);
} }
return clipboard; return clipboard;
} }

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.extent.clipboard.io; package com.sk89q.worldedit.extent.clipboard.io;
import com.boydti.fawe.Fawe;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.sk89q.jnbt.ByteArrayTag; import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
@ -114,6 +115,7 @@ public class SpongeSchematicWriter implements ClipboardWriter {
metadata.put("WEOffsetX", new IntTag(offset.getBlockX())); metadata.put("WEOffsetX", new IntTag(offset.getBlockX()));
metadata.put("WEOffsetY", new IntTag(offset.getBlockY())); metadata.put("WEOffsetY", new IntTag(offset.getBlockY()));
metadata.put("WEOffsetZ", new IntTag(offset.getBlockZ())); metadata.put("WEOffsetZ", new IntTag(offset.getBlockZ()));
metadata.put("FAWEVersion", new IntTag(Fawe.get().getVersion().build));
schematic.put("Metadata", new CompoundTag(metadata)); schematic.put("Metadata", new CompoundTag(metadata));