Skip to content

Database

BeestoXd edited this page Aug 15, 2026 · 1 revision

Database

Everything persistent lives in one database: spawners, their stored loot, and (with the internal economy) player balances.

Choosing a backend

DATABASE:
  TYPE: SQLITE          # SQLITE | MYSQL | MARIADB
Backend Good for
SQLite (default) Any single server. No setup, no external process, a single file.
MySQL / MariaDB Sharing data across servers, or an existing central database.

Changing this requires a full restart. The connection is opened once at start-up.

The JDBC drivers are declared as libraries in plugin.yml and downloaded by the server on first start, which keeps the plugin jar small. The first start therefore needs internet access; after that the drivers are cached by the server.

SQLite

DATABASE:
  TYPE: SQLITE
  SQLITE:
    FILE: spawners.db

The file is created inside plugins/UltimateVirtualSpawner/. Nothing else to do.

MySQL / MariaDB

DATABASE:
  TYPE: MYSQL
  MYSQL:
    HOST: localhost
    PORT: 3306
    DATABASE: ultimatevirtualspawner
    USERNAME: uvs
    PASSWORD: 'your-password'
    USE_SSL: false

Create the database and user first — the plugin creates tables, not databases:

CREATE DATABASE ultimatevirtualspawner CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'uvs'@'%' IDENTIFIED BY 'your-password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER ON ultimatevirtualspawner.* TO 'uvs'@'%';
FLUSH PRIVILEGES;

The connection string sets useUnicode=true, characterEncoding=UTF-8, autoReconnect=true and allowPublicKeyRetrieval=true. Set USE_SSL: true for a remote database over an untrusted network.

MARIADB is accepted as a synonym for MYSQL and uses the same driver.

If the database is unreachable

The plugin refuses to enable:

The database is unavailable, so no spawner data could be read.
Refusing to start: running now would treat every managed spawner
as an ordinary vanilla spawner. Fix the database error logged
above and restart - the stored spawner data itself is untouched.

This is deliberate, and it is the safe behaviour. Starting without data would mean every managed spawner in the world looks unmanaged — players could break them as vanilla spawners, losing their stacks and stored loot permanently. Refusing to start loses nothing.

Fix the connection error logged just above that block and restart.

Schema

uvs_spawners

One row per managed spawner block.

Column Type
id auto-increment Primary key
world varchar(96)
x, y, z integer
owner_uuid varchar(36)
owner_name varchar(32) Name snapshot at placement time
mob_type varchar(64) Type key from spawners.yml
stack_amount bigint
access_mode varchar(24) OWNER_ONLY / OWNER_AND_TEAM / PUBLIC
last_processed_at bigint Epoch millis; drives the catch-up maths
created_at, updated_at bigint Epoch millis
disabled_loot_keys text Filtered-off loot keys
stored_xp double

Unique on (world, x, y, z) — one managed spawner per block, enforced by the database.

uvs_spawner_loot

One row per material stored in a spawner.

Column Type
spawner_id bigint
loot_key varchar(64)
material varchar(96)
amount bigint

Primary key (spawner_id, loot_key).

uvs_balances

Only used by the internal economy.

Column Type
player_uuid varchar(36), primary key
player_name varchar(32)
balance double
updated_at bigint

Writes

Writes go through a dedicated daemon thread, so disk latency or a distant MySQL server never stalls a game tick. Balance writes are additionally serialised per player.

On shutdown, pending work is flushed before the connection closes. Always stop the server properly (/stop) rather than killing the process — a hard kill can lose the last few seconds of generation.

Backups

Back up the database with the world, on the same schedule. Spawner data is only meaningful alongside the world that contains the blocks.

SQLite — stop the server, then copy the file:

cp plugins/UltimateVirtualSpawner/spawners.db backups/spawners-$(date +%F).db

Copying a live SQLite file can capture a half-written transaction. Stop the server, or use sqlite3 spawners.db ".backup 'backup.db'" which is safe while running.

MySQL

mysqldump -u uvs -p ultimatevirtualspawner > uvs-$(date +%F).sql

Migrating SQLite to MySQL

There is no built-in migration command. Move the three tables manually:

  1. Stop the server.
  2. Export from SQLite:
    sqlite3 plugins/UltimateVirtualSpawner/spawners.db .dump > dump.sql
  3. Adjust the dump for MySQL — the main edits are AUTOINCREMENT to AUTO_INCREMENT, and dropping the SQLite-specific pragma and transaction lines.
  4. Create the MySQL database and let the plugin start once against it, so the tables are created with the right types. Then import only the INSERT statements.
  5. Set DATABASE.TYPE: MYSQL with your credentials, and restart.
  6. Verify with /spawner version — the managed spawner count should match what it was before.

Keep the old spawners.db until you have confirmed the move.

Rollbacks and stray spawners

If the world is rolled back but the database is not — or vice versa — the plugin reconciles what it can:

  • Block exists, no database row: the block still carries its own persistent data (type, stack, owner, access mode), so the plugin recreates the row and logs Restored a stray … spawner … from its block data; its stored loot could not be recovered. Stack and ownership come back; stored loot does not, because it only ever lived in the database.
  • Database row, no block: the row stays but generates nothing, since generation checks that the block is still a spawner. Clean it up by placing a spawner there and using /spawner remove, or by deleting the row directly.

Backing up the world and the database together avoids both cases.

Direct SQL

Useful queries when investigating:

-- biggest spawners
SELECT owner_name, mob_type, stack_amount, world, x, y, z
FROM uvs_spawners ORDER BY stack_amount DESC LIMIT 20;

-- spawners per player
SELECT owner_name, COUNT(*) AS blocks, SUM(stack_amount) AS spawners
FROM uvs_spawners GROUP BY owner_name ORDER BY spawners DESC;

-- most stored loot
SELECT s.owner_name, l.material, l.amount
FROM uvs_spawner_loot l JOIN uvs_spawners s ON s.id = l.spawner_id
ORDER BY l.amount DESC LIMIT 20;

-- richest players
SELECT player_name, balance FROM uvs_balances ORDER BY balance DESC LIMIT 20;

Edit rows only while the server is stopped. The plugin holds spawner state in memory and writes it back, so live edits will be overwritten.

See also

Clone this wiki locally