Fix //snapshot in 1.18 and re-implement biome/entity restoration (#1620)

* Re-add "//snap" and "//snapshot"

* Place code in correct method

* Use CompoundBinaryTags in AnvilChunk18 and implement biome/entity restoration

* Address comments

* Fix biome reading

* Fix retrieval of entities from zipped snapshot world

Co-authored-by: Alex <mc.cache@web.de>
This commit is contained in:
Jordan 2022-02-24 10:33:25 +01:00 committed by GitHub
parent 49d5183685
commit fc2662e51e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 334 additions and 105 deletions

View File

@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@ -367,8 +368,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
readEntityIntoTag(mcEntity, minecraftTag);
//add Id for AbstractChangeSet to work
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
final Map<String, BinaryTag> tags = new HashMap<>();
tag.keySet().forEach(key -> tags.put(key, tag.get(key)));
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
tags.put("Id", StringBinaryTag.of(id));
return CompoundBinaryTag.from(tags);
};

View File

@ -8,6 +8,7 @@ import com.fastasyncworldedit.core.entity.LazyBaseEntity;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@ -359,8 +360,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
readEntityIntoTag(mcEntity, minecraftTag);
//add Id for AbstractChangeSet to work
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
final Map<String, BinaryTag> tags = new HashMap<>();
tag.keySet().forEach(key -> tags.put(key, tag.get(key)));
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
tags.put("Id", StringBinaryTag.of(id));
return CompoundBinaryTag.from(tags);
};

View File

@ -2,9 +2,16 @@ package com.fastasyncworldedit.core.util;
import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.BinaryTagType;
import com.sk89q.worldedit.util.nbt.BinaryTagTypes;
import com.sk89q.worldedit.util.nbt.ByteBinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.util.nbt.IntBinaryTag;
import com.sk89q.worldedit.util.nbt.ShortBinaryTag;
import com.sk89q.worldedit.world.storage.InvalidFormatException;
import java.util.HashMap;
import java.util.Map;
public class NbtUtils {
/**
@ -32,4 +39,45 @@ public class NbtUtils {
return childTagCast;
}
/**
* Get an integer from a tag.
*
* @param tag the tag to read from
* @param key the key to look for
* @return child tag
* @throws InvalidFormatException if the format of the items is invalid
* @since TODO
*/
public static int getInt(CompoundBinaryTag tag, String key) throws InvalidFormatException {
BinaryTag childTag = tag.get(key);
if (childTag == null) {
throw new InvalidFormatException("Missing a \"" + key + "\" tag");
}
BinaryTagType<?> type = childTag.type();
if (type == BinaryTagTypes.INT) {
return ((IntBinaryTag) childTag).intValue();
}
if (type == BinaryTagTypes.BYTE) {
return ((ByteBinaryTag) childTag).intValue();
}
if (type == BinaryTagTypes.SHORT) {
return ((ShortBinaryTag) childTag).intValue();
}
throw new InvalidFormatException(key + " tag is not of int, short or byte tag type.");
}
/**
* Get a mutable map of the values stored inside a {@link CompoundBinaryTag}
*
* @param tag {@link CompoundBinaryTag} to get values for
* @return Mutable map of values
* @since TODO
*/
public static Map<String, BinaryTag> getCompoundBinaryTagValues(CompoundBinaryTag tag) {
Map<String, BinaryTag> value = new HashMap<>();
tag.forEach((e) -> value.put(e.getKey(), e.getValue()));
return value;
}
}

View File

