Search in sources :

Example 16 with Dimension

use of org.pepsoft.worldpainter.Dimension in project WorldPainter by Captain-Chaos.

the class MouseOrTabletOperation method mousePressed.

// MouseListener (these methods are only invoked in legacy mode)
@Override
public void mousePressed(MouseEvent me) {
    x = me.getX();
    y = me.getY();
    altDown = me.isAltDown() || me.isAltGraphDown();
    undo = (me.getButton() == MouseEvent.BUTTON3) || altDown;
    ctrlDown = me.isControlDown() || me.isMetaDown();
    shiftDown = me.isShiftDown();
    first = true;
    if (!oneShot) {
        if (timer == null) {
            timer = new Timer(delay, e -> {
                Point worldCoords = view.viewToWorld((int) x, (int) y);
                tick(worldCoords.x, worldCoords.y, undo, first, 1.0f);
                view.updateStatusBar(worldCoords.x, worldCoords.y);
                first = false;
            });
            timer.setInitialDelay(0);
            timer.start();
        // start = System.currentTimeMillis();
        }
    } else {
        Point worldCoords = view.viewToWorld((int) x, (int) y);
        tick(worldCoords.x, worldCoords.y, undo, true, 1.0f);
        view.updateStatusBar(worldCoords.x, worldCoords.y);
        Dimension dimension = getDimension();
        if (dimension != null) {
            dimension.armSavePoint();
        }
        logOperation(undo ? statisticsKeyUndo : statisticsKey);
    }
}
Also used : WorldPainterView(org.pepsoft.worldpainter.WorldPainterView) EventVO(org.pepsoft.worldpainter.vo.EventVO) HashMap(java.util.HashMap) EventLogger(org.pepsoft.worldpainter.EventLogger) PenListener(jpen.event.PenListener) Dimension(org.pepsoft.worldpainter.Dimension) java.awt(java.awt) PropertyVetoException(java.beans.PropertyVetoException) AwtPenToolkit(jpen.owner.multiAwt.AwtPenToolkit) SystemUtils(org.pepsoft.util.SystemUtils) java.awt.event(java.awt.event) Map(java.util.Map) jpen(jpen) javax.swing(javax.swing) Dimension(org.pepsoft.worldpainter.Dimension)

Example 17 with Dimension

use of org.pepsoft.worldpainter.Dimension in project WorldPainter by Captain-Chaos.

the class AbstractWorldExporter method secondPass.

protected List<Fixup> secondPass(List<Layer> secondaryPassLayers, Dimension dimension, MinecraftWorld minecraftWorld, Map<Layer, LayerExporter> exporters, Collection<Tile> tiles, Point regionCoords, ProgressReceiver progressReceiver) throws OperationCancelled {
    // Apply other secondary pass layers
    if (logger.isDebugEnabled()) {
        logger.debug("Start of second pass for region {},{}", regionCoords.x, regionCoords.y);
    }
    int layerCount = secondaryPassLayers.size(), counter = 0;
    Rectangle area = new Rectangle((regionCoords.x << 9) - 16, (regionCoords.y << 9) - 16, 544, 544);
    Rectangle exportedArea = new Rectangle((regionCoords.x << 9), (regionCoords.y << 9), 512, 512);
    List<Fixup> fixups = new ArrayList<>();
    // boolean frost = false;
    for (Layer layer : secondaryPassLayers) {
        // if (layer instanceof Frost) {
        // frost = true;
        // continue;
        // }
        @SuppressWarnings("unchecked") SecondPassLayerExporter exporter = (SecondPassLayerExporter) exporters.get(layer);
        if (logger.isDebugEnabled()) {
            logger.debug("Exporting layer {} for region {},{}", layer, regionCoords.x, regionCoords.y);
        }
        if (progressReceiver != null) {
            if (minecraftWorld instanceof InvertedWorld) {
                progressReceiver.setMessage("Exporting layer " + layer + " for ceiling");
            } else {
                progressReceiver.setMessage("Exporting layer " + layer);
            }
        }
        List<Fixup> layerFixups = exporter.render(dimension, area, exportedArea, minecraftWorld);
        if (layerFixups != null) {
            fixups.addAll(layerFixups);
        }
        if (progressReceiver != null) {
            counter++;
            progressReceiver.setProgress((float) counter / layerCount);
        }
    }
    // Garden / seeds first and second pass
    GardenExporter gardenExporter = new GardenExporter();
    Set<Seed> firstPassProcessedSeeds = new HashSet<>();
    Set<Seed> secondPassProcessedSeeds = new HashSet<>();
    tiles.stream().filter(tile -> tile.getLayers().contains(GardenCategory.INSTANCE)).forEach(tile -> {
        gardenExporter.firstPass(dimension, tile, minecraftWorld, firstPassProcessedSeeds);
        gardenExporter.secondPass(dimension, tile, minecraftWorld, secondPassProcessedSeeds);
    });
    // elegant
    if ((dimension.getDim() == 0) && world.isCreateGoodiesChest()) {
        Point goodiesPoint = (Point) world.getSpawnPoint().clone();
        goodiesPoint.translate(3, 3);
        int height = Math.min(dimension.getIntHeightAt(goodiesPoint) + 1, dimension.getMaxHeight() - 1);
        minecraftWorld.setMaterialAt(goodiesPoint.x, goodiesPoint.y, height, Material.CHEST_NORTH);
        Chunk chunk = minecraftWorld.getChunk(goodiesPoint.x >> 4, goodiesPoint.y >> 4);
        if ((chunk != null) && (chunk.getTileEntities() != null)) {
            Chest goodiesChest = createGoodiesChest();
            goodiesChest.setX(goodiesPoint.x);
            goodiesChest.setY(height);
            goodiesChest.setZ(goodiesPoint.y);
            chunk.getTileEntities().add(goodiesChest);
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug("End of second pass for region {},{}", regionCoords.x, regionCoords.y);
    }
    return fixups;
}
Also used : java.util(java.util) org.pepsoft.minecraft(org.pepsoft.minecraft) BLOCKS(org.pepsoft.minecraft.Block.BLOCKS) EventVO(org.pepsoft.worldpainter.vo.EventVO) LoggerFactory(org.slf4j.LoggerFactory) ParallelProgressManager(org.pepsoft.util.ParallelProgressManager) PlatformProvider(org.pepsoft.worldpainter.plugins.PlatformProvider) SimpleDateFormat(java.text.SimpleDateFormat) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) CustomLayer(org.pepsoft.worldpainter.layers.CustomLayer) Platform(org.pepsoft.worldpainter.Platform) PlatformManager(org.pepsoft.worldpainter.plugins.PlatformManager) CombinedLayer(org.pepsoft.worldpainter.layers.CombinedLayer) DateFormat(java.text.DateFormat) PrintWriter(java.io.PrintWriter) Constants(org.pepsoft.worldpainter.Constants) Box(org.pepsoft.util.Box) AttributeKeyVO(org.pepsoft.worldpainter.vo.AttributeKeyVO) Logger(org.slf4j.Logger) Seed(org.pepsoft.worldpainter.gardenofeden.Seed) java.util.concurrent(java.util.concurrent) GardenExporter(org.pepsoft.worldpainter.gardenofeden.GardenExporter) FileOutputStream(java.io.FileOutputStream) IOException(java.io.IOException) GardenCategory(org.pepsoft.worldpainter.layers.GardenCategory) File(java.io.File) Layer(org.pepsoft.worldpainter.layers.Layer) Dimension(org.pepsoft.worldpainter.Dimension) java.awt(java.awt) List(java.util.List) Constants(org.pepsoft.minecraft.Constants) ProgressReceiver(org.pepsoft.util.ProgressReceiver) OperationCancelled(org.pepsoft.util.ProgressReceiver.OperationCancelled) Tile(org.pepsoft.worldpainter.Tile) World2(org.pepsoft.worldpainter.World2) NotNull(org.jetbrains.annotations.NotNull) CustomLayer(org.pepsoft.worldpainter.layers.CustomLayer) CombinedLayer(org.pepsoft.worldpainter.layers.CombinedLayer) Layer(org.pepsoft.worldpainter.layers.Layer) Seed(org.pepsoft.worldpainter.gardenofeden.Seed) GardenExporter(org.pepsoft.worldpainter.gardenofeden.GardenExporter)

Example 18 with Dimension

use of org.pepsoft.worldpainter.Dimension in project WorldPainter by Captain-Chaos.

the class JavaWorldMerger method mergeBiomes.

/**
 * Merge only the biomes, leave everything else the same.
 */
