Files
Iris/docs/09 - PlaceholderAPI.md
T
Brian Neumann-Fopiano ebfe278b3b Docvks
2026-08-10 15:47:26 -04:00

181 lines
9.8 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; related runtime and integration details are in `07 - Pregeneration.md` and `28 - Integrations.md`.
## Tutorial: verify a placeholder before using it in another plugin
Prerequisites: Bukkit-family Iris, PlaceholderAPI installed before Iris enables, a full server restart, and a player in a loaded Iris world.
1. Confirm registration: `/papi info iris`. The output must list expansion id `iris` and its published paths.
2. Confirm the service: `/papi parse me %iris_available%`. Expect `true` while Iris terrain service is live.
3. Confirm player context: `/papi parse me %iris_world.available%`. Expect `true` while the named player is in an Iris world.
4. Parse a concrete terrain value: `/papi parse me %iris_world.biome-key%`. Expect a load key such as `desert/hot-dunes`, not `---`.
5. While standing in the Iris world, run `/iris pregen start 352 center=0,0 gui=false`, then parse `/papi parse me %iris_pregen.percent%`. Expect a numeric value from `0.00` through `100.00` with no percent sign.
6. Stop with `/iris pregen stop` or let the job finish, then run `/papi parse me %iris_pregen.available%`. Expect `false`; other pregen values return `---` after the snapshot clears.
7. Copy the exact verified placeholder into the scoreboard, chat, or HUD plugin and test that consumer once more.
The workflow passes when registration, player-scoped terrain, and global pregen values each produce their documented value shape. Do not debug formatting in the consuming plugin until direct `/papi parse` succeeds.
### Recovery
| Symptom | Meaning | Recovery |
|---|---|---|
| `/papi info iris` has no expansion | PlaceholderAPI was unavailable when Iris scheduled registration | Perform a full restart with both plugins installed; `/papi reload` alone does not trigger Iris registration |
| Placeholder remains literal | Path is unknown or uses a removed pre-2.0 name | Copy an exact path from `/papi info iris` or the full table below |
| World value is `---` | No online player context, player is outside Iris, or terrain service has no reading | Parse as a named online player after entering a loaded Iris world |
| `world.available` is true but biome lags movement | Player view cache is within its one-second TTL | Wait one second or trigger an immediate publish by teleport/world change before diagnosing the consumer |
| Pregen value is `---` | No global job snapshot is active | Start a job and wait for its first event; use `pregen.available` as the guard in board templates |
| Scoreboard adds `%` twice | `pregen.percent` deliberately omits the suffix | Add one literal `%` in the consumer format, not in the placeholder |
## 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.