3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-11-19 09:20:08 +01: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;
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.NBTInputStream;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.input.InputParseException;
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.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.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.IOException;
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
public class FastSchematicReaderV3 implements ClipboardReader {
private final DataInputStream dataStream;
private final NBTInputStream nbtStream;
private static final Logger LOGGER = LogManagerCompat.getLogger();
public FastSchematicReaderV3(DataInputStream dataInputStream, NBTInputStream inputStream) {
this.dataStream = Objects.requireNonNull(dataInputStream, "dataInputStream");
this.nbtStream = Objects.requireNonNull(inputStream, "inputStream");
private final DataInputStream dataInputStream;
private final NBTInputStream nbtInputStream;
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
public Clipboard read() throws IOException {
final DataFixer dataFixer =
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer();
dataStream.skipNBytes(1 + 2); // 1 Byte = TAG_Compound, 2 Bytes = Short (Length of tag name = "")
dataStream.skipNBytes(1 + 2 + 9); // as above + 9 bytes = "Schematic"
public Clipboard read(final UUID uuid, final Function<BlockVector3, Clipboard> createOutput) 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"
this.dataInputStream.mark(Integer.MAX_VALUE); // allow resets to basically the start of stream (file) - just skip header
byte type;
while ((type = dataStream.readByte()) != NBTConstants.TYPE_END) {
Clipboard clipboard = null;
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()
);
}
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);
}
}
}
}
}
return null;
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
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 {
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 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)) + ")"));
return;
}
if (format == null) {
if (format == null || !format.isFormat(file)) {
format = ClipboardFormats.findByFile(file);
if (format == null) {
actor.print(Caption.of("worldedit.schematic.unknown-format", TextComponent.of(formatName)));

Datei anzeigen

@ -20,6 +20,7 @@
package com.sk89q.worldedit.extent.clipboard.io;
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.FastSchematicWriterV3;
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.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.NamedTag;
@ -41,11 +43,13 @@ import org.anarres.parallelgzip.ParallelGZIPOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -55,13 +59,21 @@ import java.util.zip.GZIPOutputStream;
/**
* A collection of supported clipboard formats.
*/
@SuppressWarnings("removal") //FAWE: suppress JNBT deprecations
public enum BuiltInClipboardFormat implements ClipboardFormat {
//FAWE start - register fast clipboard io
FAST_NEW("new_fast") { // For testing purposes
FAST_V3("fast", "fawe", "schem") { // For testing purposes
@Override
public ClipboardReader getReader(final InputStream inputStream) throws IOException {
return SPONGE_V3_SCHEMATIC.getReader(inputStream); // TODO: use FastSchematicReaderV3 when finished
public ClipboardReader getReader(InputStream inputStream) throws IOException {
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
@ -79,45 +91,41 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Override
public boolean isFormat(final File file) {
return FAST.isFormat(file);
/**
* TODO: test if this actually works
* try (final DataInputStream stream = new DataInputStream(new GZIPInputStream(Files.newInputStream(file.toPath())));
* final NBTInputStream nbt = new NBTInputStream(stream)) {
* 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) {
* return false;
* }
* stream.readShort(); // 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() == FastSchematicWriterV3.CURRENT_VERSION;
* }
* nbt.readTagPayloadLazy(type, 1);
* } while (true);
* } catch (IOException ignored) {
* }
* return false;
*/
try (final DataInputStream stream = new DataInputStream(new GZIPInputStream(Files.newInputStream(file.toPath())));
final NBTInputStream nbt = new NBTInputStream(stream)) {
if (stream.readByte() != NBTConstants.TYPE_COMPOUND) {
return false;
}
stream.skipNBytes(2); // TAG name length ("" = 0), no need to read name as no bytes are written for root tag
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() == FastSchematicWriterV3.CURRENT_VERSION;
}
nbt.readTagPayloadLazy(type, 0);
} while (true);
} catch (IOException ignored) {
}
return false;
}
@Override
public String getPrimaryFileExtension() {
return FAST.getPrimaryFileExtension();
return "schem";
}
},
FAST("fast", "fawe", "sponge", "schem") {
FAST_V2("fast.2", "fawe.2", "schem.2") {
@Override
public String getPrimaryFileExtension() {
return "schem";
@ -148,8 +156,29 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Override
public boolean isFormat(File file) {
String name = file.getName().toLowerCase(Locale.ROOT);
return name.endsWith(".schem") || name.endsWith(".sponge");
try (final DataInputStream stream = new DataInputStream(new GZIPInputStream(Files.newInputStream(file.toPath())));
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.
*/
@Deprecated
SPONGE_V2_SCHEMATIC("slow", "safe", "sponge.2") {
SPONGE_V2_SCHEMATIC("slow.2", "safe.2", "sponge.2") {
@Override
public String getPrimaryFileExtension() {
return "schem";
@ -271,8 +300,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
return true;
}
},
SPONGE_V3_SCHEMATIC("sponge.3", "sponge", "schem") {
SPONGE_V3_SCHEMATIC("sponge.3", "slow", "safe") {
@Override
public String getPrimaryFileExtension() {
return "schem";
@ -299,7 +327,7 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
return false;
}
Tag schematicTag = rootCompoundTag.getValue()
.get("Schematic");
.get("Schematic");
if (!(schematicTag instanceof CompoundTag)) {
return false;
}
@ -426,6 +454,17 @@ public enum BuiltInClipboardFormat implements ClipboardFormat {
@Deprecated
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;
BuiltInClipboardFormat(String... aliases) {

Datei anzeigen

@ -62,56 +62,64 @@ import static com.sk89q.worldedit.extent.clipboard.io.SchematicNbtUtil.requireTa
/**
* Common code shared between schematic readers.
*/
class ReaderUtil {
public class ReaderUtil { //FAWE - make public
private static final Logger LOGGER = LogManagerCompat.getLogger();
static void checkSchematicVersion(int version, CompoundTag schematicTag) throws IOException {
int schematicVersion = requireTag(schematicTag.getValue(), "Version", IntTag.class)
.getValue();
.getValue();
checkState(
version == schematicVersion,
"Schematic is not version %s, but %s", version, schematicVersion
version == schematicVersion,
"Schematic is not version %s, but %s", version, schematicVersion
);
}
static VersionedDataFixer getVersionedDataFixer(Map<String, Tag> schematic, Platform platform,
int liveDataVersion) throws IOException {
static VersionedDataFixer getVersionedDataFixer(
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;
int dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue();
if (dataVersion < 0) {
if (schematicDataVersion < 0) {
LOGGER.warn(
"Schematic has an unknown data version ({}). Data may be incompatible.",
dataVersion
"Schematic has an unknown data version ({}). Data may be incompatible.",
schematicDataVersion
);
} else if (dataVersion > liveDataVersion) {
} else if (schematicDataVersion > liveDataVersion) {
LOGGER.warn(
"Schematic was made in a newer Minecraft version ({} > {})."
+ " Data may be incompatible.",
dataVersion, liveDataVersion
"Schematic was made in a newer Minecraft version ({} > {})."
+ " Data may be incompatible.",
schematicDataVersion, liveDataVersion
);
} else if (dataVersion < liveDataVersion) {
} else if (schematicDataVersion < liveDataVersion) {
fixer = platform.getDataFixer();
if (fixer != null) {
LOGGER.debug(
"Schematic was made in an older Minecraft version ({} < {}),"
+ " will attempt DFU.",
dataVersion, liveDataVersion
"Schematic was made in an older Minecraft version ({} < {}),"
+ " will attempt DFU.",
schematicDataVersion, liveDataVersion
);
} else {
LOGGER.info(
"Schematic was made in an older Minecraft version ({} < {}),"
+ " but DFU is not available. Data may be incompatible.",
dataVersion, liveDataVersion
"Schematic was made in an older Minecraft version ({} < {}),"
+ " but DFU is not available. Data may be incompatible.",
schematicDataVersion, liveDataVersion
);
}
}
return new VersionedDataFixer(dataVersion, fixer);
return new VersionedDataFixer(schematicDataVersion, fixer);
}
//FAWE end
static Map<Integer, BlockState> decodePalette(
Map<String, Tag> paletteObject, VersionedDataFixer fixer
Map<String, Tag> paletteObject, VersionedDataFixer fixer
) throws IOException {
Map<Integer, BlockState> palette = new HashMap<>();
@ -136,15 +144,15 @@ class ReaderUtil {
}
static void initializeClipboardFromBlocks(
Clipboard clipboard, Map<Integer, BlockState> palette, byte[] blocks, ListTag tileEntities,
VersionedDataFixer fixer, boolean dataIsNested
Clipboard clipboard, Map<Integer, BlockState> palette, byte[] blocks, ListTag tileEntities,
VersionedDataFixer fixer, boolean dataIsNested
) throws IOException {
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
if (tileEntities != null) {
List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream()
.map(tag -> (CompoundTag) tag)
.map(CompoundTag::getValue)
.collect(Collectors.toList());
.map(tag -> (CompoundTag) tag)
.map(CompoundTag::getValue)
.collect(Collectors.toList());
for (Map<String, Tag> tileEntity : tileEntityTags) {
int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue();
@ -168,8 +176,8 @@ class ReaderUtil {
values.put("id", tileEntity.get("Id"));
if (fixer.isActive()) {
tileEntity = ((CompoundTag) AdventureNBTConverter.fromAdventure(fixer.fixUp(
DataFixer.FixTypes.BLOCK_ENTITY,
new CompoundTag(values).asBinaryTag()
DataFixer.FixTypes.BLOCK_ENTITY,
new CompoundTag(values).asBinaryTag()
))).getValue();
} else {
tileEntity = values;
@ -191,7 +199,7 @@ class ReaderUtil {
Map<String, Tag> tileEntity = tileEntitiesMap.get(offsetPos);
if (tileEntity != null) {
clipboard.setBlock(
offsetPos, state.toBaseBlock(new CompoundTag(tileEntity))
offsetPos, state.toBaseBlock(new CompoundTag(tileEntity))
);
} else {
clipboard.setBlock(offsetPos, state);
@ -222,8 +230,10 @@ class ReaderUtil {
return BlockVector3.at(parts[0], parts[1], parts[2]);
}
static void readEntities(BlockArrayClipboard clipboard, List<Tag> entList,
VersionedDataFixer fixer, boolean positionIsRelative) throws IOException {
static void readEntities(
BlockArrayClipboard clipboard, List<Tag> entList,
VersionedDataFixer fixer, boolean positionIsRelative
) throws IOException {
if (entList.isEmpty()) {
return;
}
@ -249,20 +259,21 @@ class ReaderUtil {
}
CompoundTag dataTag = dataTagBuilder.putString("id", id).build();
dataTag = ((CompoundTag) AdventureNBTConverter.fromAdventure(fixer.fixUp(
DataFixer.FixTypes.ENTITY,
dataTag.asBinaryTag()
DataFixer.FixTypes.ENTITY,
dataTag.asBinaryTag()
)));
EntityType entityType = EntityTypes.get(id);
if (entityType != null) {
Location location = NBTConversions.toLocation(clipboard,
requireTag(tags, "Pos", ListTag.class),
requireTag(dataTag.getValue(), "Rotation", ListTag.class)
Location location = NBTConversions.toLocation(
clipboard,
requireTag(tags, "Pos", ListTag.class),
requireTag(dataTag.getValue(), "Rotation", ListTag.class)
);
BaseEntity state = new BaseEntity(entityType, dataTag);
if (positionIsRelative) {
location = location.setPosition(
location.toVector().add(clipboard.getMinimumPoint().toVector3())
location.toVector().add(clipboard.getMinimumPoint().toVector3())
);
}
clipboard.createEntity(location, state);
@ -274,4 +285,5 @@ class ReaderUtil {
private ReaderUtil() {
}
}

Datei anzeigen

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