Appearance
玩家身份与状态
文档版本:基于 2026-06-01 代码分析
概述
玩家身份与状态系统管理逃走中MOD所有玩家的角色身份(Runner/Hunter/NPC)和运行时状态(淘汰/弃权/叛节者/可抓捕等)。通过 RfmPlayerIdentityManager 统一管理身份变更,PlayerStateStore 独立存储运行时状态,与权限系统完全解耦。
核心概念
三方角色
| 角色 | 枚举值 | 含义 |
|---|---|---|
| RUNNER | runner | 逃走者——躲避猎人追捕,赚取逃走币 |
| HUNTER | hunter | 猎人——追捕逃走者,获得淘汰奖励 |
| NPC | npc | 工作人员——不参与抓捕,不可被抓 |
逃走者子状态(RunnerState)
| 字段 | 默认值 | 含义 |
|---|---|---|
eliminated | false | 已被淘汰出局 |
abstained | false | 已弃权 |
traitor | false | 叛节者 |
traitorCaptureUnlocked | false | 叛节者可被猎人抓捕 |
互斥规则:
eliminated和abstained互斥(设为 true 时自动将另一方设为 false)traitorCaptureUnlocked仅在traitor == true时生效
猎人状态(HunterState)
| 字段 | 默认值 | 含义 |
|---|---|---|
catchable | true | 是否可被抓(被逃走者反击) |
type | NORMAL | 猎人类型:NORMAL 或 INVISIBLE(潜行时隐身) |
架构设计
身份与状态分离
mermaid
graph TB
subgraph Identity["PersistentRfmPlayerIdentitySavedData<br/>身份持久化 (nfa_rfm/rfm_player_identity.dat)"]
ID_RECORD["PlayerIdentityRecord<br/>uuid · originalName · nickname · role"]
end
subgraph State["PlayerStateStore<br/>状态持久化 (nfa_rfm/rfm_player_state.dat)"]
RUNNER["RunnerState<br/>eliminated · abstained<br/>traitor · traitorCaptureUnlocked"]
HUNTER["HunterState<br/>catchable · type"]
CAPTURE["canCapture · canBeCaptured"]
end
subgraph Manager["RfmPlayerIdentityManager<br/>身份管理核心门面"]
SET_ROLE["setRole()"]
GET_DISPLAY["getDisplayName()"]
SYNC["syncAndApply()"]
end
Identity --> Manager
State --> Manager
Manager --> SET_ROLE
Manager --> GET_DISPLAY
Manager --> SYNC角色切换时的状态重置
mermaid
flowchart TD
A[setRole server, uuid, originalName, role] --> B{角色是否变化?}
B -->|否| C[跳过状态重置]
B -->|是| D[onRoleChanged]
D --> E[重置 RunnerState<br/>全部字段=false]
D --> F[重置 HunterState<br/>catchable=true]
D --> G{新角色?}
G -->|HUNTER| H[canCapture=true<br/>canBeCaptured=false]
G -->|RUNNER| I[canCapture=false<br/>canBeCaptured=true]
G -->|NPC| J[canCapture=false<br/>canBeCaptured=false]
H --> K[syncAndApply<br/>更新Scoreboard队伍<br/>刷新通知目标]
I --> K
J --> K关键文件
| 文件 | 说明 |
|---|---|
com/chenxi/chenxi_rfm/common/rfm/player/RfmPlayerRole.java | 角色枚举(RUNNER/HUNTER/NPC) |
com/chenxi/chenxi_rfm/server/rfm/RfmPlayerIdentityManager.java | 身份管理核心门面 |
com/chenxi/chenxi_rfm/server/config/rfm/PersistentRfmPlayerIdentitySavedData.java | 身份持久化(PlayerIdentityRecord) |
com/chenxi/chenxi_rfm/server/config/rfm/PlayerStateStore.java | 状态持久化(RunnerState + HunterState) |
com/chenxi/chenxi_rfm/server/rfm/RfmPlayerTeamService.java | Scoreboard 队伍映射 |
com/chenxi/chenxi_rfm/server/rfm/RfmHunterTypeService.java | 猎人隐身类型特效 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmIdentityCommand.java | /nfa rfm identity 指令 |
关键流程
显示名解析
getDisplayName(record):
if nickname 非空 → 返回 nickname
else → 返回 originalName(Minecraft 原始用户名)昵称最大 16 个字符宽度(ASCII 计 1,非 ASCII 计 2),过滤控制字符。
Scoreboard 队伍映射
| 角色+状态 | 队伍名 | 前缀 | 颜色 |
|---|---|---|---|
| RUNNER + 存活 | nfa_runner_alive | [逃走者] | 绿色 |
| RUNNER + eliminated | nfa_runner_out | [出局] | 灰色 |
| RUNNER + abstained | nfa_runner_abstain | [弃权] | 黄色 |
| HUNTER | nfa_hunter | [猎人] | 黑色 |
| NPC | nfa_npc | [工作人员] | 金色 |
旧版数据迁移
在 ServerStartedEvent 时触发 migrateLegacyDataIfNeeded():
旧版 identity.dat (NBT)
├── 状态字段迁移 → PlayerStateStore (rfm_player_state.dat)
│ RunnerEliminated → RunnerState.eliminated
│ RunnerAbstained → RunnerState.abstained
│ Traitor → RunnerState.traitor
│ TraitorCaptureUnlocked → RunnerState.traitorCaptureUnlocked
│ HunterCatchable → HunterState.catchable
│
└── 权限字段迁移 → PersistentNfaPermissionSavedData
Permission(true) → GAME_ADMIN
Permission(false) → 不存储(默认PLAYER)身份变更后的同步链
setRole()→onRoleChanged()→ 重置所有状态syncAndApply()→RfmPlayerTeamService.applyPlayerTeam()+refreshAllPlayers()+RfmNoticeManager.syncNoticeTargetsToManagers()- 指令末尾:
RfmGameSessionManager.syncToAll()+RfmHudSyncManager.syncToAll()
数据持久化
PlayerIdentityRecord
json
{
"Players": [
{
"Uuid": "550e8400-...",
"OriginalName": "Steve",
"Nickname": "逃跑者A",
"Role": "runner"
}
]
}PlayerStateStore
json
{
"RunnerStates": [
{ "Uuid": "...", "Eliminated": false, "Abstained": false, "Traitor": false, "TraitorCaptureUnlocked": false }
],
"HunterStates": [
{ "Uuid": "...", "Catchable": true, "Type": "NORMAL" }
],
"CanCapture": [{ "Uuid": "...", "Value": true }],
"CanBeCaptured": [{ "Uuid": "...", "Value": true }]
}注意事项 / 限制
- 身份与权限解耦:
setRole()不会自动变更权限等级。权限由独立的NfaPermissionService管理。 - 旧版数据迁移幂等:迁移通过检测旧 NBT 字段存在性判断是否已迁移,不会重复执行。
- 显示名唯一性不强制:系统不强制昵称唯一,建议管理员通过中控面板手动检查。
- 团队切换即生效:
applyPlayerTeam()调用后玩家在 TAB 列表的名称颜色和前缀立即更新。 - role 默认值为 null:未设置身份的玩家
role为null,系统将其等同于 RUNNER 处理。