From df62c510e8dae87fbcbd0d43dd87f51818a83dae Mon Sep 17 00:00:00 2001 From: Wel Coming Date: Wed, 11 Mar 2026 19:25:31 +0800 Subject: [PATCH 1/2] fix: wait for world save to complete before creating backup --- .../quickbackupmulti/utils/BackupManager.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/common/src/main/java/io/github/skydynamic/quickbackupmulti/utils/BackupManager.java b/common/src/main/java/io/github/skydynamic/quickbackupmulti/utils/BackupManager.java index 42e781e..d1910f0 100644 --- a/common/src/main/java/io/github/skydynamic/quickbackupmulti/utils/BackupManager.java +++ b/common/src/main/java/io/github/skydynamic/quickbackupmulti/utils/BackupManager.java @@ -134,11 +134,13 @@ public static void makeBackup(CommandSourceStack commandSource, String name, Str try { commandSource.sendSystemMessage(Component.nullToEmpty(tr("quickbackupmulti.make.start"))); MinecraftServer server = commandSource.getServer(); - server.executeIfPossible(() -> server.saveEverything(true, true, true)); - for (ServerLevel serverLevel : server.getAllLevels()) { - if (serverLevel == null || serverLevel.noSave) continue; - serverLevel.noSave = true; - } + server.executeBlocking(() -> { + server.saveEverything(true, true, true); + for (ServerLevel serverLevel : server.getAllLevels()) { + if (serverLevel == null || serverLevel.noSave) continue; + serverLevel.noSave = true; + } + }); QuickbackupmultiReforged.getManager().incrementalStorage( name, @@ -152,10 +154,12 @@ public static void makeBackup(CommandSourceStack commandSource, String name, Str double intervalTime = (endTime - startTime) / 1000.0; commandSource.sendSystemMessage(Component.nullToEmpty(tr("quickbackupmulti.make.success", intervalTime))); - for (ServerLevel serverLevel : server.getAllLevels()) { - if (serverLevel == null || !serverLevel.noSave) continue; - serverLevel.noSave = false; - } + server.executeBlocking(() -> { + for (ServerLevel serverLevel : server.getAllLevels()) { + if (serverLevel == null || !serverLevel.noSave) continue; + serverLevel.noSave = false; + } + }); } catch (Exception e) { logger.error("Make Backup Failed", e); commandSource.sendSystemMessage(Component.nullToEmpty(tr("quickbackupmulti.make.fail", e.toString()))); From d78e52c002c10154061d7cf9bce271137147bc41 Mon Sep 17 00:00:00 2001 From: Wel Coming Date: Sun, 22 Mar 2026 05:32:43 +0800 Subject: [PATCH 2/2] fix prune #47 --- .../runnables/DefaultPruneRunnable.java | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/common/src/main/java/io/github/skydynamic/quickbackupmulti/schedule/runnables/DefaultPruneRunnable.java b/common/src/main/java/io/github/skydynamic/quickbackupmulti/schedule/runnables/DefaultPruneRunnable.java index 7976458..a47fb7f 100644 --- a/common/src/main/java/io/github/skydynamic/quickbackupmulti/schedule/runnables/DefaultPruneRunnable.java +++ b/common/src/main/java/io/github/skydynamic/quickbackupmulti/schedule/runnables/DefaultPruneRunnable.java @@ -10,6 +10,7 @@ import java.time.ZoneId; import java.util.*; +import java.util.function.Function; public class DefaultPruneRunnable implements Runnable { private final PruneScheduleConfig config; @@ -58,64 +59,61 @@ private static List filterBackupWithPbs(PbsConfig pbsConfig, List filteredList = new ArrayList<>(backupList); - filteredList.sort(Comparator.comparingLong(StorageInfo::getTimestamp)); - - List toDelete = new ArrayList<>(); - - Map timeUnits = Map.of( - "hour", pbsConfig.getHour(), - "day", pbsConfig.getDay(), - "week", pbsConfig.getWeek(), - "month", pbsConfig.getMonth(), - "year", pbsConfig.getYear() - ); - - for (Map.Entry entry : timeUnits.entrySet()) { - String unit = entry.getKey(); - int count = entry.getValue(); - if (count <= 0) continue; - - Map latestPerUnit = new HashMap<>(); - for (StorageInfo backup : filteredList) { - String key = DurationUtils.formatByUnit(backup.getTimestamp(), unit, zoneId); - if (!latestPerUnit.containsKey(key)) { - latestPerUnit.put(key, backup); - } else if (backup.getTimestamp() > latestPerUnit.get(key).getTimestamp()) { - toDelete.add(latestPerUnit.get(key)); - latestPerUnit.put(key, backup); - } else { - toDelete.add(backup); - } - } - filteredList.removeAll(latestPerUnit.values()); - } + List sortedBackups = new ArrayList<>(backupList); + sortedBackups.sort(Comparator.comparingLong(StorageInfo::getTimestamp).reversed()); + ListIterator backupIterator = sortedBackups.listIterator(); - if (pbsConfig.getLast() > 0 && filteredList.size() > pbsConfig.getLast()) { - int keepCount = pbsConfig.getLast(); - List toKeep = filteredList.subList(filteredList.size() - keepCount, filteredList.size()); - Set toKeepSet = new HashSet<>(toKeep); - toDelete.addAll(filteredList.stream().filter(b -> !toKeepSet.contains(b)).toList()); - } + List keepList = new ArrayList<>(); + applyPbsPolicy(pbsConfig.getLast(), backupIterator, keepList, StorageInfo::getName); + applyPbsPolicy(pbsConfig.getHour(), backupIterator, keepList, b -> DurationUtils.formatByUnit(b.getTimestamp(), "hour", zoneId)); + applyPbsPolicy(pbsConfig.getDay(), backupIterator, keepList, b -> DurationUtils.formatByUnit(b.getTimestamp(), "day", zoneId)); + applyPbsPolicy(pbsConfig.getWeek(), backupIterator, keepList, b -> DurationUtils.formatByUnit(b.getTimestamp(), "week", zoneId)); + applyPbsPolicy(pbsConfig.getMonth(), backupIterator, keepList, b -> DurationUtils.formatByUnit(b.getTimestamp(), "month", zoneId)); + applyPbsPolicy(pbsConfig.getYear(), backupIterator, keepList, b -> DurationUtils.formatByUnit(b.getTimestamp(), "year", zoneId)); + + // keepList.sort(Comparator.comparingLong(StorageInfo::getTimestamp).reversed()); if (pbsConfig.getMaxLifeTime() != null && !pbsConfig.getMaxLifeTime().equals("0s")) { long maxLifeTimeMillis = DurationUtils.parseDurationToSeconds(pbsConfig.getMaxLifeTime()) * 1000; long now = System.currentTimeMillis(); - List expired = filteredList.stream() - .filter(b -> now - b.getTimestamp() > maxLifeTimeMillis) - .toList(); - toDelete.addAll(expired); - filteredList.removeAll(expired); + keepList.removeIf(backup -> now - backup.getTimestamp() > maxLifeTimeMillis); } - if (pbsConfig.getMaxAmount() > 0 && filteredList.size() > pbsConfig.getMaxAmount()) { - int removeCount = filteredList.size() - pbsConfig.getMaxAmount(); - toDelete.addAll(filteredList.subList(0, removeCount)); + if (pbsConfig.getMaxAmount() > 0 && keepList.size() > pbsConfig.getMaxAmount()) { + keepList.subList(pbsConfig.getMaxAmount(), keepList.size()).clear(); } + List toDelete = new ArrayList<>(sortedBackups); + // I'm too lazy to write a double pointer. + toDelete.removeAll(new HashSet<>(keepList)); return toDelete; } + private static void applyPbsPolicy(int limit, ListIterator backupIterator, List keepList, Function bucketMapper) { + Set keepBuckets = new HashSet<>(); + for (StorageInfo kept : keepList) { + String key = bucketMapper.apply(kept); + keepBuckets.add(key); + } + + int count = 0; + while (backupIterator.hasNext()) { + StorageInfo backup = backupIterator.next(); + + String key = bucketMapper.apply(backup); + if (keepBuckets.contains(key)) + continue; + if (count >= limit && limit >= 0) { + backupIterator.previous(); + break; + } + + keepBuckets.add(key); + keepList.add(backup); + count++; + } + } + @FunctionalInterface public interface PruneRunnable { void execute(PruneScheduleConfig config, CommandSourceStack commandSourceStack);