Database Sync
Anyone with SQL or admin access to your database (or /lvl syncdb, manual UPDATE/DELETE, phpMyAdmin, etc.) can read and change player progression outside the game. Incorrect edits, failed migrations, or conflicting writes between servers can corrupt or permanently lose level, XP, stats, or class data. Back up your database regularly before enabling sync or running manual SQL. The RPG Leveling mod author is not responsible for data loss, rollback, or damage caused by your configuration, third-party tools, or direct database manipulation - you are responsible for securing the DB, testing changes, and keeping backups.
Store player level, XP, stat allocations, classes, and reset points in an external database (MySQL, MariaDB, PostgreSQL, or H2). Use mirror mode for dashboards and analytics, or turn off mirroring so the database is the source of truth across several game servers.
Modes
Enabled | MirrorDataOnly | Behavior |
|---|
false | (ignored) | No database; progression stays in Hytale universe/players files only. |
true | true (default) | Mirror: in-game data is authoritative; the DB is updated when data changes. |
true | false | Shared progression: the DB is authoritative; data loads when a player joins; changes save to the DB. |
For multi-server networks, set the same JdbcUrl on every node, set MirrorDataOnly to false, and keep leveling config (max level, XP rates, etc.) aligned on all servers.
Configuration File
DatabaseConfig.json lives in mods/Zuxaw_RPGLeveling/.
Parameters
| Parameter | Type | Default | Description |
|---|
| Version | string | (plugin version) | Written by the plugin for compatibility. |
| Enabled | boolean | false | Connect to the database when true. |
| MirrorDataOnly | boolean | true | true = one-way mirror to DB. false = DB is source of truth (multi-server). |
| JdbcUrl | string | H2 file URL | JDBC connection string. |
| Username | string | "" | Database user (empty for H2 file). |
| Password | string | "" | Database password. |
| MaxPoolSize | integer | 10 | Connection pool size; use 10–20 on large servers with a remote DB. |
Mirror mode (analytics)
{
"Enabled": true,
"MirrorDataOnly": true,
"JdbcUrl": "jdbc:mariadb://localhost:3306/rpgleveling",
"Username": "rpguser",
"Password": "rpgpass",
"MaxPoolSize": 10
}
On startup, player files on disk are copied to the DB once (and again after a short delay). Whenever progression changes in-game, the DB is updated asynchronously.
Multi-server (database source of truth)
{
"Enabled": true,
"MirrorDataOnly": false,
"JdbcUrl": "jdbc:mariadb://db.example.com:3306/rpgleveling",
"Username": "rpguser",
"Password": "rpgpass",
"MaxPoolSize": 15
}
- No bulk disk→DB sync on startup (avoids overwriting shared data with empty local files).
- Each joining player: one DB read, then stats and HUD refresh. If a row exists, level / XP / stats / classes come from the database; claimed rewards, language, notification/HUD prefs, and class-change cooldown are merged from
universe/players/{uuid}.json when those columns are empty in the DB, then written back. If there is no row yet, the player file is copied to the database (first visit on the network).
- On first startup with an empty database, all
universe/players/*.json files are imported once (does not overwrite a DB that already has data).
- Writes are async and coalesced (~1 second per player) to reduce load with 100+ players.
- Pending writes flush when the player disconnects.
Admin command: /lvl syncdb
Permission: rpgleveling.command.syncdb
The game uses the JdbcUrl in DatabaseConfig.json, not any other database you inspect separately. If you query Docker MariaDB but the config still points at the default H2 file (jdbc:h2:file:...), pull/push only touch H2 - your SQL changes in MariaDB will appear to do nothing. Run /lvl syncdb status in-game to see the active URL, then restart the server after changing the config.
After manual SQL edits, pull data into the game while the player is online:
| Command | Action |
|---|
/lvl syncdb status | Show JDBC URL and mirror vs source-of-truth mode |
/lvl syncdb pull | Pull DB → you (in-game) |
/lvl syncdb pullplayer <name> | Pull DB → online player |
/lvl syncdb online | Pull DB → all online players (batched) |
/lvl syncdb push | Push your in-game data → DB |
/lvl syncdb pushplayer <name> | Push online player → DB |
/lvl syncdb pushonline | Push all online players → DB |
Or have the player rejoin after SQL changes (source-of-truth mode loads on join automatically).
Offline admin commands (/lvl setlevel, setpoints, setreset, resetstats, setclasstier, resetrewards, …) update universe/players/{uuid}.json and write through to the database immediately when sync is enabled.
Connection URLs
| Database | JDBC URL format |
|---|
| MariaDB | jdbc:mariadb://host:port/database |
| MySQL | jdbc:mysql://host:port/database |
| PostgreSQL | jdbc:postgresql://host:port/database |
| H2 (file) | jdbc:h2:file:./path/to/file;MODE=MySQL |
Table name: RPGLeveling_players (created automatically).
Table columns
| Column | Description |
|---|
uuid | Player UUID (primary key) |
username | Display name |
level, experience | Level and XP |
available_stat_points, allocated_stats | Stat points (JSON map) |
reset_points | Reset points from rewards |
selected_class_id, class_tiers, class_kills_in_tier | Class progression (JSON) |
claimed_reward_levels, language, hide_* prefs, last_class_change_epoch_ms | Rewards, UI prefs, class cooldown |
last_updated | Last write time (ms) |
Not synced: inventory, position, vanilla health, or other non-RPG components.
For developers
Adding a new persisted player field: use the Cursor skill add-player-sync-field and read src/main/java/org/zuxaw/plugin/database/README.md.
| Action | API |
|---|
| Save after in-game change | databaseSyncService.saveData(playerRef, store) |
| Read world / disk | readData(playerRef, store) / readData(uuid) |
| Read SQL | readDataFromDatabase(uuid) |
saveData no-ops when sync is disabled. Deprecated for external mods: syncIfEnabled, parseSnapshotFromDisk (still delegate to the new API).
- One
SELECT per player on join (source-of-truth mode), not a full table scan.
- Writes are coalesced per player to limit DB QPS during XP bursts.
- Tune
MaxPoolSize for your DB host; avoid oversized pools.
- Restart the server after changing
JdbcUrl or Enabled (/lvl reload updates the JSON in memory only).
Limitations
- See the warning callout at the top of this page before enabling database sync or editing rows manually.
- Prefer one active session per player across the network; two servers with the same player online can race (last write wins).
- Switching DB vendor does not migrate data automatically - export/import manually.
- Leaderboard still reads local player files; after DB load on join, local files should match.