Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.rpg-leveling.zuxaw.com/llms.txt

Use this file to discover all available pages before exploring further.

RPG Leveling API

API for external mods to interact with the RPG Leveling system. Mod authors are welcome to integrate with RPG Leveling — use this API to grant XP, listen for level-ups, read player level, override config defaults, and more. See Share XP with teammates for an example (PartyPro); dependency setup and full reference are below. Simple integration! Import the API classes directly. Just declare the dependency in manifest.json and the plugin must be installed on the server or same mods folder.

Adding as Dependency

Step 1: Declare Dependency in manifest.json

Add to your plugin’s manifest.json:
{
  "OptionalDependencies": {
    "Zuxaw:RPGLeveling": "*"
  }
}
Or if you require it:
{
  "Dependencies": {
    "Zuxaw:RPGLeveling": ">=0.2.0"
  }
}

Step 2: Import and Use

Just import the API classes directly:
import org.zuxaw.plugin.api.RPGLevelingAPI;
import org.zuxaw.plugin.api.XPSource;

// Check if available and use
if (RPGLevelingAPI.isAvailable()) {
    RPGLevelingAPI api = RPGLevelingAPI.get();
    if (api != null) {
        api.addXP(playerUuid, 100.0);
    }
}

Quick Start

import org.zuxaw.plugin.api.RPGLevelingAPI;
import org.zuxaw.plugin.api.XPSource;

// Check API availability
if (RPGLevelingAPI.isAvailable()) {
    RPGLevelingAPI api = RPGLevelingAPI.get();
    if (api != null) {
        // Add XP to a player
        api.addXP(playerUuid, 100.0);
        
        // Listen for XP gains
        api.registerExperienceGainedListener(event -> {
            event.setXpAmount(event.getXpAmount() * 1.5);
        });
        
        // Listen for level ups
        api.registerLevelUpListener(event -> {
            int level = event.getNewLevel();
            // Your code here
        });
    }
}

Adding Experience

// Basic usage
api.addXP(playerUuid, 100.0);

// With custom source
XPSource QUEST = XPSource.create("QUEST");
api.addXP(playerUuid, 100.0, QUEST);
Built-in Sources:
  • XPSource.ENTITY_KILL — XP from killing entities
  • XPSource.COMMAND — XP from admin commands
Create Custom Sources:
XPSource QUEST = XPSource.create("QUEST");
XPSource PARTY_SHARE = XPSource.create("PARTY_SHARE");

Experience Gained Events

Modify or cancel XP before it’s awarded.
api.registerExperienceGainedListener(event -> {
    // Access all event properties
    PlayerRef player = event.getPlayer();
    double xp = event.getXpAmount();
    XPSource source = event.getSource();
    boolean cancelled = event.isCancelled();
    Object sourceContext = event.getSourceContext();
    
    // Modify XP
    event.setXpAmount(xp * 2.0);
    
    // Cancel XP
    event.setCancelled(true);
});

All Available Properties

PropertyTypeDescriptionMutable
getPlayer()PlayerRefPlayer gaining XPNo
getXpAmount()doubleCurrent XP amountYes (via setXpAmount())
setXpAmount(double)voidChange XP amount
getSource()XPSourceSource of XPNo
isCancelled()booleanCheck if cancelledYes (via setCancelled())
setCancelled(boolean)voidCancel or allow XP
getSourceContext()ObjectRaw source context (nullable)No
getEntityKillContext()EntityKillContextEntity kill data (for ENTITY_KILL only)No

XPSource Properties

PropertyTypeDescription
getName()StringSource name (e.g., “ENTITY_KILL”, “QUEST”)
getMetadata()ObjectOptional metadata (nullable)
equals(Object)booleanCompare sources by name

EntityKillContext Properties (for ENTITY_KILL only)

PropertyTypeDescription
getEntityUuid()UUIDUUID of killed entity
getEntityLevel()intLevel of killed entity (0 if unknown)
hasEntityLevel()booleanWhether level is known

Example — Entity Kill

api.registerExperienceGainedListener(event -> {
    XPSource source = event.getSource();
    
    // Check if entity kill
    if (source.equals(XPSource.ENTITY_KILL)) {
        EntityKillContext context = event.getEntityKillContext();
        if (context != null) {
            UUID entityUuid = context.getEntityUuid();
            int entityLevel = context.getEntityLevel();
            
            // Apply bonus based on entity level
            if (entityLevel >= 50) {
                event.setXpAmount(event.getXpAmount() * 1.5);
            }
        }
    }
});

