diff --git a/src/simpleWarBackup/BackupIO.java b/src/simpleWarBackup/BackupIO.java index e2e09b3..493c589 100644 --- a/src/simpleWarBackup/BackupIO.java +++ b/src/simpleWarBackup/BackupIO.java @@ -156,9 +156,6 @@ public class BackupIO * private .mca file. It will return length 0 if there is an error * or there is no chunk data. Otherwise, it will return the length * value recorded in the file. - * - * below writes the backup chunk data into the target region file, - * overwriting the current chunk data in use by the game. */ if (length > 0) { diff --git a/src/simpleWarBackup/ChunkAccessCache.java b/src/simpleWarBackup/ChunkAccessCache.java new file mode 100644 index 0000000..b5fec18 --- /dev/null +++ b/src/simpleWarBackup/ChunkAccessCache.java @@ -0,0 +1,147 @@ +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.Map; + +import net.minecraft.server.v1_12_R1.RegionFile; + +/** + * --------------------------------------------------------------- + * --------------------------------------------------------------- + * --------------------------------------------------------------- + * --------------------------------------------------------------- + * CURRENTLY A DUPLICATE OF THE SECONDARY CACHE IN RegionFileCache + * --------------------------------------------------------------- + * --------------------------------------------------------------- + * --------------------------------------------------------------- + * --------------------------------------------------------------- + */ +public class ChunkAccessCache +{ + private static Map cache = new HashMap(); + + /** + * TODO + * + * @param file + * @param regionFile + */ + static void cacheChunkAccess(File file, RegionFile regionFile) + { + try + { + cache.put(regionFile, new Access(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) + { + Access access = cache.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 Access + { + 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 Access object to hold 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 + */ + Access(RegionFile regionFile) throws FileNotFoundException, + IllegalAccessException, + IllegalArgumentException + { + offset = (int[]) offsetField.get(regionFile); + bytes = (RandomAccessFile) bytesField .get(regionFile); + } + } +} diff --git a/src/simpleWarBackup/RegionChunkList.java b/src/simpleWarBackup/RegionChunkList.java index b469ea8..6efd164 100644 --- a/src/simpleWarBackup/RegionChunkList.java +++ b/src/simpleWarBackup/RegionChunkList.java @@ -1,6 +1,93 @@ package simpleWarBackup; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.BitSet; +import java.util.HashMap; + public class RegionChunkList { + private final File file; + private final RandomAccessFile readwrite; + private final BitSet chunks = new BitSet(1024); + private final HashMap>> lists = + + new HashMap>>(); + + + /** + * TODO + * + * @param file + * @throws IOException + */ + RegionChunkList(File file, String backup, String town, String world) throws IOException + { + readwrite = new RandomAccessFile((this.file = file), "rw"); + + byte[] bytes = new byte[128]; + if (readwrite.length() >= 128) + { + readwrite.readFully(bytes); + + 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); + } + + + /** + * TODO + * + * @param x + * @param z + * @return + */ + boolean isChunkStored(int x, int z) + { + x &= 31; + z &= 31; + return chunks.get(x + z * 32); + } + + + /** + * TODO + * + * @param x + * @param z + */ + void storeChunk(int x, int z) + { + x &= 31; + z &= 31; + chunks.set(x + z * 32); + } + + + /** + * TODO + * + * @param x + * @param z + */ + void markChunkAsRestored(int x, int z) + { + x &= 31; + z &= 31; + chunks.clear(x + z * 32); + } } diff --git a/src/simpleWarBackup/RegionFileCache.java b/src/simpleWarBackup/RegionFileCache.java index d2cd43b..e707aaf 100644 --- a/src/simpleWarBackup/RegionFileCache.java +++ b/src/simpleWarBackup/RegionFileCache.java @@ -215,7 +215,7 @@ public final class RegionFileCache } catch (NoSuchFieldException | SecurityException e) { - /* TODO take more drastic action. Either + /* TODO take more drastic action? Either * Field missing would mean that Minecraft * has refactored, breaking the whole plugin */