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 |
|---|---|---|---|
| — | — | Open the |
| — | — | Claim every unclaimed reward for jobs at or below the player's current level. Player-only. |
| — | — | Show total levels and per-job level/experience. Player-only. |
|
| — | Reload the jobs configuration. |
|
|
| Show a player's level in a job. |
|
|
| Add |
|
|
| Subtract |
|
|
| Set the level to |
|
|
| Show a player's experience in a job. |
|
|
| Add |
|
|
| Subtract |
|
|
| Set the experience to |
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 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 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 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 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 |
|---|---|---|---|
|
|
| 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/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 experience
Aliases: /jobs xp. Class permission: jobs.experience. Works from console; <player> is an OnlinePlayer, <amount> is a double.
Subcommand | Permission | Syntax | Behaviour |
|---|---|---|---|
|
|
| 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/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.
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 |
|---|---|
|
|
| Base node for |
|
|
|
|
|
|
| Base node for |
|
|
|
|
|
|