Skip to content

Persistent Holder ​

The PersistentHolder interface is a core utility in JustRaces designed to safely store custom persistent state inside a player's PersistentDataContainer (PDC).

Instead of writing custom NBT keys directly into Paper's root PDC, JustRaces isolates all state variables into dedicated sub-containers.


Why Use PersistentHolder? ​

Paper's native PersistentDataContainer allows storing custom data on entities, but saving variables directly to the root PDC leads to cluttered structures and potential key collisions between different plugins.

Key Benefits ​

  • Clean Root Structure: Keeps the player's root entity PDC completely clean.
  • Isolated Namespaces: Encapsulates component data into designated plugin categories.
  • Nested Containers: Supports complex data structures (including storing nested PersistentDataContainer instances inside parent containers).

Storage Architecture ​

PersistentHolder is directly integrated into the base classes of all major components. Each category manages its own sub-container key:

Component CategoryBase Classes / ImplementationsParent PDC Sub-Container Key
AbilitiesBaseAbilityjustraces:abilities
TraitsBaseTraitListener, BaseTraitRunnablejustraces:traits

Working with Data ​

Classes inheriting from PersistentHolder gain direct access to shorthand helper methods for data manipulation.

Instead of dealing with raw Paper PDC types and PersistentDataType objects, you can read, write, and remove data directly:

  • Primitives & Basic Types: Shorthands for int, double, boolean, long, String, etc.
  • Complex Objects: Native support for UUID, ItemStack[] (inventories/kits), and PersistentDataContainer (nested NBT structures)

Check the API Interface

For the full list of supported data types and helper methods, check the PersistentHolder interface directly in the source code or IDE autocomplete.


Practical Example: Usage Counter ​

In this example, an ability tracks how many times a player has activated it. Once the counter reaches 10, the stored data resets automatically.

java
public class ExamplePersistentAbility extends BaseAbility {

    private static final NamespacedKey USE_AMOUNT_KEY = new NamespacedKey("example", "use_amount");

    @Override
    public NamespacedKey getKey() {
        return new NamespacedKey("example", "test_persistent_ability");
    }

    @Override
    public long cooldown() {
        return 20L; // 1 second cooldown
    }

    @Override
    protected boolean onActivation(Player player, AbilityContext ctx) {
        // 1. Fetch current usage count from the player's ability container
        int useAmount = getEntryInt(player, USE_AMOUNT_KEY);

        if (useAmount >= 10) {
            // 2. Clear entry data when limit is reached
            removeEntryData(player, USE_AMOUNT_KEY);
            player.sendMessage("Ability usage counter has been reset!");
        } else {
            // 3. Increment and save updated value
            useAmount++;
            setEntryInt(player, USE_AMOUNT_KEY, useAmount);

            player.sendMessage("You have used this ability %s time(s).".formatted(useAmount));
        }

        return true;
    }
}

NBT Storage Path

In the example above, the key example:use_amount is stored inside BukkitValues."justraces:abilities"."example:test_persistent_ability", keeping it isolated from other plugins and root player data.

NOT AN OFFICIAL MINECRAFT WEBSITE. NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.