Astral Realms Documentation Help

Configuration

AstralHud loads five files from its data folder, each reloaded together by /hud reload: config.yml, bossbar.yml, actionbar.yml, scoreboard.yml, and image-manipulation.yml. AstralHud#loadConfiguration() loads the first four unconditionally; image-manipulation.yml is wrapped in a try/catch — a malformed file logs a warning (Failed to load image-manipulation.yml configuration...) instead of aborting the rest of the reload.

config.yml

A single top-level backgrounds section.

backgrounds

Field

Type

Description

font

Key (namespaced id)

The resource-pack font that supplies the background glyphs, e.g. hud:backgrounds. Applied via Component#font on the generated background component.

mappings

Map<Integer, String>

Maps a pixel-width value to the single character in that font which is exactly that many pixels wide. Must include 0 (the start glyph), 256 (the end glyph), and enough powers of two to cover any width you need — the shipped default covers 0, 1, 2, 4, 8, 16, 32, 64, 128, 256.

backgrounds: font: "hud:backgrounds" mappings: 0: "a" 1: "b" 2: "c" 4: "d" 8: "e" 16: "f" 32: "g" 64: "h" 128: "i" 256: "j"

BackgroundsConfiguration#build(length) greedily covers the requested pixel width using the largest mapped values first (256, 128, 64, …), each preceded by a NEG_1 negative-space codepoint so consecutive background glyphs overlap into one continuous image instead of stacking side by side; it starts with the 0-glyph and ends with the 256-glyph plus a negative-space offset sized to the total width. Only powers of two present in mappings are used, so removing an entry (e.g. 64) forces the builder to reach the same width using smaller pieces. See Background images for how the boss bar uses this.

bossbar.yml

Field

Type

Description

update-rate

long (ms)

How often an unchanged container's placeholders are re-rendered ("refresh").

refresh-rate

long (ms)

How often the next-priority eligible container in each slot is selected ("rotate").

containers

Map<String, InfoContainer>

The pool of displayable entries, keyed by an arbitrary map key (the container's own id field is what's actually used for lookups, and should match the map key).

InfoContainer fields

Field

Type

Description

id

String

The container's identity, used in log messages and cache lookups.

slot

int

Containers sharing a slot compete for the same display position; only one is shown at a time.

priority

int

Within a slot, eligible containers are shown in descending priority order as rotation advances.

content

ComponentWrapper

The rendered text — a placeholder-aware MiniMessage string.

requirements

PaperRequirementList (optional)

Gates eligibility; a container whose requirements fail is skipped during rotation. Omit for an always-eligible container.

extra-length

int

Extra pixels padded onto this container's background width, e.g. to leave room around the text. Defaults to 0.

containers: name: id: "name" slot: 0 priority: 1 content: "%player_name%" extra-length: 0 test: id: "test" slot: 1 priority: 1 content: "Test" health: id: "health" slot: 1 priority: 0 content: "%health_bar%"

Here slot 1 has two containers (test, health); BossBarUpdateTask rotates between them every refresh-rate ms, always picking the highest-priority one that's currently eligible, while slot 0 (name) has nothing to rotate into. Each slot's current text is separated from the next by two spaces.

actionbar.yml

Same container shape as the boss bar (InfoContainer), minus the background-image step — the action bar has no extra-length-driven backdrop. One extra timing field:

Field

Type

Description

update-rate

long (ms)

Refresh interval — same meaning as the boss bar's.

refresh-rate

long (ms)

Rotation interval — same meaning as the boss bar's.

resend-rate

long (ms)

How often the last-rendered component is re-sent to the client even if unchanged. Needed because the vanilla client hides the action bar a few seconds after each packet.

containers

Map<String, InfoContainer>

Same shape as boss bar containers (its extra-length field is accepted but unused here).

update-rate: 1000000000 refresh-rate: 100000000

scoreboard.yml

Field

Type

Description

update-rate

long (ms)