Example — Custom Source

XPSource QUEST = XPSource.create("QUEST");

api.registerExperienceGainedListener(event -> {
    XPSource source = event.getSource();
    
    // Check source by name
    if (source.getName().equals("QUEST")) {
        // Or check by equals
        if (source.equals(QUEST)) {
            // Modify XP for quests
            event.setXpAmount(event.getXpAmount() * 1.2);
        }
    }
    
    // Access source metadata (if custom source has it)
    Object metadata = source.getMetadata();
    if (metadata != null) {
        // Your custom logic
    }
});

Level Up Events

React when players level up.
api.registerLevelUpListener(event -> {
    PlayerRef player = event.getPlayer();
    int oldLevel = event.getOldLevel();
    int newLevel = event.getNewLevel();
    int levelsGained = event.getLevelsGained();
    
    // Your code here
});
Available Methods:
MethodReturnsDescription
getPlayer()PlayerRefPlayer who leveled up
getOldLevel()intLevel before leveling up
getNewLevel()intLevel after leveling up
getLevelsGained()intNumber of levels gained

Getting Player Information

When you only have a UUID (e.g. from a command or async context), use the UUID overload. It uses Holder only and may return incorrect data when Holder is null.
// By UUID (Holder-only; use playerRef+store when you have Store)
PlayerLevelInfo info = api.getPlayerLevelInfo(playerUuid);
if (info != null) {
    int level = info.getLevel();
    double xp = info.getExperience();
    double xpNeeded = info.getXpNeededForNext();
    boolean isMax = info.isMaxLevel();
}

// Quick access by UUID
int level = api.getPlayerLevel(playerUuid);
double xp = api.getPlayerXP(playerUuid);
boolean isMax = api.isPlayerMaxLevel(playerUuid);
When you have a PlayerRef and Store (e.g. in a system tick or GUI), use the two-arg overload for correct level/XP. Store is required.
// By PlayerRef + Store (required; call from world thread)
PlayerLevelInfo info = api.getPlayerLevelInfo(playerRef, store);
int level = info.getLevel();
double xp = info.getExperience();
double xpNeeded = info.getXpNeededForNext();
boolean isMax = info.isMaxLevel();

Common Use Cases

Party XP Sharing

XPSource PARTY_SHARE = XPSource.create("PARTY_SHARE");

api.registerExperienceGainedListener(event -> {
    if (event.getSource().equals(XPSource.ENTITY_KILL)) {
        // Cancel original XP
        event.setCancelled(true);
        
        // Share XP with party (your party system code)
        double sharedXP = event.getXpAmount() / partySize;
        for (UUID member : partyMembers) {
            api.addXP(member, sharedXP, PARTY_SHARE);
        }
    }
});

Quest Rewards

XPSource QUEST = XPSource.create("QUEST");

// When quest completes
api.addXP(playerUuid, questXPReward, QUEST);

Level-Based Actions

api.registerLevelUpListener(event -> {
    int level = event.getNewLevel();
    // Your reward/notification code here
});

API Methods Reference

Availability & Version

MethodReturnsDescription
get()RPGLevelingAPIGet API instance (null if not loaded)
isAvailable()booleanCheck if API is available
getVersion()StringGet API version (e.g., “0.2.0”)

Listener Management

MethodReturnsDescription
registerExperienceGainedListener(listener)voidRegister XP gain handler
unregisterExperienceGainedListener(listener)booleanUnregister handler (returns true if was registered)
isExperienceGainedListenerRegistered(listener)booleanCheck if handler is registered
getExperienceGainedListenerCount()intGet number of registered handlers
registerLevelUpListener(listener)voidRegister level up handler
unregisterLevelUpListener(listener)booleanUnregister handler (returns true if was registered)
isLevelUpListenerRegistered(listener)booleanCheck if handler is registered
getLevelUpListenerCount()intGet number of registered handlers

Level reads (player and monster)

