Astral Realms Documentation Help

Requirements & Missions

A rank's requirements map is the set of individual missions a player must clear before that rank becomes unlockable. Each entry deserialises to a WrappedRankRequirement, is grouped under a category (defined in categories.yml), and is advanced one click at a time through the update-rank-requirement action from the shipped mission menus. See Ranks & Progression for the surrounding rank file shape and the unlock flow, and AstralRanks Overview for how this fits into the bigger picture.

Requirement entries

Each entry in a rank's requirements: map deserialises via WrappedRankRequirementTypeSerializer into a WrappedRankRequirement:

Field

YAML key

Type

Description

id

(map key)

String

Not a YAML field — taken from the entry's key in the requirements: map. Used as the progress key in PlayerRankData and as the argument to update-rank-requirement.

displayItem

display-item

ItemStackWrapper

The item shown for this requirement in the ranks-requirements menu.

requirement

requirement

RankRequirement

The typed requirement node — see Requirement types.

category

category

String

Id of a RequirementCategory from categories.yml. Not cross-validated against categories.yml at rank-load time — a typo'd or otherwise-missing category id only surfaces later: any update-rank-requirement call against it returns REQUIREMENT_NOT_FOUND.

requirements: eco: category: "eco" display-item: material: GOLD_INGOT name: "<#267ad9><bold>Mission Richesse" lore: - "" - "<#e3dbcc>Progression : <#26d971>%parameter_requirement_current%/%parameter_requirement_total%" requirement: type: economy currency: "default" amount: 250

WrappedRankRequirement is itself a ComplexPlaceholder (namespace requirement) — see Placeholders for the full %requirement_*% chain.

Requirement types

The requirement.type field selects the concrete RankRequirement implementation via RankRequirementTypeSerializer. An unrecognised type fails deserialisation for the whole rank file.

type

Class

YAML keys

Amount unit

shouldCountUntilFull()

economy

CurrencyRankRequirement

currency, amount

Currency units (int)

true

job

JobsLevelRankRequirement

level

Summed job levels

false

experience

ExperienceRankRequirement

amount

Total XP points

true

item

ItemRankRequirement

material, amount

Item count

true

economy

requirement: type: economy currency: "default" amount: 250

CurrencyRankRequirement#take looks up the Core EconomyService, reads the player's cached balance for currency, and withdraws min(available, amount - currentValue) (truncated to an int — fractional balances beyond the whole unit are not counted). If the withdraw call fails, no progress is recorded.

job

requirement: type: job level: 5

Backed by AstralJobs' JobsAPI.getTotalLevels(player) — the sum of the player's levels across every job, not one specific job. See shouldCountUntilFull semantics below for why this type behaves differently from the other three.

experience

requirement: type: experience amount: 5

Backed by Core's ExperienceUtilsgetTotalExperience converts the player's level + XP bar progress into a single cumulative XP-point count (vanilla formula); take withdraws the needed amount via setTotalExperience(player, -toTake), which recomputes and re-applies the player's level and XP bar.

item

requirement: type: item material: OAK_LOG amount: 8

ItemRankRequirement#take scans player.getInventory().getContents() (hotbar + main storage only — armor and off-hand slots are not scanned), removing matching stacks slot by slot until amount - currentValue items have been taken. Matching is by Material only — enchantments, custom NBT, and display name are ignored, so any stack of that material counts (unlike AstralCollections' entry matching, see Blueprint YAML Reference).

shouldCountUntilFull semantics

RankRequirement#shouldCountUntilFull() controls whether RankService#updateRequirement persists partial progress after a take() call:

  • economy, experience, item (true) — every successful take() adds the taken amount to the stored progress (current + amount), even if that doesn't reach the required total. Progress accumulates across multiple menu clicks — a player can deposit 3 of 8 logs now and 5 more later.

  • job (false) — progress is only written to PlayerRankData when the taken amount already meets or exceeds the required total in a single call; otherwise nothing is persisted and the stored progress stays at its previous value (typically 0). In practice this means the job requirement is not incremental: each click recomputes JobsAPI.getTotalLevels(player) live and either the player's current total levels already cover the requirement (it completes immediately) or they don't (the progress message shows the live total/required split, but nothing is saved) — leveling up jobs elsewhere and re-opening the menu is what actually advances it, not clicking the requirement itself.

Categories

Categories are defined in categories.yml (CategoryConfiguration.categories, keyed by category id) and deserialise to RequirementCategory via CategoryTypeSerializer:

Field

YAML key

Type

Description

id

(map key)

String

The category id used by category: on requirement entries and by the %category_*%/%parameters_category_id% placeholders.

itemStack

(inline)

ItemStackWrapper

The category's display item — material, name, lore, etc. sit directly on the category node (not under a nested key).

rewards

rewards

Map<String, PaperActionList>

Per-rank reward action lists, keyed by rank id.

categories: farmer: material: "wheat" name: "<green>Farmer" lore: - "<requirements>" - "<yellow>Click to open!" rewards: default: - "[console] give %player_name% diamond 5"

rewards is keyed by rank id, not by category — the same category can be reused across several ranks in the chain, each with its own reward list (default here refers to the rank whose id: "default", i.e. config.yml's default-group).

Category completion

Whenever update-rank-requirement pushes a requirement to completion, RankService#updateRequirement:

  1. Checks PlayerRankData#hasCompletedCategory(rankId, categoryId) — if that rank/category pair is already marked completed, it stops here (rewards are never re-granted).

  2. Otherwise calls RankService#hasCompletedCategory, which re-checks every requirement in the rank that shares this category id against the player's stored progress.

  3. If all of them are satisfied, it calls PlayerRankData#setCategoryCompleted(rankId, categoryId) and runs category.rewards().get(rankId) on the main thread.

RequirementCategory is itself a ComplexPlaceholder (namespace category); its requirements case returns an ItemProvider of every WrappedRankRequirement in the current %parameters_rank% whose category matches — that's what backs %category_requirements_size% (see Placeholders in lore).

update-rank-requirement

[update-rank-requirement] <rankId> <categoryId> <requirementId>

Drives progress on a single requirement for the executing player. Record fields: rankId, category, requirement (all @Single PlaceholderWrapper<String>). Implemented by UpdateRequirementAction, which resolves the rank and delegates to RankService#updateRequirement:

  1. Looks up the category (REQUIREMENT_NOT_FOUND if missing) and the matching requirement in the rank whose category and map-key both match (REQUIREMENT_NOT_FOUND if no match).

  2. Reads current progress from PlayerRankData; if current >= required already, sends REQUIREMENT_ALREADY_COMPLETED and stops.

  3. Calls requirement.take(player, current). On failure sends REQUIREMENT_TAKE_ERROR; if the taken amount is 0, sends REQUIREMENT_NO_PROGRESS.

  4. Otherwise updates stored progress per shouldCountUntilFull and:

    • If the new total meets or exceeds the required amount: persists the full amount, sends REQUIREMENT_COMPLETED, and runs the category completion check.

    • Otherwise sends REQUIREMENT_PROGRESS with %current%, %total%, and %c% (remaining) placeholders resolved against the new totals.

See Messages for the exact message keys and Developer API for the registered action signature.

actions: - "[update-rank-requirement] %parameters_rank_id% %parameters_category_id% %parameter_requirement_id%" - "[refresh]"

has-completed-requirement/has-completed-category

Two PaperRequirements for gating menu items or other actions on mission progress:

Requirement

Arguments

Checks

has-completed-requirement

rankId, categoryId, requirementId

RankService#hasCompletedRequirement — the single named requirement's stored progress meets its amount.

has-completed-category

rankId, categoryId

RankService#hasCompletedCategory — every requirement in the rank sharing that category id is satisfied.

view-requirements: - "[has-completed-category] %parameters_rank_id% %parameter_category_id%"

Both read the player's PlayerRankData via SyncAPI; has-completed-category throws if no player data is found for the executor. See Developer API for the registered requirement signatures (#has-completed-category, #has-completed-requirement).

rank-details (menus/categories.yml)

Lists a rank's categories: the categories layout binds taint: "category" to provider: "%ranks_categories%" (RanksPlaceholders → every loaded RequirementCategory). The category_open item is shown when %parameter_category_requirements_size% > 0 (i.e. the category has at least one requirement in the current rank) and opens the requirements menu:

actions: - "[open-menu] ranks-requirements:rank=%parameters_rank%:category=%parameter_category%"

category_close overrides it (higher priority) once [has-completed-category] %parameters_rank_id% %parameter_category_id% passes.

ranks-requirements (menus/requirements.yml)

Lists one category's requirements: provider: "%parameters_rank_requirements%" chains the menu's rank parameter (a RankConfiguration) into its requirements case (an ItemProvider of every WrappedRankRequirement on that rank). Two competing item variants are filtered to the current category via [compare] %parameter_requirement_category% == %parameters_category_id%:

  • requirement-open (priority: 1) — the default state. Copies the requirement's display-item (%parameter_requirement_item%) and, on left-click, runs update-rank-requirement followed by [refresh].

  • requirement-done (priority: 2) — overrides it once %parameter_requirement_current% >= %parameter_requirement_total%, showing a locked/checkmark icon instead.

A scoreboard-display button runs set-displayed-ranks-quests scoped to the current category; rank-details's own scoreboard-display button runs it with the literal argument all.

set-displayed-ranks-quests

[set-displayed-ranks-quests] <categoryId | all>

Single record field categoryWrapper. Toggles PlayerRankData#categoryQuestsDisplay for the executing player:

  • If the stored value already equals the given argument, it's cleared to null and QUESTS_DISPLAY_DISABLED is sent (turning the scoreboard display off).

  • Otherwise the argument is stored as-is and QUESTS_DISPLAY_ENABLED is sent.

"all" has no special handling here — it's just stored like any other category id. It's RanksPlaceholders' ranks_questsDisplay case that treats a stored value of "all" (case-insensitively) as "show every category's requirements" instead of filtering to one, rendering up to 5 incomplete requirements through the QUEST_DISPLAY_FORMAT message. See Player data & sync and Developer API.

Placeholders in lore

Placeholder

Resolves to

Requires

%parameter_requirement_current%

WrappedRankRequirement's "current" case — the player's stored progress for this requirement (via PlayerRankData#getRankProgressValue).

A Player context and a resolvable parameters_rank (RankConfiguration) in scope.

%parameter_requirement_total%

requirement.amount() — the required total.

%category_requirements_size%

The category's matching requirements list (filtered to the current %parameters_rank%), chained into ItemProvider's size.

A resolvable parameters_rank in scope.

These are the exact placeholders the shipped rank files use in display-item.lore:

lore: - "<#e3dbcc>Progression : <#26d971>%parameter_requirement_current%/%parameter_requirement_total%"

See Placeholders for the full %requirement_*%/%category_*% reference.

Minimal example

One requirement entry of each type, as they appear on ranks/default.yml:

requirements: eco: category: "eco" display-item: material: GOLD_INGOT name: "<#267ad9><bold>Mission Richesse" lore: - "<#e3dbcc>Progression : <#26d971>%parameter_requirement_current%/%parameter_requirement_total%" requirement: type: economy currency: "default" amount: 250 jobs-level: category: "jobs-level" display-item: material: DIAMOND_PICKAXE name: "<#267ad9><bold>Mission Niveau métiers" lore: - "<#e3dbcc>Progression : <#26d971>%parameter_requirement_current%/%parameter_requirement_total% Niveaux métiers" requirement: type: job level: 5 player-level: category: "player-level" display-item: material: EXPERIENCE_BOTTLE name: "<#267ad9><bold>Mission Niveau joueur" lore: - "<#e3dbcc>Progression : <#26d971>%parameter_requirement_current%/%parameter_requirement_total% Niveaux joueur" requirement: type: experience amount: 5 lumberjack: category: "lumberjack" display-item: material: "oak_log" name: "<#267ad9><bold>Mission Bûcheron" lore: - "<#e3dbcc>Progression : <#26d971>%parameter_requirement_current%/%parameter_requirement_total% bûches de chêne" requirement: type: item material: OAK_LOG amount: 8
Last modified: 25 July 2026