Storage
Where player progress is saved, databases, network setups and moving data.
By default player progress is stored in flat files, one per player, in plugins/zQuestRank/playerdata/<uuid>.yml. No database needed, and if you run one server you can leave it like that forever.
Since 1.5.0 you can also keep the data in a database. Pick one in storage.yml, a new file next to config.yml. A shared database (MySQL, MariaDB or MongoDB) lets every server of a BungeeCord or Velocity network use the same ranks and quest progress, so a player can start a quest on one server and finish it on another.
Nothing to do on update. storage.yml is created with type: YAML, which works exactly like before, and your playerdata/ folder stays as it is.
Storage types
| Type | For | Where the data lives |
|---|---|---|
YAML | One server (default) | One file per player in playerdata/ |
SQLITE | One server | A single database file, zquestrank.db in the plugin folder |
H2 | One server | A single database file, zquestrank.mv.db in the plugin folder |
MYSQL | A whole network | Your MySQL server |
MARIADB | A whole network | Your MariaDB server |
MONGODB | A whole network | Your MongoDB server (4.2 or newer) |
SQLITEandH2are still for one server only. They are handy if you just prefer one database file over a folder full of small files. Two servers can not share them.- The H2 driver and the MongoDB driver are not inside the jar. They are downloaded once into
plugins/zQuestRank/libon the first start, so the server needs internet access that one time (or you put the jars in that folder yourself). MySQL, MariaDB and SQLite need no download. - The plugin makes its own tables (or collections for MongoDB), all starting with
zqr_. You only create the empty database.
storage.yml is read on startup only. /rank admin reload does not switch storage, restart the server after changing it.
storage.yml
Every key has a comment above it in the file too.
| Key | What it does | Default |
|---|---|---|
storage-version | Used by the updater to add new keys. Do not touch | 1 |
type | YAML, SQLITE, H2, MYSQL, MARIADB or MONGODB. A typo falls back to YAML | YAML |
server-name | Name of this server inside your network, shown in logs and used when moving data. Give every server its own | server |
sync-interval-ticks | How often (20 ticks = 1 second) progress is sent to the database and changes from other servers are pulled in | 20 |
sqlite.file | Database file inside the zQuestRank folder | zquestrank.db |
h2.file | Database file inside the zQuestRank folder, without the .mv.db ending | zquestrank |
mysql.host / mysql.port | Database server address and port. Used for MYSQL and MARIADB | localhost / 3306 |
mysql.database | Database name | zquestrank |
mysql.username / mysql.password | Login for the database | root / empty |
mysql.use-ssl | Connect over SSL | false |
mysql.table-prefix | Start of every table name. Must be the same on every server | zqr_ |
mysql.jdbc-url | A full JDBC URL, used instead of the values above when filled in | empty |
mongodb.uri | Connection string, with the login inside if your database needs one | mongodb://localhost:27017 |
mongodb.database | Database name | zquestrank |
mongodb.collection-prefix | Start of every collection name. Must be the same on every server | zqr_ |
messaging.type | NONE, REDIS or RABBITMQ. See Instant sync | NONE |
messaging.channel | Redis channel or RabbitMQ exchange name, the same on every server | zquestrank |
messaging.redis.host / port | Redis address and port | localhost / 6379 |
messaging.redis.username / password | Redis login. Leave the user empty for a plain password setup, both empty for none | empty |
messaging.redis.use-ssl | Connect over SSL | false |
messaging.rabbitmq.host / port | RabbitMQ address and port | localhost / 5672 |
messaging.rabbitmq.username / password | RabbitMQ login | guest / guest |
messaging.rabbitmq.virtual-host | RabbitMQ virtual host | / |
messaging.rabbitmq.use-ssl | Connect over SSL | false |
migration.migrate-data | Copy every player into the new type on the next start. Turns itself off when done. See Moving your data | false |
migration.locked-to | Managed by the plugin: the storage this server uses right now. Do not edit | empty |
migration.completed-at | Managed by the plugin: when the last move finished. Do not edit | 0 |
Sync between servers
With a shared type, every server reads and writes the same data:
- A player's rank and progress load from the database when they join, whichever server that is.
- Progress is sent to the database every
sync-interval-ticks(one second by default) and again when the player leaves or switches server. So when someone hops from survival to skyblock mid-quest, their count is already there waiting. - Changes made on another server (a claim, an admin
setrank) are pulled in on the next sync.
The default of 20 ticks is fine for most networks. Raising it means fewer database writes but a slightly longer wait before other servers see a change.
Instant sync with Redis or RabbitMQ
Optional. With messaging.type set to REDIS or RABBITMQ, a server tells the others about a change straight away instead of waiting for their next sync. Handy when players hop between servers a lot and you want them to never see an old number, even for a second.
- It only works with a shared type (
MYSQL,MARIADB,MONGODB). WithYAML,SQLITEorH2it is ignored and the console says so. - If Redis or RabbitMQ can not be reached, the plugin carries on without it and the console says so. Nothing breaks, changes just arrive on the normal sync.
- Every server must use the same
messaging.channel. - The RabbitMQ client is downloaded once into
plugins/zQuestRank/lib. Redis needs nothing extra.
No double rewards
On a network two servers could in theory try to pay out the same thing at the same moment, for example a player clicking Claim Rank on one server just as they switch to another. The plugin makes sure only one server ever wins:
- A quest reward is paid once, by one server only.
- A rank claim goes through once. Money, XP and items are taken and rewards are given only by the server that won. If the player clicks Claim on a menu that is already out of date, they just get "This rank was already claimed." and nothing is paid twice.
Setting up a network
Example: two servers, survival and skyblock, sharing one MySQL database, with Redis for instant sync.
Make the database
Create an empty database called zquestrank on your MySQL server and a user that can use it. The plugin makes the tables itself.
Fill in storage.yml on every server
Same settings everywhere, only server-name changes:
type: MYSQL
server-name: "survival" # "skyblock" on the other server
sync-interval-ticks: 20
mysql:
host: "10.0.0.5"
port: 3306
database: "zquestrank"
username: "zqr"
password: "yourpassword"
use-ssl: false
table-prefix: "zqr_"
jdbc-url: ""
messaging:
type: REDIS
channel: "zquestrank"
redis:
host: "10.0.0.5"
port: 6379
username: ""
password: "redispassword"
use-ssl: falseBring old progress along (if any)
If a server already has players in its playerdata/ folder, also set migration.migrate-data: true on that server so their progress is copied in. See Moving your data. A brand new server with no player files just switches on its own.
Keep ranks.yml the same
Use the same ranks.yml on every server, so rank keys and quest ids match everywhere. Then restart all servers.
On startup the console shows a line like Storage: MYSQL at ... (server-name: survival) and Messaging: REDIS at .... That means it is connected.
If the database is down
- At startup: if the database can not be reached (wrong password, server offline, driver could not be downloaded), zQuestRank stops itself with a clear message in the console saying what went wrong. Fix
storage.ymlor the database, then restart. It never quietly falls back to local files, so no progress ends up in the wrong place. - While running: if a player's data can not be loaded when they join, they get a short kick message asking them to try again in a moment (
general.storage-load-failedin the lang file), instead of joining with empty progress.
Moving your data
You can move player data from any storage to any other one: YAML to MySQL, SQLite to MongoDB, MySQL back to YAML, anything.
Change the type
In storage.yml set type to where the data should go, and fill in that section (for example mysql:).
Turn on the move
Set migration.migrate-data: true.
Restart
On startup every player is copied from the old storage into the new one. The plugin checks the player count after the copy, and only then switches over. migrate-data turns itself back off.
Good to know:
- The old data is kept as a backup. A YAML folder is renamed to
playerdata.migrated. Old database tables are left alone. When moving to YAML and aplayerdata/folder already exists, it is moved aside toplayerdata.before-<time>first. - If something fails, nothing switches. The server stays on the old storage, untouched, and the console says why. Fix it and restart to try again.
- Without
migrate-data, nothing moves. If you changetypebut forget the flag, the server keeps using its old storage and the console tells you exactly what to do. (A fresh server with no player files just switches, since there is nothing to copy.) - On a network, do it per server. Each server that has its own
playerdata/folder copies its players in on its own restart. Running the move again is safe, players already copied are skipped. migration.locked-toandmigration.completed-atare written by the plugin to remember which storage this server is on. Leave them alone.
What is stored
- The player's current rank.
- Progress numbers for each quest of the current rank.
- Which of those quests are completed, and whether their quest reward was already paid.
- For
FIND_HEAD,EXPLOREandKILL_PLAYER(withunique-players) quests, the heads, places or players already counted, so the same one never counts twice. - The last rank they claimed and the last rank whose
on-reachcommands ran for them. These two keep a leftover rank permission from pulling a player back onto a rank they already finished, and they are what lets a rank added later be handed out on its own.
Progress always belongs to the current rank. When a player claims a rank (or an admin uses setrank), the quest progress resets and the next rank starts fresh. Finished ranks stay finished; the plugin only needs to remember where the player currently stands.
When it saves
- Data loads when a player joins and unloads when they leave.
- With YAML, every progress change saves in the background. With a database, changes are sent every
sync-interval-ticks. Either way a crash loses next to nothing. - Everything saves when the player leaves and on server shutdown.
Progress survives relogs, restarts, server switches and plugin updates.
Config backups
plugins/zQuestRank/backups/ holds a copy of config.yml, storage.yml and of your language file from just before an update changed them, named after the version they had (config.yml.9.bak). Nothing is copied on a normal restart, only when a newer build is about to merge new keys in. If an update ever leaves a file looking wrong, copy the .bak back over it, rename it, and restart. ranks.yml is never rewritten by the plugin at all, so it needs no backup of this kind.
Resetting a player
/rank admin resetquests <player>wipes the quest progress of their current rank./rank admin setrank <player> <number>moves them to any rank (also resets the quest progress, since it belongs to the new rank).- With YAML, deleting a player's file in
playerdata/while they are offline resets them completely to rank one. With a database, usesetrank <player> 1instead.
Both commands need the player online on the server where you run them. On a network the change then reaches the other servers on the next sync (or right away with Redis or RabbitMQ).
See Commands for the admin command details.