# Placeholders

PlaceholderAPI on Paper and Text Placeholder API on Fabric/NeoForge/Forge, for voice state in tab lists, scoreboards and chat

Source: https://socketsvc.vercel.app/zh-TW/server/placeholders
SocketSVC publishes its live voice state as placeholders, so other plugins and mods can put "who is talking" into a tab list, scoreboard, chat format or hologram without touching the voice API.

The same values are served on every platform — only the syntax differs.

| Server side | Framework | Syntax |
| --- | --- | --- |
| Paper plugin | [PlaceholderAPI](https://www.spigotmc.org/resources/placeholderapi.6245/) | `%socketsvc_<identifier>%` |
| Fabric/Quilt/NeoForge/Forge server mod | [Text Placeholder API](https://modrinth.com/mod/placeholder-api) | `%socketsvc:<identifier>%` |

Both are optional. If the placeholder framework is not installed, SocketSVC logs nothing beyond a debug line and voice chat is unaffected — there is no config to enable.

<Callout title="No expansion to download">
The Paper expansion ships **inside** the SocketSVC jar and registers itself on startup. Do not run `/papi ecloud download SocketSVC` — there is no eCloud expansion, and you do not need one. `/papi list` should show `SocketSVC` after a restart.
</Callout>

## Identifiers

### Server-wide

These answer the same for everyone and work in console or player-less renders.

| Identifier | Example | Meaning |
| --- | --- | --- |
| `version` | `0.2.0` | SocketSVC server version |
| `running` | `true` | Whether the voice listener is accepting connections |
| `port` | `24455` | Configured listener port |
| `url` | `ws://voice.example.com:24455` | Public WebSocket URL handed to clients |
| `connected_count` | `12` | Players currently connected to voice chat |
| `speaking_count` | `3` | Players whose microphone relayed audio in the last 400 ms |
| `deafened_count` | `1` | Players who have deafened themselves |
| `hearing_range` | `48` | `hearing-range`, or `unlimited` |
| `fade_start_distance` | `33.6` | `fade-start-distance` |
| `spectator_interaction` | `false` | The matching [config field](/server/configuration#fields) |
| `visible_players_only` | `false` | The matching [config field](/server/configuration#fields) |
| `soundboard_enabled` | `true` | The matching [config field](/server/configuration#fields) |

### About the viewing player

These are answered about whoever the placeholder is being rendered for.

| Identifier | Example | Meaning |
| --- | --- | --- |
| `connected` | `true` | Has an open voice connection |
| `speaking` | `false` | Microphone relayed audio in the last 400 ms |
| `deafened` | `false` | Has deafened themselves |
| `spatial` | `true` | Client negotiated spatial audio (see [Spatial audio API](/server/spatial-audio-api)) |
| `can_speak` | `true` | Holds `socketsvc.speak` |
| `can_listen` | `true` | Holds `socketsvc.listen` |
| `can_soundboard` | `true` | Holds `socketsvc.soundboard` |
| `status` | `speaking` | One of `disconnected`, `deafened`, `speaking`, `listening` |

`status` is the one identifier written to be shown as-is. It is ranked, not combined: a deafened player reads `deafened` even while their microphone is open, because they are not hearing anyone.

### About another player

These take a second player's name and answer **from the viewer's point of view**.

| Identifier | Example | Meaning |
| --- | --- | --- |
| `can_hear_<name>` | `true` | Whether the viewer can currently hear that player |
| `volume_<name>` | `74` | How loudly, 0–100, after distance falloff |
| `distance_<name>` | `12.5` | Blocks between them, blank in different worlds |

Text Placeholder API passes the name as an argument after a space instead: `%socketsvc:can_hear Alex%`. Both forms are accepted on both platforms.

These are **directional**. `can_hear_Alex` rendered for Steve asks whether Steve hears Alex, which is not the same question as the reverse once one side has deafened themselves, lost `socketsvc.listen`, or is hidden by [`visible-players-only`](/server/configuration#visible-players-only). Names are matched exactly and case-sensitively against online players; an unknown name resolves to an empty string.

The answer applies every rule the relay itself applies — range falloff, world, visibility, spectator mode, deafen and both players' permissions — because it is computed by the same code that decides what to route. A `can_hear` of `true` means audio is genuinely flowing right now.

## Values

Values are machine-readable rather than pretty, because placeholder output is usually fed into another plugin's comparison or formatting:

- Booleans are `true` / `false`, never `Yes` / `No` or a coloured symbol.
- Numbers carry no units, separators or trailing `.0`.
- An unknown identifier is left untouched rather than blanked, so a typo is visible instead of silently producing an empty column.
- A player-scoped identifier rendered with no player (console, or a template compiled once at startup) resolves to an empty string rather than an error.

Turn them into something readable with whatever conditional or replacement feature your tab-list, scoreboard or chat plugin already provides — most compare a placeholder's value against a string and substitute text for it. `%socketsvc_status%` is usually the one to branch on, since its four values map directly onto four icons.

## Examples

<Tabs items={['Paper', 'Fabric/Quilt/NeoForge/Forge']}>
  <Tab value="Paper">
```yaml
# Scoreboard line
"&7Voice: &f%socketsvc_connected_count% &7online, &f%socketsvc_speaking_count% &7talking"

# Chat format prefix
"%socketsvc_status% &8| &f%player_name%&7: "

# Only render a proximity marker when they can actually hear you
"%socketsvc_can_hear_Alex%"
```
  </Tab>
  <Tab value="Fabric/Quilt/NeoForge/Forge">
```
Voice: %socketsvc:connected_count% online, %socketsvc:speaking_count% talking
Status: %socketsvc:status%
Can hear Alex: %socketsvc:can_hear Alex%
Distance: %socketsvc:distance Alex% blocks
```
  </Tab>
</Tabs>

## Notes and limits

- **Mute is not a placeholder.** SocketSVC enforces microphone mute entirely on the client, which never tells the server about it, so the server genuinely does not know. `speaking` is the honest equivalent: a muted player simply never relays a frame. Deafen *is* known, because it changes what the server routes.
- **`speaking` is a 400 ms window** after the last relayed frame, matching the client's own speaking indicator so a nameplate and a tab-list column never disagree. It stays `false` for a player whose frames are being dropped for lack of `socketsvc.speak`.
- **Reload-safe.** `/ssvc-server reload` swaps the listener underneath; placeholders follow it without re-registration and never need a server restart.
- **Text Placeholder API is bound reflectively**, because it is published per Minecraft version and its releases do not cover SocketSVC's whole [supported matrix](/client/development/versions). One code path therefore serves every version and loader, and simply does nothing when the mod is absent. If a future release changes the API shape, SocketSVC logs a warning and carries on rather than failing to start.
- **For mods without a placeholder framework**, `SocketSvcModApi.placeholder(viewer, identifier)` returns the same values directly. See [the mod API](/api). The Paper plugin exposes the resolver the same way through `SocketSvcPlugin#placeholderSource()`.