summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorKeuin <[email protected]>2020-04-23 20:13:35 +0800
committerkeuin <[email protected]>2020-04-23 20:13:35 +0800
commitba19ab350516e26a8e52cc217878bde63c0b7eee (patch)
tree482a4ad4b4b8c4d630833f93863195f56ef34244 /src/main/java
parentffd446e4decbe487b8e56b6726be0ecc81f8428d (diff)
Refactored code.
Added startup message on the first start after restoring a backup. Adjusted text color.
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/keuin/kbackupfabric/KBCommands.java (renamed from src/main/java/com/keuin/kbackupfabric/KBCommandHandler.java)50
-rw-r--r--src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java65
-rw-r--r--src/main/java/com/keuin/kbackupfabric/KBPluginInitializer.java12
-rw-r--r--src/main/java/com/keuin/kbackupfabric/KBRegister.java (renamed from src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java)18
-rw-r--r--src/main/java/com/keuin/kbackupfabric/data/BackupMetadata.java23
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java53
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java58
-rw-r--r--src/main/java/com/keuin/kbackupfabric/worker/BackupWorker.java50
8 files changed, 237 insertions, 92 deletions
diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommandHandler.java b/src/main/java/com/keuin/kbackupfabric/KBCommands.java
index 943fb68..c636a0d 100644
--- a/src/main/java/com/keuin/kbackupfabric/KBCommandHandler.java
+++ b/src/main/java/com/keuin/kbackupfabric/KBCommands.java
@@ -1,5 +1,7 @@
package com.keuin.kbackupfabric;
+import com.keuin.kbackupfabric.data.BackupMetadata;
+import com.keuin.kbackupfabric.util.PrintUtil;
import com.keuin.kbackupfabric.worker.BackupWorker;
import com.keuin.kbackupfabric.worker.RestoreWorker;
import com.mojang.brigadier.arguments.StringArgumentType;
@@ -13,10 +15,9 @@ import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.*;
-import static com.keuin.kbackupfabric.util.PrintUtil.debug;
-import static com.keuin.kbackupfabric.util.PrintUtil.message;
+import static com.keuin.kbackupfabric.util.PrintUtil.*;
-public final class KBCommandHandler {
+public final class KBCommands {
private static final int SUCCESS = 1;
@@ -33,18 +34,18 @@ public final class KBCommandHandler {
* @return stat code.
*/
public static int help(CommandContext<ServerCommandSource> context) {
- message(context, "==== KBackup Manual ====");
- message(context, "/kb /kb help Print help menu.");
- message(context, "/kb list Show all backups.");
- message(context, "/kb backup [backup_name] Backup world, nether, end to backup_name. By default, the name is current system time.");
- message(context, "/kb restore <backup_name> Delete current three worlds, restore the older version from given backup. By default, this command is identical with /kb list.");
- message(context, "/kb confirm Confirm and start restoring.");
- message(context, "/kb cancel Cancel the restoration to be confirmed. If cancelled, /kb confirm will not effect without another valid /kb restore command.");
+ msgInfo(context, "==== KBackup Manual ====");
+ msgInfo(context, "/kb /kb help Print help menu.");
+ msgInfo(context, "/kb list Show all backups.");
+ msgInfo(context, "/kb backup [backup_name] Backup the whole level to backup_name. The default name is current system time.");
+ msgInfo(context, "/kb restore <backup_name> Delete the whole current level and restore from given backup. /kb restore is identical with /kb list.");
+ msgInfo(context, "/kb confirm Confirm and start restoring.");
+ msgInfo(context, "/kb cancel Cancel the restoration to be confirmed. If cancelled, /kb confirm will not run.");
return SUCCESS;
}
public static int list(CommandContext<ServerCommandSource> context) {
- message(context, "Available backups: (file is not checked, manipulation may affect this plugin)");
+ msgInfo(context, "Available backups: (file is not checked, manipulation may affect this plugin)");
MinecraftServer server = context.getSource().getMinecraftServer();
File[] files = getBackupSaveDirectory(server).listFiles(
(dir, name) -> dir.isDirectory() && name.toLowerCase().endsWith(".zip") && name.toLowerCase().startsWith(getBackupFileNamePrefix())
@@ -56,7 +57,7 @@ public final class KBCommandHandler {
++i;
String backupName = getBackupName(file.getName());
backupIndexNameMapper.put(i, backupName);
- message(context, String.format("[%d] %s, size: %.1fMB", i, backupName, file.length() * 1.0 / 1024 / 1024));
+ msgInfo(context, String.format("[%d] %s, size: %.1fMB", i, backupName, file.length() * 1.0 / 1024 / 1024));
}
}
return SUCCESS;
@@ -74,7 +75,7 @@ public final class KBCommandHandler {
if (backupName.matches("[0-9]*")) {
// Numeric param is not allowed
backupName = String.format("a%s", backupName);
- message(context, String.format("Pure numeric name is not allowed. Renamed to %s", backupName));
+ msgWarn(context, String.format("Pure numeric name is not allowed. Renamed to %s", backupName));
}
return doBackup(context, backupName);
}
@@ -104,13 +105,13 @@ public final class KBCommandHandler {
// Validate backupName
if (!isBackupNameValid(backupName, server)) {
// Invalid backupName
- message(context, "Invalid backup name! Please check your input. The list index number is also valid.", false);
+ msgErr(context, "Invalid backup name! Please check your input. The list index number is also valid.", false);
return FAILED;
}
// Update confirm pending variable
restoreBackupNameToBeConfirmed = backupName;
- message(context, String.format("WARNING: You will LOST YOUR CURRENT WORLD COMPLETELY! It will be replaced with the backup %s . Please use /kb confirm to proceed executing.", restoreBackupNameToBeConfirmed), true);
+ msgWarn(context, String.format("WARNING: You will LOST YOUR CURRENT WORLD COMPLETELY! It will be replaced with the backup %s . Please use /kb confirm to proceed executing.", restoreBackupNameToBeConfirmed), true);
return SUCCESS;
}
@@ -129,7 +130,8 @@ public final class KBCommandHandler {
}
private static int doBackup(CommandContext<ServerCommandSource> context, String backupName) {
- BackupWorker.invoke(context, backupName);
+ BackupMetadata metadata = new BackupMetadata(System.currentTimeMillis(), backupName);
+ BackupWorker.invoke(context, backupName, metadata);
return SUCCESS;
}
@@ -141,22 +143,22 @@ public final class KBCommandHandler {
*/
public static int confirm(CommandContext<ServerCommandSource> context) {
if (restoreBackupNameToBeConfirmed == null) {
- message(context, "Nothing to be confirmed. Please execute /kb restore <backup_name> first.");
+ msgInfo(context, "Nothing to be confirmed. Please execute /kb restore <backup_name> first.");
return FAILED;
}
// do restore to backupName
String backupName = restoreBackupNameToBeConfirmed;
- message(context, String.format("Restoring worlds to %s ...", backupName), true);
+ PrintUtil.msgInfo(context, String.format("Restoring worlds to %s ...", backupName), true);
// Get server
MinecraftServer server = context.getSource().getMinecraftServer();
String backupFileName = getBackupFileName(backupName);
debug("Backup file name: " + backupFileName);
File backupFile = new File(getBackupSaveDirectory(server), backupFileName);
- message(context, "Server will shutdown in a few seconds, depended on your world size and the disk speed, the restore progress may take seconds or minutes.", true);
- message(context, "Please do not force the server stop, or the level would be broken.", true);
- message(context, "After it shuts down, please restart the server manually.", true);
+ PrintUtil.msgInfo(context, "Server will shutdown in a few seconds, depended on your world size and the disk speed, the restore progress may take seconds or minutes.", true);
+ PrintUtil.msgInfo(context, "Please do not force the server stop, or the level would be broken.", true);
+ PrintUtil.msgInfo(context, "After it shuts down, please restart the server manually.", true);
final int WAIT_SECONDS = 10;
for (int i = 0; i < WAIT_SECONDS; ++i) {
try {
@@ -164,7 +166,7 @@ public final class KBCommandHandler {
} catch (InterruptedException ignored) {
}
}
- message(context, "Shutting down ...", true);
+ PrintUtil.msgInfo(context, "Shutting down ...", true);
RestoreWorker.invoke(server, backupFile.getPath(), getLevelPath(server));
return SUCCESS;
}
@@ -178,10 +180,10 @@ public final class KBCommandHandler {
public static int cancel(CommandContext<ServerCommandSource> context) {
if (restoreBackupNameToBeConfirmed != null) {
restoreBackupNameToBeConfirmed = null;
- message(context, "The restoration is cancelled.", true);
+ PrintUtil.msgInfo(context, "The restoration is cancelled.", true);
return SUCCESS;
} else {
- message(context, "Nothing to cancel.");
+ msgErr(context, "Nothing to cancel.");
return FAILED;
}
}
diff --git a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java
new file mode 100644
index 0000000..4f0f1ff
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java
@@ -0,0 +1,65 @@
+package com.keuin.kbackupfabric;
+
+import com.keuin.kbackupfabric.data.BackupMetadata;
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.event.server.ServerStartCallback;
+import net.fabricmc.fabric.api.registry.CommandRegistry;
+import net.minecraft.server.MinecraftServer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static com.keuin.kbackupfabric.util.PrintUtil.debug;
+import static com.keuin.kbackupfabric.util.PrintUtil.info;
+import static org.apache.commons.io.FileUtils.forceDelete;
+
+/**
+ * This is the Main file of this plugin.
+ * It contains all events, including the init event.
+ */
+public final class KBPluginEvents implements ModInitializer, ServerStartCallback {
+ @Override
+ public void onInitialize() {
+ System.out.println("Initializing KBackup...");
+ CommandRegistry.INSTANCE.register(false, KBRegister::registerCommands);
+ ServerStartCallback.EVENT.register(this);
+ }
+
+ @Override
+ public void onStartServer(MinecraftServer server) {
+ // When the server starts, we check if we have just recovered from a backup.
+ // If so, then we print some message.
+ debug("KBackup onStartServer");
+ // TODO:
+ // Check
+ try {
+ File levelDirectory = new File(server.getRunDirectory(), server.getLevelName());
+ File metadataFile = new File(levelDirectory, BackupMetadata.metadataFileName);
+ if (metadataFile.exists()) {
+ // Metadata exists. Deserialize.
+ BackupMetadata metadata = null;
+ FileInputStream fileInputStream = new FileInputStream(metadataFile);
+ ObjectInputStream in = new ObjectInputStream(fileInputStream);
+ metadata = (BackupMetadata) in.readObject();
+ in.close();
+ fileInputStream.close();
+
+ // Print metadata
+ info("Recovered from previous backup:");
+ info("Backup Name: " + metadata.getBackupName());
+ info("Create Time: " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date(metadata.getBackupTime())));
+
+ // Delete metadata file
+ if (!metadataFile.delete()) {
+ System.gc();
+ forceDelete(metadataFile);
+ }
+ }
+ } catch (IOException | ClassNotFoundException ignored) {
+ }
+ }
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/KBPluginInitializer.java b/src/main/java/com/keuin/kbackupfabric/KBPluginInitializer.java
deleted file mode 100644
index c3582c7..0000000
--- a/src/main/java/com/keuin/kbackupfabric/KBPluginInitializer.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.keuin.kbackupfabric;
-
-import net.fabricmc.api.ModInitializer;
-import net.fabricmc.fabric.api.registry.CommandRegistry;
-
-public final class KBPluginInitializer implements ModInitializer {
- @Override
- public void onInitialize() {
- System.out.println("Initializing KBackup...");
- CommandRegistry.INSTANCE.register(false, KBCommandRegister::register);
- }
-}
diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java b/src/main/java/com/keuin/kbackupfabric/KBRegister.java
index a9dcb38..11f05a1 100644
--- a/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java
+++ b/src/main/java/com/keuin/kbackupfabric/KBRegister.java
@@ -6,27 +6,27 @@ import com.mojang.brigadier.arguments.StringArgumentType;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
-public final class KBCommandRegister {
+public final class KBRegister {
// First make method to register
- public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
+ public static void registerCommands(CommandDispatcher<ServerCommandSource> dispatcher) {
// register /kb and /kb help for help menu
- dispatcher.register(CommandManager.literal("kb").executes(KBCommandHandler::help));
- dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("help").executes(KBCommandHandler::help)));
+ dispatcher.register(CommandManager.literal("kb").executes(KBCommands::help));
+ dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("help").executes(KBCommands::help)));
// register /kb list for showing the backup list. OP is required.
- dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("list").requires(PermissionValidator::op).executes(KBCommandHandler::list)));
+ dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("list").requires(PermissionValidator::op).executes(KBCommands::list)));
// register /kb backup [name] for performing backup. OP is required.
- dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("backup").then(CommandManager.argument("backupName", StringArgumentType.string()).requires(PermissionValidator::op).executes(KBCommandHandler::backup)).requires(PermissionValidator::op).executes(KBCommandHandler::backupWithDefaultName)));
+ dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("backup").then(CommandManager.argument("backupName", StringArgumentType.string()).requires(PermissionValidator::op).executes(KBCommands::backup)).requires(PermissionValidator::op).executes(KBCommands::backupWithDefaultName)));
// register /kb restore <name> for performing restore. OP is required.
- dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("restore").then(CommandManager.argument("backupName", StringArgumentType.string()).requires(PermissionValidator::op).executes(KBCommandHandler::restore)).executes(KBCommandHandler::list)));
+ dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("restore").then(CommandManager.argument("backupName", StringArgumentType.string()).requires(PermissionValidator::op).executes(KBCommands::restore)).executes(KBCommands::list)));
// register /kb confirm for confirming the execution. OP is required.
- dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("confirm").requires(PermissionValidator::op).executes(KBCommandHandler::confirm)));
+ dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("confirm").requires(PermissionValidator::op).executes(KBCommands::confirm)));
// register /kb cancel for cancelling the execution to be confirmed. OP is required.
- dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("cancel").requires(PermissionValidator::op).executes(KBCommandHandler::cancel)));
+ dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("cancel").requires(PermissionValidator::op).executes(KBCommands::cancel)));
}
}
diff --git a/src/main/java/com/keuin/kbackupfabric/data/BackupMetadata.java b/src/main/java/com/keuin/kbackupfabric/data/BackupMetadata.java
new file mode 100644
index 0000000..48670c6
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/data/BackupMetadata.java
@@ -0,0 +1,23 @@
+package com.keuin.kbackupfabric.data;
+
+import java.io.Serializable;
+
+public class BackupMetadata implements Serializable {
+ public static final String metadataFileName = "kbackup_metadata";
+ private static final long serialVersionUID = 1L;
+ private final long BackupTime;
+ private final String backupName;
+
+ public BackupMetadata(long backupTime, String backupName) {
+ BackupTime = backupTime;
+ this.backupName = backupName;
+ }
+
+ public long getBackupTime() {
+ return BackupTime;
+ }
+
+ public String getBackupName() {
+ return backupName;
+ }
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java b/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java
index 8c8a687..4b93d17 100644
--- a/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java
+++ b/src/main/java/com/keuin/kbackupfabric/util/PrintUtil.java
@@ -3,27 +3,60 @@ package com.keuin.kbackupfabric.util;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.LiteralText;
+import net.minecraft.text.Style;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public final class PrintUtil {
private static final Logger LOGGER = LogManager.getLogger();
+
private static final boolean printDebugMessages = true;
private static final boolean printErrorMessages = true;
private static final boolean printInfoMessages = true;
+
private static final Object syncDebug = new Object();
private static final Object syncError = new Object();
private static final Object syncInfo = new Object();
private static final Object syncMessage = new Object();
- public static void message(CommandContext<ServerCommandSource> context, String messageText) {
- message(context, messageText, false);
+ private static final Style infoStyle = new Style().setColor(Formatting.WHITE);
+ private static final Style debugStyle = new Style().setUnderline(true);
+ private static final Style warnStyle = new Style().setColor(Formatting.YELLOW);
+ private static final Style errorStyle = new Style().setColor(Formatting.DARK_RED);
+
+
+ public static CommandContext<ServerCommandSource> msgInfo(CommandContext<ServerCommandSource> context, String messageText) {
+ return msgInfo(context, messageText, false);
+ }
+
+ public static CommandContext<ServerCommandSource> msgWarn(CommandContext<ServerCommandSource> context, String messageText) {
+ return msgWarn(context, messageText, false);
+ }
+
+ public static CommandContext<ServerCommandSource> msgErr(CommandContext<ServerCommandSource> context, String messageText) {
+ return msgErr(context, messageText, false);
+ }
+
+ public static CommandContext<ServerCommandSource> msgInfo(CommandContext<ServerCommandSource> context, String messageText, boolean broadcastToOps) {
+ return message(context, messageText, broadcastToOps, infoStyle);
+ }
+
+ public static CommandContext<ServerCommandSource> msgWarn(CommandContext<ServerCommandSource> context, String messageText, boolean broadcastToOps) {
+ return message(context, messageText, broadcastToOps, warnStyle);
+ }
+
+ public static CommandContext<ServerCommandSource> msgErr(CommandContext<ServerCommandSource> context, String messageText, boolean broadcastToOps) {
+ return message(context, messageText, broadcastToOps, errorStyle);
}
- public static CommandContext<ServerCommandSource> message(CommandContext<ServerCommandSource> context, String messageText, boolean broadcastToOps) {
+ public static CommandContext<ServerCommandSource> message(CommandContext<ServerCommandSource> context, String messageText, boolean broadcastToOps, Style style) {
synchronized (syncMessage) {
- context.getSource().sendFeedback(new LiteralText("[KBackup] " + messageText), broadcastToOps);
+ Text text = new LiteralText(messageText);
+ text.setStyle(style);
+ context.getSource().sendFeedback(text, broadcastToOps);
}
return context;
}
@@ -31,8 +64,8 @@ public final class PrintUtil {
public static void debug(String message) {
synchronized (syncDebug) {
if (printDebugMessages) {
- System.out.println(String.format("[DEBUG] [KBackup] %s", message));
- LOGGER.debug(message);
+ //System.out.println(String.format("[DBG] [KB] %s", message));
+ LOGGER.debug("[KB][DEBUG] " + message);
}
}
}
@@ -40,8 +73,8 @@ public final class PrintUtil {
public static void error(String message) {
synchronized (syncError) {
if (printErrorMessages) {
- System.out.println(String.format("[ERROR] [KBackup] %s", message));
- LOGGER.error(message);
+ //System.out.println(String.format("[ERR] [KB] %s", message));
+ LOGGER.error("[KB][ERROR]" + message);
}
}
}
@@ -49,8 +82,8 @@ public final class PrintUtil {
public static void info(String message) {
synchronized (syncInfo) {
if (printInfoMessages) {
- System.out.println(String.format("[INFO] [KBackup] %s", message));
- LOGGER.info(message);
+ //System.out.println(String.format("[INF] [KB] %s", message));
+ LOGGER.info("[KB][INFO] " + message);
}
}
}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java b/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java
index fec4436..5297dda 100644
--- a/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java
+++ b/src/main/java/com/keuin/kbackupfabric/util/ZipUtil.java
@@ -1,5 +1,7 @@
package com.keuin.kbackupfabric.util;
+import com.keuin.kbackupfabric.data.BackupMetadata;
+
import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;
@@ -9,12 +11,12 @@ public final class ZipUtil {
/**
* 递归压缩文件夹
*
- * @param srcRootDir 压缩文件夹根目录的子路径
- * @param file 当前递归压缩的文件或目录对象
- * @param zos 压缩文件存储对象
+ * @param srcRootDir 压缩文件夹根目录的子路径
+ * @param file 当前递归压缩的文件或目录对象
+ * @param zipOutputStream 压缩文件存储对象
* @throws IOException IO Error
*/
- private static void zip(String srcRootDir, File file, ZipOutputStream zos) throws IOException {
+ private static void zip(String srcRootDir, File file, ZipOutputStream zipOutputStream) throws IOException {
if (file == null) {
return;
}
@@ -31,13 +33,13 @@ public final class ZipUtil {
subPath = subPath.substring(srcRootDir.length() + File.separator.length());
}
ZipEntry entry = new ZipEntry(subPath);
- zos.putNextEntry(entry);
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
- while ((count = bis.read(data, 0, bufferLen)) != -1) {
- zos.write(data, 0, count);
+ zipOutputStream.putNextEntry(entry);
+ BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
+ while ((count = inputStream.read(data, 0, bufferLen)) != -1) {
+ zipOutputStream.write(data, 0, count);
}
- bis.close();
- zos.closeEntry();
+ inputStream.close();
+ zipOutputStream.closeEntry();
}
// 如果是目录,则压缩整个目录
else {
@@ -45,11 +47,16 @@ public final class ZipUtil {
File[] childFileList = file.listFiles();
if (childFileList != null) {
for (File value : childFileList)
- zip(srcRootDir, value, zos);
+ zip(srcRootDir, value, zipOutputStream);
}
}
}
+// public static void makeZipBackup(String srcPath, String zipPath, String zipFileName) throws IOException, ZipUtilException {
+// zip(srcPath, zipPath, zipFileName, null);
+// }
+
+
/**
* 对文件或文件目录进行压缩
*
@@ -59,12 +66,12 @@ public final class ZipUtil {
* @throws IOException IO Error
* @throws ZipUtilException General exception, such as loop recursion or invalid input.
*/
- public static void zip(String srcPath, String zipPath, String zipFileName) throws IOException, ZipUtilException {
+ public static void makeBackupZip(String srcPath, String zipPath, String zipFileName, BackupMetadata backupMetadata) throws IOException, ZipUtilException {
if (srcPath.isEmpty() || zipPath.isEmpty() || zipFileName.isEmpty()) {
throw new ZipUtilException("Parameter for zip() contains null.");
}
- CheckedOutputStream cos;
- ZipOutputStream zos = null;
+ CheckedOutputStream checkedOutputStream;
+ ZipOutputStream zipOutputStream = null;
try {
File srcFile = new File(srcPath);
@@ -94,8 +101,18 @@ public final class ZipUtil {
}
}
- cos = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
- zos = new ZipOutputStream(cos);
+ checkedOutputStream = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
+ zipOutputStream = new ZipOutputStream(checkedOutputStream);
+
+ // If with backup metadata, we serialize it and write it into file "kbackup_metadata"
+ ZipEntry metadataEntry = new ZipEntry(BackupMetadata.metadataFileName);
+ zipOutputStream.putNextEntry(metadataEntry);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(backupMetadata);
+ oos.close();
+ zipOutputStream.write(baos.toByteArray());
+ zipOutputStream.closeEntry();
//如果只是压缩一个文件,则需要截取该文件的父目录
String srcRootDir = srcPath;
@@ -106,12 +123,12 @@ public final class ZipUtil {
}
}
//调用递归压缩方法进行目录或文件压缩
- zip(srcRootDir, srcFile, zos);
- zos.flush();
+ zip(srcRootDir, srcFile, zipOutputStream);
+ zipOutputStream.flush();
} finally {
try {
- if (zos != null) {
- zos.close();
+ if (zipOutputStream != null) {
+ zipOutputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
@@ -126,7 +143,6 @@ public final class ZipUtil {
* @param unzipFilePath 解压后的文件保存的路径
* @param includeZipFileName 解压后的文件保存的路径是否包含压缩文件的文件名。true-包含;false-不包含
*/
- @SuppressWarnings("unchecked")
public static void unzip(String zipFilePath, String unzipFilePath, boolean includeZipFileName) throws ZipUtilException, IOException {
if (zipFilePath.isEmpty() || unzipFilePath.isEmpty()) {
throw new ZipUtilException("Parameter for unzip() contains null.");
diff --git a/src/main/java/com/keuin/kbackupfabric/worker/BackupWorker.java b/src/main/java/com/keuin/kbackupfabric/worker/BackupWorker.java
index 30fd8b5..0ca9015 100644
--- a/src/main/java/com/keuin/kbackupfabric/worker/BackupWorker.java
+++ b/src/main/java/com/keuin/kbackupfabric/worker/BackupWorker.java
@@ -1,5 +1,7 @@
package com.keuin.kbackupfabric.worker;
+import com.keuin.kbackupfabric.data.BackupMetadata;
+import com.keuin.kbackupfabric.util.PrintUtil;
import com.keuin.kbackupfabric.util.ZipUtil;
import com.keuin.kbackupfabric.util.ZipUtilException;
import com.mojang.brigadier.context.CommandContext;
@@ -14,7 +16,7 @@ import java.util.Map;
import static com.keuin.kbackupfabric.util.BackupFilesystemUtil.*;
import static com.keuin.kbackupfabric.util.PrintUtil.debug;
-import static com.keuin.kbackupfabric.util.PrintUtil.message;
+import static com.keuin.kbackupfabric.util.PrintUtil.msgInfo;
/**
* The backup worker
@@ -24,18 +26,21 @@ public final class BackupWorker implements Runnable {
private final CommandContext<ServerCommandSource> context;
private final String backupName;
private final Map<World, Boolean> oldWorldsSavingDisabled;
+ private final BackupMetadata backupMetadata;
+ private final long startTime;
-
- private BackupWorker(CommandContext<ServerCommandSource> context, String backupName, Map<World, Boolean> oldWorldsSavingDisabled) {
+ private BackupWorker(CommandContext<ServerCommandSource> context, String backupName, Map<World, Boolean> oldWorldsSavingDisabled, BackupMetadata backupMetadata, long startTime) {
this.context = context;
this.backupName = backupName;
this.oldWorldsSavingDisabled = oldWorldsSavingDisabled;
+ this.backupMetadata = backupMetadata;
+ this.startTime = startTime;
}
- public static void invoke(CommandContext<ServerCommandSource> context, String backupName) {
+ public static void invoke(CommandContext<ServerCommandSource> context, String backupName, BackupMetadata backupMetadata) {
//// Save world, save old autosave configs
- message(context, String.format("Making backup %s, please wait ...", backupName), true);
+ PrintUtil.msgInfo(context, String.format("Making backup %s, please wait ...", backupName), true);
Map<World, Boolean> oldWorldsSavingDisabled = new HashMap<>(); // old switch stat
// Get server
@@ -54,39 +59,52 @@ public final class BackupWorker implements Runnable {
server.save(true, true, true);
// Start threaded worker
- BackupWorker worker = new BackupWorker(context, backupName, oldWorldsSavingDisabled);
+ BackupWorker worker = new BackupWorker(context, backupName, oldWorldsSavingDisabled, backupMetadata, System.currentTimeMillis());
Thread workerThread = new Thread(worker, "BackupWorker");
workerThread.start();
}
@Override
public void run() {
- String destPathFolderName = "";
+ String backupSaveDirectory = "";
MinecraftServer server = context.getSource().getMinecraftServer();
try {
//// Do our main backup logic
// Create backup saving directory
- File destPathFile = getBackupSaveDirectory(server);
- destPathFolderName = destPathFile.getName();
- if (!destPathFile.isDirectory() && !destPathFile.mkdir()) {
- message(context, String.format("Failed to create backup saving directory: %s. Failed to backup.", destPathFolderName));
+ File backupSaveDirectoryFile = getBackupSaveDirectory(server);
+ backupSaveDirectory = backupSaveDirectoryFile.getName();
+ if (!backupSaveDirectoryFile.isDirectory() && !backupSaveDirectoryFile.mkdir()) {
+ msgInfo(context, String.format("Failed to create backup saving directory: %s. Failed to backup.", backupSaveDirectory));
return;
}
// Make zip
String levelPath = getLevelPath(server);
- debug(String.format("zip(srcPath=%s, destPath=%s)", levelPath, destPathFile.toString()));
- ZipUtil.zip(levelPath, destPathFile.toString(), getBackupFileName(backupName));
+ String backupFileName = getBackupFileName(backupName);
+ debug(String.format("zip(srcPath=%s, destPath=%s)", levelPath, backupSaveDirectoryFile.toString()));
+ ZipUtil.makeBackupZip(levelPath, backupSaveDirectoryFile.toString(), backupFileName, backupMetadata);
+ File backupZipFile = new File(backupSaveDirectoryFile, backupFileName);
// Restore old autosave switch stat
server.getWorlds().forEach(world -> world.savingDisabled = oldWorldsSavingDisabled.getOrDefault(world, true));
- message(context, "Done.", true);
+ // Print finish message: time elapsed and file size
+ long timeEscapedMillis = System.currentTimeMillis() - startTime;
+ msgInfo(context, String.format("Backup finished. (%.2fs)", timeEscapedMillis / 1000.0), true);
+ try {
+ double fileSize = backupZipFile.length() * 1.0 / 1024 / 1024;
+ if (fileSize > 1000)
+ msgInfo(context, String.format("File size: %.2fGB", fileSize / 1024));
+ else
+ msgInfo(context, String.format("File size: %.2fMB", fileSize));
+ } catch (SecurityException ignored) {
+ }
+
} catch (SecurityException e) {
- message(context, String.format("Failed to create backup saving directory: %s. Failed to backup.", destPathFolderName));
+ msgInfo(context, String.format("Failed to create backup saving directory: %s. Failed to backup.", backupSaveDirectory));
} catch (IOException | ZipUtilException e) {
- message(context, "Failed to make zip: " + e.getMessage());
+ msgInfo(context, "Failed to make zip: " + e.getMessage());
}
}
}