@ -20,6 +20,7 @@
package com.sk89q.worldedit.extension.factory.parser;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
@ -132,9 +133,7 @@ public class DefaultItemParser extends InputParser<BaseItem> {
if (itemNbtData == null) {
itemNbtData = otherTag;
} else {
for (String key : otherTag.keySet()) {
itemNbtData.put(key, otherTag.get(key));
}
itemNbtData.put(NbtUtils.getCompoundBinaryTagValues(otherTag));
}
} catch (IOException e) {
throw new NoMatchException(TranslatableComponent.of(

View File

@ -467,7 +467,9 @@ public final class PlatformCommandManager {
);
registerSubCommands(
"snapshot",
ImmutableList.of("snap"),
//FAWE start - add "/" aliases as well
ImmutableList.of("snap", "/snapshot", "/snap"),
//FAWE end
"Snapshot commands for restoring backups",
SnapshotCommandsRegistration.builder(),
new SnapshotCommands(worldEdit)

View File

@ -31,9 +31,7 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
/**
* The chunk format for Minecraft 1.15 and newer
*/
//FAWE start - biome and entity restore
public class AnvilChunk15 extends AnvilChunk13 {
//FAWE end
/**
* Construct the chunk with a compound tag.

View File

@ -95,7 +95,7 @@ public class AnvilChunk17 implements Chunk {
blocks = new BlockState[16][]; // initialise with default length
ListBinaryTag sections = NbtUtils.getChildTag(rootTag, "Sections", BinaryTagTypes.LIST);
ListBinaryTag sections = rootTag.getList("Sections");
for (BinaryTag rawSectionTag : sections) {
if (!(rawSectionTag instanceof CompoundBinaryTag)) {
@ -107,7 +107,7 @@ public class AnvilChunk17 implements Chunk {
continue; // Empty section.
}
int y = NbtUtils.getChildTag(sectionTag, "Y", BinaryTagTypes.BYTE).value();
int y = NbtUtils.getInt(tag, "Y");
updateSectionIndexRange(y);
// parse palette
@ -143,7 +143,7 @@ public class AnvilChunk17 implements Chunk {
}
// parse block states
long[] blockStatesSerialized = NbtUtils.getChildTag(sectionTag, "BlockStates", BinaryTagTypes.LONG_ARRAY).value();
long[] blockStatesSerialized = sectionTag.getLongArray("BlockStates");
BlockState[] chunkSectionBlocks = new BlockState[4096];
blocks[y - minSectionPosition] = chunkSectionBlocks;
@ -197,7 +197,7 @@ public class AnvilChunk17 implements Chunk {
if (rootTag.get("TileEntities") == null) {
return;
}
ListBinaryTag tags = NbtUtils.getChildTag(rootTag, "TileEntities", BinaryTagTypes.LIST);
ListBinaryTag tags = rootTag.getList("TileEntities");
for (BinaryTag tag : tags) {
if (!(tag instanceof CompoundBinaryTag)) {
@ -274,7 +274,7 @@ public class AnvilChunk17 implements Chunk {
if (rootTag.get("Biomes") == null) {
return;
}
int[] stored = NbtUtils.getChildTag(rootTag, "Biomes", BinaryTagTypes.INT_ARRAY).value();
int[] stored = rootTag.getIntArray("Biomes");
for (int i = 0; i < 1024; i++) {
biomes[i] = BiomeTypes.getLegacy(stored[i]);
}
@ -297,7 +297,7 @@ public class AnvilChunk17 implements Chunk {
if (entityTagSupplier == null || (entityTag = entityTagSupplier.get()) == null) {
return;
}
ListBinaryTag tags = NbtUtils.getChildTag(entityTag, "Entities", BinaryTagTypes.LIST);
ListBinaryTag tags = entityTag.getList("Entities");
for (BinaryTag tag : tags) {
if (!(tag instanceof CompoundBinaryTag)) {

View File

@ -19,91 +19,134 @@
package com.sk89q.worldedit.world.chunk;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.LongArrayTag;
import com.sk89q.jnbt.NBTUtils;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.BinaryTagTypes;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.util.nbt.ListBinaryTag;
import com.sk89q.worldedit.world.DataException;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.entity.EntityTypes;
import com.sk89q.worldedit.world.storage.InvalidFormatException;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* The chunk format for Minecraft 1.18 and newer
*/
public class AnvilChunk18 implements Chunk {
private final CompoundTag rootTag;
//FAWE start - CBT
private final CompoundBinaryTag rootTag;
//FAWE end
private final Int2ObjectOpenHashMap<BlockState[]> blocks;
private final int rootX;
private final int rootZ;
//FAWE start - entity and biome restore
private final int sectionCount;
private final Supplier<CompoundBinaryTag> entityTagSupplier;
private Int2ObjectOpenHashMap<BiomeType[]> biomes = null;
private List<BaseEntity> entities;
private Map<BlockVector3, CompoundBinaryTag> tileEntities;
//FAWE end
private Map<BlockVector3, Map<String, Tag>> tileEntities;
/**
* Construct the chunk with a compound tag.
*
* @param tag the tag to read
* @throws DataException on a data error
* @deprecated Use {@link AnvilChunk18#AnvilChunk18(CompoundBinaryTag, Supplier)}
*/
@Deprecated
public AnvilChunk18(CompoundTag tag) throws DataException {
//FAWE start - CBT
this(tag.asBinaryTag(), () -> null);
}
/**
* Construct the chunk with a compound tag.
*
* @param tag the tag to read
* @throws DataException on a data error
* @deprecated Use {@link AnvilChunk18#AnvilChunk18(CompoundBinaryTag, Supplier)}
* @since TODO
*/
@Deprecated
public AnvilChunk18(CompoundTag tag, Supplier<CompoundTag> entitiesTag) throws DataException {
//FAWE start - CBT
this(tag.asBinaryTag(), () -> {
CompoundTag compoundTag = entitiesTag.get();
return compoundTag == null ? null : compoundTag.asBinaryTag();
});
}
/**
* Construct the chunk with a compound tag.
*
* @param tag the tag to read
* @throws DataException on a data error
* @since TODO
*/
public AnvilChunk18(CompoundBinaryTag tag, Supplier<CompoundBinaryTag> entityTag) throws DataException {
//FAWE end
rootTag = tag;
entityTagSupplier = entityTag;
rootX = NBTUtils.getChildTag(rootTag.getValue(), "xPos", IntTag.class).getValue();
rootZ = NBTUtils.getChildTag(rootTag.getValue(), "zPos", IntTag.class).getValue();
//FAWE start - CBT
ListBinaryTag sections = rootTag.getList("sections");
this.sectionCount = sections.size();
List<Tag> sections = NBTUtils.getChildTag(rootTag.getValue(), "sections", ListTag.class).getValue();
blocks = new Int2ObjectOpenHashMap<>(sections.size());
for (Tag rawSectionTag : sections) {
if (!(rawSectionTag instanceof CompoundTag sectionTag)) {
for (BinaryTag rawSectionTag : sections) {
if (!(rawSectionTag instanceof CompoundBinaryTag sectionTag)) {
continue;
}
Object yValue = sectionTag.getValue().get("Y").getValue(); // sometimes a byte, sometimes an int
if (!(yValue instanceof Number)) {
throw new InvalidFormatException("Y is not numeric: " + yValue);
}
int y = ((Number) yValue).intValue();
int y = NbtUtils.getInt(sectionTag, "Y"); // sometimes a byte, sometimes an int
Tag rawBlockStatesTag = sectionTag.getValue().get("block_states"); // null for sections outside of the world limits
if (rawBlockStatesTag instanceof CompoundTag blockStatesTag) {
BinaryTag rawBlockStatesTag = sectionTag.get("block_states"); // null for sections outside of the world limits
if (rawBlockStatesTag instanceof CompoundBinaryTag blockStatesTag) {
// parse palette
List<CompoundTag> paletteEntries = blockStatesTag.getList("palette", CompoundTag.class);
ListBinaryTag paletteEntries = blockStatesTag.getList("palette");
int paletteSize = paletteEntries.size();
if (paletteSize == 0) {
continue;
}
BlockState[] palette = new BlockState[paletteSize];
for (int paletteEntryId = 0; paletteEntryId < paletteSize; paletteEntryId++) {
CompoundTag paletteEntry = paletteEntries.get(paletteEntryId);
CompoundBinaryTag paletteEntry = (CompoundBinaryTag) paletteEntries.get(paletteEntryId);
BlockType type = BlockTypes.get(paletteEntry.getString("Name"));
if (type == null) {
throw new InvalidFormatException("Invalid block type: " + paletteEntry.getString("Name"));
}
BlockState blockState = type.getDefaultState();
if (paletteEntry.containsKey("Properties")) {
CompoundTag properties = NBTUtils.getChildTag(paletteEntry.getValue(), "Properties", CompoundTag.class);
BinaryTag propertiesTag = paletteEntry.get("Properties");
if (propertiesTag instanceof CompoundBinaryTag properties) {
for (Property<?> property : blockState.getStates().keySet()) {
if (properties.containsKey(property.getName())) {
String value = properties.getString(property.getName());
String value;
if (!(value = properties.getString(property.getName())).isEmpty()) {
try {
blockState = getBlockStateWith(blockState, property, value);
} catch (IllegalArgumentException e) {
throw new InvalidFormatException("Invalid block state for " + blockState.getBlockType().getId() + ", " + property.getName() + ": " + value);
throw new InvalidFormatException("Invalid block state for " + blockState
.getBlockType()
.getId() + ", " + property.getName() + ": " + value);
}
}
}
@ -117,7 +160,7 @@ public class AnvilChunk18 implements Chunk {
}
// parse block states
long[] blockStatesSerialized = NBTUtils.getChildTag(blockStatesTag.getValue(), "data", LongArrayTag.class).getValue();
long[] blockStatesSerialized = blockStatesTag.getLongArray("data");
BlockState[] chunkSectionBlocks = new BlockState[16 * 16 * 16];
blocks.put(y, chunkSectionBlocks);
@ -125,6 +168,7 @@ public class AnvilChunk18 implements Chunk {
readBlockStates(palette, blockStatesSerialized, chunkSectionBlocks);
}
}
//FAWE end
}
protected void readBlockStates(BlockState[] palette, long[] blockStatesSerialized, BlockState[] chunkSectionBlocks) throws InvalidFormatException {
@ -146,26 +190,24 @@ public class AnvilChunk18 implements Chunk {
* Used to load the tile entities.
*/
private void populateTileEntities() throws DataException {
//FAWE start - CBT
tileEntities = new HashMap<>();
if (!rootTag.getValue().containsKey("block_entities")) {
if (!(rootTag.get("block_entities") instanceof ListBinaryTag tags)) {
return;
}
List<Tag> tags = NBTUtils.getChildTag(rootTag.getValue(),
"block_entities", ListTag.class).getValue();
for (Tag tag : tags) {
if (!(tag instanceof CompoundTag t)) {
for (BinaryTag tag : tags) {
if (!(tag instanceof CompoundBinaryTag t)) {
throw new InvalidFormatException("CompoundTag expected in block_entities");
}
Map<String, Tag> values = new HashMap<>(t.getValue());
int x = ((IntTag) values.get("x")).getValue();
int y = ((IntTag) values.get("y")).getValue();
int z = ((IntTag) values.get("z")).getValue();
int x = t.getInt("x");
int y = t.getInt("y");
int z = t.getInt("z");
BlockVector3 vec = BlockVector3.at(x, y, z);
tileEntities.put(vec, values);
tileEntities.put(vec, t);
}
//FAWE end
}
/**
@ -178,24 +220,21 @@ public class AnvilChunk18 implements Chunk {
* @throws DataException thrown if there is a data error
*/
@Nullable
private CompoundTag getBlockTileEntity(BlockVector3 position) throws DataException {
private CompoundBinaryTag getBlockTileEntity(BlockVector3 position) throws DataException {
//FAWE start - CBT
if (tileEntities == null) {
populateTileEntities();
}
Map<String, Tag> values = tileEntities.get(position);
if (values == null) {
return null;
}
return new CompoundTag(values);
return tileEntities.get(position);
//FAWE end
}
@Override
public BaseBlock getBlock(BlockVector3 position) throws DataException {
int x = position.getX() - rootX * 16;
int x = position.getX() & 15;
int y = position.getY();
int z = position.getZ() - rootZ * 16;
int z = position.getZ() & 15;
int section = y >> 4;
int yIndex = y & 0x0F;
@ -206,7 +245,7 @@ public class AnvilChunk18 implements Chunk {
}
BlockState state = sectionBlocks[sectionBlocks.length == 1 ? 0 : ((yIndex << 8) | (z << 4) | x)];
CompoundTag tileEntity = getBlockTileEntity(position);
CompoundBinaryTag tileEntity = getBlockTileEntity(position);
if (tileEntity != null) {
return state.toBaseBlock(tileEntity);
@ -215,4 +254,110 @@ public class AnvilChunk18 implements Chunk {
return state.toBaseBlock();
}
@Override
public BiomeType getBiome(final BlockVector3 position) throws DataException {
if (biomes == null) {
populateBiomes();
}
int x = (position.getX() & 15) >> 2;
int y = (position.getY() & 15) >> 2;
int z = (position.getZ() & 15) >> 2;
int section = position.getY() >> 4;
BiomeType[] sectionBiomes = biomes.get(section);
if (sectionBiomes.length == 1) {
return sectionBiomes[0];
}
return biomes.get(section)[y << 4 | z << 2 | x];
}
private void populateBiomes() throws DataException {
biomes = new Int2ObjectOpenHashMap<>(sectionCount);
ListBinaryTag sections = rootTag.getList("sections");
for (BinaryTag rawSectionTag : sections) {
if (!(rawSectionTag instanceof CompoundBinaryTag sectionTag)) {
continue;
}
int y = NbtUtils.getInt(sectionTag, "Y"); // sometimes a byte, sometimes an int
BinaryTag rawBlockStatesTag = sectionTag.get("biomes"); // null for sections outside of the world limits
if (rawBlockStatesTag instanceof CompoundBinaryTag biomeTypesTag) {
// parse palette
ListBinaryTag paletteEntries = biomeTypesTag.getList("palette");
int paletteSize = paletteEntries.size();
if (paletteSize == 0) {
continue;
}
BiomeType[] palette = new BiomeType[paletteSize];
for (int paletteEntryId = 0; paletteEntryId < paletteSize; paletteEntryId++) {
String paletteEntry = paletteEntries.getString(paletteEntryId);
BiomeType type = BiomeType.REGISTRY.get(paletteEntry);
if (type == null) {
throw new InvalidFormatException("Invalid biome type: " + paletteEntry);
}
palette[paletteEntryId] = type;
}
if (paletteSize == 1) {
// the same block everywhere
biomes.put(y, palette);
continue;
}
// parse block states
long[] biomesSerialized = biomeTypesTag.getLongArray("data");
if (biomesSerialized.length == 0) {
throw new InvalidFormatException("Biome data not present.");
}
BiomeType[] chunkSectionBiomes = new BiomeType[64];
biomes.put(y, chunkSectionBiomes);
readBiomes(palette, biomesSerialized, chunkSectionBiomes);
}
}
}
protected void readBiomes(BiomeType[] palette, long[] biomesSerialized, BiomeType[] chunkSectionBiomes) throws
InvalidFormatException {
PackedIntArrayReader reader = new PackedIntArrayReader(biomesSerialized, 64);
for (int biomePos = 0; biomePos < chunkSectionBiomes.length; biomePos++) {
int index = reader.get(biomePos);
if (index >= palette.length) {
throw new InvalidFormatException("Invalid biome table entry: " + index);
}
chunkSectionBiomes[biomePos] = palette[index];
}
}
@Override
public List<BaseEntity> getEntities() throws DataException {
if (entities == null) {
populateEntities();
}
return entities;
}
/**
* Used to load the biomes.
*/
private void populateEntities() throws DataException {
entities = new ArrayList<>();
CompoundBinaryTag entityTag;
if (entityTagSupplier == null || (entityTag = entityTagSupplier.get()) == null) {
return;
}
ListBinaryTag tags = NbtUtils.getChildTag(entityTag, "Entities", BinaryTagTypes.LIST);
for (BinaryTag tag : tags) {
if (!(tag instanceof CompoundBinaryTag t)) {
throw new InvalidFormatException("CompoundTag expected in Entities");
}
entities.add(new BaseEntity(EntityTypes.get(t.getString("id")), LazyReference.computed(t)));
}
}
}

View File

@ -32,28 +32,56 @@ public class PackedIntArrayReader {
}
}
private static final int SIZE = 4096;
private final long[] data;
private final int elementBits;
private final long maxValue;
private final int elementsPerLong;
private final int factor;
//FAWE start - allow other sizes of data to be parsed
private final int storedSize;
//FAWE end
/**
* Create a new PackedIntArrayReader instance based on an array of longs containing 4096 integers.
*
* @param data long array containing data
*/
public PackedIntArrayReader(long[] data) {
this.data = data;
this.storedSize = 4096;
this.elementBits = data.length * 64 / 4096;
this.maxValue = (1L << elementBits) - 1L;
this.elementsPerLong = 64 / elementBits;
this.factor = FACTORS[elementsPerLong - 1];
int j = (SIZE + this.elementsPerLong - 1) / this.elementsPerLong;
int j = (storedSize + this.elementsPerLong - 1) / this.elementsPerLong;
if (j != data.length) {
throw new IllegalStateException("Invalid packed-int array provided, should be of length " + j);
}
}
//FAWE start - allow other sizes of data to be parsed
/**
* Create a new PackedIntArrayReader instance based on an array of longs containing a certain number of integers.
*
* @param data long array containing data
* @param storedSize the amount of integers stored in the long array
*/
public PackedIntArrayReader(long[] data, int storedSize) {
this.data = data;
this.storedSize = storedSize;
this.elementBits = data.length * 64 / storedSize;
this.maxValue = (1L << elementBits) - 1L;
this.elementsPerLong = 64 / elementBits;
this.factor = FACTORS[elementsPerLong - 1];
int j = (storedSize + this.elementsPerLong - 1) / this.elementsPerLong;
if (j != data.length) {
throw new IllegalStateException("Invalid packed-int array provided, should be of length " + j);
}
}
//FAWE end
public int get(int index) {
checkElementIndex(index, SIZE);
checkElementIndex(index, storedSize);
int i = this.adjustIndex(index);
long l = this.data[i];
int j = (index - i * this.elementsPerLong) * this.elementBits;

View File

@ -73,28 +73,8 @@ public class ChunkStoreHelper {
* @throws DataException if the rootTag is not valid chunk data
*/
public static Chunk getChunk(CompoundTag rootTag) throws DataException {
int dataVersion = rootTag.getInt("DataVersion");
if (dataVersion == 0) {
dataVersion = -1;
}
final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING);
final int currentDataVersion = platform.getDataVersion();
if ((dataVersion > 0 || hasLevelSections(rootTag)) && dataVersion < currentDataVersion) { // only fix up MCA format, DFU doesn't support MCR chunks
final DataFixer dataFixer = platform.getDataFixer();
if (dataFixer != null) {
rootTag = (CompoundTag) AdventureNBTConverter.fromAdventure(dataFixer.fixUp(DataFixer.FixTypes.CHUNK,
rootTag.asBinaryTag(), dataVersion));
dataVersion = currentDataVersion;
}
}
if (dataVersion >= Constants.DATA_VERSION_MC_1_18) {
return new AnvilChunk18(rootTag);
}
//FAWE start - biome and entity restore
return getChunk(rootTag, () -> null);
//FAWE end
}
/**
@ -105,9 +85,32 @@ public class ChunkStoreHelper {
* location
* @return a Chunk implementation
* @throws DataException if the rootTag is not valid chunk data
* @since TODO
*/
public static Chunk getChunk(CompoundTag rootTag, Supplier<CompoundTag> entitiesTag) throws DataException {
//FAWE end
int dataVersion = rootTag.getInt("DataVersion");
if (dataVersion == 0) {
dataVersion = -1;
}
final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING);
final int currentDataVersion = platform.getDataVersion();
if ((dataVersion > 0 || hasLevelSections(rootTag)) && dataVersion < currentDataVersion) { // only fix up MCA format, DFU doesn't support MCR chunks
final DataFixer dataFixer = platform.getDataFixer();
if (dataFixer != null) {
//FAWE start - CBT
rootTag = (CompoundTag) AdventureNBTConverter.fromAdventure(dataFixer.fixUp(DataFixer.FixTypes.CHUNK,
rootTag.asBinaryTag(), dataVersion));
//FAWE end
dataVersion = currentDataVersion;
}
}
if (dataVersion >= Constants.DATA_VERSION_MC_1_18) {
return new AnvilChunk18(rootTag, entitiesTag);
}
Map<String, Tag> children = rootTag.getValue();
CompoundTag tag = null;
@ -130,10 +133,6 @@ public class ChunkStoreHelper {
throw new ChunkStoreException("Missing root 'Level' tag");
}
int dataVersion = rootTag.getInt("DataVersion");
if (dataVersion == 0) {
dataVersion = -1;
}
//FAWE start - biome and entity restore
if (dataVersion >= Constants.DATA_VERSION_MC_1_17) {
return new AnvilChunk17(tag, entitiesTag);

View File

@ -80,14 +80,13 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore {
protected InputStream getInputStream(String name, String worldName, @Nullable String folderOverride) throws IOException,
DataException {
// Detect subfolder for the world's files
if (folderOverride != null) {
if (!folderOverride.isEmpty()) {
name = folderOverride + "/" + name;
}
} else if (folder != null) {
//FAWE end
if (folder != null) {
if (!folder.isEmpty()) {
//FAWE end
name = folder + "/" + name;
if (folderOverride != null) {
name = name.replace("region", folderOverride);
}
}
} else {
Pattern pattern = Pattern.compile(".*\\.mc[ra]$");
@ -105,6 +104,12 @@ public class TrueZipMcRegionChunkStore extends McRegionChunkStore {
endIndex = entryName.lastIndexOf('\\');
}
folder = entryName.substring(0, endIndex);
//FAWE start - biome and entity restore
if (folderOverride != null && folder.endsWith(folderOverride)) {
name = folder + "/" + name;
break;
}
//FAWE end
if (folder.endsWith("poi") || folder.endsWith("entities")) {
continue;
}

View File

@ -78,14 +78,13 @@ public class ZippedMcRegionChunkStore extends McRegionChunkStore {
protected InputStream getInputStream(String name, String worldName, @Nullable String folderOverride) throws IOException,
DataException {
// Detect subfolder for the world's files
if (folderOverride != null) {
if (!folderOverride.isEmpty()) {
name = folderOverride + "/" + name;
}
} else if (folder != null) {
if (folder != null) {
if (!folder.isEmpty()) {
//FAWE end
name = folder + "/" + name;
if (folderOverride != null) {
name = name.replace("region", folderOverride);
}
}
} else {
Pattern pattern = Pattern.compile(".*\\.mc[ra]$");
@ -101,6 +100,12 @@ public class ZippedMcRegionChunkStore extends McRegionChunkStore {
endIndex = entryName.lastIndexOf('\\');
}
folder = entryName.substring(0, endIndex);
//FAWE start - biome and entity restore
if (folderOverride != null && folder.endsWith(folderOverride)) {
name = folder + "/" + name;
break;
}
//FAWE end
if (folder.endsWith("poi") || folder.endsWith("entities")) {
continue;
}