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
| Property | Type | Description | Mutable |
|---|
getPlayer() | PlayerRef | Player gaining XP | No |
getXpAmount() | double | Current XP amount | Yes (via setXpAmount()) |
setXpAmount(double) | void | Change XP amount | — |
getSource() | XPSource | Source of XP | No |
isCancelled() | boolean | Check if cancelled | Yes (via setCancelled()) |
setCancelled(boolean) | void | Cancel or allow XP | — |
getSourceContext() | Object | Raw source context (nullable) | No |
getEntityKillContext() | EntityKillContext | Entity kill data (for ENTITY_KILL only) | No |
XPSource Properties
| Property | Type | Description |
|---|
getName() | String | Source name (e.g., “ENTITY_KILL”, “QUEST”) |
getMetadata() | Object | Optional metadata (nullable) |
equals(Object) | boolean | Compare sources by name |
EntityKillContext Properties (for ENTITY_KILL only)
| Property | Type | Description |
|---|
getEntityUuid() | UUID | UUID of killed entity |
getEntityLevel() | int | Level of killed entity (0 if unknown) |
hasEntityLevel() | boolean | Whether 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:
| Method | Returns | Description |
|---|
getPlayer() | PlayerRef | Player who leveled up |
getOldLevel() | int | Level before leveling up |
getNewLevel() | int | Level after leveling up |
getLevelsGained() | int | Number of levels gained |
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
| Method | Returns | Description |
|---|
get() | RPGLevelingAPI | Get API instance (null if not loaded) |
isAvailable() | boolean | Check if API is available |
getVersion() | String | Get API version (e.g., “0.2.0”) |
Listener Management
| Method | Returns | Description |
|---|
registerExperienceGainedListener(listener) | void | Register XP gain handler |
unregisterExperienceGainedListener(listener) | boolean | Unregister handler (returns true if was registered) |
isExperienceGainedListenerRegistered(listener) | boolean | Check if handler is registered |
getExperienceGainedListenerCount() | int | Get number of registered handlers |
registerLevelUpListener(listener) | void | Register level up handler |
unregisterLevelUpListener(listener) | boolean | Unregister handler (returns true if was registered) |
isLevelUpListenerRegistered(listener) | boolean | Check if handler is registered |
getLevelUpListenerCount() | int | Get 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.
| Method | Returns | Description |
|---|
getPlayerLevelInfo(playerUuid) | PlayerLevelInfo or null | Full level/XP info for a player by UUID (Holder-only; use playerRef+store when you have Store for correct data) |
getPlayerLevelInfo(playerRef, store) | PlayerLevelInfo | Full level/XP info for a player by PlayerRef; store required (call from world thread) |
getPlayerLevel(playerUuid) | int | Player level (-1 if not found) |
getMonsterLevel(store, npcRef) | int | Monster/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). |
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:
| Method | Returns | Description |
|---|
getClassId() | String | Selected class ID (e.g. “Heavy”), or empty string |
getDisplayName() | String | Display name (e.g. “Heavy Warrior”), or empty string |
getSelectedClassTier() | int | Tier of selected class (0–4), or 0 if none |
getKillsInCurrentTier() | int | Kills towards next tier for selected class |
hasClass() | boolean | Whether 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)
| Method | Returns | Description |
|---|
getPlayerClassInfo(playerUuid) | PlayerClassInfo or null | Full class info by UUID (Holder-only) |
getPlayerClassInfo(playerRef, entityRef, store) | PlayerClassInfo | Full class info by PlayerRef; store required (call from world thread) |
getPlayerClassId(playerUuid) | String or null | Selected class ID (null if player not found) |
getPlayerClassTier(playerUuid) | int | Selected class tier (-1 if not found or no class) |
getPlayerAllClassTiers(playerUuid) | Map<String, Integer> or null | All 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 File | Key Format | Example Fields |
|---|
RPGLevelingConfig.json | PascalCase | MaxLevel, RateExp, EnableHUD, DifficultyPreset |
StatsLevelConfig.json | PascalCase | StatPointsPerLevel, MaxStatPointsHealth, DamageStatValuePerPoint |
DatabaseConfig.json | PascalCase | Enabled, JdbcUrl |
ZoneLevelConfig.json | PascalCase | Zones (array), zone entries |
InstanceLevelConfig.json | PascalCase | Instances (array), instance entries |
LevelRewardsConfig.json | PascalCase | Rewards (array) |
Classes/ClassesGlobalConfig.json | PascalCase | EnableClasses, ClassChangeCooldownSeconds |
MessagesLanguageMapping.json | mixed | Message keys |
Behavior Summary
| On-Disk Value | Action | Reason |
|---|
| Matches RPG Leveling’s default | Applied | Admin hasn’t customized it |
Matches an alsoReplaces value | Applied | Mod is updating its own previous override |
| Already at the new target value | Skipped | Already applied (e.g. second restart) |
| Any other value | Skipped | Admin customized it, preserved |
| Field missing from file | Added | Field 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
| Method | Returns | Description |
|---|
registerConfigDefaults(modId, configurator) | void | Register config default overrides (call in setup()) |
getConfigDefaultsRegistry() | ConfigDefaultsRegistry | Get 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)