Astral Realms Documentation Help

Placeholders

AstralJobs registers one root namespace, jobs, with AstralCore (JobsPlaceholders, registered on enable via RootPlaceholderContainer#registerPlaceholder). Everything else — job_*, action_*, reward_*, booster_* — is a chain point reached by walking through jobs_*, or a namespace that other AstralJobs code registers directly into a PlaceholderContainer when it sends a message or resolves an item (for example, booster-activation messages in Messages register a BoosterEntity and its %booster_*% keys become available there). For the placeholder syntax itself (%a_b_c%, %a_{b_c}%, $e(...)) see Placeholders.

Several placeholders below require the placeholder context to be a Player (a menu/dialog opened for a player, or a message sent to one) — these are marked player-only. Most resolve to null outside that context; %reward_claimed% resolves to false, and the two booster-breakdown placeholders (boosterBonus, boosterMisc) resolve to 0 instead, because they are rendered inside menu lore where a null raises an InvalidWrapException and aborts the whole item compute.

jobs_* (root)

Placeholder

Type

Description

%jobs_job_<id>%

Job?

Looks up a loaded job by its id (JobService#findById). null if no job has that id. Chain further tokens into job_* — e.g. %jobs_job_farmer_name%.

%jobs_jobs%

ItemProvider\<Job\>

Every loaded job. Use as a layout provider to list all jobs.

%jobs_modifier_<jobId>_<scope>[_<sub>]%

double?

Player-only. The player's combined modifier for <jobId>/<scope>. See Modifiers below.

%jobs_boosterBonus_<scope>_<personal\|global>%

double

The bonus percent contributed by active booster items alone, split by origin. Never null — every failure path yields 0. See Booster bonus below.

%jobs_boosterMisc_<jobId>_<scope>%

double

Player-only (yields 0, not null, outside a player context). The residual bonus percent that isn't a personal or global booster item — permission boosters and API-added modifiers. See Booster misc below.

%jobs_boosters_global%

ItemProvider\<BoosterEntity\>

Every currently active global booster. No player context required.

%jobs_boosters_global_<TYPE>%

BoosterEntity

The first active global booster of <TYPE> (EXPERIENCE, MONEY, or BOTH), or BoosterEntity.EMPTY if none is active. No player context required.

%jobs_boosters_player%

ItemProvider\<BoosterEntity\>?

Player-only. Every booster currently active for the player.

%jobs_boosters_player_<TYPE>%

BoosterEntity?

Player-only. The first active booster of <TYPE> for the player, or BoosterEntity.EMPTY if none is active.

Modifiers — jobs_modifier_<jobId>_<scope>_<sub>

Reads the player's active JobModifiers (PlayerJobModifiers, populated from booster activations and the permissions-boosters map in config.yml) and folds them into two running totals, both starting at 0.0: RELATIVE modifiers are summed into a bonusPercent as raw percentage values (matching PlayerJobModifiers#compute, which stacks them additively), ABSOLUTE modifiers are summed into a flat total. A modifier counts toward the query when:

  • its scope is GLOBAL (always counts, regardless of the queried scope), or its scope matches <scope> exactly, and

  • it has no jobId (applies to every job) or its jobId equals <jobId>.

<scope> must be one of EXPERIENCE, MONEY, GLOBAL (case-insensitive) — anything else resolves to null. <sub> is optional and defaults to percent:

<sub>

Returns

percent, bonus

bonusPercent — the sum of every matching RELATIVE modifier's value, as a signed percentage.

multiplier

1 + (bonusPercent / 100) (1.0 = no relative modifier).

flat

The summed ABSOLUTE contribution.

anything else

null.

lore: - "<gray>XP bonus: <green>+%jobs_modifier_miner_experience_percent%%" - "<gray>Money multiplier: <green>x%jobs_modifier_miner_money_multiplier%"

Booster bonus — jobs_boosterBonus_<scope>_<source>

The bonus percent contributed by active booster items only, split by where the booster came from. Both tokens are mandatory:

  • <scope> is a JobModifierScope — EXPERIENCE, MONEY, or GLOBAL (case-insensitive).

  • <source> is personal or global (lowercased before matching).

personal sums the player's active boosters (BoosterService#findActiveByPlayer) filtered to BoosterScope.INDIVIDUAL, so a global booster the player happens to own isn't counted twice; it is player-only. global sums BoosterService#globalActiveBoosters() and needs no player context.

For each booster the value is skipped unless the booster is active and affects the requested scope — a BOTH-type booster counts toward any scope, otherwise JobModifierScope.fromBoosterType(type) must equal <scope>. The per-booster bonus uses the same factor-or-bonus convention as multiplier (raw > 1.0 ? raw - 1.0 : raw, ×100) and the boosters sum additively.

The result is a percent (5.0 means +5%) and is never null: fewer than two remaining tokens, an unparsable <scope>, an unknown <source>, or (for personal) a non-player context all yield 0.

# menus/main.yml lore: - " <text_color>Personnel : <secondary_color>$format-number($orElse(%jobs_boosterBonus_MONEY_personal%,0),####)%" - " <text_color>Global : <secondary_color>$format-number($orElse(%jobs_boosterBonus_MONEY_global%,0),####)%"

Booster misc — jobs_boosterMisc_<jobId>_<scope>

The residual bonus percent for <jobId>/<scope> — everything that is not an active personal or global booster item, i.e. in practice permissions-boosters entries plus anything another plugin added through JobsAPI.

Computed as total − personal − global, where total sums the value of every RELATIVE modifier on the player matching the same rules as jobs_modifier (ABSOLUTE modifiers are excluded), and personal/global are the same booster sums as boosterBonus. Because RELATIVE modifiers stack additively, personal + global + misc always equals the total shown by %jobs_modifier_<jobId>_<scope>%.

Player-only, but like boosterBonus it yields 0 rather than null outside a player context, on fewer than two remaining tokens, or on an unparsable <scope>.

# menus/main.yml lore: - " <text_color>Autres : <secondary_color>$format-number($orElse(%jobs_boosterMisc_{variables_job_id}_MONEY%,0),####)%"

job_*

A Job — reached via %jobs_job_<id>%, or from any placeholder container a job has been registered into directly. A bare %job_x% where x isn't one of the keys below (and isn't menu or player, which need a further sub-key) resolves to null; a bare %job% (no further token) resolves to the Job itself.

Placeholder

Type

Description

%job_id%

String

The job's id (from its YAML file, e.g. farmer).

%job_name%

Component

The job's display-name.

%job_actions%

ItemProvider\<JobActionPlaceholder\>

Every material/entity key in the job's actions map. Chain into action_*.

%job_rewards%

ItemProvider\<JobRewardPlaceholder\>

Every level in the job's rewards map. Chain into reward_*.

%job_menu_title%

Component

The job's menu-title, resolved against the current context.

%job_menu_icon%

ItemStack

The job's menu-icon, resolved against the current context.

%job_menu_secondary-icon%

ItemStack

The job's menu-secondary-icon, resolved against the current context.

%job_player_level%

int

Player-only, and needs a further sub-key. The player's level for this job (LevelService#level), or 0 if they have no level entry.

%job_player_experience%

double

Player-only. The player's accumulated experience for this job, or 0.0 if they have no level entry.

action_*

A JobActionPlaceholder — one per material/entity key in a job's actions map (e.g. the sugar_cane entry under farmer's actions, which itself carries both a block-break and a block-place reward/experience pair).

