summaryrefslogtreecommitdiff
path: root/src/main/java/com/keuin/kbackupfabric/KBCommands.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/keuin/kbackupfabric/KBCommands.java')
-rw-r--r--src/main/java/com/keuin/kbackupfabric/KBCommands.java303
1 files changed, 190 insertions, 113 deletions
diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommands.java b/src/main/java/com/keuin/kbackupfabric/KBCommands.java
index bc04291..1b52b6b 100644
--- a/src/main/java/com/keuin/kbackupfabric/KBCommands.java
+++ b/src/main/java/com/keuin/kbackupfabric/KBCommands.java
@@ -5,24 +5,29 @@ import com.keuin.kbackupfabric.operation.BackupOperation;
import com.keuin.kbackupfabric.operation.DeleteOperation;
import com.keuin.kbackupfabric.operation.RestoreOperation;
import com.keuin.kbackupfabric.operation.abstracts.i.Invokable;
-import com.keuin.kbackupfabric.operation.backup.BackupMethod;
-import com.keuin.kbackupfabric.operation.backup.IncrementalBackupMethod;
-import com.keuin.kbackupfabric.operation.backup.PrimitiveBackupMethod;
+import com.keuin.kbackupfabric.operation.backup.method.ConfiguredBackupMethod;
+import com.keuin.kbackupfabric.operation.backup.method.ConfiguredIncrementalBackupMethod;
+import com.keuin.kbackupfabric.operation.backup.method.ConfiguredPrimitiveBackupMethod;
+import com.keuin.kbackupfabric.util.PrintUtil;
import com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil;
-import com.keuin.kbackupfabric.util.backup.BackupType;
+import com.keuin.kbackupfabric.util.backup.name.IncrementalBackupFileNameEncoder;
+import com.keuin.kbackupfabric.util.backup.name.PrimitiveBackupFileNameEncoder;
import com.keuin.kbackupfabric.util.backup.suggestion.BackupNameSuggestionProvider;
-import com.keuin.kbackupfabric.util.backup.BackupNameTimeFormatter;
-import com.keuin.kbackupfabric.util.PrintUtil;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import java.io.File;
-import java.util.*;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
-import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.*;
import static com.keuin.kbackupfabric.util.PrintUtil.*;
+import static com.keuin.kbackupfabric.util.backup.BackupFilesystemUtil.*;
public final class KBCommands {
@@ -30,10 +35,11 @@ public final class KBCommands {
private static final int SUCCESS = 1;
private static final int FAILED = -1;
private static final String DEFAULT_BACKUP_NAME = "noname";
+ private static boolean notifiedPreviousRestoration = false;
//private static final Logger LOGGER = LogManager.getLogger();
- private static final List<String> backupNameList = new ArrayList<>(); // index -> backupName
+ private static final List<String> backupFileNameList = new ArrayList<>(); // index -> backupName
private static Invokable pendingOperation = null;
//private static BackupMethod activatedBackupMethod = new PrimitiveBackupMethod(); // The backup method we currently using
@@ -62,8 +68,9 @@ public final class KBCommands {
*/
public static int kb(CommandContext<ServerCommandSource> context) {
int statCode = list(context);
- if (MetadataHolder.hasMetadata()) {
+ if (MetadataHolder.hasMetadata() && !notifiedPreviousRestoration) {
// Output metadata info
+ notifiedPreviousRestoration = true;
msgStress(context, "Restored from backup " + MetadataHolder.getMetadata().getBackupName());
}
return statCode;
@@ -78,25 +85,29 @@ public final class KBCommands {
public static int list(CommandContext<ServerCommandSource> context) {
MinecraftServer server = context.getSource().getMinecraftServer();
File[] files = getBackupSaveDirectory(server).listFiles(
- (dir, name) -> dir.isDirectory() && name.toLowerCase().endsWith(".zip") && name.toLowerCase().startsWith(getBackupFileNamePrefix())
+ (dir, name) -> dir.isDirectory() &&
+ (name.toLowerCase().endsWith(".zip") && name.toLowerCase().startsWith(getBackupFileNamePrefix())
+ || name.toLowerCase().endsWith(".kbi"))
);
- backupNameList.clear();
- if (files != null) {
- if (files.length != 0) {
- msgInfo(context, "Available backups: (file is not checked, manipulation may affect this plugin)");
+ synchronized (backupFileNameList) {
+ backupFileNameList.clear();
+ if (files != null) {
+ if (files.length != 0) {
+ msgInfo(context, "Available backups: (file is not checked, manipulation may affect this plugin)");
+ } else {
+ msgInfo(context, "There are no available backups. To make a new backup, run /kb backup.");
+ }
+ int i = 0;
+ for (File file : files) {
+ ++i;
+ String backupFileName = file.getName();
+ msgInfo(context, String.format("[%d] %s", i, getPrimitiveBackupInformationString(backupFileName, file.length())));
+ backupFileNameList.add(backupFileName);
+ }
} else {
- msgInfo(context, "There are no available backups. To make a new backup, check /kb backup.");
- }
- int i = 0;
- for (File file : files) {
- ++i;
- String backupName = getBackupName(file.getName());
- backupNameList.add(backupName);
- msgInfo(context, String.format("[%d] %s, size: %.1fMB", i, backupName, file.length() * 1.0 / 1024 / 1024));
+ msgErr(context, "Error: failed to list files in backup folder.");
}
- } else {
- msgErr(context, "Error: failed to list files in backup folder.");
}
return SUCCESS;
}
@@ -109,13 +120,13 @@ public final class KBCommands {
*/
public static int primitiveBackup(CommandContext<ServerCommandSource> context) {
//KBMain.backup("name")
- String backupName = StringArgumentType.getString(context, "backupName");
- if (backupName.matches("[0-9]*")) {
+ String customBackupName = StringArgumentType.getString(context, "backupName");
+ if (customBackupName.matches("[0-9]*")) {
// Numeric param is not allowed
- backupName = String.format("a%s", backupName);
- msgWarn(context, String.format("Pure numeric name is not allowed. Renaming to %s", backupName));
+ customBackupName = String.format("a%s", customBackupName);
+ msgWarn(context, String.format("Pure numeric name is not allowed. Renaming to %s", customBackupName));
}
- return doBackup(context, backupName, PrimitiveBackupMethod.getInstance());
+ return doBackup(context, customBackupName, false);
}
/**
@@ -125,24 +136,39 @@ public final class KBCommands {
* @return stat code.
*/
public static int primitiveBackupWithDefaultName(CommandContext<ServerCommandSource> context) {
- return doBackup(context, DEFAULT_BACKUP_NAME, PrimitiveBackupMethod.getInstance());
+ return doBackup(context, DEFAULT_BACKUP_NAME, false);
}
public static int incrementalBackup(CommandContext<ServerCommandSource> context) {
- //KBMain.backup("name")
- String backupName = StringArgumentType.getString(context, "backupName");
- if (backupName.matches("[0-9]*")) {
+ String customBackupName = StringArgumentType.getString(context, "backupName");
+ if (customBackupName.matches("[0-9]*")) {
// Numeric param is not allowed
- backupName = String.format("a%s", backupName);
- msgWarn(context, String.format("Pure numeric name is not allowed. Renaming to %s", backupName));
+ customBackupName = String.format("a%s", customBackupName);
+ msgWarn(context, String.format("Pure numeric name is not allowed. Renaming to %s", customBackupName));
}
- return doBackup(context, backupName, IncrementalBackupMethod.getInstance());
+ return doBackup(context, customBackupName, true);
}
public static int incrementalBackupWithDefaultName(CommandContext<ServerCommandSource> context) {
- return doBackup(context, DEFAULT_BACKUP_NAME, IncrementalBackupMethod.getInstance());
+ return doBackup(context, DEFAULT_BACKUP_NAME, true);
}
+
+// public static int incrementalBackup(CommandContext<ServerCommandSource> context) {
+// //KBMain.backup("name")
+// String backupName = StringArgumentType.getString(context, "backupName");
+// if (backupName.matches("[0-9]*")) {
+// // Numeric param is not allowed
+// backupName = String.format("a%s", backupName);
+// msgWarn(context, String.format("Pure numeric name is not allowed. Renaming to %s", backupName));
+// }
+// return doBackup(context, backupName, IncrementalBackupMethod.getInstance());
+// }
+//
+// public static int incrementalBackupWithDefaultName(CommandContext<ServerCommandSource> context) {
+// return doBackup(context, DEFAULT_BACKUP_NAME, IncrementalBackupMethod.getInstance());
+// }
+
/**
* Delete an existing backup with context parameter backupName.
* Simply set the pending backupName to given backupName, for the second confirmation.
@@ -152,14 +178,14 @@ public final class KBCommands {
*/
public static int delete(CommandContext<ServerCommandSource> context) {
- String backupName = parseBackupName(context, StringArgumentType.getString(context, "backupName"));
+ String backupFileName = parseBackupFileName(context, StringArgumentType.getString(context, "backupName"));
MinecraftServer server = context.getSource().getMinecraftServer();
- if (backupName == null)
+ if (backupFileName == null)
return list(context); // Show the list and return
// Validate backupName
- if (!isBackupNameValid(backupName, server)) {
+ if (!isBackupFileExists(backupFileName, server)) {
// Invalid backupName
msgErr(context, "Invalid backup name! Please check your input. The list index number is also valid.");
return FAILED;
@@ -167,9 +193,9 @@ public final class KBCommands {
// Update pending task
//pendingOperation = AbstractConfirmableOperation.createDeleteOperation(context, backupName);
- pendingOperation = new DeleteOperation(context, backupName);
+ pendingOperation = new DeleteOperation(context, backupFileName);
- msgWarn(context, String.format("DELETION WARNING: The deletion is irreversible! You will lose the backup %s permanently. Use /kb confirm to start or /kb cancel to abort.", backupName), true);
+ msgWarn(context, String.format("DELETION WARNING: The deletion is irreversible! You will lose the backup %s permanently. Use /kb confirm to start or /kb cancel to abort.", backupFileName), true);
return SUCCESS;
}
@@ -182,55 +208,89 @@ public final class KBCommands {
* @return stat code.
*/
public static int restore(CommandContext<ServerCommandSource> context) {
- //KBMain.restore("name")
- MinecraftServer server = context.getSource().getMinecraftServer();
- String backupName = parseBackupName(context, StringArgumentType.getString(context, "backupName"));
- backupName = parseBackupName(context, backupName);
-
- if (backupName == null)
- return list(context); // Show the list and return
-
- // Validate backupName
- if (!isBackupNameValid(backupName, server)) {
- // Invalid backupName
- msgErr(context, "Invalid backup name! Please check your input. The list index number is also valid.", false);
- return FAILED;
- }
-
- // Detect backup type
+ try {
+ //KBMain.restore("name")
+ MinecraftServer server = context.getSource().getMinecraftServer();
+ String backupFileName = parseBackupFileName(context, StringArgumentType.getString(context, "backupName"));
+ backupFileName = parseBackupFileName(context, backupFileName);
+ if (backupFileName == null)
+ return list(context); // Show the list and return
- // Update pending task
- //pendingOperation = AbstractConfirmableOperation.createRestoreOperation(context, backupName);
- File backupFile = new File(getBackupSaveDirectory(server), getBackupFileName(backupName));
- pendingOperation = new RestoreOperation(context, backupFile.getAbsolutePath(), getLevelPath(server), backupName);
+ // Validate backupName
+ if (!isBackupFileExists(backupFileName, server)) {
+ // Invalid backupName
+ msgErr(context, "Invalid backup name! Please check your input. The list index number is also valid.", false);
+ return FAILED;
+ }
- msgWarn(context, String.format("RESET WARNING: You will LOSE YOUR CURRENT WORLD PERMANENTLY! The worlds will be replaced with backup %s . Use /kb confirm to start or /kb cancel to abort.", backupName), true);
- return SUCCESS;
+ // Detect backup type
+
+
+ // Update pending task
+ //pendingOperation = AbstractConfirmableOperation.createRestoreOperation(context, backupName);
+// File backupFile = new File(getBackupSaveDirectory(server), getBackupFileName(backupName));
+ // TODO: improve this
+ ConfiguredBackupMethod method = backupFileName.endsWith(".zip") ?
+ new ConfiguredPrimitiveBackupMethod(
+ backupFileName, getLevelPath(server), getBackupSaveDirectory(server).getAbsolutePath()
+ ) : new ConfiguredIncrementalBackupMethod(
+ backupFileName, getLevelPath(server),
+ getBackupSaveDirectory(server).getAbsolutePath(),
+ getIncrementalBackupBaseDirectory(server).getAbsolutePath()
+ );
+ // String backupSavePath, String levelPath, String backupFileName
+// getBackupSaveDirectory(server).getAbsolutePath(), getLevelPath(server), backupFileName
+ pendingOperation = new RestoreOperation(context, method);
+
+ msgWarn(context, String.format("RESET WARNING: You will LOSE YOUR CURRENT WORLD PERMANENTLY! The worlds will be replaced with backup %s . Use /kb confirm to start or /kb cancel to abort.", backupFileName), true);
+ return SUCCESS;
+ } catch (IOException e) {
+ msgErr(context, String.format("An I/O exception occurred while making backup: %s", e));
+ }
+ return FAILED;
}
- private static int doBackup(CommandContext<ServerCommandSource> context, String customBackupName, BackupMethod backupMethod) {
- // Real backup name (compatible with legacy backup): date_name, such as 2020-04-23_21-03-00_test
- //KBMain.backup("name")
- String backupName = BackupNameTimeFormatter.getTimeString() + "_" + customBackupName;
+ private static int doBackup(CommandContext<ServerCommandSource> context, String customBackupName, boolean incremental) {
+ try {
+ // Real backup name (compatible with legacy backup): date_name, such as 2020-04-23_21-03-00_test
+ //KBMain.backup("name")
+// String backupName = BackupNameTimeFormatter.getTimeString() + "_" + customBackupName;
+
+ // Validate file name
+ final char[] ILLEGAL_CHARACTERS = {'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'};
+ for (char c : ILLEGAL_CHARACTERS) {
+ if (customBackupName.contains(String.valueOf(c))) {
+ msgErr(context, String.format("Name cannot contain special character \"%c\".", c));
+ return FAILED;
+ }
+ }
- // Validate file name
- final char[] ILLEGAL_CHARACTERS = {'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'};
- for (char c : ILLEGAL_CHARACTERS) {
- if (backupName.contains(String.valueOf(c))) {
- msgErr(context, String.format("Name cannot contain special character \"%c\".", c));
+ PrintUtil.info("Start backup...");
+
+ // configure backup method
+ MinecraftServer server = context.getSource().getMinecraftServer();
+ ConfiguredBackupMethod method = !incremental ? new ConfiguredPrimitiveBackupMethod(
+ new PrimitiveBackupFileNameEncoder().encode(customBackupName, LocalDateTime.now()),
+ getLevelPath(server),
+ getBackupSaveDirectory(server).getAbsolutePath()
+ ) : new ConfiguredIncrementalBackupMethod(
+ new IncrementalBackupFileNameEncoder().encode(customBackupName, LocalDateTime.now()),
+ getLevelPath(server),
+ getBackupSaveDirectory(server).getAbsolutePath(),
+ getIncrementalBackupBaseDirectory(server).getAbsolutePath()
+ );
+
+ // dispatch to operation worker
+ BackupOperation operation = new BackupOperation(context, method);
+ if (operation.invoke()) {
+ return SUCCESS;
+ } else if (operation.isBlocked()) {
+ msgWarn(context, "Another task is running, cannot issue new backup at once.");
return FAILED;
}
- }
-
- // Do backup
- PrintUtil.info("Invoking backup worker ...");
- //BackupWorker.invoke(context, backupName, metadata);
- BackupOperation operation = new BackupOperation(context, backupName, backupMethod);
- if (operation.invoke()) {
- return SUCCESS;
- } else if (operation.isBlocked()) {
- msgWarn(context, "Another task is running, cannot issue new backup at once.");
+ } catch (IOException e) {
+ msgErr(context, String.format("An I/O exception occurred while making backup: %s", e));
}
return FAILED;
}
@@ -290,15 +350,22 @@ public final class KBCommands {
files.removeIf(f -> !f.getName().startsWith(BackupFilesystemUtil.getBackupFileNamePrefix()));
files.sort((x, y) -> (int) (BackupFilesystemUtil.getBackupTimeFromBackupFileName(y.getName()) - BackupFilesystemUtil.getBackupTimeFromBackupFileName(x.getName())));
File prevBackupFile = files.get(0);
- String backupName = getBackupName(prevBackupFile.getName());
- int i = backupNameList.indexOf(backupName);
- if (i == -1) {
- backupNameList.add(backupName);
- i = backupNameList.size();
- } else {
- ++i;
+ String backupFileName = prevBackupFile.getName();
+ int i;
+ synchronized (backupFileNameList) {
+ i = backupFileNameList.indexOf(backupFileName);
+ if (i == -1) {
+ backupFileNameList.add(backupFileName);
+ i = backupFileNameList.size();
+ } else {
+ ++i;
+ }
}
- msgInfo(context, String.format("The most recent backup: [%d] %s , size: %s", i, backupName, humanFileSize(prevBackupFile.length())));
+ msgInfo(context, String.format(
+ "The most recent backup: [%d] %s",
+ i,
+ getPrimitiveBackupInformationString(backupFileName, prevBackupFile.length())
+ ));
} catch (NullPointerException e) {
msgInfo(context, "There are no backups available.");
} catch (SecurityException ignored) {
@@ -308,35 +375,45 @@ public final class KBCommands {
return SUCCESS;
}
- /**
- * Select the backup method we use.
- * @param context the context.
- * @return stat code.
- */
- public static int setMethod(CommandContext<ServerCommandSource> context) {
- String desiredMethodName = StringArgumentType.getString(context, "backupMethod");
- List<BackupType> backupMethods = Arrays.asList(BackupType.PRIMITIVE_ZIP_BACKUP, BackupType.OBJECT_TREE_BACKUP);
- for (BackupType method : backupMethods) {
- if(method.getName().equals(desiredMethodName)) {
- // Incremental backup
-// activatedBackupMethod =
- msgInfo(context, String.format("Backup method is set to: %s", desiredMethodName));
- return SUCCESS;
- }
- }
-
- return SUCCESS;
+ private static String getPrimitiveBackupInformationString(String backupFileName, long backupFileSizeBytes) {
+ return String.format(
+ "%s , size: %s",
+ new PrimitiveBackupFileNameEncoder().decode(backupFileName),
+ getFriendlyFileSizeString(backupFileSizeBytes)
+ );
}
-
- private static String parseBackupName(CommandContext<ServerCommandSource> context, String userInput) {
+// /**
+// * Select the backup method we use.
+// * @param context the context.
+// * @return stat code.
+// */
+// public static int setMethod(CommandContext<ServerCommandSource> context) {
+// String desiredMethodName = StringArgumentType.getString(context, "backupMethod");
+// List<BackupType> backupMethods = Arrays.asList(BackupType.PRIMITIVE_ZIP_BACKUP, BackupType.OBJECT_TREE_BACKUP);
+// for (BackupType method : backupMethods) {
+// if(method.getName().equals(desiredMethodName)) {
+// // Incremental backup
+//// activatedBackupMethod =
+// msgInfo(context, String.format("Backup method is set to: %s", desiredMethodName));
+// return SUCCESS;
+// }
+// }
+//
+// return SUCCESS;
+// }
+
+
+ private static String parseBackupFileName(CommandContext<ServerCommandSource> context, String userInput) {
try {
String backupName = StringArgumentType.getString(context, "backupName");
if (backupName.matches("[0-9]*")) {
// If numeric input
int index = Integer.parseInt(backupName) - 1;
- return backupNameList.get(index); // Replace input number with real backup name.
+ synchronized (backupFileNameList) {
+ return backupFileNameList.get(index); // Replace input number with real backup file name.
+ }
}
} catch (NumberFormatException | IndexOutOfBoundsException ignored) {
}