From f7f918f19b5d0597d21f03b7085425c3a93cc5f6 Mon Sep 17 00:00:00 2001 From: Keuin Date: Thu, 23 Apr 2020 20:57:17 +0800 Subject: Added backup name completion. --- README.md | 1 - .../com/keuin/kbackupfabric/KBCommandRegister.java | 34 ++++++++++ .../java/com/keuin/kbackupfabric/KBCommands.java | 4 +- .../com/keuin/kbackupfabric/KBPluginEvents.java | 17 +++-- .../java/com/keuin/kbackupfabric/KBRegister.java | 32 --------- .../util/BackupNameSuggestionProvider.java | 75 ++++++++++++++++++++++ 6 files changed, 121 insertions(+), 42 deletions(-) create mode 100644 src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java delete mode 100644 src/main/java/com/keuin/kbackupfabric/KBRegister.java create mode 100644 src/main/java/com/keuin/kbackupfabric/util/BackupNameSuggestionProvider.java diff --git a/README.md b/README.md index 3125323..5536dbb 100644 --- a/README.md +++ b/README.md @@ -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/KBCommandRegister.java b/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java new file mode 100644 index 0000000..ec67a64 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/KBCommandRegister.java @@ -0,0 +1,34 @@ +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 KBCommandRegister { + // First make method to register + public static void registerCommands(CommandDispatcher 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))); + + // register /kb list for showing the backup list. OP is required. + 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(KBCommands::backup)).requires(PermissionValidator::op).executes(KBCommands::backupWithDefaultName))); + + // register /kb restore for performing restore. OP is required. + 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))); + + // 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(KBCommands::cancel))); + + } +} 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 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/KBRegister.java b/src/main/java/com/keuin/kbackupfabric/KBRegister.java deleted file mode 100644 index 11f05a1..0000000 --- a/src/main/java/com/keuin/kbackupfabric/KBRegister.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.keuin.kbackupfabric; - -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 { - // First make method to register - public static void registerCommands(CommandDispatcher 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))); - - // register /kb list for showing the backup list. OP is required. - 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(KBCommands::backup)).requires(PermissionValidator::op).executes(KBCommands::backupWithDefaultName))); - - // register /kb restore 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))); - - // 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))); - - // 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(KBCommands::cancel))); - - } -} 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 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 stringCollection) { +// candidateList.clear(); +// candidateList.addAll(stringCollection); +// } + + public static SuggestionProvider getSuggestionProvider() { + return (context, builder) -> getCompletableFuture(builder); + } + + private static CompletableFuture 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; + } +} -- cgit v1.2.3