Reproduction (from production logs, 2026-04-22):
02:54:13 - 02:54:44 player 724b9ff8 waits 30s for server 1708833664 (60 attempts)
02:54:31 - 02:55:02 player 46284b41 waits 30s for server 0 (zombie)
05:10:53 - 05:11:55 player 95d0db86 waits 62s for server 1708833664 (120 attempts)
05:10:59 - 05:12:01 player 724b9ff8 waits 62s for server 1708833664 (120 attempts)
User report: 'un joueur se connecte et son inventaire s'affiche 30 secondes
après sa connexion'.
Root cause: doPlayerJoin's last_server poll waits for the previous server to
clear online=0. If the peer is alive (heartbeat fresh) but the player is
ghost-online there (proxy bypass, network drop, or actively playing on the
other server without clean logout), the peer NEVER flushes → we wait the
full join_poll_max_attempts * join_poll_interval_ms (60s default) for
nothing. Meanwhile the player sees an empty inventory on this server.
The zombie-peer short-circuit already handled dead peers. This commit adds
the complementary case: ALIVE peers with a stuck session.
Fix:
- New config key join_peer_alive_max_wait_seconds (default 5, range 0-600).
- When the peer's heartbeat is fresh but player.online is still 1,
wait at most this many seconds, then force-claim ownership by setting
online=0 AND last_server=self.
- The peer will be prevented from overwriting us: writeSnapshotToDB
already has the last_server guard (added in Phase 2) which blocks any
future save the peer issues for this player — they see a GUARD log
and skip downstream backpack/SS/RS2 writes.
- Default 5s is a reasonable trade-off: legitimate slow flushes complete
within that window, ghost sessions don't block the player 60s+.
- Set to 0 to force-claim immediately (most aggressive, best for proxies).
- Set high to restore the legacy behavior (wait full poll length).
Also removed the per-tick 'Player X still being saved...' LOGGER.info line
that was spamming the Minecraft server log every 500ms during a ghost wait
— the SyncLogger.raceCondition entry already captures the same information
in the dedicated sync.log and avoids polluting server.log with 120+ lines
per join.
|
||
|---|---|---|
| .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