Skip to content
Nacvark edited this page Jul 29, 2026 · 2 revisions

🌎 Choose language: EN — English · RU — Русский

API

Everything on this page is optional. A HUD needs no code — this is for plugins that want to feed the HUD their own data or drive it.

Adding the dependency

Only hudengine-api is published; it carries interfaces and no implementation.

<dependency>
    <groupId>io.github.nacvark</groupId>
    <artifactId>hudengine-api</artifactId>
    <version>1.0.0</version>
    <scope>provided</scope>
</dependency>
compileOnly("io.github.nacvark:hudengine-api:1.0.0")

Then declare the plugin in plugin.yml:

softdepend: [HUDEngine]

softdepend rather than depend unless your plugin is useless without a HUD. It only orders startup; your plugin still loads if HUDEngine is absent.

Getting hold of the engine

HUDEngine registers itself as a Bukkit service, and only after the configuration compiled. That is deliberate: an engine that failed to compile is not usable, and a null service says so honestly instead of failing later in a way nobody can trace.

Optional<HudEngine> maybe = HudEngineProvider.find();   // empty if unavailable
HudEngine hud = HudEngineProvider.get();                // throws if unavailable

Use find() at startup, get() once you know it is there.

Publishing your own values

Register a function under a key, and [key] works in any pattern in layouts/.

HudEngineProvider.find().ifPresent(hud ->
        hud.values().register("myplugin:mana", player ->
                String.valueOf(manaOf(player))));
texts:
  1:
    name: default
    pattern: "Mana [myplugin:mana]"

Namespace your keys. mana collides with whatever else calls itself mana; myplugin:mana does not.

Returning "1" or "0" makes the value usable as a condition, so you can show and hide elements without any further API.

Providers run once per HUD tick per player, on that player's thread. Keep them to a lookup. If a value is expensive, compute it on your own schedule and return the cached result:

hud.values().register("myplugin:rank", player ->
        rankCache.getOrDefault(player.getUniqueId(), ""));   // no database call here

register returns whatever was registered under that key before, or null. unregister(key) removes yours — do it in onDisable().

Compass points

This is where the API earns its keep. Fixed points live in points/; anything that moves comes from code.

hud.compass().register("quests", player ->
        questsOf(player).stream()
                .map(quest -> CompassPoint.at(quest.location(), "quest"))
                .toList());

Three ways to build a point:

CompassPoint.at(location, "quest");                  // shown from any distance
CompassPoint.within(location, "quest", 300);         // only within 300 blocks
CompassPoint.flat("world", 128, -512, "bank");       // no Y, so height is ignored

The icon names a key under file: in your compass definition. Omit it for the default marker.

Party members, quest objectives, the nearest shop, a moving boss — all the same shape: return the current list, the engine draws it.

Driving a player's HUDs

PlayerHuds huds = hud.player(player);

huds.toggle();                    // returns the new visibility
huds.setVisible(false);
huds.show("boss_fight");
huds.hide("boss_fight");
huds.showFor("level_up", 60);     // show, then hide again after 60 ticks
huds.reset();                     // back to the configured default set
huds.visible();                   // what is on screen now
huds.refresh();                   // rebuild immediately instead of on the next tick

showFor is the one to reach for on an event — a level-up banner, a kill notification, a warning that expires by itself.

Measuring text

The HUD font is not the chat font, so String.length() tells you nothing about how wide a line will be. HudMetrics measures in the font a given element actually uses:

int px = hud.metrics().width("dialog", "dialog_text_1", "Hello");
List<String> lines = hud.metrics().wrap("dialog", "dialog_text_1", description, 220);

wrap breaks text to a pixel width, which is what you want for dialogue or descriptions of unpredictable length.

Reacting to reloads

/hudengine reload swaps the compiled model. Anything you cached about HUD keys is stale afterwards:

@EventHandler
public void onHudsReloaded(HudsReloadedEvent event) {
    event.huds();     // the HUD keys that now exist
}

Registered values and compass providers survive a reload — you do not need to register them again.

Clone this wiki locally