Merge remote-tracking branch 'upstream/master' into merge

This commit is contained in:
Jesse Boyd
2019-11-19 21:23:47 +00:00
272 changed files with 16041 additions and 6107 deletions

View File

@ -24,6 +24,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import java.io.Closeable;
import java.io.IOException;
import java.util.OptionalInt;
import java.util.UUID;
import java.util.function.Function;
@ -44,6 +45,15 @@ public interface ClipboardReader extends Closeable {
return read(UUID.randomUUID());
}
/**
* Get the DataVersion from a file (if possible).
*
* @return The data version, or empty
*/
default OptionalInt getDataVersion() {
return OptionalInt.empty();
}
default Clipboard read(UUID uuid) throws IOException {
return read(uuid, DiskOptimizedClipboard::new);
}

View File

@ -19,8 +19,9 @@
package com.sk89q.worldedit.extent.clipboard.io;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
@ -59,6 +60,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.HashSet;
import java.util.Set;
/**
* Reads schematic files that are compatible with MCEdit and other editors.
@ -176,7 +179,8 @@ public class MCEditSchematicReader extends NBTSchematicReader {
}
// Need to pull out tile entities
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
final ListTag tileEntityTag = getTag(schematic, "TileEntities", ListTag.class);
List<Tag> tileEntities = tileEntityTag == null ? new ArrayList<>() : tileEntityTag.getValue();
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
Map<BlockVector3, BlockState> blockStates = new HashMap<>();
@ -206,6 +210,11 @@ public class MCEditSchematicReader extends NBTSchematicReader {
if (values.isEmpty()) {
t = null;
}
if (values.isEmpty()) {
t = null;
} else {
t = new CompoundTag(values);
}
if (fixer != null && t != null) {
t = fixer.fixUp(DataFixer.FixTypes.BLOCK_ENTITY, t, -1);
@ -221,9 +230,8 @@ public class MCEditSchematicReader extends NBTSchematicReader {
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin);
// Don't log a torrent of errors
int failedBlockSets = 0;
Set<Integer> unknownBlocks = new HashSet<>();
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
for (int z = 0; z < length; ++z) {
@ -242,18 +250,7 @@ public class MCEditSchematicReader extends NBTSchematicReader {
log.warn("Unknown block when pasting schematic: "
+ blocks[index] + ":" + blockData[index] + ". Please report this issue.");
}
} catch (WorldEditException e) {
switch (failedBlockSets) {
case 0:
log.warn("Failed to set block on a Clipboard", e);
break;
case 1:
log.warn("Failed to set block on a Clipboard (again) -- no more messages will be logged", e);
break;
default:
}
failedBlockSets++;
} catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this
}
}
}

View File

@ -56,6 +56,8 @@ import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.entity.EntityTypes;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.extension.platform.Capability;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import org.slf4j.Logger;
@ -63,7 +65,6 @@ import org.slf4j.LoggerFactory;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -72,6 +73,8 @@ import java.util.UUID;
import java.util.function.Function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.world.storage.NBTConversions;
import java.util.OptionalInt;
/**
* Reads schematic files using the Sponge Schematic Specification.
@ -96,6 +99,7 @@ public class SpongeSchematicReader extends NBTSchematicReader {
private int offsetX, offsetY, offsetZ;
private char[] palette, biomePalette;
private BlockVector3 min = BlockVector3.ZERO;
private int schematicVersion = -1;
/**
@ -210,7 +214,58 @@ public class SpongeSchematicReader extends NBTSchematicReader {
}
@Override
public Clipboard read(UUID uuid, Function<BlockVector3, Clipboard> createOutput) throws IOException {
public Clipboard read() throws IOException {
CompoundTag schematicTag = getBaseTag();
Map<String, Tag> schematic = schematicTag.getValue();
final Platform platform = WorldEdit.getInstance().getPlatformManager()
.queryCapability(Capability.WORLD_EDITING);
int liveDataVersion = platform.getDataVersion();
if (schematicVersion == 1) {
dataVersion = 1631; // this is a relatively safe assumption unless someone imports a schematic from 1.12, e.g. sponge 7.1-
fixer = platform.getDataFixer();
return readVersion1(schematicTag);
} else if (schematicVersion == 2) {
dataVersion = requireTag(schematic, "DataVersion", IntTag.class).getValue();
if (dataVersion > liveDataVersion) {
log.warn("Schematic was made in a newer Minecraft version ({} > {}). Data may be incompatible.",
dataVersion, liveDataVersion);
} else if (dataVersion < liveDataVersion) {
fixer = platform.getDataFixer();
if (fixer != null) {
log.debug("Schematic was made in an older Minecraft version ({} < {}), will attempt DFU.",
dataVersion, liveDataVersion);
} else {
log.info("Schematic was made in an older Minecraft version ({} < {}), but DFU is not available. Data may be incompatible.",
dataVersion, liveDataVersion);
}
}
BlockArrayClipboard clip = readVersion1(schematicTag);
return readVersion2(clip, schematicTag);
}
throw new IOException("This schematic version is currently not supported");
}
@Override
public OptionalInt getDataVersion() {
try {
CompoundTag schematicTag = getBaseTag();
Map<String, Tag> schematic = schematicTag.getValue();
if (schematicVersion == 1) {
return OptionalInt.of(1631);
} else if (schematicVersion == 2) {
return OptionalInt.of(requireTag(schematic, "DataVersion", IntTag.class).getValue());
}
return OptionalInt.empty();
} catch (IOException e) {
return OptionalInt.empty();
}
}
@Override
public Clipboard getBaseTag(UUID uuid, Function<BlockVector3, Clipboard> createOutput) throws IOException {
StreamDelegate root = createDelegate();
inputStream.readNamedTagLazy(root);
if (blocks != null) blocks.close();
@ -349,6 +404,106 @@ public class SpongeSchematicReader extends NBTSchematicReader {
return clipboard;
}
private Clipboard readVersion2(BlockArrayClipboard version1, CompoundTag schematicTag) throws IOException {
Map<String, Tag> schematic = schematicTag.getValue();
if (schematic.containsKey("BiomeData")) {
readBiomes(version1, schematic);
}
if (schematic.containsKey("Entities")) {
readEntities(version1, schematic);
}
return version1;
}
private void readBiomes(BlockArrayClipboard clipboard, Map<String, Tag> schematic) throws IOException {
ByteArrayTag dataTag = requireTag(schematic, "BiomeData", ByteArrayTag.class);
IntTag maxTag = requireTag(schematic, "BiomePaletteMax", IntTag.class);
CompoundTag paletteTag = requireTag(schematic, "BiomePalette", CompoundTag.class);
Map<Integer, BiomeType> palette = new HashMap<>();
if (maxTag.getValue() != paletteTag.getValue().size()) {
throw new IOException("Biome palette size does not match expected size.");
}
for (Entry<String, Tag> palettePart : paletteTag.getValue().entrySet()) {
String key = palettePart.getKey();
if (fixer != null) {
key = fixer.fixUp(DataFixer.FixTypes.BIOME, key, dataVersion);
}
BiomeType biome = BiomeTypes.get(key);
if (biome == null) {
log.warn("Unknown biome type :" + key +
" in palette. Are you missing a mod or using a schematic made in a newer version of Minecraft?");
}
Tag idTag = palettePart.getValue();
if (!(idTag instanceof IntTag)) {
throw new IOException("Biome mapped to non-Int tag.");
}
palette.put(((IntTag) idTag).getValue(), biome);
}
int width = clipboard.getDimensions().getX();
byte[] biomes = dataTag.getValue();
int biomeIndex = 0;
int biomeJ = 0;
int bVal;
int varIntLength;
BlockVector2 min = clipboard.getMinimumPoint().toBlockVector2();
while (biomeJ < biomes.length) {
bVal = 0;
varIntLength = 0;
while (true) {
bVal |= (biomes[biomeJ] & 127) << (varIntLength++ * 7);
if (varIntLength > 5) {
throw new IOException("VarInt too big (probably corrupted data)");
}
if (((biomes[biomeJ] & 128) != 128)) {
biomeJ++;
break;
}
biomeJ++;
}
int z = biomeIndex / width;
int x = biomeIndex % width;
BiomeType type = palette.get(bVal);
clipboard.setBiome(min.add(x, z), type);
biomeIndex++;
}
}
private void readEntities(BlockArrayClipboard clipboard, Map<String, Tag> schematic) throws IOException {
List<Tag> entList = requireTag(schematic, "Entities", ListTag.class).getValue();
if (entList.isEmpty()) {
return;
}
for (Tag et : entList) {
if (!(et instanceof CompoundTag)) {
continue;
}
CompoundTag entityTag = (CompoundTag) et;
Map<String, Tag> tags = entityTag.getValue();
String id = requireTag(tags, "Id", StringTag.class).getValue();
entityTag = entityTag.createBuilder().putString("id", id).remove("Id").build();
if (fixer != null) {
entityTag = fixer.fixUp(DataFixer.FixTypes.ENTITY, entityTag, dataVersion);
}
EntityType entityType = EntityTypes.get(id);
if (entityType != null) {
Location location = NBTConversions.toLocation(clipboard,
requireTag(tags, "Pos", ListTag.class),
requireTag(tags, "Rotation", ListTag.class));
BaseEntity state = new BaseEntity(entityType, entityTag);
clipboard.createEntity(location, state);
} else {
log.warn("Unknown entity when pasting schematic: " + id);
}
}
}
@Override
public void close() throws IOException {
inputStream.close();

View File

@ -20,13 +20,16 @@
package com.sk89q.worldedit.extent.clipboard.io;
import com.boydti.fawe.jnbt.streamer.IntValueReader;
import com.google.common.collect.Maps;
import com.boydti.fawe.object.FaweOutputStream;
import com.boydti.fawe.object.clipboard.LinearClipboard;
import com.boydti.fawe.util.IOUtil;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntArrayTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.StringTag;
@ -61,7 +64,9 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.sk89q.worldedit.math.Vector3;
import java.util.Objects;
import com.sk89q.worldedit.util.Location;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@ -334,6 +339,90 @@ public class SpongeSchematicWriter implements ClipboardWriter {
schematic.writeNamedTag("Entities", new ListTag(CompoundTag.class, entities));
}
private void writeBiomes(Clipboard clipboard, Map<String, Tag> schematic) {
BlockVector3 min = clipboard.getMinimumPoint();
int width = clipboard.getRegion().getWidth();
int length = clipboard.getRegion().getLength();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(width * length);
int paletteMax = 0;
Map<String, Integer> palette = new HashMap<>();
for (int z = 0; z < length; z++) {
int z0 = min.getBlockZ() + z;
for (int x = 0; x < width; x++) {
int x0 = min.getBlockX() + x;
BlockVector2 pt = BlockVector2.at(x0, z0);
BiomeType biome = clipboard.getBiome(pt);
String biomeKey = biome.getId();
int biomeId;
if (palette.containsKey(biomeKey)) {
biomeId = palette.get(biomeKey);
} else {
biomeId = paletteMax;
palette.put(biomeKey, biomeId);
paletteMax++;
}
while ((biomeId & -128) != 0) {
buffer.write(biomeId & 127 | 128);
biomeId >>>= 7;
}
buffer.write(biomeId);
}
}
schematic.put("BiomePaletteMax", new IntTag(paletteMax));
Map<String, Tag> paletteTag = new HashMap<>();
palette.forEach((key, value) -> paletteTag.put(key, new IntTag(value)));
schematic.put("BiomePalette", new CompoundTag(paletteTag));
schematic.put("BiomeData", new ByteArrayTag(buffer.toByteArray()));
}
private void writeEntities(Clipboard clipboard, Map<String, Tag> schematic) {
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()));
final Location location = e.getLocation();
values.put("Pos", writeVector(location.toVector()));
values.put("Rotation", writeRotation(location));
return new CompoundTag(values);
}).filter(Objects::nonNull).collect(Collectors.toList());
if (entities.isEmpty()) {
return;
}
schematic.put("Entities", new ListTag(CompoundTag.class, entities));
}
private Tag writeVector(Vector3 vector) {
List<DoubleTag> list = new ArrayList<>();
list.add(new DoubleTag(vector.getX()));
list.add(new DoubleTag(vector.getY()));
list.add(new DoubleTag(vector.getZ()));
return new ListTag(DoubleTag.class, list);
}
private Tag writeRotation(Location location) {
List<FloatTag> list = new ArrayList<>();
list.add(new FloatTag(location.getYaw()));
list.add(new FloatTag(location.getPitch()));
return new ListTag(FloatTag.class, list);
}
@Override
public void close() throws IOException {
outputStream.close();