Skip to content

Voice Forwarding

DNTOF edited this page Aug 28, 2026 · 1 revision

语音转发(SPY)

启用 voice_enabled 后,插件在独立端口(默认 8082)上提供语音 WebSocket,实时推送服务器内所有频道的语音(近距离、对讲机、Intercom 全局广播、SCP 频道、旁观者等),解码为 48kHz 单声道 float32 PCM。开发代号 SPY(v2.3 推出)。

端点

端点 说明
GET /ws?key=control_token(或 X-Control-Token 头) WebSocket 升级,实时语音流(需要 control_token 鉴权)
GET /status?key=control_token(或 X-Control-Token 头) JSON:当前正在说话的玩家(昵称/UserID/角色/频道,1.5s 无新包视为停止)

帧格式

  • 连接成功:文本帧 {"type":"hello","sampleRate":48000,"channels":1,"format":"float32"}
  • 说话者信息(新一轮讲话 / 频道或角色变化时推送):文本帧 {"type":"speaker","nickname":"...","userid":"...","playerid":n,"role":"...","channel":n}
  • 语音帧(二进制):[0]=0x01 [1]=channel [2-3]=playerId(LE) [4-7]=seq(LE) [8..]=float32 PCM,每包为 10ms Opus 帧(480 样本,约 100 包/秒)

实现要点

  • 基于 LabAPI PlayerEvents.SendingVoiceMessage 事件(挂在 VoiceTransceiver.ServerReceiveMessage 上,服务器收到的每个语音包都会触发;保留按内容哈希去重的纵深防御,避免重复解码破坏 Opus 解码器状态)
  • 解码器按说话者(netId)分开维护,解码异常时自动重建自愈;所有状态以 netId 为键,不用 ReferenceHub(玩家断开后 Hub 销毁会抛 NRE)
  • 主循环 MEC 协程内全程 try/catch 保护,任何异常都不会杀死转发管道
  • 主线程零阻塞保证:读侧仅在有数据时非阻塞收取;发送侧先经 Poll 可写检查(缓冲满跳帧,实时流允许丢帧),持续 3 秒不可写判死连接,外加 250ms 发送超时兜底——对端停止收数据绝不可能冻结服务器主线程
  • 连接层防护:监听连接上限 8(满载时新连接在 TCP 层直接被关闭,中继侧应对为退避重连,不会得到 WS 层错误码)、握手超时 10 秒、鉴权失败当场断开

Clone this wiki locally