Placeholder

Type

Description

%action_name%

TranslatableComponent

The material/entity's translatable name, computed once from the first action-type entry under this key (JobActionType#translatable) — in practice this is the same regardless of which action type looks it up, since it just wraps the block/item/entity's own translation key.

%action_item%

ItemStack

A representative icon ItemStack for the key (JobActionType#buildItem), with attributes, "placed on", and the additional tooltip hidden.

%action_computed%

List\<Component\>?

Player-only. One rendered action-display block per action-type entry under this key — see below.

%action_computed% loops every action-type entry (e.g. block-break, block-place) defined for the key, looks up its localized label from action-type-displays in config.yml (skipping — with a warning logged — any entry with no matching label), and renders the action-display template once per entry with four extra placeholders scoped to that render:

Placeholder

Description

%experience%

The entry's experience value, run through the job's leveling equations and the player's active modifiers (ModifierService#computeWithLevel) — the actual XP the player would earn right now.

%money%

The entry's reward value, computed the same way for the MONEY scope.

%formattedName%

The action type's localized label from action-type-displays (e.g. Casser for block-break).

%operation%

The entry's operation (UP/DOWN/NONE) rendered as a bold colored arrow: ↑ green, ↓ red, - gray.

# config.yml action-display: - " <red>Experience: <green>%experience%" - " <red>Money: <green>%money%" action-type-displays: block-break: "Casser" block-place: "Placer"

reward_*

A JobRewardPlaceholder — one per level in a job's rewards map.

Placeholder

Type

Description

%reward_level%

int

The reward's level key.

%reward_description%

ComponentWrapperList

The reward's description lines, unresolved (as configured in the job's YAML).