How often the eligible-container selection is re-evaluated (ScoreboardService#refresh — picks the highest-priority container whose requirements pass).

rotation-rate

long (ms)

How often the currently-selected container's title/content are re-rendered (ScoreboardService#update).

containers

Map<String, SBContainer>

The pool of displayable scoreboards, keyed by an arbitrary map key.

SBContainer fields

Field

Type

Description

id

String

The container's identity.

priority

int

Containers are compared across the entire surface (no slot grouping) — the highest-priority eligible container wins.

title

ComponentWrapper

The scoreboard's title line.

content

ComponentWrapperList

The scoreboard's body lines, top to bottom.

requirements

PaperRequirementList (optional)

Gates eligibility, same as the bar surfaces.

containers: health: id: "health" priority: 1 title: "Health Status" content: - "Health: %player_health%" requirements: - "[compare] %player_health% < 20" name: id: "name" priority: 0 title: "Player Name" content: - "Name: %player_name%"

With this example, players below 20 health see the health container (higher priority, requirement passes); everyone else falls through to name. If no container's requirements pass, the scoreboard is removed until one does.

image-manipulation.yml

Backs the imgmanip, imgprovider, and stringprovider placeholders. Every entry is pre-rendered into an in-memory cache once, on load — none of it is re-evaluated per placeholder call except the placeholder's own lookup key.

Field

Type

Description

manipulations

List of ImageManipulationEntry

Repeated-glyph "meters" — an element string repeated N times, for every N in a bounded range. Backs %imgmanip_<id>%.

image-provider

Map<String, Map<Integer, ComponentWrapper>>

Per-id lookup tables keyed by integer. Backs %imgprovider_<id>_...%.

image-provider-string

Map<String, Map<String, ComponentWrapper>>

Per-id lookup tables keyed by string. Backs %stringprovider_<id>_...%.

ImageManipulationEntry

Field

Type

Description

id

String

Looked up by the first path token after %imgmanip_.

element

ComponentWrapper

The glyph/text repeated to build each amount's image. Resolved once at load time (against an empty placeholder container), not per-player — don't put player-specific placeholders in element.

amount

PlaceholderWrapper<Double>

Not used at cache-build time; consumed by %imgmanip_<id>% at render time to pick which pre-built amount to return (rounded to the nearest int).

lower-bound/upper-bound

int

The inclusive range of amounts pre-rendered and cached (element repeated lowerBoundupperBound times). A rendered amount outside this range returns null.

manipulations: - id: "hp-bar" element: "<red>■" amount: "%player_health%" lower-bound: 0 upper-bound: 20 image-provider: compass: 0: "<gray>↑" 45: "<gray>↗" 90: "<gray>→" image-provider-string: rank-icon: "vip": "<gold>★" "default": "<gray>☆"

The shipped default image-manipulation.yml ships with all three sections empty ([]).

Update timing

Every surface's per-player update task runs every 5 ticks (~250ms) regardless of configuration and, each run, checks the surface's own intervals against that player's last-action timestamps:

Surface

"Rotate" check

"Refresh" check

Extra

Boss bar

lastRotation + refresh-rate <= now → re-select containers, then re-render

lastUpdate + update-rate <= now → re-render only

Action bar

Same as boss bar

Same as boss bar

lastResend + resend-rate <= now → re-send the last-rendered component, checked first, every run

Scoreboard

lastRefresh + update-rate <= now → re-select the eligible container, then re-render

lastUpdate + rotation-rate <= now → re-render only

Note the scoreboard's field names are the reverse of what they sound like: update-rate drives container selection and rotation-rate drives placeholder re-rendering — match the boss bar/action bar's refresh-rate behavior, not their own names. A rotate/select always implies a re-render in the same pass; the refresh-only branch only fires when a rotate/select didn't already happen that run.

See also

  • Overview — how the boss bar, action bar, and scoreboard fit together conceptually.

  • Placeholders — the imgmanip/imgprovider/stringprovider placeholders backed by image-manipulation.yml.

  • Commands/hud reload.

Last modified: 25 July 2026