Use the API for all level reads so behavior is consistent across the plugin and external mods.
MethodReturnsDescription
getPlayerLevelInfo(playerUuid)PlayerLevelInfo or nullFull level/XP info for a player by UUID (Holder-only; use playerRef+store when you have Store for correct data)
getPlayerLevelInfo(playerRef, store)PlayerLevelInfoFull level/XP info for a player by PlayerRef; store required (call from world thread)
getPlayerLevel(playerUuid)intPlayer level (-1 if not found)
getMonsterLevel(store, npcRef)intMonster/NPC level (1–100, or 0 if unknown). Uses cache, entity overrides (ZoneLevelConfig), or computed from HP/zone. Use when you have the entity Store and Ref (e.g. in damage/defense systems).

Getting Player Class Information

Read the player’s selected class, tier, and all class progress.
// By UUID (Holder-only)
RPGLevelingAPI.PlayerClassInfo classInfo = api.getPlayerClassInfo(playerUuid);
if (classInfo != null && classInfo.hasClass()) {
    String classId = classInfo.getClassId();           // e.g. "Heavy"
    String display = classInfo.getDisplayName();        // e.g. "Heavy Warrior"
    int tier = classInfo.getSelectedClassTier();        // 0–4
    int kills = classInfo.getKillsInCurrentTier();      // kills towards next tier
    
    Map<String, Integer> allTiers = classInfo.getAllClassTiers();
    // e.g. {"Heavy": 3, "Edge": 1, "Arcane": 0}
}

// Quick access by UUID
String classId = api.getPlayerClassId(playerUuid);     // null if player not found
int tier = api.getPlayerClassTier(playerUuid);          // -1 if not found or no class
Map<String, Integer> all = api.getPlayerAllClassTiers(playerUuid); // null if not found
When you have a PlayerRef, entity Ref, and Store (e.g. in a system tick or GUI), use the three-arg overload for correct data:
RPGLevelingAPI.PlayerClassInfo classInfo = api.getPlayerClassInfo(playerRef, entityRef, store);
PlayerClassInfo Properties:
MethodReturnsDescription
getClassId()StringSelected class ID (e.g. “Heavy”), or empty string
getDisplayName()StringDisplay name (e.g. “Heavy Warrior”), or empty string
getSelectedClassTier()intTier of selected class (0–4), or 0 if none
getKillsInCurrentTier()intKills towards next tier for selected class
hasClass()booleanWhether the player has a class selected
getAllClassTiers()Map<String, Integer>All class IDs the player has progress in, mapped to tier (0–4)

Class reads (player)

MethodReturnsDescription
getPlayerClassInfo(playerUuid)PlayerClassInfo or nullFull class info by UUID (Holder-only)
getPlayerClassInfo(playerRef, entityRef, store)PlayerClassInfoFull class info by PlayerRef; store required (call from world thread)
getPlayerClassId(playerUuid)String or nullSelected class ID (null if player not found)
getPlayerClassTier(playerUuid)intSelected class tier (-1 if not found or no class)
getPlayerAllClassTiers(playerUuid)Map<String, Integer> or nullAll class tiers (null if player not found)

Config Defaults Override API

Override RPG Leveling’s default config values from your mod. Admin-customized values are never overwritten. Call registerConfigDefaults in your plugin’s setup() method. RPG Leveling applies overrides in its start() phase (after all mods have registered).
Config default overrides are startup-only — they are not re-applied on /lvl reload.
Only one mod can override a given field. If two mods both try to set MaxLevel, only the first to register wins — the second is silently ignored (a warning is logged). There is no merging or conflict resolution beyond first-come-first-served. Prefer adding content via array appends (e.g. new instances, zones, rewards) rather than replacing hard-coded scalar values — arrays compose safely across multiple mods, while scalar overrides do not. Only override global values like MaxLevel, RateExp, or DifficultyPreset if your mod absolutely requires it.

Basic Usage

import org.zuxaw.plugin.api.RPGLevelingAPI;

// In your plugin's setup() method:
RPGLevelingAPI.registerConfigDefaults("MyMod", defaults -> {
    defaults
        .forConfig("RPGLevelingConfig.json")
            .setDefault("MaxLevel", 155)
            .setDefault("RateExp", 5.0)
            .done();
});
How it works: when the server starts, RPG Leveling reads the on-disk config and its bundled default. If the on-disk value matches the bundled default (meaning the admin hasn’t touched it), the override is applied. If the admin has customized the value, the override is skipped.

