Using LeonsDB in your plugin

This guide shows how a Paper plugin stores its data in LeonsDB. A complete working example is in connector/example in the repository.

Setup

  1. Install the LeonsDB plugin on your Minecraft server and put a server token in plugins/LeonsDB/config.yml (see Running the server).

  2. In your plugin, compile against the LeonsDB API only (the leonsdb-api jar). Do not shade it: the LeonsDB plugin provides it at runtime.

  3. Add LeonsDB as a dependency in your plugin.yml:

    depend: [LeonsDB]
    

Declare your data

@PlayerData(namespace = "practicepvp", collection = "stats")
public class Stats {
    @Indexed public int kills;
    public int deaths;
    @Transactional public long coins = 100;   // default for new players
    public Location lastLocation;
    public ItemStack[] kit;
    public transient int streak;              // transient: not stored
}

Register your classes once, in onEnable:

@Override
public void onEnable() {
    LeonsDB.register(Stats.class, Clan.class);
}

If LeonsDB is unreachable, or a field’s type conflicts with stored data, register throws. Let it: running without your data would be worse.

Player data

A player’s data is loaded before they join, and kept in memory while they are online. Reading it never touches the network:

Stats stats = LeonsDB.player(player.getUniqueId()).get(Stats.class);
stats.kills++;

That is all. LeonsDB notices the change and saves it in the background, about once per second, when the player leaves and when the server stops. Forty kills between two saves become one “kills +40” write.

Use get and change fields on the main thread. Keep the reference only while the player is online.

Money and other changes that must never be lost

Use update. The change is applied immediately and saved right away; the future completes once LeonsDB has stored it durably:

LeonsDB.player(player.getUniqueId())
       .update(Stats.class, s -> s.coins -= price)
       .whenComplete((ok, err) -> {
           // Back to the main thread before touching Bukkit.
           Bukkit.getScheduler().runTask(plugin, () -> {
               if (err == null) giveItem(player);
               else player.sendMessage("Purchase not confirmed yet, try again.");
           });
       });

If the future fails, the change is not lost: it stays in memory and LeonsDB keeps retrying in the background. It only means it is not confirmed yet.

To save everything of a player immediately (for example before transferring them to another server), call save(); it returns a future too.

Global data

Data that does not belong to one player (clans, bans, settings) lives in global collections, keyed by any string:

@GlobalData(namespace = "practicepvp", collection = "clans")
public class Clan {
    public UUID leader;
    public List<UUID> members = new ArrayList<>();
}

LeonsDB.global(Clan.class).update("red", clan -> clan.members.add(uuid))
       .thenAccept(clan -> ...);

Several servers may change the same record at the same time. update re-reads the latest version and re-applies your change when that happens, so no change is ever lost, but your change function may run more than once: it must only modify the object it is given. Global operations are asynchronous; never wait for them on the main thread.

Supported types

Java type Notes
int, long, double, boolean and their boxed forms Whole numbers are saved as increments, so merged changes are exact.
String, UUID, Instant, byte[]
Enums Stored by name.
List<T>, Map<String, T> Of any supported type, nested as you like. Elements must not be null.
ItemStack, ItemStack[], Location Items use Paper’s own format and are upgraded automatically when Minecraft updates.

float, short, byte and char are not supported; use double, int or String. A null field is stored as “no value”.

Changing your data classes

What happens when things go wrong

Situation What LeonsDB does
LeonsDB is unreachable when a player joins The player is asked to try again. They never play with empty data, which would later overwrite their real data.
LeonsDB goes down while players are online Players keep playing. Changes stay in memory and are saved as soon as LeonsDB is back.
A player rejoins before their data finished saving The join waits for the save, so it never loads outdated data.
The Minecraft server stops Everything is saved first. If LeonsDB cannot be reached within 10 seconds, unsaved changes are written to plugins/LeonsDB/recovery/.
The Minecraft server crashes Changes from the last moment (up to about one second) may be lost. Changes made with update were already confirmed.
A save request is answered but the answer is lost LeonsDB detects this and never applies the change twice.

Rules of thumb