commit
28641383f9
|
@ -9,6 +9,6 @@ loader_version=0.8.9+build.203
|
||||||
fabric_version=0.14.1+build.372-1.16
|
fabric_version=0.14.1+build.372-1.16
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 1.2.4
|
mod_version = 1.3.0
|
||||||
maven_group = net.szum123321
|
maven_group = net.szum123321
|
||||||
archives_base_name = textile_backup
|
archives_base_name = textile_backup
|
|
@ -21,18 +21,21 @@ package net.szum123321.textile_backup;
|
||||||
import blue.endless.jankson.Comment;
|
import blue.endless.jankson.Comment;
|
||||||
import io.github.cottonmc.cotton.config.annotations.ConfigFile;
|
import io.github.cottonmc.cotton.config.annotations.ConfigFile;
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ConfigFile(name = TextileBackup.MOD_ID)
|
@ConfigFile(name = TextileBackup.MOD_ID)
|
||||||
public class ConfigHandler {
|
public class ConfigHandler {
|
||||||
@Comment("\nTime between automatic backups in seconds\n")
|
@Comment("\nTime between automatic backups in seconds\n" +
|
||||||
|
"When set to 0 backups will not be performed automatically\n")
|
||||||
public long backupInterval = 3600;
|
public long backupInterval = 3600;
|
||||||
|
|
||||||
@Comment("\nShould backups be done even if there are no players?\n")
|
@Comment("\nShould backups be done even if there are no players?\n")
|
||||||
public boolean doBackupsOnEmptyServer = false;
|
public boolean doBackupsOnEmptyServer = false;
|
||||||
|
|
||||||
@Comment("\nShould backup be made on server shutdown\n")
|
@Comment("\nShould backup be made on server shutdown?\n")
|
||||||
public boolean shutdownBackup = true;
|
public boolean shutdownBackup = true;
|
||||||
|
|
||||||
@Comment("\nA path to backup folder\n")
|
@Comment("\nA path to backup folder\n")
|
||||||
|
@ -84,10 +87,24 @@ public class ConfigHandler {
|
||||||
public Set<String> playerBlacklist = new HashSet<>();
|
public Set<String> playerBlacklist = new HashSet<>();
|
||||||
|
|
||||||
@Comment("\nFormat of date&time used to name backup files.\n" +
|
@Comment("\nFormat of date&time used to name backup files.\n" +
|
||||||
"Remember not to use '#' symbol and any other character that is not allowed by your operating system such as:\n" +
|
"Remember not to use '#' symbol or any other character that is not allowed by your operating system such as:\n" +
|
||||||
"':', '\\', etc\n")
|
"':', '\\', etc...\n" +
|
||||||
|
"For more info: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html\n")
|
||||||
public String dateTimeFormat = "dd.MM.yyyy_HH-mm-ss";
|
public String dateTimeFormat = "dd.MM.yyyy_HH-mm-ss";
|
||||||
|
|
||||||
|
public Optional<String> sanitize() {
|
||||||
|
if(compressionCoreCountLimit > Runtime.getRuntime().availableProcessors())
|
||||||
|
return Optional.of("compressionCoreCountLimit is too big! Your system only has: " + Runtime.getRuntime().availableProcessors() + " cores!");
|
||||||
|
|
||||||
|
try {
|
||||||
|
DateTimeFormatter.ofPattern(dateTimeFormat);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Optional.of("dateTimeFormat is wrong!\n" + e.getMessage() + "\n See: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
public enum ArchiveFormat {
|
public enum ArchiveFormat {
|
||||||
ZIP(".zip"),
|
ZIP(".zip"),
|
||||||
GZIP(".tar.gz"),
|
GZIP(".tar.gz"),
|
||||||
|
|
|
@ -23,28 +23,46 @@ import io.github.cottonmc.cotton.config.ConfigManager;
|
||||||
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
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.minecraft.server.command.ServerCommandSource;
|
||||||
import net.szum123321.textile_backup.commands.BlacklistCommand;
|
import net.szum123321.textile_backup.commands.BlacklistCommand;
|
||||||
import net.szum123321.textile_backup.commands.CleanupCommand;
|
import net.szum123321.textile_backup.commands.CleanupCommand;
|
||||||
import net.szum123321.textile_backup.commands.StartBackupCommand;
|
import net.szum123321.textile_backup.commands.StartBackupCommand;
|
||||||
import net.szum123321.textile_backup.commands.WhitelistCommand;
|
import net.szum123321.textile_backup.commands.WhitelistCommand;
|
||||||
|
import net.szum123321.textile_backup.core.BackupScheduler;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class TextileBackup implements ModInitializer {
|
public class TextileBackup implements ModInitializer {
|
||||||
public static final String MOD_ID = "textile_backup";
|
public static final String MOD_ID = "textile_backup";
|
||||||
public static final Logger LOGGER = LogManager.getFormatterLogger("Textile Backup");
|
public static final Logger LOGGER = LogManager.getFormatterLogger("Textile Backup");
|
||||||
|
|
||||||
public static ConfigHandler config;
|
public static ConfigHandler config;
|
||||||
|
|
||||||
|
public static final BackupScheduler scheduler = new BackupScheduler();
|
||||||
|
public static final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
config = ConfigManager.loadConfig(ConfigHandler.class);
|
config = ConfigManager.loadConfig(ConfigHandler.class);
|
||||||
|
|
||||||
registerCommands();
|
Optional<String> errorMessage = config.sanitize();
|
||||||
|
|
||||||
|
if(errorMessage.isPresent()) {
|
||||||
|
LOGGER.fatal("TextileBackup config file has wrong settings! \n" + errorMessage.get());
|
||||||
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCommands(){
|
if(TextileBackup.config.backupInterval > 0)
|
||||||
|
ServerTickEvents.END_SERVER_TICK.register(scheduler::tick);
|
||||||
|
|
||||||
|
ServerLifecycleEvents.SERVER_STOPPED.register(ignored -> executorService.shutdown());
|
||||||
|
|
||||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(
|
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> dispatcher.register(
|
||||||
LiteralArgumentBuilder.<ServerCommandSource>literal("backup")
|
LiteralArgumentBuilder.<ServerCommandSource>literal("backup")
|
||||||
.requires((ctx) -> {
|
.requires((ctx) -> {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
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.core.BackupHelper;
|
import net.szum123321.textile_backup.core.BackupHelper;
|
||||||
|
|
||||||
public class StartBackupCommand {
|
public class StartBackupCommand {
|
||||||
|
@ -34,13 +35,27 @@ public class StartBackupCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int executeWithComment(CommandContext<ServerCommandSource> source) {
|
private static int executeWithComment(CommandContext<ServerCommandSource> source) {
|
||||||
BackupHelper.create(source.getSource().getMinecraftServer(), source.getSource(), true, StringArgumentType.getString(source, "comment").replace("#", ""));
|
TextileBackup.executorService.submit(
|
||||||
|
BackupHelper.create(
|
||||||
|
source.getSource().getMinecraftServer(),
|
||||||
|
source.getSource(),
|
||||||
|
true,
|
||||||
|
StringArgumentType.getString(source, "comment").replace("#", "")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int execute(ServerCommandSource source){
|
private static int execute(ServerCommandSource source){
|
||||||
BackupHelper.create(source.getMinecraftServer(), source,true, null);
|
TextileBackup.executorService.submit(
|
||||||
|
BackupHelper.create(
|
||||||
|
source.getMinecraftServer(),
|
||||||
|
source,
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import java.util.Comparator;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class BackupHelper {
|
public class BackupHelper {
|
||||||
public static Thread create(MinecraftServer server, ServerCommandSource ctx, boolean save, String comment) {
|
public static Runnable create(MinecraftServer server, ServerCommandSource ctx, boolean save, String comment) {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
@ -53,11 +53,7 @@ public class BackupHelper {
|
||||||
if (save)
|
if (save)
|
||||||
server.save(true, true, false);
|
server.save(true, true, false);
|
||||||
|
|
||||||
Thread thread = new Thread(new MakeBackupThread(server, ctx, comment));
|
return new MakeBackupRunnable(server, ctx, comment);
|
||||||
|
|
||||||
thread.start();
|
|
||||||
|
|
||||||
return thread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void executeFileLimit(ServerCommandSource ctx, String worldName) {
|
public static void executeFileLimit(ServerCommandSource ctx, String worldName) {
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package net.szum123321.textile_backup.core;
|
||||||
|
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.szum123321.textile_backup.TextileBackup;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
public class BackupScheduler {
|
||||||
|
private boolean scheduled;
|
||||||
|
private long nextBackup;
|
||||||
|
|
||||||
|
public BackupScheduler() {
|
||||||
|
scheduled = false;
|
||||||
|
nextBackup = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick(MinecraftServer server) {
|
||||||
|
long now = Instant.now().getEpochSecond();
|
||||||
|
|
||||||
|
if(TextileBackup.config.doBackupsOnEmptyServer || server.getPlayerManager().getCurrentPlayerCount() > 0) {
|
||||||
|
if(scheduled) {
|
||||||
|
if(nextBackup <= now) {
|
||||||
|
TextileBackup.executorService.submit(BackupHelper.create(server, null, true, null));
|
||||||
|
|
||||||
|
nextBackup = now + TextileBackup.config.backupInterval;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextBackup = now + TextileBackup.config.backupInterval;
|
||||||
|
scheduled = true;
|
||||||
|
}
|
||||||
|
} else if(!TextileBackup.config.doBackupsOnEmptyServer && server.getPlayerManager().getCurrentPlayerCount() == 0) {
|
||||||
|
if(scheduled && nextBackup <= now) {
|
||||||
|
TextileBackup.executorService.submit(BackupHelper.create(server, null, true, null));
|
||||||
|
|
||||||
|
scheduled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,12 +34,12 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
public class MakeBackupThread implements Runnable {
|
public class MakeBackupRunnable implements Runnable {
|
||||||
private final MinecraftServer server;
|
private final MinecraftServer server;
|
||||||
private final ServerCommandSource ctx;
|
private final ServerCommandSource ctx;
|
||||||
private final String comment;
|
private final String comment;
|
||||||
|
|
||||||
public MakeBackupThread(MinecraftServer server, ServerCommandSource ctx, String comment){
|
public MakeBackupRunnable(MinecraftServer server, ServerCommandSource ctx, String comment){
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.comment = comment;
|
this.comment = comment;
|
|
@ -19,45 +19,18 @@
|
||||||
package net.szum123321.textile_backup.mixin;
|
package net.szum123321.textile_backup.mixin;
|
||||||
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.PlayerManager;
|
|
||||||
import net.szum123321.textile_backup.TextileBackup;
|
import net.szum123321.textile_backup.TextileBackup;
|
||||||
import net.szum123321.textile_backup.core.BackupHelper;
|
import net.szum123321.textile_backup.core.BackupHelper;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import java.util.function.BooleanSupplier;
|
|
||||||
|
|
||||||
@Mixin(MinecraftServer.class)
|
@Mixin(MinecraftServer.class)
|
||||||
public abstract class MinecraftServerMixin {
|
public abstract class MinecraftServerMixin {
|
||||||
@Shadow private long timeReference;
|
|
||||||
|
|
||||||
@Shadow public abstract PlayerManager getPlayerManager();
|
|
||||||
|
|
||||||
private long lastBackup = 0;
|
|
||||||
|
|
||||||
@Inject(method = "tick", at = @At("HEAD"))
|
|
||||||
public void tick(BooleanSupplier shouldKeepTicking, CallbackInfo ci){
|
|
||||||
if(timeReference - lastBackup >= TextileBackup.config.backupInterval * 1000){
|
|
||||||
if(getPlayerManager().getCurrentPlayerCount() == 0 && !TextileBackup.config.doBackupsOnEmptyServer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lastBackup = timeReference;
|
|
||||||
|
|
||||||
BackupHelper.create((MinecraftServer)(Object)this, null, true, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "shutdown", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/server/MinecraftServer;save(ZZZ)Z"))
|
@Inject(method = "shutdown", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/server/MinecraftServer;save(ZZZ)Z"))
|
||||||
public void onShutdown(CallbackInfo ci){
|
public void onShutdown(CallbackInfo ci){
|
||||||
if(TextileBackup.config.shutdownBackup) {
|
if(TextileBackup.config.shutdownBackup)
|
||||||
try {
|
TextileBackup.executorService.submit(BackupHelper.create((MinecraftServer) (Object) this, null, false, "shutdown"));
|
||||||
BackupHelper.create((MinecraftServer) (Object) this, null, false, "shutdown").join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue