Astral Realms Documentation Help

Placeholders

AstralRanks registers one root placeholder namespace and three model namespaces that surface throughout its menus and messages. For the placeholder syntax itself (%a_b_c%, %a_{b_c}%, $e(...)) see Placeholders.

Namespace

Backing class

Globally registered?

ranks_*

RanksPlaceholders

Yes — RootPlaceholderContainer.get().registerPlaceholder(new RanksPlaceholders(this)) in AstralRanks#onEnable. Usable anywhere, not just inside AstralRanks menus.

rank_*

RankConfiguration

No — only reachable once a rank instance is placed into scope. See How rank/category/requirement enter menu scope.

category_*

RequirementCategory

No — same as rank_*.

requirement_*

WrappedRankRequirement

No — same as rank_*.

ranks_* (root)

Placeholder

Type

Description

%ranks_categories%

Collection<RequirementCategory>

Every category from categories.yml, in map order — categoriesConfiguration().categories().values(). Used as the categories layout's provider in menus/categories.yml (rank-details).

%ranks_current%

RankConfigurationplayer-only

The player's next rank — RankService#getNextRank(player). null if resolved without a Player context. Chains into any rank_* key, e.g. %ranks_current_name% or %ranks_current_item_lore%, since the returned RankConfiguration is itself a ComplexPlaceholder.

%ranks_questsDisplay%

List<Component>player-only

Up to 5 formatted lines, one per incomplete requirement in the player's enabled quest-display category, rendered through the quest-display-format message. null if the player has no PlayerRankData, no display category enabled, or no next rank. See Quest-display lines.

%ranks_enabledCategoryDisplay%

String — player-only

The category id (or "all") the player toggled on for the compact display, or null if disabled — PlayerRankData#categoryQuestsDisplay.

%ranks_categoryDisplayStatus%

Boolean — player-only

true when categoryQuestsDisplay is non-null — whether the quest display is on at all, independent of which category.

%ranks_unlockable_<rankId>%

Boolean — player-only

RankService#canUnlock(player, rankId) for the given rank id. Requires a second path token (the rank id) after unlockable; returns null without one or outside a Player context.

Quest-display lines

%ranks_questsDisplay% walks configuration.requirements() for the player's next rank, skips any requirement outside the enabled category (unless it's "all"), skips any already at or above its amount, and renders each remaining one through the quest-display-format message. Each line gets its own small PlaceholderContainer with three bare, unprefixed keys — not the usual requirement_* namespace:

Placeholder (inside the format string only)

Value

%name%

requirement.displayItem().name().

%current%

The player's stored progress on that requirement.

%total%

requirement.requirement().amount().

# messages.yml quest-display-format: "%name% </bold><secondary_color>%current%<gray>/<secondary_color>%total%"

The resulting list is capped at 5 entries (.limit(5)) regardless of how many requirements remain incomplete.

rank_*

A RankConfiguration — see Ranks & Progression for the underlying YAML shape.

Placeholder

Type

Description

%rank_id%

String

The rank's id.

%rank_item%

ItemStackPlaceholderItemStack

The display item, resolved through the current placeholder function. Bare, it yields the raw ItemStack; chain further per ItemStackPlaceholder sub-keys.

%rank_name%

Component

display-name.

%rank_next-rank%

String

next-rank id, or null on the last rank in the chain.

%rank_permission%

String

The rank's permission node.

%rank_requirements%

ItemProvider

requirements() values, wrapped for use as a layout provider; also exposes _size, _at_<index>, _list, _iterator. Reached in the shipped menus as %parameters_rank_requirements% — the categories layout's provider in menus/requirements.yml.

%rank_available%

Boolean — requires player context

RankService#isNextRank(player, id). Throws IllegalArgumentException if resolved outside a Player context (e.g. from console).

%rank_unlockable%

Boolean — requires player context

RankService#canUnlock(player, this). Same context requirement as available.

category_*

A RequirementCategory — id, display item, and per-rank rewards from categories.yml.

Placeholder

Type

Description

%category_id%

String

The category id.

%category_item%

ItemStackPlaceholderItemStack

The category's display item.

%category_rewards%

Map<String, PaperActionList>

Raw rewards map keyed by rank id — a leaf-level Java object, not further placeholder-chainable.

%category_requirements%

ItemProvider — requires parameters_rank in scope

Every requirement on the in-scope rank whose category equals this category's id (rank.requirements().values().stream().filter(req -> req.category().equals(id))). Looks up the rank via context.function().apply("parameters_rank") and throws IllegalArgumentException if that doesn't resolve to a RankConfiguration — i.e. this only works from a menu/message where a rank is open under the rank parameter. See below.

requirement_*

A WrappedRankRequirement — one mission entry inside a rank's requirements map.

Placeholder

Type

Description

%requirement_id%

String

The requirement's map key.

%requirement_item%

ItemStackPlaceholderItemStack

displayItem, the requirement's display item.

%requirement_requirement%

RankRequirement

The wrapped requirement itself — see Requirements & Missions for the economy/job/experience/item types.

%requirement_category%

String

The category id this requirement is grouped under.

%requirement_current%

int — requires player + parameters_rank in scope

The player's stored progress, via PlayerRankData#getRankProgressValue(player, rank.id(), id). Looks up the rank the same way as category_requirements — throws IllegalArgumentException if parameters_rank isn't in scope, or IllegalStateException if the player has no PlayerRankData.

%requirement_total%

int

requirement.amount() — the target amount; no player or rank context required.

