refactor: Restructure project into multi-module setup and improve bundler

- Convert project to multi-module structure with 'bundler' subproject
- Extract bundler functionality into separate module with proper dependencies
- Improve IO state management with atomic reference for better synchronization
- Enhance bundler performance with parallel processing support
- Add CLI interface to bundler with various compression options
- Fix file handling issues in bundle reader/writer
- Update compression configuration and remove redundant options
- Improve region file synchronization with proper state management
- Add dictionary support for better compression ratios
- Refactor backup strategies to use new bundler API
- Update dependencies and build configuration
- Fix path resolution issues in bundle operations
`
This commit is contained in:
iceBear67 committed 2025-09-26 16:55:36 +08:00
1 parent 3a4256cee1
commit 28280bc162
26 files changed
+605 -536

No files matched your search

+24 -13
View File
@@ -1,6 +1,6 @@
package io.ib67.sfcraft;
import io.ib67.sfcraft.bundler.Bundle;
import io.ib67.sfcraft.bundler.BundleWriter;
import io.ib67.sfcraft.config.JustBackupConfig;
import io.ib67.sfcraft.mixin.LevelStorageAccessor;
import io.ib67.sfcraft.strategy.BackupStrategy;
@@ -12,9 +12,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.function.Supplier;
import static io.ib67.sfcraft.JustBackupMod.PERFORMING_BACKUP;
import static io.ib67.sfcraft.JustBackupMod.BACKUP_LOCK;
@Log4j2
public record BackupWorker(
@@ -30,11 +31,16 @@ public record BackupWorker(
throw new IllegalStateException("MinecraftServer is not initialized yet, not doing backup");
}
int i = 0;
while (!PERFORMING_BACKUP.compareAndSet(false, true)) {
if (i++ % 10 == 0) log.info("Server is saving world while performing backup! waiting...");
while (!BACKUP_LOCK.compareAndSet(IOState.IDLE, IOState.BACKUP)) {
log.info("Server is saving world while performing backup! waiting...");
i++;
Thread.sleep(2000);
}
// performing backup is now true
var backupName = "Backup_" + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
var backupParentRoot = Path.of(config.temporaryBackupDir());
if (Files.notExists(backupParentRoot)) Files.createDirectories(backupParentRoot);
var backupFile = backupParentRoot.resolve(backupName + ".jpack");
try {
if (i == 0) {
// not saved yet.
@@ -42,22 +48,27 @@ public record BackupWorker(
}
var session = ((LevelStorageAccessor) server).getSession();
var path = session.getDirectory().getRootPath();
var backupName = "Backup_" + DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
var backupParentRoot = Path.of(config.temporaryBackupDir());
if (Files.notExists(backupParentRoot)) Files.createDirectories(backupParentRoot);
var backupFile = backupParentRoot.resolve(backupName + ".jpack");
log.info("Creating bundle {}", backupName);
//todo diff
Path worldDir = Path.of(path);
new Bundle(new WorldDir(worldDir))
.compress(config.compress())
.allowGunzip(config.allowGunzip())
.buildBundle(backupFile);
var tree = new WorldDir(worldDir);
BundleWriter.createBundle(backupFile, tree.everything(), c -> c.allowGunzip(config.allowGunzip())
.relativeRoot(worldDir)
.compressionLevel(config.compressionLevel()));
var backup = strategy.createBackup(worldDir, backupFile);
Files.deleteIfExists(backupFile);
return backup;
} catch (Exception e) {
Files.deleteIfExists(backupFile);
throw e;
} finally {
PERFORMING_BACKUP.set(false);
// Though we won't access the state within backup progress, there are certain cases
// that this state will be changed to STORAGE_SYNC.
// Also take a look at MixinRegionBasedStorage.
while (!BACKUP_LOCK.compareAndSet(IOState.BACKUP, IOState.IDLE)) {
log.error("Expect BACKUP state but got {}", BACKUP_LOCK.get());
Thread.sleep(2000);
}
}
}
}