Search in sources :

Example 6 with OperationCancelled

use of org.pepsoft.util.ProgressReceiver.OperationCancelled in project WorldPainter by Captain-Chaos.

the class AbstractWorldExporter method parallelExportRegions.

protected final ChunkFactory.Stats parallelExportRegions(Dimension dimension, Platform platform, File worldDir, ProgressReceiver progressReceiver) throws OperationCancelled {
    if (progressReceiver != null) {
        progressReceiver.setMessage("Exporting " + dimension.getName() + " dimension");
    }
    long start = System.currentTimeMillis();
    final Dimension ceiling;
    switch(dimension.getDim()) {
        case DIM_NORMAL:
            ceiling = dimension.getWorld().getDimension(DIM_NORMAL_CEILING);
            break;
        case DIM_NETHER:
            ceiling = dimension.getWorld().getDimension(DIM_NETHER_CEILING);
            break;
        case DIM_END:
            ceiling = dimension.getWorld().getDimension(DIM_END_CEILING);
            break;
        default:
            throw new IllegalArgumentException("Dimension " + dimension.getDim() + " not supported");
    }
    final ChunkFactory.Stats collectedStats = new ChunkFactory.Stats();
    boolean wasDirty = dimension.isDirty(), ceilingWasDirty = (ceiling != null) && ceiling.isDirty();
    dimension.rememberChanges();
    if (ceiling != null) {
        ceiling.rememberChanges();
    }
    try {
        final Map<Layer, LayerExporter> exporters = setupDimensionForExport(dimension);
        final Map<Layer, LayerExporter> ceilingExporters = (ceiling != null) ? setupDimensionForExport(ceiling) : null;
        // Determine regions to export
        int lowestRegionX = Integer.MAX_VALUE, highestRegionX = Integer.MIN_VALUE, lowestRegionZ = Integer.MAX_VALUE, highestRegionZ = Integer.MIN_VALUE;
        final Set<Point> regions = new HashSet<>(), exportedRegions = new HashSet<>();
        final boolean tileSelection = selectedTiles != null;
        if (tileSelection) {
            // Sanity check
            assert selectedDimensions.size() == 1;
            assert selectedDimensions.contains(dimension.getDim());
            for (Point tile : selectedTiles) {
                int regionX = tile.x >> 2;
                int regionZ = tile.y >> 2;
                regions.add(new Point(regionX, regionZ));
                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()) {
                // Also add regions for any bedrock wall and/or border
                // tiles, if present
                int r = (((dimension.getBorder() != null) && (!dimension.getBorder().isEndless())) ? dimension.getBorderSize() : 0) + (((dimension.getBorder() == null) || (!dimension.getBorder().isEndless())) && dimension.isBedrockWall() ? 1 : 0);
                for (int dx = -r; dx <= r; dx++) {
                    for (int dy = -r; dy <= r; dy++) {
                        int regionX = (tile.getX() + dx) >> 2;
                        int regionZ = (tile.getY() + dy) >> 2;
                        regions.add(new Point(regionX, regionZ));
                        if (regionX < lowestRegionX) {
                            lowestRegionX = regionX;
                        }
                        if (regionX > highestRegionX) {
                            highestRegionX = regionX;
                        }
                        if (regionZ < lowestRegionZ) {
                            lowestRegionZ = regionZ;
                        }
                        if (regionZ > highestRegionZ) {
                            highestRegionZ = regionZ;
                        }
                    }
                }
            }
            if (ceiling != null) {
                for (Tile tile : ceiling.getTiles()) {
                    int regionX = tile.getX() >> 2;
                    int regionZ = tile.getY() >> 2;
                    regions.add(new Point(regionX, regionZ));
                    if (regionX < lowestRegionX) {
                        lowestRegionX = regionX;
                    }
                    if (regionX > highestRegionX) {
                        highestRegionX = regionX;
                    }
                    if (regionZ < lowestRegionZ) {
                        lowestRegionZ = regionZ;
                    }
                    if (regionZ > highestRegionZ) {
                        highestRegionZ = regionZ;
                    }
                }
            }
        }
        // Sort the regions to export the first two rows together, and then
        // row by row, to get the optimum tempo of performing fixups
        java.util.List<Point> sortedRegions = new ArrayList<>(regions.size());
        if (lowestRegionZ == highestRegionZ) {
            // No point in sorting it
            sortedRegions.addAll(regions);
        } else {
            for (int x = lowestRegionX; x <= highestRegionX; x++) {
                for (int z = lowestRegionZ; z <= (lowestRegionZ + 1); z++) {
                    Point regionCoords = new Point(x, z);
                    if (regions.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 (regions.contains(regionCoords)) {
                        sortedRegions.add(regionCoords);
                    }
                }
            }
        }
        final WorldPainterChunkFactory chunkFactory = new WorldPainterChunkFactory(dimension, exporters, platform, world.getMaxHeight());
        final WorldPainterChunkFactory ceilingChunkFactory = (ceiling != null) ? new WorldPainterChunkFactory(ceiling, ceilingExporters, platform, world.getMaxHeight()) : null;
        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")), sortedRegions.size()), 1);
        } else {
            threads = Math.max(Math.min(Math.min(maxThreadsByMem, runtime.availableProcessors()), sortedRegions.size()), 1);
        }
        logger.info("Using " + threads + " thread(s) for export (cores: " + runtime.availableProcessors() + ", available memory: " + (maxMemoryAvailable / 1048576L) + " MB)");
        final Map<Point, List<Fixup>> fixups = new HashMap<>();
        ExecutorService executor = Executors.newFixedThreadPool(threads, new ThreadFactory() {

            @Override
            public synchronized Thread newThread(Runnable r) {
                Thread thread = new Thread(threadGroup, r, "Exporter-" + nextID++);
                thread.setPriority(Thread.MIN_PRIORITY);
                return thread;
            }

            private final ThreadGroup threadGroup = new ThreadGroup("Exporters");

            private int nextID = 1;
        });
        final ParallelProgressManager parallelProgressManager = (progressReceiver != null) ? new ParallelProgressManager(progressReceiver, regions.size()) : null;
        try {
            // Export each individual region
            for (Point region : sortedRegions) {
                final Point regionCoords = region;
                executor.execute(() -> {
                    ProgressReceiver progressReceiver1 = (parallelProgressManager != null) ? parallelProgressManager.createProgressReceiver() : null;
                    if (progressReceiver1 != null) {
                        try {
                            progressReceiver1.checkForCancellation();
                        } catch (OperationCancelled e) {
                            return;
                        }
                    }
                    try {
                        WorldRegion worldRegion = new WorldRegion(regionCoords.x, regionCoords.y, dimension.getMaxHeight(), platform);
                        ExportResults exportResults = null;
                        try {
                            exportResults = exportRegion(worldRegion, dimension, ceiling, platform, regionCoords, tileSelection, exporters, ceilingExporters, chunkFactory, ceilingChunkFactory, (progressReceiver1 != null) ? new SubProgressReceiver(progressReceiver1, 0.0f, 0.9f) : null);
                            if (logger.isDebugEnabled()) {
                                logger.debug("Generated region " + regionCoords.x + "," + regionCoords.y);
                            }
                            if (exportResults.chunksGenerated) {
                                synchronized (collectedStats) {
                                    collectedStats.landArea += exportResults.stats.landArea;
                                    collectedStats.surfaceArea += exportResults.stats.surfaceArea;
                                    collectedStats.waterArea += exportResults.stats.waterArea;
                                }
                            }
                        } finally {
                            if ((exportResults != null) && exportResults.chunksGenerated) {
                                long saveStart = System.currentTimeMillis();
                                worldRegion.save(worldDir, dimension.getDim());
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Saving region took {} ms", System.currentTimeMillis() - saveStart);
                                }
                            }
                        }
                        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(regions, 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(366, 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, fixups);
            }
        }
        // Calculate total size of dimension
        collectedStats.time = System.currentTimeMillis() - start;
        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();
        }
        // If the dimension wasn't dirty make sure it still isn't
        dimension.setDirty(wasDirty);
        if (ceiling != null) {
            // Undo any changes we made (such as applying any combined layers)
            if (ceiling.undoChanges()) {
                // TODO: some kind of cleverer undo mechanism (undo history
                // cloning?) so we don't mess up the user's redo history
                ceiling.clearRedo();
                ceiling.armSavePoint();
            }
            // If the dimension wasn't dirty make sure it still isn't
            ceiling.setDirty(ceilingWasDirty);
        }
    }
    return collectedStats;
}
Also used : List(java.util.List) OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) Tile(org.pepsoft.worldpainter.Tile) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) Dimension(org.pepsoft.worldpainter.Dimension) CustomLayer(org.pepsoft.worldpainter.layers.CustomLayer) CombinedLayer(org.pepsoft.worldpainter.layers.CombinedLayer) Layer(org.pepsoft.worldpainter.layers.Layer) java.util(java.util) ParallelProgressManager(org.pepsoft.util.ParallelProgressManager) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) ProgressReceiver(org.pepsoft.util.ProgressReceiver)