rank, category, and requirement are never registered globally — a value only becomes resolvable once something places an instance into the active PlaceholderContainer. AstralRanks does this three ways:

1. Layout taint — %parameter_<namespace>_...% (singular). A layouts: entry supplies a collection of placeholders and a taint; item variants with a matching taints: entry render once per element. While an item is rendering, core's ParameterPlaceholder (namespace parameter) exposes that one element under its own namespace() key:

# menus/ranks.yml layouts: ranks: taint: "rank" provider: "%parameters_ranks%" items: rank: taints: ["rank"] name: "%parameter_rank_item_name%" view-requirements: - "[compare] %parameter_rank_available% == true"

The same pattern drives %parameter_category_...% (menus/categories.yml's categories layout, tainted category, provider %ranks_categories%) and %parameter_requirement_...% (menus/requirements.yml's categories layout, tainted requirement, provider %parameters_rank_requirements%).

2. Menu open-parameters — %parameters_<key>% (plural). MenuService passes Java objects into MenuContainer#computeAndOpen by key: openMainMenu passes ranks; openRankMenu passes rank and categories; openRequirementsMenu passes rank and category. Inside the opened menu, core's ParametersPlaceholder (namespace parameters) resolves those by key and chains into whichever namespace the value implements — %parameters_rank_id%, %parameters_category_id%, %parameters_rank_unlockable%. The same mechanism forwards a value from a %parameter_...% tainted element into an [open-menu] action's :key=value args, carrying a rank or category selected in one menu into the rank/category open-parameters of the next:

# menus/categories.yml (rank-details) — forwards this menu's own "rank" open-parameter, # plus the currently-tainted category element, into the next menu actions: - "[open-menu] ranks-requirements:rank=%parameters_rank%:category=%parameter_category%"
# menus/requirements.yml (ranks-requirements) — reads both back actions: - "[update-rank-requirement] %parameters_rank_id% %parameters_category_id% %parameter_requirement_id%"

This parameters_rank open-parameter is also exactly what %category_requirements% and %requirement_current% look up directly in Java (context.function().apply("parameters_rank")) rather than through a nested config placeholder — both throw if the container they're evaluated in has no rank parameter in scope.

3. Direct registration — message placeholders. Outside menus, RankService#unlock and MainCommand#onBroadcast build a one-off PlaceholderContainer and call .registerPlaceholder(configuration), which registers the RankConfiguration under its own namespace (rank) directly — no parameter/parameters wrapper involved. That container backs the unlock-success and unlock-broadcast messages, so those templates use the bare form:

# messages.yml unlock-success: "<success>Vous êtes maintenant %rank_item_name%." unlock-broadcast: "<newline>...%player_name%...a débloqué le rang ...%rank_item_name% !<newline>"

ItemStackPlaceholder sub-keys

%rank_item%, %category_item%, and %requirement_item% all resolve to an ItemStackPlaceholder wrapping the displayed ItemStack. Used bare (e.g. copy-from: "%parameter_requirement_item%") it yields the raw ItemStack; chained further, it exposes:

Sub-key

Returns

material

The item's Material, wrapped for further chaining (MaterialPlaceholder).

name

Display name — itemMeta.displayName(), falling back to itemName(), then the item's translation key.

hover-name

itemStack.displayName() (the name as shown in a tooltip).

lore

The item's lore lines, or an empty list.

amount

Stack size.

custom-model-data

Custom model data value, or 0.

durability

Remaining durability (maxDamage - damage), or 0 if unbreakable or non-damageable.

damage

Current damage, or 0 if unbreakable or non-damageable.

enchantments

The item's enchantment map.

unbreakable

Whether the item is flagged unbreakable.

Seen throughout the shipped menus as %parameter_rank_item_name%/%parameter_rank_item_lore% (item name and lore, menus/ranks.yml), %parameter_category_item_name% (menus/categories.yml), and %parameter_requirement_item_lore% (menus/requirements.yml) — plus the bare %rank_item_name% form used inside messages.yml.

Where these are consumed

File

Usage

menus/ranks.yml (ranks-list)

name/lore on rank, rank-complete, rank-lock (%parameter_rank_item_name%, %parameter_rank_item_lore%); view-requirements on %parameter_rank_available%/%parameter_rank_permission%; [open-menu] rank-details:rank=%parameter_rank%.

menus/categories.yml (rank-details)

copy-from: "%parameter_category_item%"; view-requirements on %parameter_category_requirements_size% and %parameters_rank_unlockable%; [unlock-rank] %parameters_rank_id%; [open-menu] ranks-requirements:rank=%parameters_rank%:category=%parameter_category%.

menus/requirements.yml (ranks-requirements)

copy-from: "%parameter_requirement_item%"; view-requirements comparing %parameter_requirement_category% against %parameters_category_id%, and %parameter_requirement_current% against %parameter_requirement_total%; [update-rank-requirement] %parameters_rank_id% %parameters_category_id% %parameter_requirement_id%.

messages.yml

unlock-success/unlock-broadcast use %rank_item_name% (direct registration — see above); quest-display-format uses the bare %name%/%current%/%total% triple (see Quest-display lines).

Where to go next

  • Ranks & Progression — the rank YAML shape and the unlock flow that registers %rank_*% for messages.

  • Requirements & Missionscategory/requirement YAML shape and reward payout.

  • Messages — the full message-key catalogue, including unlock-success, unlock-broadcast, and quest-display-format.

  • Developer APIunlock-rank, update-rank-requirement, and set-displayed-ranks-quests, the action that drives categoryQuestsDisplay.

Last modified: 25 July 2026