Shinoyuki-BetterAutoSave

Shinoyuki-BetterAutoSave

Async world saving for Forge 1.20.1 servers — chunk, entity and saved-data serialization moved off the main thread. Kills autosave lag spikes.

von
9.1K Downloads
forgeneoforgeoptimization
Server mit dieser Mod mieten

Über diese Mod

BetterAutoSave

简体中文 | English

BetterAutoSave

Make server autosaves stutter-free ~
Parts of this mod's code were generated by Claude Opus 4.8 / Claude Fable 5. If you run into any issue, please open an issue

Download: Modrinth · GitHub Releases

Project status: actively developed and in a fast pre-1.0 iteration phase, with frequent updates (including releases coordinated with BetterBackup). The core async save has been validated in production for a long time and is safe by default; async chunk loading is a newer, opt-in feature and is off by default. Watch Releases / Modrinth for updates

What problem does this mod solve

A vanilla Minecraft server autosaves every 5 minutes. During that save, the main thread has to serialize every modified chunk and write it to disk — and the whole server is frozen while it happens. On an empty server you will not notice, but on a server with many mods and players this pause is routinely 200 ms to several seconds, and every player lags at once.

Besides the periodic autosave, several other moments stutter the same way: players teleporting or large numbers of chunks being unloaded (a chunk must be saved before it leaves memory), entity-dense areas during a save (large farms / mob grinders), and global data such as villages and raids (vanilla SavedData) where a single large file hits the disk.

BAS makes the main thread do only the one thing that must happen in place — taking an independent snapshot of the data to be saved. Serialization and disk IO are handed to background threads. Because the background works on copies, it never interferes with the main thread, which lets go immediately after the snapshot. Chunks, entities and saved data all go through this pipeline. When the server is struggling BAS automatically slows down, but forces full speed as the next autosave cycle approaches so a backlog can never build up.

Requirements and installation

Both loaders are maintained from the same source. Server-side only on both; clients do not need to install it.

  • Forge 1.20.1: Forge 47.3.22 or newer (47.3 / 47.4 lines both work), Java 17 or newer
  • NeoForge 1.21.1: NeoForge 21.1 line, Java 21 or newer

Download the jar matching your loader from Modrinth or Releases and drop it into the server's mods/ folder:

  • For Forge, use the -all jar (named like shinoyuki_betterautosave-<version>-all.jar; it bundles MixinExtras and other dependencies). The plain thin jar crashes on load for missing dependencies.
  • For NeoForge, use shinoyuki_betterautosave-neoforge-<version>.jar.

After the first launch the config file is generated at config/Shinoyuki-Optimize/shinoyuki_betterautosave/common.toml. The defaults work out of the box; most servers do not need to change anything.

Will it lose world data?

No. BAS is designed on one premise: it must never be less safe than vanilla.

  • On shutdown it waits for every pending save to hit the disk before letting the server exit, and the final save goes through the vanilla synchronous path.
  • BAS never "holds saves for later" — a chunk enters background processing the moment it should be saved. There is no "nothing saved for minutes, crash loses it all" window (some similar mods have this problem).
  • If a background write fails it retries automatically and never pretends it succeeded: chunks and saved data fall back to the vanilla synchronous write once retries are exhausted; entities have no coordinate recovery queue and are already evicted from memory by vanilla, so an exhausted retry logs an ERROR and drops that chunk's latest entity increment — the same outcome as vanilla here (vanilla entity saving likewise has no retry and no synchronous fallback; BAS actually retries a few more times first).

Beyond that, the Forge build also fixes three vanilla paths that silently lose data (player data read failure, truncating writes for advancements and stats, and level.dat having only a single backup). Those fixes are on by default — see the configuration reference.

Common configuration