%reward_claimed%

Boolean

Whether the viewing player has already claimed this reward (JobsPlayerData#hasClaimedReward). Outside a player context this resolves to false (not null); if the player has no synced JobsPlayerData at all it resolves to null.

%reward_claimable%

Boolean?

Whether the player can claim it right now — not yet claimed and their current level (LevelService#level, 0 if absent) is >= the reward's level. Player-only — resolves to null outside a player context (note the asymmetry with claimed above, which resolves to false instead).

booster_* (blueprint)

A BoosterBlueprint — one per file under boosters/ (see Boosters). Registered into a placeholder container wherever plugin code hands a blueprint off directly (for example, when activating a booster the blueprint is registered alongside the resulting BoosterEntity).

Placeholder

Type

Description

%booster_type%

BoosterType

EXPERIENCE, MONEY, or BOTH.

%booster_scope%

BoosterScope

INDIVIDUAL or GLOBAL.

%booster_duration%

Duration

The configured duration, as a raw java.time.Duration — not directly print-friendly; use formattedDuration below.

%booster_formattedDuration%

String

The duration formatted for display, e.g. 1h 5m.

%booster_multiplier%

double

The configured multiplier (e.g. 1.15 for +15%).

%booster_item%

ItemStack

The blueprint's configured item, resolved against the current context — chain into item_* sub-keys.

booster_* (active)

A BoosterEntity — an activated (or queued) booster instance, registered into a placeholder container by BoosterService whenever it sends an activation/expiry message (see the *-booster-activated/*-booster-expired keys in Messages) or resolved via %jobs_boosters_global_<TYPE>%/%jobs_boosters_player_<TYPE>% above. Shares the registered namespace booster with the blueprint above, but exposes a different set of keys:

Placeholder

Type

Description

%booster_id%

UUID

The active booster instance's id (not the blueprint id).

%booster_owner%

MinecraftPlayerPlaceholder

The owning player — chain into player_* sub-keys.

%booster_type%

BoosterType

EXPERIENCE, MONEY, or BOTH.

%booster_formattedType%

Component

The type's localized label — the type-experience/type-money/type-global message.

%booster_scope%

BoosterScope

INDIVIDUAL or GLOBAL.

%booster_duration%

long

The configured duration in milliseconds — a raw long, not a Duration (no formattedDuration on the active entity).

%booster_remainingTime%

long

Milliseconds remaining until expiry, clamped to 0 (never negative). BoosterEntity exposes no formatted variant, so every shipped messages.yml value wraps it in AstralCore's $format-date(…, format-duration) inline function.

%booster_timeUntilActive%

long

Milliseconds until a queued booster goes live (activatesAt - now), clamped to 0 — so it reads 0 for an already-active booster. Used by global-booster-queued.

%booster_multiplier%

double

The multiplier this instance was activated with.

%booster_expiresAt%

long

Epoch milliseconds when the booster expires.

%booster_activatesAt%

long

Epoch milliseconds when the booster starts counting down (in the future for a queued global booster stacked behind another).

# messages.yml personal-booster-activated: "<success>Votre <secondary_color>%booster_formattedType% <#80ed99>a été activé pour <secondary_color>$format-date(%booster_remainingTime%, format-duration) <#80ed99>!"
Last modified: 25 September 2026