AstralHud Overview
AstralHud renders the shared HUD elements for AstralRealms — a per-player boss bar, action bar, and scoreboard, each built from a rotating set of configurable "containers" — plus a font-based image system used to draw pixel-precise backgrounds and progress-bar-style graphics inside chat components. It depends on AstralCore and packetevents (hard dependencies in plugin.yml), and soft-depends on AstralSync and CraftEngine.
The three HUD surfaces
Boss bar, action bar, and scoreboard are each driven by their own service/task/listener triplet, but all three follow the same model:
Containers — named entries defined in that surface's YAML file (
bossbar.yml,actionbar.yml,scoreboard.yml). Each container has acontent(ortitle/contentfor the scoreboard) built from aComponentWrapper— a placeholder-aware MiniMessage string — and an optional requirement list gating whether it's eligible to display.Slots (boss bar/action bar only) — containers share a
slotindex. Only one container per slot is shown at a time; when a slot has more than one eligible container, the surface rotates through them in priority order.Priority — within a slot (or, for the scoreboard, across the whole surface), eligible containers are sorted by
prioritydescending. The scoreboard always shows the single highest-priority container whose requirements pass; it does not rotate.Update loop — a repeating async task (
BossBarUpdateTask,ActionBarUpdateTask,ScoreboardUpdateTask) runs every 5 ticks per player and, per the configured intervals, re-selects eligible containers ("rotate") and/or re-renders the current container's placeholders ("refresh"). See Configuration for the exact field/interval mapping.
See Configuration for the full YAML shape of each surface.
Boss bar
BossBarService maintains one PlayerBossBar per online player and speaks the boss bar protocol directly through packetevents (WrapperPlayServerBossBar) rather than the Bukkit BossBar API — the bar itself is always blue/progress with no health/flags set; only its title text changes. Containers are grouped by slot, and the rendered title concatenates each slot's current container content, separated by two spaces, with a generated background image (see below) placed before each piece of text so it renders at a fixed pixel width regardless of content length.
Action bar
ActionBarService maintains one PlayerActionBar per online player and sends WrapperPlayServerActionBar packets directly. It behaves like the boss bar (slots, rotation, priority, per-slot concatenation) but has no background-image step, and adds a third timing field — resend-rate — because the client clears the action bar a few seconds after each packet: the surface must periodically resend the last-rendered component even when nothing changed.
Scoreboard
ScoreboardService maintains one PlayerScoreboard per player, backed by the bundled FastBoard packet scoreboard implementation (no vanilla Scoreboard/Team objects, no 1.20.4-style team-prefix line-length limits). Unlike the bar surfaces, a scoreboard container has a title in addition to its content lines, and there is no slot /rotation concept — ScoreboardService#refresh simply walks every configured container by priority descending and shows the first one whose requirements pass. If none pass, the scoreboard is deleted (FastBoard#delete) until one does.
Background images
BackgroundService#buildBackground renders a MiniMessage component's plain-text pixel width (via the CharacterInfo per-character width table) into a string of custom-font characters from the backgrounds.font resource-pack font (hud:backgrounds by default), using the resource-pack negative-space offset codepoints (FontOffset) to position it precisely behind the text with no visible gap. This is what lets the boss bar draw a solid-color/textured backdrop behind each container's text at exactly the width that text needs. It's currently only wired into the boss bar; the action bar and scoreboard do not use it. See backgrounds for the YAML shape.
Image manipulation
ImageManipulationConfiguration (image-manipulation.yml) defines reusable font-based graphics — e.g. repeating a single glyph N times to build a progress bar — plus lookup tables that map a numeric or string key straight to a pre-built Component. ImageManipulationService pre-renders every entry into an in-memory cache on load; the three placeholders in the imgmanip, imgprovider, and stringprovider namespaces read from that cache at render time. See Image Manipulation for the config shape and Placeholders for how to use the results.
Join handling
Boss bar, action bar, and scoreboard are all created for a player on the same event, chosen based on whether AstralSync is enabled at startup:
AstralSyncpresent —AstralSyncListeneradds all three surfaces onPlayerDataLoadedEvent(AstralSync's post-data-load join hook), ensuring player-data-backed placeholders are ready before the HUD first renders.AstralSyncabsent —VanillaListenerfalls back to the plain BukkitPlayerJoinEvent.
Each surface's own listener (BossbarListener, ActionbarListener, ScoreboardListener) removes that player's entry on PlayerQuitEvent.
Where to go next
Configuration —
config.yml,bossbar.yml,actionbar.yml,scoreboard.yml, andimage-manipulation.yml.Placeholders —
imgmanip_*,imgprovider_*,stringprovider_*.Commands —
/hud reload.