Key Default Description
general.enabled true Master switch; off means vanilla behavior, as if not installed
throttle.chunksPerTickBase 4 Max chunks snapshotted by the main thread per game tick
throttle.adaptiveEnabled true Slow down automatically when the server struggles; keep it on
workers.chunkWorkerThreads 2 Background threads for chunks
workers.entityWorkerThreads 2 Background threads for entities
workers.savedDataWorkerThreads 1 Background threads for saved data; raise to 2 with mods that write a lot of vanilla SavedData
compat.eventCompatMode PARTIAL Event compatibility level; leave it alone unless you know you need it

The Forge build has 43 settings, the NeoForge build 26. Every setting, why each default is what it is, and the recommended rollout path are documented in CONFIGURATION.en.md, covering player data protection, level.dat integrity, async chunk loading, Prometheus monitoring and working with backup tools.

Feature matrix across the two builds

The two builds are not identical. Some gaps exist because NeoForge fixed the problem upstream, so the corresponding option is unnecessary there; others are Forge-first and not yet ported symmetrically.

Feature Forge 1.20.1 NeoForge 1.21.1
Async saving (chunks / entities / SavedData) yes yes
Async chunk loading ([load] section) yes no (the section does not exist; the NeoForge build has no load-side mixins at all)
level.dat registry cache yes not needed (upstream moved the table out of level.dat)
level.dat startup check yes not needed (1.21 already falls back and quarantines on a read failure)
level.dat startup backup / post-write verify yes no (1.21 has no equivalent either; port pending)
playerdata read fallback yes not needed (fixed upstream in 1.21)
advancements / stats atomic write yes no (1.21 still truncates on write; port pending)
advancements dirty skip, staggered player saving yes no (performance only; port pending)
Sync chunk load / inter-tick gap diagnostics yes yes

In-game commands

Requires OP (permission level 2).

Command Effect
/betterautosave status One-line current status
/betterautosave metrics One-line metrics summary
/betterautosave debug Full diagnostics: queue depths, per-stage timings, counters
/betterautosave flush Drain every pending save to disk. The command returns immediately and polls in the background until it completes or times out (safety.shutdownTimeoutSeconds)
/betterautosave drain-unload Wait for all pending chunks to land; likewise polls in the background and returns immediately
/betterautosave hottest-chunks [count] List the slowest-saving chunks (default 10, accepts 1-50) to locate hotspots
/betterautosave force-async Force one background save pass over all chunks in the current dimension (diagnostic)
/betterautosave diagnose [count] List the sources of main-thread synchronous chunk loads and the inter-tick gap statistics (default 10, accepts 1-50)
/betterautosave diagnose reset Clear both tables above (cumulative counters are kept; see below)

High-cost chunks usually sit where block entities are dense — large automated farms, mod shop panels, complex redstone.

The stalls ordinary monitoring cannot see

Two kinds of stall never show up on a normal dashboard. Since 0.20.0 BAS records both itself — observing only, never intervening, on by default.

Main-thread synchronous chunk loads. When a chunk is not in memory and something on the main thread asks for it directly, the main thread waits in place until the disk read — and if needed, terrain generation — has finished, and the whole server is frozen meanwhile. A production stress test with 74 players measured a single wait of 5.2 seconds. BAS records every wait over 50 ms (threshold configurable): how long it blocked, the chunk coordinates, the dimension, and the first non-vanilla class on the call stack.

Long pauses between ticks. MSPT only measures time spent inside a game tick; the wait between two ticks is not counted. The same stress test contained pauses of 17.1 and 14.8 seconds that were invisible on the TPS graph and on the dashboard — everything looked healthy while players were timing out. BAS takes one timestamp at the start and one at the end of every tick and records any gap over 1 second (configurable); an optional deep mode, off by default, attributes a gap to the individual task that caused it.

Cost: the sync-load probe sits on the branch taken after vanilla's four-slot chunk cache misses, so it is not reached at all on a cache hit; on a miss it only adds two nanosecond reads, and a stack is captured solely once the wait exceeds the threshold — never during normal operation. The tick gap check is two nanosecond reads per tick.