public void mergeBiomes(File backupDir, ProgressReceiver progressReceiver) throws IOException, ProgressReceiver.OperationCancelled {
    // Read existing level.dat file and perform sanity checks
    Level level = performSanityChecks(true);
    // Backup existing level
    File worldDir = levelDatFile.getParentFile();
    if (!worldDir.renameTo(backupDir)) {
        throw new FileInUseException("Could not move " + worldDir + " to " + backupDir);
    }
    if (!worldDir.mkdirs()) {
        throw new IOException("Could not create " + worldDir);
    }
    // Copy everything that we are not going to generate (this includes the
    // Nether and End dimensions)
    File[] files = backupDir.listFiles();
    // noinspection ConstantConditions // Cannot happen because we previously loaded level.dat from it
    for (File file : files) {
        if ((!file.getName().equalsIgnoreCase("session.lock")) && (!file.getName().equalsIgnoreCase("region"))) {
            if (file.isFile()) {
                FileUtils.copyFileToDir(file, worldDir);
            } else if (file.isDirectory()) {
                FileUtils.copyDir(file, new File(worldDir, file.getName()));
            } else {
                logger.warn("Not copying " + file + "; not a regular file or directory");
            }
        }
    }
    // Write session.lock file
    File sessionLockFile = new File(worldDir, "session.lock");
    try (DataOutputStream sessionOut = new DataOutputStream(new FileOutputStream(sessionLockFile))) {
        sessionOut.writeLong(System.currentTimeMillis());
    }
    // Process all chunks and copy just the biomes
    if (progressReceiver != null) {
        progressReceiver.setMessage("Merging biomes");
    }
    // Find all the region files of the existing level
    File oldRegionDir = new File(backupDir, "region");
    final Pattern regionFilePattern = Pattern.compile("r\\.-?\\d+\\.-?\\d+\\.mca");
    File[] oldRegionFiles = oldRegionDir.listFiles((dir, name) -> regionFilePattern.matcher(name).matches());
    // Process each region file, copying every chunk unmodified, except
    // for the biomes
    // Can only happen for corrupted maps
    @SuppressWarnings("ConstantConditions") int totalChunkCount = oldRegionFiles.length * 32 * 32, chunkCount = 0;
    File newRegionDir = new File(worldDir, "region");
    newRegionDir.mkdirs();
    Dimension dimension = world.getDimension(DIM_NORMAL);
    for (File file : oldRegionFiles) {
        try (RegionFile oldRegion = new RegionFile(file)) {
            String[] parts = file.getName().split("\\.");
            int regionX = Integer.parseInt(parts[1]);
            int regionZ = Integer.parseInt(parts[2]);
            File newRegionFile = new File(newRegionDir, "r." + regionX + "." + regionZ + ".mca");
            try (RegionFile newRegion = new RegionFile(newRegionFile)) {
                for (int x = 0; x < 32; x++) {
                    for (int z = 0; z < 32; z++) {
                        if (oldRegion.containsChunk(x, z)) {
                            ChunkImpl2 chunk;
                            try (NBTInputStream in = new NBTInputStream(oldRegion.getChunkDataInputStream(x, z))) {
                                CompoundTag tag = (CompoundTag) in.readTag();
                                chunk = new ChunkImpl2(tag, level.getMaxHeight());
                            }
                            int chunkX = chunk.getxPos(), chunkZ = chunk.getzPos();
                            for (int xx = 0; xx < 16; xx++) {
                                for (int zz = 0; zz < 16; zz++) {
                                    chunk.setBiome(xx, zz, dimension.getLayerValueAt(Biome.INSTANCE, (chunkX << 4) | xx, (chunkZ << 4) | zz));
                                }
                            }
                            try (NBTOutputStream out = new NBTOutputStream(newRegion.getChunkDataOutputStream(x, z))) {
                                out.writeTag(chunk.toNBT());
                            }
                        }
                        chunkCount++;
                        if (progressReceiver != null) {
                            progressReceiver.setProgress((float) chunkCount / totalChunkCount);
                        }
                    }
                }
            }
        }
    }
    // Rewrite session.lock file
    try (DataOutputStream sessionOut = new DataOutputStream(new FileOutputStream(sessionLockFile))) {
        sessionOut.writeLong(System.currentTimeMillis());
    }
}
Also used : FileInUseException(org.pepsoft.worldpainter.util.FileInUseException) Pattern(java.util.regex.Pattern) Dimension(org.pepsoft.worldpainter.Dimension) NBTOutputStream(org.jnbt.NBTOutputStream) NBTInputStream(org.jnbt.NBTInputStream) CompoundTag(org.jnbt.CompoundTag)

Example 19 with Dimension

use of org.pepsoft.worldpainter.Dimension in project WorldPainter by Captain-Chaos.

the class JavaWorldMerger method merge.

