Astral Realms Documentation Help

Messages

plugins/AstralJobs/messages.yml holds every player-facing string the plugin sends. It's loaded into the JobsMessages enum on enable (loadEnum("messages.yml", JobsMessages.class)), so keys are kebab-case (LEVEL_UP → level-up) and values are MiniMessage strings that resolve the standard AstralCore variable tags (<success>, <warning>, <error>, plus server-defined ones like <secondary_color>/<text_color>) and any placeholder reachable from the container the message is sent with.

Default file

level-up: "<success>Vous êtes passé au niveau <#26d971>%level% <#80ed99>dans le métier de <#26d971>%job% <#80ed99>! Récupérez votre récompense dans le <#26d971>/metier<#80ed99>." experience-gained: "<font:hud:icons>f</font> <font:hud:shift_1><gray><#26d971>%notification_money% <gray>| <white><font:hud:icons>g</font> <gray><#26d971>%notification_experience% <gray>| <#26d971>%notification_current%<gray>/<#26d971>%notification_total%" # Boosters same-type-booster-active: "<warning>Vous avez déjà un élixir d'activé du même type. Veuillez attendre qu'il expire avant d'en activer un autre." # -- Personal personal-booster-activated: "<success>Votre <secondary_color>%booster_formattedType% <#80ed99>a été activé pour <secondary_color>$format-date(%booster_remainingTime%, format-duration) <#80ed99>!" personal-booster-expired: "<warning>Votre <secondary_color>%booster_formattedType% <#ffd60a>a expiré." # -- Global global-booster-activated: "<success>Votre booster <secondary_color>%booster_formattedType% <#80ed99>a été activé pour <secondary_color>$format-date(%booster_remainingTime%, format-duration) <#80ed99>!" global-booster-activated-notification: "<success>Un booster <secondary_color>%booster_formattedType% <#80ed99>a été activé pour <secondary_color>$format-date(%booster_remainingTime%, format-duration) <#80ed99>!" global-booster-broadcast: "<success><secondary_color>%booster_owner_name% <#80ed99>a activé un booster <secondary_color>%booster_formattedType% <#80ed99>pour tout le serveur pendant <secondary_color>$format-date(%booster_remainingTime%, format-duration) <#80ed99>!" global-booster-queued: "<success>Votre booster <secondary_color>%booster_formattedType% <#80ed99>a été mis en attente il sera activé dans <secondary_color>$format-date(%booster_timeUntilActive%, format-duration) <#80ed99>!" global-booster-expired: "<warning>Votre booster <secondary_color>%booster_formattedType% <#ffd60a>a expiré." # Boosters types type-experience: "Élixir d'experience" type-money: "Élixir de richesse" type-global: "Élixir global" # Rewards reward-cannot-be-claimed: "<warning>Vous ne pouvez récupérer cette récompense. Vous devez passer niveau <#26d971>%level% <#ffd60a>pour la récupérer." no-rewards-available: "<warning>Il n'y a pas de récompense disponible." # Jobs info job-info-header: "<gold>--- <primary_color>Métiers - Niveau totaux %levels% <gold>---" job-info-entry: "<secondary_color>%job% <text_color>Niveau <secondary_color>%level% <text_color>| Experience: <secondary_color>%current%<text_color>/<secondary_color>%required%" job-info-footer: "" # Miscellaneous unexpected-error: "<error>Une erreur est apparue. Veuillez réessayer plus tard."

Progression

Key

Placeholders

Sent to

When

level-up

%level%, %job%

Player

Once per level gained, inside ActionService's level-up loop — a single action that crosses several thresholds fires this once per threshold crossed. %level% is the new level; %job% is the job's configured display-name.

experience-gained

%notification_money%, %notification_experience%, %notification_current%, %notification_total%

Player, action bar

Rendered as a raw action-bar packet (WrapperPlayServerActionBar via PacketEvents, not sendActionBar) by NotificationService#sendNotification. A background is spliced in via AstralHudHook#addBackground when AstralHud is present (see Overview → Integrations).

experience-gained isn't sent per XP gain — ActionService accumulates every surviving experience/money gain for a player+job into a NotificationEntry (NotificationService#update), and a repeating task (NotificationRenderTask, every 5 ticks) re-renders the action bar for any entry that hasn't rendered in the last 350 ms (NOTIFICATION_COOLDOWN_MS) and has updated within the last 5 s (STALE_TIMEOUT_MS). Past that 5 s idle window the entry is flushed and dropped instead (NotificationCleanUpTask) — see Jobs → Level-up flow for the full accumulate/flush cycle.

NotificationEntry exposes its accumulated values two-decimal-formatted, registered under its own notification namespace rather than via registerDirect:

Placeholder

Resolves to

%notification_money%

