3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-10-02 03:40:06 +02:00

chore/feat: more work on the fast v3 reader

Dieser Commit ist enthalten in:
Pierre Maurice Schwang 2024-06-16 17:15:35 +02:00
Ursprung 500812efe1
Commit 870a96e9c0
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 37E613079F3E5BB9
7 geänderte Dateien mit 503 neuen und 101 gelöschten Zeilen

Datei anzeigen

@ -1,45 +1,326 @@
package com.fastasyncworldedit.core.extent.clipboard.io; package com.fastasyncworldedit.core.extent.clipboard.io;
import com.fastasyncworldedit.core.extent.clipboard.LinearClipboard;
import com.fastasyncworldedit.core.extent.clipboard.SimpleClipboard;
import com.fastasyncworldedit.core.internal.io.VarIntStreamIterator;
import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
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.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
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.world.DataFixer; 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 org.apache.logging.log4j.Logger;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
import java.util.OptionalInt;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
/**
* 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)
*/
@SuppressWarnings("removal") // JNBT @SuppressWarnings("removal") // JNBT
public class FastSchematicReaderV3 implements ClipboardReader { public class FastSchematicReaderV3 implements ClipboardReader {
private final DataInputStream dataStream; private static final Logger LOGGER = LogManagerCompat.getLogger();
private final NBTInputStream nbtStream;
public FastSchematicReaderV3(DataInputStream dataInputStream, NBTInputStream inputStream) { private final DataInputStream dataInputStream;
this.dataStream = Objects.requireNonNull(dataInputStream, "dataInputStream"); private final NBTInputStream nbtInputStream;
this.nbtStream = Objects.requireNonNull(inputStream, "inputStream");
private VersionedDataFixer dataFixer;
private MutableBlockVector3 dimensions = MutableBlockVector3.at(0, 0, 0);
private BlockVector3 offset;
private BlockState[] blockPalette;
private BiomeType[] biomePalette;
private int dataVersion = -1;
private boolean blocksWritten = false;
private boolean biomesWritten = false;
private boolean entitiesWritten = false;
private boolean needAdditionalIterate = true;
public FastSchematicReaderV3(
DataInputStream dataInputStream,
NBTInputStream nbtInputStream
) throws IOException {
this.dataInputStream = Objects.requireNonNull(dataInputStream, "dataInputStream");
this.nbtInputStream = Objects.requireNonNull(nbtInputStream, "nbtInputStream");
if (!dataInputStream.markSupported()) {
throw new IOException("InputStream does not support mark");
}
} }
@Override @Override
public Clipboard read() throws IOException { public Clipboard read(final UUID uuid, final Function<BlockVector3, Clipboard> createOutput) throws IOException {
final DataFixer dataFixer = dataInputStream.skipNBytes(1 + 2); // 1 Byte = TAG_Compound, 2 Bytes = Short (Length of tag name = "")
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer(); dataInputStream.skipNBytes(1 + 2 + 9); // as above + 9 bytes = "Schematic"
dataStream.skipNBytes(1 + 2); // 1 Byte = TAG_Compound, 2 Bytes = Short (Length of tag name = "") this.dataInputStream.mark(Integer.MAX_VALUE); // allow resets to basically the start of stream (file) - just skip header
dataStream.skipNBytes(1 + 2 + 9); // as above + 9 bytes = "Schematic"
byte type; Clipboard clipboard = null;
while ((type = dataStream.readByte()) != NBTConstants.TYPE_END) {
while (needAdditionalIterate) {
this.needAdditionalIterate = false;
this.dataInputStream.reset();
this.dataInputStream.mark(Integer.MAX_VALUE);
while (dataInputStream.readByte() != NBTConstants.TYPE_END) {
String tag = readTagName();
switch (tag) {
case "Version" -> this.dataInputStream.skipNBytes(4); // We know it's v3 (skip 4 byte version int)
case "DataVersion" -> {
this.dataVersion = this.dataInputStream.readInt();
this.dataFixer = new VersionedDataFixer(
this.dataVersion,
WorldEdit
.getInstance()
.getPlatformManager()
.queryCapability(Capability.WORLD_EDITING)
.getDataFixer()
);
} }
return null; case "Width", "Height", "Length" -> {
if (clipboard != null) {
continue;
}
if (tag.equals("Width")) {
this.dimensions.mutX(this.dataInputStream.readShort() & 0xFFFF);
} else if (tag.equals("Height")) {
this.dimensions.mutY(this.dataInputStream.readShort() & 0xFFFF);
} else {
this.dimensions.mutZ(this.dataInputStream.readShort() & 0xFFFF);
}
if (areDimensionsAvailable()) {
clipboard = createOutput.apply(this.dimensions);
}
}
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 "Metadata" -> this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_COMPOUND, 0); // Skip metadata
case "Blocks" -> {
if (clipboard == null) {
needAdditionalIterate = true;
} else {
this.readBlocks(clipboard);
}
}
case "Biomes" -> {
if (clipboard == null) {
needAdditionalIterate = true;
} else {
this.readBiomes(clipboard);
}
}
case "Entities" -> {
if (clipboard == null) {
needAdditionalIterate = true;
} else {
this.readEntities(clipboard);
}
}
}
}
}
if (clipboard == null) {
throw new NullPointerException("Failed to read schematic: Clipboard is null");
}
clipboard.setOrigin(this.offset);
if (clipboard instanceof SimpleClipboard simpleClipboard && !this.offset.equals(BlockVector3.ZERO)) {
clipboard = new BlockArrayClipboard(simpleClipboard, this.offset);
}
return clipboard;
}
@Override
public OptionalInt getDataVersion() {
return this.dataVersion > -1 ? OptionalInt.of(this.dataVersion) : OptionalInt.empty();
}
// TODO: BlockEntities
private void readBlocks(Clipboard target) throws IOException {
BiConsumer<Integer, Character> blockStateApplier;
if (target instanceof LinearClipboard linearClipboard) {
blockStateApplier = (dataIndex, paletteIndex) -> linearClipboard.setBlock(dataIndex, this.blockPalette[paletteIndex]);
} else {
blockStateApplier = (dataIndex, paletteIndex) -> {
int y = dataIndex / (dimensions.x() * dimensions.z());
int remainder = dataIndex - (y * dimensions.x() * dimensions.z());
int z = remainder / dimensions.x();
int x = remainder - z * dimensions.x();
target.setBlock(x, y, z, this.blockPalette[paletteIndex]);
};
}
this.blockPalette = new BlockState[BlockTypesCache.states.length];
readPalette(
() -> this.blockPalette.length == 0,
() -> this.blocksWritten,
() -> this.blocksWritten = true,
(value, index) -> {
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();
}
},
blockStateApplier,
(type, tag) -> {
if (!tag.equals("BlockEntities")) {
LOGGER.warn("Found additional tag in block palette: {}. Will skip tag.", tag);
try {
this.nbtInputStream.readTagPayloadLazy(type, 0);
} catch (IOException e) {
LOGGER.error("Failed to skip additional tag", e);
}
return;
}
// TODO: Process block entities
try {
this.nbtInputStream.readTagPayloadLazy(type, 0);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
);
}
private void readBiomes(Clipboard target) throws IOException {
BiConsumer<Integer, Character> biomeApplier;
if (target instanceof LinearClipboard linearClipboard) {
biomeApplier = (dataIndex, paletteIndex) -> linearClipboard.setBiome(dataIndex, this.biomePalette[paletteIndex]);
} else {
biomeApplier = (dataIndex, paletteIndex) -> {
int y = dataIndex / (dimensions.x() * dimensions.z());
int remainder = dataIndex - (y * dimensions.x() * dimensions.z());
int z = remainder / dimensions.x();
int x = remainder - z * dimensions.x();
target.setBiome(x, y, z, this.biomePalette[paletteIndex]);
};
}
this.biomePalette = new BiomeType[BiomeType.REGISTRY.size()];
readPalette(
() -> this.biomePalette.length == 0,
() -> this.biomesWritten,
() -> this.biomesWritten = true,
(value, index) -> {
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;
},
biomeApplier,
(type, tag) -> {
LOGGER.warn("Found additional tag in biome palette: {}. Will skip tag.", tag);
try {
this.nbtInputStream.readTagPayloadLazy(type, 0);
} catch (IOException e) {
LOGGER.error("Failed to skip additional tag", e);
}
}
);
}
private void readEntities(Clipboard target) throws IOException {
// TODO
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_LIST, 0);
}
/**
* 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(
BooleanSupplier paletteAlreadyInitialized,
BooleanSupplier dataAlreadyWritten,
Runnable firstWrite,
BiConsumer<String, Character> paletteInitializer,
BiConsumer<Integer, Character> paletteDataApplier,
BiConsumer<Byte, String> additionalTag
) throws IOException {
if (dataAlreadyWritten.getAsBoolean()) {
return;
}
boolean hasPalette = paletteAlreadyInitialized.getAsBoolean();
byte type;
String tag;
while ((type = this.dataInputStream.readByte()) != NBTConstants.TYPE_END) {
tag = readTagName();
if (tag.equals("Palette")) {
if (hasPalette) {
// Skip data, as palette already exists
this.nbtInputStream.readTagPayloadLazy(NBTConstants.TYPE_COMPOUND, 0);
} else {
// Read all palette entries
while (this.dataInputStream.readByte() != NBTConstants.TYPE_END) {
String value = this.dataInputStream.readUTF();
char index = (char) this.dataInputStream.readInt();
paletteInitializer.accept(value, index);
}
hasPalette = true;
}
continue;
}
if (tag.equals("Data")) {
// No palette or dimensions are yet available - will need to read Data next round
if (!hasPalette || !areDimensionsAvailable()) {
this.needAdditionalIterate = true;
return;
}
int length = this.dataInputStream.readInt();
// Write data into clipboard
firstWrite.run();
int i = 0;
for (var iter = new VarIntStreamIterator(this.dataInputStream, length); iter.hasNext(); i++) {
paletteDataApplier.accept(i, (char) iter.nextInt());
}
continue;
}
additionalTag.accept(type, tag);
}
}
private String readTagName() throws IOException {
return dataInputStream.readUTF();
}
private boolean areDimensionsAvailable() {
return this.dimensions.x() != 0 && this.dimensions.y() != 0 && this.dimensions.z() != 0;
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
nbtStream.close(); // closes the DataInputStream implicitly nbtInputStream.close(); // closes the DataInputStream implicitly
} }
} }

Datei anzeigen

@ -50,7 +50,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/ */
public class FastSchematicWriterV2 implements ClipboardWriter { public class FastSchematicWriterV2 implements ClipboardWriter {
private static final int CURRENT_VERSION = 2; public static final int CURRENT_VERSION = 2;
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;

Datei anzeigen

@ -0,0 +1,70 @@
package com.fastasyncworldedit.core.internal.io;
import java.io.IOException;
import java.io.InputStream;
import java.util.NoSuchElementException;
import java.util.PrimitiveIterator;
/**
* Basically {@link com.sk89q.worldedit.internal.util.VarIntIterator} but backed by {@link java.io.InputStream}
*/
public class VarIntStreamIterator implements PrimitiveIterator.OfInt {
private final InputStream parent;
private final int limit;
private int index;
private boolean hasNextInt;
private int nextInt;
public VarIntStreamIterator(final InputStream parent, int limit) {
this.parent = parent;
this.limit = limit;
}
@Override
public boolean hasNext() {
if (hasNextInt) {
return true;
}
if (index >= limit) {
return false;
}
try {
nextInt = readNextInt();
} catch (IOException e) {
throw new RuntimeException(e);
}
return hasNextInt = true;
}
@Override
public int nextInt() {
if (!hasNext()) {
throw new NoSuchElementException();
}
hasNextInt = false;
return nextInt;
}
private int readNextInt() throws IOException {
int value = 0;
for (int bitsRead = 0; ; bitsRead += 7) {
if (index >= limit) {
throw new IllegalStateException("Ran out of bytes while reading VarInt (probably corrupted data)");
}
byte next = (byte) this.parent.read();
index++;
value |= (next & 0x7F) << bitsRead;
if (bitsRead > 7 * 5) {
throw new IllegalStateException("VarInt too big (probably corrupted data)");
}
if ((next & 0x80) == 0) {
break;
}
}
return value;
}
}

Datei anzeigen

@ -396,7 +396,7 @@ public class SchematicCommands {
.isInSubDirectory(saveDir, file)) + ")")); .isInSubDirectory(saveDir, file)) + ")"));
return; return;
} }
if (format == null) { if (format == null || !format.isFormat(file)) {
format = ClipboardFormats.findByFile(file); format = ClipboardFormats.findByFile(file);
if (format == null) { if (format == null) {
actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName))); actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName)));

