Progression
Progression
Voxy's progression system is split into two services obtained from VoxyAPI:
getLevelManager()returns aLevelManagerfor reading and changing a player's level and experience, and for reaching the level configuration.getRewardService()returns aRewardServicefor granting and processing reward actions.
VoxyAPI api = VoxyAPI.getAPI();
LevelManager levels = api.getLevelManager();
RewardService rewards = api.getRewardService();
Every method on both services is synchronous. They operate on a loaded PlayerData object, which you obtain from the DataManager (see Player Data).
Optional<PlayerData> maybe = api.getDataManager().get(playerId);
if (maybe.isEmpty()) return;
PlayerData data = maybe.get();
Reading level and experience
The current level and experience live on PlayerData and are read directly:
int level = data.getLevel(); // current level
long experience = data.getExperience(); // current experience points
Changing level and experience
LevelManager exposes the safe way to mutate progression. Each method returns a boolean describing whether the change was applied. If a player is online, level-up messages are sent automatically.
// Set the level outright. Returns false if it exceeds the max level.
// Lowering the level resets experience to 0.
boolean set = levels.setLevel(data, 25);
// Add levels. Returns false if the result would exceed the max level.
boolean gained = levels.giveLevel(data, 5);
// Set experience directly.
boolean setXp = levels.setExperience(data, 10_000L);
// Add experience. May trigger one or more level-ups.
boolean gaveXp = levels.giveExperience(data, 2_500L);
// Attempt to consume experience and unlock the next level.
// Returns true if the player actually leveled up.
boolean leveled = levels.tryUnlockNext(data);
Required experience
getRequiredExperience(int level) returns the total experience points needed to reach a given level, computed from the configured formula:
long needed = levels.getRequiredExperience(30);
long remaining = Math.max(0, needed - data.getExperience());
The default formula is 1250 * (<level> ^ 2) + (6250 * <level>) - 7500 with a max level of 1000. <level> is the placeholder substituted per level.
Level settings
levels.getSettings() returns a LevelSettings holding the experience formula, the max level, progress-bar styles, per-level display formats, and level rewards.
LevelSettings settings = levels.getSettings();
String formula = settings.getExpFormula(); // e.g. "1250 * (<level> ^ 2) + (6250 * <level>) - 7500"
int max = settings.getMaxLevel();
Bar styles
A BarStyle is a record describing how a progress bar renders. build(long current, long max) produces the bar string for a given progress.
BarStyle style = settings.getBarStyle("bar"); // null if the style is not defined
if (style != null) {
String bar = style.build(data.getExperience(), levels.getRequiredExperience(data.getLevel() + 1));
}
The record components are name, size, barChar, donePrefix, progressPrefix, and remainingPrefix. Use settings.getBarStyles() for the full Map<String, BarStyle>, or settings.addBarStyle(style) to register one.
Level formats
A LevelFormat is a record of level, displayFormat, and chatDisplayFormat. Formats are stored in a NavigableMap keyed by level, so getFormat(int) returns the format that applies at or below the requested level (its floor entry), or null if none is configured.
LevelFormat format = settings.getFormat(data.getLevel());
if (format != null) {
String nameTag = format.display(data.getLevel()); // rendered display string
String chatTag = format.chat(data.getLevel()); // rendered chat string
}
display() and chat() (no-argument) render using the format's own level, while the int-argument overloads render for an arbitrary level.
Level rewards
A LevelReward is a record of id, level, and a Reward to grant when that level is reached. LevelSettings stores them in a Map<Integer, LevelReward> keyed by level.
LevelReward reward = settings.getLevelReward(data.getLevel()); // null if none at that level
if (reward != null) {
rewards.grant(data, reward.reward());
}
Use settings.getLevelRewards() for the full Map<Integer, LevelReward>, or settings.addLevelReward(LevelReward) / settings.setLevelReward(int, LevelReward) to register one.
Granting rewards
A Reward is a bundle of RewardActions. RewardService.grant(PlayerData, Reward) runs any actions that can execute on the current server immediately and queues the rest on the player's data for later.
RunCommandAction action = new RunCommandAction();
action.setTarget(RunCommandAction.Target.PROXY);
action.setDescription("A shiny rank upgrade");
action.setRarity(RewardRarity.LEGENDARY);
action.getCommands().add("lp user <player> parent add vip");
Reward reward = new Reward();
reward.addAction(action);
rewards.grant(data, reward);
Reward rarities
RewardRarity describes how special an action is, in ascending priority: HIDDEN, COMMON, RARE, EPIC, LEGENDARY, MYTHICAL. Each carries an Adventure Style (getStyle() / setStyle(Style)) and a getPriority(). RewardRarity.fromString(String) parses a name, falling back to COMMON for null or unknown values.
RunCommand actions and targets
RunCommandAction is the built-in action type (RewardActionType.RUN_COMMAND). Its Target enum controls where the commands run:
| Target | Runs the commands on |
|---|---|
CURRENT |
The server the player is currently on |
SPECIFIC |
A named server (setServer(String)) |
GROUP |
A named server group (setGroup(String)) |
ANY |
Any available server |
PROXY |
The proxy node |
Additional toggles: setRequirePlayerOnline(boolean) (default true) and setEnsureExecuted(boolean) (default false); when ensureExecuted is on, execute stops and reports failure if a command fails to dispatch. Commands themselves live in the getCommands() list.
Pending and queued actions
Actions that could not run immediately are stored on PlayerData.getQueuedActions(). You can inspect them by player id or process them with a command dispatcher:
List<RewardAction> pending = rewards.getPendingActions(playerId);
// Dispatch queued commands; the function returns true when a command ran.
Function<String, Boolean> dispatcher = command -> {
// hand the command to your platform's console/command executor
return true;
};
int executed = rewards.process(data, dispatcher);
process removes actions from the queue as they succeed and returns the number executed.
Key methods
| Method | Returns | Notes |
|---|---|---|
LevelManager.setLevel(PlayerData, int) |
boolean |
False if it exceeds max level; lowering resets XP |
LevelManager.giveLevel(PlayerData, int) |
boolean |
False if the result exceeds max level |
LevelManager.setExperience(PlayerData, long) |
boolean |
Sets XP directly |
LevelManager.giveExperience(PlayerData, long) |
boolean |
May trigger level-ups |
LevelManager.getRequiredExperience(int) |
long |
Total XP to reach a level |
LevelManager.tryUnlockNext(PlayerData) |
boolean |
True if the player leveled up |
LevelManager.getSettings() |
LevelSettings |
Formula, max level, styles, formats, rewards |
LevelSettings.getBarStyle(String) |
BarStyle |
Null if undefined |
LevelSettings.getFormat(int) |
LevelFormat |
Floor match, null if none |
LevelSettings.getLevelReward(int) |
LevelReward |
Null if none at that level |
BarStyle.build(long, long) |
String |
Renders the progress bar |
PlayerData.getLevel() / getExperience() |
int / long |
Current progression |
RewardService.grant(PlayerData, Reward) |
void |
Runs now, queues the rest |
RewardService.getPendingActions(UUID) |
List<RewardAction> |
Queued actions for a player |
RewardService.process(PlayerData, Function<String, Boolean>) |
int |
Actions executed |