diff options
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java (renamed from src/main/java/com/keuin/kbackupfabric/KBRegister.java) | 6 | ||||
-rw-r--r-- | src/main/java/com/keuin/kbackupfabric/KBCommands.java | 4 | ||||
-rw-r--r-- | src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java | 17 | ||||
-rw-r--r-- | src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java | 75 |
5 files changed, 91 insertions, 12 deletions
@@ -17,7 +17,6 @@ commands: To-Do List: -- Add backup name completion. - Optimize the backup name output. (add datetime output automatically, name is essential and is separated from the datetime) - More thorough test. - Enhance ZipUtil for hashing sub-files and generating incremental diff-table (A:Add, M:Modification, D:Deletion) diff --git a/src/main/java/com/keuin/kbackupfabric/KBRegister.java b/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java index 11f05a1..ec67a64 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBRegister.java +++ b/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java @@ -1,15 +1,17 @@ package com.keuin.kbackupfabric; +import com.keuin.kbackupfabric.util.BackupNameSuggestionProvider; import com.keuin.kbackupfabric.util.PermissionValidator; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; -public final class KBRegister { +public final class KBCommandRegister { // First make method to register public static void registerCommands(CommandDispatcher<ServerCommandSource> dispatcher) { // register /kb and /kb help for help menu + dispatcher.register(CommandManager.literal("kb").executes(KBCommands::help)); dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("help").executes(KBCommands::help))); @@ -20,7 +22,7 @@ public final class KBRegister { 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(KBCommands::restore)).executes(KBCommands::list))); + dispatcher.register(CommandManager.literal("kb").then(CommandManager.literal("restore").then(CommandManager.argument("backupName", StringArgumentType.string()).suggests(BackupNameSuggestionProvider.getSuggestionProvider()).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(KBCommands::confirm))); diff --git a/src/main/java/com/keuin/kbackupfabric/KBCommands.java b/src/main/java/com/keuin/kbackupfabric/KBCommands.java index c636a0d..b932c80 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBCommands.java +++ b/src/main/java/com/keuin/kbackupfabric/KBCommands.java @@ -111,7 +111,7 @@ public final class KBCommands { // Update confirm pending variable restoreBackupNameToBeConfirmed = backupName; - 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); + msgWarn(context, String.format("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.", restoreBackupNameToBeConfirmed), true); return SUCCESS; } @@ -124,7 +124,7 @@ public final class KBCommands { */ public static int backupWithDefaultName(CommandContext<ServerCommandSource> context) { //KBMain.backup("name") - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); String timeString = LocalDateTime.now().format(formatter); return doBackup(context, timeString); } diff --git a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java index 4f0f1ff..a1ffcad 100644 --- a/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java +++ b/src/main/java/com/keuin/kbackupfabric/KBPluginEvents.java @@ -1,6 +1,8 @@ package com.keuin.kbackupfabric; import com.keuin.kbackupfabric.data.BackupMetadata; +import com.keuin.kbackupfabric.util.BackupFilesystemUtil; +import com.keuin.kbackupfabric.util.BackupNameSuggestionProvider; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.server.ServerStartCallback; import net.fabricmc.fabric.api.registry.CommandRegistry; @@ -24,18 +26,19 @@ import static org.apache.commons.io.FileUtils.forceDelete; public final class KBPluginEvents implements ModInitializer, ServerStartCallback { @Override public void onInitialize() { - System.out.println("Initializing KBackup..."); - CommandRegistry.INSTANCE.register(false, KBRegister::registerCommands); + System.out.println("KBackup: Binding events and commands ..."); + CommandRegistry.INSTANCE.register(false, KBCommandRegister::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 + debug("KBackup: Initializing ..."); + + // Update backup suggestion list + BackupNameSuggestionProvider.setBackupSaveDirectory(BackupFilesystemUtil.getBackupSaveDirectory(server).getPath()); + + // Check if we have just recovered from a previous backup. If so, print message. try { File levelDirectory = new File(server.getRunDirectory(), server.getLevelName()); File metadataFile = new File(levelDirectory, BackupMetadata.metadataFileName); diff --git a/src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java b/src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java new file mode 100644 index 0000000..08e12b2 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java @@ -0,0 +1,75 @@ +package com.keuin.kbackupfabric.util; + +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.server.command.ServerCommandSource; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; + +public class BackupNameSuggestionProvider { + private static final List<String> candidateCacheList = new ArrayList<>(); + private static final Object syncSetDirectory = new Object(); + private static final Object syncUpdate = new Object(); + private static final long CACHE_TTL = 10000; + private static String backupSaveDirectory; + private static long cacheUpdateTime = 0; + + public static void setBackupSaveDirectory(String backupSaveDirectory) { + synchronized (syncSetDirectory) { + BackupNameSuggestionProvider.backupSaveDirectory = backupSaveDirectory; + } + // Immediately perform a update + updateCandidateList(); + } + + public static void updateCandidateList() { + synchronized (syncUpdate) { + try { + File file = new File(backupSaveDirectory); + candidateCacheList.clear(); + File[] files = file.listFiles(); + if (files == null) + return; + for (File f : files) + candidateCacheList.add(BackupFilesystemUtil.getBackupName(f.getName())); + cacheUpdateTime = System.currentTimeMillis(); + } catch (NullPointerException ignored) { + } + } + } + +// private static void updateCandidateList(Collection<String> stringCollection) { +// candidateList.clear(); +// candidateList.addAll(stringCollection); +// } + + public static SuggestionProvider<ServerCommandSource> getSuggestionProvider() { + return (context, builder) -> getCompletableFuture(builder); + } + + private static CompletableFuture<Suggestions> getCompletableFuture(SuggestionsBuilder builder) { + if (isCacheExpired()) + updateCandidateList(); + String remaining = builder.getRemaining().toLowerCase(Locale.ROOT); + + if (candidateCacheList.isEmpty()) { // If the list is empty then return no suggestions + return Suggestions.empty(); // No suggestions + } + + for (String string : candidateCacheList) { // Iterate through the supplied list + if (string.toLowerCase(Locale.ROOT).startsWith(remaining)) { + builder.suggest(string); // Add every single entry to suggestions list. + } + } + return builder.buildFuture(); // Create the CompletableFuture containing all the suggestions + } + + private static boolean isCacheExpired() { + return System.currentTimeMillis() - cacheUpdateTime > CACHE_TTL; + } +} |