diff --git a/src/main/java/net/szum123321/textile_backup/Statics.java b/src/main/java/net/szum123321/textile_backup/Statics.java index fbf6564..9a98ebb 100644 --- a/src/main/java/net/szum123321/textile_backup/Statics.java +++ b/src/main/java/net/szum123321/textile_backup/Statics.java @@ -20,7 +20,7 @@ package net.szum123321.textile_backup; import net.szum123321.textile_backup.core.restore.AwaitThread; -import java.io.File; +import java.nio.file.Path; import java.time.format.DateTimeFormatter; import java.util.Optional; import java.util.concurrent.ExecutorService; @@ -35,6 +35,6 @@ public class Statics { public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true); public static boolean disableWatchdog = false; public static AwaitThread restoreAwaitThread = null; - public static Optional untouchableFile = Optional.empty(); + public static Optional untouchableFile = Optional.empty(); public static boolean disableTMPFiles = false; } 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 d2cae78..168c23b 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 @@ -21,7 +21,6 @@ package net.szum123321.textile_backup.commands.manage; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; 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; @@ -31,11 +30,13 @@ import net.szum123321.textile_backup.Statics; import net.szum123321.textile_backup.commands.FileSuggestionProvider; import net.szum123321.textile_backup.core.Utilities; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeParseException; -import java.util.Arrays; import java.util.Optional; +import java.util.stream.Stream; public class DeleteCommand { private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); @@ -57,28 +58,28 @@ public class DeleteCommand { throw CommandExceptions.DATE_TIME_PARSE_COMMAND_EXCEPTION_TYPE.create(e); } - File root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer())); + Path root = Utilities.getBackupRootPath(Utilities.getLevelName(source.getServer())); - Optional optionalFile = Arrays.stream(root.listFiles()) - .filter(Utilities::isValidBackup) - .filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime)) - .findFirst(); + try (Stream stream = Files.list(root)) { + stream.filter(Utilities::isValidBackup) + .filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime)) + .findFirst().ifPresent(file -> { + if(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(file)) { + try { + Files.delete(file); + log.sendInfo(source, "File {} successfully deleted!", file); - if(optionalFile.isPresent()) { - if(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(optionalFile.get())) { - if(optionalFile.get().delete()) { - log.sendInfo(source, "File {} successfully deleted!", optionalFile.get().getName()); - - if(source.getEntity() instanceof PlayerEntity) - log.info("Player {} deleted {}.", source.getPlayer().getName(), optionalFile.get().getName()); - } else { - log.sendError(source, "Something went wrong while deleting file!"); - } - } else { - 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 { + if(source.isExecutedByPlayer()) + log.info("Player {} deleted {}.", source.getPlayer().getName(), file); + } catch (IOException e) { + log.sendError(source, "Something went wrong while deleting file!"); + } + } else { + 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"); + } + }); + } catch (IOException ignored) { log.sendError(source, "Couldn't find file by this name."); log.sendHint(source, "Maybe try /backup list"); } 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 bb516c9..85d984b 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 @@ -82,7 +82,7 @@ public class RestoreBackupCommand { Optional backupFile = RestoreHelper.findFileAndLockIfPresent(dateTime, source.getServer()); if(backupFile.isPresent()) { - log.info("Found file to restore {}", backupFile.get().getFile().getName()); + log.info("Found file to restore {}", backupFile.get().getFile().getFileName().toString()); } else { log.sendInfo(source, "No file created on {} was found!", dateTime.format(Statics.defaultDateTimeFormatter)); 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 8a56b91..bc33d84 100644 --- a/src/main/java/net/szum123321/textile_backup/core/Utilities.java +++ b/src/main/java/net/szum123321/textile_backup/core/Utilities.java @@ -60,19 +60,22 @@ public class Utilities { return ((MinecraftServerSessionAccessor)server).getSession().getDirectoryName(); } - public static File getWorldFolder(MinecraftServer server) { + public static Path getWorldFolder(MinecraftServer server) { return ((MinecraftServerSessionAccessor)server) .getSession() - .getWorldDirectory(World.OVERWORLD) - .toFile(); + .getWorldDirectory(World.OVERWORLD); } - public static File getBackupRootPath(String worldName) { - File path = new File(config.get().path).getAbsoluteFile(); + public static Path getBackupRootPath(String worldName) { + Path path = Path.of(config.get().path).toAbsolutePath(); - if (config.get().perWorldBackup) path = path.toPath().resolve(worldName).toFile(); + if (config.get().perWorldBackup) path = path.resolve(worldName); - if (!path.exists()) path.mkdirs(); + try { + Files.createDirectories(path); + } catch (IOException e) { + //I REALLY shouldn't be handling this here + } return path; } @@ -81,7 +84,7 @@ public class Utilities { boolean flag = false; Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir")); if( - FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server)) >= + FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server).toFile()) >= tmp_dir.toFile().getUsableSpace() ) { log.error("Not enough space left in TMP directory! ({})", tmp_dir); @@ -130,11 +133,11 @@ public class Utilities { .findAny(); } - public static Optional getArchiveExtension(File f) { - return getArchiveExtension(f.getName()); + public static Optional getArchiveExtension(Path f) { + return getArchiveExtension(f.getFileName().toString()); } - public static Optional getFileCreationTime(File file) { + public static Optional getFileCreationTime(Path file) { LocalDateTime creationTime = null; if(getArchiveExtension(file).isPresent()) { @@ -143,7 +146,7 @@ public class Utilities { try { creationTime = LocalDateTime.from( Utilities.getDateTimeFormatter().parse( - file.getName().split(fileExtension)[0].split("#")[0] + file.getFileName().toString().split(fileExtension)[0].split("#")[0] ) ); } catch (Exception ignored) {} @@ -152,7 +155,7 @@ public class Utilities { try { creationTime = LocalDateTime.from( Utilities.getBackupDateTimeFormatter().parse( - file.getName().split(fileExtension)[0].split("#")[0] + file.getFileName().toString().split(fileExtension)[0].split("#")[0] ) ); } catch (Exception ignored2){} @@ -160,7 +163,7 @@ public class Utilities { if(creationTime == null) { try { - FileTime fileTime = (FileTime) Files.getAttribute(file.toPath(), "creationTime"); + FileTime fileTime = (FileTime) Files.getAttribute(file, "creationTime"); creationTime = LocalDateTime.ofInstant(fileTime.toInstant(), ZoneOffset.systemDefault()); } catch (IOException ignored3) {} } @@ -169,7 +172,7 @@ public class Utilities { return Optional.ofNullable(creationTime); } - public static boolean isValidBackup(File f) { + public static boolean isValidBackup(Path f) { return getArchiveExtension(f).isPresent() && getFileCreationTime(f).isPresent() && isFileOk(f); } @@ -177,6 +180,10 @@ public class Utilities { return f.exists() && f.isFile(); } + public static boolean isFileOk(Path f) { + return Files.exists(f) && Files.isRegularFile(f); + } + public static DateTimeFormatter getDateTimeFormatter() { return DateTimeFormatter.ofPattern(config.get().dateTimeFormat); } 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 864f231..359dc7c 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 @@ -26,11 +26,13 @@ import net.szum123321.textile_backup.config.ConfigHelper; import net.szum123321.textile_backup.core.Utilities; import org.apache.commons.io.FileUtils; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.LocalDateTime; import java.time.ZoneOffset; -import java.util.Arrays; import java.util.Comparator; +import java.util.stream.Stream; public class BackupHelper { private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME); @@ -77,52 +79,76 @@ public class BackupHelper { } public static int executeFileLimit(ServerCommandSource ctx, String worldName) { - File root = Utilities.getBackupRootPath(worldName); + Path root = Utilities.getBackupRootPath(worldName); int deletedFiles = 0; - if (root.isDirectory() && root.exists() && root.listFiles() != null) { + + if (Files.isDirectory(root) && Files.exists(root) && !isEmpty(root)) { 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) > config.get().maxAge) - .map(f -> deleteFile(f, ctx)) - .filter(b -> b).count(); //a bit awkward + try(Stream stream = Files.list(root)) { + deletedFiles += stream + .filter(Utilities::isValidBackup)// We check if we can get restoreableFile'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) > config.get().maxAge) + .map(f -> deleteFile(f, ctx)) + .filter(b -> b).count(); //a bit awkward + } catch (IOException e) { + throw new RuntimeException(e); + } + } + if (config.get().backupsToKeep > 0 /*&& root.listFiles().length > config.get().backupsToKeep*/) { + try(Stream stream = Files.list(root)) { + deletedFiles += stream + .filter(Utilities::isValidBackup) + .sorted(Comparator.comparing(f -> Utilities.getFileCreationTime((Path) f).get()).reversed()) + .skip(config.get().backupsToKeep) + .map(f -> deleteFile(f, ctx)) + .filter(b -> b).count(); + } catch (IOException e) { + throw new RuntimeException(e); + } } - 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(config.get().backupsToKeep) - .map(f -> deleteFile(f, ctx)) - .filter(b -> b).count(); - } - - if (config.get().maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) { + //It is fucking quadratic! + /*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 > config.get().maxSize) .map(f -> deleteFile(f, ctx)) .filter(b -> b).count(); - } + }*/ } return deletedFiles; } - private static boolean deleteFile(File f, ServerCommandSource ctx) { - if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) { - if(f.delete()) { - log.sendInfoAL(ctx, "Deleting: {}", f.getName()); - return true; - } else { - log.sendErrorAL(ctx, "Something went wrong while deleting: {}.", f.getName()); + private static boolean isEmpty(Path path) { + if (Files.isDirectory(path)) { + try (Stream entries = Files.list(path)) { + return entries.findFirst().isEmpty(); + } catch (IOException e) { + return false; } } return false; } + + private static boolean deleteFile(Path f, ServerCommandSource ctx) { + if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) { + try { + Files.delete(f); + log.sendInfoAL(ctx, "Deleting: {}", f); + } catch (IOException e) { + if(ctx.isExecutedByPlayer()) log.sendError(ctx, "Something went wrong while deleting: {}.", f); + log.error("Something went wrong while deleting: {}.", f, e); + return false; + } + return true; + } + + return false; + } } \ No newline at end of file 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 70a17ac..af8ba94 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 @@ -56,13 +56,12 @@ public class MakeBackupRunnable implements Runnable { log.sendInfoAL(context, "Starting backup"); - Path world = Utilities.getWorldFolder(context.getServer()).toPath(); + Path world = Utilities.getWorldFolder(context.getServer()); log.trace("Minecraft world is: {}", world); Path outFile = Utilities .getBackupRootPath(Utilities.getLevelName(context.getServer())) - .toPath() .resolve(getFileName()); log.trace("Outfile is: {}", outFile); diff --git a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java index 01ad8bd..2e597b3 100644 --- a/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java +++ b/src/main/java/net/szum123321/textile_backup/core/restore/RestoreContext.java @@ -21,43 +21,15 @@ package net.szum123321.textile_backup.core.restore; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.util.Util; import net.szum123321.textile_backup.core.ActionInitiator; import javax.annotation.Nullable; -import java.util.UUID; -public record RestoreContext(RestoreHelper.RestoreableFile file, +public record RestoreContext(RestoreHelper.RestoreableFile restoreableFile, MinecraftServer server, @Nullable String comment, ActionInitiator initiator, ServerCommandSource commandSource) { - - public RestoreHelper.RestoreableFile getFile() { - return file; - } - - public MinecraftServer getServer() { - return server; - } - - @Nullable - public String getComment() { - return comment; - } - - public ActionInitiator getInitiator() { - return initiator; - } - - public UUID getInitiatorUUID() { - return initiator.equals(ActionInitiator.Player) && commandSource.getEntity() != null ? commandSource.getEntity().getUuid(): Util.NIL_UUID; - } - - public ServerCommandSource getCommandSource() { - return commandSource; - } - public static final class Builder { private RestoreHelper.RestoreableFile file; private MinecraftServer server; 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 a55c1d9..977fd24 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 @@ -28,23 +28,31 @@ import net.szum123321.textile_backup.core.ActionInitiator; import net.szum123321.textile_backup.core.Utilities; import org.jetbrains.annotations.NotNull; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; 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)); + Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server)); - Optional optionalFile = Arrays.stream(root.listFiles()) - .map(RestoreableFile::newInstance) - .flatMap(Optional::stream) - .filter(rf -> rf.getCreationTime().equals(backupTime)) - .findFirst(); + Optional optionalFile; + try (Stream stream = Files.list(root)) { + optionalFile = stream + .map(RestoreableFile::newInstance) + .flatMap(Optional::stream) + .filter(rf -> rf.getCreationTime().equals(backupTime)) + .findFirst(); + } catch (IOException e) { + throw new RuntimeException(e); + } Statics.untouchableFile = optionalFile.map(RestoreableFile::getFile); @@ -52,8 +60,8 @@ public class RestoreHelper { } public static AwaitThread create(RestoreContext ctx) { - if(ctx.getInitiator() == ActionInitiator.Player) - log.info("Backup restoration was initiated by: {}", ctx.getCommandSource().getName()); + if(ctx.initiator() == ActionInitiator.Player) + log.info("Backup restoration was initiated by: {}", ctx.commandSource().getName()); else log.info("Backup restoration was initiated form Server Console"); @@ -69,28 +77,32 @@ public class RestoreHelper { } public static List getAvailableBackups(MinecraftServer server) { - File root = Utilities.getBackupRootPath(Utilities.getLevelName(server)); + Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server)); - return Arrays.stream(root.listFiles()) - .filter(Utilities::isValidBackup) - .map(RestoreableFile::newInstance) - .flatMap(Optional::stream) - .collect(Collectors.toList()); + try (Stream stream = Files.list(root)) { + return stream.filter(Utilities::isValidBackup) + .map(RestoreableFile::newInstance) + .flatMap(Optional::stream) + .collect(Collectors.toList()); + } catch (IOException e) { + log.error("Error while listing available backups", e); + return new LinkedList<>(); + } } public static class RestoreableFile implements Comparable { - private final File file; + private final Path file; private final ConfigPOJO.ArchiveFormat archiveFormat; private final LocalDateTime creationTime; private final String comment; - private RestoreableFile(File file) throws NoSuchElementException { + private RestoreableFile(Path file) throws NoSuchElementException { this.file = file; - archiveFormat = Utilities.getArchiveExtension(file).orElseThrow(() -> new NoSuchElementException("Couldn't get file extension!")); + archiveFormat = Utilities.getArchiveExtension(file).orElseThrow(() -> new NoSuchElementException("Couldn't get restoreableFile extension!")); String extension = archiveFormat.getCompleteString(); - creationTime = Utilities.getFileCreationTime(file).orElseThrow(() -> new NoSuchElementException("Couldn't get file creation time!")); + creationTime = Utilities.getFileCreationTime(file).orElseThrow(() -> new NoSuchElementException("Couldn't get restoreableFile creation time!")); - final String filename = file.getName(); + final String filename = file.getFileName().toString(); if(filename.split("#").length > 1) { this.comment = filename.split("#")[1].split(extension)[0]; @@ -99,7 +111,7 @@ public class RestoreHelper { } } - public static Optional newInstance(File file) { + public static Optional newInstance(Path file) { try { return Optional.of(new RestoreableFile(file)); } catch (NoSuchElementException ignored) {} @@ -107,7 +119,7 @@ public class RestoreHelper { return Optional.empty(); } - public File getFile() { + public Path getFile() { return file; }