Appearance
电话核心
文档版本:基于 2026-06-01 代码分析
概述
电话核心系统为逃走中MOD提供完整的语音通话功能。玩家通过手机物品注册获取唯一号码,可拨打其他玩家进行语音通话。系统基于 Simple Voice Chat API 实现语音桥接,支持号码转发路由、通话超时控制和铃声调度。
核心概念
号码生成
号码是确定性的——以玩家 UUID 的 leastSigBits ^ mostSigBits 为种子,生成 XXX-XXX-XXX 格式(9位数字带连字符)。同一 UUID 始终生成相同号码。
通话状态机
mermaid
stateDiagram-v2
[*] --> outgoing_ringing : 主叫拨号
[*] --> incoming_ringing : 被叫收到来电
[*] --> dialing_failed_pending : 空号/无效号码
outgoing_ringing --> active : 被叫接听
incoming_ringing --> active : 被叫接听
outgoing_ringing --> ended : 超时/挂断
incoming_ringing --> ended : 超时/挂断
active --> ended : 挂断/超时
dialing_failed_pending --> failed : 暂挂期结束
ended --> [*]
failed --> [*]三个超时参数
| 参数 | 默认值 | 说明 |
|---|---|---|
call_wait_seconds | 30 | 响铃等待秒数 |
max_call_seconds | 60 | 最大通话秒数 |
message_cooldown_seconds | 5 | 消息冷却(见消息系统) |
架构设计
通话生命周期
mermaid
sequenceDiagram
participant Caller as 主叫方
participant Session as PhoneCallSessionManager
participant Sound as PhoneServerSoundScheduler
participant Voice as PhoneCallVoiceBridge
participant Callee as 被叫方
Caller->>Session: startCall(caller, targetNumber)
Session->>Session: 号码解析+查重忙
Session->>Sound: startIncomingRingLoop(callId, calleeUuid)
Session->>Callee: SyncPhoneCallStatePayload(ringing)
Callee->>Session: handleAction(acceptCall)
Session->>Sound: stopIncomingRingLoop
Session->>Voice: createAndJoinCallGroup
Voice->>Voice: 创建SimpleVoiceChat隐藏群组
Session->>Caller: SyncPhoneCallStatePayload(active)
Session->>Callee: SyncPhoneCallStatePayload(active)
Note over Caller,Callee: 通话中...[tick监听超时]
Caller->>Session: handleAction(endCall)
Session->>Voice: leaveAndRemoveGroup
Session->>Session: 记录通话历史
Session->>Caller: SyncPhoneCallStatePayload(ended)
Session->>Callee: SyncPhoneCallStatePayload(ended)关键文件
| 文件 | 说明 |
|---|---|
com/chenxi/chenxi_rfm/server/phone/PhoneCallSessionManager.java | 通话会话管理核心 |
com/chenxi/chenxi_rfm/server/phone/PhoneCallVoiceBridge.java | Simple Voice Chat 语音桥接 |
com/chenxi/chenxi_rfm/server/phone/PhoneServerSoundScheduler.java | 铃声调度器 |
com/chenxi/chenxi_rfm/server/phone/PhoneCallHistoryService.java | 通话记录服务 |
com/chenxi/chenxi_rfm/server/phone/PhoneCallNotifier.java | 通话状态推送 |
com/chenxi/chenxi_rfm/server/config/phone/PersistentPhoneRegistrySavedData.java | 手机注册表持久化 |
com/chenxi/chenxi_rfm/server/config/phone/PersistentPhoneCallHistorySavedData.java | 通话历史持久化 |
com/chenxi/chenxi_rfm/server/config/phone/PhoneCallRouteConfig.java | 号码转发路由表 |
com/chenxi/chenxi_rfm/server/voice/NfaVoicechatPlugin.java | SimpleVoiceChat 插件入口 |
com/chenxi/chenxi_rfm/server/voice/NfaVoicechatManager.java | VoicechatServerApi 持有器 |
关键流程
手机注册
- 玩家右键手机物品 → 打开
NFAPhoneMenu - 客户端检查
NFA_PHONE_ID组件 → 发送RegisterPhoneAppPayload - 服务端
registerPhone(phoneId, ownerUuid, ownerName):phoneId已存在且属于自己 →ALREADY_REGISTERED_BY_SELF- 已存在但属于他人 →
ALREADY_REGISTERED_BY_OTHER - 不存在 → 生成新号码并创建记录
拨号流程
- 格式化目标号码(去除非数字,补全连字符)
- 从注册表查找被叫人
- 若号码不在注册表 → 尝试
PhoneCallRouteConfig.findForwardTarget(server, normalizedTarget)号码转发 - 查重忙(
isBusy)→ 主叫或被叫已在通话中则拒绝 - 创建 Session,状态
outgoing_ringing,开始响铃
语音桥接
- 创建隐藏非持久化 Simple Voice Chat 群组
- 将主叫和被叫加入同一群组
- 挂断时解散群组
- 群组名格式为
c+ 时间戳后6位 base36 + 3位随机 base36(最长 10 字符)
重连支持
玩家重新登录时,onPlayerLoggedIn() 检查是否有活跃通话,有则重新推送当前状态包恢复通话 UI。
通话历史
每次通话结束写入两条记录:
- 主叫方向:
⬆ 呼叫+ "通话X秒" 或 "铃响X秒" - 被叫方向:
⬇ 来电+ 同上
数据持久化
PersistentPhoneRegistrySavedData
文件:<world>/data/nfa_rfm/phone/registry.dat
List<Record: phoneId, ownerUuid, ownerName, phoneNumber>PersistentPhoneCallHistorySavedData
文件:<world>/data/nfa_rfm/phone/call_history.dat
List<Record: ownerName, peerName, peerPhone, directionText, statusText, missed, timeMillis>铃声参数
- 音效:
chenxi_rfm:phone_ring - 音量:
25/16 ≈ 1.5625 - 分类:
SoundSource.RECORDS - 循环间隔:2 秒
注意事项
- 号码确定性:同一 UUID 永远生成同一号码,不可更改
- 多手机共享号码:一个玩家名下可注册多个手机,所有手机共享同一由 UUID 确定性生成的号码
- 语音依赖 Simple Voice Chat:若未安装该模组,语音桥接静默失败(通话仍可建立但无语音)
- 号码转发独立于注册:即使目标号码未注册,也可以通过路由表转发到注册号码
- tick 驱动一切:通话超时、铃声调度均依赖 ServerTick 事件