# Build 2880
## Added
- **Fixed-time backup schedules:** The `checkpoint:` and `world-backup:` sections in `database.yml` support a `times` list of UTC `HH:mm` values. For example, `times: ["00:00", "06:00", "12:00", "18:00"]` runs four times daily.
- A non-empty `times` list takes precedence over `interval-hours`; an empty list keeps the interval-based schedule. Times are polled once per minute, and each configured minute fires at most once.
- **Optional world backups:** Minecraft world folders, including the overworld, nether, and end, can be backed up and restored through `/sf checkpoint worlds`. World archives are stored separately from regular checkpoints and use independent retention. This feature is disabled by default.
- **World snapshot metadata:** Each snapshot contains `worlds.zip` and `metadata.json`, including a checksum, per-world file counts, and capture versions. Worlds are saved on the main thread, while ZIP compression runs off-thread. Volatile lock files such as `session.lock` and `*.lock` are excluded.
- **World backup commands:** Available operations include `worlds`, `worlds list`, `worlds create` (admin), `worlds info <id>`, and `worlds restore <id> confirm` (console-only).
- **World-backup configuration:** The `world-backup` section supports `enabled` and `automatic` (both default to `false`), `interval-hours` (default `6`), `keep` (default `3`), and `worlds`. An empty `worlds` list auto-detects the primary world and its `_nether` / `_the_end` companions.
## Changed
- **Transactional storage restore:** `applyPendingStorageRestore` now uses same-filesystem renames instead of creating a full pre-restore snapshot. It moves the live block sections aside, extracts the staged archive into the empty data root, and restores the originals if the apply fails.
- **Space-efficient rollback:** Rename-based rollback does not require extra disk space. If moving a section back still fails, the original remains in the scratch directory and its exact recovery path is logged. This replaces the earlier snapshot-ZIP rollback.
- **Lag-tolerant scheduling:** Fixed-time jobs check the interval between the previous check and the current time, avoiding missed schedules when low server TPS delays polling. Large forward clock jumps trigger a job once rather than once for every missed day.
- **Thread visibility:** Storage-engine task fields are now `volatile` to improve visibility between asynchronous schedulers and shutdown.
- **Startup schedule status:** The storage banner shows the active schedule as either an interval, such as `every 6h`, or a list of UTC times.
## Fixed
- **Storage restore rollback:** If a staged storage restore fails partway through, such as when the disk fills during replacement, the original data is restored. Failed staging data is also cleaned up.
- **Apply storage restores during startup:** Restored block data is staged under `<dataFolder>/storage-restore-pending/` and applied by `CheckpointService.applyPendingStorageRestore(...)` from `Slimefun.onLoad()`, before Slimefun loads block data into memory. This prevents shutdown from overwriting restored files with older in-memory state. A restart is required to apply the restore.
- **Prevent partial combined restores:** If a `both`-scope restore stages storage but the database restore fails, the staged storage restore is discarded so it cannot apply on the next startup. The database portion is rolled back separately.
- **Legacy sound configuration:** Existing `sounds.yml` files containing old Bukkit sound enum names are validated and translated to registry keys. Unknown values fall back to built-in defaults, preventing guide page-turn sound crashes on newer server versions.
- **Deferred inventory cleanup:** Deferred inventories for destroyed blocks are removed, while entries for moved blocks are carried across with the block.
- **Cleaner automatic error reporting:** Addon linkage errors and plugin enable/disable lifecycle failures are skipped because they recur on restart and are already logged to the console. Genuine runtime `SEVERE` errors continue to be reported.
- **Daily report limit:** Automatic reports retain one-hour duplicate suppression and the 20-per-10-minute burst cap, with a new limit of two reports per rolling 24 hours. The counter resets on restart.
## Performance
- **Cached block-storage snapshot:** `BlockStorage.getRawStorage()` now caches its snapshot and rebuilds it only when the block set changes, avoiding repeated full-map reconstruction and allocation overhead.
- **Less cargo inventory copying:** Cargo hot paths read live-backed block states rather than deep-copying vanilla container state.
- **Lower debug overhead:** Cargo filter and insert paths avoid allocating debug arguments while debug logging is disabled.
- **Reusable item wrappers:** Cargo distribution reuses an `ItemStackWrapper` until the stack amount changes, reducing metadata cloning while preserving routing behavior.
## Notes
- **Backup retention:** Checkpoint retention is controlled by `checkpoint.max-checkpoints`; world-backup retention uses `world-backup.keep`. A new backup is written and verified before the oldest backup is removed.
- **Investigated — no behavior change:** Cargo item filtering and Woodcutter/Programmable android block placement were left unchanged because they were identified as real workload rather than avoidable waste. A proposed wrapper-hoisting optimization was reverted because it could change slot selection after a partial insert.
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }