From a8f98c460e00ad70e19e094634a4a2cc52c0cec8 Mon Sep 17 00:00:00 2001 From: szymon Date: Sat, 26 Jun 2021 13:10:35 +0200 Subject: [PATCH] Many, many changes: - I migrated from cotton config to Cloth config + Auto config, that way it's easy to provide modmenu integration while keeping backward-compatibility. Because settings might change at runtime it's now necessary to keep the config reference up-to-date. I done this by using wrapper class around ConfigHolder<> and having a reference to main instance in each class accessing the config. - Switched form using one global logger to creating new instance in every class. - Removed BackupScheduler from Statics. Why did I put it there in the first place? --- build.gradle | 25 ++- gradle.properties | 8 + .../textile_backup/ConfigHandler.java | 154 ---------------- .../szum123321/textile_backup/Statics.java | 12 +- .../textile_backup/TextileBackup.java | 40 ++-- .../CustomLogger.java => TextileLogger.java} | 16 +- .../textile_backup/client/ModMenuEntry.java | 13 ++ .../commands/create/CleanupCommand.java | 8 +- .../commands/create/StartBackupCommand.java | 6 +- .../commands/manage/BlacklistCommand.java | 38 ++-- .../commands/manage/DeleteCommand.java | 18 +- .../commands/manage/ListBackupsCommand.java | 7 +- .../commands/manage/WhitelistCommand.java | 38 ++-- .../commands/restore/KillRestoreCommand.java | 9 +- .../restore/RestoreBackupCommand.java | 16 +- .../textile_backup/config/ConfigHelper.java | 32 ++++ .../textile_backup/config/ConfigPOJO.java | 174 ++++++++++++++++++ .../textile_backup/core/Utilities.java | 30 ++- .../core/create/BackupHelper.java | 30 +-- .../core/create/BackupScheduler.java | 12 +- .../core/create/MakeBackupRunnable.java | 32 ++-- .../compressors/AbstractCompressor.java | 29 +-- .../compressors/ParallelZipCompressor.java | 7 +- .../create/compressors/ZipCompressor.java | 7 +- .../core/restore/AwaitThread.java | 8 +- .../core/restore/RestoreBackupRunnable.java | 34 ++-- .../core/restore/RestoreHelper.java | 22 ++- .../decompressors/GenericTarDecompressor.java | 15 +- .../decompressors/ZipDecompressor.java | 15 +- .../assets/textile_backup/lang/en_us.json | 49 +++++ src/main/resources/fabric.mod.json | 3 + 31 files changed, 562 insertions(+), 345 deletions(-) delete mode 100644 src/main/java/net/szum123321/textile_backup/ConfigHandler.java rename src/main/java/net/szum123321/textile_backup/{core/CustomLogger.java => TextileLogger.java} (89%) create mode 100644 src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java create mode 100644 src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java create mode 100644 src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java create mode 100644 src/main/resources/assets/textile_backup/lang/en_us.json diff --git a/build.gradle b/build.gradle index 42ed2c0..e2418ff 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,13 @@ minecraft { repositories{ maven { url 'https://server.bbkr.space/artifactory/libs-release' } maven { url 'https://jitpack.io' } + maven { url "https://maven.shedaniel.me/" } + maven { + url "https://maven.terraformersmc.com/releases/" + content { + includeGroup "com.terraformersmc" + } + } } dependencies { @@ -27,20 +34,30 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - modImplementation "io.github.cottonmc.cotton:cotton-config:1.0.0-rc.7" + //General config library + modApi("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_version}") { + exclude(group: "net.fabricmc.fabric-api") + } - include "io.github.cottonmc:Jankson-Fabric:3.0.0+j1.2.0" - include "io.github.cottonmc.cotton:cotton-logging:1.0.0-rc.4" - include "io.github.cottonmc.cotton:cotton-config:1.0.0-rc.7" + //Mod menu + modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") + //General compression library modImplementation "org.apache.commons:commons-compress:1.19" include "org.apache.commons:commons-compress:1.19" + //LZMA support modImplementation "org.tukaani:xz:1.8" include "org.tukaani:xz:1.8" + //Gzip compression, parallel, GITHUB modImplementation 'com.github.shevek:parallelgzip:master-SNAPSHOT' include 'com.github.shevek:parallelgzip:master-SNAPSHOT' + + // Lazy DFU makes the dev env start up much faster by loading DataFixerUpper lazily, which would otherwise take a long time. We rarely need it anyway. + modRuntime("com.github.astei:lazydfu:${project.lazydfu_version}") { + exclude(module: "fabric-loader") + } } processResources { diff --git a/gradle.properties b/gradle.properties index dc60769..0aa132a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,14 @@ loader_version=0.11.5 #Fabric api fabric_version=0.35.1+1.17 +#Cloth Config +cloth_version=5.0.34 + +#ModMenu +modmenu_version=2.0.2 + +lazydfu_version=0.1.2 + # Mod Properties mod_version = 2.1.0 maven_group = net.szum123321 diff --git a/src/main/java/net/szum123321/textile_backup/ConfigHandler.java b/src/main/java/net/szum123321/textile_backup/ConfigHandler.java deleted file mode 100644 index 6ef018d..0000000 --- a/src/main/java/net/szum123321/textile_backup/ConfigHandler.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - A simple backup mod for Fabric - Copyright (C) 2020 Szum123321 - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package net.szum123321.textile_backup; - -import blue.endless.jankson.Comment; -import io.github.cottonmc.cotton.config.annotations.ConfigFile; - -import java.io.File; -import java.time.format.DateTimeFormatter; -import java.util.*; - -@ConfigFile(name = Statics.MOD_ID) -public class ConfigHandler { - @Comment("\nTime between automatic backups in seconds\n" + - "When set to 0 backups will not be performed automatically\n") - public long backupInterval = 3600; - - @Comment("\nDelay in seconds between typing-in /backup restore and it actually starting\n") - public int restoreDelay = 30; - - @Comment("\nShould backups be done even if there are no players?\n") - public boolean doBackupsOnEmptyServer = false; - - @Comment("\nShould backup be made on server shutdown?\n") - public boolean shutdownBackup = true; - - @Comment("\nShould world be backed up before restoring a backup?\n") - public boolean backupOldWorlds = true; - - @Comment("\nShould every world have its own backup folder?\n") - public boolean perWorldBackup = true; - - @Comment("\nA path to the backup folder\n") - public String path = "backup/"; - - @Comment("\nThis setting allows you to exclude files form being backedup.\n"+ - "Be very careful when setting it, as it is easy corrupt your world!\n") - public List fileBlacklist = new ArrayList<>(); - - @Comment("\nShould backups be deleted after being restored?\n") - public boolean deleteOldBackupAfterRestore = true; - - @Comment("\nMaximum number of backups to keep. If set to 0 then no backup will be deleted based their amount\n") - public int backupsToKeep = 10; - - @Comment("\nMaximum age of backups to keep in seconds.\n If set to 0 then backups will not be deleted based their age \n") - public long maxAge = 0; - - @Comment("\nMaximum size of backup folder in kilo bytes (1024).\n" + - "If set to 0 then backups will not be deleted\n") - public int maxSize = 0; - - @Comment("\nCompression level \n0 - 9\n Only affects zip compression.\n") - public int compression = 7; - - @Comment("\nLimit how many cores can be used for compression.\n" + - "0 means that all available cores will be used\n") - public int compressionCoreCountLimit = 0; - - @Comment(value = "\nAvailable formats are:\n" + - "ZIP - normal zip archive using standard deflate compression\n" + - "GZIP - tar.gz using gzip compression\n" + - "BZIP2 - tar.bz2 archive using bzip2 compression\n" + - "LZMA - tar.xz using lzma compression\n" + - "TAR - .tar with no compression\n") - public ArchiveFormat format = ArchiveFormat.ZIP; - - @Comment("\nMinimal permission level required to run commands\n") - public int permissionLevel = 4; - - @Comment("\nPlayer on singleplayer is always allowed to run command. Warning! On lan party everyone will be allowed to run it.\n") - public boolean alwaysSingleplayerAllowed = true; - - @Comment("\nPlayers allowed to run backup commands without sufficient permission level\n") - public Set playerWhitelist = new HashSet<>(); - - @Comment("\nPlayers banned from running backup commands besides their sufficient permission level\n") - public Set playerBlacklist = new HashSet<>(); - - @Comment("\nFormat of date&time used to name backup files.\n" + - "Remember not to use '#' symbol or any other character that is not allowed by your operating system such as:\n" + - "':', '\\', etc...\n" + - "For more info: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html\n") - public String dateTimeFormat = "yyyy.MM.dd_HH-mm-ss"; - - public Optional sanitize() { - if(compressionCoreCountLimit > Runtime.getRuntime().availableProcessors()) - return Optional.of("compressionCoreCountLimit is too big! Your system only has: " + Runtime.getRuntime().availableProcessors() + " cores!"); - - try { - DateTimeFormatter.ofPattern(dateTimeFormat); - } catch (IllegalArgumentException e) { - return Optional.of("dateTimeFormat is wrong!\n" + e.getMessage() + "\n See: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html"); - } - - File path = new File(Statics.CONFIG.path).getAbsoluteFile(); - - if (!path.exists()) { - try { - path.mkdirs(); - } catch (Exception e) { - return Optional.of("Something went wrong while creating backup folder!\n" + e.getMessage()); - } - } - - return Optional.empty(); - } - - public enum ArchiveFormat { - ZIP("zip"), - GZIP("tar", "gz"), - BZIP2("tar", "bz2"), - LZMA("tar", "xz"), - TAR("tar"); - - private final List extensionPieces; - - ArchiveFormat(String... extensionParts) { - extensionPieces = Arrays.asList(extensionParts); - } - - public String getCompleteString() { - StringBuilder builder = new StringBuilder(); - - extensionPieces.forEach(s -> builder.append('.').append(s)); - - return builder.toString(); - } - - boolean isMultipart() { - return extensionPieces.size() > 1; - } - - public String getLastPiece() { - return extensionPieces.get(extensionPieces.size() - 1); - } - } -} diff --git a/src/main/java/net/szum123321/textile_backup/Statics.java b/src/main/java/net/szum123321/textile_backup/Statics.java index 1cfd42a..3cf6d90 100644 --- a/src/main/java/net/szum123321/textile_backup/Statics.java +++ b/src/main/java/net/szum123321/textile_backup/Statics.java @@ -18,8 +18,6 @@ package net.szum123321.textile_backup; -import net.szum123321.textile_backup.core.CustomLogger; -import net.szum123321.textile_backup.core.create.BackupScheduler; import net.szum123321.textile_backup.core.restore.AwaitThread; import java.io.File; @@ -30,16 +28,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; public class Statics { - public static final String MOD_ID = "textile_backup"; - public static final String MOD_NAME = "Textile Backup"; - public static final CustomLogger LOGGER = new CustomLogger(MOD_ID, MOD_NAME); - public static ConfigHandler CONFIG; - - public static final BackupScheduler scheduler = new BackupScheduler(); - public static ExecutorService executorService = Executors.newSingleThreadExecutor(); - public final static DateTimeFormatter defaultDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss"); + public static ExecutorService executorService = Executors.newSingleThreadExecutor(); + public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true); public static boolean disableWatchdog = false; public static AwaitThread restoreAwaitThread = null; diff --git a/src/main/java/net/szum123321/textile_backup/TextileBackup.java b/src/main/java/net/szum123321/textile_backup/TextileBackup.java index e2ffd7b..a638dda 100644 --- a/src/main/java/net/szum123321/textile_backup/TextileBackup.java +++ b/src/main/java/net/szum123321/textile_backup/TextileBackup.java @@ -19,8 +19,9 @@ package net.szum123321.textile_backup; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import io.github.cottonmc.cotton.config.ConfigManager; +import me.shedaniel.autoconfig.AutoConfig; +import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -34,40 +35,41 @@ import net.szum123321.textile_backup.commands.manage.WhitelistCommand; import net.szum123321.textile_backup.commands.restore.KillRestoreCommand; import net.szum123321.textile_backup.commands.manage.ListBackupsCommand; import net.szum123321.textile_backup.commands.restore.RestoreBackupCommand; +import net.szum123321.textile_backup.config.ConfigHelper; +import net.szum123321.textile_backup.config.ConfigPOJO; import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.Utilities; import net.szum123321.textile_backup.core.create.BackupContext; import net.szum123321.textile_backup.core.create.BackupHelper; +import net.szum123321.textile_backup.core.create.BackupScheduler; -import java.util.Optional; import java.util.concurrent.Executors; public class TextileBackup implements ModInitializer { + public static final String MOD_NAME = "Textile Backup"; + public static final String MOD_ID = "textile_backup"; + + private final static TextileLogger log = new TextileLogger(MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + @Override public void onInitialize() { - Statics.LOGGER.info("Starting Textile Backup by Szum123321."); + log.info("Starting Textile Backup by Szum123321"); - Statics.CONFIG = ConfigManager.loadConfig(ConfigHandler.class); - Optional errorMessage = Statics.CONFIG.sanitize(); - - if(errorMessage.isPresent()) { - Statics.LOGGER.fatal("TextileBackup config file has wrong settings!\n{}", errorMessage.get()); - System.exit(1); - } + ConfigHelper.updateInstance(AutoConfig.register(ConfigPOJO.class, JanksonConfigSerializer::new)); //TODO: finish writing wiki - if(Statics.CONFIG.format == ConfigHandler.ArchiveFormat.ZIP) { + if(config.get().format == ConfigPOJO.ArchiveFormat.ZIP) { Statics.tmpAvailable = Utilities.isTmpAvailable(); if(!Statics.tmpAvailable) { - Statics.LOGGER.warn(""" + log.warn(""" WARNING! It seems like the temporary folder is not accessible on this system! This will cause problems with multithreaded zip compression, so a normal one will be used instead. For more info please read: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems"""); } } - if(Statics.CONFIG.backupInterval > 0) - ServerTickEvents.END_SERVER_TICK.register(Statics.scheduler::tick); + ServerTickEvents.END_SERVER_TICK.register(new BackupScheduler()::tick); //Restart Executor Service in singleplayer ServerLifecycleEvents.SERVER_STARTING.register(ignored -> { @@ -77,7 +79,7 @@ public class TextileBackup implements ModInitializer { ServerLifecycleEvents.SERVER_STOPPED.register(server -> { Statics.executorService.shutdown(); - if (Statics.CONFIG.shutdownBackup && Statics.globalShutdownBackupFlag.get()) { + if (config.get().shutdownBackup && Statics.globalShutdownBackupFlag.get()) { BackupHelper.create( BackupContext.Builder .newBackupContextBuilder() @@ -93,11 +95,11 @@ public class TextileBackup implements ModInitializer { LiteralArgumentBuilder.literal("backup") .requires((ctx) -> { try { - return ((Statics.CONFIG.playerWhitelist.contains(ctx.getEntityOrThrow().getEntityName()) || - ctx.hasPermissionLevel(Statics.CONFIG.permissionLevel)) && - !Statics.CONFIG.playerBlacklist.contains(ctx.getEntityOrThrow().getEntityName())) || + return ((config.get().playerWhitelist.contains(ctx.getEntityOrThrow().getEntityName()) || + ctx.hasPermissionLevel(config.get().permissionLevel)) && + !config.get().playerBlacklist.contains(ctx.getEntityOrThrow().getEntityName())) || (ctx.getMinecraftServer().isSinglePlayer() && - Statics.CONFIG.alwaysSingleplayerAllowed); + config.get().alwaysSingleplayerAllowed); } catch (Exception ignored) { //Command was called from server console. return true; } diff --git a/src/main/java/net/szum123321/textile_backup/core/CustomLogger.java b/src/main/java/net/szum123321/textile_backup/TextileLogger.java similarity index 89% rename from src/main/java/net/szum123321/textile_backup/core/CustomLogger.java rename to src/main/java/net/szum123321/textile_backup/TextileLogger.java index b40fea2..9036e41 100644 --- a/src/main/java/net/szum123321/textile_backup/core/CustomLogger.java +++ b/src/main/java/net/szum123321/textile_backup/TextileLogger.java @@ -1,6 +1,6 @@ /* * A simple backup mod for Fabric - * Copyright (C) 2020 Szum123321 + * Copyright (C) 2021 Szum123321 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package net.szum123321.textile_backup.core; +package net.szum123321.textile_backup; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.ServerCommandSource; @@ -29,11 +29,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.MessageFactory; import org.apache.logging.log4j.message.ParameterizedMessageFactory; +import org.apache.logging.log4j.util.StackLocatorUtil; /* This is practically just a copy-pate of Cotton's ModLogger with a few changes */ -public class CustomLogger { +public class TextileLogger { //private final boolean isDev = FabricLoader.getInstance().isDevelopmentEnvironment(); private final MessageFactory messageFactory; @@ -42,12 +43,19 @@ public class CustomLogger { private final String prefix; private final MutableText prefixText; - public CustomLogger(String name, String prefix) { +/* public TextileLogger(String name, String prefix) { this.messageFactory = ParameterizedMessageFactory.INSTANCE; this.logger = LogManager.getLogger(name, messageFactory); this.prefix = "[" + prefix + "]" + " "; this.prefixText = new LiteralText(this.prefix).styled(style -> style.withColor(0x5B23DA)); } +*/ + public TextileLogger(String prefix) { + this.messageFactory = ParameterizedMessageFactory.INSTANCE; + this.logger = LogManager.getLogger(StackLocatorUtil.getCallerClass(2), messageFactory); + this.prefix = "[" + prefix + "]" + " "; + this.prefixText = new LiteralText(this.prefix).styled(style -> style.withColor(0x5B23DA)); + } public MutableText getPrefixText() { return prefixText.shallowCopy(); diff --git a/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java b/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java new file mode 100644 index 0000000..e3cc4e2 --- /dev/null +++ b/src/main/java/net/szum123321/textile_backup/client/ModMenuEntry.java @@ -0,0 +1,13 @@ +package net.szum123321.textile_backup.client; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import me.shedaniel.autoconfig.AutoConfig; +import net.szum123321.textile_backup.config.ConfigPOJO; + +public class ModMenuEntry implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> AutoConfig.getConfigScreen(ConfigPOJO.class, parent).get(); + } +} diff --git a/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java index d76ddaf..5e44864 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/create/CleanupCommand.java @@ -21,19 +21,21 @@ package net.szum123321.textile_backup.commands.create; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.LiteralText; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.create.BackupHelper; import net.szum123321.textile_backup.core.Utilities; public class CleanupCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static LiteralArgumentBuilder register() { return CommandManager.literal("cleanup") .executes(ctx -> execute(ctx.getSource())); } private static int execute(ServerCommandSource source) { - Statics.LOGGER.sendInfo( + log.sendInfo( source, "Deleted: {} files.", BackupHelper.executeFileLimit(source, Utilities.getLevelName(source.getMinecraftServer())) diff --git a/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java index bf24c41..f7cafe0 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/create/StartBackupCommand.java @@ -23,12 +23,16 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.create.BackupContext; import net.szum123321.textile_backup.core.create.BackupHelper; import javax.annotation.Nullable; public class StartBackupCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static LiteralArgumentBuilder register() { return CommandManager.literal("start") .then(CommandManager.argument("comment", StringArgumentType.string()) @@ -51,7 +55,7 @@ public class StartBackupCommand { ) ); } catch (Exception e) { - Statics.LOGGER.error("Something went wrong while executing command!", e); + log.error("Something went wrong while executing command!", e); throw e; } } diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java index b8960b3..67e237c 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/manage/BlacklistCommand.java @@ -3,14 +3,18 @@ package net.szum123321.textile_backup.commands.manage; 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.argument.EntityArgumentType; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; +import net.szum123321.textile_backup.config.ConfigHelper; public class BlacklistCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + public static LiteralArgumentBuilder register() { return CommandManager.literal("blacklist") .then(CommandManager.literal("add") @@ -27,7 +31,7 @@ public class BlacklistCommand { } private static int help(ServerCommandSource source) { - Statics.LOGGER.sendInfo(source, "Available command are: add [player], remove [player], list."); + log.sendInfo(source, "Available command are: add [player], remove [player], list."); return 1; } @@ -37,12 +41,12 @@ public class BlacklistCommand { builder.append("Currently on the blacklist are: "); - for(String name : Statics.CONFIG.playerBlacklist){ + for(String name : config.get().playerBlacklist){ builder.append(name); builder.append(", "); } - Statics.LOGGER.sendInfo(source, builder.toString()); + log.sendInfo(source, builder.toString()); return 1; } @@ -50,11 +54,11 @@ public class BlacklistCommand { private static int executeAdd(CommandContext ctx) throws CommandSyntaxException { ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player"); - if(Statics.CONFIG.playerBlacklist.contains(player.getEntityName())) { - Statics.LOGGER.sendInfo(ctx.getSource(), "Player: {} is already blacklisted.", player.getEntityName()); + if(config.get().playerBlacklist.contains(player.getEntityName())) { + log.sendInfo(ctx.getSource(), "Player: {} is already blacklisted.", player.getEntityName()); } else { - Statics.CONFIG.playerBlacklist.add(player.getEntityName()); - ConfigManager.saveConfig(Statics.CONFIG); + config.get().playerBlacklist.add(player.getEntityName()); + config.save(); StringBuilder builder = new StringBuilder(); @@ -62,8 +66,8 @@ public class BlacklistCommand { builder.append(player.getEntityName()); builder.append(" added to the blacklist"); - if(Statics.CONFIG.playerWhitelist.contains(player.getEntityName())){ - Statics.CONFIG.playerWhitelist.remove(player.getEntityName()); + if(config.get().playerWhitelist.contains(player.getEntityName())){ + config.get().playerWhitelist.remove(player.getEntityName()); builder.append(" and removed form the whitelist"); } @@ -71,7 +75,7 @@ public class BlacklistCommand { ctx.getSource().getMinecraftServer().getCommandManager().sendCommandTree(player); - Statics.LOGGER.sendInfo(ctx.getSource(), builder.toString()); + log.sendInfo(ctx.getSource(), builder.toString()); } return 1; @@ -80,15 +84,15 @@ public class BlacklistCommand { private static int executeRemove(CommandContext ctx) throws CommandSyntaxException { ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player"); - if(!Statics.CONFIG.playerBlacklist.contains(player.getEntityName())) { - Statics.LOGGER.sendInfo(ctx.getSource(), "Player: {} newer was blacklisted.", player.getEntityName()); + if(!config.get().playerBlacklist.contains(player.getEntityName())) { + log.sendInfo(ctx.getSource(), "Player: {} newer was blacklisted.", player.getEntityName()); } else { - Statics.CONFIG.playerBlacklist.remove(player.getEntityName()); - ConfigManager.saveConfig(Statics.CONFIG); + config.get().playerBlacklist.remove(player.getEntityName()); + config.save(); ctx.getSource().getMinecraftServer().getCommandManager().sendCommandTree(player); - Statics.LOGGER.sendInfo(ctx.getSource(), "Player: {} removed from the blacklist successfully.", player.getEntityName()); + log.sendInfo(ctx.getSource(), "Player: {} removed from the blacklist successfully.", player.getEntityName()); } return 1; diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java index d98db4c..d4a6929 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/manage/DeleteCommand.java @@ -24,6 +24,8 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.commands.CommandExceptions; import net.szum123321.textile_backup.Statics; import net.szum123321.textile_backup.commands.FileSuggestionProvider; @@ -36,6 +38,8 @@ import java.util.Arrays; import java.util.Optional; public class DeleteCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static LiteralArgumentBuilder register() { return CommandManager.literal("delete") .then(CommandManager.argument("file", StringArgumentType.word()) @@ -63,20 +67,20 @@ public class DeleteCommand { if(optionalFile.isPresent()) { if(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(optionalFile.get())) { if(optionalFile.get().delete()) { - Statics.LOGGER.sendInfo(source, "File {} successfully deleted!", optionalFile.get().getName()); + log.sendInfo(source, "File {} successfully deleted!", optionalFile.get().getName()); if(source.getEntity() instanceof PlayerEntity) - Statics.LOGGER.info("Player {} deleted {}.", source.getPlayer().getName(), optionalFile.get().getName()); + log.info("Player {} deleted {}.", source.getPlayer().getName(), optionalFile.get().getName()); } else { - Statics.LOGGER.sendError(source, "Something went wrong while deleting file!"); + log.sendError(source, "Something went wrong while deleting file!"); } } else { - Statics.LOGGER.sendError(source, "Couldn't delete the file because it's being restored right now."); - Statics.LOGGER.sendHint(source, "If you want to abort restoration then use: /backup killR"); + log.sendError(source, "Couldn't delete the file because it's being restored right now."); + log.sendHint(source, "If you want to abort restoration then use: /backup killR"); } } else { - Statics.LOGGER.sendError(source, "Couldn't find file by this name."); - Statics.LOGGER.sendHint(source, "Maybe try /backup list"); + log.sendError(source, "Couldn't find file by this name."); + log.sendHint(source, "Maybe try /backup list"); } return 0; diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java index 2deb6d8..7d609ca 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/manage/ListBackupsCommand.java @@ -21,12 +21,15 @@ package net.szum123321.textile_backup.commands.manage; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.restore.RestoreHelper; import java.util.*; public class ListBackupsCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static LiteralArgumentBuilder register() { return CommandManager.literal("list") .executes(ctx -> { StringBuilder builder = new StringBuilder(); @@ -50,7 +53,7 @@ public class ListBackupsCommand { } } - Statics.LOGGER.sendInfo(ctx.getSource(), builder.toString()); + log.sendInfo(ctx.getSource(), builder.toString()); return 1; }); diff --git a/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java b/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java index 8f5cc8f..d7ac8d1 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/manage/WhitelistCommand.java @@ -3,14 +3,18 @@ package net.szum123321.textile_backup.commands.manage; 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.argument.EntityArgumentType; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; +import net.szum123321.textile_backup.config.ConfigHelper; public class WhitelistCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + public static LiteralArgumentBuilder register(){ return CommandManager.literal("whitelist") .then(CommandManager.literal("add") @@ -27,7 +31,7 @@ public class WhitelistCommand { } private static int help(ServerCommandSource source){ - Statics.LOGGER.sendInfo(source, "Available command are: add [player], remove [player], list."); + log.sendInfo(source, "Available command are: add [player], remove [player], list."); return 1; } @@ -37,12 +41,12 @@ public class WhitelistCommand { builder.append("Currently on the whitelist are: "); - for(String name : Statics.CONFIG.playerWhitelist){ + for(String name : config.get().playerWhitelist){ builder.append(name); builder.append(", "); } - Statics.LOGGER.sendInfo(source, builder.toString()); + log.sendInfo(source, builder.toString()); return 1; } @@ -50,11 +54,11 @@ public class WhitelistCommand { private static int executeAdd(CommandContext ctx) throws CommandSyntaxException { ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player"); - if(Statics.CONFIG.playerWhitelist.contains(player.getEntityName())) { - Statics.LOGGER.sendInfo(ctx.getSource(), "Player: {} is already whitelisted.", player.getEntityName()); + if(config.get().playerWhitelist.contains(player.getEntityName())) { + log.sendInfo(ctx.getSource(), "Player: {} is already whitelisted.", player.getEntityName()); } else { - Statics.CONFIG.playerWhitelist.add(player.getEntityName()); - ConfigManager.saveConfig(Statics.CONFIG); + config.get().playerWhitelist.add(player.getEntityName()); + config.save(); StringBuilder builder = new StringBuilder(); @@ -62,8 +66,8 @@ public class WhitelistCommand { builder.append(player.getEntityName()); builder.append(" added to the whitelist"); - if(Statics.CONFIG.playerBlacklist.contains(player.getEntityName())){ - Statics.CONFIG.playerBlacklist.remove(player.getEntityName()); + if(config.get().playerBlacklist.contains(player.getEntityName())){ + config.get().playerBlacklist.remove(player.getEntityName()); builder.append(" and removed form the blacklist"); } @@ -71,7 +75,7 @@ public class WhitelistCommand { ctx.getSource().getMinecraftServer().getCommandManager().sendCommandTree(player); - Statics.LOGGER.sendInfo(ctx.getSource(), builder.toString()); + log.sendInfo(ctx.getSource(), builder.toString()); } return 1; @@ -80,15 +84,15 @@ public class WhitelistCommand { private static int executeRemove(CommandContext ctx) throws CommandSyntaxException { ServerPlayerEntity player = EntityArgumentType.getPlayer(ctx, "player"); - if(!Statics.CONFIG.playerWhitelist.contains(player.getEntityName())) { - Statics.LOGGER.sendInfo(ctx.getSource(), "Player: {} newer was whitelisted.", player.getEntityName()); + if(!config.get().playerWhitelist.contains(player.getEntityName())) { + log.sendInfo(ctx.getSource(), "Player: {} newer was whitelisted.", player.getEntityName()); } else { - Statics.CONFIG.playerWhitelist.remove(player.getEntityName()); - ConfigManager.saveConfig(Statics.CONFIG); + config.get().playerWhitelist.remove(player.getEntityName()); + config.save(); ctx.getSource().getMinecraftServer().getCommandManager().sendCommandTree(player); - Statics.LOGGER.sendInfo(ctx.getSource(), "Player: {} removed from the whitelist successfully.", player.getEntityName()); + log.sendInfo(ctx.getSource(), "Player: {} removed from the whitelist successfully.", player.getEntityName()); } return 1; diff --git a/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java b/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java index c6a474d..14ce9bd 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/restore/KillRestoreCommand.java @@ -23,10 +23,13 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import java.util.Optional; public class KillRestoreCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); public static LiteralArgumentBuilder register() { return CommandManager.literal("killR") .executes(ctx -> { @@ -35,16 +38,16 @@ public class KillRestoreCommand { Statics.globalShutdownBackupFlag.set(true); Statics.untouchableFile = Optional.empty(); - Statics.LOGGER.info("{} cancelled backup restoration.", ctx.getSource().getEntity() instanceof PlayerEntity ? + log.info("{} cancelled backup restoration.", ctx.getSource().getEntity() instanceof PlayerEntity ? "Player: " + ctx.getSource().getName() : "SERVER" ); if(ctx.getSource().getEntity() instanceof PlayerEntity) - Statics.LOGGER.sendInfo(ctx.getSource(), "Backup restoration successfully stopped."); + log.sendInfo(ctx.getSource(), "Backup restoration successfully stopped."); } else { - Statics.LOGGER.sendInfo(ctx.getSource(), "Failed to stop backup restoration"); + log.sendInfo(ctx.getSource(), "Failed to stop backup restoration"); } return 1; }); diff --git a/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java b/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java index ad516f3..beba511 100644 --- a/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java +++ b/src/main/java/net/szum123321/textile_backup/commands/restore/RestoreBackupCommand.java @@ -24,6 +24,8 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.commands.CommandExceptions; import net.szum123321.textile_backup.Statics; import net.szum123321.textile_backup.commands.FileSuggestionProvider; @@ -36,6 +38,8 @@ import java.time.format.DateTimeParseException; import java.util.Optional; public class RestoreBackupCommand { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static LiteralArgumentBuilder register() { return CommandManager.literal("restore") .then(CommandManager.argument("file", StringArgumentType.word()) @@ -57,9 +61,9 @@ public class RestoreBackupCommand { ).executes(context -> { ServerCommandSource source = context.getSource(); - Statics.LOGGER.sendInfo(source, "To restore given backup you have to provide exact creation time in format:"); - Statics.LOGGER.sendInfo(source, "[YEAR]-[MONTH]-[DAY]_[HOUR].[MINUTE].[SECOND]"); - Statics.LOGGER.sendInfo(source, "Example: /backup restore 2020-08-05_10.58.33"); + log.sendInfo(source, "To restore given backup you have to provide exact creation time in format:"); + log.sendInfo(source, "[YEAR]-[MONTH]-[DAY]_[HOUR].[MINUTE].[SECOND]"); + log.sendInfo(source, "Example: /backup restore 2020-08-05_10.58.33"); return 1; }); @@ -78,9 +82,9 @@ public class RestoreBackupCommand { Optional backupFile = RestoreHelper.findFileAndLockIfPresent(dateTime, source.getMinecraftServer()); if(backupFile.isPresent()) { - Statics.LOGGER.info("Found file to restore {}", backupFile.get().getFile().getName()); + log.info("Found file to restore {}", backupFile.get().getFile().getName()); } else { - Statics.LOGGER.sendInfo(source, "No file created on {} was found!", dateTime.format(Statics.defaultDateTimeFormatter)); + log.sendInfo(source, "No file created on {} was found!", dateTime.format(Statics.defaultDateTimeFormatter)); return 0; } @@ -97,7 +101,7 @@ public class RestoreBackupCommand { return 1; } else { - Statics.LOGGER.sendInfo(source, "Someone has already started another restoration."); + log.sendInfo(source, "Someone has already started another restoration."); return 0; } diff --git a/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java b/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java new file mode 100644 index 0000000..07332bd --- /dev/null +++ b/src/main/java/net/szum123321/textile_backup/config/ConfigHelper.java @@ -0,0 +1,32 @@ +/* + * A simple backup mod for Fabric + * Copyright (C) 2021 Szum123321 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.szum123321.textile_backup.config; + +import me.shedaniel.autoconfig.ConfigHolder; + +public class ConfigHelper { + public static final ConfigHelper INSTANCE = new ConfigHelper(); + private ConfigHolder configHolder; + + public static void updateInstance(ConfigHolder ch) { INSTANCE.configHolder = ch; } + + public ConfigPOJO get() { return configHolder.get(); } + + public void save() { configHolder.save(); } +} diff --git a/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java b/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java new file mode 100644 index 0000000..2447156 --- /dev/null +++ b/src/main/java/net/szum123321/textile_backup/config/ConfigPOJO.java @@ -0,0 +1,174 @@ +/* + * A simple backup mod for Fabric + * Copyright (C) 2021 Szum123321 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.szum123321.textile_backup.config; + +import me.shedaniel.autoconfig.ConfigData; +import me.shedaniel.autoconfig.annotation.Config; +import me.shedaniel.autoconfig.annotation.ConfigEntry; +import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment; +import net.szum123321.textile_backup.TextileBackup; + +import java.time.format.DateTimeFormatter; +import java.util.*; + +@Config(name = TextileBackup.MOD_ID) +public class ConfigPOJO implements ConfigData { + @Comment(""" + Format of date&time used to name backup files. + Remember not to use '#' symbol or any other character that is not allowed by your operating system such as: + ':', '\\', etc... + For more info: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html""") + public String dateTimeFormat = "yyyy.MM.dd_HH-mm-ss"; + + @Comment("Should every world have its own backup folder?") + @ConfigEntry.Gui.Excluded + public boolean perWorldBackup = true; + + @Comment("A path to the backup folder") + public String path = "backup/"; + + @Comment(""" + This setting allows you to exclude files form being backed-up. + Be very careful when setting it, as it is easy corrupt your world!""") + public List fileBlacklist = new ArrayList<>(); + + @Comment("Should backups be deleted after being restored?") + public boolean deleteOldBackupAfterRestore = true; + + @Comment("Maximum number of backups to keep.\nIf set to 0 then no backup will be deleted based their amount") + public int backupsToKeep = 10; + + @Comment(""" + Maximum age of backups to keep in seconds. + If set to 0 then backups will not be deleted based their age""") + public long maxAge = 0; + + @Comment(""" + Maximum size of backup folder in kilo bytes (1024). + If set to 0 then backups will not be deleted""") + public int maxSize = 0; + + @Comment(""" + Time between automatic backups in seconds + When set to 0 backups will not be performed automatically""") + @ConfigEntry.Gui.Tooltip() + @ConfigEntry.Category("Create") + public long backupInterval = 3600; + + @Comment("Should backups be done even if there are no players?") + @ConfigEntry.Category("Create") + public boolean doBackupsOnEmptyServer = false; + + @Comment("Should backup be made on server shutdown?") + @ConfigEntry.Category("Create") + public boolean shutdownBackup = true; + + @Comment("Should world be backed up before restoring a backup?") + @ConfigEntry.Category("Create") + public boolean backupOldWorlds = true; + + @Comment("Compression level 0 - 9 Only affects zip compression.") + @ConfigEntry.BoundedDiscrete(max = 9) + @ConfigEntry.Category("Create") + public int compression = 7; + + @Comment(""" + Limit how many cores can be used for compression. + 0 means that all available cores will be used""") + @ConfigEntry.Category("Create") + public int compressionCoreCountLimit = 0; + + @Comment(value = """ + Available formats are: + ZIP - normal zip archive using standard deflate compression + GZIP - tar.gz using gzip compression + BZIP2 - tar.bz2 archive using bzip2 compression + LZMA - tar.xz using lzma compression + TAR - .tar with no compression""") + @ConfigEntry.Category("Create") + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) + public ArchiveFormat format = ArchiveFormat.ZIP; + + @Comment("Minimal permission level required to run commands") + @ConfigEntry.Category("Manage") + public int permissionLevel = 4; + + @Comment(""" + Player on singleplayer is always allowed to run command. + Warning! On lan party everyone will be allowed to run it.""") + @ConfigEntry.Category("Manage") + public boolean alwaysSingleplayerAllowed = true; + + @Comment("Players allowed to run backup commands without sufficient permission level") + @ConfigEntry.Category("Manage") + public Set playerWhitelist = new HashSet<>(); + + @Comment("Players banned from running backup commands besides their sufficient permission level") + @ConfigEntry.Category("Manage") + public Set playerBlacklist = new HashSet<>(); + + @Comment("Delay in seconds between typing-in /backup restore and it actually starting") + @ConfigEntry.Category("Restore") + public int restoreDelay = 30; + + @Override + public void validatePostLoad() throws ValidationException { + if(compressionCoreCountLimit > Runtime.getRuntime().availableProcessors()) + throw new ValidationException("compressionCoreCountLimit is too high! Your system only has: " + Runtime.getRuntime().availableProcessors() + " cores!"); + + try { + DateTimeFormatter.ofPattern(dateTimeFormat); + } catch (IllegalArgumentException e) { + throw new ValidationException( + "dateTimeFormat is wrong! See: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html", + e + ); + } + } + + public enum ArchiveFormat { + ZIP("zip"), + GZIP("tar", "gz"), + BZIP2("tar", "bz2"), + LZMA("tar", "xz"), + TAR("tar"); + + private final List extensionPieces; + + ArchiveFormat(String... extensionParts) { + extensionPieces = Arrays.asList(extensionParts); + } + + public String getCompleteString() { + StringBuilder builder = new StringBuilder(); + + extensionPieces.forEach(s -> builder.append('.').append(s)); + + return builder.toString(); + } + + boolean isMultipart() { + return extensionPieces.size() > 1; + } + + public String getLastPiece() { + return extensionPieces.get(extensionPieces.size() - 1); + } + } +} diff --git a/src/main/java/net/szum123321/textile_backup/core/Utilities.java b/src/main/java/net/szum123321/textile_backup/core/Utilities.java index e85ecae..68ba125 100644 --- a/src/main/java/net/szum123321/textile_backup/core/Utilities.java +++ b/src/main/java/net/szum123321/textile_backup/core/Utilities.java @@ -21,7 +21,8 @@ package net.szum123321.textile_backup.core; import net.minecraft.server.MinecraftServer; import net.minecraft.server.world.ServerWorld; import net.minecraft.world.World; -import net.szum123321.textile_backup.ConfigHandler; +import net.szum123321.textile_backup.config.ConfigHelper; +import net.szum123321.textile_backup.config.ConfigPOJO; import net.szum123321.textile_backup.Statics; import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor; @@ -36,6 +37,8 @@ import java.util.Arrays; import java.util.Optional; public class Utilities { + private final static ConfigHelper config = ConfigHelper.INSTANCE; + public static String getLevelName(MinecraftServer server) { return ((MinecraftServerSessionAccessor)server).getSession().getDirectoryName(); } @@ -47,18 +50,16 @@ public class Utilities { } public static File getBackupRootPath(String worldName) { - File path = new File(Statics.CONFIG.path).getAbsoluteFile(); + File path = new File(config.get().path).getAbsoluteFile(); - if (Statics.CONFIG.perWorldBackup) - path = path.toPath().resolve(worldName).toFile(); + if (config.get().perWorldBackup) path = path.toPath().resolve(worldName).toFile(); - if (!path.exists()) { - path.mkdirs(); - } + if (!path.exists()) path.mkdirs(); return path; } + //This is quite pointless public static boolean isTmpAvailable() { try { File tmp = File.createTempFile("textile_backup_tmp_test", String.valueOf(Instant.now().getEpochSecond())); @@ -88,26 +89,23 @@ public class Utilities { public static boolean isBlacklisted(Path path) { if(isWindows()) { //hotfix! - if (path.getFileName().toString().equals("session.lock")) { - Statics.LOGGER.trace("Skipping session.lock"); - return true; - } + if (path.getFileName().toString().equals("session.lock")) return true; } - for(String i : Statics.CONFIG.fileBlacklist) if(path.startsWith(i)) return true; + for(String i : config.get().fileBlacklist) if(path.startsWith(i)) return true; return false; } - public static Optional getArchiveExtension(String fileName) { + public static Optional getArchiveExtension(String fileName) { String[] parts = fileName.split("\\."); - return Arrays.stream(ConfigHandler.ArchiveFormat.values()) + return Arrays.stream(ConfigPOJO.ArchiveFormat.values()) .filter(format -> format.getLastPiece().equals(parts[parts.length - 1])) .findAny(); } - public static Optional getArchiveExtension(File f) { + public static Optional getArchiveExtension(File f) { return getArchiveExtension(f.getName()); } @@ -155,7 +153,7 @@ public class Utilities { } public static DateTimeFormatter getDateTimeFormatter() { - return DateTimeFormatter.ofPattern(Statics.CONFIG.dateTimeFormat); + return DateTimeFormatter.ofPattern(config.get().dateTimeFormat); } public static DateTimeFormatter getBackupDateTimeFormatter() { diff --git a/src/main/java/net/szum123321/textile_backup/core/create/BackupHelper.java b/src/main/java/net/szum123321/textile_backup/core/create/BackupHelper.java index d590d59..7d643e6 100644 --- a/src/main/java/net/szum123321/textile_backup/core/create/BackupHelper.java +++ b/src/main/java/net/szum123321/textile_backup/core/create/BackupHelper.java @@ -25,6 +25,9 @@ import net.minecraft.text.MutableText; import net.minecraft.util.Formatting; import net.minecraft.util.Util; import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; +import net.szum123321.textile_backup.config.ConfigHelper; import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.Utilities; import org.apache.commons.io.FileUtils; @@ -37,6 +40,9 @@ import java.util.Comparator; import java.util.UUID; public class BackupHelper { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + public static Runnable create(BackupContext ctx) { notifyPlayers(ctx); @@ -55,17 +61,17 @@ public class BackupHelper { builder.append(" on: "); builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now())); - Statics.LOGGER.info(builder.toString()); + log.info(builder.toString()); if (ctx.shouldSave()) { - Statics.LOGGER.sendInfoAL(ctx, "Saving server..."); + log.sendInfoAL(ctx, "Saving server..."); ctx.getServer().getPlayerManager().saveAllPlayerData(); try { ctx.getServer().save(false, true, true); } catch (Exception e) { - Statics.LOGGER.sendErrorAL(ctx,"An exception occurred when trying to save the world!"); + log.sendErrorAL(ctx,"An exception occurred when trying to save the world!"); } } @@ -73,7 +79,7 @@ public class BackupHelper { } private static void notifyPlayers(BackupContext ctx) { - MutableText message = Statics.LOGGER.getPrefixText(); + MutableText message = log.getPrefixText(); message.append(new LiteralText("Warning! Server backup will begin shortly. You may experience some lag.").formatted(Formatting.WHITE)); UUID uuid; @@ -94,30 +100,30 @@ public class BackupHelper { int deletedFiles = 0; if (root.isDirectory() && root.exists() && root.listFiles() != null) { - if (Statics.CONFIG.maxAge > 0) { // delete files older that configured + if (config.get().maxAge > 0) { // delete files older that configured final LocalDateTime now = LocalDateTime.now(); deletedFiles += Arrays.stream(root.listFiles()) .filter(Utilities::isValidBackup)// We check if we can get file's creation date so that the next line won't throw an exception - .filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > Statics.CONFIG.maxAge) + .filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge) .map(f -> deleteFile(f, ctx)) .filter(b -> b).count(); //a bit awkward } - if (Statics.CONFIG.backupsToKeep > 0 && root.listFiles().length > Statics.CONFIG.backupsToKeep) { + if (config.get().backupsToKeep > 0 && root.listFiles().length > config.get().backupsToKeep) { deletedFiles += Arrays.stream(root.listFiles()) .filter(Utilities::isValidBackup) .sorted(Comparator.comparing(f -> Utilities.getFileCreationTime((File) f).get()).reversed()) - .skip(Statics.CONFIG.backupsToKeep) + .skip(config.get().backupsToKeep) .map(f -> deleteFile(f, ctx)) .filter(b -> b).count(); } - if (Statics.CONFIG.maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > Statics.CONFIG.maxSize) { + if (config.get().maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) { deletedFiles += Arrays.stream(root.listFiles()) .filter(Utilities::isValidBackup) .sorted(Comparator.comparing(f -> Utilities.getFileCreationTime(f).get())) - .takeWhile(f -> FileUtils.sizeOfDirectory(root) / 1024 > Statics.CONFIG.maxSize) + .takeWhile(f -> FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) .map(f -> deleteFile(f, ctx)) .filter(b -> b).count(); } @@ -129,10 +135,10 @@ public class BackupHelper { private static boolean deleteFile(File f, ServerCommandSource ctx) { if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) { if(f.delete()) { - Statics.LOGGER.sendInfoAL(ctx, "Deleting: {}", f.getName()); + log.sendInfoAL(ctx, "Deleting: {}", f.getName()); return true; } else { - Statics.LOGGER.sendErrorAL(ctx, "Something went wrong while deleting: {}.", f.getName()); + log.sendErrorAL(ctx, "Something went wrong while deleting: {}.", f.getName()); } } diff --git a/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java b/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java index 8ff78a9..b2ca19e 100644 --- a/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java +++ b/src/main/java/net/szum123321/textile_backup/core/create/BackupScheduler.java @@ -20,11 +20,14 @@ package net.szum123321.textile_backup.core.create; import net.minecraft.server.MinecraftServer; import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.config.ConfigHelper; import net.szum123321.textile_backup.core.ActionInitiator; import java.time.Instant; public class BackupScheduler { + private final static ConfigHelper config = ConfigHelper.INSTANCE; + private boolean scheduled; private long nextBackup; @@ -34,9 +37,10 @@ public class BackupScheduler { } public void tick(MinecraftServer server) { + if(config.get().backupInterval < 1) return; long now = Instant.now().getEpochSecond(); - if(Statics.CONFIG.doBackupsOnEmptyServer || server.getPlayerManager().getCurrentPlayerCount() > 0) { + if(config.get().doBackupsOnEmptyServer || server.getPlayerManager().getCurrentPlayerCount() > 0) { if(scheduled) { if(nextBackup <= now) { Statics.executorService.submit( @@ -50,13 +54,13 @@ public class BackupScheduler { ) ); - nextBackup = now + Statics.CONFIG.backupInterval; + nextBackup = now + config.get().backupInterval; } } else { - nextBackup = now + Statics.CONFIG.backupInterval; + nextBackup = now + config.get().backupInterval; scheduled = true; } - } else if(!Statics.CONFIG.doBackupsOnEmptyServer && server.getPlayerManager().getCurrentPlayerCount() == 0) { + } else if(!config.get().doBackupsOnEmptyServer && server.getPlayerManager().getCurrentPlayerCount() == 0) { if(scheduled && nextBackup <= now) { Statics.executorService.submit( BackupHelper.create( diff --git a/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java b/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java index 5138a37..5879060 100644 --- a/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java +++ b/src/main/java/net/szum123321/textile_backup/core/create/MakeBackupRunnable.java @@ -19,6 +19,9 @@ package net.szum123321.textile_backup.core.create; import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; +import net.szum123321.textile_backup.config.ConfigHelper; import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.create.compressors.*; import net.szum123321.textile_backup.core.Utilities; @@ -33,6 +36,9 @@ import java.io.OutputStream; import java.time.LocalDateTime; public class MakeBackupRunnable implements Runnable { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + private final BackupContext context; public MakeBackupRunnable(BackupContext context){ @@ -45,11 +51,11 @@ public class MakeBackupRunnable implements Runnable { Utilities.disableWorldSaving(context.getServer()); Statics.disableWatchdog = true; - Statics.LOGGER.sendInfoAL(context, "Starting backup"); + log.sendInfoAL(context, "Starting backup"); File world = Utilities.getWorldFolder(context.getServer()); - Statics.LOGGER.trace("Minecraft world is: {}", world); + log.trace("Minecraft world is: {}", world); File outFile = Utilities .getBackupRootPath(Utilities.getLevelName(context.getServer())) @@ -57,32 +63,32 @@ public class MakeBackupRunnable implements Runnable { .resolve(getFileName()) .toFile(); - Statics.LOGGER.trace("Outfile is: {}", outFile); + log.trace("Outfile is: {}", outFile); outFile.getParentFile().mkdirs(); try { outFile.createNewFile(); } catch (IOException e) { - Statics.LOGGER.error("An exception occurred when trying to create new backup file!", e); + log.error("An exception occurred when trying to create new backup file!", e); if(context.getInitiator() == ActionInitiator.Player) - Statics.LOGGER.sendError(context, "An exception occurred when trying to create new backup file!"); + log.sendError(context, "An exception occurred when trying to create new backup file!"); return; } int coreCount; - if(Statics.CONFIG.compressionCoreCountLimit <= 0) { + if(config.get().compressionCoreCountLimit <= 0) { coreCount = Runtime.getRuntime().availableProcessors(); } else { - coreCount = Math.min(Statics.CONFIG.compressionCoreCountLimit, Runtime.getRuntime().availableProcessors()); + coreCount = Math.min(config.get().compressionCoreCountLimit, Runtime.getRuntime().availableProcessors()); } - Statics.LOGGER.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors()); + log.trace("Running compression on {} threads. Available cores: {}", coreCount, Runtime.getRuntime().availableProcessors()); - switch (Statics.CONFIG.format) { + switch (config.get().format) { case ZIP -> { if (Statics.tmpAvailable && coreCount > 1) ParallelZipCompressor.getInstance().createArchive(world, outFile, context, coreCount); @@ -98,16 +104,16 @@ public class MakeBackupRunnable implements Runnable { }.createArchive(world, outFile, context, coreCount); case TAR -> new AbstractTarArchiver().createArchive(world, outFile, context, coreCount); default -> { - Statics.LOGGER.warn("Specified compressor ({}) is not supported! Zip will be used instead!", Statics.CONFIG.format); + log.warn("Specified compressor ({}) is not supported! Zip will be used instead!", config.get().format); if (context.getInitiator() == ActionInitiator.Player) - Statics.LOGGER.sendError(context.getCommandSource(), "Error! No correct compression format specified! Using default compressor!"); + log.sendError(context.getCommandSource(), "Error! No correct compression format specified! Using default compressor!"); ZipCompressor.getInstance().createArchive(world, outFile, context, coreCount); } } BackupHelper.executeFileLimit(context.getCommandSource(), Utilities.getLevelName(context.getServer())); - Statics.LOGGER.sendInfoAL(context, "Done!"); + log.sendInfoAL(context, "Done!"); } finally { Utilities.enableWorldSaving(context.getServer()); Statics.disableWatchdog = false; @@ -119,6 +125,6 @@ public class MakeBackupRunnable implements Runnable { return Utilities.getDateTimeFormatter().format(now) + (context.getComment() != null ? "#" + context.getComment().replace("#", "") : "") + - Statics.CONFIG.format.getCompleteString(); + config.get().format.getCompleteString(); } } diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java index a4634d0..105a317 100644 --- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java +++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/AbstractCompressor.java @@ -18,7 +18,8 @@ package net.szum123321.textile_backup.core.create.compressors; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.NoSpaceLeftOnDeviceException; import net.szum123321.textile_backup.core.Utilities; @@ -32,6 +33,8 @@ import java.time.Instant; import java.util.concurrent.ExecutionException; public abstract class AbstractCompressor { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public void createArchive(File inputFile, File outputFile, BackupContext ctx, int coreLimit) { Instant start = Instant.now(); @@ -48,35 +51,35 @@ public abstract class AbstractCompressor { //hopefully one broken file won't spoil the whole archive addEntry(file, inputFile.toPath().relativize(file.toPath()).toString(), arc); } catch (IOException e) { - Statics.LOGGER.error("An exception occurred while trying to compress: {}", inputFile.toPath().relativize(file.toPath()).toString(), e); + log.error("An exception occurred while trying to compress: {}", inputFile.toPath().relativize(file.toPath()).toString(), e); if (ctx.getInitiator() == ActionInitiator.Player) - Statics.LOGGER.sendError(ctx, "Something went wrong while compressing files!"); + log.sendError(ctx, "Something went wrong while compressing files!"); } }); finish(arc); } catch(NoSpaceLeftOnDeviceException e) { - Statics.LOGGER.error("CRITICAL ERROR OCCURRED!"); - Statics.LOGGER.error("The backup is corrupted."); - Statics.LOGGER.error("Don't panic! This is a known issue!"); - Statics.LOGGER.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems"); - Statics.LOGGER.error("In case this isn't it here's also the exception itself!", e); + log.error("CRITICAL ERROR OCCURRED!"); + log.error("The backup is corrupted."); + log.error("Don't panic! This is a known issue!"); + log.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems"); + log.error("In case this isn't it here's also the exception itself!", e); if(ctx.getInitiator() == ActionInitiator.Player) { - Statics.LOGGER.sendError(ctx, "Backup failed. The file is corrupt."); - Statics.LOGGER.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems"); + log.sendError(ctx, "Backup failed. The file is corrupt."); + log.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems"); } } catch (IOException | InterruptedException | ExecutionException e) { - Statics.LOGGER.error("An exception occurred!", e); + log.error("An exception occurred!", e); } catch (Exception e) { if(ctx.getInitiator() == ActionInitiator.Player) - Statics.LOGGER.sendError(ctx, "Something went wrong while compressing files!"); + log.sendError(ctx, "Something went wrong while compressing files!"); } close(); - Statics.LOGGER.sendInfoAL(ctx, "Compression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now()))); + log.sendInfoAL(ctx, "Compression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now()))); } protected abstract OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException; diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java index b7a0c9e..bab11a7 100644 --- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java +++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ParallelZipCompressor.java @@ -18,7 +18,8 @@ package net.szum123321.textile_backup.core.create.compressors; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.NoSpaceLeftOnDeviceException; import net.szum123321.textile_backup.core.create.BackupContext; import org.apache.commons.compress.archivers.zip.*; @@ -36,6 +37,8 @@ import java.util.zip.ZipEntry; https://stackoverflow.com/users/2987755/dkb */ public class ParallelZipCompressor extends ZipCompressor { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + //These fields are used to discriminate against the issue #51 private final static SimpleStackTraceElement[] STACKTRACE = { new SimpleStackTraceElement("sun.nio.ch.FileDispatcherImpl", "write0", true), @@ -130,7 +133,7 @@ public class ParallelZipCompressor extends ZipCompressor { try { return new FileInputStream(sourceFile); } catch (IOException e) { - Statics.LOGGER.error("An exception occurred while trying to create an input stream from file: {}!", sourceFile.getName(), e); + log.error("An exception occurred while trying to create an input stream from file: {}!", sourceFile.getName(), e); } return null; diff --git a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java index 46f2c35..ea98afd 100644 --- a/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java +++ b/src/main/java/net/szum123321/textile_backup/core/create/compressors/ZipCompressor.java @@ -18,7 +18,7 @@ package net.szum123321.textile_backup.core.create.compressors; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.config.ConfigHelper; import net.szum123321.textile_backup.core.Utilities; import net.szum123321.textile_backup.core.create.BackupContext; import org.apache.commons.compress.archivers.zip.Zip64Mode; @@ -27,12 +27,13 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.compress.utils.IOUtils; import java.io.*; -import java.nio.file.Files; import java.time.LocalDateTime; import java.util.zip.CRC32; import java.util.zip.Checksum; public class ZipCompressor extends AbstractCompressor { + private final static ConfigHelper config = ConfigHelper.INSTANCE; + public static ZipCompressor getInstance() { return new ZipCompressor(); } @@ -43,7 +44,7 @@ public class ZipCompressor extends AbstractCompressor { arc.setMethod(ZipArchiveOutputStream.DEFLATED); arc.setUseZip64(Zip64Mode.AsNeeded); - arc.setLevel(Statics.CONFIG.compression); + arc.setLevel(config.get().compression); arc.setComment("Created on: " + Utilities.getDateTimeFormatter().format(LocalDateTime.now())); return arc; diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java b/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java index 6c60ab7..c022092 100644 --- a/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java +++ b/src/main/java/net/szum123321/textile_backup/core/restore/AwaitThread.java @@ -18,7 +18,8 @@ package net.szum123321.textile_backup.core.restore; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import java.util.concurrent.atomic.AtomicInteger; @@ -26,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; This thread waits some amount of time and then starts a new, independent thread */ public class AwaitThread extends Thread { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); private final static AtomicInteger threadCounter = new AtomicInteger(0); private final int delay; @@ -40,13 +42,13 @@ public class AwaitThread extends Thread { @Override public void run() { - Statics.LOGGER.info("Countdown begins... Waiting {} second.", delay); + log.info("Countdown begins... Waiting {} second.", delay); // 𝄞 This is final count down! Tu ruru Tu, Tu Ru Tu Tu ♪ try { Thread.sleep(delay * 1000L); } catch (InterruptedException e) { - Statics.LOGGER.info("Backup restoration cancelled."); + log.info("Backup restoration cancelled."); return; } diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java index 5cbaca3..33ab6d2 100644 --- a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java +++ b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreBackupRunnable.java @@ -18,7 +18,10 @@ package net.szum123321.textile_backup.core.restore; -import net.szum123321.textile_backup.ConfigHandler; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; +import net.szum123321.textile_backup.config.ConfigHelper; +import net.szum123321.textile_backup.config.ConfigPOJO; import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.LivingServer; import net.szum123321.textile_backup.Statics; @@ -31,6 +34,9 @@ import net.szum123321.textile_backup.core.restore.decompressors.ZipDecompressor; import java.io.File; public class RestoreBackupRunnable implements Runnable { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + private final RestoreContext ctx; public RestoreBackupRunnable(RestoreContext ctx) { @@ -41,12 +47,12 @@ public class RestoreBackupRunnable implements Runnable { public void run() { Statics.globalShutdownBackupFlag.set(false); - Statics.LOGGER.info("Shutting down server..."); + log.info("Shutting down server..."); ctx.getServer().stop(false); awaitServerShutdown(); - if(Statics.CONFIG.backupOldWorlds) { + if(config.get().backupOldWorlds) { BackupHelper.create( BackupContext.Builder .newBackupContextBuilder() @@ -59,31 +65,35 @@ public class RestoreBackupRunnable implements Runnable { File worldFile = Utilities.getWorldFolder(ctx.getServer()); - Statics.LOGGER.info("Deleting old world..."); + log.info("Deleting old world..."); if(!deleteDirectory(worldFile)) - Statics.LOGGER.error("Something went wrong while deleting old world!"); + log.error("Something went wrong while deleting old world!"); worldFile.mkdirs(); - Statics.LOGGER.info("Starting decompression..."); + log.info("Starting decompression..."); - if(ctx.getFile().getArchiveFormat() == ConfigHandler.ArchiveFormat.ZIP) + if(ctx.getFile().getArchiveFormat() == ConfigPOJO.ArchiveFormat.ZIP) ZipDecompressor.decompress(ctx.getFile().getFile(), worldFile); else GenericTarDecompressor.decompress(ctx.getFile().getFile(), worldFile); - if(Statics.CONFIG.deleteOldBackupAfterRestore) { - Statics.LOGGER.info("Deleting old backup"); + if(config.get().deleteOldBackupAfterRestore) { + log.info("Deleting old backup"); if(!ctx.getFile().getFile().delete()) - Statics.LOGGER.info("Something went wrong while deleting old backup"); + log.info("Something went wrong while deleting old backup"); } //in case we're playing on client Statics.globalShutdownBackupFlag.set(true); - Statics.LOGGER.info("Done!"); + log.info("Done!"); + + //Might solve #37 + //Idk if it's a good idea... + //Runtime.getRuntime().exit(0); } private void awaitServerShutdown() { @@ -91,7 +101,7 @@ public class RestoreBackupRunnable implements Runnable { try { Thread.sleep(100); } catch (InterruptedException e) { - Statics.LOGGER.error("Exception occurred!", e); + log.error("Exception occurred!", e); } } } diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java index 44b7c42..6b0d749 100644 --- a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java +++ b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreHelper.java @@ -24,7 +24,10 @@ import net.minecraft.text.LiteralText; import net.minecraft.text.MutableText; import net.minecraft.util.Formatting; import net.minecraft.util.Util; -import net.szum123321.textile_backup.ConfigHandler; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; +import net.szum123321.textile_backup.config.ConfigHelper; +import net.szum123321.textile_backup.config.ConfigPOJO; import net.szum123321.textile_backup.Statics; import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.Utilities; @@ -36,6 +39,9 @@ import java.util.*; import java.util.stream.Collectors; public class RestoreHelper { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + private final static ConfigHelper config = ConfigHelper.INSTANCE; + public static Optional findFileAndLockIfPresent(LocalDateTime backupTime, MinecraftServer server) { File root = Utilities.getBackupRootPath(Utilities.getLevelName(server)); @@ -52,24 +58,24 @@ public class RestoreHelper { public static AwaitThread create(RestoreContext ctx) { if(ctx.getInitiator() == ActionInitiator.Player) - Statics.LOGGER.info("Backup restoration was initiated by: {}", ctx.getCommandSource().getName()); + log.info("Backup restoration was initiated by: {}", ctx.getCommandSource().getName()); else - Statics.LOGGER.info("Backup restoration was initiated form Server Console"); + log.info("Backup restoration was initiated form Server Console"); notifyPlayers(ctx); return new AwaitThread( - Statics.CONFIG.restoreDelay, + config.get().restoreDelay, new RestoreBackupRunnable(ctx) ); } private static void notifyPlayers(RestoreContext ctx) { - MutableText message = Statics.LOGGER.getPrefixText(); + MutableText message = log.getPrefixText(); message.append( new LiteralText( "Warning! The server is going to shut down in " + - Statics.CONFIG.restoreDelay + + config.get().restoreDelay + " seconds!" ).formatted(Formatting.WHITE) ); @@ -100,7 +106,7 @@ public class RestoreHelper { public static class RestoreableFile implements Comparable { private final File file; - private final ConfigHandler.ArchiveFormat archiveFormat; + private final ConfigPOJO.ArchiveFormat archiveFormat; private final LocalDateTime creationTime; private final String comment; @@ -131,7 +137,7 @@ public class RestoreHelper { return file; } - public ConfigHandler.ArchiveFormat getArchiveFormat() { + public ConfigPOJO.ArchiveFormat getArchiveFormat() { return archiveFormat; } diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java index a305b5b..bc26258 100644 --- a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java +++ b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/GenericTarDecompressor.java @@ -18,7 +18,8 @@ package net.szum123321.textile_backup.core.restore.decompressors; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.Utilities; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; @@ -32,6 +33,8 @@ import java.time.Duration; import java.time.Instant; public class GenericTarDecompressor { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static void decompress(File input, File target) { Instant start = Instant.now(); @@ -43,7 +46,7 @@ public class GenericTarDecompressor { while ((entry = archiveInputStream.getNextTarEntry()) != null) { if(!archiveInputStream.canReadEntryData(entry)) { - Statics.LOGGER.error("Something when wrong while trying to decompress {}", entry.getName()); + log.error("Something when wrong while trying to decompress {}", entry.getName()); continue; } @@ -55,22 +58,22 @@ public class GenericTarDecompressor { File parent = file.getParentFile(); if (!parent.isDirectory() && !parent.mkdirs()) { - Statics.LOGGER.error("Failed to create {}", parent); + log.error("Failed to create {}", parent); } else { try (OutputStream outputStream = Files.newOutputStream(file.toPath()); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) { IOUtils.copy(archiveInputStream, bufferedOutputStream); } catch (IOException e) { - Statics.LOGGER.error("An exception occurred while trying to decompress file: {}", file.getName(), e); + log.error("An exception occurred while trying to decompress file: {}", file.getName(), e); } } } } } catch (IOException | CompressorException e) { - Statics.LOGGER.error("An exception occurred! ", e); + log.error("An exception occurred! ", e); } - Statics.LOGGER.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now()))); + log.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now()))); } private static InputStream getCompressorInputStream(InputStream inputStream) throws CompressorException { diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java index a07e952..3fee7eb 100644 --- a/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java +++ b/src/main/java/net/szum123321/textile_backup/core/restore/decompressors/ZipDecompressor.java @@ -18,7 +18,8 @@ package net.szum123321.textile_backup.core.restore.decompressors; -import net.szum123321.textile_backup.Statics; +import net.szum123321.textile_backup.TextileBackup; +import net.szum123321.textile_backup.TextileLogger; import net.szum123321.textile_backup.core.Utilities; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; @@ -30,6 +31,8 @@ import java.time.Duration; import java.time.Instant; public class ZipDecompressor { + private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); + public static void decompress(File inputFile, File target) { Instant start = Instant.now(); @@ -40,7 +43,7 @@ public class ZipDecompressor { while ((entry = zipInputStream.getNextZipEntry()) != null) { if(!zipInputStream.canReadEntryData(entry)){ - Statics.LOGGER.error("Something when wrong while trying to decompress {}", entry.getName()); + log.error("Something when wrong while trying to decompress {}", entry.getName()); continue; } @@ -52,21 +55,21 @@ public class ZipDecompressor { File parent = file.getParentFile(); if (!parent.isDirectory() && !parent.mkdirs()) { - Statics.LOGGER.error("Failed to create {}", parent); + log.error("Failed to create {}", parent); } else { try (OutputStream outputStream = Files.newOutputStream(file.toPath()); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) { IOUtils.copy(zipInputStream, bufferedOutputStream); } catch (IOException e) { - Statics.LOGGER.error("An exception occurred while trying to decompress file: {}", file.getName(), e); + log.error("An exception occurred while trying to decompress file: {}", file.getName(), e); } } } } } catch (IOException e) { - Statics.LOGGER.error("An exception occurred! ", e); + log.error("An exception occurred! ", e); } - Statics.LOGGER.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now()))); + log.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now()))); } } diff --git a/src/main/resources/assets/textile_backup/lang/en_us.json b/src/main/resources/assets/textile_backup/lang/en_us.json new file mode 100644 index 0000000..779b4d7 --- /dev/null +++ b/src/main/resources/assets/textile_backup/lang/en_us.json @@ -0,0 +1,49 @@ +{ + "text.autoconfig.textile_backup.title": "Textile Backup Configuration", + + "text.autoconfig.textile_backup.category.default": "General", + "text.autoconfig.textile_backup.category.Create": "Backup settings", + "text.autoconfig.textile_backup.category.Restore": "Restore", + "text.autoconfig.textile_backup.category.Manage": "Management", + + "text.autoconfig.textile_backup.option.backupInterval": "Backup Interval", + "text.autoconfig.textile_backup.option.backupInterval.@Tooltip": "AAAAAA", + + "text.autoconfig.textile_backup.option.restoreDelay": "Restore Delay", + + "text.autoconfig.textile_backup.option.doBackupsOnEmptyServer": "Do backups on empty server", + + "text.autoconfig.textile_backup.option.shutdownBackup": "Make a backup on shutdown", + + "text.autoconfig.textile_backup.option.backupOldWorlds": "Backup old worlds", + + "text.autoconfig.textile_backup.option.perWorldBackup": "Use separate folders for different worlds", + + "text.autoconfig.textile_backup.option.path": "Path to backup folder", + + "text.autoconfig.textile_backup.option.fileBlacklist": "Blacklised files", + + "text.autoconfig.textile_backup.option.deleteOldBackupAfterRestore": "Delete restored backup", + + "text.autoconfig.textile_backup.option.backupsToKeep": "Number of backups to keep", + + "text.autoconfig.textile_backup.option.maxAge": "Max age of backup", + + "text.autoconfig.textile_backup.option.maxSize": "Max size of backup folder", + + "text.autoconfig.textile_backup.option.compression": "Compression level (Zip only)", + + "text.autoconfig.textile_backup.option.compressionCoreCountLimit": "Max number of cores used for compression", + + "text.autoconfig.textile_backup.option.format": "Archive and compression format", + + "text.autoconfig.textile_backup.option.permissionLevel": "Min permission level required to run any command", + + "text.autoconfig.textile_backup.option.alwaysSingleplayerAllowed": "Always allow on sigle-player", + + "text.autoconfig.textile_backup.option.playerWhitelist": "Admin Whitelist", + + "text.autoconfig.textile_backup.option.playerBlacklist": "Admin Blacklist", + + "text.autoconfig.textile_backup.option.dateTimeFormat": "Date&Time format" +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index d7e5138..0af516a 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -27,6 +27,9 @@ "entrypoints": { "main": [ "net.szum123321.textile_backup.TextileBackup" + ], + "modmenu": [ + "net.szum123321.textile_backup.client.ModMenuEntry" ] }, "mixins": [