RegionFileCache and BackupIO
This commit is contained in:
parent
88705dc761
commit
be8d780d5c
5 changed files with 217 additions and 61 deletions
8
plugin.yml
Normal file
8
plugin.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
main: simpleWarBackup.Main
|
||||||
|
version: 1.0.0
|
||||||
|
name: SimpleWarBackup
|
||||||
|
commands:
|
||||||
|
testBackupChun:
|
||||||
|
description: only iie gets to do this
|
||||||
|
testRestoreChunk:
|
||||||
|
description: only iie gets to do this, too
|
112
src/simpleWarBackup/BackupIO.java
Normal file
112
src/simpleWarBackup/BackupIO.java
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package simpleWarBackup;
|
||||||
|
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_12_R1.CraftChunk;
|
||||||
|
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
|
||||||
|
|
||||||
|
import net.minecraft.server.v1_12_R1.NBTCompressedStreamTools;
|
||||||
|
import net.minecraft.server.v1_12_R1.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_12_R1.RegionFile;
|
||||||
|
|
||||||
|
public class BackupIO
|
||||||
|
{
|
||||||
|
private static File dataFolder, backupsFolder;
|
||||||
|
|
||||||
|
public static final HashMap<World, HashMap<String, File>> backups
|
||||||
|
= new HashMap<World, HashMap<String, File>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param dataFolder
|
||||||
|
* @param backupsFolder
|
||||||
|
*/
|
||||||
|
public static void initialize(File dataFolder, File backupsFolder)
|
||||||
|
{
|
||||||
|
BackupIO.dataFolder = dataFolder;
|
||||||
|
BackupIO.backupsFolder = backupsFolder;
|
||||||
|
BackupIO.dataFolder.mkdir();
|
||||||
|
BackupIO.backupsFolder.mkdir();
|
||||||
|
|
||||||
|
initializeBackupsMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
public static void initializeBackupsMap()
|
||||||
|
{
|
||||||
|
HashMap<String, File> map;
|
||||||
|
File worldFolder;
|
||||||
|
File backupFolder;
|
||||||
|
|
||||||
|
/* Directories that do not exist will be made when
|
||||||
|
* the plugin attempts to save a backup for the
|
||||||
|
* given world.
|
||||||
|
*/
|
||||||
|
for (World world : Bukkit.getWorlds())
|
||||||
|
{
|
||||||
|
backups.put(world, map = new HashMap<String, File>());
|
||||||
|
worldFolder = new File(backupsFolder, world.getName());
|
||||||
|
if (worldFolder.exists() && worldFolder.list() != null)
|
||||||
|
{
|
||||||
|
for (String backupName : worldFolder.list())
|
||||||
|
{
|
||||||
|
backupFolder = new File(worldFolder, backupName);
|
||||||
|
map.put(backupName, backupFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param backupFolder
|
||||||
|
* @param chunkX
|
||||||
|
* @param chunkZ
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static DataOutputStream chunkDataOutputStream(File backupFolder, int chunkX, int chunkZ)
|
||||||
|
{
|
||||||
|
return RegionFileCache.get(backupFolder, chunkX, chunkZ) // get(...) returns a RegionFile
|
||||||
|
.b(chunkX & 31, chunkZ & 31); // b(...) returns a DataOutputStream
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @param backup
|
||||||
|
* @param world
|
||||||
|
* @param chunks
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void backupChunks(String backup, World world, Chunk... chunks) throws IOException
|
||||||
|
{
|
||||||
|
final File folder = backups.get(world).get(backup);
|
||||||
|
final net.minecraft.server.v1_12_R1.World worldNMS
|
||||||
|
= ((CraftWorld) world).getHandle();
|
||||||
|
|
||||||
|
NBTTagCompound chunkNBT;
|
||||||
|
DataOutputStream output;
|
||||||
|
|
||||||
|
for (Chunk chunk : chunks)
|
||||||
|
{
|
||||||
|
chunkNBT = ChunkNBTWriter.write(((CraftChunk) chunk).getHandle(), worldNMS);
|
||||||
|
output = BackupIO.chunkDataOutputStream(folder, chunk.getX(), chunk.getZ());
|
||||||
|
|
||||||
|
NBTCompressedStreamTools.a(chunkNBT, (DataOutput) output);
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ public final class ChunkNBTWriter
|
||||||
* @param DATA_VERSION
|
* @param DATA_VERSION
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static NBTTagCompound getChunkNBT(Chunk chunk, World world)
|
public static NBTTagCompound write(Chunk chunk, World world)
|
||||||
{
|
{
|
||||||
NBTTagCompound chunkNBT = new NBTTagCompound();
|
NBTTagCompound chunkNBT = new NBTTagCompound();
|
||||||
NBTTagCompound levelNBT = new NBTTagCompound();
|
NBTTagCompound levelNBT = new NBTTagCompound();
|
||||||
|
@ -67,7 +67,7 @@ public final class ChunkNBTWriter
|
||||||
* @param chunk
|
* @param chunk
|
||||||
* @param world
|
* @param world
|
||||||
*/
|
*/
|
||||||
public static void saveEntities(NBTTagCompound levelNBT, Chunk chunk, World world)
|
private static void saveEntities(NBTTagCompound levelNBT, Chunk chunk, World world)
|
||||||
{
|
{
|
||||||
//the three TagLists to be written to levelNBT
|
//the three TagLists to be written to levelNBT
|
||||||
NBTTagList entitiesNBT = new NBTTagList();
|
NBTTagList entitiesNBT = new NBTTagList();
|
||||||
|
@ -150,7 +150,7 @@ public final class ChunkNBTWriter
|
||||||
* @param worldTime
|
* @param worldTime
|
||||||
* @param worldHasSkyLight
|
* @param worldHasSkyLight
|
||||||
*/
|
*/
|
||||||
public static void saveBody(NBTTagCompound levelNBT, Chunk chunk, World world)
|
private static void saveBody(NBTTagCompound levelNBT, Chunk chunk, World world)
|
||||||
{
|
{
|
||||||
/* passed variables worldTime and worldHasSkyLight have been
|
/* passed variables worldTime and worldHasSkyLight have been
|
||||||
* replaced with world. The values are now obtained directly:
|
* replaced with world. The values are now obtained directly:
|
||||||
|
|
|
@ -1,78 +1,57 @@
|
||||||
package simpleWarBackup;
|
package simpleWarBackup;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.craftbukkit.v1_12_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_12_R1.CraftChunk;
|
||||||
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import net.minecraft.server.v1_12_R1.Chunk;
|
|
||||||
import net.minecraft.server.v1_12_R1.ChunkRegionLoader;
|
|
||||||
import net.minecraft.server.v1_12_R1.DataConverterManager;
|
|
||||||
import net.minecraft.server.v1_12_R1.MinecraftServer;
|
|
||||||
import net.minecraft.server.v1_12_R1.NBTTagCompound;
|
|
||||||
import net.minecraft.server.v1_12_R1.World;
|
|
||||||
|
|
||||||
public class Main extends JavaPlugin implements Listener
|
public class Main extends JavaPlugin implements Listener
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
private static File dataFolder,
|
|
||||||
backupsFolder;
|
|
||||||
|
|
||||||
public static final HashMap<org.bukkit.World, HashMap<String, File>> backups
|
|
||||||
= new HashMap<org.bukkit.World, HashMap<String, File>>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
public static void mapBackupDirectoriesToWorlds()
|
|
||||||
{
|
|
||||||
/* Directories that do not exist will be made when
|
|
||||||
* the plugin attempts to save a backup for the
|
|
||||||
* given world.
|
|
||||||
*/
|
|
||||||
|
|
||||||
HashMap<String, File> map;
|
|
||||||
|
|
||||||
File worldFolder;
|
|
||||||
File backupFolder;
|
|
||||||
|
|
||||||
for (org.bukkit.World world : Bukkit.getWorlds())
|
|
||||||
{
|
|
||||||
backups.put(world, map = new HashMap<String, File>());
|
|
||||||
|
|
||||||
worldFolder = new File(backupsFolder, world.getName());
|
|
||||||
for (String backupName : worldFolder.list())
|
|
||||||
{
|
|
||||||
backupFolder = new File(worldFolder, backupName);
|
|
||||||
map.put(backupName, backupFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void onEnable()
|
public void onEnable()
|
||||||
{
|
{
|
||||||
dataFolder = this.getDataFolder();
|
getCommand("testBackupChunk").setExecutor(this);
|
||||||
backupsFolder = new File(dataFolder, "Backup Files");
|
getCommand("testRestoreChunk").setExecutor(this);
|
||||||
|
|
||||||
dataFolder.mkdir();
|
File dataFolder = this.getDataFolder();
|
||||||
backupsFolder.mkdir();
|
BackupIO.initialize(dataFolder,
|
||||||
|
new File(dataFolder,
|
||||||
mapBackupDirectoriesToWorlds();
|
"Backup Files"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean backupChunk(org.bukkit.World bukkitWorld, int chunkX, int chunkZ)
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
|
||||||
{
|
{
|
||||||
Chunk chunk = ((CraftChunk) bukkitWorld.getChunkAt(chunkX, chunkZ)).getHandle();
|
if (!(sender instanceof Player) || !sender.getName().equals("iie")) return false;
|
||||||
World world = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
|
|
||||||
|
Location loc = ((Player) sender).getLocation();
|
||||||
|
|
||||||
|
World world = loc.getWorld();
|
||||||
|
Chunk chunk = loc.getChunk();
|
||||||
|
|
||||||
|
if (label.equals("testBackupChunk"))
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
BackupIO.backupChunks("test", world, chunk);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (label.equals("testRestoreChunk"))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,70 @@
|
||||||
package simpleWarBackup;
|
package simpleWarBackup;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
|
|
||||||
import net.minecraft.server.v1_12_R1.RegionFile;
|
import net.minecraft.server.v1_12_R1.RegionFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO write this
|
||||||
|
*/
|
||||||
public class RegionFileCache
|
public class RegionFileCache
|
||||||
{
|
{
|
||||||
public static final Map<File, RegionFile> files = Maps.newHashMap();
|
private static final Map<File, RegionFile> regionFiles = new HashMap<File, RegionFile>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.clearCace()}.
|
||||||
|
*
|
||||||
|
* @param backupFolder folder containing "region" folder
|
||||||
|
* @param x chunk X
|
||||||
|
* @param z chunk Z
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static synchronized RegionFile get(File backupFolder, int x, int z)
|
||||||
|
{
|
||||||
|
x >>= 5; z >>= 5;
|
||||||
|
File regionFolder = new File(backupFolder, "region");
|
||||||
|
File regionFileFile = new File(regionFolder, "r." + x + "." + z + ".mca");
|
||||||
|
RegionFile regionFile = regionFiles.get(regionFileFile);
|
||||||
|
|
||||||
|
if (regionFile != null)
|
||||||
|
return regionFile;
|
||||||
|
if (!regionFolder.exists())
|
||||||
|
regionFolder.mkdirs();
|
||||||
|
if (regionFiles.size() >= 16)
|
||||||
|
clearCache();
|
||||||
|
|
||||||
|
regionFile = new RegionFile(regionFileFile);
|
||||||
|
RegionFileCache.regionFiles.put(regionFileFile, 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.regionFiles.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.regionFiles.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue