> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.refineryteam.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Placeholders

> A shared PlaceholderAPI-style registry living inside RefineryCore — no external dependency.

`xyz.refineryteam.refinerycore.api.text.Placeholders` is a lightweight PlaceholderAPI-style resolver. Plugins register named placeholders once, then any string can reference them as `%namespace_key%` and get substituted before MiniMessage parsing. Unlike PlaceholderAPI there is no external dependency — the registry lives inside RefineryCore, so suite plugins share one namespace without each shipping their own expansion.

## Registering a placeholder

```java EconomyExpansion.java icon="java" theme={"system"}
// Answers %economy_balance%
Placeholders.register("economy", "balance", (player, args) ->
    String.valueOf(Economy.getBalance(player)));

// With arguments: %myplugin_top:3% passes args = "3"
Placeholders.register("myplugin", "top", (player, args) ->
    Leaderboard.entry(Integer.parseInt(args)));
```

The resolver receives the viewing player (possibly `null` for console/global contexts) and anything after a colon in the placeholder body. Return `""` rather than `null` for empty values.

| Method                                             | Description                                                                                                            |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `register(namespace, name, resolver)`              | Registers a namespaced placeholder answering `%namespace_name%` — prefer this form to avoid collisions between plugins |
| `register(name, resolver)`                         | Registers an unnamespaced placeholder answering `%name%`                                                               |
| `unregister(namespace, name)` / `unregister(name)` | Removes a previously registered placeholder                                                                            |

## Rendering placeholders

```java BroadcastTask.java icon="java" theme={"system"}
// Substitutes placeholders, then parses the result as MiniMessage:
Component text = Placeholders.render("<gray>Balance: <gold>%economy_balance%", player);

// Raw substitution only — for scoreboard lines, Discord webhooks, etc:
String plain = Placeholders.apply("Top player: %myplugin_top:1%", player);
```

| Method                 | Description                                                                                  |
| ---------------------- | -------------------------------------------------------------------------------------------- |
| `render(raw, player)`  | Substitutes all known placeholders, then parses the result as MiniMessage into a `Component` |
| `apply(raw, player)`   | Substitutes placeholders and returns the raw string, without MiniMessage parsing             |
| `hasPlaceholders(raw)` | Whether any registered placeholder matches in the string                                     |

## Resolution rules

* Names are matched case-insensitively; namespaces are lowercased internally.
* For a token like `%economy_balance%`, the resolver first tries an exact namespace split (`economy` + `balance`), then falls back to treating the whole body as a bare unnamespaced name.
* Anything after a `:` inside the token is passed to the resolver as its `args` string (e.g. `%myplugin_top:3%` → `"3"`).
* **Unknown placeholders are left verbatim**, so other systems — or PlaceholderAPI itself, if installed — can still handle them.
* If a resolver throws, the token resolves to an empty string instead of breaking the render.

<Warning>
  Resolvers run synchronously wherever you call `render`/`apply`. Keep them cheap — don't perform database queries or HTTP calls inside a resolver; precompute the value and read it from memory.
</Warning>
