Astral Realms Documentation Help

Commands

Base command: /jobs (JobsCommand, @CommandAlias("jobs|job|metier|metiers")). Level and experience administration live in two class-scoped subcommand groups, JobLevelCommand (/jobs level) and JobExperienceCommand (/jobs experience//jobs xp).

Command

Permission

Completion

Description

/jobs

Open the jobs-main menu. Player-only.

/jobs claim

Claim every unclaimed reward for jobs at or below the player's current level. Player-only.

/jobs info

Show total levels and per-job level/experience. Player-only.

/jobs reload

jobs.reload

Reload the jobs configuration.

/jobs level info <player> <job>

jobs.level

@players @jobs

Show a player's level in a job.

/jobs level add <player> <job> <amount>

jobs.level + jobs.level.add

@players @jobs @nothing

Add amount levels.

/jobs level take <player> <job> <amount>

jobs.level + jobs.level.take

@players @jobs @nothing

Subtract amount levels.

/jobs level set <player> <job> <amount>

jobs.level + jobs.level.set

@players @jobs @nothing

Set the level to amount.

/jobs experience info <player> <job>

jobs.experience

@players @jobs

Show a player's experience in a job.

/jobs experience add <player> <job> <amount>

jobs.experience + jobs.experience.add

@players @jobs @nothing

Add amount experience.

/jobs experience take <player> <job> <amount>

jobs.experience + jobs.experience.take

@players @jobs @nothing

Subtract amount experience.

/jobs experience set <player> <job> <amount>

jobs.experience + jobs.experience.set

@players @jobs @nothing

Set the experience to amount.

JobLevelCommand and JobExperienceCommand each carry a class-level @CommandPermission (jobs.level and jobs.experience respectively), so every subcommand in that group requires the class node in addition to its own — info has no method-level permission and only needs the class node, while add/take/set need both.

/jobs

Player-only. The @Default handler opens the jobs-main blueprint via MenuContainer#computeAndOpen(player, "jobs-main"). If the future throws, the player gets JobsMessages#UNEXPECTED_ERROR and the error is logged.

/jobs

/jobs claim

Player-only.

Walks every registered job (plugin.jobs().jobs()). For each job, the player's current level is looked up (LevelService#level, defaulting to 0 if the player has no level entry for that job), then every reward in Job#rewards() keyed by a level <= that level and not already claimed (JobsPlayerData#hasClaimedReward(jobId, level)) is claimed: the reward is marked claimed on the player's JobsPlayerData, a JobClaimRewardEvent is fired, and the reward's action pipeline runs (JobReward#actions()) against a DefaultPaperActionContext built from the player's placeholder container. If no reward across any job qualifies, the sender gets JobsMessages#NO_REWARDS_AVAILABLE instead. See Jobs for how reward tiers and their actions are configured, and Developer API for JobClaimRewardEvent.

/jobs claim

/jobs info

Player-only.

Reads every JobLevelEntity the player has (LevelService#findByPlayer); if there are none, the sender gets JobsMessages#UNEXPECTED_ERROR. Otherwise it sends JOB_INFO_HEADER with %levels% set to the sum of the player's levels across all jobs, then one JOB_INFO_ENTRY line per job with %job%, %level%, %current% (current experience, formatted to two decimals) and %required% — the experience needed for the next level, computed as job.computeLevelProgress(level + 1) and formatted to two decimals. This is the same threshold expression used by the level-up action bar and by /jobs level info//jobs experience info, so all four surfaces agree. A JOB_INFO_FOOTER line closes the list.

/jobs info

/jobs reload

Permission: jobs.reload. Works from console — the handler takes a CommandSender.

Calls AstralJobs#loadConfiguration(). The sender gets a "Reloading jobs configuration..." message, then either "Jobs configuration reloaded successfully." or, if loadConfiguration() throws, "Failed to reload jobs configuration, see console for details." with the exception logged.

/jobs reload > Reloading jobs configuration... > Jobs configuration reloaded successfully.

/jobs level

Class permission: jobs.level. Works from console — every handler takes a CommandSender, and <player> is resolved as an ACF OnlinePlayer (must currently be online).

Subcommand

Permission

Syntax

Behaviour

info

jobs.level

<player> <job>

Reports the player's current level and experience/required-experience for that job. Errors if the player has no level data for the job.

add

jobs.level + jobs.level.add

<player> <job> <amount>

level + amount (integer).

take

jobs.level + jobs.level.take

<player> <job> <amount>

level - amount (integer).

set

jobs.level + jobs.level.set

<player> <job> <amount>

amount (integer).

add/take/set all go through LevelService#updateLevel(uuid, jobId, IntUnaryOperator), an async operation that clamps the result to a minimum of 0 (Math.max(0, ...)). On failure the sender gets an error with the exception message; if the player has no level entity for that job (updateLevel resolves false), the sender is told so; otherwise a confirmation is sent ("Added N level(s) to...", "Took N level(s) from...", or "Set level to N for...").

/jobs level info Steve miner /jobs level add Steve miner 5

/jobs experience

Aliases: /jobs xp. Class permission: jobs.experience. Works from console; <player> is an OnlinePlayer, <amount> is a double.

Subcommand

Permission

Syntax

Behaviour

info

jobs.experience

<player> <job>

Reports the player's current experience/required-experience and level for that job. Errors if the player has no experience data for the job.

add

jobs.experience + jobs.experience.add

<player> <job> <amount>

experience + amount.

take

jobs.experience + jobs.experience.take

<player> <job> <amount>

experience - amount.

set

jobs.experience + jobs.experience.set

<player> <job> <amount>

amount.

add/take/set go through LevelService#updateExperience(uuid, jobId, DoubleUnaryOperator), which clamps the result to a minimum of 0.0, following the same async success/failure/no-data messaging pattern as /jobs level.

/jobs xp info Steve miner /jobs xp add Steve miner 250.5

Argument resolution

<job> is resolved by JobContextResolver, an ACF ContextResolver<Job, ...>: it pops the next argument and looks it up with plugin.jobs().findById(jobId), throwing InvalidCommandArgument ("Job not found for ID: <jobId> ") if no such job is loaded. Tab completion (@jobs, registered as JobCompletionHandler) lists the ids of every loaded job (plugin.jobs().ids()). <player> is resolved as an ACF OnlinePlayer and must be online at invocation time; its tab completion is ACF's built-in @players. See Jobs for where a job's id is set and how jobs are loaded.

Permission summary

Node

Grants

jobs.reload

/jobs reload.

jobs.level

Base node for /jobs level; required alongside every subcommand's own node below.

jobs.level.add

/jobs level add.

jobs.level.take

/jobs level take.

jobs.level.set

/jobs level set.

jobs.experience

Base node for /jobs experience//jobs xp; required alongside every subcommand's own node below.

jobs.experience.add

/jobs experience add//jobs xp add.

jobs.experience.take

/jobs experience take//jobs xp take.

jobs.experience.set

/jobs experience set//jobs xp set.

Last modified: 25 July 2026