From 312cb5769247413c8a26f0ed3a00e703011e5e92 Mon Sep 17 00:00:00 2001 From: Szum123321 Date: Mon, 23 Mar 2020 17:42:17 +0100 Subject: [PATCH] Added: -per world backup -configurable datetime format -configurable permission level -player black and whitelist --- gradle.properties | 4 +- .../textile_backup/ConfigHandler.java | 22 +++- .../textile_backup/TextileBackup.java | 7 +- .../commands/BlacklistCommand.java | 103 ++++++++++++++++++ .../commands/CleanupCommand.java | 9 +- .../commands/StartBackupCommand.java | 7 +- .../commands/WhitelistCommand.java | 103 ++++++++++++++++++ .../textile_backup/core/BackupHelper.java | 46 ++++++-- .../textile_backup/core/MakeBackupThread.java | 4 +- .../mixin/MinecraftServerMixin.java | 2 +- src/main/resources/fabric.mod.json | 2 +- 11 files changed, 283 insertions(+), 26 deletions(-) create mode 100644 src/main/java/net/szum123321/textile_backup/commands/BlacklistCommand.java create mode 100644 src/main/java/net/szum123321/textile_backup/commands/WhitelistCommand.java diff --git a/gradle.properties b/gradle.properties index 8c9bf61..7168448 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,13 +2,13 @@ org.gradle.jvmargs=-Xmx1G minecraft_version=20w12a -yarn_mappings=20w12a+build.18 +yarn_mappings=20w12a+build.19 loader_version=0.7.8+build.189 #Fabric api fabric_version=0.5.5+build.311-1.16 # Mod Properties - mod_version = 1.0.4-1.16 + mod_version = 1.1.0-1.16 maven_group = net.szum123321 archives_base_name = textile_backup \ No newline at end of file diff --git a/src/main/java/net/szum123321/textile_backup/ConfigHandler.java b/src/main/java/net/szum123321/textile_backup/ConfigHandler.java index a51ee49..c1120c2 100644 --- a/src/main/java/net/szum123321/textile_backup/ConfigHandler.java +++ b/src/main/java/net/szum123321/textile_backup/ConfigHandler.java @@ -21,6 +21,9 @@ package net.szum123321.textile_backup; import blue.endless.jankson.Comment; import io.github.cottonmc.cotton.config.annotations.ConfigFile; +import java.util.HashSet; +import java.util.Set; + @ConfigFile(name = TextileBackup.MOD_ID) public class ConfigHandler { @Comment("\nTime between backups in seconds\n") @@ -35,10 +38,13 @@ public class ConfigHandler { @Comment("\nA path to backup folder\n") public String path = "backup/"; - @Comment("\nMaximum number of backups to keep. if 0 then no backup will be deleted\n") + @Comment("\nShould every world has its won backup folder?\n") + public boolean perWorldBackup = false; + + @Comment("\nMaximum number of backups to keep. if 0 then no backup will be deleted based on its amount\n") public int backupsToKeep = 10; - @Comment("\nMaximum age of backups to keep in seconds.\n if 0 then backups will not be deleted based on age \n") + @Comment("\nMaximum age of backups to keep in seconds.\n if 0 then backups will not be deleted based on its age \n") public int maxAge = 0; @Comment("\nMaximum size of backup folder in kilo bytes. \n") @@ -49,4 +55,16 @@ public class ConfigHandler { @Comment("\nPrint info to game out\n") public boolean log = true; + + @Comment("\nMinimal permission level required to run commands\n") + public int permissionLevel = 4; + + @Comment("\nPlayers allowed to run backup commands without sufficient permission level\n") + public Set whitelist = new HashSet<>(); + + @Comment("\nPlayers banned from running backup commands besides their sufficient permission level\n") + public Set blacklist = new HashSet<>(); + + @Comment("\nFormat of date&time used to name backup files.\n") + public String dateTimeFormat = "dd.MM.yyyy_HH-mm-ss"; } diff --git a/src/main/java/net/szum123321/textile_backup/TextileBackup.java b/src/main/java/net/szum123321/textile_backup/TextileBackup.java index 6918974..476dfba 100644 --- a/src/main/java/net/szum123321/textile_backup/TextileBackup.java +++ b/src/main/java/net/szum123321/textile_backup/TextileBackup.java @@ -25,8 +25,10 @@ import io.github.cottonmc.cotton.logging.ModLogger; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.registry.CommandRegistry; import net.minecraft.server.command.ServerCommandSource; +import net.szum123321.textile_backup.commands.BlacklistCommand; import net.szum123321.textile_backup.commands.CleanupCommand; import net.szum123321.textile_backup.commands.StartBackupCommand; +import net.szum123321.textile_backup.commands.WhitelistCommand; public class TextileBackup implements ModInitializer { public static final String MOD_ID = "textile_backup"; @@ -41,7 +43,6 @@ public class TextileBackup implements ModInitializer { logger.info("Loading TextileBackup by Szum123321"); config = ConfigManager.loadConfig(ConfigHandler.class); - config.backupInterval *= 1000; registerCommands(); } @@ -49,8 +50,10 @@ public class TextileBackup implements ModInitializer { private void registerCommands(){ CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register( LiteralArgumentBuilder.literal("backup") - .then(StartBackupCommand.register()) + .then(BlacklistCommand.register()) .then(CleanupCommand.register()) + .then(StartBackupCommand.register()) + .then(WhitelistCommand.register()) )); } } diff --git a/src/main/java/net/szum123321/textile_backup/commands/BlacklistCommand.java b/src/main/java/net/szum123321/textile_backup/commands/BlacklistCommand.java new file mode 100644 index 0000000..0e827b6 --- /dev/null +++ b/src/main/java/net/szum123321/textile_backup/commands/BlacklistCommand.java @@ -0,0 +1,103 @@ +package net.szum123321.textile_backup.commands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import io.github.cottonmc.cotton.config.ConfigManager; +import net.minecraft.command.arguments.EntityArgumentType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.TranslatableText; +import net.szum123321.textile_backup.TextileBackup; + +public class BlacklistCommand { + public static LiteralArgumentBuilder register(){ + return CommandManager.literal("whitelist") + .requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) || + ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) && + !TextileBackup.config.blacklist.contains(ctx.getName())) + .then(CommandManager.literal("add") + .then(CommandManager.argument("Player", EntityArgumentType.player())) + .executes(BlacklistCommand::executeAdd) + ) + .then(CommandManager.literal("remove") + .then(CommandManager.argument("Player", EntityArgumentType.player())) + .executes(BlacklistCommand::executeRemove) + ) + .then(CommandManager.literal("list") + .executes(ctx -> executeList(ctx.getSource())) + ) + .executes(ctx -> help(ctx.getSource())); + } + + private static int help(ServerCommandSource source){ + source.sendFeedback(new TranslatableText("Available command are: add [player], remove [player], list."), false); + + return 1; + } + + private static int executeList(ServerCommandSource source){ + StringBuilder builder = new StringBuilder(); + + builder.append("Currently on the blacklist are: "); + + for(String name : TextileBackup.config.blacklist){ + builder.append(name); + builder.append(", "); + } + + source.sendFeedback(new TranslatableText(builder.toString()), false); + + return 1; + } + + private static int executeAdd(CommandContext ctx) throws CommandSyntaxException { + PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player"); + + if(TextileBackup.config.blacklist.contains(player.getEntityName())) { + ctx.getSource().sendFeedback(new TranslatableText("Player: {} is already blacklisted.", player.getEntityName()), false); + }else{ + TextileBackup.config.blacklist.add(player.getEntityName()); + ConfigManager.saveConfig(TextileBackup.config); + + StringBuilder builder = new StringBuilder(); + + builder.append("Player: "); + builder.append(player.getEntityName()); + builder.append(" added to the blacklist"); + + if(TextileBackup.config.whitelist.contains(player.getEntityName())){ + TextileBackup.config.whitelist.remove(player.getEntityName()); + builder.append(" and removed form the whitelist"); + } + + builder.append(" successfully."); + + ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false); + } + + return 1; + } + + private static int executeRemove(CommandContext ctx) throws CommandSyntaxException { + PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player"); + + if(!TextileBackup.config.blacklist.contains(player.getEntityName())) { + ctx.getSource().sendFeedback(new TranslatableText("Player: {} newer was blacklisted.", player.getEntityName()), false); + }else{ + TextileBackup.config.blacklist.remove(player.getEntityName()); + ConfigManager.saveConfig(TextileBackup.config); + + StringBuilder builder = new StringBuilder(); + + builder.append("Player: "); + builder.append(player.getEntityName()); + builder.append(" removed from the blacklist successfully."); + + ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false); + } + + return 1; + } +} diff --git a/src/main/java/net/szum123321/textile_backup/commands/CleanupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/CleanupCommand.java index b9ca813..1e9cbeb 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/CleanupCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/CleanupCommand.java @@ -21,17 +21,22 @@ package net.szum123321.textile_backup.commands; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.TranslatableText; +import net.szum123321.textile_backup.TextileBackup; import net.szum123321.textile_backup.core.BackupHelper; public class CleanupCommand { public static LiteralArgumentBuilder register(){ return CommandManager.literal("cleanup") - .requires(ctx -> ctx.hasPermissionLevel(4)) + .requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) || + ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) && + !TextileBackup.config.blacklist.contains(ctx.getName())) .executes(ctx -> execute(ctx.getSource())); } private static int execute(ServerCommandSource source){ - BackupHelper.executeFileLimit(source); + BackupHelper.executeFileLimit(source, source.getMinecraftServer().getLevelName()); + source.sendFeedback(new TranslatableText("Done"), false); return 1; } diff --git a/src/main/java/net/szum123321/textile_backup/commands/StartBackupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/StartBackupCommand.java index 64a710b..06671fe 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/StartBackupCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/StartBackupCommand.java @@ -23,12 +23,15 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import net.szum123321.textile_backup.TextileBackup; import net.szum123321.textile_backup.core.BackupHelper; public class StartBackupCommand { public static LiteralArgumentBuilder register(){ return CommandManager.literal("start") - .requires(ctx -> ctx.hasPermissionLevel(4)) + .requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) || + ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) && + !TextileBackup.config.blacklist.contains(ctx.getName())) .then(CommandManager.argument("Comment", StringArgumentType.word()) .executes(StartBackupCommand::executeWithComment) ).executes(ctx -> execute(ctx.getSource())); @@ -41,7 +44,7 @@ public class StartBackupCommand { } private static int execute(ServerCommandSource source){ - BackupHelper.create(source.getMinecraftServer(), source, true, null); + BackupHelper.create(source.getMinecraftServer(), source,true, null); return 1; } diff --git a/src/main/java/net/szum123321/textile_backup/commands/WhitelistCommand.java b/src/main/java/net/szum123321/textile_backup/commands/WhitelistCommand.java new file mode 100644 index 0000000..da01066 --- /dev/null +++ b/src/main/java/net/szum123321/textile_backup/commands/WhitelistCommand.java @@ -0,0 +1,103 @@ +package net.szum123321.textile_backup.commands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import io.github.cottonmc.cotton.config.ConfigManager; +import net.minecraft.command.arguments.EntityArgumentType; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.TranslatableText; +import net.szum123321.textile_backup.TextileBackup; +import sun.security.krb5.Config; + +public class WhitelistCommand { + public static LiteralArgumentBuilder register(){ + return CommandManager.literal("whitelist") + .requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) || + ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) && + !TextileBackup.config.blacklist.contains(ctx.getName())) + .then(CommandManager.literal("add") + .then(CommandManager.argument("Player", EntityArgumentType.player())) + .executes(WhitelistCommand::executeAdd) + ) + .then(CommandManager.literal("remove") + .then(CommandManager.argument("Player", EntityArgumentType.player())) + .executes(WhitelistCommand::executeRemove) + ) + .then(CommandManager.literal("list") + .executes(ctx -> executeList(ctx.getSource())) + ) + .executes(ctx -> help(ctx.getSource())); + } + + private static int help(ServerCommandSource source){ + source.sendFeedback(new TranslatableText("Available command are: add [player], remove [player], list."), false); + + return 1; + } + + private static int executeList(ServerCommandSource source){ + StringBuilder builder = new StringBuilder(); + + builder.append("Currently on the whitelist are: "); + + for(String name : TextileBackup.config.whitelist){ + builder.append(name); + builder.append(", "); + } + + source.sendFeedback(new TranslatableText(builder.toString()), false); + + return 1; + } + + private static int executeAdd(CommandContext ctx) throws CommandSyntaxException { + PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player"); + + if(TextileBackup.config.whitelist.contains(player.getEntityName())) { + ctx.getSource().sendFeedback(new TranslatableText("Player: {} is already whitelisted.", player.getEntityName()), false); + }else{ + TextileBackup.config.whitelist.add(player.getEntityName()); + ConfigManager.saveConfig(TextileBackup.config); + + StringBuilder builder = new StringBuilder(); + + builder.append("Player: "); + builder.append(player.getEntityName()); + builder.append(" added to the whitelist"); + + if(TextileBackup.config.blacklist.contains(player.getEntityName())){ + TextileBackup.config.blacklist.remove(player.getEntityName()); + builder.append(" and removed form the blacklist"); + } + + builder.append(" successfully."); + + ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false); + } + + return 1; + } + + private static int executeRemove(CommandContext ctx) throws CommandSyntaxException { + PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player"); + + if(!TextileBackup.config.whitelist.contains(player.getEntityName())) { + ctx.getSource().sendFeedback(new TranslatableText("Player: {} newer was on the whitelist.", player.getEntityName()), false); + }else{ + TextileBackup.config.whitelist.remove(player.getEntityName()); + ConfigManager.saveConfig(TextileBackup.config); + StringBuilder builder = new StringBuilder(); + + builder.append("Player: "); + builder.append(player.getEntityName()); + builder.append(" removed from the whitelist successfully."); + + ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false); + } + + return 1; + } +} diff --git a/src/main/java/net/szum123321/textile_backup/core/BackupHelper.java b/src/main/java/net/szum123321/textile_backup/core/BackupHelper.java index a47bb8f..2306a1c 100644 --- a/src/main/java/net/szum123321/textile_backup/core/BackupHelper.java +++ b/src/main/java/net/szum123321/textile_backup/core/BackupHelper.java @@ -74,12 +74,10 @@ public class BackupHelper { MakeBackupThread thread = new MakeBackupThread(server, ctx, comment); thread.start(); - - executeFileLimit(ctx); } - public static void executeFileLimit(ServerCommandSource ctx){ - File root = getBackupRootPath(); + public static void executeFileLimit(ServerCommandSource ctx, String worldName){ + File root = getBackupRootPath(worldName); FileFilter filter = f -> f.getName().endsWith("zip"); @@ -89,11 +87,25 @@ public class BackupHelper { Arrays.stream(root.listFiles()).forEach(f ->{ if(f.exists() && f.isFile()){ - LocalDateTime creationTime = LocalDateTime.from( - getDateTimeFormatter().parse( - f.getName().split(".zip")[0].split("#")[0] - ) - ); + LocalDateTime creationTime; + + try { + creationTime = LocalDateTime.from( + getDateTimeFormatter().parse( + f.getName().split(".zip")[0].split("#")[0] + ) + ); + }catch(Exception e){ + System.out.println(e.getClass()); + System.out.println(e.toString()); + + creationTime = LocalDateTime.from( + getBackupDateTimeFormatter().parse( + f.getName().split(".zip")[0].split("#")[0] + ) + ); + + } if(now.toEpochSecond(ZoneOffset.UTC) - creationTime.toEpochSecond(ZoneOffset.UTC) > TextileBackup.config.maxAge) { log("Deleting: " + f.getName(), ctx); @@ -128,9 +140,12 @@ public class BackupHelper { } } - public static File getBackupRootPath(){ + public static File getBackupRootPath(String worldName){ File path = new File(TextileBackup.config.path); + if(TextileBackup.config.perWorldBackup) + path = path.toPath().resolve(worldName).toFile(); + if(!path.exists()){ try{ path.mkdirs(); @@ -150,8 +165,15 @@ public class BackupHelper { } public static DateTimeFormatter getDateTimeFormatter(){ - String os = System.getProperty("os.name"); - if (os.toLowerCase().startsWith("win")) { + if(TextileBackup.config.dateTimeFormat != null) + return DateTimeFormatter.ofPattern(TextileBackup.config.dateTimeFormat); + else + return getBackupDateTimeFormatter(); + } + + public static DateTimeFormatter getBackupDateTimeFormatter(){ + String os = System.getProperty("os.name"); + if(os.toLowerCase().startsWith("win")){ return DateTimeFormatter.ofPattern("dd.MM.yyyy_HH-mm-ss"); } else { return DateTimeFormatter.ofPattern("dd.MM.yyyy_HH:mm:ss"); diff --git a/src/main/java/net/szum123321/textile_backup/core/MakeBackupThread.java b/src/main/java/net/szum123321/textile_backup/core/MakeBackupThread.java index 25127f5..fa7493e 100644 --- a/src/main/java/net/szum123321/textile_backup/core/MakeBackupThread.java +++ b/src/main/java/net/szum123321/textile_backup/core/MakeBackupThread.java @@ -45,7 +45,7 @@ public class MakeBackupThread extends Thread { .getWorldDir(); File outFile = BackupHelper - .getBackupRootPath() + .getBackupRootPath(server.getLevelName()) .toPath() .resolve(getFileName()) .toFile(); @@ -61,7 +61,7 @@ public class MakeBackupThread extends Thread { Compressor.createArchive(world, outFile, ctx); - BackupHelper.executeFileLimit(ctx); + BackupHelper.executeFileLimit(ctx, server.getLevelName()); BackupHelper.log("Done!", ctx); } diff --git a/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java b/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java index 29aa103..b306f4f 100644 --- a/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java +++ b/src/main/java/net/szum123321/textile_backup/mixin/MinecraftServerMixin.java @@ -38,7 +38,7 @@ public abstract class MinecraftServerMixin { @Inject(method = "tick", at = @At("HEAD")) public void tick(CallbackInfo ci){ - if(timeReference - lastBackup >= TextileBackup.config.backupInterval){ + if(timeReference - lastBackup >= TextileBackup.config.backupInterval * 1000){ if(getPlayerManager().getCurrentPlayerCount() == 0 && !TextileBackup.config.doBackupsOnEmptyServer) return; diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 571ea75..73e90a0 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -4,7 +4,7 @@ "version": "${version}", "name": "Textile Backup", - "description": "Small, configurable, fully server-side backup mod for Fabric", + "description": "Small, configurable, fully server-sided backup mod for Fabric", "authors": [ "Szum123321" ],