# 生存战争联机版语音通话模组 **Repository Path**: EmpyrealSynergyStudio/SCnetVoiceChat ## Basic Information - **Project Name**: 生存战争联机版语音通话模组 - **Description**: 生存战争联机版语音通话功能 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-06 - **Last Updated**: 2026-10-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 生存战争联机版 x26.10.1 语音聊天模组(VoiceChat) - **包名**:`com.scnet.voicechat` **版本**:1.0.0 **目标版本**:x26.10.1(客户端/服务端均为 SCNET 2.4.0 / Survivalcraft.dll 2.4.0.0) - **产物**:`VoiceChat.netmod`(单 dll 双端部署:同一文件放客户端 NetMods/ 与服务端 NetMods/) ## 功能 1. **按住说话(PTT)**:默认按住 `V` 键说话,松开停止。 2. **语音转发(服务端)**:客户端采集 → ADPCM 编码 → 语音包(ID=200) → 服务端后台线程转发给其他所有在线玩家(排除说话人本人)。 3. **举报功能**:默认按 `J` 键打开举报面板 → 数字键选择目标玩家 → 数字键选择原因(1 语音辱骂 / 2 语音骚扰噪音 / 3 作弊外挂 / 4 其他)→ 自动发送举报包(ID=201);服务端后台线程写入 `app:/Reports/举报记录_YYYY-MM-DD.txt`。 4. **说话人指示**:客户端屏幕左上角实时显示"谁正在说话"(1.2 秒内活跃者)。 5. **设置持久化**:PTT 键 / 举报键 / 语音开关通过 `SaveSettings/LoadSettings` 保存。 ## 架构(核心要求:服务端转发不占用游戏线程) ``` 客户端采集线程 ──> ADPCM 编码(20ms/包,160B) ──> VoicePackage(200) ──> CommonLib.Net.QueuePackage │ NetNode.QueuePackage 仅入队 ConcurrentQueue(O(1),游戏线程零负载) ▼ 服务端 NetNode 收包 → VoicePackage.Handle(isServer=true) → VoiceRelayService.Enqueue(仅入队,立即返回) │ VoiceRelayService 独立后台线程(IsBackground)消费队列 → 构造转发副本(保留 SenderNickname/Seq/Audio,Except=From) → CommonLib.Net.QueuePackage(NetNode 自带 SendFlushWorker 后台发送通道) ▼ 其他客户端 NetNode 收包 → VoicePackage.Handle(isServer=false) → VoicePlayback → SoundBuffer + Sound.Play()(Engine.Audio 经 AudioThread.Enqueue,任意线程安全) ``` - **三级异步解耦**:收包线程只入队 → 独立转发线程消费 → NetNode 自带的 `SendFlushWorkerLoop`(`Task.Run` + unbounded Channel)做序列化/压缩/发送。**游戏主线程全程只做一次队列入队**。 - 举报同样:`ReportPackage.Handle(isServer=true)` 仅入队 → `ReportStore` 后台线程写盘(磁盘 IO 不在游戏线程)。 ## 数据包(动态 ID 预留段 200~249) | ID | 包 | 方向 | 内容 | |---|---|---|---| | 200 | VoicePackage | 客户端→服务端→其他客户端 | SenderNickname / Seq / ADPCM 音频(160B) | | 201 | ReportPackage | 客户端→服务端 | ReporterNickname / TargetNickname / ReasonCode / Detail | - 均实现 `Game.NetWork.IPackage`,`MinNeedState => ClientState.Connected`,注册于 `__ModInitialize`(`PackageManager.RegisterDynamicPackage`)。 ## 语音参数 - 采样:16 kHz / 16 bit / 单声道(`ALC_EXT_CAPTURE`,win 用 `openal32.dll`,linux 用 `libopenal.so.1`) - 编码:IMA ADPCM 4-bit(每包独立状态,无跨包依赖 → 丢包不影响后续),20ms/包 = 320 samples = 160B - 带宽:单人说话 ≈ 8 KB/s 上行(原始 PCM 为 32 KB/s,压缩 4:1) - 播放:`SoundBuffer(short[] PCM, 0, len, 1, 16000)` + `Sound.Play()`,队列上限 25 包(≈500ms),每帧最多播放 3 包,防延迟累积 ## 部署 ```bash # 1) 打包(已产出 VoiceChat.netmod;重新打包): bash ../scripts/pack_netmod.sh . VoiceChat # 需要 out/VoiceChat.dll 存在 # 2) 双端放置同一个文件: cp VoiceChat.netmod "<客户端目录>/NetMods/" cp VoiceChat.netmod "<服务端目录>/NetMods/" ``` - **必须双端同放**:联机版要求客户端/服务端模组清单一致(数量+PackageName+Version),否则客户端会自动卸载。 - 服务端进程同样执行 `ModLoader.__ModInitialize`(已用元数据校验双端所有引用类型均存在),服务端无 GUI,客户端专用钩子(GuiUpdate/GuiDraw/UpdateInput)在服务端不会触发,无副作用。 - 服务端控制台出现 `[VoiceChat] 服务端语音转发线程已激活` 表示转发服务就绪。 ## 键位 | 键 | 功能 | |---|---| | V(按住) | 说话 | | J | 打开/关闭举报面板 | | 数字键 1-9 | 选择举报目标 | | 数字键 1-4 | 选择举报原因 | | Esc | 取消举报 | ## 源码结构 ``` src/ ├── VoiceChatModLoader.cs # 主加载器(双端合一:注册包/钩子、PTT、举报 UI、HUD) ├── VoicePackage.cs # 语音数据包 (ID=200) ├── ReportPackage.cs # 举报数据包 (ID=201) ├── VoiceCapture.cs # ALC_EXT_CAPTURE 采集(后台线程 + P/Invoke 双平台) ├── VoicePlayback.cs # 播放队列 + 说话人指示 ├── AdpcmCodec.cs # IMA ADPCM 4bit 编解码(每包独立状态) ├── VoiceRelayService.cs # 服务端转发(独立后台线程,不占游戏线程) └── ReportStore.cs # 举报后台写盘(app:/Reports/) ``` ## 编译 - SDK:.NET 10.0.400(本机);游戏为 self-contained 发布(运行时 net11.0-rc)。 - 方案:`DisableImplicitFrameworkReferences` + 直接引用游戏自带全套 runtime 程序集(`/Users/mac/scmod/lib101/runtime11/`,从客户端安装包解出的 254 个 dll),因此无需 .NET 11 SDK 也能对 net11 程序集编译,且编译产物与游戏运行环境 100% 匹配。 - `dotnet build -c Release -o out` 后 `out/` 只有 `VoiceChat.dll`,其余为游戏自带引用,不打包。 ## 已知限制 / 未验证项 - **未实机联机测试**:已通过 ① 编译 0 错 ② 双端引用完整性(所有 Game/Engine 类型引用在客户端集与服务端集均可解析)③ 打包校验;未在真实客户端/服务端进程内运行(本机无 net11 运行时与游戏本体运行环境)。 - 麦克风采集依赖 OpenAL `ALC_EXT_CAPTURE`,若用户系统 OpenAL 驱动不含该扩展,采集将不可用(会打日志告警),播放不受影响。 - 安卓端:客户端包为 APK(非 dll 部署),本模组暂不支持安卓端采集(未内置 OpenSL/AAudio 采集实现),安卓端只可播放他人语音(若安卓能加载 netmod 且 OpenAL 提供 capture 扩展则可采集,未验证)。 - 举报记录为纯文本追加(`app:/Reports/`),无管理后台/自动处罚,需服主人工查看。 - 语音包未加密/未鉴权:任何客户端可构造语音包伪造他人昵称发言(服务端未校验 SenderNickname 与 From 的对应关系)。如需严格鉴权,可在服务端转发时用 `pkg.From.PlayerGuid` 关联昵称表覆写 SenderNickname(下个版本计划)。