Money accumulated since the last render.

%notification_experience%

XP accumulated since the last render.

%notification_current%

The player's current XP in that job.

%notification_total%

XP required for the next level — the progress-bar denominator, not a job total.

%notification_level%

The number of levels gained since the entry was created — NotificationEntry#incrementLevel is called once per level-up and never reset while the entry lives, so this is a level-up count, not the player's job level.

%notification_job%

The job's display-name.

Boosters

Key

Placeholders

Sent to

When

same-type-booster-active

—

Player

BoosterService#use finds the player already has an active/queued booster of the same type and scope as the item being used (BoosterRepository#hasActiveBooster). The item is refunded to the player's mailbox. The blueprint's %booster_*% chain is registered on the container even though the default text doesn't use it.

personal-booster-activated

%booster_formattedType%, %booster_remainingTime%

Player

An INDIVIDUAL-scope booster is created and saved successfully in BoosterService#use.

personal-booster-expired

%booster_formattedType%

Owner

The watchdog's BoosterService#expire deletes an INDIVIDUAL booster whose owner is currently online.

global-booster-activated

%booster_formattedType%, %booster_remainingTime%

Player (purchaser), then the owner

Sent by BoosterService#use to the purchaser when the new GLOBAL booster is immediately active. Otherwise sent later by BoosterService#activate, to the owner if they're online, when the queued booster crosses activatesAt on a watchdog tick.

global-booster-activated-notification

%booster_formattedType%, %booster_remainingTime%

Every other online player

BoosterService#activate fires this to everyone except the owner — only for a booster that goes live on a watchdog tick, i.e. a queued global reaching activatesAt (a global that is already running when the server starts is pre-marked by BoosterService's constructor, so a restart never re-announces it). use() pre-marks immediately-active boosters as activated, so in that case the rest of the server sees only global-booster-broadcast.

global-booster-broadcast

%booster_owner_name%, %booster_formattedType%, %booster_remainingTime%

The whole server

BoosterService#broadcastGlobalActivation, via AstralCore's ChatService#broadcastMessage. GLOBAL-scope only, and fired exactly once per booster — both use() and the watchdog's activate() gate it behind the same atomic first-activation set. Silently skipped when no ChatService is registered.

global-booster-queued

%booster_formattedType%, %booster_timeUntilActive%

Player (purchaser)

BoosterService#use when the new GLOBAL booster is not immediately active — i.e. it was chained behind an already-running global of the same type.

global-booster-expired

%booster_formattedType%

Owner

The watchdog's BoosterService#expire deletes a GLOBAL booster whose owner is currently online.

Activating a second GLOBAL booster of the same type while one is already running doesn't reject it — BoosterRepository#findLastGloballyQueued chains the new one behind the currently-running one (activatesAt = the running booster's expiresAt), and BoosterWatchdogTask activates it once that time arrives. The purchaser gets global-booster-queued at purchase time and global-booster-activated when it actually goes live.

See Placeholders → booster_* (active) for the full BoosterEntity placeholder catalogue, including how %booster_remainingTime%/%booster_timeUntilActive% are rendered through $format-date(…, format-duration).

Booster type labels

%booster_formattedType% on an active booster resolves to one of these three keys via BoosterType#message (BoosterType → JobsMessages):

BoosterType

Key

Default text

EXPERIENCE

type-experience

Élixir d'experience

MONEY

type-money

Élixir de richesse

BOTH

type-global

Élixir global

Rewards

Key

Placeholders

Sent to

When

reward-cannot-be-claimed

%level%

Player

The claim-job-reward action runs for a level the player hasn't reached yet. %level% is the reward's required level, not the player's current one.

no-rewards-available

—

Player

/jobs claim finds no unclaimed reward at or below the player's current level, across any job.

Jobs info

Sent in sequence by /jobs info: one job-info-header, then one job-info-entry per job the player has a JobLevelEntity for, then one job-info-footer.

Key

Placeholders

When

job-info-header

%levels%

First line. %levels% is the sum of the player's level across every job.

job-info-entry

%job%, %level%, %current%, %required%

One per job. %current% and %required% are the player's experience and the XP needed for the next level (job.computeLevelProgress(level + 1)), both formatted to two decimals — the same threshold used by the level-up loop, so this always agrees with when the next level-up message will fire.

job-info-footer

—

Last line. Shipped as an empty string, so by default it renders nothing.

Miscellaneous

Key

Placeholders

Sent to

When

unexpected-error

—

Player

Generic catch-all: menu-open failure on /jobs, missing JobsPlayerData on /jobs claim or the claim-job-reward action, no JobLevelEntity at all on /jobs info, or a BoosterRepository check/save failure in BoosterService#use. The triggering exception is always logged server-side.

Last modified: 25 September 2026