Example 7 with OperationCancelled

use of org.pepsoft.util.ProgressReceiver.OperationCancelled in project WorldPainter by Captain-Chaos.

the class App method newWorld.

private void newWorld() {
    if (!saveIfNecessary()) {
        return;
    }
    Configuration config = Configuration.getInstance();
    final NewWorldDialog dialog = new NewWorldDialog(this, strings.getString("generated.world"), World2.DEFAULT_OCEAN_SEED, config.getDefaultPlatform(), DIM_NORMAL, config.getDefaultMaxHeight());
    dialog.setVisible(true);
    if (!dialog.isCancelled()) {
        // Free up memory of the world and the undo buffer
        setWorld(null);
        if (!dialog.checkMemoryRequirements(this)) {
            return;
        }
        World2 newWorld = ProgressDialog.executeTask(this, new ProgressTask<World2>() {

            @Override
            public String getName() {
                return strings.getString("creating.new.world");
            }

            @Override
            public World2 execute(ProgressReceiver progressReceiver) throws OperationCancelled {
                return dialog.getSelectedWorld(progressReceiver);
            }
        });
        if (newWorld == null) {
            // Operation cancelled by user
            return;
        }
        // Log an event
        EventVO event = new EventVO(EVENT_KEY_ACTION_NEW_WORLD).addTimestamp();
        event.setAttribute(ATTRIBUTE_KEY_MAX_HEIGHT, newWorld.getMaxHeight());
        event.setAttribute(ATTRIBUTE_KEY_TILES, newWorld.getDimension(0).getTiles().size());
        config.logEvent(event);
        setWorld(newWorld);
        lastSelectedFile = null;
        disableImportedWorldOperation();
    }
}
Also used : OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) EventVO(org.pepsoft.worldpainter.vo.EventVO)

