Skip to main content

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

EnabledMirrorDataOnlyBehavior
false(ignored)No database; progression stays in Hytale universe/players files only.
truetrue (default)Mirror: in-game data is authoritative; the DB is updated when data changes.
truefalseShared 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

ParameterTypeDefaultDescription
Versionstring(plugin version)Written by the plugin for compatibility.
EnabledbooleanfalseConnect to the database when true.
MirrorDataOnlybooleantruetrue = one-way mirror to DB. false = DB is source of truth (multi-server).
JdbcUrlstringH2 file URLJDBC connection string.
Usernamestring""Database user (empty for H2 file).
Passwordstring""Database password.
MaxPoolSizeinteger10Connection 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:
CommandAction
/lvl syncdb statusShow JDBC URL and mirror vs source-of-truth mode
/lvl syncdb pullPull DB → you (in-game)
/lvl syncdb pullplayer <name>Pull DB → online player
/lvl syncdb onlinePull DB → all online players (batched)
/lvl syncdb pushPush your in-game data → DB
/lvl syncdb pushplayer <name>Push online player → DB
/lvl syncdb pushonlinePush 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

DatabaseJDBC URL format
MariaDBjdbc:mariadb://host:port/database
MySQLjdbc:mysql://host:port/database
PostgreSQLjdbc:postgresql://host:port/database
H2 (file)jdbc:h2:file:./path/to/file;MODE=MySQL
Table name: RPGLeveling_players (created automatically).

Table columns

ColumnDescription
uuidPlayer UUID (primary key)
usernameDisplay name
level, experienceLevel and XP
available_stat_points, allocated_statsStat points (JSON map)
reset_pointsReset points from rewards
selected_class_id, class_tiers, class_kills_in_tierClass progression (JSON)
claimed_reward_levels, language, hide_* prefs, last_class_change_epoch_msRewards, UI prefs, class cooldown
last_updatedLast 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.
ActionAPI
Save after in-game changedatabaseSyncService.saveData(playerRef, store)
Read world / diskreadData(playerRef, store) / readData(uuid)
Read SQLreadDataFromDatabase(uuid)
saveData no-ops when sync is disabled. Deprecated for external mods: syncIfEnabled, parseSnapshotFromDisk (still delegate to the new API).

Performance notes

  • 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.