Datei anzeigen

@ -20,6 +20,7 @@
package com.sk89q.worldedit.extent.clipboard.io; package com.sk89q.worldedit.extent.clipboard.io;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2; import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV3;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriterV2; import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriterV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriterV3; import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriterV3;
import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStructure; import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStructure;
@ -28,6 +29,7 @@ import com.fastasyncworldedit.core.internal.io.ResettableFileInputStream;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTConstants;
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.NamedTag;
@ -41,11 +43,13 @@ import org.anarres.parallelgzip.ParallelGZIPOutputStream;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -55,13 +59,21 @@ import java.util.zip.GZIPOutputStream;
/** /**
* A collection of supported clipboard formats. * A collection of supported clipboard formats.
*/ */
@SuppressWarnings("removal") //FAWE: suppress JNBT deprecations
public enum BuiltInClipboardFormat implements ClipboardFormat { public enum BuiltInClipboardFormat implements ClipboardFormat {
//FAWE start - register fast clipboard io //FAWE start - register fast clipboard io
FAST_NEW("new_fast") { // For testing purposes FAST_V3("fast", "fawe", "schem") { // For testing purposes
@Override @Override
public ClipboardReader getReader(final InputStream inputStream) throws IOException { public ClipboardReader getReader(InputStream inputStream) throws IOException {
return SPONGE_V3_SCHEMATIC.getReader(inputStream); // TODO: use FastSchematicReaderV3 when finished if (inputStream instanceof FileInputStream fileInputStream) {
inputStream = new ResettableFileInputStream(fileInputStream);
}
BufferedInputStream buffered = new BufferedInputStream(new GZIPInputStream(new BufferedInputStream(inputStream)));
DataInputStream dataInputStream = new DataInputStream(buffered);
NBTInputStream nbtStream = new NBTInputStream(buffered);
return new FastSchematicReaderV3(dataInputStream, nbtStream);
} }
@Override @Override
@ -79,45 +91,41 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Override @Override
public boolean isFormat(final File file) { public boolean isFormat(final File file) {
return FAST.isFormat(file); try (final DataInputStream stream = new DataInputStream(new GZIPInputStream(Files.newInputStream(file.toPath())));
/** final NBTInputStream nbt = new NBTInputStream(stream)) {
* TODO: test if this actually works if (stream.readByte() != NBTConstants.TYPE_COMPOUND) {
* try (final DataInputStream stream = new DataInputStream(new GZIPInputStream(Files.newInputStream(file.toPath()))); return false;
* final NBTInputStream nbt = new NBTInputStream(stream)) { }
* if (stream.readByte() != NBTConstants.TYPE_COMPOUND) { stream.skipNBytes(2); // TAG name length ("" = 0), no need to read name as no bytes are written for root tag
* return false; if (stream.readByte() != NBTConstants.TYPE_COMPOUND) {
* } return false;
* stream.readShort(); // TAG name length ("" = 0), no need to read name as no bytes are written for root tag }
* if (stream.readByte() != NBTConstants.TYPE_COMPOUND) { stream.skipNBytes(2); // TAG name length ("Schematic" = 9)
* return false; stream.skipNBytes(9); // "Schematic"
* }
* stream.readShort(); // TAG name length ("Schematic" = 9) // We can't guarantee the specific order of nbt data, so scan and skip, if required
* stream.skipNBytes(9); // "Schematic" do {
* byte type = stream.readByte();
* // We can't guarantee the specific order of nbt data, so scan and skip, if required String name = stream.readUTF();
* do { if (type == NBTConstants.TYPE_END) {
* byte type = stream.readByte(); return false;
* String name = stream.readUTF(); }
* if (type == NBTConstants.TYPE_END) { if (type == NBTConstants.TYPE_INT && name.equals("Version")) {
* return false; return stream.readInt() == FastSchematicWriterV3.CURRENT_VERSION;
* } }
* if (type == NBTConstants.TYPE_INT && name.equals("Version")) { nbt.readTagPayloadLazy(type, 0);
* return stream.readInt() == FastSchematicWriterV3.CURRENT_VERSION; } while (true);
* } } catch (IOException ignored) {
* nbt.readTagPayloadLazy(type, 1); }
* } while (true); return false;
* } catch (IOException ignored) {
* }
* return false;
*/
} }
@Override @Override
public String getPrimaryFileExtension() { public String getPrimaryFileExtension() {
return FAST.getPrimaryFileExtension(); return "schem";
} }
}, },
FAST("fast", "fawe", "sponge", "schem") { FAST_V2("fast.2", "fawe.2", "schem.2") {
@Override @Override
public String getPrimaryFileExtension() { public String getPrimaryFileExtension() {
return "schem"; return "schem";
@ -148,8 +156,29 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Override @Override
public boolean isFormat(File file) { public boolean isFormat(File file) {
String name = file.getName().toLowerCase(Locale.ROOT); try (final DataInputStream stream = new DataInputStream(new GZIPInputStream(Files.newInputStream(file.toPath())));
return name.endsWith(".schem") || name.endsWith(".sponge"); final NBTInputStream nbt = new NBTInputStream(stream)) {
if (stream.readByte() != NBTConstants.TYPE_COMPOUND) {
return false;
}
stream.skipNBytes(2); // TAG name length ("Schematic" = 9)
stream.skipNBytes(9); // "Schematic"
// We can't guarantee the specific order of nbt data, so scan and skip, if required
do {
byte type = stream.readByte();
String name = stream.readUTF();
if (type == NBTConstants.TYPE_END) {
return false;
}
if (type == NBTConstants.TYPE_INT && name.equals("Version")) {
return stream.readInt() == FastSchematicWriterV2.CURRENT_VERSION;
}
nbt.readTagPayloadLazy(type, 0);
} while (true);
} catch (IOException ignored) {
}
return false;
} }
}, },
@ -231,7 +260,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
* Avoid using with any large schematics/clipboards for reading/writing. * Avoid using with any large schematics/clipboards for reading/writing.
*/ */
@Deprecated @Deprecated
SPONGE_V2_SCHEMATIC("slow", "safe", "sponge.2") { SPONGE_V2_SCHEMATIC("slow.2", "safe.2", "sponge.2") {
@Override @Override
public String getPrimaryFileExtension() { public String getPrimaryFileExtension() {
return "schem"; return "schem";
@ -271,8 +300,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
return true; return true;
} }
}, },
SPONGE_V3_SCHEMATIC("sponge.3", "sponge", "schem") { SPONGE_V3_SCHEMATIC("sponge.3", "slow", "safe") {
@Override @Override
public String getPrimaryFileExtension() { public String getPrimaryFileExtension() {
return "schem"; return "schem";
@ -426,6 +454,17 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Deprecated @Deprecated
public static final BuiltInClipboardFormat SPONGE_SCHEMATIC = SPONGE_V2_SCHEMATIC; public static final BuiltInClipboardFormat SPONGE_SCHEMATIC = SPONGE_V2_SCHEMATIC;
//FAWE start
/**
* For backwards compatibility, this points to the fast implementation of the Sponge Schematic Specification (Version 2)
* format. This should not be used going forwards.
*
* @deprecated Use {@link #FAST_V2} or {@link #FAST_V3}
*/
@Deprecated
public static final BuiltInClipboardFormat FAST = FAST_V2;
//FAWE end
private final ImmutableSet<String> aliases; private final ImmutableSet<String> aliases;
BuiltInClipboardFormat(String... aliases) { BuiltInClipboardFormat(String... aliases) {

Datei anzeigen

@ -62,7 +62,8 @@ import static com.sk89q.worldedit.extent.clipboard.io.SchematicNbtUtil.requireTa
/** /**
* Common code shared between schematic readers. * Common code shared between schematic readers.
*/ */
class ReaderUtil { public class ReaderUtil { //FAWE - make public
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
static void checkSchematicVersion(int version, CompoundTag schematicTag) throws IOException { static void checkSchematicVersion(int version, CompoundTag schematicTag) throws IOException {
@ -75,40 +76,47 @@ class ReaderUtil {
); );
} }
static VersionedDataFixer getVersionedDataFixer(Map<String, Tag> schematic, Platform platform, static VersionedDataFixer getVersionedDataFixer(
int liveDataVersion) throws IOException { Map<String, Tag> schematic, Platform platform,
int liveDataVersion
) throws IOException {
//FAWE - delegate to new method
return getVersionedDataFixer(requireTag(schematic, "DataVersion", IntTag.class).getValue(), platform, liveDataVersion);
}
//FAWE start - make getVersionedDataFixer without schematic compound + public
public static VersionedDataFixer getVersionedDataFixer(int schematicDataVersion, Platform platform, int liveDataVersion) {
DataFixer fixer = null; DataFixer fixer = null;
int dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue(); if (schematicDataVersion < 0) {
if (dataVersion < 0) {
LOGGER.warn( LOGGER.warn(
"Schematic has an unknown data version ({}). Data may be incompatible.", "Schematic has an unknown data version ({}). Data may be incompatible.",
dataVersion schematicDataVersion
); );
} else if (dataVersion > liveDataVersion) { } else if (schematicDataVersion > liveDataVersion) {
LOGGER.warn( LOGGER.warn(
"Schematic was made in a newer Minecraft version ({} > {})." "Schematic was made in a newer Minecraft version ({} > {})."
+ " Data may be incompatible.", + " Data may be incompatible.",
dataVersion, liveDataVersion schematicDataVersion, liveDataVersion
); );
} else if (dataVersion < liveDataVersion) { } else if (schematicDataVersion < liveDataVersion) {
fixer = platform.getDataFixer(); fixer = platform.getDataFixer();
if (fixer != null) { if (fixer != null) {
LOGGER.debug( LOGGER.debug(
"Schematic was made in an older Minecraft version ({} < {})," "Schematic was made in an older Minecraft version ({} < {}),"
+ " will attempt DFU.", + " will attempt DFU.",
dataVersion, liveDataVersion schematicDataVersion, liveDataVersion
); );
} else { } else {
LOGGER.info( LOGGER.info(
"Schematic was made in an older Minecraft version ({} < {})," "Schematic was made in an older Minecraft version ({} < {}),"
+ " but DFU is not available. Data may be incompatible.", + " but DFU is not available. Data may be incompatible.",
dataVersion, liveDataVersion schematicDataVersion, liveDataVersion
); );
} }
} }
return new VersionedDataFixer(schematicDataVersion, fixer);
return new VersionedDataFixer(dataVersion, fixer);
} }
//FAWE end
static Map<Integer, BlockState> decodePalette( static Map<Integer, BlockState> decodePalette(
Map<String, Tag> paletteObject, VersionedDataFixer fixer Map<String, Tag> paletteObject, VersionedDataFixer fixer
@ -222,8 +230,10 @@ class ReaderUtil {
return BlockVector3.at(parts[0], parts[1], parts[2]); return BlockVector3.at(parts[0], parts[1], parts[2]);
} }
static void readEntities(BlockArrayClipboard clipboard, List<Tag> entList, static void readEntities(
VersionedDataFixer fixer, boolean positionIsRelative) throws IOException { BlockArrayClipboard clipboard, List<Tag> entList,
VersionedDataFixer fixer, boolean positionIsRelative
) throws IOException {
if (entList.isEmpty()) { if (entList.isEmpty()) {
return; return;
} }
@ -255,7 +265,8 @@ class ReaderUtil {
EntityType entityType = EntityTypes.get(id); EntityType entityType = EntityTypes.get(id);
if (entityType != null) { if (entityType != null) {
Location location = NBTConversions.toLocation(clipboard, Location location = NBTConversions.toLocation(
clipboard,
requireTag(tags, "Pos", ListTag.class), requireTag(tags, "Pos", ListTag.class),
requireTag(dataTag.getValue(), "Rotation", ListTag.class) requireTag(dataTag.getValue(), "Rotation", ListTag.class)
); );
@ -274,4 +285,5 @@ class ReaderUtil {
private ReaderUtil() { private ReaderUtil() {
} }
} }

Datei anzeigen

@ -24,12 +24,12 @@ import com.sk89q.worldedit.world.DataFixer;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class VersionedDataFixer { public final class VersionedDataFixer { //FAWE - public
private final int dataVersion; private final int dataVersion;
@Nullable @Nullable
private final DataFixer fixer; private final DataFixer fixer;
VersionedDataFixer(int dataVersion, @Nullable DataFixer fixer) { public VersionedDataFixer(int dataVersion, @Nullable DataFixer fixer) { //FAWE - public
this.dataVersion = dataVersion; this.dataVersion = dataVersion;
this.fixer = fixer; this.fixer = fixer;
} }