Compatibility
Minecraft: Java Edition
Platforms
Tags
Creators
Details
InvSYNC
Cross-server player data synchronisation over MySQL, for Paper 1.21+.
Author: LucasTHCR - https://dc.gg/paperstream
What it does
Shares inventory, armour, offhand, ender chest, XP, health, hunger, potion effects and status values (absorption, exhaustion, air, burning, fall distance) between any number of servers through one MySQL database. Every part can be switched off individually.
Custom and modded items are stored through the server's own item format, so full NBT and component data survives. No ProtocolLib, no extra dependency.
What makes it different
Most inventory sync plugins write on quit, read on join and put a fixed delay in between. When the timing does not hold, items duplicate or a session is lost. InvSYNC does not rely on timing.
Ownership instead of a delay. A player's row is claimed by exactly one server at a time. When the player leaves, that server writes their data and hands the row back. The next server waits for the handover, not for a timer. Two servers can never hold the same player at once.
Everything happens before the player enters the world. Claiming, reading and decoding run in the pre-login phase. A failure ends as a login refusal rather than a half applied inventory.
Failures never destroy data.
| Situation | What happens |
|---|---|
| Another server still owns the data | Login refused, retry in a moment |
| Stored data cannot be decoded | Login refused, stored data untouched, copy kept for inspection |
| Data written by a newer Minecraft version | Refused. Item data can be upgraded, never downgraded |
| Database unreachable at login | Login refused, because letting a player in without their data means saving that emptiness later |
| Database unreachable at save | Write parked on disk and retried; ownership stays held so no other server serves the outdated revision |
| Inventory cannot be read | Previous revision stays, ownership handed back |
| Server crashed while holding rows | Released automatically on next start |
Empty is not the same as missing. A synchronised but empty inventory is applied, a section that is switched off is left alone. Collapsing the two duplicates items: a player who empties their inventory on one server gets the contents back on the next one.
Snapshots. Every save keeps a restorable copy. /invsync snapshots <player>
lists them, /invsync restore <player> <id> rolls one back.
Install
- Create an empty MySQL database. InvSYNC creates the tables itself.
- Drop the jar into
plugins/on every server that should share data. - Start once to generate
config.yml, then stop. - Fill in the database section.
- Give every server its own
server-id. Leave it empty to derive one from the hostname and port, which is fine as long as no two servers collide. - Start.
All servers must run the same Minecraft version, or at least never an older one than the version that last wrote the data.
Commands
| Command | Purpose |
|---|---|
/invsync status |
Connection, ownership, parked saves |
/invsync save <player> |
Write their data now |
/invsync unlock <player> |
Release data stuck on a server that will not come back |
/invsync snapshots <player> |
List restorable copies |
/invsync restore <player> <id> |
Roll back to a copy |
/invsync reload |
Re-read config.yml |
Permissions: invsync.admin covers all of them, or grant
invsync.admin.<subcommand> individually.
invsync.sync (default true) marks a player as synchronised, invsync.bypass
excludes them entirely.
Configuration notes
database.* and server-id are only read on startup. Everything else is picked
up by /invsync reload.
ownership.on-timeout decides what happens when the previous server does not hand
the data back in time. deny refuses the login and loses nothing. takeover
loads the data anyway and can duplicate items. The default is deny.
auto-save only protects against crashes. A normal quit always saves.
Building
mvn clean package
Produces target/InvSYNC-1.0-RELEASE.jar. MySQL Connector and HikariCP are shaded
and relocated, so the jar has no external dependencies.
Tests
mvn test
The ownership tests start a real MariaDB for the run, because the locking depends
on MySQL behaviour that a compatibility layer does not reproduce: INSERT IGNORE,
affected row counts and InnoDB row locking. That server binary is a 149 MB test
artifact; mvn package -DskipTests avoids it entirely.
What is covered:
- 1000 races between 8 servers claiming the same player, exactly one winner each.
- 500 consecutive server switches without losing or forking a revision.
- Stale takeover, crash recovery scoping, refused writes after ownership moved, snapshot pruning, maintenance skipping claimed rows.
- 2000 randomised snapshot round trips, plus format 1 and format 2 payloads decoding correctly under the current version.
- Truncated, foreign and newer-format payloads raising an error rather than producing plausible looking data.
- 1000 randomised economy settle runs against a fake economy plugin that loads late and overwrites, asserting no run ever reports success with a wrong balance.
Not covered: anything that needs a running Paper server, which includes item serialisation itself and the real Vault path.
Limits worth knowing
- Ender chest size is assumed to be the vanilla 27 slots. Plugins that resize it are not accounted for.
- Economy balances can be synchronised through Vault, but the feature is off by
default and needs a second switch to enable. A balance changed while the player
is offline is overwritten on their next login, and no design inside this plugin
can prevent that. Pointing the economy plugin at a shared database is the better
answer. See
docs/vault-economy-plan.md. - Homes, claims and other plugin data are not synchronised.
- Folia is not supported. Scheduling is confined to a few call sites, so adding it later is not a rewrite.
Metrics
InvSYNC reports anonymous usage data to bStats: server software and version, Java version, player count, and a handful of InvSYNC settings (timeout behaviour, which sections are synchronised, snapshot count, whether economy sync is on, and how many saves are currently parked on disk). No player names, no UUIDs, no inventory data, no database credentials.
Opt out for every plugin at once in plugins/bStats/config.yml.
License
GNU General Public License v3.0. See LICENSE.