On attribution: the detection reports which call chain the block happened on, not who has a bug. Stalls reported by this feature usually reflect another mod's call pattern — fetching a chunk synchronously is perfectly reasonable in many situations, its cost simply scales with server size, view distance and disk speed — and are not by themselves evidence of a defect in that mod. Treat it as the starting point of an investigation, not its conclusion.

Read it with /betterautosave diagnose, or scrape the four new Prometheus metrics (bas_sync_load_stalls_total, bas_tick_gap_max_seconds and two more). The eight settings and a full sample of the command output are in section 8 of CONFIGURATION.en.md.

Design boundary

BAS has been evaluated to the end of what it can do here: under the extreme compatibility constraints it holds itself to, there is no meaningful async chunk optimization left to take. Where room does remain, taking it would break that compatibility — producing data-safety problems and conflicts between mods.

In a production stress test with 74 players online, sampled over 550 seconds, NbtIo writes accounted for 0.03% of main-thread time, ChunkSerializer serialization for 0.67%, and everything BAS itself does for 1.42% in total. That is not the same as "there is no optimization left" — the same sample still shows roughly 0.3 percentage points on the table (copySections unconditionally makes two PalettedContainer.copy calls even for empty sections, about 0.1 pp; batching the POI replay, about 0.1 to 0.2 pp). But that is already down in the noise, and what can be taken without changing the compatibility premise adds up to less than half a percentage point.

It is also not the same as "BAS makes chunk loading stop being the bottleneck". The opposite is true: the bottleneck sits in the half of the chunk system BAS cannot reach. In that same sample, DistanceManager distance-field propagation consumed 82% of the chunk system's main-thread budget, and the tasks actually driving loads forward only 18%.

What each further step would break is concrete. Moving ForgeCaps onto a worker thread belongs to the same family as the data loss in issue #8: mods that attach a chunk capability lose data silently. Moving ChunkDataEvent.Load onto a worker thread calls every listener off the main thread; it throws nothing and simply rots over time. Moving POI / SectionStorage onto a worker thread runs into SectionStorage not being thread-safe, and the outcome is silently corrupted villager AI data. Taking over DistanceManager means a state machine that is not thread-safe, and a head-on conflict with C2ME. Requiring installation on both sides, or forcing everything async, gives up single-side installation, opt-in and instant rollback — the largest differentiator BAS has.

Performance has reached the limit compatibility allows, so this release changes direction: instead of chasing those last fractions of a percent, BAS now tells server owners where the stalls actually come from. The full reasoning is in ROADMAP.md (Chinese).

Mod conflicts

  • Cannot be installed together (all take over the same save path): Fast Async World Save (fastasyncworldsave, BAS logs a WARN when it detects this one), Smooth Chunk Save, and other async / per-tick save mods.
  • C2ME / C2ME-Forge: split it by feature. The save side is pick-one; parallel loading is complementary under BAS's default config but becomes pick-one once BAS async loading is enabled; worldgen is always complementary.
  • Compatible: Starlight, Radium / Canary, Modernfix, FerriteCore and similar.

The reasoning, the full list of injection points and the data-integrity contract of each compatibility level are in COMPATIBILITY.en.md.

Quick recovery if something goes wrong

All three options keep world data intact:

  1. Disable temporarily: set general.enabled to false, restart or /reload. The mod stays installed but all logic is skipped — pure vanilla.
  2. Uninstall completely: move the jar out of mods/ and restart. World data remains protected by vanilla saving; uninstalling loses nothing.
  3. Tune instead of removing: if you suspect a performance setting, adjust chunksPerTickBase (1-64) or switch eventCompatMode to FULL first — no need to uninstall.

Building / development

./gradlew build                 # compile + run all tests (common / forge / neoforge)
./gradlew :forge:runServer      # start a 1.20.1 Forge dev server
./gradlew :neoforge:runServer   # start a 1.21.1 NeoForge dev server

Module layout: common/ (zero-Minecraft pure-algorithm core, source-merged into both loaders — the crown-jewel save state machine lives here once, never forked) + forge/ (1.20.1) + neoforge/ (1.21.1).

