It works! (At least now...)

2.x-1.16
szymon 2020-08-03 13:45:24 +02:00
parent b81b7d27f8
commit 7c7c4c7493
20 changed files with 411 additions and 73 deletions

View File

@ -6,7 +6,7 @@ yarn_mappings=1.16.1+build.21
loader_version=0.9.0+build.204
#Fabric api
fabric_version=0.15.0+build.379-1.16.1
fabric_version=0.16.2+build.385-1.16.1
# Mod Properties
mod_version = 1.4.0

View File

@ -117,7 +117,7 @@ public class ConfigHandler {
this.extension = extension;
}
public String getExtension() {
public String getString() {
return extension;
}
}

View File

@ -26,17 +26,16 @@ import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.commands.BlacklistCommand;
import net.szum123321.textile_backup.commands.CleanupCommand;
import net.szum123321.textile_backup.commands.StartBackupCommand;
import net.szum123321.textile_backup.commands.WhitelistCommand;
import net.szum123321.textile_backup.core.BackupScheduler;
import net.szum123321.textile_backup.commands.*;
import net.szum123321.textile_backup.core.create.BackupScheduler;
import net.szum123321.textile_backup.core.restore.RestoreHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
public class TextileBackup implements ModInitializer {
public static final String MOD_ID = "textile_backup";
@ -46,6 +45,7 @@ public class TextileBackup implements ModInitializer {
public static final BackupScheduler scheduler = new BackupScheduler();
public static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static final AtomicBoolean globalShutdownBackupFlag = new AtomicBoolean(true);
@Override
public void onInitialize() {
@ -85,6 +85,7 @@ public class TextileBackup implements ModInitializer {
.then(CleanupCommand.register())
.then(StartBackupCommand.register())
.then(WhitelistCommand.register())
.then(RestoreBackupCommand.register())
));
}
}

View File

