use of org.pepsoft.util.SubProgressReceiver in project WorldPainter by Captain-Chaos.
the class Export method main.
public static void main(String[] args) throws IOException, ClassNotFoundException, OperationCancelled, CertificateException {
// Logger rootLogger = Logger.getLogger("");
// rootLogger.setLevel(Level.OFF);
// Load or initialise configuration
// This will migrate the configuration directory if necessary
Configuration config = Configuration.load();
if (config == null) {
System.out.println("Creating new configuration");
config = new Configuration();
}
Configuration.setInstance(config);
System.out.println("Installation ID: " + config.getUuid());
// Load trusted WorldPainter root certificate
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate trustedCert = (X509Certificate) certificateFactory.generateCertificate(ClassLoader.getSystemResourceAsStream("wproot.pem"));
// Load the plugins
File pluginsDir = new File(Configuration.getConfigDir(), "plugins");
if (pluginsDir.isDirectory()) {
PluginManager.loadPlugins(pluginsDir, trustedCert.getPublicKey());
}
WPPluginManager.initialise(config.getUuid());
File worldFile = new File(args[0]);
System.out.println("Loading " + worldFile);
World2 world;
try (ObjectInputStream in = new ObjectInputStream(new GZIPInputStream(new FileInputStream(worldFile)))) {
world = (World2) in.readObject();
}
for (int i = 0; i < Terrain.CUSTOM_TERRAIN_COUNT; i++) {
MixedMaterial material = world.getMixedMaterial(i);
Terrain.setCustomMaterial(i, material);
}
if (world.getPlatform() == null) {
if (world.getMaxHeight() == Constants.DEFAULT_MAX_HEIGHT_2) {
world.setPlatform(DefaultPlugin.JAVA_ANVIL);
} else {
world.setPlatform(DefaultPlugin.JAVA_MCREGION);
}
}
File exportDir;
if (args.length > 1) {
exportDir = new File(args[1]);
} else {
File minecraftDir = MinecraftUtil.findMinecraftDir();
exportDir = new File(minecraftDir, "saves");
}
System.out.println("Exporting to " + exportDir);
System.out.println("+---------+---------+---------+---------+---------+");
JavaWorldExporter exporter = new JavaWorldExporter(world);
exporter.export(exportDir, world.getName(), exporter.selectBackupDir(new File(exportDir, FileUtils.sanitiseName(world.getName()))), new ProgressReceiver() {
@Override
public void setProgress(float progressFraction) throws OperationCancelled {
int progress = (int) (progressFraction * 50);
while (progress > previousProgress) {
System.out.print('.');
previousProgress++;
}
}
@Override
public void exceptionThrown(Throwable exception) {
exception.printStackTrace();
System.exit(1);
}
@Override
public void reset() {
System.out.println();
previousProgress = -1;
}
@Override
public void done() {
}
@Override
public void setMessage(String message) throws OperationCancelled {
}
@Override
public void checkForCancellation() throws OperationCancelled {
}
@Override
public void subProgressStarted(SubProgressReceiver subProgressReceiver) throws OperationCancelled {
}
private int previousProgress = -1;
});
System.out.println();
System.out.println("World " + world.getName() + " exported successfully");
}
use of org.pepsoft.util.SubProgressReceiver in project WorldPainter by Captain-Chaos.
the class JavaMapImporter method doImport.
public World2 doImport(ProgressReceiver progressReceiver) throws IOException, ProgressReceiver.OperationCancelled {
long start = System.currentTimeMillis();
logger.info("Importing map from " + levelDatFile.getAbsolutePath());
Level level = Level.load(levelDatFile);
int version = level.getVersion();
if ((version != SUPPORTED_VERSION_1) && (version != SUPPORTED_VERSION_2)) {
throw new UnsupportedOperationException("Level format version " + version + " not supported");
}
String name = level.getName().trim();
int maxHeight = level.getMaxHeight();
World2 world = new World2((version == SUPPORTED_VERSION_1) ? JAVA_MCREGION : JAVA_ANVIL, maxHeight);
world.addHistoryEntry(HistoryEntry.WORLD_IMPORTED_FROM_MINECRAFT_MAP, level.getName(), levelDatFile.getParentFile());
world.setCreateGoodiesChest(false);
world.setName(name);
world.setSpawnPoint(new Point(level.getSpawnX(), level.getSpawnZ()));
world.setImportedFrom(levelDatFile);
world.setMapFeatures(level.isMapFeatures());
if (level.isHardcore()) {
world.setGameType(GameType.HARDCORE);
} else {
world.setGameType(GameType.values()[level.getGameType()]);
}
world.setGenerator(level.getGenerator());
world.setGeneratorOptions(level.getGeneratorOptions());
world.setDifficulty(level.getDifficulty());
if ((version == SUPPORTED_VERSION_2) && (level.getBorderSize() > 0.0)) {
// If the world is version 0x4abd and actually has border settings,
// load them
world.getBorderSettings().setCentreX((int) (level.getBorderCenterX() + 0.5));
world.getBorderSettings().setCentreY((int) (level.getBorderCenterZ() + 0.5));
world.getBorderSettings().setSize((int) (level.getBorderSize() + 0.5));
world.getBorderSettings().setSafeZone((int) (level.getBorderSafeZone() + 0.5));
world.getBorderSettings().setWarningBlocks((int) (level.getBorderWarningBlocks() + 0.5));
world.getBorderSettings().setWarningTime((int) (level.getBorderWarningTime() + 0.5));
world.getBorderSettings().setSizeLerpTarget((int) (level.getBorderSizeLerpTarget() + 0.5));
world.getBorderSettings().setSizeLerpTime((int) level.getBorderSizeLerpTime());
world.getBorderSettings().setDamagePerBlock((int) (level.getBorderDamagePerBlock() + 0.5));
}
File worldDir = levelDatFile.getParentFile();
File regionDir = new File(worldDir, "region");
File netherDir = new File(worldDir, "DIM-1/region");
File endDir = new File(worldDir, "DIM1/region");
int dimCount = 1;
if (netherDir.isDirectory() && dimensionsToImport.contains(DIM_NETHER)) {
dimCount++;
}
if (endDir.isDirectory() && dimensionsToImport.contains(DIM_END)) {
dimCount++;
}
long minecraftSeed = level.getSeed();
tileFactory.setSeed(minecraftSeed);
Dimension dimension = new Dimension(minecraftSeed, tileFactory, DIM_NORMAL, maxHeight);
dimension.setEventsInhibited(true);
try {
dimension.setCoverSteepTerrain(false);
dimension.setSubsurfaceMaterial(Terrain.STONE);
dimension.setBorderLevel(62);
// Turn off smooth snow
FrostSettings frostSettings = new FrostSettings();
frostSettings.setMode(FrostSettings.MODE_FLAT);
dimension.setLayerSettings(Frost.INSTANCE, frostSettings);
ResourcesExporterSettings resourcesSettings = (ResourcesExporterSettings) dimension.getLayerSettings(Resources.INSTANCE);
resourcesSettings.setMinimumLevel(0);
if (version == SUPPORTED_VERSION_1) {
resourcesSettings.setChance(BLK_EMERALD_ORE, 0);
}
Configuration config = Configuration.getInstance();
dimension.setGridEnabled(config.isDefaultGridEnabled());
dimension.setGridSize(config.getDefaultGridSize());
dimension.setContoursEnabled(config.isDefaultContoursEnabled());
dimension.setContourSeparation(config.getDefaultContourSeparation());
String dimWarnings = importDimension(regionDir, dimension, version, (progressReceiver != null) ? new SubProgressReceiver(progressReceiver, 0.0f, 1.0f / dimCount) : null);
if (dimWarnings != null) {
if (warnings == null) {
warnings = dimWarnings;
} else {
warnings = warnings + dimWarnings;
}
}
} finally {
dimension.setEventsInhibited(false);
}
world.addDimension(dimension);
int dimNo = 1;
if (netherDir.isDirectory() && dimensionsToImport.contains(DIM_NETHER)) {
HeightMapTileFactory netherTileFactory = TileFactoryFactory.createNoiseTileFactory(minecraftSeed + 1, Terrain.NETHERRACK, maxHeight, 188, 192, true, false, 20f, 1.0);
SimpleTheme theme = (SimpleTheme) netherTileFactory.getTheme();
SortedMap<Integer, Terrain> terrainRanges = theme.getTerrainRanges();
terrainRanges.clear();
terrainRanges.put(-1, Terrain.NETHERRACK);
theme.setTerrainRanges(terrainRanges);
theme.setLayerMap(null);
dimension = new Dimension(minecraftSeed + 1, netherTileFactory, DIM_NETHER, maxHeight);
dimension.setEventsInhibited(true);
try {
dimension.setCoverSteepTerrain(false);
dimension.setSubsurfaceMaterial(Terrain.NETHERRACK);
ResourcesExporterSettings resourcesSettings = (ResourcesExporterSettings) dimension.getLayerSettings(Resources.INSTANCE);
resourcesSettings.setMinimumLevel(0);
if (version == SUPPORTED_VERSION_1) {
resourcesSettings.setChance(BLK_QUARTZ_ORE, 0);
}
String dimWarnings = importDimension(netherDir, dimension, version, (progressReceiver != null) ? new SubProgressReceiver(progressReceiver, (float) dimNo++ / dimCount, 1.0f / dimCount) : null);
if (dimWarnings != null) {
if (warnings == null) {
warnings = dimWarnings;
} else {
warnings = warnings + dimWarnings;
}
}
} finally {
dimension.setEventsInhibited(false);
}
world.addDimension(dimension);
}
if (endDir.isDirectory() && dimensionsToImport.contains(DIM_END)) {
HeightMapTileFactory endTileFactory = TileFactoryFactory.createNoiseTileFactory(minecraftSeed + 2, Terrain.END_STONE, maxHeight, 32, 0, false, false, 20f, 1.0);
SimpleTheme theme = (SimpleTheme) endTileFactory.getTheme();
SortedMap<Integer, Terrain> terrainRanges = theme.getTerrainRanges();
terrainRanges.clear();
terrainRanges.put(-1, Terrain.END_STONE);
theme.setTerrainRanges(terrainRanges);
theme.setLayerMap(Collections.emptyMap());
dimension = new Dimension(minecraftSeed + 2, endTileFactory, DIM_END, maxHeight);
dimension.setEventsInhibited(true);
try {
dimension.setCoverSteepTerrain(false);
dimension.setSubsurfaceMaterial(Terrain.END_STONE);
String dimWarnings = importDimension(endDir, dimension, version, (progressReceiver != null) ? new SubProgressReceiver(progressReceiver, (float) dimNo / dimCount, 1.0f / dimCount) : null);
if (dimWarnings != null) {
if (warnings == null) {
warnings = dimWarnings;
} else {
warnings = warnings + dimWarnings;
}
}
} finally {
dimension.setEventsInhibited(false);
}
world.addDimension(dimension);
}
// Log an event
Configuration config = Configuration.getInstance();
if (config != null) {
EventVO event = new EventVO(EVENT_KEY_ACTION_IMPORT_MAP).duration(System.currentTimeMillis() - start);
event.setAttribute(EventVO.ATTRIBUTE_TIMESTAMP, new Date(start));
event.setAttribute(ATTRIBUTE_KEY_MAX_HEIGHT, world.getMaxHeight());
event.setAttribute(ATTRIBUTE_KEY_PLATFORM, world.getPlatform().displayName);
event.setAttribute(ATTRIBUTE_KEY_MAP_FEATURES, world.isMapFeatures());
event.setAttribute(ATTRIBUTE_KEY_GAME_TYPE_NAME, world.getGameType().name());
event.setAttribute(ATTRIBUTE_KEY_ALLOW_CHEATS, world.isAllowCheats());
event.setAttribute(ATTRIBUTE_KEY_GENERATOR, world.getGenerator().name());
if (world.getPlatform().equals(JAVA_ANVIL) && (world.getGenerator() == Generator.FLAT)) {
event.setAttribute(ATTRIBUTE_KEY_GENERATOR_OPTIONS, world.getGeneratorOptions());
}
event.setAttribute(ATTRIBUTE_KEY_TILES, dimension.getTiles().size());
config.logEvent(event);
}
return world;
}
use of org.pepsoft.util.SubProgressReceiver in project WorldPainter by Captain-Chaos.
the class World2 method transform.
/**
* Transforms all dimensions of this world horizontally. If an undo manager
* is installed this operation will destroy all undo info.
*
* @param transform The coordinate transform to apply.
* @param progressReceiver A progress receiver which will be informed of
* rotation progress.
*/
public void transform(CoordinateTransform transform, ProgressReceiver progressReceiver) throws ProgressReceiver.OperationCancelled {
int dimCount = dimensions.size(), dim = 0;
for (Dimension dimension : dimensions.values()) {
dimension.transform(transform, (progressReceiver != null) ? new SubProgressReceiver(progressReceiver, (float) dim / dimCount, 1.0f / dimCount) : null);
dim++;
}
Point oldSpawnPoint = spawnPoint;
spawnPoint = transform.transform(spawnPoint);
propertyChangeSupport.firePropertyChange("spawnPoint", oldSpawnPoint, spawnPoint);
Direction oldUpIs = upIs;
upIs = transform.inverseTransform(upIs);
propertyChangeSupport.firePropertyChange("upIs", oldUpIs, upIs);
dirty = true;
}
use of org.pepsoft.util.SubProgressReceiver in project WorldPainter by Captain-Chaos.
the class MultiProgressComponent method subProgressStarted.
@Override
public synchronized void subProgressStarted(SubProgressReceiver subProgressReceiver) throws OperationCancelled {
checkForCancellation();
doOnEventThread(() -> {
synchronized (MultiProgressComponent.this) {
ProgressViewer progressViewer = new ProgressViewer(subProgressReceiver);
JPanel progressPanel = null;
ProgressReceiver parent = subProgressReceiver.getParent();
if (parent == null) {
// No parent; insert at start
scrollablePanel1.add(progressViewer, 0);
progressPanel = progressViewer;
} else {
boolean parentFound = false;
do {
for (int i = 0; i < scrollablePanel1.getComponentCount(); i++) {
Component component = scrollablePanel1.getComponent(i);
ProgressViewer parentViewer;
try {
parentViewer = (ProgressViewer) ((component instanceof ProgressViewer) ? component : ((JPanel) component).getComponent(1));
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Component " + i + " is a " + component.getClass());
throw e;
}
if (parentViewer.getSubProgressReceiver() == parent) {
// Progress viewer for parent found; insert below
Integer parentIndentation = (Integer) parentViewer.getClientProperty(CLIENT_PROPERTY_INDENTATION);
int indentation = (parentIndentation != null) ? parentIndentation + 1 : 1;
progressPanel = new JPanel();
progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.LINE_AXIS));
progressPanel.add(Box.createHorizontalStrut(indentation * INDENTATION_SIZE));
progressPanel.add(progressViewer);
scrollablePanel1.add(progressPanel, i + 1);
parentFound = true;
break;
}
}
if (parent instanceof SubProgressReceiver) {
parent = ((SubProgressReceiver) parent).getParent();
} else {
parent = null;
}
} while ((!parentFound) && (parent != null));
if (!parentFound) {
// Progress viewer not found for any ancestor; append to end
scrollablePanel1.add(progressViewer);
progressPanel = progressViewer;
}
}
subProgressReceiver.addListener(new ProgressReceiver() {
@Override
public void setProgress(float progress) throws OperationCancelled {
if (progress >= 1.0f) {
doOnEventThread(() -> removeViewerHierarchy(subProgressReceiver));
}
}
@Override
public void exceptionThrown(Throwable exception) {
doOnEventThread(() -> removeViewerHierarchy(subProgressReceiver));
}
@Override
public void done() {
doOnEventThread(() -> removeViewerHierarchy(subProgressReceiver));
}
/**
* Remove a particular viewer, and any children which may
* still exist (this happens in the wild; not entirely clear
* why; may be because they never started any progress;
* perhaps some kind of race condition).
*/
private void removeViewerHierarchy(SubProgressReceiver subProgressReceiver) {
// Remove any children
for (Component component : scrollablePanel1.getComponents()) {
ProgressViewer viewer = (ProgressViewer) ((component instanceof ProgressViewer) ? component : ((JPanel) component).getComponent(1));
if (viewer.getSubProgressReceiver().getParent() == subProgressReceiver) {
if (logger.isTraceEnabled()) {
logger.trace("Progress receiver still has child; removing child. Stack trace is of child creation", viewer.getSubProgressReceiver().getCreationTrace());
}
removeViewerHierarchy(viewer.getSubProgressReceiver());
}
}
// itself
for (Component component : scrollablePanel1.getComponents()) {
ProgressViewer viewer = (ProgressViewer) ((component instanceof ProgressViewer) ? component : ((JPanel) component).getComponent(1));
if (viewer.getSubProgressReceiver() == subProgressReceiver) {
scrollablePanel1.remove(component);
break;
}
}
}
@Override
public void setMessage(String message) throws OperationCancelled {
}
@Override
public void checkForCancellation() throws OperationCancelled {
}
@Override
public void reset() throws OperationCancelled {
}
@Override
public void subProgressStarted(SubProgressReceiver subProgressReceiver) throws OperationCancelled {
}
});
}
jScrollPane1.validate();
});
}
use of org.pepsoft.util.SubProgressReceiver in project WorldPainter by Captain-Chaos.
the class JavaWorldMerger method mergeDimension.
private void mergeDimension(final File worldDir, File backupWorldDir, final Dimension dimension, final Platform platform, ProgressReceiver progressReceiver) throws ProgressReceiver.OperationCancelled, IOException {
if (progressReceiver != null) {
progressReceiver.setMessage("merging " + dimension.getName() + " dimension");
}
final File dimensionDir, backupDimensionDir;
switch(dimension.getDim()) {
case org.pepsoft.worldpainter.Constants.DIM_NORMAL:
dimensionDir = worldDir;
backupDimensionDir = backupWorldDir;
break;
case org.pepsoft.worldpainter.Constants.DIM_NETHER:
dimensionDir = new File(worldDir, "DIM-1");
backupDimensionDir = new File(backupWorldDir, "DIM-1");
break;
case org.pepsoft.worldpainter.Constants.DIM_END:
dimensionDir = new File(worldDir, "DIM1");
backupDimensionDir = new File(backupWorldDir, "DIM1");
break;
default:
throw new IllegalArgumentException("Dimension " + dimension.getDim() + " not supported");
}
File regionDir = new File(dimensionDir, "region");
if (!regionDir.exists()) {
regionDir.mkdirs();
}
dimension.rememberChanges();
try {
// Gather all layers used on the map
final Map<Layer, LayerExporter> exporters = new HashMap<>();
Set<Layer> allLayers = dimension.getAllLayers(false);
allLayers.addAll(dimension.getMinimumLayers());
// If there are combined layers, apply them and gather any newly
// added layers, recursively
boolean done;
do {
done = true;
for (Layer layer : new HashSet<>(allLayers)) {
if (layer instanceof CombinedLayer) {
// Apply the combined layer
Set<Layer> addedLayers = ((CombinedLayer) layer).apply(dimension);
// Remove the combined layer from the list
allLayers.remove(layer);
// Add any layers it might have added
allLayers.addAll(addedLayers);
// Signal that we have to go around at least once more,
// in case any of the newly added layers are themselves
// combined layers
done = false;
}
}
} while (!done);
// Load all layer settings into the exporters
for (Layer layer : allLayers) {
@SuppressWarnings("unchecked") LayerExporter exporter = layer.getExporter();
if (exporter != null) {
exporter.setSettings(dimension.getLayerSettings(layer));
exporters.put(layer, exporter);
}
}
// Sort tiles into regions
int lowestRegionX = Integer.MAX_VALUE, highestRegionX = Integer.MIN_VALUE, lowestRegionZ = Integer.MAX_VALUE, highestRegionZ = Integer.MIN_VALUE;
Map<Point, Map<Point, Tile>> tilesByRegion = new HashMap<>();
final boolean tileSelection = selectedTiles != null;
if (tileSelection) {
// Sanity check
assert selectedDimensions.size() == 1;
assert selectedDimensions.contains(dimension.getDim());
for (Point tileCoords : selectedTiles) {
Tile tile = dimension.getTile(tileCoords);
boolean nonReadOnlyChunksFound = false;
outerLoop: for (int chunkX = 0; chunkX < TILE_SIZE; chunkX += 16) {
for (int chunkY = 0; chunkY < TILE_SIZE; chunkY += 16) {
if (!tile.getBitLayerValue(ReadOnly.INSTANCE, chunkX, chunkY)) {
nonReadOnlyChunksFound = true;
break outerLoop;
}
}
}
if (!nonReadOnlyChunksFound) {
// be merged
continue;
}
int regionX = tileCoords.x >> 2;
int regionZ = tileCoords.y >> 2;
Point regionCoords = new Point(regionX, regionZ);
Map<Point, Tile> tilesForRegion = tilesByRegion.computeIfAbsent(regionCoords, k -> new HashMap<>());
tilesForRegion.put(tileCoords, tile);
if (regionX < lowestRegionX) {
lowestRegionX = regionX;
}
if (regionX > highestRegionX) {
highestRegionX = regionX;
}
if (regionZ < lowestRegionZ) {
lowestRegionZ = regionZ;
}
if (regionZ > highestRegionZ) {
highestRegionZ = regionZ;
}
}
} else {
for (Tile tile : dimension.getTiles()) {
boolean nonReadOnlyChunksFound = false;
outerLoop: for (int chunkX = 0; chunkX < TILE_SIZE; chunkX += 16) {
for (int chunkY = 0; chunkY < TILE_SIZE; chunkY += 16) {
if (!tile.getBitLayerValue(ReadOnly.INSTANCE, chunkX, chunkY)) {
nonReadOnlyChunksFound = true;
break outerLoop;
}
}
}
if (!nonReadOnlyChunksFound) {
// be merged
continue;
}
int regionX = tile.getX() >> 2;
int regionZ = tile.getY() >> 2;
Point regionCoords = new Point(regionX, regionZ);
Map<Point, Tile> tilesForRegion = tilesByRegion.computeIfAbsent(regionCoords, k -> new HashMap<>());
tilesForRegion.put(new Point(tile.getX(), tile.getY()), tile);
if (regionX < lowestRegionX) {
lowestRegionX = regionX;
}
if (regionX > highestRegionX) {
highestRegionX = regionX;
}
if (regionZ < lowestRegionZ) {
lowestRegionZ = regionZ;
}
if (regionZ > highestRegionZ) {
highestRegionZ = regionZ;
}
}
}
// Read the region coordinates of the existing map
final File backupRegionDir = new File(backupDimensionDir, "region");
final Pattern regionFilePattern = platform.equals(DefaultPlugin.JAVA_ANVIL) ? Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mca") : Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mcr");
File[] existingRegionFiles = backupRegionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches());
Map<Point, File> existingRegions = new HashMap<>();
for (File file : existingRegionFiles) {
String[] parts = file.getName().split("\\.");
int regionX = Integer.parseInt(parts[1]);
int regionZ = Integer.parseInt(parts[2]);
existingRegions.put(new Point(regionX, regionZ), file);
if (regionX < lowestRegionX) {
lowestRegionX = regionX;
}
if (regionX > highestRegionX) {
highestRegionX = regionX;
}
if (regionZ < lowestRegionZ) {
lowestRegionZ = regionZ;
}
if (regionZ > highestRegionZ) {
highestRegionZ = regionZ;
}
}
final Set<Point> allRegionCoords = new HashSet<>();
allRegionCoords.addAll(tilesByRegion.keySet());
allRegionCoords.addAll(existingRegions.keySet());
// Sort the regions to export the first two rows together, and then
// row by row, to get the optimum tempo of performing fixups
List<Point> sortedRegions = new ArrayList<>(allRegionCoords.size());
if (lowestRegionZ == highestRegionZ) {
// No point in sorting it
sortedRegions.addAll(allRegionCoords);
} else {
for (int x = lowestRegionX; x <= highestRegionX; x++) {
for (int z = lowestRegionZ; z <= (lowestRegionZ + 1); z++) {
Point regionCoords = new Point(x, z);
if (allRegionCoords.contains(regionCoords)) {
sortedRegions.add(regionCoords);
}
}
}
for (int z = lowestRegionZ + 2; z <= highestRegionZ; z++) {
for (int x = lowestRegionX; x <= highestRegionX; x++) {
Point regionCoords = new Point(x, z);
if (allRegionCoords.contains(regionCoords)) {
sortedRegions.add(regionCoords);
}
}
}
}
// Merge each individual region
final WorldPainterChunkFactory chunkFactory = new WorldPainterChunkFactory(dimension, exporters, world.getPlatform(), world.getMaxHeight());
Runtime runtime = Runtime.getRuntime();
runtime.gc();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long memoryInUse = totalMemory - freeMemory;
long maxMemory = runtime.maxMemory();
long maxMemoryAvailable = maxMemory - memoryInUse;
int maxThreadsByMem = (int) (maxMemoryAvailable / 250000000L);
int threads;
if (System.getProperty("org.pepsoft.worldpainter.threads") != null) {
threads = Math.max(Math.min(Integer.parseInt(System.getProperty("org.pepsoft.worldpainter.threads")), tilesByRegion.size()), 1);
} else {
threads = Math.max(Math.min(Math.min(maxThreadsByMem, runtime.availableProcessors()), allRegionCoords.size()), 1);
}
logger.info("Using " + threads + " thread(s) for merge (cores: " + runtime.availableProcessors() + ", available memory: " + (maxMemoryAvailable / 1048576L) + " MB)");
final Map<Point, List<Fixup>> fixups = new HashMap<>();
final Set<Point> exportedRegions = new HashSet<>();
ExecutorService executor = Executors.newFixedThreadPool(threads, new ThreadFactory() {
@Override
public synchronized Thread newThread(Runnable r) {
Thread thread = new Thread(threadGroup, r, "Merger-" + nextID++);
thread.setPriority(Thread.MIN_PRIORITY);
return thread;
}
private final ThreadGroup threadGroup = new ThreadGroup("Mergers");
private int nextID = 1;
});
final ParallelProgressManager parallelProgressManager = (progressReceiver != null) ? new ParallelProgressManager(progressReceiver, allRegionCoords.size()) : null;
try {
// Merge each individual region
for (final Point regionCoords : sortedRegions) {
if (existingRegions.containsKey(regionCoords)) {
if (tilesByRegion.containsKey(regionCoords)) {
// Region exists in new and existing maps; merge it
final Map<Point, Tile> tiles = tilesByRegion.get(regionCoords);
executor.execute(() -> {
ProgressReceiver progressReceiver1 = (parallelProgressManager != null) ? parallelProgressManager.createProgressReceiver() : null;
if (progressReceiver1 != null) {
try {
progressReceiver1.checkForCancellation();
} catch (ProgressReceiver.OperationCancelled e) {
return;
}
}
try {
List<Fixup> regionFixups = new ArrayList<>();
WorldRegion minecraftWorld = new WorldRegion(regionCoords.x, regionCoords.y, dimension.getMaxHeight(), platform);
try {
String regionWarnings = mergeRegion(minecraftWorld, backupRegionDir, dimension, platform, regionCoords, tiles, tileSelection, exporters, chunkFactory, regionFixups, (progressReceiver1 != null) ? new SubProgressReceiver(progressReceiver1, 0.0f, 0.9f) : null);
if (regionWarnings != null) {
if (warnings == null) {
warnings = regionWarnings;
} else {
warnings = warnings + regionWarnings;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Merged region " + regionCoords.x + "," + regionCoords.y);
}
} finally {
minecraftWorld.save(worldDir, dimension.getDim());
}
synchronized (fixups) {
if (!regionFixups.isEmpty()) {
fixups.put(new Point(regionCoords.x, regionCoords.y), regionFixups);
}
exportedRegions.add(regionCoords);
}
// thread is not already doing it
if (performingFixups.tryAcquire()) {
try {
Map<Point, List<Fixup>> myFixups = new HashMap<>();
synchronized (fixups) {
for (Iterator<Map.Entry<Point, List<Fixup>>> i = fixups.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<Point, List<Fixup>> entry = i.next();
Point fixupRegionCoords = entry.getKey();
if (isReadyForFixups(allRegionCoords, exportedRegions, fixupRegionCoords)) {
myFixups.put(fixupRegionCoords, entry.getValue());
i.remove();
}
}
}
if (!myFixups.isEmpty()) {
performFixups(worldDir, dimension, platform, (progressReceiver1 != null) ? new SubProgressReceiver(progressReceiver1, 0.9f, 0.1f) : null, myFixups);
}
} finally {
performingFixups.release();
}
}
} catch (Throwable t) {
if (progressReceiver1 != null) {
progressReceiver1.exceptionThrown(t);
} else {
logger.error("Exception while exporting region", t);
}
}
});
} else {
// Region only exists in existing world. Copy it to the new
// world
ProgressReceiver subProgressReceiver = (parallelProgressManager != null) ? parallelProgressManager.createProgressReceiver() : null;
if (subProgressReceiver != null) {
subProgressReceiver.setMessage("Copying region " + regionCoords.x + "," + regionCoords.y + " unchanged");
}
FileUtils.copyFileToDir(existingRegions.get(regionCoords), regionDir, subProgressReceiver);
synchronized (fixups) {
exportedRegions.add(regionCoords);
}
if (logger.isDebugEnabled()) {
logger.debug("Copied region " + regionCoords.x + "," + regionCoords.y);
}
}
} else {
// Region only exists in new world. Create it as new
executor.execute(() -> {
ProgressReceiver progressReceiver1 = (parallelProgressManager != null) ? parallelProgressManager.createProgressReceiver() : null;
if (progressReceiver1 != null) {
try {
progressReceiver1.checkForCancellation();
} catch (ProgressReceiver.OperationCancelled e) {
return;
}
}
try {
WorldRegion minecraftWorld = new WorldRegion(regionCoords.x, regionCoords.y, dimension.getMaxHeight(), platform);
ExportResults exportResults = null;
try {
exportResults = exportRegion(minecraftWorld, dimension, null, platform, regionCoords, tileSelection, exporters, null, chunkFactory, null, (progressReceiver1 != null) ? new SubProgressReceiver(progressReceiver1, 0.9f, 0.1f) : null);
if (logger.isDebugEnabled()) {
logger.debug("Generated region " + regionCoords.x + "," + regionCoords.y);
}
} finally {
if ((exportResults != null) && exportResults.chunksGenerated) {
minecraftWorld.save(worldDir, dimension.getDim());
}
}
synchronized (fixups) {
if ((exportResults.fixups != null) && (!exportResults.fixups.isEmpty())) {
fixups.put(new Point(regionCoords.x, regionCoords.y), exportResults.fixups);
}
exportedRegions.add(regionCoords);
}
// thread is not already doing it
if (performingFixups.tryAcquire()) {
try {
Map<Point, List<Fixup>> myFixups = new HashMap<>();
synchronized (fixups) {
for (Iterator<Map.Entry<Point, List<Fixup>>> i = fixups.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<Point, List<Fixup>> entry = i.next();
Point fixupRegionCoords = entry.getKey();
if (isReadyForFixups(allRegionCoords, exportedRegions, fixupRegionCoords)) {
myFixups.put(fixupRegionCoords, entry.getValue());
i.remove();
}
}
}
if (!myFixups.isEmpty()) {
performFixups(worldDir, dimension, platform, (progressReceiver1 != null) ? new SubProgressReceiver(progressReceiver1, 0.9f, 0.1f) : null, myFixups);
}
} finally {
performingFixups.release();
}
}
} catch (Throwable t) {
if (progressReceiver1 != null) {
progressReceiver1.exceptionThrown(t);
} else {
logger.error("Exception while exporting region", t);
}
}
});
}
}
} finally {
executor.shutdown();
try {
executor.awaitTermination(1000, TimeUnit.DAYS);
} catch (InterruptedException e) {
throw new RuntimeException("Thread interrupted while waiting for all tasks to finish", e);
}
}
// performing fixups and thread B added new ones and then quit
synchronized (fixups) {
if (!fixups.isEmpty()) {
if (progressReceiver != null) {
progressReceiver.setMessage("doing remaining fixups for " + dimension.getName());
progressReceiver.reset();
}
performFixups(worldDir, dimension, platform, (progressReceiver != null) ? new SubProgressReceiver(progressReceiver, 0.9f, 0.1f) : null, fixups);
}
}
if (progressReceiver != null) {
progressReceiver.setProgress(1.0f);
}
} finally {
// Undo any changes we made (such as applying any combined layers)
if (dimension.undoChanges()) {
// TODO: some kind of cleverer undo mechanism (undo history
// cloning?) so we don't mess up the user's redo history
dimension.clearRedo();
dimension.armSavePoint();
}
}
}
Aggregations