The version roadmap and capability overview are in ROADMAP.md (Chinese); dual-version porting details are in archive/MULTIVERSION_PLAN.md (Chinese).

License

AGPL-3.0-or-later, with two section 7 additional permissions (LICENSE-EXCEPTION.md): a modpack distribution exception — unmodified official release jars may be included verbatim in modpacks and server packs with no obligation beyond keeping the project name and a repository link — and a Minecraft linking exception explicitly permitting combination with Minecraft itself and LGPL-licensed mod loaders. Modified versions of this mod remain under the full AGPL, including its section 13 network terms.

Verfügbare Versionen

BetterAutoSave v0.20.1 (NeoForge 1.21.1)release
MC 1.21.1neoforge
22. August 2026
BetterAutoSave v0.20.1 (Forge 1.20.1)release
MC 1.20.1forge
22. August 2026
BetterAutoSave v0.20.0 (NeoForge 1.21.1)release
MC 1.21.1neoforge
21. August 2026
BetterAutoSave v0.20.0 (Forge 1.20.1)release
MC 1.20.1forge
21. August 2026
BetterAutoSave v0.19.0 (NeoForge 1.21.1)release
MC 1.21.1neoforge
3. August 2026

Shinoyuki-BetterAutoSave auf dem Server installieren

1

Server bestellen

Bestelle einen Minecraft Java Server mit mindestens 3 GB RAM (4 GB empfohlen).

2

forge Loader setzen

Wähle im Panel unter "Egg" den forge-Loader und die passende Minecraft-Version (1.21.1).

3

Mod installieren

Öffne den Mod-Browser im Dashboard und suche nach "Shinoyuki-BetterAutoSave". Klicke "Installieren" – fertig! Alternativ: Lade die .jar via SFTP in den /mods Ordner.

Kompatibilität

Mod-Loader

forgeneoforge

Minecraft-Versionen

1.21.1, 1.20.1

Server-seitig

Erforderlich

Empfohlener RAM

4 GB(min. 3 GB)

Häufige Fragen

Shinoyuki-BetterAutoSave Server crasht beim Start – was tun?

Häufigste Ursache: falsche forge-Version oder zu wenig RAM. Prüfe im Server-Log (latest.log), ob ein "OutOfMemoryError" oder "Mixin"-Fehler auftritt. Bei Mado Hosting: Stelle sicher, dass mindestens 3 GB RAM zugewiesen sind und der Loader zur Mod-Version passt (1.21.1). Über das Panel kannst du den Loader mit einem Klick wechseln.

Ist Shinoyuki-BetterAutoSave mit forge und neoforge kompatibel?

Shinoyuki-BetterAutoSave unterstützt offiziell forge, neoforge für Minecraft 1.21.1, 1.20.1. Im Mado Dashboard werden inkompatible Loader-Kombinationen automatisch erkannt.

Server laggt mit Shinoyuki-BetterAutoSave – wie optimiere ich die Performance?

Empfohlener RAM: 4 GB (+1 GB pro 8 Spieler). Prüfe mit /spark profiler, ob Shinoyuki-BetterAutoSave den meisten Tick-Time verbraucht. Häufige Fixes: Server-View-Distance auf 8-10 reduzieren, bei Forge "performant" oder "starlight" als Zusatz-Mod installieren. Bei Mado Hosting läuft dein Server auf NVMe-SSDs mit dedizierten CPU-Kernen für minimale Latenz.

Modded Server mieten

Installiere Shinoyuki-BetterAutoSave mit nur einem Klick auf deinem Server.

Empfohlener RAM
4 GBab €5.2/Monat
Min. 3 GB | +1 GB pro 8 Spieler
Jetzt Server erstellen
1-Klick Mod Installation
NVMe SSD Speicher
DDoS-Schutz inklusive

Details

Lizenz
GNU Affero General Public License v3.0 or later
Server-seitig
Erforderlich

Unterstützte Versionen

1.21.11.20.1