commit
267776789d
|
@ -6,7 +6,7 @@ yarn_mappings=1.19+build.4
|
|||
loader_version=0.14.8
|
||||
|
||||
#Fabric api
|
||||
fabric_version=0.56.0+1.19
|
||||
fabric_version=0.56.1+1.19
|
||||
|
||||
#Cloth Config
|
||||
cloth_version=7.0.72
|
||||
|
@ -15,12 +15,12 @@ cloth_version=7.0.72
|
|||
modmenu_version=4.0.0
|
||||
|
||||
#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
|
||||
pgzip_commit_hash=af5f5c297e735f3f2df7aa4eb0e19a5810b8aff6
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 2.3.1
|
||||
mod_version = 2.4.0
|
||||
maven_group = net.szum123321
|
||||
archives_base_name = textile_backup
|
|
@ -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<File> untouchableFile = Optional.empty();
|
||||
public static Optional<Path> untouchableFile = Optional.empty();
|
||||
public static boolean disableTMPFiles = false;
|
||||
}
|
||||
|
|
|
@ -85,12 +85,16 @@ public class TextileLogger {
|
|||
log(Level.ERROR, msg, data);
|
||||
}
|
||||
|
||||
void error(String message, Throwable throwable) {
|
||||
logger.error(prefix + message, throwable);
|
||||
}
|
||||
|
||||
public void fatal(String msg, Object... data) {
|
||||
log(Level.FATAL, msg, data);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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) {
|
||||
sendInfo(context.getCommandSource(), msg, args);
|
||||
sendInfo(context.commandSource(), msg, 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) {
|
||||
sendError(context.getCommandSource(), msg, args);
|
||||
sendError(context.commandSource(), msg, 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) {
|
||||
sendInfoAL(context.getCommandSource(), msg, args);
|
||||
sendInfoAL(context.commandSource(), msg, 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) {
|
||||
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.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<File> optionalFile = Arrays.stream(root.listFiles())
|
||||
.filter(Utilities::isValidBackup)
|
||||
.filter(file -> Utilities.getFileCreationTime(file).orElse(LocalDateTime.MIN).equals(dateTime))
|
||||
.findFirst();
|
||||
try (Stream<Path> 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");
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ public class RestoreBackupCommand {
|
|||
Optional<RestoreHelper.RestoreableFile> 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));
|
||||
|
||||
|
|
|
@ -32,18 +32,20 @@ import net.szum123321.textile_backup.config.ConfigPOJO;
|
|||
import net.szum123321.textile_backup.Statics;
|
||||
import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.file.SimplePathVisitor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Utilities {
|
||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||
|
@ -60,28 +62,47 @@ 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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 +151,11 @@ public class Utilities {
|
|||
.findAny();
|
||||
}
|
||||
|
||||
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(File f) {
|
||||
return getArchiveExtension(f.getName());
|
||||
public static Optional<ConfigPOJO.ArchiveFormat> getArchiveExtension(Path f) {
|
||||
return getArchiveExtension(f.getFileName().toString());
|
||||
}
|
||||
|
||||
public static Optional<LocalDateTime> getFileCreationTime(File file) {
|
||||
public static Optional<LocalDateTime> getFileCreationTime(Path file) {
|
||||
LocalDateTime creationTime = null;
|
||||
|
||||
if(getArchiveExtension(file).isPresent()) {
|
||||
|
@ -143,7 +164,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 +173,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 +181,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 +190,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 +198,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);
|
||||
}
|
||||
|
|
|
@ -33,18 +33,6 @@ public record BackupContext(@NotNull MinecraftServer server,
|
|||
boolean save,
|
||||
String comment) {
|
||||
|
||||
public MinecraftServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public ServerCommandSource getCommandSource() {
|
||||
return commandSource;
|
||||
}
|
||||
|
||||
public ActionInitiator getInitiator() {
|
||||
return initiator;
|
||||
}
|
||||
|
||||
public boolean startedByPlayer() {
|
||||
return initiator == ActionInitiator.Player;
|
||||
}
|
||||
|
@ -53,14 +41,6 @@ public record BackupContext(@NotNull MinecraftServer server,
|
|||
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 {
|
||||
private MinecraftServer server;
|
||||
private ServerCommandSource commandSource;
|
||||
|
|
|
@ -24,13 +24,16 @@ 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.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.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class BackupHelper {
|
||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||
|
@ -38,7 +41,7 @@ public class BackupHelper {
|
|||
|
||||
public static Runnable create(BackupContext ctx) {
|
||||
if(config.get().broadcastBackupStart) {
|
||||
Utilities.notifyPlayers(ctx.getServer(),
|
||||
Utilities.notifyPlayers(ctx.server(),
|
||||
"Warning! Server backup will begin shortly. You may experience some lag."
|
||||
);
|
||||
} else {
|
||||
|
@ -49,12 +52,12 @@ public class BackupHelper {
|
|||
|
||||
builder.append("Backup started ");
|
||||
|
||||
builder.append(ctx.getInitiator().getPrefix());
|
||||
builder.append(ctx.initiator().getPrefix());
|
||||
|
||||
if(ctx.startedByPlayer())
|
||||
builder.append(ctx.getCommandSource().getDisplayName().getString());
|
||||
builder.append(ctx.commandSource().getDisplayName().getString());
|
||||
else
|
||||
builder.append(ctx.getInitiator().getName());
|
||||
builder.append(ctx.initiator().getName());
|
||||
|
||||
builder.append(" on: ");
|
||||
builder.append(Utilities.getDateTimeFormatter().format(LocalDateTime.now()));
|
||||
|
@ -64,10 +67,10 @@ public class BackupHelper {
|
|||
if (ctx.shouldSave()) {
|
||||
log.sendInfoAL(ctx, "Saving server...");
|
||||
|
||||
ctx.getServer().getPlayerManager().saveAllPlayerData();
|
||||
ctx.server().getPlayerManager().saveAllPlayerData();
|
||||
|
||||
try {
|
||||
ctx.getServer().save(false, true, true);
|
||||
ctx.server().save(false, true, true);
|
||||
} catch (Exception e) {
|
||||
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) {
|
||||
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<Path> stream = Files.list(root)) {
|
||||
deletedFiles += stream
|
||||
.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)
|
||||
.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) {
|
||||
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();
|
||||
}
|
||||
int noToKeep = config.get().backupsToKeep > 0 ? config.get().backupsToKeep : Integer.MAX_VALUE;
|
||||
long maxSize = config.get().maxSize > 0 ? config.get().maxSize : Long.MAX_VALUE;
|
||||
|
||||
if (config.get().maxSize > 0 && FileUtils.sizeOfDirectory(root) / 1024 > config.get().maxSize) {
|
||||
deletedFiles += Arrays.stream(root.listFiles())
|
||||
AtomicInteger currentNo = new AtomicInteger(countBackups(root));
|
||||
AtomicLong currentSize = new AtomicLong(countSize(root));
|
||||
|
||||
try(Stream<Path> stream = Files.list(root)) {
|
||||
deletedFiles += stream
|
||||
.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();
|
||||
.takeWhile(f -> (currentNo.get() > noToKeep) || (currentSize.get() > maxSize))
|
||||
.peek(f -> currentNo.decrementAndGet())
|
||||
.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;
|
||||
}
|
||||
|
||||
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 int countBackups(Path path) {
|
||||
try(Stream<Path> stream = Files.list(path)) {
|
||||
return (int) stream
|
||||
.filter(Utilities::isValidBackup)
|
||||
.count();
|
||||
} catch (IOException ignored) {}
|
||||
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;
|
||||
}
|
||||
|
||||
//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 org.apache.commons.compress.compressors.lzma.LZMACompressorOutputStream;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class MakeBackupRunnable implements Runnable {
|
||||
|
@ -48,33 +49,30 @@ public class MakeBackupRunnable implements Runnable {
|
|||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Utilities.disableWorldSaving(context.getServer());
|
||||
Utilities.disableWorldSaving(context.server());
|
||||
Statics.disableWatchdog = true;
|
||||
|
||||
Utilities.updateTMPFSFlag(context.getServer());
|
||||
Utilities.updateTMPFSFlag(context.server());
|
||||
|
||||
log.sendInfoAL(context, "Starting backup");
|
||||
|
||||
File world = Utilities.getWorldFolder(context.getServer());
|
||||
Path world = Utilities.getWorldFolder(context.server());
|
||||
|
||||
log.trace("Minecraft world is: {}", world);
|
||||
|
||||
File outFile = Utilities
|
||||
.getBackupRootPath(Utilities.getLevelName(context.getServer()))
|
||||
.toPath()
|
||||
.resolve(getFileName())
|
||||
.toFile();
|
||||
Path outFile = Utilities
|
||||
.getBackupRootPath(Utilities.getLevelName(context.server()))
|
||||
.resolve(getFileName());
|
||||
|
||||
log.trace("Outfile is: {}", outFile);
|
||||
|
||||
outFile.getParentFile().mkdirs();
|
||||
|
||||
try {
|
||||
outFile.createNewFile();
|
||||
Files.createDirectories(outFile.getParent());
|
||||
Files.createFile(outFile);
|
||||
} catch (IOException 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!");
|
||||
|
||||
return;
|
||||
|
@ -92,10 +90,13 @@ public class MakeBackupRunnable implements Runnable {
|
|||
|
||||
switch (config.get().format) {
|
||||
case ZIP -> {
|
||||
if (coreCount > 1 && !Statics.disableTMPFiles)
|
||||
if (coreCount > 1 && !Statics.disableTMPFiles) {
|
||||
ParallelZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||
else
|
||||
log.trace("Using PARALLEL Zip Compressor. Threads: {}", coreCount);
|
||||
} else {
|
||||
ZipCompressor.getInstance().createArchive(world, outFile, context, coreCount);
|
||||
log.trace("Using REGULAR Zip Compressor. Threads: {}");
|
||||
}
|
||||
}
|
||||
case BZIP2 -> ParallelBZip2Compressor.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);
|
||||
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) {
|
||||
Utilities.notifyPlayers(
|
||||
context.getServer(),
|
||||
context.server(),
|
||||
"Done!"
|
||||
);
|
||||
} else {
|
||||
log.sendInfoAL(context, "Done!");
|
||||
}
|
||||
} finally {
|
||||
Utilities.enableWorldSaving(context.getServer());
|
||||
Utilities.enableWorldSaving(context.server());
|
||||
Statics.disableWatchdog = false;
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +128,7 @@ public class MakeBackupRunnable implements Runnable {
|
|||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
return Utilities.getDateTimeFormatter().format(now) +
|
||||
(context.getComment() != null ? "#" + context.getComment().replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
|
||||
(context.comment() != null ? "#" + context.comment().replaceAll("[\\\\/:*?\"<>|#]", "") : "") +
|
||||
config.get().format.getCompleteString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,29 +31,29 @@ import java.nio.file.Path;
|
|||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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) {
|
||||
public void createArchive(Path inputFile, Path outputFile, BackupContext ctx, int coreLimit) {
|
||||
Instant start = Instant.now();
|
||||
|
||||
try (FileOutputStream outStream = new FileOutputStream(outputFile);
|
||||
try (OutputStream outStream = Files.newOutputStream(outputFile);
|
||||
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())
|
||||
.filter(path -> !Utilities.isBlacklisted(inputFile.toPath().relativize(path)))
|
||||
.map(Path::toFile)
|
||||
.filter(File::isFile)
|
||||
.forEach(file -> {
|
||||
fileStream
|
||||
.filter(path -> !Utilities.isBlacklisted(inputFile.relativize(path)))
|
||||
.filter(Files::isRegularFile).forEach(file -> {
|
||||
try {
|
||||
//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) {
|
||||
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!");
|
||||
}
|
||||
});
|
||||
|
@ -67,14 +67,14 @@ public abstract class AbstractCompressor {
|
|||
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);
|
||||
|
||||
if(ctx.getInitiator() == ActionInitiator.Player) {
|
||||
if(ctx.initiator() == ActionInitiator.Player) {
|
||||
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) {
|
||||
log.error("An exception occurred!", e);
|
||||
} catch (Exception e) {
|
||||
if(ctx.getInitiator() == ActionInitiator.Player)
|
||||
if(ctx.initiator() == ActionInitiator.Player)
|
||||
log.sendError(ctx, "Something went wrong while compressing files!");
|
||||
} finally {
|
||||
close();
|
||||
|
@ -86,13 +86,13 @@ public abstract class AbstractCompressor {
|
|||
}
|
||||
|
||||
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 {
|
||||
;//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() {
|
||||
;//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 java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
@ -65,13 +67,13 @@ public class ParallelZipCompressor extends ZipCompressor {
|
|||
}
|
||||
|
||||
@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);
|
||||
|
||||
if(ZipCompressor.isDotDat(file.getName())) {
|
||||
entry.setMethod(ZipArchiveOutputStream.STORED);
|
||||
entry.setSize(file.length());
|
||||
entry.setCompressedSize(file.length());
|
||||
if(ZipCompressor.isDotDat(file.getFileName().toString())) {
|
||||
entry.setMethod(ZipEntry.STORED);
|
||||
entry.setSize(Files.size(file));
|
||||
entry.setCompressedSize(Files.size(file));
|
||||
entry.setCrc(getCRC(file));
|
||||
} 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() {
|
||||
try {
|
||||
return new FileInputStream(sourceFile);
|
||||
return Files.newInputStream(sourceFile);
|
||||
} 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;
|
||||
|
|
|
@ -27,9 +27,12 @@ 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.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Checksum;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class ZipCompressor extends AbstractCompressor {
|
||||
private final static ConfigHelper config = ConfigHelper.INSTANCE;
|
||||
|
@ -51,14 +54,14 @@ public class ZipCompressor extends AbstractCompressor {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void addEntry(File file, String entryName, OutputStream arc) throws IOException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(file)){
|
||||
protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
|
||||
try (InputStream fileInputStream = Files.newInputStream(file)){
|
||||
ZipArchiveEntry entry = (ZipArchiveEntry)((ZipArchiveOutputStream)arc).createArchiveEntry(file, entryName);
|
||||
|
||||
if(isDotDat(file.getName())) {
|
||||
entry.setMethod(ZipArchiveOutputStream.STORED);
|
||||
entry.setSize(file.length());
|
||||
entry.setCompressedSize(file.length());
|
||||
if(isDotDat(file.getFileName().toString())) {
|
||||
entry.setMethod(ZipEntry.STORED);
|
||||
entry.setSize(Files.size(file));
|
||||
entry.setCompressedSize(Files.size(file));
|
||||
entry.setCrc(getCRC(file));
|
||||
}
|
||||
|
||||
|
@ -76,15 +79,15 @@ public class ZipCompressor extends AbstractCompressor {
|
|||
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();
|
||||
byte[] buffer = new byte[8192];
|
||||
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);
|
||||
} 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();
|
||||
|
|
|
@ -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.utils.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class AbstractTarArchiver extends AbstractCompressor {
|
||||
protected OutputStream getCompressorOutputStream(OutputStream stream, BackupContext ctx, int coreLimit) throws IOException {
|
||||
|
@ -44,8 +43,8 @@ public class AbstractTarArchiver extends AbstractCompressor {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void addEntry(File file, String entryName, OutputStream arc) throws IOException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(file)){
|
||||
protected void addEntry(Path file, String entryName, OutputStream arc) throws IOException {
|
||||
try (InputStream fileInputStream = Files.newInputStream(file)){
|
||||
TarArchiveEntry entry = (TarArchiveEntry)((TarArchiveOutputStream) arc).createArchiveEntry(file, entryName);
|
||||
((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.ZipDecompressor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class RestoreBackupRunnable implements Runnable {
|
||||
private final static TextileLogger log = new TextileLogger(TextileBackup.MOD_NAME);
|
||||
|
@ -49,54 +51,57 @@ public class RestoreBackupRunnable implements Runnable {
|
|||
|
||||
log.info("Shutting down server...");
|
||||
|
||||
ctx.getServer().stop(false);
|
||||
ctx.server().stop(false);
|
||||
awaitServerShutdown();
|
||||
|
||||
if(config.get().backupOldWorlds) {
|
||||
BackupHelper.create(
|
||||
BackupContext.Builder
|
||||
.newBackupContextBuilder()
|
||||
.setServer(ctx.getServer())
|
||||
.setServer(ctx.server())
|
||||
.setInitiator(ActionInitiator.Restore)
|
||||
.setComment("Old_World" + (ctx.getComment() != null ? "_" + ctx.getComment() : ""))
|
||||
.setComment("Old_World" + (ctx.comment() != null ? "_" + ctx.comment() : ""))
|
||||
.build()
|
||||
).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.error("Something went wrong while deleting old world!");
|
||||
log.info("Starting decompression...");
|
||||
|
||||
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)
|
||||
ZipDecompressor.decompress(ctx.getFile().getFile(), worldFile);
|
||||
else
|
||||
GenericTarDecompressor.decompress(ctx.getFile().getFile(), worldFile);
|
||||
Utilities.deleteDirectory(worldFile);
|
||||
|
||||
if(config.get().deleteOldBackupAfterRestore) {
|
||||
log.info("Deleting old backup");
|
||||
Files.move(tmp, worldFile);
|
||||
|
||||
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
|
||||
Statics.globalShutdownBackupFlag.set(true);
|
||||
|
||||
log.info("Done!");
|
||||
|
||||
//Might solve #37
|
||||
//Idk if it's a good idea...
|
||||
//Runtime.getRuntime().exit(0);
|
||||
}
|
||||
|
||||
private void awaitServerShutdown() {
|
||||
while(((LivingServer)ctx.getServer()).isAlive()) {
|
||||
while(((LivingServer)ctx.server()).isAlive()) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} 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.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;
|
||||
|
|
|
@ -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<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())
|
||||
.map(RestoreableFile::newInstance)
|
||||
.flatMap(Optional::stream)
|
||||
.filter(rf -> rf.getCreationTime().equals(backupTime))
|
||||
.findFirst();
|
||||
Optional<RestoreableFile> optionalFile;
|
||||
try (Stream<Path> 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<RestoreableFile> 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<Path> 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<RestoreableFile> {
|
||||
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!"));
|
||||
String extension = archiveFormat.getCompleteString();
|
||||
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) {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,16 +29,17 @@ import org.apache.commons.compress.utils.IOUtils;
|
|||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
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) {
|
||||
public static void decompress(Path input, Path target) throws IOException {
|
||||
Instant start = Instant.now();
|
||||
|
||||
try (InputStream fileInputStream = new FileInputStream(input);
|
||||
try (InputStream fileInputStream = Files.newInputStream(input);
|
||||
InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
|
||||
InputStream compressorInputStream = getCompressorInputStream(bufferedInputStream);
|
||||
TarArchiveInputStream archiveInputStream = new TarArchiveInputStream(compressorInputStream)) {
|
||||
|
@ -50,27 +51,20 @@ public class GenericTarDecompressor {
|
|||
continue;
|
||||
}
|
||||
|
||||
File file = target.toPath().resolve(entry.getName()).toFile();
|
||||
Path file = target.resolve(entry.getName());
|
||||
|
||||
if(entry.isDirectory()) {
|
||||
file.mkdirs();
|
||||
Files.createDirectories(file);
|
||||
} else {
|
||||
File parent = file.getParentFile();
|
||||
|
||||
if (!parent.isDirectory() && !parent.mkdirs()) {
|
||||
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) {
|
||||
log.error("An exception occurred while trying to decompress file: {}", file.getName(), e);
|
||||
}
|
||||
Files.createDirectories(file.getParent());
|
||||
try (OutputStream outputStream = Files.newOutputStream(file);
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
|
||||
IOUtils.copy(archiveInputStream, bufferedOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | CompressorException e) {
|
||||
log.error("An exception occurred! ", e);
|
||||
} catch (CompressorException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
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.TextileLogger;
|
||||
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.utils.IOUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ZipDecompressor {
|
||||
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();
|
||||
|
||||
try(ZipFile zipFile = new ZipFile(inputFile)) {
|
||||
zipFile.getEntries().asIterator().forEachRemaining(entry -> {
|
||||
File file = target.toPath().resolve(entry.getName()).toFile();
|
||||
try(ZipFile zipFile = new ZipFile(inputFile.toFile())) {
|
||||
for (Iterator<ZipArchiveEntry> it = zipFile.getEntries().asIterator(); it.hasNext(); ) {
|
||||
ZipArchiveEntry entry = it.next();
|
||||
Path file = target.resolve(entry.getName());
|
||||
|
||||
if(entry.isDirectory()) {
|
||||
file.mkdirs();
|
||||
Files.createDirectories(file);
|
||||
} else {
|
||||
File parent = file.getParentFile();
|
||||
|
||||
if (!parent.isDirectory() && !parent.mkdirs()) {
|
||||
log.error("Failed to create {}", parent);
|
||||
} 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);
|
||||
}
|
||||
Files.createDirectories(file.getParent());
|
||||
try (OutputStream outputStream = Files.newOutputStream(file);
|
||||
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
|
||||
IOUtils.copy(zipFile.getInputStream(entry), bufferedOutputStream);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
log.error("An exception occurred! ", e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Decompression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
|
||||
|
|
Loading…
Reference in New Issue