Example 8 with OperationCancelled

use of org.pepsoft.util.ProgressReceiver.OperationCancelled in project WorldPainter by Captain-Chaos.

the class App method open.

public void open(final File file) {
    logger.info("Loading world " + file.getAbsolutePath());
    // Free up memory of the world and the undo buffer
    setWorld(null);
    final World2 newWorld = ProgressDialog.executeTask(this, new ProgressTask<World2>() {

        @Override
        public String getName() {
            return strings.getString("loading.world");
        }

        @Override
        public World2 execute(ProgressReceiver progressReceiver) throws OperationCancelled {
            try {
                WorldIO worldIO = new WorldIO();
                worldIO.load(new FileInputStream(file));
                World2 world = worldIO.getWorld();
                world.addHistoryEntry(HistoryEntry.WORLD_LOADED, file);
                if (logger.isDebugEnabled() && (world.getMetadata() != null)) {
                    logMetadataAsDebug(world.getMetadata());
                }
                return world;
            } catch (UnloadableWorldException e) {
                logger.error("Could not load world from file " + file, e);
                if (e.getMetadata() != null) {
                    logMetadataAsError(e.getMetadata());
                }
                reportUnloadableWorldException(e);
                return null;
            } catch (IOException e) {
                throw new RuntimeException("I/O error while loading world", e);
            }
        }

        private void appendMetadata(StringBuilder sb, Map<String, Object> metadata) {
            for (Map.Entry<String, Object> entry : metadata.entrySet()) {
                switch(entry.getKey()) {
                    case World2.METADATA_KEY_WP_VERSION:
                        sb.append("Saved with WorldPainter ").append(entry.getValue());
                        String build = (String) metadata.get(World2.METADATA_KEY_WP_BUILD);
                        if (build != null) {
                            sb.append(" (").append(build).append(')');
                        }
                        sb.append('\n');
                        break;
                    case World2.METADATA_KEY_TIMESTAMP:
                        sb.append("Saved on ").append(SimpleDateFormat.getDateTimeInstance().format((Date) entry.getValue())).append('\n');
                        break;
                    case World2.METADATA_KEY_PLUGINS:
                        String[][] plugins = (String[][]) entry.getValue();
                        for (String[] plugin : plugins) {
                            sb.append("Plugin: ").append(plugin[0]).append(" (").append(plugin[1]).append(")\n");
                        }
                        break;
                }
            }
        }

        private void logMetadataAsDebug(Map<String, Object> metadata) {
            StringBuilder sb = new StringBuilder("Metadata from world file:\n");
            appendMetadata(sb, metadata);
            logger.debug(sb.toString());
        }

        private void logMetadataAsError(Map<String, Object> metadata) {
            StringBuilder sb = new StringBuilder("Metadata from world file:\n");
            appendMetadata(sb, metadata);
            logger.error(sb.toString());
        }

        private void reportUnloadableWorldException(UnloadableWorldException e) {
            try {
                String text;
                if (e.getMetadata() != null) {
                    StringBuilder sb = new StringBuilder("WorldPainter could not load the file. The cause may be one of:\n" + "\n" + "* The file is damaged or corrupted\n" + "* The file was created with a newer version of WorldPainter\n" + "* The file was created using WorldPainter plugins which you do not have\n" + "\n");
                    appendMetadata(sb, e.getMetadata());
                    text = sb.toString();
                } else {
                    text = "WorldPainter could not load the file. The cause may be one of:\n" + "\n" + "* The file is not a WorldPainter world\n" + "* The file is damaged or corrupted\n" + "* The file was created with a newer version of WorldPainter\n" + "* The file was created using WorldPainter plugins which you do not have";
                }
                SwingUtilities.invokeAndWait(() -> showMessageDialog(App.this, text, strings.getString("file.damaged"), ERROR_MESSAGE));
            } catch (InterruptedException e2) {
                throw new RuntimeException("Thread interrupted while reporting unloadable file " + file, e2);
            } catch (InvocationTargetException e2) {
                throw new RuntimeException("Invocation target exception while reporting unloadable file " + file, e2);
            }
        }
    }, false);
    if (newWorld == null) {
        // The file was damaged
        return;
    }
    if (!isBackupFile(file)) {
        lastSelectedFile = file;
    } else {
        lastSelectedFile = null;
    }
    // Log an event
    Configuration config = Configuration.getInstance();
    EventVO event = new EventVO(EVENT_KEY_ACTION_OPEN_WORLD).addTimestamp();
    event.setAttribute(ATTRIBUTE_KEY_MAX_HEIGHT, newWorld.getMaxHeight());
    Dimension loadedDimension = newWorld.getDimension(0);
    event.setAttribute(ATTRIBUTE_KEY_TILES, loadedDimension.getTiles().size());
    logLayers(loadedDimension, event, "");
    loadedDimension = newWorld.getDimension(1);
    if (loadedDimension != null) {
        event.setAttribute(ATTRIBUTE_KEY_NETHER_TILES, loadedDimension.getTiles().size());
        logLayers(loadedDimension, event, "nether.");
    }
    loadedDimension = newWorld.getDimension(2);
    if (loadedDimension != null) {
        event.setAttribute(ATTRIBUTE_KEY_END_TILES, loadedDimension.getTiles().size());
        logLayers(loadedDimension, event, "end.");
    }
    if (newWorld.getImportedFrom() != null) {
        event.setAttribute(ATTRIBUTE_KEY_IMPORTED_WORLD, true);
    }
    config.logEvent(event);
    Set<World2.Warning> warnings = newWorld.getWarnings();
    if ((warnings != null) && (!warnings.isEmpty())) {
        for (World2.Warning warning : warnings) {
            switch(warning) {
                case AUTO_BIOMES_DISABLED:
                    if (showOptionDialog(this, "Automatic Biomes were previously enabled for this world but have been disabled.\nPress More Info for more information, including how to reenable it.", "Automatic Biomes Disabled", DEFAULT_OPTION, WARNING_MESSAGE, null, new Object[] { "More Info", "OK" }, "OK") == 0) {
                        try {
                            DesktopUtils.open(new URL("https://www.worldpainter.net/doc/legacy/newautomaticbiomes"));
                        } catch (MalformedURLException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
                case AUTO_BIOMES_ENABLED:
                    if (showOptionDialog(this, "Automatic Biomes were previously disabled for this world but have been enabled.\nPress More Info for more information, including how to disable it.", "Automatic Biomes Enabled", DEFAULT_OPTION, WARNING_MESSAGE, null, new Object[] { "More Info", "OK" }, "OK") == 0) {
                        try {
                            DesktopUtils.open(new URL("https://www.worldpainter.net/doc/legacy/newautomaticbiomes"));
                        } catch (MalformedURLException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    break;
            }
        }
    }
    if (newWorld.isAskToConvertToAnvil() && (newWorld.getMaxHeight() == DEFAULT_MAX_HEIGHT_1) && (newWorld.getImportedFrom() == null)) {
        if (showConfirmDialog(this, strings.getString("this.world.is.128.blocks.high"), strings.getString("convert.world.height"), YES_NO_OPTION) == YES_OPTION) {
            ChangeHeightDialog.resizeWorld(newWorld, HeightTransform.IDENTITY, DEFAULT_MAX_HEIGHT_2, this);
            newWorld.addHistoryEntry(HistoryEntry.WORLD_MAX_HEIGHT_CHANGED, DEFAULT_MAX_HEIGHT_2);
            // with the old format
            if (newWorld.getPlatform() != null) {
                newWorld.setPlatform(DefaultPlugin.JAVA_ANVIL);
            }
            // Log event
            config.logEvent(new EventVO(EVENT_KEY_ACTION_MIGRATE_HEIGHT).addTimestamp());
        }
        // Don't ask again, no matter what the user answered
        newWorld.setAskToConvertToAnvil(false);
    }
    if (newWorld.isAskToRotate() && (newWorld.getUpIs() == Direction.WEST) && (newWorld.getImportedFrom() == null)) {
        if (showConfirmDialog(this, strings.getString("this.world.was.created.when.north.was.to.the.right"), strings.getString("rotate.world"), YES_NO_OPTION) == YES_OPTION) {
            ProgressDialog.executeTask(this, new ProgressTask<java.lang.Void>() {

                @Override
                public String getName() {
                    return strings.getString("rotating.world");
                }

                @Override
                public java.lang.Void execute(ProgressReceiver progressReceiver) throws OperationCancelled {
                    newWorld.transform(CoordinateTransform.ROTATE_CLOCKWISE_270_DEGREES, progressReceiver);
                    for (Dimension dimension : newWorld.getDimensions()) {
                        newWorld.addHistoryEntry(HistoryEntry.WORLD_DIMENSION_ROTATED, dimension.getName(), 270);
                    }
                    return null;
                }
            }, false);
            // Log event
            config.logEvent(new EventVO(EVENT_KEY_ACTION_MIGRATE_ROTATION).addTimestamp());
        }
        // Don't ask again, no matter what the user answered
        newWorld.setAskToRotate(false);
    }
    // Make sure the world name is always the same as the file name, to
    // avoid confusion, unless the only difference is illegal filename
    // characters changed into underscores. Do this here as well as when
    // saving, because the file might have been renamed
    String name = isBackupFile(file) ? getOriginalFile(file).getName() : file.getName();
    int p = name.lastIndexOf('.');
    if (p != -1) {
        name = name.substring(0, p);
    }
    String worldName = newWorld.getName();
    if (worldName.length() != name.length()) {
        newWorld.setName(name);
    } else {
        for (int i = 0; i < name.length(); i++) {
            if ((name.charAt(i) != '_') && (name.charAt(i) != worldName.charAt(i))) {
                newWorld.setName(name);
                break;
            }
        }
    }
    newWorld.setDirty(false);
    setWorld(newWorld);
    addRecentlyUsedWorld(file);
    if (newWorld.getImportedFrom() != null) {
        enableImportedWorldOperation();
    } else {
        disableImportedWorldOperation();
    }
}
Also used : MalformedURLException(java.net.MalformedURLException) URL(java.net.URL) HistoryEntry(org.pepsoft.worldpainter.history.HistoryEntry) Void(java.lang.Void) EventVO(org.pepsoft.worldpainter.vo.EventVO) OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) InvocationTargetException(java.lang.reflect.InvocationTargetException) Paint(org.pepsoft.worldpainter.painting.Paint)

Example 9 with OperationCancelled

use of org.pepsoft.util.ProgressReceiver.OperationCancelled in project WorldPainter by Captain-Chaos.

the class App method importCustomItemsFromWorld.

private void importCustomItemsFromWorld(CustomItemsTreeModel.ItemType itemType) {
    File dir;
    Configuration config = Configuration.getInstance();
    if (lastSelectedFile != null) {
        dir = lastSelectedFile.getParentFile();
    } else if ((config != null) && (config.getWorldDirectory() != null)) {
        dir = config.getWorldDirectory();
    } else {
        dir = DesktopUtils.getDocumentsFolder();
    }
    File selectedFile = FileUtils.selectFileForOpen(this, "Select a WorldPainter world", dir, new FileFilter() {

        @Override
        public boolean accept(File f) {
            return f.isDirectory() || f.getName().toLowerCase().endsWith(".world");
        }

        @Override
        public String getDescription() {
            return strings.getString("worldpainter.files.world");
        }
    });
    if (selectedFile != null) {
        if (!selectedFile.isFile()) {
            if (logger.isDebugEnabled()) {
                try {
                    logger.debug("Path not a file according to File.isFile(): \"" + selectedFile + "\" (directory: " + selectedFile.isDirectory() + "; length: " + selectedFile.length() + "; absolutePath: \"" + selectedFile.getAbsolutePath() + "\"; canonicalPath: \"" + selectedFile.getCanonicalPath() + "\")");
                } catch (IOException e) {
                    logger.debug("Path not a file according to File.isFile(): \"" + selectedFile + "\" (directory: " + selectedFile.isDirectory() + "; length: " + selectedFile.length() + "; absolutePath: \"" + selectedFile.getAbsolutePath() + "\")");
                    logger.warn("I/O error while trying to report canonical path of file: \"" + selectedFile + "\"", e);
                }
            }
            showMessageDialog(this, "The specified path does not exist or is not a file", "File Does Not Exist", ERROR_MESSAGE);
            return;
        }
        if (!selectedFile.canRead()) {
            showMessageDialog(this, "WorldPainter is not authorised to read the selected file", "Access Denied", ERROR_MESSAGE);
            return;
        }
        final World2 selectedWorld = ProgressDialog.executeTask(this, new ProgressTask<World2>() {

            @Override
            public String getName() {
                return strings.getString("loading.world");
            }

            @Override
            public World2 execute(ProgressReceiver progressReceiver) throws OperationCancelled {
                try {
                    WorldIO worldIO = new WorldIO();
                    worldIO.load(new FileInputStream(selectedFile));
                    World2 world = worldIO.getWorld();
                    return world;
                } catch (UnloadableWorldException e) {
                    logger.error("Could not load world from file " + selectedFile, e);
                    if (e.getMetadata() != null) {
                        logMetadataAsError(e.getMetadata());
                    }
                    reportUnloadableWorldException(e);
                    return null;
                } catch (IOException e) {
                    throw new RuntimeException("I/O error while loading world", e);
                }
            }

            private void appendMetadata(StringBuilder sb, Map<String, Object> metadata) {
                for (Map.Entry<String, Object> entry : metadata.entrySet()) {
                    switch(entry.getKey()) {
                        case World2.METADATA_KEY_WP_VERSION:
                            sb.append("Saved with WorldPainter ").append(entry.getValue());
                            String build = (String) metadata.get(World2.METADATA_KEY_WP_BUILD);
                            if (build != null) {
                                sb.append(" (").append(build).append(')');
                            }
                            sb.append('\n');
                            break;
                        case World2.METADATA_KEY_TIMESTAMP:
                            sb.append("Saved on ").append(SimpleDateFormat.getDateTimeInstance().format((Date) entry.getValue())).append('\n');
                            break;
                        case World2.METADATA_KEY_PLUGINS:
                            String[][] plugins = (String[][]) entry.getValue();
                            for (String[] plugin : plugins) {
                                sb.append("Plugin: ").append(plugin[0]).append(" (").append(plugin[1]).append(")\n");
                            }
                            break;
                    }
                }
            }

            private void logMetadataAsError(Map<String, Object> metadata) {
                StringBuilder sb = new StringBuilder("Metadata from world file:\n");
                appendMetadata(sb, metadata);
                logger.error(sb.toString());
            }

            private void reportUnloadableWorldException(UnloadableWorldException e) {
                try {
                    String text;
                    if (e.getMetadata() != null) {
                        StringBuilder sb = new StringBuilder("WorldPainter could not load the file. The cause may be one of:\n" + "\n" + "* The file is damaged or corrupted\n" + "* The file was created with a newer version of WorldPainter\n" + "* The file was created using WorldPainter plugins which you do not have\n" + "\n");
                        appendMetadata(sb, e.getMetadata());
                        text = sb.toString();
                    } else {
                        text = "WorldPainter could not load the file. The cause may be one of:\n" + "\n" + "* The file is not a WorldPainter world\n" + "* The file is damaged or corrupted\n" + "* The file was created with a newer version of WorldPainter\n" + "* The file was created using WorldPainter plugins which you do not have";
                    }
                    SwingUtilities.invokeAndWait(() -> showMessageDialog(App.this, text, strings.getString("file.damaged"), ERROR_MESSAGE));
                } catch (InterruptedException e2) {
                    throw new RuntimeException("Thread interrupted while reporting unloadable file " + selectedFile, e2);
                } catch (InvocationTargetException e2) {
                    throw new RuntimeException("Invocation target exception while reporting unloadable file " + selectedFile, e2);
                }
            }
        }, false);
        if (selectedWorld == null) {
            // The file was damaged
            return;
        }
        if (CustomItemsTreeModel.hasCustomItems(selectedWorld, itemType)) {
            ImportCustomItemsDialog dialog = new ImportCustomItemsDialog(this, selectedWorld, selectedColourScheme, itemType);
            dialog.setVisible(true);
            if (!dialog.isCancelled()) {
                StringBuilder errors = new StringBuilder();
                Set<CustomLayer> existingCustomLayers = null;
                boolean refreshView = false, showError = false;
                for (Object selectedItem : dialog.getSelectedItems()) {
                    if (selectedItem instanceof CustomLayer) {
                        if (existingCustomLayers == null) {
                            existingCustomLayers = getCustomLayers();
                        }
                        if (existingCustomLayers.contains(selectedItem)) {
                            errors.append("Custom Layer \"" + ((CustomLayer) selectedItem).getName() + "\" already exists\n");
                        } else {
                            registerCustomLayer((CustomLayer) selectedItem, false);
                        }
                    } else if (selectedItem instanceof MixedMaterial) {
                        MixedMaterial customMaterial = (MixedMaterial) selectedItem;
                        int index = findNextCustomTerrainIndex();
                        if (index == -1) {
                            errors.append("No free slots for Custom Terrain \"" + customMaterial.getName() + "\"\n");
                            showError = true;
                            continue;
                        }
                        customMaterial = MixedMaterialManager.getInstance().register(customMaterial);
                        addButtonForNewCustomTerrain(index, customMaterial, false);
                    } else if (selectedItem instanceof CustomBiome) {
                        CustomBiome customBiome = (CustomBiome) selectedItem;
                        if (!customBiomeManager.addCustomBiome(null, customBiome)) {
                            errors.append("ID already in use for Custom Biome " + customBiome.getId() + " (\"" + customBiome + "\")\n");
                            showError = true;
                        } else {
                            refreshView = true;
                        }
                    } else {
                        throw new InternalError("Unsupported custom item type " + selectedItem.getClass() + " encountered");
                    }
                }
                if (refreshView) {
                    view.refreshTiles();
                }
                if (errors.length() > 0) {
                    JOptionPane.showMessageDialog(App.this, "Not all items have been imported:\n\n" + errors, "Not All Items Imported", showError ? JOptionPane.ERROR_MESSAGE : JOptionPane.WARNING_MESSAGE);
                }
            }
        } else {
            String what;
            switch(itemType) {
                case ALL:
                    what = "layers, terrains or biomes";
                    break;
                case BIOME:
                    what = "biomes";
                    break;
                case LAYER:
                    what = "layers";
                    break;
                case TERRAIN:
                    what = "terrains";
                    break;
                default:
                    throw new InternalError();
            }
            showMessageDialog(this, "The selected world has no custom " + what + ".", "No Custom Items", WARNING_MESSAGE);
        }
    }
}
Also used : HistoryEntry(org.pepsoft.worldpainter.history.HistoryEntry) CustomBiome(org.pepsoft.worldpainter.biomeschemes.CustomBiome) FileFilter(javax.swing.filechooser.FileFilter) ImportCustomItemsDialog(org.pepsoft.worldpainter.importing.ImportCustomItemsDialog) OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) InvocationTargetException(java.lang.reflect.InvocationTargetException)

Example 10 with OperationCancelled

use of org.pepsoft.util.ProgressReceiver.OperationCancelled in project WorldPainter by Captain-Chaos.

the class App method save.

/**
 * Save the world to the specified file, overwriting it if it already exists
 * without asking for confirmation. Shows a progress indicator while saving.
 *
 * @param file The file to which to save the world.
 */
private boolean save(File file) {
    // Check for write access to directory
    if (!file.getParentFile().isDirectory()) {
        showMessageDialog(this, strings.getString("the.selected.path.does.not.exist"), strings.getString("non.existant.path"), ERROR_MESSAGE);
        return false;
    }
    if (!file.getParentFile().canWrite()) {
        showMessageDialog(this, strings.getString("you.do.not.have.write.access"), strings.getString("access.denied"), ERROR_MESSAGE);
        return false;
    }
    // Normalise the filename
    String name = file.getName();
    name = name.trim();
    if (name.isEmpty()) {
        // NOI18N
        name = strings.getString("generated.world") + ".world";
    } else {
        name = FileUtils.sanitiseName(name);
    }
    final File normalisedFile = new File(file.getParentFile(), name);
    // Make sure the world name is always the same as the file name, to
    // avoid confusion (unless the only difference is illegal filename
    // characters changed into underscores
    int p = name.lastIndexOf('.');
    if (p != -1) {
        name = name.substring(0, p).trim();
    }
    String worldName = world.getName();
    if (worldName.length() != name.length()) {
        world.setName(name);
        // NOI18N
        setTitle("WorldPainter - " + name + " - " + dimension.getName());
    } else {
        for (int i = 0; i < name.length(); i++) {
            if ((name.charAt(i) != '_') && (name.charAt(i) != worldName.charAt(i))) {
                world.setName(name);
                // NOI18N
                setTitle("WorldPainter - " + name + " - " + dimension.getName());
                break;
            }
        }
    }
    logger.info("Saving world " + world.getName() + " to " + file.getAbsolutePath());
    saveCustomMaterials();
    saveCustomBiomes();
    // currently in use
    if (!paletteManager.isEmpty()) {
        List<CustomLayer> customLayers = new ArrayList<>();
        for (Palette palette : paletteManager.getPalettes()) {
            customLayers.addAll(palette.getLayers());
        }
        dimension.setCustomLayers(customLayers);
    } else {
        dimension.setCustomLayers(Collections.EMPTY_LIST);
    }
    if (dimension != null) {
        Point viewPosition = view.getViewCentreInWorldCoords();
        if (viewPosition != null) {
            this.dimension.setLastViewPosition(viewPosition);
        }
    }
    ProgressDialog.executeTask(this, new ProgressTask<java.lang.Void>() {

        @Override
        public String getName() {
            return strings.getString("saving.world");
        }

        @Override
        public java.lang.Void execute(ProgressReceiver progressReceiver) throws OperationCancelled {
            try {
                Configuration config = Configuration.getInstance();
                if ((config.getWorldFileBackups() > 0) && normalisedFile.isFile()) {
                    progressReceiver.setMessage(strings.getString("creating.backup.s"));
                    for (int i = config.getWorldFileBackups(); i > 0; i--) {
                        File nextBackupFile = (i > 1) ? BackupUtil.getBackupFile(normalisedFile, i - 1) : normalisedFile;
                        if (nextBackupFile.isFile()) {
                            File backupFile = BackupUtil.getBackupFile(normalisedFile, i);
                            if (backupFile.isFile()) {
                                if (!backupFile.delete()) {
                                    throw new RuntimeException("Could not delete old backup file " + backupFile);
                                }
                            }
                            if (!nextBackupFile.renameTo(backupFile)) {
                                throw new RuntimeException("Could not move " + nextBackupFile + " to " + backupFile);
                            }
                        }
                    }
                    progressReceiver.setMessage(null);
                }
                world.addHistoryEntry(HistoryEntry.WORLD_SAVED, normalisedFile);
                WorldIO worldIO = new WorldIO(world);
                worldIO.save(new FileOutputStream(normalisedFile));
                Map<String, byte[]> layoutData = config.getJideLayoutData();
                if (layoutData == null) {
                    layoutData = new HashMap<>();
                }
                layoutData.put(world.getName(), dockingManager.getLayoutRawData());
                config.setJideLayoutData(layoutData);
                return null;
            } catch (IOException e) {
                throw new RuntimeException("I/O error saving file (message: " + e.getMessage() + ")", e);
            }
        }
    }, false);
    // Log an event
    Configuration config = Configuration.getInstance();
    if (config != null) {
        EventVO event = new EventVO(EVENT_KEY_ACTION_SAVE_WORLD).addTimestamp();
        event.setAttribute(ATTRIBUTE_KEY_MAX_HEIGHT, world.getMaxHeight());
        Dimension loadedDimension = world.getDimension(0);
        event.setAttribute(ATTRIBUTE_KEY_TILES, loadedDimension.getTiles().size());
        logLayers(loadedDimension, event, "");
        loadedDimension = world.getDimension(1);
        if (loadedDimension != null) {
            event.setAttribute(ATTRIBUTE_KEY_NETHER_TILES, loadedDimension.getTiles().size());
            logLayers(loadedDimension, event, "nether.");
        }
        loadedDimension = world.getDimension(2);
        if (loadedDimension != null) {
            event.setAttribute(ATTRIBUTE_KEY_END_TILES, loadedDimension.getTiles().size());
            logLayers(loadedDimension, event, "end.");
        }
        if (world.getImportedFrom() != null) {
            event.setAttribute(ATTRIBUTE_KEY_IMPORTED_WORLD, true);
        }
        config.logEvent(event);
    }
    if (currentUndoManager != null) {
        currentUndoManager.armSavePoint();
    }
    world.setDirty(false);
    lastSelectedFile = file;
    addRecentlyUsedWorld(file);
    Configuration.getInstance().setWorldDirectory(file.getParentFile());
    return true;
}
Also used : OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) Paint(org.pepsoft.worldpainter.painting.Paint) Void(java.lang.Void) EventVO(org.pepsoft.worldpainter.vo.EventVO)

Aggregations

OperationCancelled (org.pepsoft.util.ProgressReceiver.OperationCancelled)13 ProgressReceiver (org.pepsoft.util.ProgressReceiver)9 Layer (org.pepsoft.worldpainter.layers.Layer)5 javax.swing (javax.swing)4 ProgressDialog (org.pepsoft.util.swing.ProgressDialog)4 ProgressTask (org.pepsoft.util.swing.ProgressTask)4 File (java.io.File)3 InvocationTargetException (java.lang.reflect.InvocationTargetException)3 java.util (java.util)3 Arrays (java.util.Arrays)3 Collection (java.util.Collection)3 List (java.util.List)3 Set (java.util.Set)3 SubProgressReceiver (org.pepsoft.util.SubProgressReceiver)3 EventVO (org.pepsoft.worldpainter.vo.EventVO)3 java.awt (java.awt)2 IOException (java.io.IOException)2 Void (java.lang.Void)2 FileFilter (javax.swing.filechooser.FileFilter)2 ObservableBoolean (org.pepsoft.util.ObservableBoolean)2