public void merge(File backupDir, ProgressReceiver progressReceiver) throws IOException, ProgressReceiver.OperationCancelled {
    logger.info("Merging world " + world.getName() + " with map at " + levelDatFile.getParentFile());
    // Read existing level.dat file and perform sanity checks
    Level level = performSanityChecks(false);
    // Record start of export
    long start = System.currentTimeMillis();
    // Backup existing level
    File worldDir = levelDatFile.getParentFile();
    if (!worldDir.renameTo(backupDir)) {
        throw new FileInUseException("Could not move " + worldDir + " to " + backupDir);
    }
    if (!worldDir.mkdirs()) {
        throw new IOException("Could not create " + worldDir);
    }
    // Set the world to the same Minecraft version as the existing map, in
    // case it has changed. This affects the type of chunks created in the
    // first pass
    int version = level.getVersion();
    Platform platform = (version == SUPPORTED_VERSION_1) ? DefaultPlugin.JAVA_MCREGION : DefaultPlugin.JAVA_ANVIL;
    world.setPlatform(platform);
    // Modify it if necessary and write it to the the new level
    if ((selectedDimensions == null) || selectedDimensions.contains(DIM_NORMAL)) {
        Dimension surfaceDimension = world.getDimension(DIM_NORMAL);
        level.setSeed(surfaceDimension.getMinecraftSeed());
        Point spawnPoint = world.getSpawnPoint();
        level.setSpawnX(spawnPoint.x);
        level.setSpawnY(Math.max(surfaceDimension.getIntHeightAt(spawnPoint), surfaceDimension.getWaterLevelAt(spawnPoint)));
        level.setSpawnZ(spawnPoint.y);
    }
    // Save the level.dat file. This will also create a session.lock file, hopefully kicking out any Minecraft
    // instances which may have the map open:
    level.save(worldDir);
    // Copy everything that we are not going to generate
    File[] files = backupDir.listFiles();
    // noinspection ConstantConditions // Cannot happen because we previously loaded level.dat from it
    for (File file : files) {
        if ((!file.getName().equalsIgnoreCase("level.dat")) && (!file.getName().equalsIgnoreCase("level.dat_old")) && (!file.getName().equalsIgnoreCase("session.lock")) && (((selectedDimensions != null) && (!selectedDimensions.contains(DIM_NORMAL))) || (!file.getName().equalsIgnoreCase("region"))) && (!file.getName().equalsIgnoreCase("maxheight.txt")) && (!file.getName().equalsIgnoreCase("Height.txt")) && (((selectedDimensions != null) && (!selectedDimensions.contains(DIM_NETHER))) || (!file.getName().equalsIgnoreCase("DIM-1"))) && (((selectedDimensions != null) && (!selectedDimensions.contains(DIM_END))) || (!file.getName().equalsIgnoreCase("DIM1")))) {
            if (file.isFile()) {
                FileUtils.copyFileToDir(file, worldDir);
            } else if (file.isDirectory()) {
                FileUtils.copyDir(file, new File(worldDir, file.getName()));
            } else {
                logger.warn("Not copying " + file + "; not a regular file or directory");
            }
        }
    }
    if ((selectedDimensions == null) ? (world.getDimension(DIM_NORMAL) != null) : selectedDimensions.contains(DIM_NORMAL)) {
        mergeDimension(worldDir, backupDir, world.getDimension(DIM_NORMAL), platform, progressReceiver);
    }
    if ((selectedDimensions == null) ? (world.getDimension(DIM_NETHER) != null) : selectedDimensions.contains(DIM_NETHER)) {
        mergeDimension(worldDir, backupDir, world.getDimension(DIM_NETHER), platform, progressReceiver);
    }
    if ((selectedDimensions == null) ? (world.getDimension(DIM_END) != null) : selectedDimensions.contains(DIM_END)) {
        mergeDimension(worldDir, backupDir, world.getDimension(DIM_END), platform, progressReceiver);
    }
    // Update the session.lock file, hopefully kicking out any Minecraft instances which may have tried to open the
    // map in the mean time:
    File sessionLockFile = new File(worldDir, "session.lock");
    try (DataOutputStream sessionOut = new DataOutputStream(new FileOutputStream(sessionLockFile))) {
        sessionOut.writeLong(System.currentTimeMillis());
    }
    // Record the merge in the world history
    if (selectedDimensions == null) {
        world.addHistoryEntry(HistoryEntry.WORLD_MERGED_FULL, level.getName(), worldDir);
    } else {
        String dimNames = selectedDimensions.stream().map(dim -> {
            switch(dim) {
                case DIM_NORMAL:
                    return "Surface";
                case DIM_NETHER:
                    return "Nether";
                case DIM_END:
                    return "End";
                default:
                    return Integer.toString(dim);
            }
        }).collect(Collectors.joining(", "));
        world.addHistoryEntry(HistoryEntry.WORLD_MERGED_PARTIAL, level.getName(), worldDir, dimNames);
    }
    if (!levelDatFile.equals(world.getMergedWith())) {
        world.setMergedWith(levelDatFile);
    }
    // Log an event
    Configuration config = Configuration.getInstance();
    if (config != null) {
        EventVO event = new EventVO(EVENT_KEY_ACTION_MERGE_WORLD).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(DefaultPlugin.JAVA_ANVIL) && (world.getGenerator() == Generator.FLAT)) {
            event.setAttribute(ATTRIBUTE_KEY_GENERATOR_OPTIONS, world.getGeneratorOptions());
        }
        if ((selectedDimensions == null) || selectedDimensions.contains(DIM_NORMAL)) {
            Dimension surfaceDimension = world.getDimension(0);
            event.setAttribute(ATTRIBUTE_KEY_TILES, surfaceDimension.getTiles().size());
            logLayers(surfaceDimension, event, "");
        }
        if (world.getImportedFrom() == null) {
            event.setAttribute(ATTRIBUTE_KEY_IMPORTED_WORLD, false);
        }
        config.logEvent(event);
    }
}
Also used : FileInUseException(org.pepsoft.worldpainter.util.FileInUseException) java.util(java.util) org.pepsoft.minecraft(org.pepsoft.minecraft) BLOCKS(org.pepsoft.minecraft.Block.BLOCKS) EventVO(org.pepsoft.worldpainter.vo.EventVO) ParallelProgressManager(org.pepsoft.util.ParallelProgressManager) org.pepsoft.worldpainter.layers(org.pepsoft.worldpainter.layers) NBTOutputStream(org.jnbt.NBTOutputStream) SubProgressReceiver(org.pepsoft.util.SubProgressReceiver) PlatformManager(org.pepsoft.worldpainter.plugins.PlatformManager) org.pepsoft.worldpainter.exporting(org.pepsoft.worldpainter.exporting) ThreadFactory(java.util.concurrent.ThreadFactory) ExecutorService(java.util.concurrent.ExecutorService) org.pepsoft.worldpainter(org.pepsoft.worldpainter) Constants(org.pepsoft.worldpainter.Constants) HistoryEntry(org.pepsoft.worldpainter.history.HistoryEntry) FileInUseException(org.pepsoft.worldpainter.util.FileInUseException) Collectors(java.util.stream.Collectors) Executors(java.util.concurrent.Executors) NBTInputStream(org.jnbt.NBTInputStream) Dimension(org.pepsoft.worldpainter.Dimension) java.awt(java.awt) TimeUnit(java.util.concurrent.TimeUnit) CompoundTag(org.jnbt.CompoundTag) List(java.util.List) java.io(java.io) Constants(org.pepsoft.minecraft.Constants) ProgressReceiver(org.pepsoft.util.ProgressReceiver) Tag(org.jnbt.Tag) FileUtils(org.pepsoft.util.FileUtils) Pattern(java.util.regex.Pattern) Dimension(org.pepsoft.worldpainter.Dimension) EventVO(org.pepsoft.worldpainter.vo.EventVO)

Example 20 with Dimension

use of org.pepsoft.worldpainter.Dimension 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)

Aggregations

Dimension (org.pepsoft.worldpainter.Dimension)39 File (java.io.File)9 Tile (org.pepsoft.worldpainter.Tile)7 World2 (org.pepsoft.worldpainter.World2)6 EventVO (org.pepsoft.worldpainter.vo.EventVO)6 FileOutputStream (java.io.FileOutputStream)5 ProgressReceiver (org.pepsoft.util.ProgressReceiver)5 java.awt (java.awt)4 FileInputStream (java.io.FileInputStream)4 ObjectInputStream (java.io.ObjectInputStream)4 GZIPInputStream (java.util.zip.GZIPInputStream)4 SubProgressReceiver (org.pepsoft.util.SubProgressReceiver)4 ObjectOutputStream (java.io.ObjectOutputStream)3 java.util (java.util)3 List (java.util.List)3 Map (java.util.Map)3 GZIPOutputStream (java.util.zip.GZIPOutputStream)3 FileInUseException (org.pepsoft.worldpainter.util.FileInUseException)3 java.awt.event (java.awt.event)2 PropertyVetoException (java.beans.PropertyVetoException)2