Added:
-per world backup -configurable datetime format -configurable permission level -player black and whitelistForge-1.14.4
parent
b30e35a308
commit
312cb57692
|
@ -2,13 +2,13 @@
|
|||
org.gradle.jvmargs=-Xmx1G
|
||||
|
||||
minecraft_version=20w12a
|
||||
yarn_mappings=20w12a+build.18
|
||||
yarn_mappings=20w12a+build.19
|
||||
loader_version=0.7.8+build.189
|
||||
|
||||
#Fabric api
|
||||
fabric_version=0.5.5+build.311-1.16
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.0.4-1.16
|
||||
mod_version = 1.1.0-1.16
|
||||
maven_group = net.szum123321
|
||||
archives_base_name = textile_backup
|
|
@ -21,6 +21,9 @@ package net.szum123321.textile_backup;
|
|||
import blue.endless.jankson.Comment;
|
||||
import io.github.cottonmc.cotton.config.annotations.ConfigFile;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ConfigFile(name = TextileBackup.MOD_ID)
|
||||
public class ConfigHandler {
|
||||
@Comment("\nTime between backups in seconds\n")
|
||||
|
@ -35,10 +38,13 @@ public class ConfigHandler {
|
|||
@Comment("\nA path to backup folder\n")
|
||||
public String path = "backup/";
|
||||
|
||||
@Comment("\nMaximum number of backups to keep. if 0 then no backup will be deleted\n")
|
||||
@Comment("\nShould every world has its won backup folder?\n")
|
||||
public boolean perWorldBackup = false;
|
||||
|
||||
@Comment("\nMaximum number of backups to keep. if 0 then no backup will be deleted based on its amount\n")
|
||||
public int backupsToKeep = 10;
|
||||
|
||||
@Comment("\nMaximum age of backups to keep in seconds.\n if 0 then backups will not be deleted based on age \n")
|
||||
@Comment("\nMaximum age of backups to keep in seconds.\n if 0 then backups will not be deleted based on its age \n")
|
||||
public int maxAge = 0;
|
||||
|
||||
@Comment("\nMaximum size of backup folder in kilo bytes. \n")
|
||||
|
@ -49,4 +55,16 @@ public class ConfigHandler {
|
|||
|
||||
@Comment("\nPrint info to game out\n")
|
||||
public boolean log = true;
|
||||
|
||||
@Comment("\nMinimal permission level required to run commands\n")
|
||||
public int permissionLevel = 4;
|
||||
|
||||
@Comment("\nPlayers allowed to run backup commands without sufficient permission level\n")
|
||||
public Set<String> whitelist = new HashSet<>();
|
||||
|
||||
@Comment("\nPlayers banned from running backup commands besides their sufficient permission level\n")
|
||||
public Set<String> blacklist = new HashSet<>();
|
||||
|
||||
@Comment("\nFormat of date&time used to name backup files.\n")
|
||||
public String dateTimeFormat = "dd.MM.yyyy_HH-mm-ss";
|
||||
}
|
||||
|
|
|
@ -25,8 +25,10 @@ import io.github.cottonmc.cotton.logging.ModLogger;
|
|||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.registry.CommandRegistry;
|
||||
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;
|
||||
|
||||
public class TextileBackup implements ModInitializer {
|
||||
public static final String MOD_ID = "textile_backup";
|
||||
|
@ -41,7 +43,6 @@ public class TextileBackup implements ModInitializer {
|
|||
logger.info("Loading TextileBackup by Szum123321");
|
||||
|
||||
config = ConfigManager.loadConfig(ConfigHandler.class);
|
||||
config.backupInterval *= 1000;
|
||||
|
||||
registerCommands();
|
||||
}
|
||||
|
@ -49,8 +50,10 @@ public class TextileBackup implements ModInitializer {
|
|||
private void registerCommands(){
|
||||
CommandRegistry.INSTANCE.register(false, dispatcher -> dispatcher.register(
|
||||
LiteralArgumentBuilder.<ServerCommandSource>literal("backup")
|
||||
.then(StartBackupCommand.register())
|
||||
.then(BlacklistCommand.register())
|
||||
.then(CleanupCommand.register())
|
||||
.then(StartBackupCommand.register())
|
||||
.then(WhitelistCommand.register())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package net.szum123321.textile_backup.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import io.github.cottonmc.cotton.config.ConfigManager;
|
||||
import net.minecraft.command.arguments.EntityArgumentType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
|
||||
public class BlacklistCommand {
|
||||
public static LiteralArgumentBuilder<ServerCommandSource> register(){
|
||||
return CommandManager.literal("whitelist")
|
||||
.requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) ||
|
||||
ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) &&
|
||||
!TextileBackup.config.blacklist.contains(ctx.getName()))
|
||||
.then(CommandManager.literal("add")
|
||||
.then(CommandManager.argument("Player", EntityArgumentType.player()))
|
||||
.executes(BlacklistCommand::executeAdd)
|
||||
)
|
||||
.then(CommandManager.literal("remove")
|
||||
.then(CommandManager.argument("Player", EntityArgumentType.player()))
|
||||
.executes(BlacklistCommand::executeRemove)
|
||||
)
|
||||
.then(CommandManager.literal("list")
|
||||
.executes(ctx -> executeList(ctx.getSource()))
|
||||
)
|
||||
.executes(ctx -> help(ctx.getSource()));
|
||||
}
|
||||
|
||||
private static int help(ServerCommandSource source){
|
||||
source.sendFeedback(new TranslatableText("Available command are: add [player], remove [player], list."), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeList(ServerCommandSource source){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Currently on the blacklist are: ");
|
||||
|
||||
for(String name : TextileBackup.config.blacklist){
|
||||
builder.append(name);
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
source.sendFeedback(new TranslatableText(builder.toString()), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeAdd(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException {
|
||||
PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player");
|
||||
|
||||
if(TextileBackup.config.blacklist.contains(player.getEntityName())) {
|
||||
ctx.getSource().sendFeedback(new TranslatableText("Player: {} is already blacklisted.", player.getEntityName()), false);
|
||||
}else{
|
||||
TextileBackup.config.blacklist.add(player.getEntityName());
|
||||
ConfigManager.saveConfig(TextileBackup.config);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Player: ");
|
||||
builder.append(player.getEntityName());
|
||||
builder.append(" added to the blacklist");
|
||||
|
||||
if(TextileBackup.config.whitelist.contains(player.getEntityName())){
|
||||
TextileBackup.config.whitelist.remove(player.getEntityName());
|
||||
builder.append(" and removed form the whitelist");
|
||||
}
|
||||
|
||||
builder.append(" successfully.");
|
||||
|
||||
ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeRemove(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException {
|
||||
PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player");
|
||||
|
||||
if(!TextileBackup.config.blacklist.contains(player.getEntityName())) {
|
||||
ctx.getSource().sendFeedback(new TranslatableText("Player: {} newer was blacklisted.", player.getEntityName()), false);
|
||||
}else{
|
||||
TextileBackup.config.blacklist.remove(player.getEntityName());
|
||||
ConfigManager.saveConfig(TextileBackup.config);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Player: ");
|
||||
builder.append(player.getEntityName());
|
||||
builder.append(" removed from the blacklist successfully.");
|
||||
|
||||
ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -21,17 +21,22 @@ package net.szum123321.textile_backup.commands;
|
|||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import net.szum123321.textile_backup.core.BackupHelper;
|
||||
|
||||
public class CleanupCommand {
|
||||
public static LiteralArgumentBuilder<ServerCommandSource> register(){
|
||||
return CommandManager.literal("cleanup")
|
||||
.requires(ctx -> ctx.hasPermissionLevel(4))
|
||||
.requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) ||
|
||||
ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) &&
|
||||
!TextileBackup.config.blacklist.contains(ctx.getName()))
|
||||
.executes(ctx -> execute(ctx.getSource()));
|
||||
}
|
||||
|
||||
private static int execute(ServerCommandSource source){
|
||||
BackupHelper.executeFileLimit(source);
|
||||
BackupHelper.executeFileLimit(source, source.getMinecraftServer().getLevelName());
|
||||
source.sendFeedback(new TranslatableText("Done"), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,15 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|||
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.BackupHelper;
|
||||
|
||||
public class StartBackupCommand {
|
||||
public static LiteralArgumentBuilder<ServerCommandSource> register(){
|
||||
return CommandManager.literal("start")
|
||||
.requires(ctx -> ctx.hasPermissionLevel(4))
|
||||
.requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) ||
|
||||
ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) &&
|
||||
!TextileBackup.config.blacklist.contains(ctx.getName()))
|
||||
.then(CommandManager.argument("Comment", StringArgumentType.word())
|
||||
.executes(StartBackupCommand::executeWithComment)
|
||||
).executes(ctx -> execute(ctx.getSource()));
|
||||
|
@ -41,7 +44,7 @@ public class StartBackupCommand {
|
|||
}
|
||||
|
||||
private static int execute(ServerCommandSource source){
|
||||
BackupHelper.create(source.getMinecraftServer(), source, true, null);
|
||||
BackupHelper.create(source.getMinecraftServer(), source,true, null);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package net.szum123321.textile_backup.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import io.github.cottonmc.cotton.config.ConfigManager;
|
||||
import net.minecraft.command.arguments.EntityArgumentType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.szum123321.textile_backup.TextileBackup;
|
||||
import sun.security.krb5.Config;
|
||||
|
||||
public class WhitelistCommand {
|
||||
public static LiteralArgumentBuilder<ServerCommandSource> register(){
|
||||
return CommandManager.literal("whitelist")
|
||||
.requires(ctx -> TextileBackup.config.whitelist.contains(ctx.getName()) ||
|
||||
ctx.hasPermissionLevel(TextileBackup.config.permissionLevel) &&
|
||||
!TextileBackup.config.blacklist.contains(ctx.getName()))
|
||||
.then(CommandManager.literal("add")
|
||||
.then(CommandManager.argument("Player", EntityArgumentType.player()))
|
||||
.executes(WhitelistCommand::executeAdd)
|
||||
)
|
||||
.then(CommandManager.literal("remove")
|
||||
.then(CommandManager.argument("Player", EntityArgumentType.player()))
|
||||
.executes(WhitelistCommand::executeRemove)
|
||||
)
|
||||
.then(CommandManager.literal("list")
|
||||
.executes(ctx -> executeList(ctx.getSource()))
|
||||
)
|
||||
.executes(ctx -> help(ctx.getSource()));
|
||||
}
|
||||
|
||||
private static int help(ServerCommandSource source){
|
||||
source.sendFeedback(new TranslatableText("Available command are: add [player], remove [player], list."), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeList(ServerCommandSource source){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Currently on the whitelist are: ");
|
||||
|
||||
for(String name : TextileBackup.config.whitelist){
|
||||
builder.append(name);
|
||||
builder.append(", ");
|
||||
}
|
||||
|
||||
source.sendFeedback(new TranslatableText(builder.toString()), false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeAdd(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException {
|
||||
PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player");
|
||||
|
||||
if(TextileBackup.config.whitelist.contains(player.getEntityName())) {
|
||||
ctx.getSource().sendFeedback(new TranslatableText("Player: {} is already whitelisted.", player.getEntityName()), false);
|
||||
}else{
|
||||
TextileBackup.config.whitelist.add(player.getEntityName());
|
||||
ConfigManager.saveConfig(TextileBackup.config);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Player: ");
|
||||
builder.append(player.getEntityName());
|
||||
builder.append(" added to the whitelist");
|
||||
|
||||
if(TextileBackup.config.blacklist.contains(player.getEntityName())){
|
||||
TextileBackup.config.blacklist.remove(player.getEntityName());
|
||||
builder.append(" and removed form the blacklist");
|
||||
}
|
||||
|
||||
builder.append(" successfully.");
|
||||
|
||||
ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int executeRemove(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException {
|
||||
PlayerEntity player = EntityArgumentType.getPlayer(ctx, "Player");
|
||||
|
||||
if(!TextileBackup.config.whitelist.contains(player.getEntityName())) {
|
||||
ctx.getSource().sendFeedback(new TranslatableText("Player: {} newer was on the whitelist.", player.getEntityName()), false);
|
||||
}else{
|
||||
TextileBackup.config.whitelist.remove(player.getEntityName());
|
||||
ConfigManager.saveConfig(TextileBackup.config);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Player: ");
|
||||
builder.append(player.getEntityName());
|
||||
builder.append(" removed from the whitelist successfully.");
|
||||
|
||||
ctx.getSource().sendFeedback(new TranslatableText(builder.toString()), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -74,12 +74,10 @@ public class BackupHelper {
|
|||
MakeBackupThread thread = new MakeBackupThread(server, ctx, comment);
|
||||
|
||||
thread.start();
|
||||
|
||||
executeFileLimit(ctx);
|
||||
}
|
||||
|
||||
public static void executeFileLimit(ServerCommandSource ctx){
|
||||
File root = getBackupRootPath();
|
||||
public static void executeFileLimit(ServerCommandSource ctx, String worldName){
|
||||
File root = getBackupRootPath(worldName);
|
||||
|
||||
FileFilter filter = f -> f.getName().endsWith("zip");
|
||||
|
||||
|
@ -89,11 +87,25 @@ public class BackupHelper {
|
|||
|
||||
Arrays.stream(root.listFiles()).forEach(f ->{
|
||||
if(f.exists() && f.isFile()){
|
||||
LocalDateTime creationTime = LocalDateTime.from(
|
||||
getDateTimeFormatter().parse(
|
||||
f.getName().split(".zip")[0].split("#")[0]
|
||||
)
|
||||
);
|
||||
LocalDateTime creationTime;
|
||||
|
||||
try {
|
||||
creationTime = LocalDateTime.from(
|
||||
getDateTimeFormatter().parse(
|
||||
f.getName().split(".zip")[0].split("#")[0]
|
||||
)
|
||||
);
|
||||
}catch(Exception e){
|
||||
System.out.println(e.getClass());
|
||||
System.out.println(e.toString());
|
||||
|
||||
creationTime = LocalDateTime.from(
|
||||
getBackupDateTimeFormatter().parse(
|
||||
f.getName().split(".zip")[0].split("#")[0]
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if(now.toEpochSecond(ZoneOffset.UTC) - creationTime.toEpochSecond(ZoneOffset.UTC) > TextileBackup.config.maxAge) {
|
||||
log("Deleting: " + f.getName(), ctx);
|
||||
|
@ -128,9 +140,12 @@ public class BackupHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static File getBackupRootPath(){
|
||||
public static File getBackupRootPath(String worldName){
|
||||
File path = new File(TextileBackup.config.path);
|
||||
|
||||
if(TextileBackup.config.perWorldBackup)
|
||||
path = path.toPath().resolve(worldName).toFile();
|
||||
|
||||
if(!path.exists()){
|
||||
try{
|
||||
path.mkdirs();
|
||||
|
@ -150,8 +165,15 @@ public class BackupHelper {
|
|||
}
|
||||
|
||||
public static DateTimeFormatter getDateTimeFormatter(){
|
||||
String os = System.getProperty("os.name");
|
||||
if (os.toLowerCase().startsWith("win")) {
|
||||
if(TextileBackup.config.dateTimeFormat != null)
|
||||
return DateTimeFormatter.ofPattern(TextileBackup.config.dateTimeFormat);
|
||||
else
|
||||
return getBackupDateTimeFormatter();
|
||||
}
|
||||
|
||||
public static DateTimeFormatter getBackupDateTimeFormatter(){
|
||||
String os = System.getProperty("os.name");
|
||||
if(os.toLowerCase().startsWith("win")){
|
||||
return DateTimeFormatter.ofPattern("dd.MM.yyyy_HH-mm-ss");
|
||||
} else {
|
||||
return DateTimeFormatter.ofPattern("dd.MM.yyyy_HH:mm:ss");
|
||||
|
|
|
@ -45,7 +45,7 @@ public class MakeBackupThread extends Thread {
|
|||
.getWorldDir();
|
||||
|
||||
File outFile = BackupHelper
|
||||
.getBackupRootPath()
|
||||
.getBackupRootPath(server.getLevelName())
|
||||
.toPath()
|
||||
.resolve(getFileName())
|
||||
.toFile();
|
||||
|
@ -61,7 +61,7 @@ public class MakeBackupThread extends Thread {
|
|||
|
||||
Compressor.createArchive(world, outFile, ctx);
|
||||
|
||||
BackupHelper.executeFileLimit(ctx);
|
||||
BackupHelper.executeFileLimit(ctx, server.getLevelName());
|
||||
|
||||
BackupHelper.log("Done!", ctx);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public abstract class MinecraftServerMixin {
|
|||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
public void tick(CallbackInfo ci){
|
||||
if(timeReference - lastBackup >= TextileBackup.config.backupInterval){
|
||||
if(timeReference - lastBackup >= TextileBackup.config.backupInterval * 1000){
|
||||
if(getPlayerManager().getCurrentPlayerCount() == 0 && !TextileBackup.config.doBackupsOnEmptyServer)
|
||||
return;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"version": "${version}",
|
||||
|
||||
"name": "Textile Backup",
|
||||
"description": "Small, configurable, fully server-side backup mod for Fabric",
|
||||
"description": "Small, configurable, fully server-sided backup mod for Fabric",
|
||||
"authors": [
|
||||
"Szum123321"
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue