# SocketSvcModApi

Spatial speaker API for Fabric/NeoForge/Forge server mods - the mod-side equivalent of the Paper plugin's SocketSvcApi, identifying callers by mod id instead of a Bukkit plugin.

Source: https://socketsvc.vercel.app/zh-TW/api/server-mod/SocketSvcModApi
<Callout title="Generated from source">
  Interface `com.socketsvc.api.SocketSvcModApi` — generated from [SocketSvcModApi.java](https://github.com/HippoDev03/SocketSVC/blob/main/server/common/src/main/java/com/socketsvc/api/SocketSvcModApi.java). Edit the Javadoc in the Java source, then run `pnpm gen:api`.
</Callout>

```java
package com.socketsvc.api;

public interface SocketSvcModApi { ... }
```

Spatial speaker API for Fabric/NeoForge/Forge server mods - the mod-side equivalent of the Paper
plugin's `SocketSvcApi`, identifying callers by mod id instead of a Bukkit plugin. The
channel, position and PCM types are shared with Paper, so addon logic ports unchanged.

Look it up lazily with `#get()`; it is null until the dedicated server has started and
SocketSVC's listener is up, and becomes null again when the server stops.

## Constants

| Constant | Description |
| --- | --- |
| `int API_VERSION = 4` | 2 added the voice group methods; 3 added the voice mute and client-presence methods; 4 added the microphone tap. Each addition is appended to the bottom of this interface and documented with the version that brought it. |

## Methods

### get

```java
static SocketSvcModApi get()
```

### register

```java
static void register(SocketSvcModApi api)
```

Called by SocketSVC itself; not for addons.

### createLocationalChannel

```java
LocationalAudioChannel createLocationalChannel(String ownerModId, SpatialPosition position, SpatialSettings settings)
```

### createLocationalChannel

```java
LocationalAudioChannel createLocationalChannel(String ownerModId, UUID id, String name, SpatialPosition position, SpatialSettings settings)
```

### supportsSpatialAudio

```java
boolean supportsSpatialAudio(UUID player)
```

Whether `player`'s client negotiated spatial audio.

### closeOwner

```java
void closeOwner(String ownerModId)
```

Closes every channel `ownerModId` created.

### placeholder

```java
default String placeholder(UUID viewer, String identifier)
```

Resolves a SocketSVC placeholder identifier - the same set served as
`%socketsvc:<identifier>%` through Text Placeholder API, documented under
"Placeholders" in the server docs.

For mods that want the values without a placeholder framework in between, or on a loader
where none is installed. Safe to call from any thread.

| Parameter | Description |
| --- | --- |
| `viewer` | the player to answer player-scoped identifiers about, or `null` |

**Returns:** the value, or `null` if the identifier is not one of SocketSVC's

### isVoiceGroupsEnabled

```java
default boolean isVoiceGroupsEnabled()
```

Whether the operator has switched voice groups on.

### voiceGroups

```java
default java.util.List<VoiceGroupInfo> voiceGroups()
```

Every live group, ordered by name. Empty while groups are disabled.

### voiceGroupOf

```java
default VoiceGroupInfo voiceGroupOf(UUID player)
```

The group `player` is in, or `null`.

### voiceGroup

```java
default VoiceGroupInfo voiceGroup(UUID groupId)
```

One group by id, or `null` if it has been disbanded.

### createVoiceGroup

```java
default UUID createVoiceGroup(String name, String password, VoiceGroupType type)
```

Creates a group with no members.

Created groups are persistent, because a group with nobody in it and no persistence
would be disbanded the instant it was made. The per-player policy switches
(`allow-player-created`, `allow-passwords`, `allow-persistent`) do not
apply here: they exist to restrain players, and an addon is the operator's own code.

| Parameter | Description |
| --- | --- |
| `password` | `null` for a group anyone may join |

**Returns:** the new group's id, or `null` if it was refused (name taken, invalid, or the server is already at `groups.max-groups`)

### addToVoiceGroup

```java
default boolean addToVoiceGroup(UUID player, UUID groupId)
```

Puts `player` in `groupId` without asking for its password, leaving whichever
group they were in. False if the group is gone or full.

### removeFromVoiceGroup

```java
default boolean removeFromVoiceGroup(UUID player)
```

Takes `player` out of whatever group they are in. False if they were in none.

### removeVoiceGroup

```java
default boolean removeVoiceGroup(UUID groupId)
```

Disbands a group, whoever is in it. False if it was already gone.

### setVoiceGroupType

```java
default boolean setVoiceGroupType(UUID groupId, VoiceGroupType type)
```

Changes a live group's type; audible immediately, on the next relayed frame.

### isVoiceMuted

```java
default boolean isVoiceMuted(UUID player)
```

Whether `player` may not talk right now, for any reason - an individual mute or the
server-wide one they are not exempt from. This is the question a chat or moderation addon
actually wants; `#voiceMute` answers the narrower one.

### voiceMute

```java
default VoiceMuteInfo voiceMute(UUID player)
```

The individual mute against `player`, or `null` - including when they are
silenced only by the server-wide mute.

### voiceMutes

```java
default java.util.List<VoiceMuteInfo> voiceMutes()
```

Every individual mute, longest-standing first.

### muteVoice

```java
default boolean muteVoice(UUID player, long durationMillis, String reason, String moderator)
```

Mutes `player`, replacing any mute already against them.

| Parameter | Description |
| --- | --- |
| `durationMillis` | how long it lasts, or `0` for a mute that stands until lifted |
| `reason` | shown to the muted player; `null` for none. Trimmed, capped at 200 characters, and refused if it carries control characters |
| `moderator` | recorded on the mute and shown in `/ssvc-server mutes`; `null` for an addon that would rather not say |

**Returns:** whether anything changed. False when the player already held exactly this mute, or when they hold `socketsvc.mute.exempt`

### unmuteVoice

```java
default boolean unmuteVoice(UUID player)
```

Lifts an individual mute. False if there was none. Does not touch the server-wide mute.

### isEveryoneVoiceMuted

```java
default boolean isEveryoneVoiceMuted()
```

Whether everybody without `socketsvc.mute.exempt` is currently silenced.

### setEveryoneVoiceMuted

```java
default boolean setEveryoneVoiceMuted(boolean muted)
```

Silences, or stops silencing, everybody without `socketsvc.mute.exempt`.

**Returns:** whether this changed anything

### voiceClientState

```java
default VoiceClientState voiceClientState(UUID player)
```

Whether `player` is running the SocketSVC client mod.

Answered from plugin-channel registration - the same question SocketSVC already asks
before it sends that player a handshake - and deliberately not from whether they have a live
voice session, which also depends on the voice port being reachable from where they are.

Three states, because for a moment after somebody joins the honest answer is that nobody
knows yet: see [`VoiceClientState`](/api/shared/VoiceClientState). An addon that acts on `ABSENT` should say what
it does about `UNKNOWN` rather than treating it as a no.

### hasVoiceClient

```java
default boolean hasVoiceClient(UUID player)
```

`#voiceClientState` collapsed to the one state most callers want. `false` for a
player nobody has heard from yet, so do not use this to decide whether to refuse somebody -
`voiceClientState` is the one that can tell "no" from "not yet".

### requiresVoiceClient

```java
default boolean requiresVoiceClient()
```

Whether this server refuses players without the client mod (`require-client.enabled`).
Off unless an operator switched it on.

### isMicrophoneTapEnabled

```java
default boolean isMicrophoneTapEnabled()
```

Whether the operator has switched microphone taps on (`microphone-tap.enabled`).
Check this once and hide your own feature rather than letting
`#subscribeMicrophone` throw.

### subscribeMicrophone

```java
default MicrophoneTapHandle subscribeMicrophone(String ownerModId, MicrophoneTap.Format format, MicrophoneTap tap)
```

Receives every microphone frame from every player who holds `socketsvc.bridge`, until
the returned handle is closed.

Audio arrives on the relay's network thread, 50 times a second per talking player. Do not
block in the callback and do not touch the server's world or entity state from it.

`closeOwner(ownerModId)` closes these too, and the server stopping closes all of
them, so a mod that taps for its whole lifetime can discard the handle.

| Parameter | Description |
| --- | --- |
| `ownerModId` | your mod id; named in the log line announcing the subscription |
| `format` | `MicrophoneTap.Format#OPUS` for the client's own frames, `MicrophoneTap.Format#PCM` to also get them decoded |

**Throws `IllegalStateException`:** when `microphone-tap.enabled` is false, the mod is unknown, or the server already has the maximum number of subscriptions. The message says which

### canTapMicrophone

```java
default boolean canTapMicrophone(UUID player)
```

Whether `player`'s microphone may currently reach a tap - the
`socketsvc.bridge` check the relay itself applies, so a plugin can tell a player
why it is not hearing them.

## Nested types

- `final class Registry`