commit
267776789d
|
@ -6,7 +6,7 @@ yarn_mappings=1.19+build.4
|
||||||
loader_version=0.14.8
|
loader_version=0.14.8
|
||||||
|
|
||||||
#Fabric api
|
#Fabric api
|
||||||
fabric_version=0.56.0+1.19
|
fabric_version=0.56.1+1.19
|
||||||
|
|
||||||
#Cloth Config
|
#Cloth Config
|
||||||
cloth_version=7.0.72
|
cloth_version=7.0.72
|
||||||
|
@ -15,12 +15,12 @@ cloth_version=7.0.72
|
||||||
modmenu_version=4.0.0
|
modmenu_version=4.0.0
|
||||||
|
|
||||||
#Lazy DFU for faster dev start
|
#Lazy DFU for faster dev start
|
||||||
lazydfu_version=0.1.2
|
lazydfu_version=v0.1.3
|
||||||
|
|
||||||
#Hash of commit form which parallel gzip will be build
|
#Hash of commit form which parallel gzip will be build
|
||||||
pgzip_commit_hash=af5f5c297e735f3f2df7aa4eb0e19a5810b8aff6
|
pgzip_commit_hash=af5f5c297e735f3f2df7aa4eb0e19a5810b8aff6
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 2.3.1
|
mod_version = 2.4.0
|
||||||
maven_group = net.szum123321
|
maven_group = net.szum123321
|
||||||
archives_base_name = textile_backup
|
archives_base_name = textile_backup
|
|
@ -20,7 +20,7 @@ package net.szum123321.textile_backup;
|
||||||
|
|
||||||
import net.szum123321.textile_backup.core.restore.AwaitThread;
|
import net.szum123321.textile_backup.core.restore.AwaitThread;
|
||||||
|
|
||||||
import java.io.File;
|
import java.nio.file.Path;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
@ -35,6 +35,6 @@ public class Statics {
|
||||||
public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true);
|
public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true);
|
||||||
public static boolean disableWatchdog = false;
|
public static boolean disableWatchdog = false;
|
||||||
public static AwaitThread restoreAwaitThread = null;
|
public static AwaitThread restoreAwaitThread = null;
|
||||||
public static Optional<File> untouchableFile = Optional.empty();
|
public static Optional<Path> untouchableFile = Optional.empty();
|
||||||
public static boolean disableTMPFiles = false;
|
public static boolean disableTMPFiles = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,12 +85,16 @@ public class TextileLogger {
|
||||||
log(Level.ERROR, msg, data);
|
log(Level.ERROR, msg, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void error(String message, Throwable throwable) {
|
||||||
|
logger.error(prefix + message, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
public void fatal(String msg, Object... data) {
|
public void fatal(String msg, Object... data) {
|
||||||
log(Level.FATAL, msg, data);
|
log(Level.FATAL, msg, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean sendFeedback(Level level, ServerCommandSource source, String msg, Object... args) {
|
boolean sendFeedback(Level level, ServerCommandSource source, String msg, Object... args) {
|
||||||
if(source != null && source.getEntity() instanceof PlayerEntity) {
|
if(source != null && source.isExecutedByPlayer()) {
|
||||||
MutableText text = Text.literal(messageFactory.newMessage(msg, args).getFormattedMessage());
|
MutableText text = Text.literal(messageFactory.newMessage(msg, args).getFormattedMessage());
|
||||||
|
|
||||||
if(level.intLevel() == Level.TRACE.intLevel()) text.formatted(Formatting.GREEN);
|
if(level.intLevel() == Level.TRACE.intLevel()) text.formatted(Formatting.GREEN);
|
||||||
|
@ -116,7 +120,7 @@ public class TextileLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendInfo(BackupContext context, String msg, Object... args) {
|
public void sendInfo(BackupContext context, String msg, Object... args) {
|
||||||
sendInfo(context.getCommandSource(), msg, args);
|
sendInfo(context.commandSource(), msg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(ServerCommandSource source, String msg, Object... args) {
|
public void sendError(ServerCommandSource source, String msg, Object... args) {
|
||||||
|
@ -124,7 +128,7 @@ public class TextileLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendError(BackupContext context, String msg, Object... args) {
|
public void sendError(BackupContext context, String msg, Object... args) {
|
||||||
sendError(context.getCommandSource(), msg, args);
|
sendError(context.commandSource(), msg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendToPlayerAndLog(Level level, ServerCommandSource source, String msg, Object... args) {
|
public void sendToPlayerAndLog(Level level, ServerCommandSource source, String msg, Object... args) {
|
||||||
|
@ -138,7 +142,7 @@ public class TextileLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendInfoAL(BackupContext context, String msg, Object... args) {
|
public void sendInfoAL(BackupContext context, String msg, Object... args) {
|
||||||
sendInfoAL(context.getCommandSource(), msg, args);
|
sendInfoAL(context.commandSource(), msg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendErrorAL(ServerCommandSource source, String msg, Object... args) {
|
public void sendErrorAL(ServerCommandSource source, String msg, Object... args) {
|
||||||
|
@ -146,6 +150,6 @@ public class TextileLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendErrorAL(BackupContext context, String msg, Object... args) {
|
public void sendErrorAL(BackupContext context, String msg, Object... args) {
|
||||||
sendErrorAL(context.getCommandSource(), msg, args);
|
sendErrorAL(context.commandSource(), msg, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ package net.szum123321.textile_backup.commands.manage;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
|
||||||
import net.minecraft.server.command.CommandManager;
|
import net.minecraft.server.command.CommandManager;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
import net.szum123321.textile_backup.TextileBackup;
|
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.commands.FileSuggestionProvider;
|
||||||
import net.szum123321.textile_backup.core.Utilities;
|
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.LocalDateTime;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class DeleteCommand {
|
public class DeleteCommand {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
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);
|
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<File> optionalFile = Arrays.stream(root.listFiles())
|
try (Stream<Path> stream = Files.list(root)) {
|
||||||
.filter(Utilities::isValidBackup)
|
stream.filter(Utilities::isValidBackup)
|
||||||
.filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime))
|
.filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime))
|
||||||
.findFirst();
|
.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(source.isExecutedByPlayer())
|
||||||
if(Statics.untouchableFile.isEmpty() || !Statics.untouchableFile.get().equals(optionalFile.get())) {
|
log.info("Player {} deleted {}.", source.getPlayer().getName(), file);
|
||||||
if(optionalFile.get().delete()) {
|
} catch (IOException e) {
|
||||||
log.sendInfo(source, "File {} successfully deleted!", optionalFile.get().getName());
|
log.sendError(source, "Something went wrong while deleting file!");
|
||||||
|
}
|
||||||
if(source.getEntity() instanceof PlayerEntity)
|
} else {
|
||||||
log.info("Player {} deleted {}.", source.getPlayer().getName(), optionalFile.get().getName());
|
log.sendError(source, "Couldn't delete the file because it's being restored right now.");
|
||||||
} else {
|
log.sendHint(source, "If you want to abort restoration then use: /backup killR");
|
||||||
log.sendError(source, "Something went wrong while deleting file!");
|
}
|
||||||
}
|
});
|
||||||
} else {
|
} catch (IOException ignored) {
|
||||||
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 {
|
|
||||||
log.sendError(source, "Couldn't find file by this name.");
|
log.sendError(source, "Couldn't find file by this name.");
|
||||||
log.sendHint(source, "Maybe try /backup list");
|
log.sendHint(source, "Maybe try /backup list");
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class RestoreBackupCommand {
|
||||||
Optional<RestoreHelper.RestoreableFile> backupFile = RestoreHelper.findFileAndLockIfPresent(dateTime, source.getServer());
|
Optional<RestoreHelper.RestoreableFile> backupFile = RestoreHelper.findFileAndLockIfPresent(dateTime, source.getServer());
|
||||||
|
|
||||||
if(backupFile.isPresent()) {
|
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 {
|
} else {
|
||||||
log.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));
|
||||||
|
|
||||||
|
|
|
@ -32,18 +32,20 @@ import net.szum123321.textile_backup.config.ConfigPOJO;
|
||||||
import net.szum123321.textile_backup.Statics;
|
import net.szum123321.textile_backup.Statics;
|
||||||
import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
|
import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.file.SimplePathVisitor;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.nio.file.attribute.FileTime;
|
import java.nio.file.attribute.FileTime;
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class Utilities {
|
public class Utilities {
|
||||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||||
|
@ -60,28 +62,47 @@ public class Utilities {
|
||||||
return ((MinecraftServerSessionAccessor)server).getSession().getDirectoryName();
|
return ((MinecraftServerSessionAccessor)server).getSession().getDirectoryName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getWorldFolder(MinecraftServer server) {
|
public static Path getWorldFolder(MinecraftServer server) {
|
||||||
return ((MinecraftServerSessionAccessor)server)
|
return ((MinecraftServerSessionAccessor)server)
|
||||||
.getSession()
|
.getSession()
|
||||||
.getWorldDirectory(World.OVERWORLD)
|
.getWorldDirectory(World.OVERWORLD);
|
||||||
.toFile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getBackupRootPath(String worldName) {
|
public static Path getBackupRootPath(String worldName) {
|
||||||
File path = new File(config.get().path).getAbsoluteFile();
|
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;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteDirectory(Path path) throws IOException {
|
||||||
|
Files.walkFileTree(path, new SimplePathVisitor() {
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
Files.delete(file);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||||
|
Files.delete(dir);
|
||||||
|
return FileVisitResult.CONTINUE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void updateTMPFSFlag(MinecraftServer server) {
|
public static void updateTMPFSFlag(MinecraftServer server) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir"));
|
Path tmp_dir = Path.of(System.getProperty("java.io.tmpdir"));
|
||||||
if(
|
if(
|
||||||
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server)) >=
|
FileUtils.sizeOfDirectory(Utilities.getWorldFolder(server).toFile()) >=
|
||||||
tmp_dir.toFile().getUsableSpace()
|
tmp_dir.toFile().getUsableSpace()
|
||||||
) {
|
) {
|
||||||
log.error("Not enough space left in TMP directory! ({})", tmp_dir);
|
log.error("Not enough space left in TMP directory! ({})", tmp_dir);
|
||||||
|
@ -130,11 +151,11 @@ public class Utilities {
|
||||||
.findAny();
|
.findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(File f) {
|
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(Path f) {
|
||||||
return getArchiveExtension(f.getName());
|
return getArchiveExtension(f.getFileName().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<LocalDateTime> getFileCreationTime(File file) {
|
public static Optional<LocalDateTime> getFileCreationTime(Path file) {
|
||||||
LocalDateTime creationTime = null;
|
LocalDateTime creationTime = null;
|
||||||
|
|
||||||
if(getArchiveExtension(file).isPresent()) {
|
if(getArchiveExtension(file).isPresent()) {
|
||||||
|
@ -143,7 +164,7 @@ public class Utilities {
|
||||||
try {
|
try {
|
||||||
creationTime = LocalDateTime.from(
|
creationTime = LocalDateTime.from(
|
||||||
Utilities.getDateTimeFormatter().parse(
|
Utilities.getDateTimeFormatter().parse(
|
||||||
file.getName().split(fileExtension)[0].split("#")[0]
|
file.getFileName().toString().split(fileExtension)[0].split("#")[0]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
|
@ -152,7 +173,7 @@ public class Utilities {
|
||||||
try {
|
try {
|
||||||
creationTime = LocalDateTime.from(
|
creationTime = LocalDateTime.from(
|
||||||
Utilities.getBackupDateTimeFormatter().parse(
|
Utilities.getBackupDateTimeFormatter().parse(
|
||||||
file.getName().split(fileExtension)[0].split("#")[0]
|
file.getFileName().toString().split(fileExtension)[0].split("#")[0]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} catch (Exception ignored2){}
|
} catch (Exception ignored2){}
|
||||||
|
@ -160,7 +181,7 @@ public class Utilities {
|
||||||
|
|
||||||
if(creationTime == null) {
|
if(creationTime == null) {
|
||||||
try {
|
try {
|
||||||
FileTime fileTime = (FileTime) Files.getAttribute(file.toPath(), "creationTime");
|
FileTime fileTime = (FileTime) Files.getAttribute(file, "creationTime");
|
||||||
creationTime = LocalDateTime.ofInstant(fileTime.toInstant(), ZoneOffset.systemDefault());
|
creationTime = LocalDateTime.ofInstant(fileTime.toInstant(), ZoneOffset.systemDefault());
|
||||||
} catch (IOException ignored3) {}
|
} catch (IOException ignored3) {}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +190,7 @@ public class Utilities {
|
||||||
return Optional.ofNullable(creationTime);
|
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);
|
return getArchiveExtension(f).isPresent() && getFileCreationTime(f).isPresent() && isFileOk(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +198,10 @@ public class Utilities {
|
||||||
return f.exists() && f.isFile();
|
return f.exists() && f.isFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFileOk(Path f) {
|
||||||
|
return Files.exists(f) && Files.isRegularFile(f);
|
||||||
|
}
|
||||||
|
|
||||||
public static DateTimeFormatter getDateTimeFormatter() {
|
public static DateTimeFormatter getDateTimeFormatter() {
|
||||||
return DateTimeFormatter.ofPattern(config.get().dateTimeFormat);
|
return DateTimeFormatter.ofPattern(config.get().dateTimeFormat);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,18 +33,6 @@ public record BackupContext(@NotNull MinecraftServer server,
|
||||||
boolean save,
|
boolean save,
|
||||||
String comment) {
|
String comment) {
|
||||||
|
|
||||||
public MinecraftServer getServer() {
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerCommandSource getCommandSource() {
|
|
||||||
return commandSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionInitiator getInitiator() {
|
|
||||||
return initiator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean startedByPlayer() {
|
public boolean startedByPlayer() {
|
||||||
return initiator == ActionInitiator.Player;
|
return initiator == ActionInitiator.Player;
|
||||||
}
|
}
|
||||||
|
@ -53,14 +41,6 @@ public record BackupContext(@NotNull MinecraftServer server,
|
||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getComment() {
|
|
||||||
return comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getInitiatorUUID() {
|
|
||||||
return initiator.equals(ActionInitiator.Player) && commandSource.getEntity() != null ? commandSource.getEntity().getUuid(): Util.NIL_UUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private MinecraftServer server;
|
private MinecraftServer server;
|
||||||
private ServerCommandSource commandSource;
|
private ServerCommandSource commandSource;
|
||||||
|
|
|
@ -24,13 +24,16 @@ import net.szum123321.textile_backup.TextileBackup;
|
||||||
import net.szum123321.textile_backup.TextileLogger;
|
import net.szum123321.textile_backup.TextileLogger;
|
||||||
import net.szum123321.textile_backup.config.ConfigHelper;
|
import net.szum123321.textile_backup.config.ConfigHelper;
|
||||||
import net.szum123321.textile_backup.core.Utilities;
|
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.LocalDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class BackupHelper {
|
public class BackupHelper {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||||
|
@ -38,7 +41,7 @@ public class BackupHelper {
|
||||||
|
|
||||||
public static Runnable create(BackupContext ctx) {
|
public static Runnable create(BackupContext ctx) {
|
||||||
if(config.get().broadcastBackupStart) {
|
if(config.get().broadcastBackupStart) {
|
||||||
Utilities.notifyPlayers(ctx.getServer(),
|
Utilities.notifyPlayers(ctx.server(),
|
||||||
"Warning! Server backup will begin shortly. You may experience some lag."
|
"Warning! Server backup will begin shortly. You may experience some lag."
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -49,12 +52,12 @@ public class BackupHelper {
|
||||||
|
|
||||||
builder.append("Backup started ");
|
builder.append("Backup started ");
|
||||||
|
|
||||||
builder.append(ctx.getInitiator().getPrefix());
|
builder.append(ctx.initiator().getPrefix());
|
||||||
|
|
||||||
if(ctx.startedByPlayer())
|
if(ctx.startedByPlayer())
|
||||||
builder.append(ctx.getCommandSource().getDisplayName().getString());
|
builder.append(ctx.commandSource().getDisplayName().getString());
|
||||||
else
|
else
|
||||||
builder.append(ctx.getInitiator().getName());
|
builder.append(ctx.initiator().getName());
|
||||||
|
|
||||||
builder.append(" on: ");
|
builder.append(" on: ");
|
||||||
builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
|
builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
|
||||||
|
@ -64,10 +67,10 @@ public class BackupHelper {
|
||||||
if (ctx.shouldSave()) {
|
if (ctx.shouldSave()) {
|
||||||
log.sendInfoAL(ctx, "Saving server...");
|
log.sendInfoAL(ctx, "Saving server...");
|
||||||
|
|
||||||
ctx.getServer().getPlayerManager().saveAllPlayerData();
|
ctx.server().getPlayerManager().saveAllPlayerData();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ctx.getServer().save(false, true, true);
|
ctx.server().save(false, true, true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.sendErrorAL(ctx,"An exception occurred when trying to save the world!");
|
log.sendErrorAL(ctx,"An exception occurred when trying to save the world!");
|
||||||
}
|
}
|
||||||
|
@ -77,52 +80,105 @@ public class BackupHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int executeFileLimit(ServerCommandSource ctx, String worldName) {
|
public static int executeFileLimit(ServerCommandSource ctx, String worldName) {
|
||||||
File root = Utilities.getBackupRootPath(worldName);
|
Path root = Utilities.getBackupRootPath(worldName);
|
||||||
int deletedFiles = 0;
|
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
|
if (config.get().maxAge > 0) { // delete files older that configured
|
||||||
final LocalDateTime now = LocalDateTime.now();
|
final LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
deletedFiles += Arrays.stream(root.listFiles())
|
try(Stream<Path> stream = Files.list(root)) {
|
||||||
.filter(Utilities::isValidBackup)// We check if we can get file's creation date so that the next line won't throw an exception
|
deletedFiles += stream
|
||||||
.filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
|
.filter(Utilities::isValidBackup)// We check if we can get file's creation date so that the next line won't throw an exception
|
||||||
.map(f -> deleteFile(f, ctx))
|
.filter(f -> now.toEpochSecond(ZoneOffset.UTC) - Utilities.getFileCreationTime(f).get().toEpochSecond(ZoneOffset.UTC) > config.get().maxAge)
|
||||||
.filter(b -> b).count(); //a bit awkward
|
.mapToInt(f -> deleteFile(f, ctx))
|
||||||
|
.sum();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("An exception occurred while trying to delete old files!", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.get().backupsToKeep > 0 && root.listFiles().length > config.get().backupsToKeep) {
|
int noToKeep = config.get().backupsToKeep > 0 ? config.get().backupsToKeep : Integer.MAX_VALUE;
|
||||||
deletedFiles += Arrays.stream(root.listFiles())
|
long maxSize = config.get().maxSize > 0 ? config.get().maxSize : Long.MAX_VALUE;
|
||||||
.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) {
|
AtomicInteger currentNo = new AtomicInteger(countBackups(root));
|
||||||
deletedFiles += Arrays.stream(root.listFiles())
|
AtomicLong currentSize = new AtomicLong(countSize(root));
|
||||||
|
|
||||||
|
try(Stream<Path> stream = Files.list(root)) {
|
||||||
|
deletedFiles += stream
|
||||||
.filter(Utilities::isValidBackup)
|
.filter(Utilities::isValidBackup)
|
||||||
.sorted(Comparator.comparing(f -> Utilities.getFileCreationTime(f).get()))
|
.sorted(Comparator.comparing(f -> Utilities.getFileCreationTime(f).get()))
|
||||||
.takeWhile(f -> FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize)
|
.takeWhile(f -> (currentNo.get() > noToKeep) || (currentSize.get() > maxSize))
|
||||||
.map(f -> deleteFile(f, ctx))
|
.peek(f -> currentNo.decrementAndGet())
|
||||||
.filter(b -> b).count();
|
.peek(f -> {
|
||||||
|
try {
|
||||||
|
currentSize.addAndGet(Files.size(f));
|
||||||
|
} catch (IOException e) {
|
||||||
|
currentSize.set(0);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.mapToInt(f -> deleteFile(f, ctx))
|
||||||
|
.sum();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("An exception occurred while trying to delete old files!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return deletedFiles;
|
return deletedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean deleteFile(File f, ServerCommandSource ctx) {
|
private static int countBackups(Path path) {
|
||||||
if(Statics.untouchableFile.isEmpty()|| !Statics.untouchableFile.get().equals(f)) {
|
try(Stream<Path> stream = Files.list(path)) {
|
||||||
if(f.delete()) {
|
return (int) stream
|
||||||
log.sendInfoAL(ctx, "Deleting: {}", f.getName());
|
.filter(Utilities::isValidBackup)
|
||||||
return true;
|
.count();
|
||||||
} else {
|
} catch (IOException ignored) {}
|
||||||
log.sendErrorAL(ctx, "Something went wrong while deleting: {}.", f.getName());
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long countSize(Path path) {
|
||||||
|
try(Stream<Path> stream = Files.list(path)) {
|
||||||
|
return (int) stream
|
||||||
|
.filter(Utilities::isValidBackup)
|
||||||
|
.mapToLong(f -> {
|
||||||
|
try {
|
||||||
|
return Files.size(f);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isEmpty(Path path) {
|
||||||
|
if (Files.isDirectory(path)) {
|
||||||
|
try (Stream<Path> entries = Files.list(path)) {
|
||||||
|
return entries.findFirst().isEmpty();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//1 -> ok, 0 -> err
|
||||||
|
private static int 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 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -30,9 +30,10 @@ import net.szum123321.textile_backup.core.create.compressors.tar.ParallelBZip2Co
|
||||||
import net.szum123321.textile_backup.core.create.compressors.tar.ParallelGzipCompressor;
|
import net.szum123321.textile_backup.core.create.compressors.tar.ParallelGzipCompressor;
|
||||||
import org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream;
|
import org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
public class MakeBackupRunnable implements Runnable {
|
public class MakeBackupRunnable implements Runnable {
|
||||||
|
@ -48,33 +49,30 @@ public class MakeBackupRunnable implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Utilities.disableWorldSaving(context.getServer());
|
Utilities.disableWorldSaving(context.server());
|
||||||
Statics.disableWatchdog = true;
|
Statics.disableWatchdog = true;
|
||||||
|
|
||||||
Utilities.updateTMPFSFlag(context.getServer());
|
Utilities.updateTMPFSFlag(context.server());
|
||||||
|
|
||||||
log.sendInfoAL(context, "Starting backup");
|
log.sendInfoAL(context, "Starting backup");
|
||||||
|
|
||||||
File world = Utilities.getWorldFolder(context.getServer());
|
Path world = Utilities.getWorldFolder(context.server());
|
||||||
|
|
||||||
log.trace("Minecraft world is: {}", world);
|
log.trace("Minecraft world is: {}", world);
|
||||||
|
|
||||||
File outFile = Utilities
|
Path outFile = Utilities
|
||||||
.getBackupRootPath(Utilities.getLevelName(context.getServer()))
|
.getBackupRootPath(Utilities.getLevelName(context.server()))
|
||||||
.toPath()
|
.resolve(getFileName());
|
||||||
.resolve(getFileName())
|
|
||||||
.toFile();
|
|
||||||
|
|
||||||
log.trace("Outfile is: {}", outFile);
|
log.trace("Outfile is: {}", outFile);
|
||||||
|
|
||||||
outFile.getParentFile().mkdirs();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outFile.createNewFile();
|
Files.createDirectories(outFile.getParent());
|
||||||
|
Files.createFile(outFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.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)
|
if(context.initiator() == ActionInitiator.Player)
|
||||||
log.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;
|
return;
|
||||||
|
@ -92,10 +90,13 @@ public class MakeBackupRunnable implements Runnable {
|
||||||
|
|
||||||
switch (config.get().format) {
|
switch (config.get().format) {
|
||||||
case ZIP -> {
|
case ZIP -> {
|
||||||
if (coreCount > 1 && !Statics.disableTMPFiles)
|
if (coreCount > 1 && !Statics.disableTMPFiles) {
|
||||||
ParallelZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
ParallelZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||||
else
|
log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
|
||||||
|
} else {
|
||||||
ZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
ZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||||
|
log.trace("Using REGULAR Zip Compressor. Threads: {}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case BZIP2 -> ParallelBZip2Compressor.getInstance().createArchive(world, outFile, context, coreCount);
|
case BZIP2 -> ParallelBZip2Compressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||||
case GZIP -> ParallelGzipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
case GZIP -> ParallelGzipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||||
|
@ -105,26 +106,20 @@ public class MakeBackupRunnable implements Runnable {
|
||||||
}
|
}
|
||||||
}.createArchive(world, outFile, context, coreCount);
|
}.createArchive(world, outFile, context, coreCount);
|
||||||
case TAR -> new AbstractTarArchiver().createArchive(world, outFile, context, coreCount);
|
case TAR -> new AbstractTarArchiver().createArchive(world, outFile, context, coreCount);
|
||||||
default -> {
|
|
||||||
log.warn("Specified compressor ({}) is not supported! Zip will be used instead!", config.get().format);
|
|
||||||
if (context.getInitiator() == ActionInitiator.Player)
|
|
||||||
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()));
|
BackupHelper.executeFileLimit(context.commandSource(), Utilities.getLevelName(context.server()));
|
||||||
|
|
||||||
if(config.get().broadcastBackupDone) {
|
if(config.get().broadcastBackupDone) {
|
||||||
Utilities.notifyPlayers(
|
Utilities.notifyPlayers(
|
||||||
context.getServer(),
|
context.server(),
|
||||||
"Done!"
|
"Done!"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log.sendInfoAL(context, "Done!");
|
log.sendInfoAL(context, "Done!");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Utilities.enableWorldSaving(context.getServer());
|
Utilities.enableWorldSaving(context.server());
|
||||||
Statics.disableWatchdog = false;
|
Statics.disableWatchdog = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +128,7 @@ public class MakeBackupRunnable implements Runnable {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
return Utilities.getDateTimeFormatter().format(now) +
|
return Utilities.getDateTimeFormatter().format(now) +
|
||||||
(context.getComment() != null ? "#" + context.getComment().replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
|
(context.comment() != null ? "#" + context.comment().replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
|
||||||
config.get().format.getCompleteString();
|
config.get().format.getCompleteString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,29 +31,29 @@ import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class AbstractCompressor {
|
public abstract class AbstractCompressor {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||||
|
|
||||||
public void createArchive(File inputFile, File outputFile, BackupContext ctx, int coreLimit) {
|
public void createArchive(Path inputFile, Path outputFile, BackupContext ctx, int coreLimit) {
|
||||||
Instant start = Instant.now();
|
Instant start = Instant.now();
|
||||||
|
|
||||||
try (FileOutputStream outStream = new FileOutputStream(outputFile);
|
try (OutputStream outStream = Files.newOutputStream(outputFile);
|
||||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outStream);
|
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outStream);
|
||||||
OutputStream arc = createArchiveOutputStream(bufferedOutputStream, ctx, coreLimit)) {
|
OutputStream arc = createArchiveOutputStream(bufferedOutputStream, ctx, coreLimit);
|
||||||
|
Stream<Path> fileStream = Files.walk(inputFile)) {
|
||||||
|
|
||||||
Files.walk(inputFile.toPath())
|
fileStream
|
||||||
.filter(path -> !Utilities.isBlacklisted(inputFile.toPath().relativize(path)))
|
.filter(path -> !Utilities.isBlacklisted(inputFile.relativize(path)))
|
||||||
.map(Path::toFile)
|
.filter(Files::isRegularFile).forEach(file -> {
|
||||||
.filter(File::isFile)
|
|
||||||
.forEach(file -> {
|
|
||||||
try {
|
try {
|
||||||
//hopefully one broken file won't spoil the whole archive
|
//hopefully one broken file won't spoil the whole archive
|
||||||
addEntry(file, inputFile.toPath().relativize(file.toPath()).toString(), arc);
|
addEntry(file, inputFile.relativize(file).toString(), arc);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.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.relativize(file).toString(), e);
|
||||||
|
|
||||||
if (ctx.getInitiator() == ActionInitiator.Player)
|
if (ctx.initiator() == ActionInitiator.Player)
|
||||||
log.sendError(ctx, "Something went wrong while compressing files!");
|
log.sendError(ctx, "Something went wrong while compressing files!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -67,14 +67,14 @@ public abstract class AbstractCompressor {
|
||||||
For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems
|
For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems
|
||||||
In case this isn't it here's also the exception itself""", e);
|
In case this isn't it here's also the exception itself""", e);
|
||||||
|
|
||||||
if(ctx.getInitiator() == ActionInitiator.Player) {
|
if(ctx.initiator() == ActionInitiator.Player) {
|
||||||
log.sendError(ctx, "Backup failed. The file is corrupt.");
|
log.sendError(ctx, "Backup failed. The file is corrupt.");
|
||||||
log.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems");
|
log.error("For help see: https://github.com/Szum123321/textile_backup/wiki/ZIP-Problems");
|
||||||
}
|
}
|
||||||
} catch (IOException | InterruptedException | ExecutionException e) {
|
} catch (IOException | InterruptedException | ExecutionException e) {
|
||||||
log.error("An exception occurred!", e);
|
log.error("An exception occurred!", e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if(ctx.getInitiator() == ActionInitiator.Player)
|
if(ctx.initiator() == ActionInitiator.Player)
|
||||||
log.sendError(ctx, "Something went wrong while compressing files!");
|
log.sendError(ctx, "Something went wrong while compressing files!");
|
||||||
} finally {
|
} finally {
|
||||||
close();
|
close();
|
||||||
|
@ -86,13 +86,13 @@ public abstract class AbstractCompressor {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException;
|
protected abstract OutputStream createArchiveOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException;
|
||||||
protected abstract void addEntry(File file, String entryName, OutputStream arc) throws IOException;
|
protected abstract void addEntry(Path file, String entryName, OutputStream arc) throws IOException;
|
||||||
|
|
||||||
protected void finish(OutputStream arc) throws InterruptedException, ExecutionException, IOException {
|
protected void finish(OutputStream arc) throws InterruptedException, ExecutionException, IOException {
|
||||||
;//Basically this function is only needed for the ParallelZipCompressor to write out ParallelScatterZipCreator
|
//Basically this function is only needed for the ParallelZipCompressor to write out ParallelScatterZipCreator
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void close() {
|
protected void close() {
|
||||||
;//Same as above, just for ParallelGzipCompressor to shutdown ExecutorService
|
//Same as above, just for ParallelGzipCompressor to shut down ExecutorService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ import org.apache.commons.compress.archivers.zip.*;
|
||||||
import org.apache.commons.compress.parallel.InputStreamSupplier;
|
import org.apache.commons.compress.parallel.InputStreamSupplier;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
@ -65,13 +67,13 @@ public class ParallelZipCompressor extends ZipCompressor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addEntry(File file, String entryName, OutputStream arc) throws IOException {
|
protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
|
||||||
ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
|
ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
|
||||||
|
|
||||||
if(ZipCompressor.isDotDat(file.getName())) {
|
if(ZipCompressor.isDotDat(file.getFileName().toString())) {
|
||||||
entry.setMethod(ZipArchiveOutputStream.STORED);
|
entry.setMethod(ZipEntry.STORED);
|
||||||
entry.setSize(file.length());
|
entry.setSize(Files.size(file));
|
||||||
entry.setCompressedSize(file.length());
|
entry.setCompressedSize(Files.size(file));
|
||||||
entry.setCrc(getCRC(file));
|
entry.setCrc(getCRC(file));
|
||||||
} else entry.setMethod(ZipEntry.DEFLATED);
|
} else entry.setMethod(ZipEntry.DEFLATED);
|
||||||
|
|
||||||
|
@ -126,12 +128,12 @@ public class ParallelZipCompressor extends ZipCompressor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
record FileInputStreamSupplier(File sourceFile) implements InputStreamSupplier {
|
record FileInputStreamSupplier(Path sourceFile) implements InputStreamSupplier {
|
||||||
public InputStream get() {
|
public InputStream get() {
|
||||||
try {
|
try {
|
||||||
return new FileInputStream(sourceFile);
|
return Files.newInputStream(sourceFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.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.toString(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -27,9 +27,12 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||||
import org.apache.commons.compress.utils.IOUtils;
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
import java.util.zip.Checksum;
|
import java.util.zip.Checksum;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
public class ZipCompressor extends AbstractCompressor {
|
public class ZipCompressor extends AbstractCompressor {
|
||||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||||
|
@ -51,14 +54,14 @@ public class ZipCompressor extends AbstractCompressor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addEntry(File file, String entryName, OutputStream arc) throws IOException {
|
protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
|
||||||
try (FileInputStream fileInputStream = new FileInputStream(file)){
|
try (InputStream fileInputStream = Files.newInputStream(file)){
|
||||||
ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
|
ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
|
||||||
|
|
||||||
if(isDotDat(file.getName())) {
|
if(isDotDat(file.getFileName().toString())) {
|
||||||
entry.setMethod(ZipArchiveOutputStream.STORED);
|
entry.setMethod(ZipEntry.STORED);
|
||||||
entry.setSize(file.length());
|
entry.setSize(Files.size(file));
|
||||||
entry.setCompressedSize(file.length());
|
entry.setCompressedSize(Files.size(file));
|
||||||
entry.setCrc(getCRC(file));
|
entry.setCrc(getCRC(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,15 +79,15 @@ public class ZipCompressor extends AbstractCompressor {
|
||||||
return arr[arr.length - 1].contains("dat"); //includes dat_old
|
return arr[arr.length - 1].contains("dat"); //includes dat_old
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static long getCRC(File file) throws IOException {
|
protected static long getCRC(Path file) throws IOException {
|
||||||
Checksum sum = new CRC32();
|
Checksum sum = new CRC32();
|
||||||
byte[] buffer = new byte[8192];
|
byte[] buffer = new byte[8192];
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
try (InputStream stream = new FileInputStream(file)) {
|
try (InputStream stream = Files.newInputStream(file)) {
|
||||||
while ((len = stream.read(buffer)) != -1) sum.update(buffer, 0, len);
|
while ((len = stream.read(buffer)) != -1) sum.update(buffer, 0, len);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IOException("Error while calculating CRC of: " + file.getAbsolutePath(), e);
|
throw new IOException("Error while calculating CRC of: " + file.toAbsolutePath(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum.getValue();
|
return sum.getValue();
|
||||||
|
|
|
@ -24,10 +24,9 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||||
import org.apache.commons.compress.utils.IOUtils;
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileInputStream;
|
import java.nio.file.Files;
|
||||||
import java.io.IOException;
|
import java.nio.file.Path;
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
public class AbstractTarArchiver extends AbstractCompressor {
|
public class AbstractTarArchiver extends AbstractCompressor {
|
||||||
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||||
|
@ -44,8 +43,8 @@ public class AbstractTarArchiver extends AbstractCompressor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addEntry(File file, String entryName, OutputStream arc) throws IOException {
|
protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
|
||||||
try (FileInputStream fileInputStream = new FileInputStream(file)){
|
try (InputStream fileInputStream = Files.newInputStream(file)){
|
||||||
TarArchiveEntry entry = (TarArchiveEntry)((TarArchiveOutputStream) arc).createArchiveEntry(file, entryName);
|
TarArchiveEntry entry = (TarArchiveEntry)((TarArchiveOutputStream) arc).createArchiveEntry(file, entryName);
|
||||||
((TarArchiveOutputStream)arc).putArchiveEntry(entry);
|
((TarArchiveOutputStream)arc).putArchiveEntry(entry);
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,9 @@ import net.szum123321.textile_backup.core.create.BackupHelper;
|
||||||
import net.szum123321.textile_backup.core.restore.decompressors.GenericTarDecompressor;
|
import net.szum123321.textile_backup.core.restore.decompressors.GenericTarDecompressor;
|
||||||
import net.szum123321.textile_backup.core.restore.decompressors.ZipDecompressor;
|
import net.szum123321.textile_backup.core.restore.decompressors.ZipDecompressor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class RestoreBackupRunnable implements Runnable {
|
public class RestoreBackupRunnable implements Runnable {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||||
|
@ -49,54 +51,57 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
|
|
||||||
log.info("Shutting down server...");
|
log.info("Shutting down server...");
|
||||||
|
|
||||||
ctx.getServer().stop(false);
|
ctx.server().stop(false);
|
||||||
awaitServerShutdown();
|
awaitServerShutdown();
|
||||||
|
|
||||||
if(config.get().backupOldWorlds) {
|
if(config.get().backupOldWorlds) {
|
||||||
BackupHelper.create(
|
BackupHelper.create(
|
||||||
BackupContext.Builder
|
BackupContext.Builder
|
||||||
.newBackupContextBuilder()
|
.newBackupContextBuilder()
|
||||||
.setServer(ctx.getServer())
|
.setServer(ctx.server())
|
||||||
.setInitiator(ActionInitiator.Restore)
|
.setInitiator(ActionInitiator.Restore)
|
||||||
.setComment("Old_World" + (ctx.getComment() != null ? "_" + ctx.getComment() : ""))
|
.setComment("Old_World" + (ctx.comment() != null ? "_" + ctx.comment() : ""))
|
||||||
.build()
|
.build()
|
||||||
).run();
|
).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
File worldFile = Utilities.getWorldFolder(ctx.getServer());
|
Path worldFile = Utilities.getWorldFolder(ctx.server());
|
||||||
|
|
||||||
log.info("Deleting old world...");
|
try {
|
||||||
|
Path tmp = Files.createTempDirectory(
|
||||||
|
worldFile.getParent(),
|
||||||
|
ctx.restoreableFile().getFile().getFileName().toString());
|
||||||
|
|
||||||
if(!deleteDirectory(worldFile))
|
log.info("Starting decompression...");
|
||||||
log.error("Something went wrong while deleting old world!");
|
|
||||||
|
|
||||||
worldFile.mkdirs();
|
if (ctx.restoreableFile().getArchiveFormat() == ConfigPOJO.ArchiveFormat.ZIP)
|
||||||
|
ZipDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
|
||||||
|
else
|
||||||
|
GenericTarDecompressor.decompress(ctx.restoreableFile().getFile(), tmp);
|
||||||
|
|
||||||
log.info("Starting decompression...");
|
log.info("Deleting old world...");
|
||||||
|
|
||||||
if(ctx.getFile().getArchiveFormat() == ConfigPOJO.ArchiveFormat.ZIP)
|
Utilities.deleteDirectory(worldFile);
|
||||||
ZipDecompressor.decompress(ctx.getFile().getFile(), worldFile);
|
|
||||||
else
|
|
||||||
GenericTarDecompressor.decompress(ctx.getFile().getFile(), worldFile);
|
|
||||||
|
|
||||||
if(config.get().deleteOldBackupAfterRestore) {
|
Files.move(tmp, worldFile);
|
||||||
log.info("Deleting old backup");
|
|
||||||
|
|
||||||
if(!ctx.getFile().getFile().delete()) log.info("Something went wrong while deleting old backup");
|
if (config.get().deleteOldBackupAfterRestore) {
|
||||||
|
log.info("Deleting old backup");
|
||||||
|
|
||||||
|
Files.delete(ctx.restoreableFile().getFile());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("An exception occurred while trying to restore a backup!", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
//in case we're playing on client
|
//in case we're playing on client
|
||||||
Statics.globalShutdownBackupFlag.set(true);
|
Statics.globalShutdownBackupFlag.set(true);
|
||||||
|
|
||||||
log.info("Done!");
|
log.info("Done!");
|
||||||
|
|
||||||
//Might solve #37
|
|
||||||
//Idk if it's a good idea...
|
|
||||||
//Runtime.getRuntime().exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void awaitServerShutdown() {
|
private void awaitServerShutdown() {
|
||||||
while(((LivingServer)ctx.getServer()).isAlive()) {
|
while(((LivingServer)ctx.server()).isAlive()) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
@ -104,15 +109,4 @@ public class RestoreBackupRunnable implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean deleteDirectory(File f) {
|
|
||||||
boolean state = true;
|
|
||||||
|
|
||||||
if(f.isDirectory()) {
|
|
||||||
for(File f2 : f.listFiles())
|
|
||||||
state &= deleteDirectory(f2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.delete() && state;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -21,43 +21,15 @@ package net.szum123321.textile_backup.core.restore;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
import net.minecraft.util.Util;
|
|
||||||
import net.szum123321.textile_backup.core.ActionInitiator;
|
import net.szum123321.textile_backup.core.ActionInitiator;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public record RestoreContext(RestoreHelper.RestoreableFile file,
|
public record RestoreContext(RestoreHelper.RestoreableFile restoreableFile,
|
||||||
MinecraftServer server,
|
MinecraftServer server,
|
||||||
@Nullable String comment,
|
@Nullable String comment,
|
||||||
ActionInitiator initiator,
|
ActionInitiator initiator,
|
||||||
ServerCommandSource commandSource) {
|
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 {
|
public static final class Builder {
|
||||||
private RestoreHelper.RestoreableFile file;
|
private RestoreHelper.RestoreableFile file;
|
||||||
private MinecraftServer server;
|
private MinecraftServer server;
|
||||||
|
|
|
@ -28,23 +28,31 @@ import net.szum123321.textile_backup.core.ActionInitiator;
|
||||||
import net.szum123321.textile_backup.core.Utilities;
|
import net.szum123321.textile_backup.core.Utilities;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class RestoreHelper {
|
public class RestoreHelper {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||||
|
|
||||||
public static Optional<RestoreableFile> findFileAndLockIfPresent(LocalDateTime backupTime, MinecraftServer server) {
|
public static Optional<RestoreableFile> findFileAndLockIfPresent(LocalDateTime backupTime, MinecraftServer server) {
|
||||||
File root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
|
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
|
||||||
|
|
||||||
Optional<RestoreableFile> optionalFile = Arrays.stream(root.listFiles())
|
Optional<RestoreableFile> optionalFile;
|
||||||
.map(RestoreableFile::newInstance)
|
try (Stream<Path> stream = Files.list(root)) {
|
||||||
.flatMap(Optional::stream)
|
optionalFile = stream
|
||||||
.filter(rf -> rf.getCreationTime().equals(backupTime))
|
.map(RestoreableFile::newInstance)
|
||||||
.findFirst();
|
.flatMap(Optional::stream)
|
||||||
|
.filter(rf -> rf.getCreationTime().equals(backupTime))
|
||||||
|
.findFirst();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
Statics.untouchableFile = optionalFile.map(RestoreableFile::getFile);
|
Statics.untouchableFile = optionalFile.map(RestoreableFile::getFile);
|
||||||
|
|
||||||
|
@ -52,8 +60,8 @@ public class RestoreHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AwaitThread create(RestoreContext ctx) {
|
public static AwaitThread create(RestoreContext ctx) {
|
||||||
if(ctx.getInitiator() == ActionInitiator.Player)
|
if(ctx.initiator() == ActionInitiator.Player)
|
||||||
log.info("Backup restoration was initiated by: {}", ctx.getCommandSource().getName());
|
log.info("Backup restoration was initiated by: {}", ctx.commandSource().getName());
|
||||||
else
|
else
|
||||||
log.info("Backup restoration was initiated form Server Console");
|
log.info("Backup restoration was initiated form Server Console");
|
||||||
|
|
||||||
|
@ -69,28 +77,32 @@ public class RestoreHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<RestoreableFile> getAvailableBackups(MinecraftServer server) {
|
public static List<RestoreableFile> getAvailableBackups(MinecraftServer server) {
|
||||||
File root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
|
Path root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
|
||||||
|
|
||||||
return Arrays.stream(root.listFiles())
|
try (Stream<Path> stream = Files.list(root)) {
|
||||||
.filter(Utilities::isValidBackup)
|
return stream.filter(Utilities::isValidBackup)
|
||||||
.map(RestoreableFile::newInstance)
|
.map(RestoreableFile::newInstance)
|
||||||
.flatMap(Optional::stream)
|
.flatMap(Optional::stream)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error while listing available backups", e);
|
||||||
|
return new LinkedList<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RestoreableFile implements Comparable<RestoreableFile> {
|
public static class RestoreableFile implements Comparable<RestoreableFile> {
|
||||||
private final File file;
|
private final Path file;
|
||||||
private final ConfigPOJO.ArchiveFormat archiveFormat;
|
private final ConfigPOJO.ArchiveFormat archiveFormat;
|
||||||
private final LocalDateTime creationTime;
|
private final LocalDateTime creationTime;
|
||||||
private final String comment;
|
private final String comment;
|
||||||
|
|
||||||
private RestoreableFile(File file) throws NoSuchElementException {
|
private RestoreableFile(Path file) throws NoSuchElementException {
|
||||||
this.file = file;
|
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 file extension!"));
|
||||||
String extension = archiveFormat.getCompleteString();
|
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 file creation time!"));
|
||||||
|
|
||||||
final String filename = file.getName();
|
final String filename = file.getFileName().toString();
|
||||||
|
|
||||||
if(filename.split("#").length > 1) {
|
if(filename.split("#").length > 1) {
|
||||||
this.comment = filename.split("#")[1].split(extension)[0];
|
this.comment = filename.split("#")[1].split(extension)[0];
|
||||||
|
@ -99,7 +111,7 @@ public class RestoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<RestoreableFile> newInstance(File file) {
|
public static Optional<RestoreableFile> newInstance(Path file) {
|
||||||
try {
|
try {
|
||||||
return Optional.of(new RestoreableFile(file));
|
return Optional.of(new RestoreableFile(file));
|
||||||
} catch (NoSuchElementException ignored) {}
|
} catch (NoSuchElementException ignored) {}
|
||||||
|
@ -107,7 +119,7 @@ public class RestoreHelper {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getFile() {
|
public Path getFile() {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,16 +29,17 @@ import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
public class GenericTarDecompressor {
|
public class GenericTarDecompressor {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||||
|
|
||||||
public static void decompress(File input, File target) {
|
public static void decompress(Path input, Path target) throws IOException {
|
||||||
Instant start = Instant.now();
|
Instant start = Instant.now();
|
||||||
|
|
||||||
try (InputStream fileInputStream = new FileInputStream(input);
|
try (InputStream fileInputStream = Files.newInputStream(input);
|
||||||
InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
|
InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
|
||||||
InputStream compressorInputStream = getCompressorInputStream(bufferedInputStream);
|
InputStream compressorInputStream = getCompressorInputStream(bufferedInputStream);
|
||||||
TarArchiveInputStream archiveInputStream = new TarArchiveInputStream(compressorInputStream)) {
|
TarArchiveInputStream archiveInputStream = new TarArchiveInputStream(compressorInputStream)) {
|
||||||
|
@ -50,27 +51,20 @@ public class GenericTarDecompressor {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = target.toPath().resolve(entry.getName()).toFile();
|
Path file = target.resolve(entry.getName());
|
||||||
|
|
||||||
if(entry.isDirectory()) {
|
if(entry.isDirectory()) {
|
||||||
file.mkdirs();
|
Files.createDirectories(file);
|
||||||
} else {
|
} else {
|
||||||
File parent = file.getParentFile();
|
Files.createDirectories(file.getParent());
|
||||||
|
try (OutputStream outputStream = Files.newOutputStream(file);
|
||||||
if (!parent.isDirectory() && !parent.mkdirs()) {
|
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
|
||||||
log.error("Failed to create {}", parent);
|
IOUtils.copy(archiveInputStream, bufferedOutputStream);
|
||||||
} else {
|
|
||||||
try (OutputStream outputStream = Files.newOutputStream(file.toPath());
|
|
||||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
|
|
||||||
IOUtils.copy(archiveInputStream, bufferedOutputStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("An exception occurred while trying to decompress file: {}", file.getName(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException | CompressorException e) {
|
} catch (CompressorException e) {
|
||||||
log.error("An exception occurred! ", e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
log.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||||
|
|
|
@ -21,43 +21,38 @@ package net.szum123321.textile_backup.core.restore.decompressors;
|
||||||
import net.szum123321.textile_backup.TextileBackup;
|
import net.szum123321.textile_backup.TextileBackup;
|
||||||
import net.szum123321.textile_backup.TextileLogger;
|
import net.szum123321.textile_backup.TextileLogger;
|
||||||
import net.szum123321.textile_backup.core.Utilities;
|
import net.szum123321.textile_backup.core.Utilities;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||||
import org.apache.commons.compress.utils.IOUtils;
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class ZipDecompressor {
|
public class ZipDecompressor {
|
||||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||||
|
|
||||||
public static void decompress(File inputFile, File target) {
|
public static void decompress(Path inputFile, Path target) throws IOException {
|
||||||
Instant start = Instant.now();
|
Instant start = Instant.now();
|
||||||
|
|
||||||
try(ZipFile zipFile = new ZipFile(inputFile)) {
|
try(ZipFile zipFile = new ZipFile(inputFile.toFile())) {
|
||||||
zipFile.getEntries().asIterator().forEachRemaining(entry -> {
|
for (Iterator<ZipArchiveEntry> it = zipFile.getEntries().asIterator(); it.hasNext(); ) {
|
||||||
File file = target.toPath().resolve(entry.getName()).toFile();
|
ZipArchiveEntry entry = it.next();
|
||||||
|
Path file = target.resolve(entry.getName());
|
||||||
|
|
||||||
if(entry.isDirectory()) {
|
if(entry.isDirectory()) {
|
||||||
file.mkdirs();
|
Files.createDirectories(file);
|
||||||
} else {
|
} else {
|
||||||
File parent = file.getParentFile();
|
Files.createDirectories(file.getParent());
|
||||||
|
try (OutputStream outputStream = Files.newOutputStream(file);
|
||||||
if (!parent.isDirectory() && !parent.mkdirs()) {
|
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
|
||||||
log.error("Failed to create {}", parent);
|
IOUtils.copy(zipFile.getInputStream(entry), bufferedOutputStream);
|
||||||
} else {
|
|
||||||
try (OutputStream outputStream = Files.newOutputStream(file.toPath());
|
|
||||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
|
|
||||||
IOUtils.copy(zipFile.getInputStream(entry), bufferedOutputStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("An exception occurred while trying to decompress file: {}", entry.getName(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("An exception occurred! ", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
log.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||||
|
|
Loading…
Reference in New Issue