@ -22,7 +22,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.LiteralText;
import net.szum123321.textile_backup.core.BackupHelper;
import net.szum123321.textile_backup.core.create.BackupHelper;
import net.szum123321.textile_backup.core.Utilities;
public class CleanupCommand {

View File

@ -0,0 +1,70 @@
package net.szum123321.textile_backup.commands;
import com.mojang.brigadier.LiteralMessage;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.restore.RestoreHelper;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CompletableFuture;
public class RestoreBackupCommand {
public static LiteralArgumentBuilder<ServerCommandSource> register() {
return CommandManager.literal("restore")
.then(CommandManager.argument("file", StringArgumentType.greedyString())
.suggests(new FileSuggestionProvider())
.executes(RestoreBackupCommand::execute));
}
private static int execute(CommandContext<ServerCommandSource> ctx) {
String arg = StringArgumentType.getString(ctx, "file");
LocalDateTime dateTime = LocalDateTime.from(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").parse(arg));
if(ctx.getSource().getEntity() != null)
TextileBackup.LOGGER.info("Backup restoration was initiated by: {}", ctx.getSource().getName());
else
TextileBackup.LOGGER.info("Backup restoration was initiated form Server Console");
new Thread(RestoreHelper.create(dateTime, ctx.getSource().getMinecraftServer())).start();
return 1;
}
private static final class FileSuggestionProvider implements SuggestionProvider<ServerCommandSource> {
@Override
public CompletableFuture<Suggestions> getSuggestions(CommandContext<ServerCommandSource> ctx, SuggestionsBuilder builder) throws CommandSyntaxException {
String remaining = builder.getRemaining();
for(RestoreHelper.RestoreableFile file : RestoreHelper.getAvailableBackups(ctx.getSource().getMinecraftServer())) {
String formattedCreationTime = file.getCreationTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
if(formattedCreationTime.startsWith(remaining)) {
if(ctx.getSource().getEntity() != null) { //was typed by player
if(file.getComment() != null) {
builder.suggest(formattedCreationTime, new LiteralMessage("Comment: " + file.getComment()));
} else {
builder.suggest(formattedCreationTime);
}
} else { //was typed from server console
if(file.getComment() != null) {
builder.suggest(file.getCreationTime() + "#" + file.getComment());
} else {
builder.suggest(formattedCreationTime);
}
}
}
}
return builder.buildFuture();
}
}
}

View File

@ -24,8 +24,8 @@ import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.BackupContext;
import net.szum123321.textile_backup.core.BackupHelper;
import net.szum123321.textile_backup.core.create.BackupContext;
import net.szum123321.textile_backup.core.create.BackupHelper;
public class StartBackupCommand {
public static LiteralArgumentBuilder<ServerCommandSource> register() {

View File

@ -1,9 +1,13 @@
package net.szum123321.textile_backup.core;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Formatting;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.dimension.DimensionType;
import net.szum123321.textile_backup.ConfigHandler;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
@ -45,29 +49,63 @@ public class Utilities {
return System.getProperty("os.name").toLowerCase().contains("win");
}
public static Optional<String> getFileExtension(File f) {
String[] parts = f.getName().split("\\.");
public static Optional<ConfigHandler.ArchiveFormat> getFileExtension(File f) {
return getFileExtension(f.getName());
}
public static Optional<ConfigHandler.ArchiveFormat> getFileExtension(String fileName) {
String[] parts = fileName.split("\\.");
switch (parts[parts.length - 1]) {
case "zip":
return Optional.of(ConfigHandler.ArchiveFormat.ZIP.getExtension());
return Optional.of(ConfigHandler.ArchiveFormat.ZIP);
case "bz2":
return Optional.of(ConfigHandler.ArchiveFormat.BZIP2.getExtension());
return Optional.of(ConfigHandler.ArchiveFormat.BZIP2);
case "gz":
return Optional.of(ConfigHandler.ArchiveFormat.GZIP.getExtension());
return Optional.of(ConfigHandler.ArchiveFormat.GZIP);
case "xz":
return Optional.of(ConfigHandler.ArchiveFormat.LZMA.getExtension());
return Optional.of(ConfigHandler.ArchiveFormat.LZMA);
default:
return Optional.empty();
}
}
public static File getBackupRootPath(String worldName) {
File path = new File(TextileBackup.config.path).getAbsoluteFile();
if (TextileBackup.config.perWorldBackup)
path = path.toPath().resolve(worldName).toFile();
if (!path.exists()) {
try {
path.mkdirs();
} catch (Exception e) {
TextileBackup.LOGGER.error("An exception occurred!", e);
return FabricLoader
.getInstance()
.getGameDirectory()
.toPath()
.resolve(TextileBackup.config.path)
.toFile();
}
}
return path;
}
public static File getWorldFolder(MinecraftServer server) {
return ((MinecraftServerSessionAccessor)server)
.getSession()
.getWorldDirectory(RegistryKey.of(Registry.DIMENSION, DimensionType.OVERWORLD_REGISTRY_KEY.getValue()));
}
public static Optional<LocalDateTime> getFileCreationTime(File file) {
LocalDateTime creationTime = null;
if(getFileExtension(file).isPresent()) {
String fileExtension = getFileExtension(file).get();
String fileExtension = getFileExtension(file).get().getString();
try {
creationTime = LocalDateTime.from(

View File

@ -1,4 +1,4 @@
package net.szum123321.textile_backup.core;
package net.szum123321.textile_backup.core.create;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
@ -107,11 +107,12 @@ public class BackupContext {
}
public enum BackupInitiator {
Player ("Player", "by: "),
ServerConsole ("Server Console", "from: "),
Timer ("Timer", "by: "),
Shutdown ("Server Shutdown", "by: "),
Null ("Null (That shouldn't have happened)", "form: ");
Player ("Player", "by"),
ServerConsole ("Server Console", "from"),
Timer ("Timer", "by"),
Shutdown ("Server Shutdown", "by"),
Restore ("Backup Restore", "by"),
Null ("Null (That shouldn't have happened)", "form");
private final String name;
private final String prefix;
@ -126,7 +127,7 @@ public class BackupContext {
}
public String getPrefix() {
return prefix;
return prefix + ": ";
}
}
}

View File

@ -16,11 +16,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.szum123321.textile_backup.core;
package net.szum123321.textile_backup.core.create;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.Utilities;
import org.apache.commons.io.FileUtils;
import java.io.File;
@ -59,7 +59,7 @@ public class BackupHelper {
}
public static int executeFileLimit(ServerCommandSource ctx, String worldName) {
File root = getBackupRootPath(worldName);
File root = Utilities.getBackupRootPath(worldName);
AtomicInteger deletedFiles = new AtomicInteger();
if (root.isDirectory() && root.exists() && root.listFiles() != null) {
@ -127,28 +127,4 @@ public class BackupHelper {
}
private static boolean isFileOk(File f) {return f.exists() && f.isFile(); }
public static File getBackupRootPath(String worldName) {
File path = new File(TextileBackup.config.path).getAbsoluteFile();
if (TextileBackup.config.perWorldBackup)
path = path.toPath().resolve(worldName).toFile();
if (!path.exists()) {
try {
path.mkdirs();
} catch (Exception e) {
TextileBackup.LOGGER.error("An exception occurred!", e);
return FabricLoader
.getInstance()
.getGameDirectory()
.toPath()
.resolve(TextileBackup.config.path)
.toFile();
}
}
return path;
}
}

View File

@ -1,4 +1,4 @@
package net.szum123321.textile_backup.core;
package net.szum123321.textile_backup.core.create;
import net.minecraft.server.MinecraftServer;
import net.szum123321.textile_backup.TextileBackup;

View File

@ -16,19 +16,16 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.szum123321.textile_backup.core;
package net.szum123321.textile_backup.core.create;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.dimension.DimensionType;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.compressors.LZMACompressor;
import net.szum123321.textile_backup.compressors.ParallelBZip2Compressor;
import net.szum123321.textile_backup.compressors.ParallelGzipCompressor;
import net.szum123321.textile_backup.compressors.ParallelZipCompressor;
import net.szum123321.textile_backup.mixin.MinecraftServerSessionAccessor;
import net.szum123321.textile_backup.core.create.compressors.LZMACompressor;
import net.szum123321.textile_backup.core.create.compressors.ParallelBZip2Compressor;
import net.szum123321.textile_backup.core.create.compressors.ParallelGzipCompressor;
import net.szum123321.textile_backup.core.create.compressors.ParallelZipCompressor;
import net.szum123321.textile_backup.core.Utilities;
import java.io.File;
import java.io.IOException;
@ -49,13 +46,11 @@ public class MakeBackupRunnable implements Runnable {
public void run() {
Utilities.info("Starting backup", commandSource);
File world = ((MinecraftServerSessionAccessor)server)
.getSession()
.getWorldDirectory(RegistryKey.of(Registry.DIMENSION, DimensionType.OVERWORLD_REGISTRY_KEY.getValue()));
File world = Utilities.getWorldFolder(server);
TextileBackup.LOGGER.trace("Minecraft world is: {}", world);
File outFile = BackupHelper
File outFile = Utilities
.getBackupRootPath(Utilities.getLevelName(server))
.toPath()
.resolve(getFileName())
@ -118,6 +113,6 @@ public class MakeBackupRunnable implements Runnable {
private String getFileName(){
LocalDateTime now = LocalDateTime.now();
return Utilities.getDateTimeFormatter().format(now) + (comment != null ? "#" + comment.replace("#", "") : "") + TextileBackup.config.format.getExtension();
return Utilities.getDateTimeFormatter().format(now) + (comment != null ? "#" + comment.replace("#", "") : "") + TextileBackup.config.format.getString();
}
}

View File

@ -1,4 +1,4 @@
package net.szum123321.textile_backup.compressors;
package net.szum123321.textile_backup.core.create.compressors;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;

View File

@ -1,4 +1,4 @@
package net.szum123321.textile_backup.compressors;
package net.szum123321.textile_backup.core.create.compressors;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;

View File

@ -1,4 +1,4 @@
package net.szum123321.textile_backup.compressors;
package net.szum123321.textile_backup.core.create.compressors;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;

View File

@ -1,4 +1,4 @@
package net.szum123321.textile_backup.compressors;
package net.szum123321.textile_backup.core.create.compressors;
import net.minecraft.server.command.ServerCommandSource;
import net.szum123321.textile_backup.TextileBackup;

View File

@ -0,0 +1,68 @@
package net.szum123321.textile_backup.core.restore;
import net.minecraft.server.MinecraftServer;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.Utilities;
import net.szum123321.textile_backup.core.restore.decompressors.GenericTarDecompressor;
import net.szum123321.textile_backup.core.restore.decompressors.ZipDecompressor;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import java.io.File;
public class RestoreBackupRunnable implements Runnable {
private final MinecraftServer server;
private final File backupFile;
public RestoreBackupRunnable(MinecraftServer server, File backupFile) {
this.server = server;
this.backupFile = backupFile;
}
@Override
public void run() {
while(server.isRunning()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
TextileBackup.LOGGER.error("Exception occurred!", e);
}
}
File worldFile = Utilities.getWorldFolder(server);
deleteDirectory(worldFile);
worldFile.mkdirs();
switch(Utilities.getFileExtension(backupFile).get()) {
case ZIP:
ZipDecompressor.decompress(backupFile, worldFile);
break;
case GZIP:
GenericTarDecompressor.decompress(backupFile, worldFile, GzipCompressorInputStream.class);
break;
case BZIP2:
GenericTarDecompressor.decompress(backupFile, worldFile, BZip2CompressorInputStream.class);
break;
case LZMA:
GenericTarDecompressor.decompress(backupFile, worldFile, XZCompressorInputStream.class);
break;
}
TextileBackup.LOGGER.info("Done.");
}
private static void deleteDirectory(File f) {
if(f.isDirectory()) {
for(File f2 : f.listFiles())
deleteDirectory(f2);
}
f.delete();
}
}

View File

@ -0,0 +1,84 @@
package net.szum123321.textile_backup.core.restore;
import net.minecraft.server.MinecraftServer;
import net.minecraft.text.LiteralText;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.Utilities;
import net.szum123321.textile_backup.core.create.BackupContext;
import net.szum123321.textile_backup.core.create.BackupHelper;
import java.io.File;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class RestoreHelper {
public static Runnable create(LocalDateTime backupTime, MinecraftServer server) {
server.getPlayerManager().getPlayerList()
.forEach(serverPlayerEntity -> serverPlayerEntity.sendMessage(new LiteralText("Warning! The server is going to shut down in few moments!"), false));
File backupFile = Arrays.stream(Utilities.getBackupRootPath(Utilities.getLevelName(server))
.listFiles())
.filter(file -> Utilities.getFileCreationTime(file).isPresent())
.filter(file -> Utilities.getFileCreationTime(file).get().equals(backupTime))
.findFirst()
.orElseThrow();
TextileBackup.LOGGER.info("Restoring: {}", backupFile.getName());
TextileBackup.globalShutdownBackupFlag.set(false);
BackupHelper.create(
new BackupContext.Builder()
.setServer(server)
.setInitiator(BackupContext.BackupInitiator.Restore)
.setComment("Old_World")
.setSave()
.build()
).run();
TextileBackup.LOGGER.info("Shutting down server...");
server.stop(false);
return new RestoreBackupRunnable(server, backupFile);
}
public static List<RestoreableFile> getAvailableBackups(MinecraftServer server) {
File root = Utilities.getBackupRootPath(Utilities.getLevelName(server));
return Arrays.stream(root.listFiles())
.filter(File::isFile)
.filter(file -> Utilities.getFileExtension(file.getName()).isPresent())
.filter(file -> Utilities.getFileCreationTime(file).isPresent())
.map(RestoreableFile::new)
.collect(Collectors.toList());
}
public static class RestoreableFile {
private final LocalDateTime creationTime;
private final String comment;
protected RestoreableFile(File file) {
String extension = Utilities.getFileExtension(file).get().getString();
this.creationTime = Utilities.getFileCreationTime(file).get();
final String filename = file.getName();
if(filename.split("#").length > 1) {
this.comment = filename.split("#")[1].split(extension)[0];
} else {
this.comment = null;
}
}
public LocalDateTime getCreationTime() {
return creationTime;
}
public String getComment() {
return comment;
}
}
}

View File

@ -0,0 +1,54 @@
package net.szum123321.textile_backup.core.restore.decompressors;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.Utilities;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.CompressorInputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
public class GenericTarDecompressor {
public static void decompress(File archiveFile, File target, Class<? extends CompressorInputStream> DecompressorStream) {
Instant start = Instant.now();
try (FileInputStream inputStream = new FileInputStream(archiveFile);
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
CompressorInputStream compressorInputStream = DecompressorStream.getDeclaredConstructor(InputStream.class).newInstance(bufferedInputStream);
TarArchiveInputStream archiveInputStream = new TarArchiveInputStream(compressorInputStream)) {
TarArchiveEntry entry;
while ((entry = archiveInputStream.getNextTarEntry()) != null) {
if(!archiveInputStream.canReadEntryData(entry))
continue;
File file = target.toPath().resolve(entry.getName()).toFile();
if(entry.isDirectory()) {
file.mkdirs();
} else {
File parent = file.getParentFile();
if (!parent.isDirectory() && !parent.mkdirs())
throw new IOException("Failed to create directory " + parent);
try (OutputStream outputStream = Files.newOutputStream(file.toPath());
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
IOUtils.copy(archiveInputStream, bufferedOutputStream);
} catch (IOException e) {
TextileBackup.LOGGER.error("An exception occurred while trying to compress file: " + file.getName(), e);
}
}
}
} catch (IOException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
TextileBackup.LOGGER.error("An exception occurred! ", e);
}
TextileBackup.LOGGER.info("Decompression took {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
}
}

View File

@ -0,0 +1,51 @@
package net.szum123321.textile_backup.core.restore.decompressors;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.Utilities;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
public class ZipDecompressor {
public static void decompress(File archiveFile, File target) {
Instant start = Instant.now();
try (FileInputStream inputStream = new FileInputStream(archiveFile);
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream((bufferedInputStream))) {
ZipArchiveEntry entry;
while ((entry = zipInputStream.getNextZipEntry()) != null) {
if(!zipInputStream.canReadEntryData(entry))
continue;
File file = target.toPath().resolve(entry.getName()).toFile();
if(entry.isDirectory()) {
file.mkdirs();
} else {
File parent = file.getParentFile();
if (!parent.isDirectory() && !parent.mkdirs())
throw new IOException("Failed to create directory " + parent);
try (OutputStream outputStream = Files.newOutputStream(file.toPath());
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream)) {
IOUtils.copy(zipInputStream, bufferedOutputStream);
} catch (IOException e) {
TextileBackup.LOGGER.error("An exception occurred while trying to compress file: " + file.getName(), e);
}
}
}
} catch (IOException e) {
TextileBackup.LOGGER.error("An exception occurred! ", e);
}
TextileBackup.LOGGER.info("Compression took: {} seconds.", Utilities.formatDuration(Duration.between(start, Instant.now())));
}
}

View File

@ -20,8 +20,8 @@ package net.szum123321.textile_backup.mixin;
import net.minecraft.server.MinecraftServer;
import net.szum123321.textile_backup.TextileBackup;
import net.szum123321.textile_backup.core.BackupContext;
import net.szum123321.textile_backup.core.BackupHelper;
import net.szum123321.textile_backup.core.create.BackupContext;
import net.szum123321.textile_backup.core.create.BackupHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -31,7 +31,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class MinecraftServerMixin {
@Inject(method = "shutdown", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/server/MinecraftServer;save(ZZZ)Z"))
public void onFinalWorldSave(CallbackInfo ci) {
if (TextileBackup.config.shutdownBackup)
if (TextileBackup.config.shutdownBackup && TextileBackup.globalShutdownBackupFlag.get())
TextileBackup.executorService.submit(
BackupHelper.create(
new BackupContext.Builder()