Files
Iris/docs/09 - PlaceholderAPI.md
T
2026-08-08 00:29:48 -06:00

156 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 09 - PlaceholderAPI
Iris registers the `iris` PlaceholderAPI expansion on Bukkit-family servers when PlaceholderAPI is enabled at Iris enable time. It publishes sixteen keys: seven world-family readings for the player and nine global pregeneration keys. This is an operator board contract, not a Java API; plugins that need the same data with more precision use `90 - API - Getting Started.md`, `91 - API - Terrain.md`, and `92 - API - World Events.md`. PlaceholderAPI is not available on Fabric/Forge/NeoForge. See also `07 - Pregeneration.md` and `28 - Integrations.md`.
## Registration
| Item | Value |
|---|---|
| Expansion id | `iris` |
| Expansion version | `2.0.0` |
| Author string | `Volmit Software` |
| Required plugin | `Iris` |
| Soft-depend | `PlaceholderAPI` in `plugin.yml` |
| `persist()` | `true` — survives `/papi reload` without Iris restart |
Iris schedules setup after enable. If PlaceholderAPI is not enabled then, the expansion is not registered and there is no late `PluginEnableEvent` re-attempt. Soft-depend alone does not load PlaceholderAPI.
List published paths with `/papi info iris`.
## Value grammar
| Rule | Detail |
|---|---|
| Path form | Dot-separated, lowercase, no underscores. Iris lowercases the path before resolve, so `%iris_WORLD.BIOME%` works, but write lowercase |
| Plain text only | No color codes, no unit suffixes, no `%` in values, `.` as decimal separator, no thousands grouping |
| Pack name scrubbing | Section-sign sequences and `%` characters inside pack-authored names are stripped before return |
| Real zero | `0` (or `0.00` for two-decimal numbers), never `---` |
Three answers:
| Answer | When | Board shows |
|---|---|---|
| A value | Known key with data | The value |
| `---` | Known key with no data right now | `---` |
| Nothing (null to PAPI) | Unknown path | Literal `%iris_...%` |
Unknown paths stay visible on purpose. There is no catch-all blank fallback.
## Full key table
### World family
| 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 column (example: `Hot Desert Dunes`) |
| `%iris_world.biome-key%` | Surface biome load key (example: `desert/hot-dunes`) |
| `%iris_world.region%` | Region display name at the player column |
| `%iris_world.region-key%` | Region load key |
| `%iris_world.dimension%` | Dimension (pack) load key of the player's world |
`%iris_available%` does not need a player. Every other `world.*` key needs a tracked online player. Console, offline player, or untracked position: `world.available` is `false` and the rest are `---`.
### Pregeneration family
| 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``100.00`, no `%` character |
| `%iris_pregen.eta%` | Estimated seconds remaining, whole number |
| `%iris_pregen.eta-text%` | 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 (one job per server). Values match for every player and the console. Snapshot is published on pregen events (`STARTED`, `TICK`, `PAUSED`, `RESUMED`, `SAVING`) and cleared on `COMPLETED` or `CANCELLED`. After clear, `pregen.available` is `false` and other `pregen.*` keys are `---`. Before enough chunks exist for an ETA, `eta`/`eta-text` read `0` / `0s`.
### Paths as reported by `/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 `%`.
## Surface readings and cache
`world.biome`, `world.biome-key`, `world.region`, and `world.region-key` are **surface** column readings: the biome/region the generator places at ground level for that X/Z. A player in a cave under an overhang still reads the surface biome above, not the cave biome.
### Position tracking
| Event | Publish |
|---|---|
| Walking (`PlayerMoveEvent`) | At most once per second per player; skipped while the player stays in the same block column |
| Join, respawn, world change, portal, any teleport (including `/iris goto`, `/tp`, ender pearl, random TP) | Immediate |
Standing still never keeps a stale column from a previous place after an immediate publish. Quit releases the player's position and world view.
### View rebuild TTL
World views rebuild at most **once per second per player** (`VIEW_TTL_MS = 1000`), and only when something reads a `world.*` key that needs the view. Consequences:
- A board full of `world.*` keys costs one rebuild per player per second
- Values can lag a sprinting player by up to one second
- An unread board costs no terrain queries
### Pregen snapshot
Pregen values come from a single global snapshot updated by `IrisPregenerationEvent`, not per-player polling.
## Permissions
Iris never gates a placeholder on a permission. Values that should not be public (for example world seed) are not published.
## Failure policy
| Situation | Shown |
|---|---|
| Unknown path | Nothing (literal `%iris_...%`) |
| Known path, no data | `---` |
| No player context on `world.*` | `---` and `world.available` = `false` |
| Player not in an Iris world | `---` and `world.available` = `false` |
| Terrain service not registered | `---` / `world.available` = `false` / `%iris_available%` = `false` |
| No pregen job | `---` / `pregen.available` = `false` |
| Resolver throws | `---`; one warning per distinct path, max 64 distinct paths |
Failed keys are not quarantined; they keep answering `---`.
## Migration from pre-2.0 keys
Pre-2.0 underscore keys are gone. No alias and no dual-accept window. Old keys render literally.
| Old key | New key | Notes |
|---|---|---|
| `%iris_biome_name%` | `%iris_world.biome%` | Dot grammar |
| `%iris_biome_id%` | `%iris_world.biome-key%` | `id` was always the load key |
| `%iris_region_name%` | `%iris_world.region%` | Dot grammar |
| `%iris_region_id%` | `%iris_world.region-key%` | `id` was always the load key |
| `%iris_biome_file%` | removed | Exposed absolute server paths; threw without a backing file |
| `%iris_region_file%` | removed | Same as `biome_file` |
| `%iris_world_seed%` | removed | No permission context on scoreboards; use terrain API `IrisWorldInfo.seed()` when a plugin needs seed |
| `%iris_terrain_height%` | removed | Generated height before objects/edits; disagreed with the block underfoot |
| `%iris_terrain_slope%` | removed | Expensive pack-authoring diagnostic |
| `%iris_world_mode%` | removed | Studio vs production is not a live-board concern |
| `%iris_world_speed%` | removed | Mutated engine rate-window state on read; use `%iris_pregen.chunks-per-second%` for pregen rate |
Behavior change inside the renames: old keys sampled two blocks above the player's feet (cave/overhang Y). New keys are always surface for the column. `%iris_world.dimension%` is new and has no pre-2.0 equivalent.