Search in sources :

Example 1 with SubProgressReceiver

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");
}
Also used : JavaWorldExporter(org.pepsoft.worldpainter.exporting.JavaWorldExporter) OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) CertificateFactory(java.security.cert.CertificateFactory) X509Certificate(java.security.cert.X509Certificate) FileInputStream(java.io.FileInputStream) GZIPInputStream(java.util.zip.GZIPInputStream) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) ProgressReceiver(org.pepsoft.util.ProgressReceiver) File(java.io.File) ObjectInputStream(java.io.ObjectInputStream)

Example 2 with SubProgressReceiver

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;
}
Also used : SimpleTheme(org.pepsoft.worldpainter.themes.SimpleTheme) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) ResourcesExporterSettings(org.pepsoft.worldpainter.layers.exporters.ResourcesExporter.ResourcesExporterSettings) Dimension(org.pepsoft.worldpainter.Dimension) FrostSettings(org.pepsoft.worldpainter.layers.exporters.FrostExporter.FrostSettings) File(java.io.File) EventVO(org.pepsoft.worldpainter.vo.EventVO)

Example 3 with SubProgressReceiver

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;
}
Also used : SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) Direction(org.pepsoft.minecraft.Direction)

Example 4 with SubProgressReceiver

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();
    });
}
Also used : SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) ProgressReceiver(org.pepsoft.util.ProgressReceiver)

Example 5 with SubProgressReceiver

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();
        }
    }
}
Also used : ThreadFactory(java.util.concurrent.ThreadFactory) List(java.util.List) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) ProgressReceiver(org.pepsoft.util.ProgressReceiver) HistoryEntry(org.pepsoft.worldpainter.history.HistoryEntry) Pattern(java.util.regex.Pattern) ParallelProgressManager(org.pepsoft.util.ParallelProgressManager) ExecutorService(java.util.concurrent.ExecutorService)

Aggregations

SubProgressReceiver (org.pepsoft.util.SubProgressReceiver)10 ProgressReceiver (org.pepsoft.util.ProgressReceiver)4 File (java.io.File)2 List (java.util.List)2 AwtUtils.doOnEventThread (org.pepsoft.util.AwtUtils.doOnEventThread)2 ParallelProgressManager (org.pepsoft.util.ParallelProgressManager)2 OperationCancelled (org.pepsoft.util.ProgressReceiver.OperationCancelled)2 Dimension (org.pepsoft.worldpainter.Dimension)2 Tile (org.pepsoft.worldpainter.Tile)2 CombinedLayer (org.pepsoft.worldpainter.layers.CombinedLayer)2 CustomLayer (org.pepsoft.worldpainter.layers.CustomLayer)2 Layer (org.pepsoft.worldpainter.layers.Layer)2 FileInputStream (java.io.FileInputStream)1 FileOutputStream (java.io.FileOutputStream)1 ObjectInputStream (java.io.ObjectInputStream)1 PrintWriter (java.io.PrintWriter)1 CertificateFactory (java.security.cert.CertificateFactory)1 X509Certificate (java.security.cert.X509Certificate)1 java.util (java.util)1 ExecutorService (java.util.concurrent.ExecutorService)1