Two critical diagnostic/correctness improvements after user field report:
- '20s latency between inventory syncs with a full test inventory'
- 'duplication on throw + deposit in chest'
- 'bad sync on fast inter-server transfer if disconnect too quickly after modification'
(1) Real durations — 'completed in 0ms' was a lie
Every SyncLogger.saveCompleted / restoreCompleted call hardcoded 0 for the
duration field. The log line always showed 'in 0ms' regardless of actual
latency, making the user's 20s-latency reports impossible to reproduce from
logs alone. Fixed across all 4 save paths (LOGOUT / SHUTDOWN / DEATH /
EMERGENCY_FLUSH) and the RESTORE path. Durations are measured from the
start of the BG task (or the start of the restore lock acquisition) to
just before the success log line.
New info log 'Logout save completed for {uuid} in {n}ms'
New warn log '[perf-restore] slow restore for {uuid} ({n}ms)' above 1s
New info log '[perf-logout] core=Xms backpacks=Yms ss=Zms rs2=Wms total=Nms'
above 200 ms — breakdown so we can pinpoint which downstream write
takes the time in the reported 20s cases.
(2) Phase 9 force-takeover could CAUSE duplication
Phase 9 aimed to fix 30-60s join waits when the previous server was alive
but the player was ghost-online there. It force-claimed after 5s. But if
the peer was mid-way through a LEGITIMATE logout save (which is atomic
with online=1 -> online=0 via writeSnapshotToDB setOffline=true), force-
claiming before that commit read STALE DB data and restored the player
from the PRE-disconnect state — e.g., an item the player dropped just
before disconnect came back in inventory, duplicating with the ItemEntity
the peer had already spawned in the world.
Fix: the wait cap is now ADVISORY, not a hard force-claim. Past the cap,
we only force-claim when the peer's heartbeat has FROZEN (age > cap ms)
— meaning the peer's process is actually dead or stuck mid-tick, not
just slow to flush. If the peer is still heartbeating normally, we keep
waiting: writeSnapshotToDB + online=0 is an atomic UPDATE, so the flush
WILL land, we just need to be patient. A warn line every 20 attempts
(10s at default interval) tells admins the save is taking a long time
so they can profile the peer's DB connection.
New helper peerHeartbeatAgeMs(id) returns age in ms, Long.MAX_VALUE if
the peer has no heartbeat row. Used to decide force-claim vs keep-waiting.
|
||
|---|---|---|
| .github | ||
| compat-mods | ||
| gradle/wrapper | ||
| src/main | ||
| .gitattributes | ||
| .gitignore | ||
| build.gradle | ||
| CHANGELOG.md | ||
| docker-compose.yml | ||
| ERROR_LOG.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| README.md | ||
| settings.gradle | ||
| TEST_PROCEDURE_v2.1.5.html | ||
PlayerSync
PlayerSync is a Minecraft Forge mod that synchronizes player data across multiple servers using a MySQL backend. It allows players to maintain their inventory, equipment, experience, advancements, and more when moving between servers in a network.
Mod Support
Any other mods support is also possible.
Development Setup
Database Setup (Docker)
A docker-compose.yml file is provided for easily setting up a MariaDB database instance for development testing.
- Make sure Docker is installed.
- Inside your work directory run:
This will download the MariaDB image (if not already present) and start a database container in the background.docker compose up -d - Stoppinng the Database
docker compose down
Data Persistence: The database uses a Docker volume, ensuring your data persists even if you stop and restart the containers.
Database Management Tool
The docker-compose.yml also includes an Adminer service, a lightweight database management tool.
- Access Adminer in your web browser at http://localhost:8080.
- Log in using the server with
- username:
playersync - database:
playersync - password: see docker-compose.yml
- username:
For debugging purposes, you can enable use_legacy_serialization to have readable database fields. This can cause crashes and unintended side-effects. Do not enable this on a production server if not absolutely necessary!
Running the Mod
The project uses Gradle for building and running. Use the provided Gradle wrapper (gradlew for Linux/macOS, gradlew.bat for Windows).
- Make sure that the MySQL database you configured is running.
- Run the Server
or on Windows:./gradlew runServer
This task compiles the mod and starts a dedicated Minecraft server instance with the mod loaded in the.\gradlew.bat runServerrundirectory. - Run the Client
or on Windows:./gradlew runClient.\gradlew.bat runClient