> For the complete documentation index, see [llms.txt](https://mending-dev.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mending-dev.gitbook.io/docs/bossbarhud/developer-guide/using-the-api.md).

# Using the API

Concepts, examples and reference of the BossbarHUD API.

## Concepts

* **Hud:** a named group of boxes with a priority. HUDs with a lower priority are rendered further to the left.
* **Segment:** one box inside a HUD, identified by an id. A segment is placed on a `line` (default `1`) and sorted by `order` inside that line. Its content comes from a `HudSegmentProvider` (dynamic, per player) or a fixed `Component`.
* **Global HUD:** visible to everyone. `hide(player)` excludes a single player.
* **Personal HUD:** visible to nobody until you call `show(player)`.

A player sees the boxes of all HUDs they can see, grouped by line and, inside a line, ordered by HUD priority and then by segment order.

HUD ids are unique per plugin and may only contain lowercase letters, digits and `_ - . /`.

## Personal HUD for one player

```java
Hud hud = api.createHud(plugin, "minigame")
        .priority(10)
        .segment("score", 0, player -> Component.text("Score: " + scores.get(player.getUniqueId())))
        .segment("time", 1, Component.text("Time: 1:20"))
        .build();

hud.show(player);   // send the HUD to one player
hud.hide(player);   // remove it from one player
hud.unregister();   // remove it completely
```

A segment provider returns the content for the viewer. If it returns `null`, the box is hidden for that player.

## Global HUD

```java
Hud hud = api.createHud(plugin, "server-info")
        .global()
        .segment("online", 0, p -> Component.text("Online: " + Bukkit.getOnlinePlayers().size()))
        .build();

hud.hide(player);   // exclude one player
hud.show(player);   // undo
hud.reset();        // back to the default audience (everyone)
```

## Lines

By default every box is on line 1, so all boxes are placed next to each other. Use the `line` parameter to put boxes below each other:

```java
Hud hud = api.createHud(plugin, "stats")
        .segment("kills", 1, 0, p -> Component.text("Kills: " + kills(p)))    // line 1, left
        .segment("deaths", 1, 1, p -> Component.text("Deaths: " + deaths(p))) // line 1, right of kills
        .segment("time", 2, 0, Component.text("Time: 1:20"))                  // line 2
        .build();

hud.setSegment("best", 2, 1, Component.text("Best: 0:58"));                  // line 2, right of time
```

The parameters are `id, line, order, content`. The overloads without `line` use `Hud.DEFAULT_LINE` (`1`).

Lines are shared between all HUDs: boxes of different HUDs and the config HUD on the same line are placed in the same row. Only the relative order of the lines counts, so if the lines are `1` and `5` there are two rows, and a single box is always in the first row.

## Conditions

A box can have a condition. It is checked for every viewer before the provider is called. If it is false, the box is hidden for that player and the provider is not called at all.

```java
HudCondition inNether = player -> player.getWorld().getEnvironment() == World.Environment.NETHER;
HudCondition vip = api.condition("permission=server.vip && %player_level% >= 10"); // same syntax as config.yml

Hud hud = api.createHud(plugin, "status")
        .segment("nether", 1, 0, p -> Component.text("Nether"), inNether)
        .segment("vip", 1, 1, Component.text("VIP"), vip.and(inNether.negate()))
        .build();

hud.setSegment("bonus", 2, 0, p -> Component.text("Bonus: " + bonus(p)), vip.or(inNether));
```

* `HudCondition` is a functional interface (`boolean test(Player)`) with `and`, `or`, `negate` and `HudCondition.always()`.
* `api.condition(String)` parses an expression with the syntax of `segments.<id>.condition` in the config (comparisons, `&&`, `||`, `!`, parentheses, quoted strings, PlaceholderAPI placeholders, `permission=node`). It throws an `IllegalArgumentException` that names the position of the error. Parse once and keep the result: the parsed condition only resolves the placeholders and compares. Without PlaceholderAPI the placeholders stay literal text.
* The overloads with a condition always take a `line`: `segment(id, line, order, provider|content, condition)` and `setSegment(id, line, order, provider|content, condition)`.
* A condition that throws an exception hides its box; the exception is logged once.
* A provider that returns `null` still hides a box as well. Prefer a condition when the check does not need the content, because it skips the provider.

## Change a HUD at runtime

```java
hud.setSegment("time", 1, Component.text("Time: 1:19")); // add or replace, viewers update immediately
        hud.removeSegment("time");
hud.segmentIds();
```

## Update without waiting for the interval

Dynamic segments are re-evaluated every `update-interval-ticks`. If you want to show a change immediately:

```java
hud.refresh(player);   // one player
hud.refresh();         // all viewers of this HUD
api.refresh(player);   // everything this player sees
```

## Temporary message

```java
// Removed automatically when the player leaves
Hud hud = api.send(plugin, player, Component.text("Welcome!"), Component.text("Have fun"));
hud.unregister(); // remove earlier
```

## Remove HUDs

```java
hud.unregister();               // one HUD
api.unregisterAll(plugin);      // all HUDs of your plugin
api.unregisterAll();            // all HUDs of all plugins, including the config HUD
api.clear(player);              // hide every HUD from this player
api.reset(player);              // undo clear(player) and all per-HUD overrides
api.setHidden(player, true);    // master switch, used by /bossbarhud toggle
```

## Query

```java
Optional<Hud> hud = api.getHud(plugin, "minigame");
Collection<Hud> all = api.getHuds();
Collection<Hud> mine = api.getHuds(plugin);
Collection<Hud> visible = api.getHuds(player);
```

## Glyphs and shifts

You can use the Nexo tags `<glyph:id>` and `<shift:pixels>` in any component that you pass to BossbarHUD, for example one that you built with MiniMessage:

```java
Component text = MiniMessage.miniMessage().deserialize("<glyph:mouse_left> <white>Click");

hud.setSegment("action", 0, text);
```

BossbarHUD finds these tags in the text and replaces them with the real glyph characters before it calculates the length of the box. You do not have to do anything else: the glyph is displayed and the box has the correct length.

* Only `<glyph:id>` (requires Nexo) and `<shift:pixels>` are resolved. Extra arguments of the Nexo tag (index, shadow) are ignored.
* The width of every custom glyph must be known. The server admin sets it in `glyph-widths` of the config, which is the recommended way because it is one place for all plugins. You can also register it in code with `api.registerGlyphWidth('\uE000', 9)`. A glyph without a width is measured as 6 pixels. This also applies to components that already contain real glyph characters.

## Utilities

```java
Component box = api.box(Component.text("Hello"));       // text wrapped in a box background
int width = api.measure(Component.text("Hello"));       // pixel width of a component
api.registerGlyphWidth('\uE000', 9);                    // width of a custom glyph character
```

Glyph widths from the server config take precedence over widths registered through the API.

## Lifecycle and threading

* HUDs of a plugin are removed automatically when that plugin is disabled. You do not have to clean up in `onDisable`.
* Personal HUD assignments of a player are removed when the player leaves.
* All methods can be called from any thread. Updates are applied on the main thread.
* Segment providers and conditions are always called on the main thread, so keep them fast and do not block.
* The HUD rows always stay above the bossbars of other plugins. The client puts a new bossbar at the bottom, so when a row has to be added (a line whose boxes were all hidden, or after `clear(player)` and `show`), BossbarHUD briefly hides the other bars this player sees and shows them again below the HUD, in their order. This works for bars shown through the Paper/Adventure API (`player.showBossBar(bar)`); bars of the old Bukkit API (`Bukkit.createBossBar`) and vanilla bars (wither, ender dragon, raid, `/bossbar`) cannot be moved. A row that becomes empty is hidden one tick later, so `clear(player)` followed by `show` in the same tick moves nothing.

## API reference

| Type                 | Purpose                                                                            |
| -------------------- | ---------------------------------------------------------------------------------- |
| `HudAPI`             | Entry point: create, query and remove HUDs, utilities, parse conditions            |
| `Hud`                | A registered HUD: segments, audience, refresh, unregister                          |
| `HudBuilder`         | Builds a HUD: `priority`, `global`, `segment`, `build`                             |
| `HudSegmentProvider` | Supplies the content of a box for a player, `null` hides the box                   |
| `HudCondition`       | Decides per player whether a box is shown; `HudAPI#condition` parses an expression |