Updating a Previous Override

When you release a new version that changes your override value, pass the previous value(s) in alsoReplaces so the applier recognizes them as “not admin-customized”:
RPGLevelingAPI.registerConfigDefaults("MyMod", defaults -> {
    defaults
        .forConfig("RPGLevelingConfig.json")
            // v1.0 set MaxLevel to 155, v2.0 now wants 200
            .setDefault("MaxLevel", 200, 155)
            .done();
});
Without alsoReplaces, the applier would see 155 on disk, compare it to the bundled default 100, find a mismatch, and skip it (thinking an admin changed it). With alsoReplaces(155), it knows 155 was a previous mod default and migrates it to 200.

Appending to Arrays

Add elements to JSON arrays (e.g. new dungeon instances). The element is only appended if no existing element has the same ID:
import com.google.gson.JsonObject;

JsonObject dungeon = new JsonObject();
dungeon.addProperty("Id", "FrozenDungeon");
dungeon.addProperty("LevelMin", 60);
dungeon.addProperty("LevelMax", 80);

RPGLevelingAPI.registerConfigDefaults("MyMod", defaults -> {
    defaults
        .forConfig("InstanceLevelConfig.json")
            .appendToArray("Instances", "Id", dungeon)
            .done();
});

Multiple Config Files

Chain overrides for multiple config files in a single call:
RPGLevelingAPI.registerConfigDefaults("MyMod", defaults -> {
    defaults
        .forConfig("RPGLevelingConfig.json")
            .setDefault("MaxLevel", 155)
            .done()
        .forConfig("StatsLevelConfig.json")
            .setDefault("StatPointsPerLevel", 10)
            .done()
        .forConfig("InstanceLevelConfig.json")
            .appendToArray("Instances", "Id", myDungeonJson)
            .done();
});

Supported Config Files

Any RPG Leveling config file can be targeted:
Config FileKey FormatExample Fields
RPGLevelingConfig.jsonPascalCaseMaxLevel, RateExp, EnableHUD, DifficultyPreset
StatsLevelConfig.jsonPascalCaseStatPointsPerLevel, MaxStatPointsHealth, DamageStatValuePerPoint
DatabaseConfig.jsonPascalCaseEnabled, JdbcUrl
ZoneLevelConfig.jsonPascalCaseZones (array), zone entries
InstanceLevelConfig.jsonPascalCaseInstances (array), instance entries
LevelRewardsConfig.jsonPascalCaseRewards (array)
Classes/ClassesGlobalConfig.jsonPascalCaseEnableClasses, ClassChangeCooldownSeconds
MessagesLanguageMapping.jsonmixedMessage keys

Behavior Summary

On-Disk ValueActionReason
Matches RPG Leveling’s defaultAppliedAdmin hasn’t customized it
Matches an alsoReplaces valueAppliedMod is updating its own previous override
Already at the new target valueSkippedAlready applied (e.g. second restart)
Any other valueSkippedAdmin customized it, preserved
Field missing from fileAddedField was absent, mod provides it

Conflict Resolution

  • Scalar fields: first mod to register for a (configFile, jsonPath) wins. The second mod’s registration is rejected and a warning is logged at startup.
  • Array appends: multiple mods can append different elements. If two mods append an element with the same ID, the first one stays (duplicate skipped at apply time).
  • Startup log: all registered overrides are listed with source mod IDs.

Static Methods

MethodReturnsDescription
registerConfigDefaults(modId, configurator)voidRegister config default overrides (call in setup())
getConfigDefaultsRegistry()ConfigDefaultsRegistryGet the registry (internal use)

Notes

  • All methods are thread-safe
  • Level reads: Use getPlayerLevelInfo / getPlayerLevel for player level and getMonsterLevel(store, npcRef) for monster level so behavior is consistent everywhere (plugin and external mods use the same API). When you have an entity Store (e.g. in systems or GUI), call getPlayerLevelInfo(playerRef, store) — store is required for correct data when Holder is null.
  • XP amounts must be positive
  • Players must be online to receive XP
  • Level ups are handled automatically
  • Events fire in registration order
  • Handler errors are logged but don’t stop other handlers
  • API instance is automatically refreshed on plugin reload
  • Config default overrides are startup-only (not re-applied on /lvl reload)