zQuestRank

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

TypeForWhere the data lives
YAMLOne server (default)One file per player in playerdata/
SQLITEOne serverA single database file, zquestrank.db in the plugin folder
H2One serverA single database file, zquestrank.mv.db in the plugin folder
MYSQLA whole networkYour MySQL server
MARIADBA whole networkYour MariaDB server
MONGODBA whole networkYour MongoDB server (4.2 or newer)
  • SQLITE and H2 are 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/lib on 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.

KeyWhat it doesDefault
storage-versionUsed by the updater to add new keys. Do not touch1
typeYAML, SQLITE, H2, MYSQL, MARIADB or MONGODB. A typo falls back to YAMLYAML
server-nameName of this server inside your network, shown in logs and used when moving data. Give every server its ownserver
sync-interval-ticksHow often (20 ticks = 1 second) progress is sent to the database and changes from other servers are pulled in20
sqlite.fileDatabase file inside the zQuestRank folderzquestrank.db
h2.fileDatabase file inside the zQuestRank folder, without the .mv.db endingzquestrank
mysql.host / mysql.portDatabase server address and port. Used for MYSQL and MARIADBlocalhost / 3306
mysql.databaseDatabase namezquestrank
mysql.username / mysql.passwordLogin for the databaseroot / empty
mysql.use-sslConnect over SSLfalse
mysql.table-prefixStart of every table name. Must be the same on every serverzqr_
mysql.jdbc-urlA full JDBC URL, used instead of the values above when filled inempty
mongodb.uriConnection string, with the login inside if your database needs onemongodb://localhost:27017
mongodb.databaseDatabase namezquestrank
mongodb.collection-prefixStart of every collection name. Must be the same on every serverzqr_
messaging.typeNONE, REDIS or RABBITMQ. See Instant syncNONE
messaging.channelRedis channel or RabbitMQ exchange name, the same on every serverzquestrank
messaging.redis.host / portRedis address and portlocalhost / 6379
messaging.redis.username / passwordRedis login. Leave the user empty for a plain password setup, both empty for noneempty
messaging.redis.use-sslConnect over SSLfalse
messaging.rabbitmq.host / portRabbitMQ address and portlocalhost / 5672
messaging.rabbitmq.username / passwordRabbitMQ loginguest / guest
messaging.rabbitmq.virtual-hostRabbitMQ virtual host/
messaging.rabbitmq.use-sslConnect over SSLfalse
migration.migrate-dataCopy every player into the new type on the next start. Turns itself off when done. See Moving your datafalse
migration.locked-toManaged by the plugin: the storage this server uses right now. Do not editempty
migration.completed-atManaged by the plugin: when the last move finished. Do not edit0

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). With YAML, SQLITE or H2 it 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: false

Bring 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.yml or 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-failed in 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 a playerdata/ folder already exists, it is moved aside to playerdata.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 change type but 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-to and migration.completed-at are 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, EXPLORE and KILL_PLAYER (with unique-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-reach commands 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, use setrank <player> 1 instead.

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.

On this page