package speedytools.serverside.backup;
//import net.minecraft.client.Minecraft;
//import net.minecraft.command.CommandServerSay;
import net.minecraft.command.CommandException;
import net.minecraft.command.server.CommandBroadcast;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.IProgressUpdate;
import net.minecraft.world.MinecraftException;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
import speedytools.SpeedyToolsMod;
import speedytools.common.utilities.ErrorLog;
import java.nio.file.Path;
import java.util.Calendar;
/**
* Created by TheGreyGhost on 24/02/14.
* MinecraftSaveFolderBackups is used to maintain a series of backups in case the cloning tools cause severe damage to the world
* The intended operation is:
* (1) whenever the clone tool is about to be used (but no more frequently than every 5 minutes = MINIMUM_TIME_BETWEEN_BACKUPS_MS), the current save folder
* is copied to a new folder
* (2) as each new backup is created, one of the older backups may be deleted. The deletion is performed to
* keep a series of backups with increasing spacing as they get older
* Up to six backups will be kept, the oldest will be at least 14 saves old (up to 21)
* A backuplisting file is created in the original save folder to record the backup folders created
* In addition, each backup folder contains a snapshot listing of all the filenames including size and timestamp. The folder is only
* deleted if all files still match exactly.
*
* Usage:
* (1) Create a MinecraftSaveFolderBackups(saveFolderToBeBackedUp) - or MinecraftSaveFolderBackups() to use the current save folder
* (2) Each time you wish to create a backup, call .backupWorld(); if it returns true, the backup was successful
*/
public class MinecraftSaveFolderBackups
{
/**
* Creates a MinecraftSaveFolderBackups for the current save folder
*/
public MinecraftSaveFolderBackups()
{
this(null);
}
/**
* Creates a MinecraftSaveFolderBackups for the given save folder (not necessarily the current one)
* @param currentSaveFolder
*/
public MinecraftSaveFolderBackups(Path currentSaveFolder)
{
if (currentSaveFolder == null) {
currentSaveFolder = DimensionManager.getCurrentSaveRootDirectory().toPath();
}
sourceSaveFolder = currentSaveFolder;
if (sourceSaveFolder == null) return;
Path backupListingPath = sourceSaveFolder.resolve(BACKUP_LISTING_FILENAME);
storedBackups = new StoredBackups();
storedBackups.retrieveBackupListing(backupListingPath);
}
private static final String BACKUP_LISTING_FILENAME = "backuplisting.dat";
private static final long MINIMUM_TIME_BETWEEN_BACKUPS_MS = 5 * 60 * 1000; // 5 minutes
public boolean backupWorld()
{
if (sourceSaveFolder == null) return false;
Calendar now = Calendar.getInstance();
if (now.getTimeInMillis() - lastSaveTimeInMillis < MINIMUM_TIME_BETWEEN_BACKUPS_MS) return false;
boolean success = true;
try {
commandSaveOffSilent();
commandSaveAll();
Path rootSavesFolder = SpeedyToolsMod.proxy.getOrCreateSaveBackupsFolder();
Path newBackupPath = storedBackups.createBackupSave(sourceSaveFolder, rootSavesFolder, now.toString());
Path deletedBackupPath = null;
if (newBackupPath != null) {
announceOnChat("Created new world backup:" + newBackupPath.getFileName());
deletedBackupPath = storedBackups.cullSurplus(); // ignore any failure to cull.
if (deletedBackupPath != null) {
announceOnChat("Deleted old world backup:" + deletedBackupPath.getFileName());
}
}
boolean savedOK;
savedOK = storedBackups.saveBackupListing(sourceSaveFolder.resolve(BACKUP_LISTING_FILENAME));
if (!savedOK) {
ErrorLog.defaultLog().info("storedBackups.saveBackupListing failed");
}
} catch (MinecraftException me) {
success = false;
ErrorLog.defaultLog().info("backupWorld commandSaveAll failed:" + me);
} catch (Exception e) {
success = false;
ErrorLog.defaultLog().severe("MinecraftSaveFolderBackups::backupWorld() failed to create backup save: %s", e);
} finally {
commandSaveOnSilent();
}
if (success) {
lastSaveTimeInMillis = now.getTimeInMillis();
}
return success;
}
// make a chat announcement
private void announceOnChat(String message)
{
String [] allMessages = {message};
MinecraftServer minecraftServer = MinecraftServer.getServer();
CommandBroadcast commandBroadcast = new CommandBroadcast();
try {
commandBroadcast.execute(minecraftServer, allMessages);
} catch (CommandException ce) {
// just ignore
}
}
/** Stop saving the world to disk
* copied & modified from vanilla CommandSaveOff() to make silent
*/
private void commandSaveOffSilent()
{
MinecraftServer minecraftserver = MinecraftServer.getServer();
for (int i = 0; i < minecraftserver.worldServers.length; ++i)
{
if (minecraftserver.worldServers[i] != null)
{
WorldServer worldserver = minecraftserver.worldServers[i];
if (!worldserver.disableLevelSaving)
{
worldserver.disableLevelSaving = true;
}
}
}
}
/** resume saving the world to disk
* copied & modified from vanilla CommandSaveOn() to make silent
*/
private void commandSaveOnSilent()
{
MinecraftServer minecraftserver = MinecraftServer.getServer();
for (int i = 0; i < minecraftserver.worldServers.length; ++i)
{
if (minecraftserver.worldServers[i] != null)
{
WorldServer worldserver = minecraftserver.worldServers[i];
if (worldserver.disableLevelSaving)
{
worldserver.disableLevelSaving = false;
}
}
}
}
public void commandSaveAll() throws MinecraftException
{
MinecraftServer minecraftserver = MinecraftServer.getServer();
if (minecraftserver.getConfigurationManager() != null)
{
minecraftserver.getConfigurationManager().saveAllPlayerData();
}
for (int i = 0; i < minecraftserver.worldServers.length; ++i)
{
if (minecraftserver.worldServers[i] != null)
{
WorldServer worldserver = minecraftserver.worldServers[i];
boolean flag = worldserver.disableLevelSaving;
worldserver.disableLevelSaving = false;
worldserver.saveAllChunks(true, (IProgressUpdate)null);
worldserver.disableLevelSaving = flag;
}
}
}
// public boolean backupWorldUsingCommands()
// {
// if (sourceSaveFolder == null) return false;
// Calendar now = Calendar.getInstance();
//
// if (now.getTimeInMillis() - lastSaveTimeInMillis < MINIMUM_TIME_BETWEEN_BACKUPS_MS) return false;
//
// boolean success = false;
// MinecraftServer minecraftServer = MinecraftServer.getServer();
//
// CommandServerSaveOff saveOff = new CommandServerSaveOff();
// CommandServerSaveAll saveAll = new CommandServerSaveAll();
// CommandServerSaveOn saveOn = new CommandServerSaveOn();
//
// String[] dummyFlush = {"flush"};
// String[] dummy = {""};
//
// saveOff.processCommand(minecraftServer, dummy);
// saveAll.processCommand(minecraftServer, dummy);
//
// try {
// Path rootSavesFolder = new File(Minecraft.getMinecraft().mcDataDir, "saves").toPath();
// success = storedBackups.createBackupSave(sourceSaveFolder, rootSavesFolder, now.toString());
// if (success) {
// storedBackups.cullSurplus(); // ignore any failure to cull.
// }
// boolean savedOK;
// savedOK = storedBackups.saveBackupListing(sourceSaveFolder.resolve(BACKUP_LISTING_FILENAME));
// if (!savedOK) {
// ErrorLog.defaultLog().warning("storedBackups.saveBackupListing failed");
// }
// } catch (Exception e) {
// success = false;
// ErrorLog.defaultLog().severe("MinecraftSaveFolderBackups::backupWorld() failed to create backup save: %s", e);
// }
//
// saveOn.processCommand(minecraftServer, dummy);
// if (success) {
// lastSaveTimeInMillis = now.getTimeInMillis();
// }
// return success;
// }
/**
* returns true if the game has recently been backed up
* @return
*/
public boolean isBackedUpRecently()
{
Calendar now = Calendar.getInstance();
return (now.getTimeInMillis() - lastSaveTimeInMillis < MINIMUM_TIME_BETWEEN_BACKUPS_MS);
}
public Path getSourceSaveFolder() {
return sourceSaveFolder;
}
private Path sourceSaveFolder;
private StoredBackups storedBackups;
private long lastSaveTimeInMillis = 0;
}