Work in progress on supporting maps from non-default platforms

master
Captain Chaos 2019-03-31 20:08:32 +02:00
parent ea4ada6676
commit 3e3ca027fd
13 changed files with 516 additions and 310 deletions

View File

@ -0,0 +1,2 @@
@echo off
mvn clean gpg:sign deploy -DaltDeploymentRepository=ossrh-snapshots::default::https://oss.sonatype.org/content/repositories/snapshots

View File

@ -6,7 +6,7 @@
<groupId>org.pepsoft.worldpainter</groupId>
<artifactId>PluginParent</artifactId>
<version>1.2.3</version>
<version>1.3.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>PluginParent</name>
@ -40,7 +40,7 @@
<dependency>
<groupId>org.pepsoft.worldpainter</groupId>
<artifactId>WPGUI</artifactId>
<version>2.5.9</version>
<version>2.5.13-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>

View File

@ -6,6 +6,35 @@ package org.pepsoft.minecraft;
* <p>Created by Pepijn on 14-12-2016.
*/
public interface ChunkStore extends ChunkProvider {
/**
* Return a total count of all the chunks currently in the store.
*
* @return The count of all the chunks currently in the store.
*/
long getChunkCount();
/**
* Visit all known chunks. Note that the order is undefined, allowing the
* provider to use as efficient an implementation as is possible. The
* provided chunks <em>may</em> be read-only.
*
* @param visitor The visitor to invoke for each chunk.
* @return {@code true} if all chunks were visited; {@code false} if not all
* chunks may have been visited because the visitor returned {@code false}.
*/
boolean visitChunks(ChunkVisitor visitor);
/**
* Visit all known chunks for editing. Note that the order is undefined,
* allowing the provider to use as efficient an implementation as is
* possible. The provided chunks are guaranteed not to be read-only.
*
* @param visitor The visitor to invoke for each chunk.
* @return {@code true} if all chunks were visited; {@code false} if not all
* chunks may have been visited because the visitor returned {@code false}.
*/
boolean visitChunksForEditing(ChunkVisitor visitor);
/**
* Save a chunk to the store. The chunk is only guaranteerd to have been
* written to disk if this operation was performed inside a
@ -30,4 +59,18 @@ public interface ChunkStore extends ChunkProvider {
* {@link #doInTransaction(Runnable)}.
*/
void flush();
/**
* A visitor of chunks.
*/
@FunctionalInterface interface ChunkVisitor {
/**
* Visit a chunk.
*
* @param chunk The chunk to be visited.
* @return {@code true} if more chunks should be visited, or
* {@code false} if no more chunks need to be visited.
*/
boolean visitChunk(Chunk chunk);
}
}

View File

@ -276,6 +276,16 @@ public final class RegionFile implements AutoCloseable {
return readOnly;
}
public int getChunkCount() {
int count = 0;
for (int offset: offsets) {
if (offset != 0) {
count++;
}
}
return count;
}
@Override
public String toString() {
return fileName.getPath();

View File

@ -0,0 +1,108 @@
package org.pepsoft.worldpainter;
import com.google.common.collect.ImmutableList;
import org.pepsoft.minecraft.*;
import org.pepsoft.minecraft.mapexplorer.JavaMapRecognizer;
import org.pepsoft.worldpainter.exporting.*;
import org.pepsoft.worldpainter.mapexplorer.MapRecognizer;
import org.pepsoft.worldpainter.plugins.AbstractPlugin;
import org.pepsoft.worldpainter.plugins.BlockBasedPlatformProvider;
import org.pepsoft.worldpainter.util.MinecraftUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.primitives.Ints.toArray;
import static org.pepsoft.worldpainter.Constants.*;
import static org.pepsoft.worldpainter.DefaultPlugin.JAVA_ANVIL;
import static org.pepsoft.worldpainter.DefaultPlugin.JAVA_MCREGION;
import static org.pepsoft.worldpainter.util.MinecraftUtil.getRegionDir;
/**
* Created by Pepijn on 9-3-2017.
*/
public class DefaultPlatformProvider extends AbstractPlugin implements BlockBasedPlatformProvider {
public DefaultPlatformProvider() {
super("DefaultPlatforms", Version.VERSION);
}
@Override
public List<Platform> getKeys() {
return PLATFORMS;
}
@Override
public int[] getDimensions(Platform platform, File worldDir) {
ensurePlatformSupported(platform);
List<Integer> dimensions = new ArrayList<>();
for (int dim: new int[]{DIM_NORMAL, DIM_NETHER, DIM_END}) {
if (containsFiles(getRegionDir(worldDir, dim))) {
dimensions.add(dim);
}
}
return toArray(dimensions);
}
@Override
public Chunk createChunk(Platform platform, int x, int z, int maxHeight) {
if (platform.equals(JAVA_MCREGION)) {
return new ChunkImpl(x, z, maxHeight);
} else if (platform.equals(JAVA_ANVIL)) {
return new ChunkImpl2(x, z, maxHeight);
} else {
throw new IllegalArgumentException("Platform " + platform + " not supported");
}
}
@Override
public ChunkStore getChunkStore(Platform platform, File worldDir, int dimension) {
ensurePlatformSupported(platform);
Level level;
File levelDatFile = new File(worldDir, "level.dat");
try {
level = Level.load(levelDatFile);
} catch (IOException e) {
throw new RuntimeException("I/O error while trying to read level.dat", e);
}
return new JavaChunkStore(platform, getRegionDir(worldDir, dimension), false, null, level.getMaxHeight());
}
@Override
public WorldExporter getExporter(World2 world) {
Platform platform = world.getPlatform();
ensurePlatformSupported(platform);
return new JavaWorldExporter(world);
}
@Override
public File getDefaultExportDir(Platform platform) {
File minecraftDir = MinecraftUtil.findMinecraftDir();
return (minecraftDir != null) ? new File(minecraftDir, "saves") : null;
}
@Override
public PostProcessor getPostProcessor(Platform platform) {
ensurePlatformSupported(platform);
return new JavaPostProcessor();
}
@Override
public MapRecognizer getMapRecognizer() {
return new JavaMapRecognizer();
}
private void ensurePlatformSupported(Platform platform) {
if (! PLATFORMS.contains(platform)) {
throw new IllegalArgumentException("Platform " + platform + " not supported");
}
}
@SuppressWarnings("ConstantConditions") // Yes, we just checked that
private boolean containsFiles(File dir) {
return dir.isDirectory() && (dir.listFiles().length > 0);
}
private static final List<Platform> PLATFORMS = ImmutableList.of(JAVA_ANVIL, JAVA_MCREGION);
}

View File

@ -973,8 +973,4 @@ public abstract class AbstractWorldExporter implements WorldExporter {
*/
public List<Fixup> fixups;
}
interface RegionVisitor {
void visitRegion(WorldRegion region);
}
}

View File

@ -19,6 +19,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Pattern;
import static org.pepsoft.minecraft.Block.BLOCK_TYPE_NAMES;
import static org.pepsoft.minecraft.Constants.*;
@ -40,6 +41,30 @@ public class JavaChunkStore implements ChunkStore {
this.maxHeight = maxHeight;
}
@Override
public long getChunkCount() {
long[] chunkCount = {0L};
try {
visitRegions(region -> {
chunkCount[0] += region.getChunkCount();
return true;
}, true);
} catch (IOException e) {
throw new RuntimeException("I/O error while visiting regions of " + regionDir, e);
}
return chunkCount[0];
}
@Override
public boolean visitChunks(ChunkVisitor visitor) {
return visitChunks(visitor, true);
}
@Override
public boolean visitChunksForEditing(ChunkVisitor visitor) {
return visitChunks(visitor, false);
}
@Override
public void saveChunk(Chunk chunk) {
// chunksSaved++;
@ -215,7 +240,58 @@ public class JavaChunkStore implements ChunkStore {
return regionFile;
}
// private void updateStatistics() {
private RegionFile openRegionFile(Point regionCoords) throws IOException {
File file = new File(regionDir, "r." + regionCoords.x + "." + regionCoords.y + (platform.equals(JAVA_MCREGION) ? ".mcr" : ".mca"));
return file.exists() ? new RegionFile(file) : null;
}
private RegionFile openOrCreateRegionFile(Point regionCoords) throws IOException {
File file = new File(regionDir, "r." + regionCoords.x + "." + regionCoords.y + (platform.equals(JAVA_MCREGION) ? ".mcr" : ".mca"));
return new RegionFile(file);
}
@FunctionalInterface interface RegionVisitor {
boolean visitRegion(RegionFile region) throws IOException;
}
private boolean visitRegions(RegionVisitor visitor, boolean readOnly) throws IOException {
final Pattern regionFilePattern = (platform == JAVA_MCREGION)
? Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mcr")
: Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mca");
for (File file: regionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches())) {
try (RegionFile regionFile = new RegionFile(file, readOnly)) {
if (! visitor.visitRegion(regionFile)) {
return false;
}
}
}
return true;
}
private boolean visitChunks(ChunkVisitor visitor, boolean readOnly) {
try {
return visitRegions(region -> {
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
if (region.containsChunk(x, z)) {
try (NBTInputStream in = new NBTInputStream(region.getChunkDataInputStream(x & 31, z & 31))) {
CompoundTag tag = (CompoundTag) in.readTag();
Chunk chunk = platform.equals(JAVA_MCREGION) ? new ChunkImpl(tag, maxHeight, readOnly) : new ChunkImpl2(tag, maxHeight, readOnly);
if (! visitor.visitChunk(chunk)) {
return false;
}
}
}
}
}
return true;
}, readOnly);
} catch (IOException e) {
throw new RuntimeException("I/O error while visiting regions of " + regionDir, e);
}
}
// private void updateStatistics() {
// long now = System.currentTimeMillis();
// if ((now - lastStatisticsTimestamp) > 5000) {
// float elapsed = (now - lastStatisticsTimestamp) / 1000f;

View File

