Added three new classes, with some methods
classes: Backup, RegionChunkList, NameCollisionException.
This commit is contained in:
parent
0dfa339856
commit
67691336e2
14 changed files with 481 additions and 394 deletions
19
.classpath
19
.classpath
|
@ -1,11 +1,20 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<classpath>
|
<classpath>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
|
||||||
<classpathentry kind="lib" path="/Users/kevinmathewson/Local_Test_Server_1.12.2/spigot-1.12.2.jar">
|
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="javadoc_location" value="jar:file:/Users/kevinmathewson/Local_Test_Server_1.12.2/craftbukkit-1.12.2.jar!/"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="src">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="optional" value="true"/>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/bin/
|
/bin/
|
||||||
|
/target/
|
||||||
|
|
6
.project
6
.project
|
@ -10,8 +10,14 @@
|
||||||
<arguments>
|
<arguments>
|
||||||
</arguments>
|
</arguments>
|
||||||
</buildCommand>
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
</buildSpec>
|
</buildSpec>
|
||||||
<natures>
|
<natures>
|
||||||
|
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
</natures>
|
</natures>
|
||||||
</projectDescription>
|
</projectDescription>
|
||||||
|
|
|
@ -8,4 +8,5 @@ org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||||
org.eclipse.jdt.core.compiler.source=1.8
|
org.eclipse.jdt.core.compiler.source=1.8
|
||||||
|
|
4
.settings/org.eclipse.m2e.core.prefs
Normal file
4
.settings/org.eclipse.m2e.core.prefs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
activeProfiles=
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
resolveWorkspaceProjects=true
|
||||||
|
version=1
|
47
pom.xml
Normal file
47
pom.xml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.github.TBMCPlugins</groupId>
|
||||||
|
<artifactId>SimpleWarBackup</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>src</sourceDirectory>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>spigot</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>jitpack</id>
|
||||||
|
<url>https://jitpack.io/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>spigot</artifactId>
|
||||||
|
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
|
||||||
|
<artifactId>Towny</artifactId>
|
||||||
|
<version>master-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
54
src/simpleWarBackup/Backup.java
Normal file
54
src/simpleWarBackup/Backup.java
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package simpleWarBackup;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import com.palmergames.bukkit.towny.Towny;
|
||||||
|
import com.palmergames.bukkit.towny.object.Town;
|
||||||
|
import com.palmergames.bukkit.towny.object.TownBlock;
|
||||||
|
|
||||||
|
import simpleWarBackup.exceptions.NameCollisionException;
|
||||||
|
|
||||||
|
public class Backup
|
||||||
|
{
|
||||||
|
private final HashMap<String, //town
|
||||||
|
HashMap<String, //world
|
||||||
|
HashMap<Long, //region
|
||||||
|
RegionChunkList>>> lists
|
||||||
|
= new HashMap<String,
|
||||||
|
HashMap<String,
|
||||||
|
HashMap<Long,
|
||||||
|
RegionChunkList>>>();
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final File directory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO write javadoc (this is the default constructor)
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @throws NameCollisionException
|
||||||
|
*/
|
||||||
|
Backup(String name) throws NameCollisionException
|
||||||
|
{
|
||||||
|
checkName(this.name = name); //throws NameCollisionException
|
||||||
|
|
||||||
|
//com.palmergames.bukkit.towny.object.TownyUniverse.getWorld(null).getTowns();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO write javadoc
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @throws NameCollisionException
|
||||||
|
*/
|
||||||
|
private static void checkName(String name) throws NameCollisionException
|
||||||
|
{
|
||||||
|
for (String filename : Main.backupsDir.list()) if (filename == name)
|
||||||
|
{
|
||||||
|
throw new NameCollisionException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,22 +20,6 @@ import net.minecraft.server.v1_12_R1.World;
|
||||||
|
|
||||||
public class BackupIO
|
public class BackupIO
|
||||||
{
|
{
|
||||||
private static File pluginDir, backupsDir;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines two File variables: for the plugin's data folder, and for the "Backup Files"
|
|
||||||
* folder. Does not create either directory. The directories themselves are created
|
|
||||||
* as-needed, elsewhere in the plugin, during the process of saving a backup.
|
|
||||||
*
|
|
||||||
* @param plugin
|
|
||||||
*/
|
|
||||||
static void initialize(JavaPlugin plugin) //called from Main onEnable()
|
|
||||||
{
|
|
||||||
pluginDir = plugin.getDataFolder();
|
|
||||||
backupsDir = new File(pluginDir, "Backup Files");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parent directory containing the "region" destination directory, where
|
* Returns the parent directory containing the "region" destination directory, where
|
||||||
* the .mca files are stored. The path proceeds: backups > backup > town > world.
|
* the .mca files are stored. The path proceeds: backups > backup > town > world.
|
||||||
|
@ -49,13 +33,9 @@ public class BackupIO
|
||||||
* @param world
|
* @param world
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static File getDir(String backup, String town, CraftWorld world)
|
private static File getDir(String backup, String town, String worldUID)
|
||||||
{
|
{
|
||||||
return new File(
|
return new File(new File (new File(Main.backupsDir, backup), town), worldUID);
|
||||||
new File(
|
|
||||||
new File(backupsDir, backup )
|
|
||||||
, town )
|
|
||||||
, world.getName() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,41 +47,31 @@ public class BackupIO
|
||||||
* @param chunks
|
* @param chunks
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static void backup(
|
public static void backup(File worldDir, World world, Chunk... chunks)
|
||||||
String backup, String town, CraftWorld world, CraftChunk... chunks)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
final World worldNMS = world.getHandle();
|
|
||||||
Chunk chunkNMS;
|
|
||||||
NBTTagCompound chunkNBT; // to be serialized
|
NBTTagCompound chunkNBT; // to be serialized
|
||||||
|
DataOutputStream dataOutput; // serialized to here
|
||||||
|
|
||||||
final File worldDir = getDir(backup, town, world);
|
/* dataOutput is the first of a series of outputs, which proceed in
|
||||||
int x, z;
|
* the following order: DataOutputStream > BufferedOutputStream >
|
||||||
RegionFile regionFile;
|
* DeflaterOutputStream > RegionFile.ChunkBuffer
|
||||||
DataOutputStream chunkPipe; // serialized to this
|
*
|
||||||
|
* ChunkBuffer extends ByteArrayOutputStream. It holds a byte[] buffer,
|
||||||
/* chunkPipe is a series of data outputs pointing to a ChunkBuffer.
|
* and two int values which represent the chunk's coordinates.
|
||||||
* ChunkBuffer is an internal class defined within RegionFile that
|
|
||||||
* extends ByteArrayOutputStream. It holds a byte[] buffer and two
|
|
||||||
* int values representing the chunk's coordinates.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (CraftChunk chunk : chunks)
|
for (Chunk chunk : chunks)
|
||||||
{
|
{
|
||||||
chunkNMS = chunk.getHandle();
|
chunkNBT = ChunkNBTTools.save(chunk, world);
|
||||||
chunkNBT = ChunkNBTTools.save(chunkNMS, worldNMS);
|
dataOutput = RegionFile_Cache.get(worldDir, chunk.locX, chunk.locZ)
|
||||||
|
.b(chunk.locX & 31, chunk.locZ & 31);
|
||||||
|
|
||||||
x = chunk.getX();
|
/* a(...) serializes the chunk, and writes the bytes to chunkPipe.
|
||||||
z = chunk.getZ();
|
* dataOutput.close() writes the bytes to the actual .mca file.
|
||||||
regionFile = RegionFileCache.get(worldDir, x, z);
|
|
||||||
chunkPipe = regionFile.b(x & 31, z & 31);
|
|
||||||
|
|
||||||
/* below serializes chunkNBT, and writes those bytes to chunkPipe,
|
|
||||||
* which then, on close(), writes the bytes into this RegionFile's
|
|
||||||
* internal RandomAccessFile, which points to the actual .mca file.
|
|
||||||
*/
|
*/
|
||||||
NBTCompressedStreamTools.a(chunkNBT, (DataOutput) chunkPipe);
|
NBTCompressedStreamTools.a(chunkNBT, (DataOutput) dataOutput);
|
||||||
chunkPipe.close();
|
dataOutput.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,12 +85,9 @@ public class BackupIO
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private static void restore(
|
private static void restore(File worldDir, World world, long[] chunks)
|
||||||
String backup, String town, CraftWorld world, long... chunks)
|
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
final File worldDir = getDir(backup, town, world);
|
|
||||||
|
|
||||||
int x, z, i = 0;
|
int x, z, i = 0;
|
||||||
|
|
||||||
RegionFile source;
|
RegionFile source;
|
||||||
|
@ -135,25 +102,26 @@ public class BackupIO
|
||||||
* the following order: ByteArrayInputStream > GZIPInputStream >
|
* the following order: ByteArrayInputStream > GZIPInputStream >
|
||||||
* BufferedInputStream > DataInputStream.
|
* BufferedInputStream > DataInputStream.
|
||||||
*
|
*
|
||||||
* dataOutput is a series of outputs pointing to a ChunkBuffer.
|
* dataOutput is the first of a series of outputs, which proceed in
|
||||||
* ChunkBuffer is an internal class defined within RegionFile that
|
* the following order: DataOutputStream > BufferedOutputStream >
|
||||||
* extends ByteArrayOutputStream. It holds a byte[] buffer and two
|
* DeflaterOutputStream > RegionFile.ChunkBuffer
|
||||||
* int values representing the chunk's coordinates.
|
*
|
||||||
|
* ChunkBuffer extends ByteArrayOutputStream. It holds a byte[] buffer,
|
||||||
|
* and two int values which represent the chunk's coordinates.
|
||||||
*/
|
*/
|
||||||
for (long chunk : chunks)
|
for (long chunk : chunks)
|
||||||
{
|
{
|
||||||
x = (int) chunk;
|
x = (int) chunk;
|
||||||
z = (int) ( chunk >> 32 );
|
z = (int) ( chunk >> 32 );
|
||||||
|
|
||||||
source = RegionFileCache.get(worldDir, x, z);
|
source = RegionFile_Cache.get(worldDir, x, z);
|
||||||
target = RegionFileCache.getFromMinecraft(world, x, z);
|
target = RegionFile_Cache.getFromMinecraft(world, x, z);
|
||||||
dataInput = source.a(x & 31, z & 31);
|
dataInput = source.a(x & 31, z & 31);
|
||||||
dataOutput = target.b(x & 31, z & 31);
|
dataOutput = target.b(x & 31, z & 31);
|
||||||
|
|
||||||
length = RegionFileCache.getChunkLength(source, x, z);
|
length = ChunkAccess_Cache.getChunkLength(source, x, z);
|
||||||
|
|
||||||
/* getChunkLength(...) attempts to read from RegionFile source's
|
/* getChunkLength(...) will return length 0 if there is an error
|
||||||
* 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
|
* or there is no chunk data. Otherwise, it will return the length
|
||||||
* value recorded in the file.
|
* value recorded in the file.
|
||||||
*/
|
*/
|
||||||
|
@ -169,11 +137,11 @@ public class BackupIO
|
||||||
* ((long) Integer.MAX_VALUE) | (((long) Integer.MAX_VALUE) << 32)
|
* ((long) Integer.MAX_VALUE) | (((long) Integer.MAX_VALUE) << 32)
|
||||||
*
|
*
|
||||||
* coordinates in long[] 'chunks' are set to this value when they
|
* coordinates in long[] 'chunks' are set to this value when they
|
||||||
* have been written successfully back into the game world. This
|
* have been written successfully back into the game world, because
|
||||||
* value is outside the range of natural chunk coordinate pairs.
|
* this value is outside the range of natural chunk coordinate pairs.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
dataOutput.close(); //writes to the target file
|
dataOutput.close(); //writes to target's .mca file
|
||||||
dataInput.close();
|
dataInput.close();
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -193,17 +161,25 @@ public class BackupIO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean restoreTest(int x, int z) throws IOException
|
public static boolean restoreTest(int x, int z) throws IOException
|
||||||
{
|
{
|
||||||
CraftWorld world = (CraftWorld) Bukkit.getWorld("world");
|
CraftWorld world = (CraftWorld) Bukkit.getWorld("world");
|
||||||
|
|
||||||
File worldDir = new File(backupsDir, "world");
|
File worldDir = new File(Main.backupsDir, "world");
|
||||||
File backupDir = new File(worldDir, "test");
|
File backupDir = new File(worldDir, "test");
|
||||||
File regionDir = new File(backupDir, "region");
|
File regionDir = new File(backupDir, "region");
|
||||||
File mcaFile = new File(regionDir, "r.1.1.mca");
|
File mcaFile = new File(regionDir, "r.1.1.mca");
|
||||||
|
|
||||||
RegionFile regionFileSource = RegionFileCache.get(backupDir, x, z);
|
RegionFile regionFileSource = RegionFile_Cache.get(backupDir, x, z);
|
||||||
RegionFile regionFileTarget = RegionFileCache.getFromMinecraft(world, x, z);
|
RegionFile regionFileTarget = RegionFile_Cache.getFromMinecraft(world, x, z);
|
||||||
|
|
||||||
DataInputStream dataInput = regionFileSource.a(x & 31, z & 31);
|
DataInputStream dataInput = regionFileSource.a(x & 31, z & 31);
|
||||||
DataOutputStream dataOutput = regionFileTarget.b(x & 31, z & 31);
|
DataOutputStream dataOutput = regionFileTarget.b(x & 31, z & 31);
|
||||||
|
|
|
@ -11,27 +11,19 @@ import java.util.Map;
|
||||||
import net.minecraft.server.v1_12_R1.RegionFile;
|
import net.minecraft.server.v1_12_R1.RegionFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ---------------------------------------------------------------
|
* TODO write javadoc
|
||||||
* ---------------------------------------------------------------
|
|
||||||
* ---------------------------------------------------------------
|
|
||||||
* ---------------------------------------------------------------
|
|
||||||
* CURRENTLY A DUPLICATE OF THE SECONDARY CACHE IN RegionFileCache
|
|
||||||
* ---------------------------------------------------------------
|
|
||||||
* ---------------------------------------------------------------
|
|
||||||
* ---------------------------------------------------------------
|
|
||||||
* ---------------------------------------------------------------
|
|
||||||
*/
|
*/
|
||||||
public class ChunkAccessCache
|
public class ChunkAccess_Cache
|
||||||
{
|
{
|
||||||
private static Map<RegionFile, Access> cache = new HashMap<RegionFile, Access>();
|
static Map<RegionFile, Access> cache = new HashMap<RegionFile, Access>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO write javadoc
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* @param regionFile
|
* @param regionFile
|
||||||
*/
|
*/
|
||||||
static void cacheChunkAccess(File file, RegionFile regionFile)
|
static void createAndPut(File file, RegionFile regionFile)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -59,20 +51,19 @@ public class ChunkAccessCache
|
||||||
Access access = cache.get(regionFile);
|
Access access = cache.get(regionFile);
|
||||||
|
|
||||||
/* "offset" refers to the location of the chunk's bytes
|
/* "offset" refers to the location of the chunk's bytes
|
||||||
* within the .mca file - or how many bytes into the file
|
* within the .mca file (i.e. how many bytes into the file
|
||||||
* to seek when reading - divided by 4096.
|
* to seek when reading) divided by 4096.
|
||||||
*/
|
*/
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
/* The first four bytes at that location, the first four
|
/* The first four bytes at that location encode an int. This
|
||||||
* bytes of the chunk's data, are an int recording the
|
* int is the chunk's remaining length in bytes, excluding the
|
||||||
* chunk's length in bytes, not including those four.
|
* four bytes of the int.
|
||||||
*
|
*
|
||||||
* The next byte represents compression scheme. This byte,
|
* The fifth byte represents compression scheme. The first five
|
||||||
* like the four previous bytes, is omitted when the game
|
* bytes are omitted when the game reads a chunk's data.
|
||||||
* reads a chunk's data.
|
|
||||||
*
|
*
|
||||||
* This method returns the length of data actually read.
|
* The following method returns the length of data actually read.
|
||||||
*/
|
*/
|
||||||
if (access != null && (offset = access.offset[x + z * 32]) > 0)
|
if (access != null && (offset = access.offset[x + z * 32]) > 0)
|
||||||
{
|
{
|
||||||
|
@ -88,16 +79,15 @@ public class ChunkAccessCache
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives access to the private fields {@code int[] d}, and {@code RandomAccessFile c},
|
* Provides 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
|
* within a RegionFile object. The names of these fields may change in new releases of
|
||||||
* Spigot. TODO explain what both of those fields are
|
* Spigot. TODO explain what both of those fields are
|
||||||
*/
|
*/
|
||||||
private static class Access
|
private static class Access
|
||||||
{
|
{
|
||||||
static final Field offsetField;
|
static final Field offsetField;
|
||||||
final int[] offset;
|
|
||||||
|
|
||||||
static final Field bytesField;
|
static final Field bytesField;
|
||||||
|
final int[] offset;
|
||||||
final RandomAccessFile bytes;
|
final RandomAccessFile bytes;
|
||||||
|
|
||||||
static
|
static
|
||||||
|
@ -107,17 +97,12 @@ public class ChunkAccessCache
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tmp1 = RegionFile.class.getDeclaredField("d");
|
tmp1 = RegionFile.class.getDeclaredField("d");
|
||||||
tmp1.setAccessible(true);
|
|
||||||
|
|
||||||
tmp2 = RegionFile.class.getDeclaredField("c");
|
tmp2 = RegionFile.class.getDeclaredField("c");
|
||||||
|
tmp1.setAccessible(true);
|
||||||
tmp2.setAccessible(true);
|
tmp2.setAccessible(true);
|
||||||
}
|
}
|
||||||
catch (NoSuchFieldException | SecurityException e)
|
catch (NoSuchFieldException | SecurityException e)
|
||||||
{
|
{
|
||||||
/* TODO take more drastic action? Either
|
|
||||||
* Field missing would mean that Minecraft
|
|
||||||
* has refactored, breaking the whole plugin
|
|
||||||
*/
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
offsetField = tmp1;
|
offsetField = tmp1;
|
|
@ -18,14 +18,49 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
public class Main extends JavaPlugin implements Listener
|
public class Main extends JavaPlugin implements Listener
|
||||||
{
|
{
|
||||||
|
// --------------------- STATIC ---------------------
|
||||||
|
|
||||||
|
static File pluginDir;
|
||||||
|
static File backupsDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines two File variables: for the plugin's data folder, and for the "Backup Files"
|
||||||
|
* folder. Does not create either directory. The directories themselves are created
|
||||||
|
* as-needed, elsewhere in the plugin, during the process of saving a backup.
|
||||||
|
*
|
||||||
|
* @param plugin
|
||||||
|
*/
|
||||||
|
static void initialize(JavaPlugin plugin)
|
||||||
|
{
|
||||||
|
pluginDir = plugin.getDataFolder();
|
||||||
|
backupsDir = new File(pluginDir, "Backup Files");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------- INSTANCE --------------------
|
||||||
|
|
||||||
|
|
||||||
public void onEnable()
|
public void onEnable()
|
||||||
{
|
{
|
||||||
|
initialize(this);
|
||||||
|
|
||||||
|
//TEST
|
||||||
getCommand("testBackupChunk").setExecutor(this);
|
getCommand("testBackupChunk").setExecutor(this);
|
||||||
getCommand("testRestoreChunk").setExecutor(this);
|
getCommand("testRestoreChunk").setExecutor(this);
|
||||||
|
|
||||||
BackupIO.initialize(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
|
||||||
{
|
{
|
||||||
if (!(sender instanceof Player)) return false;
|
if (!(sender instanceof Player)) return false;
|
||||||
|
|
|
@ -4,53 +4,90 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO write javadoc
|
||||||
|
*/
|
||||||
public class RegionChunkList
|
public class RegionChunkList
|
||||||
{
|
{
|
||||||
private final File file;
|
/**
|
||||||
|
* TODO write javadoc
|
||||||
|
*/
|
||||||
private final RandomAccessFile readwrite;
|
private final RandomAccessFile readwrite;
|
||||||
private final BitSet chunks = new BitSet(1024);
|
|
||||||
|
|
||||||
private final HashMap<String, HashMap
|
/**
|
||||||
<String, HashMap
|
* TODO write javadoc
|
||||||
<String, RegionChunkList>>> lists =
|
*/
|
||||||
|
final BitSet chunks = new BitSet(1024);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO write javadoc
|
||||||
|
*/
|
||||||
|
final long regionCoordinates;
|
||||||
|
|
||||||
new HashMap<String, HashMap
|
|
||||||
<String, HashMap
|
|
||||||
<String, RegionChunkList>>>();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO write javadoc
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* @throws IOException
|
* @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];
|
// Write chunk list to BitSet:
|
||||||
if (readwrite.length() >= 128)
|
|
||||||
{
|
{
|
||||||
|
byte[] bytes = new byte[128];
|
||||||
readwrite.readFully(bytes);
|
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;
|
int i = 0, j, k;
|
||||||
for (byte b : bytes)
|
for (byte b : bytes)
|
||||||
for (j = 0, k = 1; j < 8; i++, j++, k <<= 1)
|
for (j = 0, k = 1; j < 8; i++, j++, k <<= 1)
|
||||||
if ((b & k) == k) chunks.set(i);
|
if ((b & k) == k) chunks.set(i);
|
||||||
}
|
}
|
||||||
else readwrite.write(bytes);
|
|
||||||
|
|
||||||
lists.get(backup)
|
// Get region coordinate from filename:
|
||||||
.get(town)
|
{
|
||||||
.put(world, this);
|
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 x
|
||||||
* @param z
|
* @param z
|
||||||
|
@ -65,7 +102,7 @@ public class RegionChunkList
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO write javadoc (and add comment to explain numbers)
|
||||||
*
|
*
|
||||||
* @param x
|
* @param x
|
||||||
* @param z
|
* @param z
|
||||||
|
@ -79,7 +116,7 @@ public class RegionChunkList
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO write javadoc (and add comment to explain numbers)
|
||||||
*
|
*
|
||||||
* @param x
|
* @param x
|
||||||
* @param z
|
* @param z
|
||||||
|
@ -90,4 +127,47 @@ public class RegionChunkList
|
||||||
z &= 31;
|
z &= 31;
|
||||||
chunks.clear(x + z * 32);
|
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.<p>
|
||||||
|
*
|
||||||
|
* When you are done with this RegionChunkList, you should invoke this method.
|
||||||
|
*/
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
readwrite.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<File, RegionFile> cache = new HashMap<File, RegionFile>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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).<p>
|
|
||||||
*
|
|
||||||
* 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<RegionFile> 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<RegionFile,
|
|
||||||
ChunkAccess> cacheChunkAccess = new HashMap<RegionFile,
|
|
||||||
ChunkAccess>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
132
src/simpleWarBackup/RegionFile_Cache.java
Normal file
132
src/simpleWarBackup/RegionFile_Cache.java
Normal file
|
@ -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<File, RegionFile> cache = new HashMap<File, RegionFile>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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).<p>
|
||||||
|
*
|
||||||
|
* 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().<p>
|
||||||
|
*
|
||||||
|
* Called by {@link #get(File, int, int) RegionFile_Cache.get(File, int, int)}.<p>
|
||||||
|
*
|
||||||
|
* Removes all entries from the cache.
|
||||||
|
*/
|
||||||
|
private static synchronized void clearCache()
|
||||||
|
{
|
||||||
|
Iterator<RegionFile> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package simpleWarBackup.exceptions;
|
||||||
|
|
||||||
|
public class NameCollisionException extends Throwable
|
||||||
|
{
|
||||||
|
//TODO serialVersionUID?
|
||||||
|
|
||||||
|
//TODO write the damn class
|
||||||
|
}
|
Loading…
Reference in a new issue