>>();
+ /**
+ * TODO write javadoc
+ */
+ final BitSet chunks = new BitSet(1024);
+
+ /**
+ * TODO write javadoc
+ */
+ final long regionCoordinates;
+
/**
- * TODO
+ * TODO write javadoc
*
* @param file
* @throws IOException
*/
- RegionChunkList(File file, String backup, String town, String world) throws IOException
+ RegionChunkList(File file) throws IOException
{
- readwrite = new RandomAccessFile((this.file = file), "rw");
+ readwrite = new RandomAccessFile(file, "rw");
- byte[] bytes = new byte[128];
- if (readwrite.length() >= 128)
- {
+ // Write chunk list to BitSet:
+ {
+ byte[] bytes = new byte[128];
readwrite.readFully(bytes);
-
+
+ /* Each bit represents one of the region's 1024 chunks (32x32). Chunks are
+ * listed in ascending x,z order: (0,0),(1,0)...(31,0),(0,1),(1,1)......(31,31).
+ *
+ * Bits in each byte are read from smallest to largest position: 1,2,4...128.
+ * The values are written to a BitSet, which represents the region in runtime.
+ *
+ * 1 = true, 0 = false.
+ */
int i = 0, j, k;
for (byte b : bytes)
for (j = 0, k = 1; j < 8; i++, j++, k <<= 1)
if ((b & k) == k) chunks.set(i);
}
- else readwrite.write(bytes);
- lists.get(backup)
- .get(town)
- .put(world, this);
+ // Get region coordinate from filename:
+ {
+ String[] name = file.getName().split(".");
+ int x = Integer.parseInt(name[1]);
+ int z = Integer.parseInt(name[2]);
+
+ regionCoordinates = (long) x | ((long) z << 32);
+ }
}
/**
- * TODO
+ * TODO write javadoc
+ *
+ * @param directory
+ * @param regionCoordinates
+ */
+ RegionChunkList(File directory, long regionCoordinates) throws IOException
+ {
+ this.regionCoordinates = regionCoordinates;
+
+ int x = (int) regionCoordinates;
+ int z = (int) (regionCoordinates >>> 32);
+ String filename = "r." + x + "." + z + ".chunklist";
+
+ File file = new File(directory, filename);
+ readwrite = new RandomAccessFile(file, "rw");
+ }
+
+
+ /**
+ * TODO write javadoc (and add comment to explain numbers)
*
* @param x
* @param z
@@ -65,7 +102,7 @@ public class RegionChunkList
/**
- * TODO
+ * TODO write javadoc (and add comment to explain numbers)
*
* @param x
* @param z
@@ -79,7 +116,7 @@ public class RegionChunkList
/**
- * TODO
+ * TODO write javadoc (and add comment to explain numbers)
*
* @param x
* @param z
@@ -90,4 +127,47 @@ public class RegionChunkList
z &= 31;
chunks.clear(x + z * 32);
}
+
+
+ /**
+ * TODO write javadoc
+ *
+ * @throws IOException
+ */
+ void saveToFile() throws IOException
+ {
+ int i = 0;
+ byte bytes[] = new byte[128];
+ for (byte b : bytes)
+ {
+ if (chunks.get(i++)) b = (byte) (b | (byte) 1 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 2 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 4 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 8 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 16 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 32 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 64 );
+ if (chunks.get(i++)) b = (byte) (b | (byte) 128);
+ }
+ readwrite.seek(0);
+ readwrite.write(bytes);
+ }
+
+
+ /**
+ * Closes the RandomAccessFile associated with this RegionChunkList.
+ *
+ * When you are done with this RegionChunkList, you should invoke this method.
+ */
+ void close()
+ {
+ try
+ {
+ readwrite.close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/src/simpleWarBackup/RegionFileCache.java b/src/simpleWarBackup/RegionFileCache.java
deleted file mode 100644
index e707aaf..0000000
--- a/src/simpleWarBackup/RegionFileCache.java
+++ /dev/null
@@ -1,251 +0,0 @@
-package simpleWarBackup;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.bukkit.Bukkit;
-import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
-
-import net.minecraft.server.v1_12_R1.RegionFile;
-import net.minecraft.server.v1_12_R1.World;
-
-/**
- * TODO write this
- */
-public final class RegionFileCache
-{
- private static final int cap = 32; //capacity - how many regionFiles to keep cached
- private static final Map cache = new HashMap();
-
- /**
- * Returns name of the region file that would contain the given chunk coordinates.
- * Name follows the standard minecraft format: "r.[region x].[region z].mca".
- * Region coordinate = chunk coordinate >> 5.
- *
- * @param x global chunk coordinate x
- * @param z global chunk coordinate z
- * @return
- */
- public static String path(int x, int z)
- {
- return "r." + (x >> 5) + "." + (z >> 5) + ".mca";
- }
-
- /**
- * Copied from net.minecraft.server.RefionFileCache.a(File, int, int).
- *
- * Returns any cached RegionFile mapped to the given File. Not finding one, creates
- * and caches, then returns, a new RegionFile. Before caching a new RegionFile, checks
- * that the cache contains fewer than 16 RegionFiles, and, finding 16 or more, calls
- * {@link #clearCache() RegionFileCache.clearCache()}.
- *
- * @param backupDir parent folder containing the "region" folder
- * @param x global chunk coordinate x
- * @param z global chunk coordinate z
- * @return
- */
- public static synchronized RegionFile get(File backupDir, int x, int z)
- {
- File regionDir = new File(backupDir, "region");
- File mcaFile = new File(regionDir, path(x,z));
-
- RegionFile regionFile = cache.get(mcaFile);
-
- if (regionFile != null)
- return regionFile;
- if (!regionDir.exists())
- regionDir.mkdirs();
- if (cache.size() >= cap)
- clearCache();
-
- regionFile = new RegionFile(mcaFile);
- RegionFileCache.cache.put(mcaFile, regionFile);
- RegionFileCache.cacheChunkAccess(mcaFile, regionFile);
- return regionFile;
- }
-
-
- /**
- * Copied from net.minecraft.server.RegionFileCache.a(). Iterates through all cached
- * RegionFiles, closing each one's private RandomAccessFile, then clears the cache.
- */
- public static synchronized void clearCache()
- {
- Iterator iterator = RegionFileCache.cache.values().iterator();
-
- /* regionFile.c() closes the private RandomAccessFile
- * inside the regionFile object, and that's all it does.
- * The entire method: if (file != null) file.close().
- */
- while (iterator.hasNext())
- {
- RegionFile regionFile = iterator.next();
- try { if (regionFile != null) regionFile.c(); }
- catch (IOException e) { e.printStackTrace(); }
- }
- RegionFileCache.cache.clear();
- RegionFileCache.cacheChunkAccess.clear();
- }
-
-
- /**
- * TODO
- *
- * @param world
- * @param x
- * @param z
- * @return
- */
- public static RegionFile getFromMinecraft(CraftWorld world, int x, int z)
- {
- /* root directory of the world, the directory
- * sharing the world's name and holding its
- * data. 'region' folder is found in here.
- */
- File dir = world.getHandle().getDataManager().getDirectory();
-
- return net.minecraft.server.v1_12_R1.RegionFileCache.a(dir, x, z);
- }
-
-
-
- /*=================================Chunk Access=================================*/
-
- /* Secondary cache, for holding references to the private fields "c" and "d" in each
- * RegionFile cached above. TODO write more
- */
-
- private static Map cacheChunkAccess = new HashMap();
-
- /**
- * TODO
- *
- * @param file
- * @param regionFile
- */
- private static void cacheChunkAccess(File file, RegionFile regionFile)
- {
- try
- {
- cacheChunkAccess.put(regionFile, new ChunkAccess(regionFile));
- }
- catch (FileNotFoundException |
- IllegalAccessException |
- IllegalArgumentException e)
- {
- e.printStackTrace();//TODO
- }
- }
-
-
- /**
- * TODO
- *
- * @param regionFile
- * @param x
- * @param z
- * @return
- */
- public static int getChunkLength(RegionFile regionFile, int x, int z)
- {
- ChunkAccess access = cacheChunkAccess.get(regionFile);
-
- /* "offset" refers to the location of the chunk's bytes
- * within the .mca file - or how many bytes into the file
- * to seek when reading - divided by 4096.
- */
- int offset;
-
- /* The first four bytes at that location, the first four
- * bytes of the chunk's data, are an int recording the
- * chunk's length in bytes, not including those four.
- *
- * The next byte represents compression scheme. This byte,
- * like the four previous bytes, is omitted when the game
- * reads a chunk's data.
- *
- * This method returns the length of data actually read.
- */
- if (access != null && (offset = access.offset[x + z * 32]) > 0)
- {
- try
- {
- access.bytes.seek(offset * 4096);
- return access.bytes.readInt() - 1;
- }
- catch (IOException e) { e.printStackTrace(); }
- }
- return 0;
- }
-
-
-
- /**
- * Gives access to the private fields {@code int[] d}, and {@code RandomAccessFile c},
- * within a RegionFile object. The names of these fields may change in new releases of
- * Spigot. TODO explain what both of those fields are
- */
- private static class ChunkAccess
- {
- static final Field offsetField;
- final int[] offset;
-
- static final Field bytesField;
- final RandomAccessFile bytes;
-
- static
- {
- Field tmp1 = null;
- Field tmp2 = null;
- try
- {
- tmp1 = RegionFile.class.getDeclaredField("d");
- tmp1.setAccessible(true);
-
- tmp2 = RegionFile.class.getDeclaredField("c");
- tmp2.setAccessible(true);
- }
- catch (NoSuchFieldException | SecurityException e)
- {
- /* TODO take more drastic action? Either
- * Field missing would mean that Minecraft
- * has refactored, breaking the whole plugin
- */
- e.printStackTrace();
- }
- offsetField = tmp1;
- bytesField = tmp2;
- }
-
-
- /**
- * Creates a new DataAccess object, which holds references to the private variables
- * {@code int[] d} and {@code RandomAccessFile c} within the given RegionFile. Variables
- * are accessed by reflection. TODO write more
- *
- * @param regionFile the RegionFile whose chunks are to be accessed
- *
- * @throws FileNotFoundException
- * @throws IllegalAccessException
- * @throws IllegalArgumentException
- */
- ChunkAccess(RegionFile regionFile)
- throws FileNotFoundException,
- IllegalAccessException,
- IllegalArgumentException
- {
- this.offset = (int[]) offsetField.get(regionFile);
- this.bytes = (RandomAccessFile) bytesField .get(regionFile);
- }
- }
-
-
-}
diff --git a/src/simpleWarBackup/RegionFile_Cache.java b/src/simpleWarBackup/RegionFile_Cache.java
new file mode 100644
index 0000000..dd8c780
--- /dev/null
+++ b/src/simpleWarBackup/RegionFile_Cache.java
@@ -0,0 +1,132 @@
+package simpleWarBackup;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
+
+import net.minecraft.server.v1_12_R1.RegionFile;
+import net.minecraft.server.v1_12_R1.World;
+
+/**
+ * TODO write this
+ */
+public final class RegionFile_Cache
+{
+ private static final int cap = 32; //capacity - how many regionFiles to keep cached
+ private static final Map cache = new HashMap();
+
+ /**
+ * Returns name of the region file that would contain the given chunk coordinates.
+ * Name follows the standard minecraft format: "r.[region x].[region z].mca".
+ * Region coordinate = chunk coordinate >> 5.
+ *
+ * @param x global chunk coordinate x
+ * @param z global chunk coordinate z
+ * @return
+ */
+ public static String path(int x, int z)
+ {
+ return "r." + (x >> 5) + "." + (z >> 5) + ".mca";
+ }
+
+ /**
+ * Method copied from net.minecraft.server.RefionFileCache.a(File, int, int).
+ *
+ * If there is a cached RegionFile for the given file and coordinates, returns this.
+ * Otherwise, creates, caches, then returns a new RegionFile for the given file and
+ * coordinates.
+ *
+ * @param backupDir parent folder containing the "region" folder
+ * @param x global chunk coordinate x
+ * @param z global chunk coordinate z
+ * @return
+ */
+ public static synchronized RegionFile get(File backupDir, int x, int z)
+ {
+ File regionDir = new File(backupDir, "region");
+ File mcaFile = new File(regionDir, path(x,z));
+
+ RegionFile regionFile = cache.get(mcaFile);
+
+ if (regionFile != null)
+ return regionFile;
+ if (!regionDir.exists())
+ regionDir.mkdirs();
+ if (cache.size() >= cap)
+ clearCache();
+
+ regionFile = new RegionFile(mcaFile);
+ RegionFile_Cache.cache.put(mcaFile, regionFile);
+ ChunkAccess_Cache.createAndPut(mcaFile, regionFile);
+ return regionFile;
+ }
+
+
+ /**
+ * Method Copied from net.minecraft.server.RegionFileCache.a().
+ *
+ * Called by {@link #get(File, int, int) RegionFile_Cache.get(File, int, int)}.
+ *
+ * Removes all entries from the cache.
+ */
+ private static synchronized void clearCache()
+ {
+ Iterator iterator = RegionFile_Cache.cache.values().iterator();
+
+ /* regionFile.c() closes the private RandomAccessFile
+ * inside the regionFile object, and that's all it does.
+ * The entire method: if (file != null) file.close().
+ */
+ while (iterator.hasNext())
+ {
+ RegionFile regionFile = iterator.next();
+ try { if (regionFile != null) regionFile.c(); }
+ catch (IOException e) { e.printStackTrace(); }
+ }
+ RegionFile_Cache.cache.clear();
+ ChunkAccess_Cache.cache.clear();
+ }
+
+
+ /**
+ * TODO
+ *
+ * @param world
+ * @param x
+ * @param z
+ * @return
+ */
+ public static RegionFile getFromMinecraft(CraftWorld world, int x, int z)
+ {
+ /* root directory of the world, the directory
+ * sharing the world's name and holding its
+ * data. 'region' folder is found in here.
+ */
+ File dir = world.getHandle().getDataManager().getDirectory();
+
+ return net.minecraft.server.v1_12_R1.RegionFileCache.a(dir, x, z);
+ }
+
+ /**
+ * TODO
+ *
+ * @param world
+ * @param x
+ * @param z
+ * @return
+ */
+ public static RegionFile getFromMinecraft(World world, int x, int z)
+ {
+ /* root directory of the world, the directory
+ * sharing the world's name and holding its
+ * data. 'region' folder is found in here.
+ */
+ File dir = world.getDataManager().getDirectory();
+
+ return net.minecraft.server.v1_12_R1.RegionFileCache.a(dir, x, z);
+ }
+}
diff --git a/src/simpleWarBackup/exceptions/NameCollisionException.java b/src/simpleWarBackup/exceptions/NameCollisionException.java
new file mode 100644
index 0000000..26d7f3d
--- /dev/null
+++ b/src/simpleWarBackup/exceptions/NameCollisionException.java
@@ -0,0 +1,8 @@
+package simpleWarBackup.exceptions;
+
+public class NameCollisionException extends Throwable
+{
+ //TODO serialVersionUID?
+
+ //TODO write the damn class
+}