@ -4,7 +4,9 @@
*/
package org.pepsoft.worldpainter.importing;
import org.jnbt.*;
import org.jnbt.CompoundTag;
import org.jnbt.NBTInputStream;
import org.jnbt.Tag;
import org.pepsoft.minecraft.*;
import org.pepsoft.util.ProgressReceiver;
import org.pepsoft.util.SubProgressReceiver;
@ -14,7 +16,6 @@ import org.pepsoft.worldpainter.history.HistoryEntry;
import org.pepsoft.worldpainter.layers.*;
import org.pepsoft.worldpainter.layers.exporters.FrostExporter.FrostSettings;
import org.pepsoft.worldpainter.layers.exporters.ResourcesExporter.ResourcesExporterSettings;
import org.pepsoft.worldpainter.plugins.PlatformManager;
import org.pepsoft.worldpainter.themes.SimpleTheme;
import org.pepsoft.worldpainter.vo.EventVO;
@ -22,28 +23,32 @@ import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.Pattern;
import static org.pepsoft.minecraft.Constants.*;
import static org.pepsoft.minecraft.Material.*;
import static org.pepsoft.worldpainter.Constants.*;
import static org.pepsoft.worldpainter.DefaultPlugin.*;
import static org.pepsoft.worldpainter.DefaultPlugin.JAVA_ANVIL;
import static org.pepsoft.worldpainter.DefaultPlugin.JAVA_MCREGION;
import static org.pepsoft.worldpainter.biomeschemes.Minecraft1_13Biomes.BIOME_NAMES;
import static org.pepsoft.worldpainter.biomeschemes.Minecraft1_13Biomes.HIGHEST_BIOME_ID;
/**
* An importer of Minecraft-like maps (with Minecraft-compatible
* {@code level.dat} files and based on {@link BlockBasedPlatformProvider}s.
*
* @author pepijn
*/
public class JavaMapImporter extends MapImporter {
public JavaMapImporter(TileFactory tileFactory, File levelDatFile, boolean populateNewChunks, Set<Point> chunksToSkip, ReadOnlyOption readOnlyOption, Set<Integer> dimensionsToImport) {
public JavaMapImporter(Platform platform, TileFactory tileFactory, File levelDatFile, boolean populateNewChunks, Set<Point> chunksToSkip, ReadOnlyOption readOnlyOption, Set<Integer> dimensionsToImport) {
if ((tileFactory == null) || (levelDatFile == null) || (readOnlyOption == null) || (dimensionsToImport == null)) {
throw new NullPointerException();
}
if (! levelDatFile.isFile()) {
throw new IllegalArgumentException(levelDatFile + " does not exist or is not a regular file");
}
this.platform = platform;
this.tileFactory = tileFactory;
this.levelDatFile = levelDatFile;
this.populateNewChunks = populateNewChunks;
@ -51,7 +56,7 @@ public class JavaMapImporter extends MapImporter {
this.readOnlyOption = readOnlyOption;
this.dimensionsToImport = dimensionsToImport;
}
public World2 doImport(ProgressReceiver progressReceiver) throws IOException, ProgressReceiver.OperationCancelled {
long start = System.currentTimeMillis();
@ -241,108 +246,63 @@ public class JavaMapImporter extends MapImporter {
}
final int maxHeight = dimension.getMaxHeight();
final int maxY = maxHeight - 1;
final JavaPlatformProvider platformProvider = (JavaPlatformProvider) PlatformManager.getInstance().getPlatformProvider(platform);
final File[] regionFiles = platformProvider.getRegionFiles(platform, regionDir);
if ((regionFiles == null) || (regionFiles.length == 0)) {
throw new RuntimeException("The " + dimension.getName() + " dimension of this map has no region files!");
}
final Set<Point> newChunks = new HashSet<>();
final Set<String> manMadeBlockTypes = new HashSet<>();
final Set<Integer> unknownBiomes = new HashSet<>();
final boolean importBiomes = dimension.getDim() == DIM_NORMAL;
final int total = regionFiles.length * 1024;
int count = 0;
final ChunkStore chunkStore = PlatformManager.getInstance().getChunkStore(platform, regionDir, dimension.getDim());
final long total = chunkStore.getChunkCount();
final AtomicInteger count = new AtomicInteger();
final StringBuilder reportBuilder = new StringBuilder();
for (File file: regionFiles) {
if (! chunkStore.visitChunks(chunk -> {
try {
try (RegionFile regionFile = new RegionFile(file, true)) {
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
if (progressReceiver != null) {
progressReceiver.setProgress((float) count / total);
count++;
}
final Point chunkCoords = new Point((regionFile.getX() << 5) | x, (regionFile.getZ() << 5) | z);
if ((chunksToSkip != null) && chunksToSkip.contains(chunkCoords)) {
continue;
}
if (regionFile.containsChunk(x, z)) {
final Chunk chunk;
try {
final InputStream chunkData = regionFile.getChunkDataInputStream(x, z);
if (chunkData == null) {
// This should never happen, since we checked
// with isChunkPresent(), but in practice it
// does. Perhaps corrupted data?
reportBuilder.append("Missing chunk data for chunk " + x + ", " + z + " in " + file + "; skipping chunk" + EOL);
logger.warn("Missing chunk data for chunk " + x + ", " + z + " in " + file + "; skipping chunk");
continue;
}
try (NBTInputStream in = new NBTInputStream(chunkData)) {
chunk = platformProvider.createChunk(platform, in.readTag(), maxHeight);
}
} catch (IOException e) {
reportBuilder.append("I/O error while reading chunk " + x + ", " + z + " from file " + file + " (message: \"" + e.getMessage() + "\"); skipping chunk" + EOL);
logger.error("I/O error while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk", e);
continue;
} catch (IllegalArgumentException e) {
reportBuilder.append("Illegal argument exception while reading chunk " + x + ", " + z + " from file " + file + " (message: \"" + e.getMessage() + "\"); skipping chunk" + EOL);
logger.error("Illegal argument exception while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk", e);
continue;
} catch (NegativeArraySizeException e) {
reportBuilder.append("Negative array size exception while reading chunk " + x + ", " + z + " from file " + file + " (message: \"" + e.getMessage() + "\"); skipping chunk" + EOL);
logger.error("Negative array size exception while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk", e);
continue;
} catch (ClassCastException e) {
reportBuilder.append("Class cast exception while reading chunk " + x + ", " + z + " from file " + file + " (message: \"" + e.getMessage() + "\"); skipping chunk" + EOL);
logger.error("Class cast exception while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk", e);
continue;
}
if (progressReceiver != null) {
progressReceiver.setProgress((float) count.getAndIncrement() / total);
}
final Point chunkCoords = chunk.getCoords();
if ((chunksToSkip != null) && chunksToSkip.contains(chunkCoords)) {
return true;
}
if ((chunk instanceof MC113AnvilChunk) && (((MC113AnvilChunk) chunk).getStatus() == MC113AnvilChunk.Status.EMPTY)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping \"empty\" chunk at {},{}", chunk.getxPos(), chunk.getzPos());
}
continue;
}
final int chunkX = chunkCoords.x;
final int chunkZ = chunkCoords.y;
final Point tileCoords = new Point(chunkX >> 3, chunkZ >> 3);
Tile tile = dimension.getTile(tileCoords);
if (tile == null) {
tile = dimension.getTileFactory().createTile(tileCoords.x, tileCoords.y);
for (int xx = 0; xx < 8; xx++) {
for (int yy = 0; yy < 8; yy++) {
newChunks.add(new Point((tileCoords.x << TILE_SIZE_BITS) | (xx << 4), (tileCoords.y << TILE_SIZE_BITS) | (yy << 4)));
}
}
dimension.addTile(tile);
}
newChunks.remove(new Point(chunkX << 4, chunkZ << 4));
final Point tileCoords = new Point(chunk.getxPos() >> 3, chunk.getzPos() >> 3);
Tile tile = dimension.getTile(tileCoords);
if (tile == null) {
tile = dimension.getTileFactory().createTile(tileCoords.x, tileCoords.y);
for (int xx = 0; xx < 8; xx++) {
for (int yy = 0; yy < 8; yy++) {
newChunks.add(new Point((tileCoords.x << TILE_SIZE_BITS) | (xx << 4), (tileCoords.y << TILE_SIZE_BITS) | (yy << 4)));
}
}
dimension.addTile(tile);
}
newChunks.remove(new Point(chunk.getxPos() << 4, chunk.getzPos() << 4));
boolean manMadeStructuresBelowGround = false;
boolean manMadeStructuresAboveGround = false;
try {
for (int xx = 0; xx < 16; xx++) {
for (int zz = 0; zz < 16; zz++) {
float height = -1.0f;
int waterLevel = 0;
boolean floodWithLava = false, frost = false;
Terrain terrain = Terrain.BEDROCK;
for (int y = maxY; y >= 0; y--) {
Material material = chunk.getMaterial(xx, y, zz);
boolean manMadeStructuresBelowGround = false;
boolean manMadeStructuresAboveGround = false;
try {
for (int xx = 0; xx < 16; xx++) {
for (int zz = 0; zz < 16; zz++) {
float height = -1.0f;
int waterLevel = 0;
boolean floodWithLava = false, frost = false;
Terrain terrain = Terrain.BEDROCK;
for (int y = Math.min(maxY, chunk.getHighestNonAirBlock(xx, zz)); y >= 0; y--) {
Material material = chunk.getMaterial(xx, y, zz);
if (!material.natural) {
if (height == -1.0f) {
manMadeStructuresAboveGround = true;
} else {
manMadeStructuresBelowGround = true;
}
if (height == -1.0f) {
manMadeStructuresAboveGround = true;
} else {
manMadeStructuresBelowGround = true;
}
manMadeBlockTypes.add(material.name);
}
String name = material.name;
if ((name == MC_SNOW) || (name == MC_ICE)) {
frost = true;
}
if ((waterLevel == 0) && ((name == MC_ICE)
String name = material.name;
if ((name == MC_SNOW) || (name == MC_ICE)) {
frost = true;
}
if ((waterLevel == 0) && ((name == MC_ICE)
|| (name == MC_FROSTED_ICE)
|| (name == MC_BUBBLE_COLUMN)
|| (((name == MC_WATER) || (name == MC_LAVA))
@ -354,9 +314,9 @@ public class JavaMapImporter extends MapImporter {
}
} else if (height == -1.0f) {
if (TERRAIN_MAPPING.containsKey(name)) {
// Terrain found
height = y - 0.4375f; // Value that falls in the middle of the lowest one eighth which will still round to the same integer value and will receive a one layer thick smooth snow block (principle of least surprise)
terrain = TERRAIN_MAPPING.get(name);
// Terrain found
height = y - 0.4375f; // Value that falls in the middle of the lowest one eighth which will still round to the same integer value and will receive a one layer thick smooth snow block (principle of least surprise)
terrain = TERRAIN_MAPPING.get(name);
}
}
}
@ -367,30 +327,30 @@ public class JavaMapImporter extends MapImporter {
if (materialAbove.isNamed(MC_SNOW)) {
int layers = materialAbove.getProperty(LAYERS);
height += layers * 0.125;
}
}
if ((waterLevel == 0) && (height >= 61.5f)) {
waterLevel = 62;
}
}
}
if ((waterLevel == 0) && (height >= 61.5f)) {
waterLevel = 62;
}
final int blockX = (chunk.getxPos() << 4) | xx;
final int blockY = (chunk.getzPos() << 4) | zz;
final Point coords = new Point(blockX, blockY);
dimension.setTerrainAt(coords, terrain);
dimension.setHeightAt(coords, Math.max(height, 0.0f));
dimension.setWaterLevelAt(blockX, blockY, waterLevel);
if (frost) {
dimension.setBitLayerValueAt(Frost.INSTANCE, blockX, blockY, true);
}
if (floodWithLava) {
dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, blockX, blockY, true);
}
if (height == -1.0f) {
dimension.setBitLayerValueAt(org.pepsoft.worldpainter.layers.Void.INSTANCE, blockX, blockY, true);
}
if (importBiomes && chunk.isBiomesAvailable()) {
final int biome = chunk.getBiome(xx, zz);
if (((biome > HIGHEST_BIOME_ID) || (BIOME_NAMES[biome] == null)) && (biome != 255)) {
final int blockX = (chunkX << 4) | xx;
final int blockY = (chunkZ << 4) | zz;
final Point coords = new Point(blockX, blockY);
dimension.setTerrainAt(coords, terrain);
dimension.setHeightAt(coords, Math.max(height, 0.0f));
dimension.setWaterLevelAt(blockX, blockY, waterLevel);
if (frost) {
dimension.setBitLayerValueAt(Frost.INSTANCE, blockX, blockY, true);
}
if (floodWithLava) {
dimension.setBitLayerValueAt(FloodWithLava.INSTANCE, blockX, blockY, true);
}
if (height == -1.0f) {
dimension.setBitLayerValueAt(org.pepsoft.worldpainter.layers.Void.INSTANCE, blockX, blockY, true);
}
if (importBiomes && chunk.isBiomesAvailable()) {
final int biome = chunk.getBiome(xx, zz);
if (((biome > HIGHEST_BIOME_ID) || (BIOME_NAMES[biome] == null)) && (biome != 255)) {
unknownBiomes.add(biome);
}
// If the biome is set (around the edges of the map Minecraft sets it to
@ -405,28 +365,27 @@ public class JavaMapImporter extends MapImporter {
}
}
} catch (NullPointerException e) {
reportBuilder.append("Null pointer exception while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk" + EOL);
logger.error("Null pointer exception while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk", e);
continue;
} catch (ArrayIndexOutOfBoundsException e) {
reportBuilder.append("Array index out of bounds while reading chunk " + x + ", " + z + " from file " + file + " (message: \"" + e.getMessage() + "\"); skipping chunk" + EOL);
logger.error("Array index out of bounds while reading chunk " + x + ", " + z + " from file " + file + "; skipping chunk", e);
continue;
}
if (((readOnlyOption == ReadOnlyOption.MAN_MADE) && (manMadeStructuresBelowGround || manMadeStructuresAboveGround))
|| ((readOnlyOption == ReadOnlyOption.MAN_MADE_ABOVE_GROUND) && manMadeStructuresAboveGround)
|| (readOnlyOption == ReadOnlyOption.ALL)) {
dimension.setBitLayerValueAt(ReadOnly.INSTANCE, chunk.getxPos() << 4, chunk.getzPos() << 4, true);
}
}
}
}
reportBuilder.append("Null pointer exception while reading chunk " + chunkX + "," + chunkZ + "; skipping chunk" + EOL);
logger.error("Null pointer exception while reading chunk " + chunkX + "," + chunkZ + "; skipping chunk", e);
return true;
} catch (ArrayIndexOutOfBoundsException e) {
reportBuilder.append("Array index out of bounds while reading chunk " + chunkX + "," + chunkZ + " (message: \"" + e.getMessage() + "\"); skipping chunk" + EOL);
logger.error("Array index out of bounds while reading chunk " + chunkX + "," + chunkZ + "; skipping chunk", e);
return true;
}
} catch (IOException e) {
reportBuilder.append("I/O error while opening region file " + file + " (message: \"" + e.getMessage() + "\"); skipping region" + EOL);
logger.error("I/O error while opening region file " + file + "; skipping region", e);
if (((readOnlyOption == ReadOnlyOption.MAN_MADE) && (manMadeStructuresBelowGround || manMadeStructuresAboveGround))
|| ((readOnlyOption == ReadOnlyOption.MAN_MADE_ABOVE_GROUND) && manMadeStructuresAboveGround)
|| (readOnlyOption == ReadOnlyOption.ALL)) {
dimension.setBitLayerValueAt(ReadOnly.INSTANCE, chunkX << 4, chunkZ << 4, true);
}
} catch (ProgressReceiver.OperationCancelled e) {
return false;
}
return true;
})) {
throw new ProgressReceiver.OperationCancelled("Operation cancelled");
}
// Process chunks that were only added to fill out a tile
@ -448,7 +407,8 @@ public class JavaMapImporter extends MapImporter {
return reportBuilder.length() != 0 ? reportBuilder.toString() : null;
}
private final Platform platform;
private final TileFactory tileFactory;
private final File levelDatFile;
private final boolean populateNewChunks;

View File

@ -2,6 +2,7 @@ package org.pepsoft.worldpainter.plugins;
import org.pepsoft.minecraft.Chunk;
import org.pepsoft.minecraft.ChunkStore;
import org.pepsoft.worldpainter.Constants;
import org.pepsoft.worldpainter.Platform;
import org.pepsoft.worldpainter.exporting.PostProcessor;

View File

@ -6,9 +6,12 @@ import org.pepsoft.worldpainter.Platform;
import org.pepsoft.worldpainter.World2;
import org.pepsoft.worldpainter.exporting.PostProcessor;
import org.pepsoft.worldpainter.exporting.WorldExporter;
import org.pepsoft.worldpainter.mapexplorer.MapRecognizer;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Created by Pepijn on 12-2-2017.
@ -46,6 +49,37 @@ public class PlatformManager extends AbstractProviderManager<Platform, PlatformP
return ((BlockBasedPlatformProvider) getImplementation(platform)).getPostProcessor(platform);
}
/**
* Identify the platform provider for a map.
*
* @param worldDir The directory to identify.
* @return The platform provider which supports the format of the specified
* map, or {@code null} if no platform provider claimed support.
*/
public PlatformProvider identifyMap(File worldDir) {
Set<PlatformProvider> candidates = new HashSet<>();
for (PlatformProvider provider: getImplementations()) {
MapRecognizer mapRecognizer = provider.getMapRecognizer();
if ((mapRecognizer != null) && mapRecognizer.isMap(worldDir)) {
candidates.add(provider);
}
}
if (candidates.isEmpty()) {
return null;
} else if (candidates.size() == 1) {
return candidates.iterator().next();
} else {
// If one of the candidates is ourselves, discount it, assuming that
// the plugin did a more specific check and is probably right
candidates.removeIf(provider -> provider.getName().equals("DefaultPlatforms"));
if (candidates.size() == 1) {
return candidates.iterator().next();
} else {
throw new RuntimeException("Multiple platform providers (" + candidates + ") claimed support for this map");
}
}
}
public static PlatformManager getInstance() {
return INSTANCE;
}

View File

@ -4,10 +4,6 @@
*/
package org.pepsoft.worldpainter.util;
import org.jnbt.CompoundTag;
import org.jnbt.NBTInputStream;
import org.jnbt.Tag;
import org.pepsoft.minecraft.*;
import org.pepsoft.util.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -21,6 +17,8 @@ import static org.pepsoft.minecraft.Constants.DATA_VERSION_MC_1_12_2;
import static org.pepsoft.minecraft.Constants.VERSION_MCREGION;
import static org.pepsoft.worldpainter.Constants.*;
import static org.pepsoft.worldpainter.Constants.*;
/**
*
* @author pepijn
@ -89,19 +87,9 @@ public class MinecraftUtil {
return null;
}
/**
* Visit all the chunks of a Minecraft map.
*
* @param worldDir The map directory.
* @param dimension The ordinal of the dimension to visit.
* @param visitor The visitor to invoke for each chunk.
* @throws IOException If an I/O error occurs while reading the chunks.
*/
public static void visitChunks(File worldDir, int dimension, ChunkVisitor visitor) throws IOException {
final File levelDatFile = new File(worldDir, "level.dat");
final Level level = Level.load(levelDatFile);
final int version = level.getVersion(), maxHeight = level.getMaxHeight(), dataVersion = level.getDataVersion();
final File regionDir;
@NotNull
public static File getRegionDir(File worldDir, int dimension) {
File regionDir;
switch (dimension) {
case DIM_NORMAL:
regionDir = new File(worldDir, "region");
@ -113,45 +101,9 @@ public class MinecraftUtil {
regionDir = new File(worldDir, "DIM1/region");
break;
default:
throw new IllegalArgumentException("Don't know where to find dimension " + dimension);
}
final Pattern regionFilePattern = (version == VERSION_MCREGION)
? Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mcr")
: Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mca");
final File[] regionFiles = regionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches());
for (File file: regionFiles) {
try (RegionFile regionFile = new RegionFile(file, true)) {
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
if (regionFile.containsChunk(x, z)) {
final Tag tag;
final InputStream chunkData = regionFile.getChunkDataInputStream(x, z);
try (NBTInputStream in = new NBTInputStream(chunkData)) {
tag = in.readTag();
} catch (RuntimeException e) {
logger.error("{} while reading tag for chunk {},{}", e.getClass().getSimpleName(), x, z);
e.printStackTrace();
continue;
}
final Chunk chunk;
try {
chunk = (version == VERSION_MCREGION)
? new MCRegionChunk((CompoundTag) tag, maxHeight)
: ((dataVersion > DATA_VERSION_MC_1_12_2)
? new MC113AnvilChunk((CompoundTag) tag, maxHeight)
: new MC12AnvilChunk((CompoundTag) tag, maxHeight));
} catch (RuntimeException e) {
logger.error("{} while parsing tag for chunk {},{}", e.getClass().getSimpleName(), x, z);
e.printStackTrace();
logger.error(tag.toString());
continue;
}
visitor.visitChunk(chunk);
}
}
}
}
throw new IllegalArgumentException("Dimension " + dimension + " not supported");
}
return regionDir;
}
private static final Logger logger = LoggerFactory.getLogger(MinecraftUtil.class);

View File

@ -100,6 +100,11 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="checkBoxImportEnd" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="jLabel6" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="labelPlatform" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
@ -118,7 +123,12 @@
<Component id="fieldFilename" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="buttonSelectFile" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="labelPlatform" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="labelOutliers1" alignment="3" min="-2" max="-2" attributes="0"/>
@ -340,5 +350,12 @@
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel6">
<Properties>
<Property name="text" type="java.lang.String" value="Map format:"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelPlatform">
</Component>
</SubComponents>
</Form>

View File

@ -4,14 +4,16 @@
*/
package org.pepsoft.worldpainter.importing;
import org.pepsoft.minecraft.ChunkStore;
import org.pepsoft.minecraft.Level;
import org.pepsoft.minecraft.RegionFile;
import org.pepsoft.util.FileUtils;
import org.pepsoft.util.ProgressReceiver;
import org.pepsoft.util.ProgressReceiver.OperationCancelled;
import org.pepsoft.util.swing.ProgressDialog;
import org.pepsoft.util.swing.ProgressTask;
import org.pepsoft.worldpainter.*;
import org.pepsoft.worldpainter.plugins.BlockBasedPlatformProvider;
import org.pepsoft.worldpainter.plugins.PlatformManager;
import org.pepsoft.worldpainter.util.MinecraftUtil;
import javax.swing.*;
@ -23,12 +25,17 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.NumberFormat;
import java.util.*;
import java.util.List;
import java.util.regex.Pattern;
import java.util.*;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toSet;
import static javax.swing.JOptionPane.ERROR_MESSAGE;
import static org.pepsoft.minecraft.Constants.VERSION_ANVIL;
import static org.pepsoft.minecraft.Constants.VERSION_MCREGION;
import static org.pepsoft.worldpainter.Constants.*;
import static org.pepsoft.worldpainter.DefaultPlugin.JAVA_ANVIL;
import static org.pepsoft.worldpainter.DefaultPlugin.JAVA_MCREGION;
/**
*
@ -87,7 +94,7 @@ public class MapImportDialog extends WorldPainterDialog {
}
private void analyseMap() {
mapStatistics = null;
mapInfo = null;
resetStats();
File levelDatFile = new File(fieldFilename.getText());
@ -100,117 +107,100 @@ public class MapImportDialog extends WorldPainterDialog {
version = testLevel.getVersion();
} catch (IOException e) {
logger.error("IOException while analysing map " + levelDatFile, e);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("selected.file.is.not.a.valid.level.dat.file"), strings.getString("invalid.file"), JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("selected.file.is.not.a.valid.level.dat.file"), strings.getString("invalid.file"), ERROR_MESSAGE);
return;
} catch (IllegalArgumentException e) {
logger.error("IllegalArgumentException while analysing map " + levelDatFile, e);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("selected.file.is.not.a.valid.level.dat.file"), strings.getString("invalid.file"), JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("selected.file.is.not.a.valid.level.dat.file"), strings.getString("invalid.file"), ERROR_MESSAGE);
return;
} catch (NullPointerException e) {
logger.error("NullPointerException while analysing map " + levelDatFile, e);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("selected.file.is.not.a.valid.level.dat.file"), strings.getString("invalid.file"), JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("selected.file.is.not.a.valid.level.dat.file"), strings.getString("invalid.file"), ERROR_MESSAGE);
return;
}
// Other sanity checks
if ((version != VERSION_MCREGION) && (version != VERSION_ANVIL)) {
logger.error("Unsupported Minecraft version while analysing map " + levelDatFile);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("unsupported.minecraft.version"), strings.getString("unsupported.version"), JOptionPane.ERROR_MESSAGE);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("unsupported.minecraft.version"), strings.getString("unsupported.version"), ERROR_MESSAGE);
return;
}
File regionDir = new File(worldDir, "region");
if (! regionDir.isDirectory()) {
logger.error("Region directory missing while analysing map " + levelDatFile);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("the.region.folder.is.missing"), strings.getString("region.folder.missing"), JOptionPane.ERROR_MESSAGE);
// Determine the platform
Platform platform;
// TODO handle non-block based platform provider matching more gracefully
BlockBasedPlatformProvider platformProvider = (BlockBasedPlatformProvider) PlatformManager.getInstance().identifyMap(worldDir);
// TODO migrate this so that the map recognizer returns the actual platform and this ugly business isn't needed
if (platformProvider.getName().equals("DefaultPlatforms")) {
platform = (version == SUPPORTED_VERSION_1) ? JAVA_MCREGION : JAVA_ANVIL;
} else {
Collection<Platform> platforms = platformProvider.getKeys();
if (platforms.size() == 1) {
platform = platforms.iterator().next();
} else {
logger.error("Could not determine single matching platform for " + levelDatFile + "(matching platforms: " + platforms + ")");
JOptionPane.showMessageDialog(MapImportDialog.this, "Could not determine single matching platform for " + levelDatFile + "(matching platforms: " + platforms + ")", "Multiple Matching Platforms", ERROR_MESSAGE);
return;
}
}
// Sanity checks for the surface dimension
Set<Integer> dimensions = stream(platformProvider.getDimensions(platform, worldDir)).boxed().collect(toSet());
if (! dimensions.contains(DIM_NORMAL)) {
logger.error("Map has no surface dimension: " + levelDatFile);
JOptionPane.showMessageDialog(MapImportDialog.this, "This map has no surface dimension; this is not supported by WorldPainter", "Missing Surface Dimension", ERROR_MESSAGE);
return;
}
final Pattern regionFilePattern = (version == VERSION_MCREGION)
? Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mcr")
: Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mca");
final File[] regionFiles = regionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches());
if ((regionFiles == null) || (regionFiles.length == 0)) {
logger.error("Region files missing while analysing map " + levelDatFile);
JOptionPane.showMessageDialog(MapImportDialog.this, strings.getString("the.region.folder.contains.no.region.files"), strings.getString("region.files.missing"), JOptionPane.ERROR_MESSAGE);
final ChunkStore surfaceChunkStore = platformProvider.getChunkStore(platform, worldDir, DIM_NORMAL);
final long surfaceChunkCount = surfaceChunkStore.getChunkCount();
if (surfaceChunkCount == 0) {
logger.error("Surface dimension does not contain any chunks: " + levelDatFile);
JOptionPane.showMessageDialog(MapImportDialog.this, "This map has an empty surface dimension; this is not supported by WorldPainter", "Empty Surface Dimension", ERROR_MESSAGE);
return;
}
// Check for Nether and End
boolean netherPresent = false, endPresent = false;
File netherRegionDir = new File(worldDir, "DIM-1/region");
if (netherRegionDir.isDirectory()) {
File[] netherRegionFiles = netherRegionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches());
if ((netherRegionFiles != null) && (netherRegionFiles.length > 0)) {
netherPresent = true;
}
}
File endRegionDir = new File(worldDir, "DIM1/region");
if (endRegionDir.isDirectory()) {
File[] endRegionFiles = endRegionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches());
if ((endRegionFiles != null) && (endRegionFiles.length > 0)) {
endPresent = true;
}
}
final boolean netherPresent = dimensions.contains(DIM_NETHER), endPresent = dimensions.contains(DIM_END);
checkBoxImportNether.setEnabled(netherPresent);
checkBoxImportNether.setSelected(netherPresent);
checkBoxImportEnd.setEnabled(endPresent);
checkBoxImportEnd.setSelected(endPresent);
mapStatistics = ProgressDialog.executeTask(this, new ProgressTask<MapStatistics>() {
mapInfo = ProgressDialog.executeTask(this, new ProgressTask<MapInfo>() {
@Override
public String getName() {
return "Analyzing map...";
}
@Override
public MapStatistics execute(ProgressReceiver progressReceiver) throws OperationCancelled {
MapStatistics stats = new MapStatistics();
int chunkCount = 0;
List<Integer> xValues = new ArrayList<>(), zValues = new ArrayList<>();
List<Point> chunks = new ArrayList<>();
int count = 0;
for (File file: regionFiles) {
String[] nameFrags = file.getName().split("\\.");
int regionX = Integer.parseInt(nameFrags[1]);
int regionZ = Integer.parseInt(nameFrags[2]);
try {
RegionFile regionFile = new RegionFile(file);
try {
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
if (regionFile.containsChunk(x, z)) {
chunkCount++;
int chunkX = regionX * 32 + x, chunkZ = regionZ * 32 + z;
if (chunkX < stats.lowestChunkX) {
stats.lowestChunkX = chunkX;
}
if (chunkX > stats.highestChunkX) {
stats.highestChunkX = chunkX;
}
if (chunkZ < stats.lowestChunkZ) {
stats.lowestChunkZ = chunkZ;
}
if (chunkZ > stats.highestChunkZ) {
stats.highestChunkZ = chunkZ;
}
xValues.add(chunkX);
zValues.add(chunkZ);
chunks.add(new Point(chunkX, chunkZ));
}
}
}
} finally {
regionFile.close();
}
} catch (IOException e) {
throw new RuntimeException("I/O error while analyzing map " + worldDir, e);
}
count++;
progressReceiver.setProgress((float) count / (regionFiles.length + 1));
}
stats.chunkCount = chunkCount;
public MapInfo execute(ProgressReceiver progressReceiver) throws OperationCancelled {
final MapInfo stats = new MapInfo();
stats.chunkCount = surfaceChunkCount;
if (chunkCount == 0) {
// TODO do this for the other dimensions as well
final List<Integer> xValues = new ArrayList<>(), zValues = new ArrayList<>();
final List<Point> chunks = new ArrayList<>();
surfaceChunkStore.visitChunks(chunk -> {
final Point coords = chunk.getCoords();
if (coords.x < stats.lowestChunkX) {
stats.lowestChunkX = coords.x;
}
if (coords.x > stats.highestChunkX) {
stats.highestChunkX = coords.x;
}
if (coords.y < stats.lowestChunkZ) {
stats.lowestChunkZ = coords.y;
}
if (coords.y > stats.highestChunkZ) {
stats.highestChunkZ = coords.y;
}
xValues.add(coords.x);
zValues.add(coords.y);
chunks.add(coords);
return true;
});
if (stats.chunkCount == 0) {
// Completely empty map (wrong region file format)?
progressReceiver.setProgress(1.0f);
return stats;
@ -274,18 +264,20 @@ public class MapImportDialog extends WorldPainterDialog {
return stats;
}
});
if ((mapStatistics != null) && (mapStatistics.chunkCount > 0)) {
int width = mapStatistics.highestChunkXNoOutliers - mapStatistics.lowestChunkXNoOutliers + 1;
int length = mapStatistics.highestChunkZNoOutliers - mapStatistics.lowestChunkZNoOutliers + 1;
int area = (mapStatistics.chunkCount - mapStatistics.outlyingChunks.size());
labelWidth.setText(FORMATTER.format(width * 16) + " blocks (from " + FORMATTER.format(mapStatistics.lowestChunkXNoOutliers << 4) + " to " + FORMATTER.format((mapStatistics.highestChunkXNoOutliers << 4) + 15) + "; " + FORMATTER.format(width) + " chunks)");
labelLength.setText(FORMATTER.format(length * 16) + " blocks (from " + FORMATTER.format(mapStatistics.lowestChunkZNoOutliers << 4) + " to " + FORMATTER.format((mapStatistics.highestChunkZNoOutliers << 4) + 15) + "; " + FORMATTER.format(length) + " chunks)");
if ((mapInfo != null) && (mapInfo.chunkCount > 0)) {
mapInfo.platform = platform;
labelPlatform.setText(platform.displayName);
int width = mapInfo.highestChunkXNoOutliers - mapInfo.lowestChunkXNoOutliers + 1;
int length = mapInfo.highestChunkZNoOutliers - mapInfo.lowestChunkZNoOutliers + 1;
long area = (mapInfo.chunkCount - mapInfo.outlyingChunks.size());
labelWidth.setText(FORMATTER.format(width * 16) + " blocks (from " + FORMATTER.format(mapInfo.lowestChunkXNoOutliers << 4) + " to " + FORMATTER.format((mapInfo.highestChunkXNoOutliers << 4) + 15) + "; " + FORMATTER.format(width) + " chunks)");
labelLength.setText(FORMATTER.format(length * 16) + " blocks (from " + FORMATTER.format(mapInfo.lowestChunkZNoOutliers << 4) + " to " + FORMATTER.format((mapInfo.highestChunkZNoOutliers << 4) + 15) + "; " + FORMATTER.format(length) + " chunks)");
labelArea.setText(FORMATTER.format(area * 256L) + " blocks² (" + FORMATTER.format(area) + " chunks)");
if (! mapStatistics.outlyingChunks.isEmpty()) {
if (! mapInfo.outlyingChunks.isEmpty()) {
// There are outlying chunks
int widthWithOutliers = mapStatistics.highestChunkX - mapStatistics.lowestChunkX + 1;
int lengthWithOutliers = mapStatistics.highestChunkZ - mapStatistics.lowestChunkZ + 1;
int areaOfOutliers = mapStatistics.outlyingChunks.size();
int widthWithOutliers = mapInfo.highestChunkX - mapInfo.lowestChunkX + 1;
int lengthWithOutliers = mapInfo.highestChunkZ - mapInfo.lowestChunkZ + 1;
int areaOfOutliers = mapInfo.outlyingChunks.size();
labelOutliers1.setVisible(true);
labelOutliers2.setVisible(true);
labelWidthWithOutliers.setText(FORMATTER.format(widthWithOutliers * 16) + " blocks (" + FORMATTER.format(widthWithOutliers) + " chunks)");
@ -306,7 +298,7 @@ public class MapImportDialog extends WorldPainterDialog {
private void setControlStates() {
String fileStr = fieldFilename.getText().trim();
File file = (! fileStr.isEmpty()) ? new File(fileStr) : null;
if ((mapStatistics == null) || (mapStatistics.chunkCount == 0) || (file == null) || (! file.isFile())) {
if ((mapInfo == null) || (mapInfo.chunkCount == 0) || (file == null) || (! file.isFile())) {
buttonOK.setEnabled(false);
} else {
buttonOK.setEnabled(true);
@ -352,7 +344,7 @@ public class MapImportDialog extends WorldPainterDialog {
private void importWorld() {
final File levelDatFile = new File(fieldFilename.getText());
final Set<Point> chunksToSkip = checkBoxImportOutliers.isSelected() ? null : mapStatistics.outlyingChunks;
final Set<Point> chunksToSkip = checkBoxImportOutliers.isSelected() ? null : mapInfo.outlyingChunks;
final MapImporter.ReadOnlyOption readOnlyOption;
if (radioButtonReadOnlyAll.isSelected()) {
readOnlyOption = MapImporter.ReadOnlyOption.ALL;
@ -384,14 +376,14 @@ public class MapImportDialog extends WorldPainterDialog {
int terrainLevel = waterLevel - 4;
TileFactory tileFactory = TileFactoryFactory.createNoiseTileFactory(0, Terrain.GRASS, maxHeight, terrainLevel, waterLevel, false, true, 20, 1.0);
Set<Integer> dimensionsToImport = new HashSet<>(3);
dimensionsToImport.add(Constants.DIM_NORMAL);
dimensionsToImport.add(DIM_NORMAL);
if (checkBoxImportNether.isSelected()) {
dimensionsToImport.add(Constants.DIM_NETHER);
}
if (checkBoxImportEnd.isSelected()) {
dimensionsToImport.add(Constants.DIM_END);
}
final MapImporter importer = new JavaMapImporter(tileFactory, levelDatFile, false, chunksToSkip, readOnlyOption, dimensionsToImport);
final MapImporter importer = new JavaMapImporter(mapInfo.platform, tileFactory, levelDatFile, false, chunksToSkip, readOnlyOption, dimensionsToImport);
World2 world = importer.doImport(progressReceiver);
if (importer.getWarnings() != null) {
try {
@ -465,6 +457,8 @@ public class MapImportDialog extends WorldPainterDialog {
checkBoxImportSurface = new javax.swing.JCheckBox();
checkBoxImportNether = new javax.swing.JCheckBox();
checkBoxImportEnd = new javax.swing.JCheckBox();
jLabel6 = new javax.swing.JLabel();
labelPlatform = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle("Import Existing Minecraft Map");
@ -549,6 +543,8 @@ public class MapImportDialog extends WorldPainterDialog {
checkBoxImportEnd.setText("Import End");
checkBoxImportEnd.setEnabled(false);
jLabel6.setText("Map format:");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
@ -611,7 +607,11 @@ public class MapImportDialog extends WorldPainterDialog {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(checkBoxImportNether)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(checkBoxImportEnd)))
.addComponent(checkBoxImportEnd))
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel6)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(labelPlatform)))
.addGap(0, 0, Short.MAX_VALUE)))
.addContainerGap())
);
@ -624,7 +624,11 @@ public class MapImportDialog extends WorldPainterDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(fieldFilename, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(buttonSelectFile))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel6)
.addComponent(labelPlatform))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(labelOutliers1))
@ -700,6 +704,7 @@ public class MapImportDialog extends WorldPainterDialog {
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel labelArea;
private javax.swing.JLabel labelAreaOutliers;
@ -709,6 +714,7 @@ public class MapImportDialog extends WorldPainterDialog {
private javax.swing.JLabel labelOutliers2;
private javax.swing.JLabel labelOutliers3;
private javax.swing.JLabel labelOutliers4;
private javax.swing.JLabel labelPlatform;
private javax.swing.JLabel labelWidth;
private javax.swing.JLabel labelWidthWithOutliers;
private javax.swing.JRadioButton radioButtonReadOnlyAll;
@ -719,7 +725,7 @@ public class MapImportDialog extends WorldPainterDialog {
private final App app;
private File previouslySelectedFile;
private MapStatistics mapStatistics;
private MapInfo mapInfo;
private World2 importedWorld;
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MapImportDialog.class);
@ -727,10 +733,11 @@ public class MapImportDialog extends WorldPainterDialog {
private static final NumberFormat FORMATTER = NumberFormat.getIntegerInstance();
private static final long serialVersionUID = 1L;
static class MapStatistics {
static class MapInfo {
Platform platform;
int lowestChunkX = Integer.MAX_VALUE, lowestChunkZ = Integer.MAX_VALUE, highestChunkX = Integer.MIN_VALUE, highestChunkZ = Integer.MIN_VALUE;
int lowestChunkXNoOutliers = Integer.MAX_VALUE, lowestChunkZNoOutliers = Integer.MAX_VALUE, highestChunkXNoOutliers = Integer.MIN_VALUE, highestChunkZNoOutliers = Integer.MIN_VALUE;
int chunkCount;
long chunkCount;
final Set<Point> outlyingChunks = new HashSet<>();
String errorMessage;
}