11 KiB
Iris placeholders
Iris registers the iris PlaceholderAPI expansion when PlaceholderAPI is enabled. It publishes
sixteen keys: seven in the world family, describing the generator around the reading player, and
nine in the pregeneration family, describing the server's running pregeneration job.
This is an operator-facing contract, not a compile surface. Nothing here needs a dependency, a
softdepend, or a line of Java. If you are writing a plugin rather than a scoreboard, the same data
is available with more precision through the terrain API and the
pregeneration events.
The pre-2.0 underscore keys are gone. There is no alias and no dual-accept window. If you are upgrading an existing board, go straight to the migration table.
The value grammar
Every key follows the same rules, so a board never has to special-case Iris:
- Paths are dot-separated and lowercase and never contain an underscore. Iris lowercases the
path before resolving it, so
%iris_WORLD.BIOME%works, but write it lowercase. - Values are plain text: no colour codes, no unit suffixes, no
%character,.as the decimal separator, no thousands grouping. - Any section sign or
%character that appears inside a pack-authored name — a biome display name, a world name — is stripped before you see it, so a pack cannot inject formatting or a nested placeholder into your board.
There are exactly three possible answers:
| Answer | When | What PlaceholderAPI shows |
|---|---|---|
| A value | The key is known and has data | The value |
--- |
The key is known and has no data right now | --- |
| Nothing | The key is not one Iris publishes | The literal %iris_...% |
The third row is deliberate. A typo stays visible on the board instead of quietly rendering as blank, which is why there is no catch-all fallback.
A real zero is 0, never ---. --- means "no reading", not "zero".
World keys
| Placeholder | Value |
|---|---|
%iris_available% |
true when the Iris terrain service is live, false otherwise |
%iris_world.available% |
true when the reading player is in an Iris world and a reading exists |
%iris_world.biome% |
Surface biome display name at the player, for example Hot Desert Dunes |
%iris_world.biome-key% |
Surface biome load key, for example desert/hot-dunes |
%iris_world.region% |
Region display name at the player |
%iris_world.region-key% |
Region load key |
%iris_world.dimension% |
Dimension (pack) load key of the player's world |
%iris_available% is the only world-family key that does not need a player. It answers for the
console and for an offline player.
Every other world.* key needs a tracked online player. For the console, an offline player, or a
player Iris has no position for yet, world.available is false and the rest are ---.
They are surface readings, and they are cached
world.biome, world.biome-key, world.region and world.region-key describe the surface at
the player's block column — the biome and region the generator places at ground level. A player
standing in a cave under an overhang reads the biome of the sky above them, not the cave they are
in. That is what a board reader means by "what biome am I in".
The reading is rebuilt at most once per second per player, and only when something actually reads one of these keys. Consequences:
- A whole board of
world.*keys costs one rebuild per player per second, however many of them are on it. - A value can lag a sprinting player by up to a second.
- A board that nobody is looking at costs nothing.
Position tracking has two speeds. Walking republishes a player's column at most once per second, and
not at all while they stand still. Anything that is not walking — joining, respawning, changing
world, stepping through a portal, or any teleport including /iris goto, /tp, an ender pearl
and a random teleport — publishes immediately. A player who arrives somewhere and stops moving
therefore never keeps showing the biome of where they came from.
Pregeneration keys
| Placeholder | Value |
|---|---|
%iris_pregen.available% |
true while a pregeneration job is running |
%iris_pregen.world% |
World name the running job is pregenerating |
%iris_pregen.percent% |
Completion, 0.00 to 100.00, with no % character |
%iris_pregen.eta% |
Estimated seconds remaining, whole number |
%iris_pregen.eta-text% |
The same estimate as 45s, 2m 5s or 1h 30m |
%iris_pregen.chunks% |
Chunks generated so far |
%iris_pregen.total% |
Chunks in the job |
%iris_pregen.chunks-per-second% |
Current rate, two decimal places |
%iris_pregen.paused% |
true while the job is paused |
pregen.* is global, not per player. Iris runs one pregeneration job per server, so these keys
read the same for everyone, including the console. %iris_pregen.world% says which world it is.
The snapshot is published when the job reports progress, once per second, and cleared the moment the
job completes or is cancelled. After that every pregen.* key except pregen.available reads ---,
and pregen.available reads false. There is no lingering "last job" state to mistake for a running
one.
pregen.eta and pregen.eta-text are two renderings of the same estimate: use eta for arithmetic
and eta-text for display. Both read 0 and 0s respectively before the job has generated enough
chunks to estimate from.
Availability
The expansion is registered only if PlaceholderAPI is enabled when Iris starts. It sets persist(),
so it survives /papi reload without Iris restarting.
%iris_available% distinguishes "Iris is installed but its terrain service is not up" from "Iris is
not installed at all" — in the second case the expansion does not exist, no key resolves, and every
%iris_...% on the board renders literally. Gate a conditional board on %iris_available% if you
want it to disappear cleanly rather than show --- rows on a server where Iris is present but still
starting.
Iris never gates a placeholder on a permission. A placeholder has no permission context to check against — the player reading a scoreboard is not necessarily the player the value describes — so values that should not be public are not published at all. That is why there is no seed key.
Migration from the pre-2.0 keys
The old underscore keys are gone. There is no alias and no dual-accept window: an old key now renders literally, so it is visible rather than silently wrong. This table is complete — every key the old expansion published appears in it.
| Old key | New key | Why |
|---|---|---|
%iris_biome_name% |
%iris_world.biome% |
Renamed onto the dot grammar |
%iris_biome_id% |
%iris_world.biome-key% |
Renamed; id was always the load key |
%iris_region_name% |
%iris_world.region% |
Renamed onto the dot grammar |
%iris_region_id% |
%iris_world.region-key% |
Renamed; id was always the load key |
%iris_biome_file% |
removed | Rendered an absolute server path into player-visible text, and threw on packs with no backing file |
%iris_region_file% |
removed | Same as biome_file |
%iris_world_seed% |
removed | Handed the world seed to anyone who could read a scoreboard, and a placeholder has no permission context to gate on |
%iris_terrain_height% |
removed | Reported the generated height, before objects and player edits, so it disagreed with the block under the player's feet |
%iris_terrain_slope% |
removed | Three extra noise samples per read for an unformatted pack-authoring diagnostic |
%iris_world_mode% |
removed | Studio or Production; a studio world exists for seconds during authoring and is never on a live board |
%iris_world_speed% |
removed | Mutated engine rate-window state every time it was read. %iris_pregen.chunks-per-second% answers the same question from a snapshot |
There is one behaviour change inside the four renames, and it will be visible on a board that has
been in service for a while. The old keys sampled two blocks above the player's feet and asked
for the biome at that exact Y, which meant a player standing under an overhang or inside a cave read
the cave biome. %iris_world.biome% and %iris_world.biome-key% are always the surface biome for
the column. If your board is checked against a screenshot from before the rename, expect
underground readings to differ.
%iris_world.dimension% is new. It has no pre-2.0 equivalent.
The three removals worth a replacement plan:
world_seedhas no replacement and will not get one. A plugin that legitimately needs the seed can read it fromIrisWorldInfo.seed()through the terrain API, where there is a caller to hold responsible.terrain_heighthas no replacement. If you want the ground height for a coordinate, useIrisTerrainService#surfaceHeight, which is the same number with its limitations documented. If you want the block under the player, use the player's own Y.world_speedis replaced by%iris_pregen.chunks-per-second%for the pregeneration case, which is what it was almost always used for. There is no per-world live generation rate key.
Failure policy
| Situation | What Iris shows |
|---|---|
| An unknown path | Nothing. PlaceholderAPI re-emits the literal %iris_...% |
| A known path with no data | --- |
No player context, on a world.* key |
---, and world.available is false |
| Player is not in an Iris world | ---, and world.available is false |
| The terrain service is not registered | ---, world.available is false, %iris_available% is false |
| No pregeneration job running | ---, and pregen.available is false |
| A resolver throws | ---, and one warning is logged naming the exact placeholder |
A resolver that throws is logged once per distinct path, up to 64 distinct paths, so a broken
key cannot flood the console from a scoreboard that re-renders every tick. The value shown is always
--- — a failure never renders a stack trace, a class name, or an empty string.
Iris does not disable a placeholder after repeated failures. There is no fault limit and no
quarantine; a key that fails keeps being asked and keeps answering ---.
Key reference
The full published list, as PlaceholderAPI reports it under /papi info iris:
available
pregen.available
pregen.chunks
pregen.chunks-per-second
pregen.eta
pregen.eta-text
pregen.paused
pregen.percent
pregen.total
pregen.world
world.available
world.biome
world.biome-key
world.dimension
world.region
world.region-key
Prefix each with %iris_ and suffix with %.