diff --git a/src/SMWorldEdit.java b/src/SMWorldEdit.java index 9f1459d62..aee5489cf 100644 --- a/src/SMWorldEdit.java +++ b/src/SMWorldEdit.java @@ -92,6 +92,9 @@ public class SMWorldEdit extends Plugin { worldEdit.setSnapshotRepository(new SnapshotRepository(snapshotsDir)); } + String shellSaveType = properties.getString("shell-save-type", "").trim(); + worldEdit.setShellSaveType(shellSaveType.equals("") ? null : shellSaveType); + for (Map.Entry entry : worldEdit.getCommands().entrySet()) { etc.getInstance().addCommand(entry.getKey(), entry.getValue()); } diff --git a/src/WorldEdit.java b/src/WorldEdit.java index 0e5724e85..154858cf6 100644 --- a/src/WorldEdit.java +++ b/src/WorldEdit.java @@ -25,6 +25,7 @@ import com.sk89q.worldedit.data.*; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.snapshots.InvalidSnapshotException; import java.util.HashMap; +import java.util.Set; import java.util.HashSet; import java.util.Arrays; import java.util.logging.Level; @@ -77,6 +78,10 @@ public class WorldEdit { * Default block change limit. -1 for no limit. */ private int defaultChangeLimit = -1; + /** + * Shell script save type. + */ + private String shellSaveType; /** * Stores the snapshot repository. May be null; */ @@ -169,6 +174,8 @@ public class WorldEdit { commands.put("/thru", "Go through the wall that you are looking at"); commands.put("/ceil", " - Get to the ceiling"); commands.put("/chunkinfo", "Get the filename of the chunk that you are in"); + commands.put("/listchunks", "Print a list of used chunks"); + commands.put("/delchunks", "Generate a shell script to delete chunks"); commands.put("/listsnapshots", "List the 5 newest snapshots"); commands.put("//use", "[SnapshotID] - Use a particular snapshot"); commands.put("//restore", "Restore a particular snapshot"); @@ -888,6 +895,92 @@ public class WorldEdit { return true; + // Dump a list of involved chunks + } else if (split[0].equalsIgnoreCase("/listchunks")) { + checkArgs(split, 0, 0, split[0]); + + Set chunks = session.getRegion().getChunks(); + + for (Vector2D chunk : chunks) { + player.print(NestedFileChunkStore.getFilename(chunk)); + } + + return true; + + // Dump a list of involved chunks + } else if (split[0].equalsIgnoreCase("/delchunks")) { + checkArgs(split, 0, 0, split[0]); + + Set chunks = session.getRegion().getChunks(); + FileOutputStream out = null; + + if (shellSaveType == null) { + player.printError("shell-save-type has to be configured in worldedit.properties"); + } else if (shellSaveType.equalsIgnoreCase("bat")) { + try { + out = new FileOutputStream("worldedit-delchunks.bat"); + OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8"); + writer.write("@ECHO off\r\n"); + writer.write("ECHO This batch file was generated by WorldEdit.\r\n"); + writer.write("ECHO It contains a list of chunks that were in the selected region\r\n"); + writer.write("ECHO at the time that the /delchunks command was used. Run this file\r\n"); + writer.write("ECHO in order to delete the chunk files listed in this file.\r\n"); + writer.write("ECHO.\r\n"); + writer.write("PAUSE\r\n"); + + for (Vector2D chunk : chunks) { + String filename = NestedFileChunkStore.getFilename(chunk); + writer.write("ECHO " + filename + "\r\n"); + writer.write("DEL \"world/" + filename + "\"\r\n"); + } + + writer.write("ECHO Complete.\r\n"); + writer.write("PAUSE\r\n"); + writer.close(); + player.print("worldedit-delchunks.bat written. Run it when no one is near the region."); + } catch (IOException e) { + player.printError("Error occurred: " + e.getMessage()); + } finally { + if (out != null) { + try { out.close(); } catch (IOException ie) {} + } + } + } else if (shellSaveType.equalsIgnoreCase("bash")) { + try { + out = new FileOutputStream("worldedit-delchunks.sh"); + OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8"); + writer.write("#!/bin/bash\n"); + writer.write("echo This shell file was generated by WorldEdit.\n"); + writer.write("echo It contains a list of chunks that were in the selected region\n"); + writer.write("echo at the time that the /delchunks command was used. Run this file\n"); + writer.write("echo in order to delete the chunk files listed in this file.\n"); + writer.write("echo\n"); + writer.write("read -p \"Press any key to continue...\"\n"); + + for (Vector2D chunk : chunks) { + String filename = NestedFileChunkStore.getFilename(chunk); + writer.write("echo " + filename + "\n"); + writer.write("rm \"world/" + filename + "\"\n"); + } + + writer.write("echo Complete.\n"); + writer.write("read -p \"Press any key to continue...\"\n"); + writer.close(); + player.print("worldedit-delchunks.sh written. Run it when no one is near the region."); + player.print("You will have to chmod it to be executable."); + } catch (IOException e) { + player.printError("Error occurred: " + e.getMessage()); + } finally { + if (out != null) { + try { out.close(); } catch (IOException ie) {} + } + } + } else { + player.printError("Unknown shell script save type. 'bat' or 'bash' expected."); + } + + return true; + // List snapshots } else if (split[0].equalsIgnoreCase("/listsnapshots")) { checkArgs(split, 0, 0, split[0]); @@ -1128,4 +1221,18 @@ public class WorldEdit { public void setSnapshotRepository(SnapshotRepository snapshotRepo) { this.snapshotRepo = snapshotRepo; } + + /** + * @return the shellSaveType + */ + public String getShellSaveType() { + return shellSaveType; + } + + /** + * @param shellSaveType the shellSaveType to set + */ + public void setShellSaveType(String shellSaveType) { + this.shellSaveType = shellSaveType; + } } diff --git a/src/com/sk89q/worldedit/data/NestedFileChunkStore.java b/src/com/sk89q/worldedit/data/NestedFileChunkStore.java index 8913d9eb2..7570a1dd3 100644 --- a/src/com/sk89q/worldedit/data/NestedFileChunkStore.java +++ b/src/com/sk89q/worldedit/data/NestedFileChunkStore.java @@ -19,9 +19,9 @@ package com.sk89q.worldedit.data; -import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.*; import java.io.*; -import java.util.*; +import java.util.Map; import org.jnbt.*; /** @@ -32,6 +32,37 @@ import org.jnbt.*; * @author sk89q */ public abstract class NestedFileChunkStore extends ChunkStore { + /** + * Get the filename of a chunk. + * + * @param pos + * @param separator + * @return + */ + public static String getFilename(Vector2D pos, String separator) { + int x = pos.getBlockX(); + int z = pos.getBlockZ(); + + String folder1 = Integer.toString(divisorMod(x, 64), 36); + String folder2 = Integer.toString(divisorMod(z, 64), 36); + String filename = "c." + Integer.toString(x, 36) + + "." + Integer.toString(z, 36) + ".dat"; + + return folder1 + separator + folder2 + separator + filename; + } + + /** + * Get the filename of a chunk, using the system's default path + * separator. + * + * @param pos + * @param separator + * @return + */ + public static String getFilename(Vector2D pos) { + return getFilename(pos, File.separator); + } + /** * Get the tag for a chunk. * diff --git a/src/com/sk89q/worldedit/regions/CuboidRegion.java b/src/com/sk89q/worldedit/regions/CuboidRegion.java index 97a6c7949..3bea3012b 100644 --- a/src/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/src/com/sk89q/worldedit/regions/CuboidRegion.java @@ -20,7 +20,11 @@ package com.sk89q.worldedit.regions; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldedit.data.ChunkStore; import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; /** * @@ -261,6 +265,29 @@ public class CuboidRegion implements Region { this.pos2 = pos2; } + /** + * Get a list of chunks that this region is within. + * + * @return + */ + public Set getChunks() { + Set chunks = new HashSet(); + + Vector min = getMinimumPoint(); + Vector max = getMaximumPoint(); + + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + Vector pt = new Vector(x, y, z); + chunks.add(ChunkStore.toChunk(pt)); + } + } + } + + return chunks; + } + /** * Get the iterator. * diff --git a/src/com/sk89q/worldedit/regions/Region.java b/src/com/sk89q/worldedit/regions/Region.java index dba313f9e..d4872ab21 100644 --- a/src/com/sk89q/worldedit/regions/Region.java +++ b/src/com/sk89q/worldedit/regions/Region.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit.regions; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import java.util.Set; /** * @@ -74,4 +76,10 @@ public interface Region extends Iterable { * @param change */ public void contract(Vector change); + /** + * Get a list of chunks. + * + * @return + */ + public Set getChunks(); }