diff --git a/src/main/java/ir/mehradn/rollback/config/RollbackConfig.java b/src/main/java/ir/mehradn/rollback/config/RollbackConfig.java index dc44353..b4e5f97 100644 --- a/src/main/java/ir/mehradn/rollback/config/RollbackConfig.java +++ b/src/main/java/ir/mehradn/rollback/config/RollbackConfig.java @@ -10,25 +10,24 @@ public static void register() { MidnightConfig.init("rollback", _RollbackConfig.class); } - public static int getMaxBackupsPerWorld() { + public static int maxBackupsPerWorld() { return _RollbackConfig.backupsPerWorld; } - public static BackupMode backupMode() { - switch (_RollbackConfig.backupFrequency) { - case ONE_PER_DAY, TWO_PER_DAY, FOUR_PER_DAY -> {return BackupMode.IN_GAME_DAY;} - case TWENTY_MINUTES, TEN_MINUTES, FIVE_MINUTES -> {return BackupMode.REAL_TIME;} - } - return BackupMode.REAL_TIME; + public static int daysPerBackup() { + return _RollbackConfig.backupFrequency; } public static int ticksPerBackup() { - switch (_RollbackConfig.backupFrequency) { - case ONE_PER_DAY, TWENTY_MINUTES -> {return 24000;} - case TWO_PER_DAY, TEN_MINUTES -> {return 12000;} - case FOUR_PER_DAY, FIVE_MINUTES -> {return 6000;} + return _RollbackConfig.backupFrequency * 24000; + } + + public static TimerMode timerMode() { + switch (_RollbackConfig.timerMode) { + case DAYLIGHT_CYCLE -> {return TimerMode.DAYLIGHT_CYCLE;} + case IN_GAME_TIME -> {return TimerMode.IN_GAME_TIME;} } - return 24000; + return TimerMode.IN_GAME_TIME; } public static CommandAccess commandAccess() { @@ -47,9 +46,9 @@ public static boolean replaceGameRulesButton() { return _RollbackConfig.replaceGameRulesButton; } - public enum BackupMode { - IN_GAME_DAY, - REAL_TIME + public enum TimerMode { + DAYLIGHT_CYCLE, + IN_GAME_TIME } public enum CommandAccess { @@ -60,13 +59,9 @@ public enum CommandAccess { // DO NOT USE OUTSIDE OF THIS CLASS // I am forced to keep it public. public static final class _RollbackConfig extends MidnightConfig { - public enum _BackupFrequency { - ONE_PER_DAY, - TWO_PER_DAY, - FOUR_PER_DAY, - TWENTY_MINUTES, - TEN_MINUTES, - FIVE_MINUTES + public enum _TimerMode { + DAYLIGHT_CYCLE, + IN_GAME_TIME } public enum _CommandAccess { @@ -76,8 +71,10 @@ public enum _CommandAccess { @Entry(min = 1, max = 10, isSlider = true) public static int backupsPerWorld = 5; + @Entry(min = 1, max = 15, isSlider = true) + public static int backupFrequency = 1; @Entry - public static _BackupFrequency backupFrequency = _BackupFrequency.ONE_PER_DAY; + public static _TimerMode timerMode = _TimerMode.DAYLIGHT_CYCLE; @Entry public static _CommandAccess commandAccess = _CommandAccess.ON_CHEATS; @Entry diff --git a/src/main/java/ir/mehradn/rollback/event/AutomatedBackup.java b/src/main/java/ir/mehradn/rollback/event/AutomatedBackup.java index 26421c0..f9e3272 100644 --- a/src/main/java/ir/mehradn/rollback/event/AutomatedBackup.java +++ b/src/main/java/ir/mehradn/rollback/event/AutomatedBackup.java @@ -10,43 +10,90 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; +import org.apache.commons.lang3.tuple.Triple; @Environment(EnvType.CLIENT) public final class AutomatedBackup { - private static int latestBackup; + private static int latestUpdate; + private static int daysPassed; + private static int sinceDay; + private static int sinceBackup; public static void register() { ServerLifecycleEvents.SERVER_STARTED.register(AutomatedBackup::onServerStarted); ServerTickEvents.END_SERVER_TICK.register(AutomatedBackup::onEndTick); + ServerLifecycleEvents.SERVER_STOPPING.register(AutomatedBackup::onServerStopping); } public static void onServerStarted(MinecraftServer server) { - latestBackup = server.getTicks(); + Rollback.LOGGER.info("Reading the timer information..."); + BackupManager backupManager = ((MinecraftServerExpanded)server).getBackupManager(); + String worldName = ((MinecraftServerExpanded)server).getSession().getLevelSummary().getName(); + Triple info = backupManager.getTimerInformation(worldName); + + latestUpdate = server.getTicks(); + daysPassed = info.getLeft(); + sinceDay = info.getMiddle(); + sinceBackup = info.getRight(); } public static void onEndTick(MinecraftServer server) { - if (shouldCreateBackup(server)) { - Rollback.LOGGER.info("Creating an automated backup..."); + int serverTick = server.getTicks(); + int worldTick = (int)server.getWorld(World.OVERWORLD).getTimeOfDay(); + + if (shouldUpdate(serverTick, worldTick)) { + Rollback.LOGGER.info("Updating the timer information..."); BackupManager backupManager = ((MinecraftServerExpanded)server).getBackupManager(); - backupManager.createRollbackBackup(server); - latestBackup = server.getTicks(); + String worldName = ((MinecraftServerExpanded)server).getSession().getLevelSummary().getName(); + + int timePassed = serverTick - latestUpdate; + latestUpdate = serverTick; + sinceDay += timePassed; + sinceBackup += timePassed; + if (isMorning(worldTick) && sinceDay >= 11900) { + daysPassed++; + sinceDay = 0; + } + + if (shouldCreateBackup(worldTick, backupManager, worldName)) { + Rollback.LOGGER.info("Creating an automated backup..."); + backupManager.createRollbackBackup(server, true); + daysPassed = 0; + sinceDay = 0; + sinceBackup = 0; + } else { + backupManager.setTimerInformation(worldName, daysPassed, sinceBackup); + } } } - private static boolean shouldCreateBackup(MinecraftServer server) { + public static void onServerStopping(MinecraftServer server) { int serverTick = server.getTicks(); - int worldTick = (int)server.getWorld(World.OVERWORLD).getTimeOfDay(); BackupManager backupManager = ((MinecraftServerExpanded)server).getBackupManager(); String worldName = ((MinecraftServerExpanded)server).getSession().getLevelSummary().getName(); - if (!backupManager.getAutomated(worldName)) - return false; - if (RollbackConfig.backupMode() == RollbackConfig.BackupMode.REAL_TIME) - return (serverTick - latestBackup) >= (RollbackConfig.ticksPerBackup() + 20); - else - return ( - ((serverTick - latestBackup) >= (RollbackConfig.ticksPerBackup() / 2 - 20)) - && (((worldTick - 20) % RollbackConfig.ticksPerBackup()) == 0) - ); + int timePassed = serverTick - latestUpdate; + sinceDay += timePassed; + sinceBackup += timePassed; + + backupManager.setTimerInformation(worldName, daysPassed, sinceDay, sinceBackup); + } + + private static boolean isMorning(int worldTick) { + return ((worldTick - 20) % 24000 == 0); + } + + private static boolean shouldUpdate(int serverTick, int worldTick) { + return (isMorning(worldTick) || (serverTick - latestUpdate + sinceBackup) % 24000 == 0); + } + + private static boolean shouldCreateBackup(int worldTick, BackupManager backupManager, String worldName) { + if (backupManager.getAutomated(worldName)) { + switch (RollbackConfig.timerMode()) { + case DAYLIGHT_CYCLE -> {return (isMorning(worldTick) && daysPassed >= RollbackConfig.daysPerBackup());} + case IN_GAME_TIME -> {return (sinceBackup >= RollbackConfig.ticksPerBackup());} + } + } + return false; } } diff --git a/src/main/java/ir/mehradn/rollback/event/RollbackCommand.java b/src/main/java/ir/mehradn/rollback/event/RollbackCommand.java index 0b55a97..baea726 100644 --- a/src/main/java/ir/mehradn/rollback/event/RollbackCommand.java +++ b/src/main/java/ir/mehradn/rollback/event/RollbackCommand.java @@ -37,7 +37,7 @@ public static void register() { .executes((context) -> deleteBackup(context, 0))) .then(CommandManager.literal("latest") .executes((context) -> deleteBackup(context, 1))) - .then(CommandManager.argument("number", IntegerArgumentType.integer(1, RollbackConfig.getMaxBackupsPerWorld())) + .then(CommandManager.argument("number", IntegerArgumentType.integer(1, RollbackConfig.maxBackupsPerWorld())) .executes((context) -> deleteBackup(context, 2)))) .then(CommandManager.literal("list") .executes(RollbackCommand::listBackups))); @@ -55,7 +55,7 @@ public static int backupNow(CommandContext context) throws MinecraftServer server = context.getSource().getServer(); BackupManager backupManager = ((MinecraftServerExpanded)server).getBackupManager(); - boolean f = backupManager.createRollbackBackup(server); + boolean f = backupManager.createRollbackBackup(server, false); if (!f) throw new SimpleCommandExceptionType(Text.translatable("rollback.createBackup.failed")).create(); diff --git a/src/main/java/ir/mehradn/rollback/util/backup/BackupManager.java b/src/main/java/ir/mehradn/rollback/util/backup/BackupManager.java index 6538c8f..b533655 100644 --- a/src/main/java/ir/mehradn/rollback/util/backup/BackupManager.java +++ b/src/main/java/ir/mehradn/rollback/util/backup/BackupManager.java @@ -17,6 +17,7 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.world.level.storage.LevelStorage; import net.minecraft.world.level.storage.LevelSummary; +import org.apache.commons.lang3.tuple.Triple; import java.io.*; import java.nio.file.Files; @@ -75,6 +76,29 @@ public void setPromptAnswer(String worldName, boolean automated) { saveMetadata(); } + public Triple getTimerInformation(String worldName) { + JsonObject worldObject = getWorldObject(worldName); + int daysPassed = worldObject.get("days_passed").getAsInt(); + int sinceDay = worldObject.get("since_day").getAsInt(); + int sinceBackup = worldObject.get("since_backup").getAsInt(); + return Triple.of(daysPassed, sinceDay, sinceBackup); + } + + public void setTimerInformation(String worldName, int daysPassed, int sinceBackup) { + JsonObject worldObject = getWorldObject(worldName); + worldObject.addProperty("days_passed", daysPassed); + worldObject.addProperty("since_backup", sinceBackup); + saveMetadata(); + } + + public void setTimerInformation(String worldName, int daysPassed, int sinceDay, int sinceBackup) { + JsonObject worldObject = getWorldObject(worldName); + worldObject.addProperty("days_passed", daysPassed); + worldObject.addProperty("since_day", sinceDay); + worldObject.addProperty("since_backup", sinceBackup); + saveMetadata(); + } + public List getRollbacksFor(String worldName) { ArrayList list = new ArrayList<>(); JsonArray array = getWorldObject(worldName).getAsJsonArray("backups"); @@ -99,7 +123,7 @@ public boolean createNormalBackup(LevelSummary summary) { } } - public boolean createRollbackBackup(MinecraftServer server) { + public boolean createRollbackBackup(MinecraftServer server, boolean automated) { Rollback.LOGGER.info("Creating a rollback backup..."); LevelStorage.Session session = ((MinecraftServerExpanded)server).getSession(); String worldName = session.getLevelSummary().getName(); @@ -107,7 +131,7 @@ public boolean createRollbackBackup(MinecraftServer server) { JsonObject worldObject = getWorldObject(worldName); JsonArray array = worldObject.getAsJsonArray("backups"); - while (array.size() >= RollbackConfig.getMaxBackupsPerWorld()) + while (array.size() >= RollbackConfig.maxBackupsPerWorld()) deleteBackup(worldName, 0); deleteNonexistentIcons(worldName); @@ -162,6 +186,11 @@ public boolean createRollbackBackup(MinecraftServer server) { path3 = this.rollbackDirectory.relativize(path3); RollbackBackup backup = new RollbackBackup(worldName, path2, path3, LocalDateTime.now(), daysPlayed); array.add(backup.toObject()); + if (automated) { + worldObject.addProperty("days_passed", 0); + worldObject.addProperty("since_day", 0); + worldObject.addProperty("since_backup", 0); + } saveMetadata(); return true; @@ -256,6 +285,12 @@ private JsonObject getWorldObject(String worldName) { worldObject.addProperty("automated", false); if (!worldObject.has("prompted")) worldObject.addProperty("prompted", false); + if (!worldObject.has("days_passed")) + worldObject.addProperty("days_passed", 0); + if (!worldObject.has("since_day")) + worldObject.addProperty("since_day", 0); + if (!worldObject.has("since_backup")) + worldObject.addProperty("since_backup", 0); if (!worldObject.has("backups")) worldObject.add("backups", new JsonArray()); diff --git a/src/main/resources/assets/rollback/lang/en_us.json b/src/main/resources/assets/rollback/lang/en_us.json index f9bab54..86e69c2 100644 --- a/src/main/resources/assets/rollback/lang/en_us.json +++ b/src/main/resources/assets/rollback/lang/en_us.json @@ -23,23 +23,15 @@ "rollback.command.list.title": "Backups:", "rollback.command.list.noBackups": "There are no backups available for this world.", "rollback.command.unavailable": "This command is only available to server host.", - "rollback.midnightconfig.title": "Rollback Config Menu", + "rollback.midnightconfig.title": "Rollback Options", "rollback.midnightconfig.backupsPerWorld": "Maximum Backups Per World:", - "rollback.midnightconfig.backupFrequency": "Automated Backup Frequency:", - "rollback.midnightconfig.allowedWorldTypes": "Create Automated Backup For:", + "rollback.midnightconfig.backupFrequency": "Minecraft Days Per Backup:", + "rollback.midnightconfig.timerMode": "Backup Timer Is Based On:", "rollback.midnightconfig.commandAccess": "Rollback Command Access:", "rollback.midnightconfig.replaceReCreateButton": "Replace Re-Create Button:", "rollback.midnightconfig.replaceGameRulesButton": "Replace GameRules Button:", - "rollback.midnightconfig.enum._BackupFrequency.ONE_PER_DAY": "1 Per Day", - "rollback.midnightconfig.enum._BackupFrequency.TWO_PER_DAY": "2 Per Day", - "rollback.midnightconfig.enum._BackupFrequency.FOUR_PER_DAY": "4 Per Day", - "rollback.midnightconfig.enum._BackupFrequency.TWENTY_MINUTES": "20 Minutes", - "rollback.midnightconfig.enum._BackupFrequency.TEN_MINUTES": "10 Minutes", - "rollback.midnightconfig.enum._BackupFrequency.FIVE_MINUTES": "5 Minutes", + "rollback.midnightconfig.enum._TimerMode.DAYLIGHT_CYCLE": "Daylight Cycle", + "rollback.midnightconfig.enum._TimerMode.IN_GAME_TIME": "In-Game Time", "rollback.midnightconfig.enum._CommandAccess.ON_CHEATS": "On Cheats", - "rollback.midnightconfig.enum._CommandAccess.ALWAYS": "Always", - "rollback.midnightconfig.enum._AllowedWorldTypes.NONE": "None", - "rollback.midnightconfig.enum._AllowedWorldTypes.SURVIVAL": "Survival Only", - "rollback.midnightconfig.enum._AllowedWorldTypes.ADVENTURE": "Survival & Adventure", - "rollback.midnightconfig.enum._AllowedWorldTypes.ALL_TYPES": "All World Types" + "rollback.midnightconfig.enum._CommandAccess.ALWAYS": "Always" } \ No newline at end of file