From be8d780d5c17e1b75a9aa90511c11d2e38eaef67 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Sun, 21 Jan 2018 16:56:04 +0000 Subject: [PATCH] RegionFileCache and BackupIO --- plugin.yml | 8 ++ src/simpleWarBackup/BackupIO.java | 112 +++++++++++++++++++++++ src/simpleWarBackup/ChunkNBTWriter.java | 6 +- src/simpleWarBackup/Main.java | 89 +++++++----------- src/simpleWarBackup/RegionFileCache.java | 63 ++++++++++++- 5 files changed, 217 insertions(+), 61 deletions(-) create mode 100644 plugin.yml create mode 100644 src/simpleWarBackup/BackupIO.java diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..e5d13cc --- /dev/null +++ b/plugin.yml @@ -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 \ No newline at end of file diff --git a/src/simpleWarBackup/BackupIO.java b/src/simpleWarBackup/BackupIO.java new file mode 100644 index 0000000..55795c7 --- /dev/null +++ b/src/simpleWarBackup/BackupIO.java @@ -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> backups + = new HashMap>(); + + /** + * 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 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()); + 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(); + } + } +} diff --git a/src/simpleWarBackup/ChunkNBTWriter.java b/src/simpleWarBackup/ChunkNBTWriter.java index f98422d..81004cf 100644 --- a/src/simpleWarBackup/ChunkNBTWriter.java +++ b/src/simpleWarBackup/ChunkNBTWriter.java @@ -38,7 +38,7 @@ public final class ChunkNBTWriter * @param DATA_VERSION * @return */ - public static NBTTagCompound getChunkNBT(Chunk chunk, World world) + public static NBTTagCompound write(Chunk chunk, World world) { NBTTagCompound chunkNBT = new NBTTagCompound(); NBTTagCompound levelNBT = new NBTTagCompound(); @@ -67,7 +67,7 @@ public final class ChunkNBTWriter * @param chunk * @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 NBTTagList entitiesNBT = new NBTTagList(); @@ -150,7 +150,7 @@ public final class ChunkNBTWriter * @param worldTime * @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 * replaced with world. The values are now obtained directly: diff --git a/src/simpleWarBackup/Main.java b/src/simpleWarBackup/Main.java index 790dfbd..729026a 100644 --- a/src/simpleWarBackup/Main.java +++ b/src/simpleWarBackup/Main.java @@ -1,78 +1,57 @@ package simpleWarBackup; import java.io.File; +import java.io.IOException; import java.util.HashMap; 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.CraftServer; import org.bukkit.craftbukkit.v1_12_R1.CraftWorld; +import org.bukkit.entity.Player; import org.bukkit.event.Listener; 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 { - - - private static File dataFolder, - backupsFolder; - - public static final HashMap> backups - = new HashMap>(); - - /** - * 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 map; - - File worldFolder; - File backupFolder; - - for (org.bukkit.World world : Bukkit.getWorlds()) - { - backups.put(world, map = new HashMap()); - - worldFolder = new File(backupsFolder, world.getName()); - for (String backupName : worldFolder.list()) - { - backupFolder = new File(worldFolder, backupName); - map.put(backupName, backupFolder); - } - } - } - - public void onEnable() { - dataFolder = this.getDataFolder(); - backupsFolder = new File(dataFolder, "Backup Files"); + getCommand("testBackupChunk").setExecutor(this); + getCommand("testRestoreChunk").setExecutor(this); - dataFolder.mkdir(); - backupsFolder.mkdir(); - - mapBackupDirectoriesToWorlds(); + File dataFolder = this.getDataFolder(); + BackupIO.initialize(dataFolder, + new File(dataFolder, + "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(); - World world = ((CraftWorld) bukkitWorld).getHandle(); + if (!(sender instanceof Player) || !sender.getName().equals("iie")) return false; + 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; } } diff --git a/src/simpleWarBackup/RegionFileCache.java b/src/simpleWarBackup/RegionFileCache.java index c394b25..9f3c214 100644 --- a/src/simpleWarBackup/RegionFileCache.java +++ b/src/simpleWarBackup/RegionFileCache.java @@ -1,13 +1,70 @@ package simpleWarBackup; import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; import java.util.Map; -import com.google.common.collect.Maps; - import net.minecraft.server.v1_12_R1.RegionFile; +/** + * TODO write this + */ public class RegionFileCache { - public static final Map files = Maps.newHashMap(); + private static final Map regionFiles = new HashMap(); + + /** + * 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 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(); + } }