Skip to content

Mod Integration Channels

artriy edited this page Jul 17, 2026 · 6 revisions

Channels: Private & Team Audio

A channel lets players who resolve the same channel key hear one another outside the ordinary proximity route. API 1.1 supports flat radio audio, flat muffled audio, and task-phase spatial audio from an explicit world position.

← Back to Mod Integration


Register a channel resolver

Register a callback that returns one channel membership for the player being evaluated, or null when the player is not on a channel:

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
{
    byte team = MyRoles.TeamOf(ctx.Player);
    return team != byte.MaxValue
        ? new VoiceChannelResult($"team:{team}")
        : null;
});

VoiceRuleContext supplies the player, phase, local/dead flags, and access to this mod's host-synced options. The key must be non-empty. Perfect Comms namespaces it by modId, so only players whose resolver returns the same key for the same mod can share the route.

Return null whenever the rule does not apply. A non-null result with an empty key is ignored and can prevent a later resolver registered under the same mod id from being considered.


VoiceChannelResult

new VoiceChannelResult(
    Key,                                      // required, non-empty
    TwoWay: true,                             // keep true in API 1.1
    Shape: VoiceAudioShape.Radio,
    Volume: 1f,                               // clamped to 0..1
    Origin: null);

Perfect Comms accepts the first non-null, non-empty channel membership for a player. Within one mod id, callbacks are checked in registration order. Avoid depending on order between different mods; register one resolver per mod and decide its channel internally.

Audio shapes

Shape Current API 1.1 behavior
Radio Flat, unfiltered audio with no distance falloff. This is the default.
Muffle Flat audio through the listener-muffle low-pass filter.
Proximity Spatial falloff only when Origin is explicitly supplied and a task-phase listener position is available.

Volume is clamped to 0f..1f before routing.

Spatial channels

For working spatial audio, use Shape: VoiceAudioShape.Proximity and provide an explicit Origin:

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
    MyRoles.SeancePoint(ctx.Player) is Vector2 spirit
        ? new VoiceChannelResult(
            "seance",
            Shape: VoiceAudioShape.Proximity,
            Origin: spirit)
        : null);

During tasks, the target is heard from Origin with the host's distance/falloff settings. A Proximity result without Origin is currently flat audio, not speaker-body proximity. Meetings do not provide a spatial listener position, so Proximity channels also fall back to flat audio there.

One-way channels are not supported in API 1.1

Leave TwoWay at true. Although the public record contains TwoWay, setting it to false does not provide a reliable one-directional route in API 1.1. Do not build role behavior around TwoWay: false.


Working patterns

Faction radio:

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
    MyRoles.IsCultist(ctx.Player)
        ? new VoiceChannelResult("cult")
        : null);

Private pair:

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
{
    byte ownerId = MyRoles.LinkOwnerOf(ctx.Player);
    return ownerId != byte.MaxValue
        ? new VoiceChannelResult($"link:{ownerId}")
        : null;
});

Host-configurable channel:

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
    ctx.GetOption("CultRadio") && MyRoles.IsCultist(ctx.Player)
        ? new VoiceChannelResult("cult")
        : null);

Routing and failure behavior

  • A living speaker muted during Tasks/Meeting/Exile by a built-in rule or VoiceRuleResult.Mute(...) stays muted; a channel does not bypass that mute. API 1.1 gate mute is not enforced for dead speakers or Lobby voice.
  • Mod channels are checked in meetings and tasks before built-in team radio.
  • Channel state is evaluated locally from your mod's state. Perfect Comms does not synchronize role or membership state for you.
  • A throwing callback is treated as null for that evaluation.
  • Register once and call PerfectCommsApi.Unregister(modId) from your unload path. Duplicate registrations accumulate.

Next

Clone this wiki locally