Appearance
权限系统
文档版本:基于 2026-06-01 代码分析
概述
逃走中MOD的权限系统采用三层等级体系,通过 NfaPermissionService 统一提供权限查询与修改服务。核心特性包括主控互斥(同时最多一个主控(MASTER_ADMIN))、引导期机制(无主控时 OP(服务器管理员)玩家可设首任主控)和命令方块无条件最高权限。
权限系统与玩家身份系统完全解耦,独立持久化于 PersistentNfaPermissionSavedData。
核心概念
三层权限等级
| 等级 | ordinal | id 字符串 | 含义 |
|---|---|---|---|
| MASTER_ADMIN | 0(最高) | "master_admin" | 唯一主控管理员,可修改他人权限 |
| GAME_ADMIN | 1 | "game_admin" | 游戏管理员,可管理游戏流程 |
| PLAYER | 2(默认) | "player" | 普通玩家 |
权限比较规则:数值越小权限越高。例如主控(MASTER_ADMIN)(0) >= 游戏管理员(GAME_ADMIN)(1) >= 普通玩家(PLAYER)(2)。
引导期(Bootstrap Phase)
当服务器中没有 MASTER_ADMIN 时进入引导期:
- OP(服务器管理员级,权限等级 >= 2)的玩家临时获得
GAME_ADMIN权限 - OP(服务器管理员)玩家可通过
/nfa permission set设置首任主控 - 首任主控设置后引导期自动退出
主控互斥
设置新主控时旧主控自动降级为 GAME_ADMIN。降级当前主控时 masterAdminUuid 置 null,系统重新进入引导期。
架构设计
权限检查流程图
mermaid
flowchart TD
A[CommandSourceStack 来源] --> B{来源类型?}
B -->|命令方块/控制台| C[返回 MASTER_ADMIN<br/>无条件通过]
B -->|玩家| D{PersistentNfaPermissionSavedData<br/>getLevel uuid}
D -->|存储等级 != PLAYER| E[返回存储等级]
D -->|存储等级 == PLAYER| F{引导期?<br/>hasMasterAdmin == false?}
F -->|是 且 OP| G[返回 GAME_ADMIN<br/>临时权限]
F -->|否| H[返回 PLAYER]
E --> I{谓词检查<br/>isAtLeast 阈值?}
G --> I
H --> I
C --> I
I -->|true| J[指令可执行]
I -->|false| K[指令不可见/不可执行]权限设置守卫流程
mermaid
flowchart TD
A[canManagePermissions<br/>server, data, caller] --> B{caller 类型?}
B -->|null 系统调用| C[通过]
B -->|玩家| D{引导期?}
D -->|是 且 OP| E[通过<br/>允许设置首任主控]
D -->|否| F{caller == masterAdminUuid?}
F -->|是| G[通过<br/>当前主控可修改]
F -->|否| H[拒绝]关键文件
| 文件 | 说明 |
|---|---|
com/chenxi/chenxi_rfm/common/permission/NfaPermissionLevel.java | 三层权限等级枚举 |
com/chenxi/chenxi_rfm/common/permission/NfaPermissionApi.java | 对外稳定公开 API 层 |
com/chenxi/chenxi_rfm/server/permission/NfaPermissionService.java | 权限服务核心(全部静态方法) |
com/chenxi/chenxi_rfm/server/config/rfm/PersistentNfaPermissionSavedData.java | 权限持久化存储(NBT) |
com/chenxi/chenxi_rfm/server/command/NfaCommandPredicates.java | 7 种权限谓词定义 |
com/chenxi/chenxi_rfm/server/command/permission/PermissionSetCommand.java | /nfa permission set |
com/chenxi/chenxi_rfm/common/network/payload/permission/SyncPermissionLevelPayload.java | 权限同步网络包 |
com/chenxi/chenxi_rfm/common/network/handler/permission/PermissionPayloadHandler.java | 客户端权限状态更新 |
com/chenxi/chenxi_rfm/client/permission/NfaPermissionClientState.java | 客户端权限缓存(仅 UI 控制) |
关键流程
7 种权限谓词
| 谓词 | 最低权限 | 命令方块/控制台 | 典型应用 |
|---|---|---|---|
MASTER_OR_CB | MASTER_ADMIN | 通过 | /nfa basic, /nfa rfm coin |
MASTER_PLAYER_ONLY | MASTER_ADMIN | 拒绝 | /nfa rfm hub |
GAME_ADMIN_OR_CB | GAME_ADMIN | 通过 | /nfa reload, /nfa rfm box |
GAME_ADMIN_PLAYER_ONLY | GAME_ADMIN | 拒绝 | /nfa rfm manager, /nfa rfm identity |
PLAYER_ONLY | 任何玩家 | 拒绝 | /nfa phone hand_id |
ANYONE | 无 | 通过 | 未使用 |
PERMISSION_SET | 特殊 | 拒绝 | /nfa permission set |
PERMISSION_SET 的特殊逻辑:
- 主控玩家 → 通过
- 引导期 + OP → 通过
- 其他 → 拒绝
二级权限检查(二次守卫)
RfmIdentityCommand 和 RfmHunterTypeCommand 采用双层设计:
- 父级挂载
GAME_ADMIN_PLAYER_ONLY—— GAME_ADMIN 可查看 - 写操作内部检查
isAtLeast(MASTER_ADMIN)—— 仅主控可修改
权限变更同步
PermissionSetCommand.setPermission()
→ NfaPermissionService.setPermission()
→ PersistentNfaPermissionSavedData.setLevel()
→ setDirty()
→ PacketDistributor.sendToPlayer(target, SyncPermissionLevelPayload)
→ PermissionPayloadHandler.handleSyncPermissionLevel()
→ NfaPermissionClientState.update(level)客户端的 NfaPermissionClientState 仅用于 UI 显隐控制,所有实际操作在服务端二次验证。
数据持久化
PersistentNfaPermissionSavedData
文件:<world>/data/nfa_rfm/nfa_permission_data.dat
json
{
"MasterAdminUuid": "550e8400-...",
"Permissions": [
{ "Uuid": "660e8400-...", "Level": "game_admin" }
]
}存储策略:
- 仅存储非
PLAYER等级,减少数据体积 MasterAdminUuid为 null 表示无主控(引导期)setLevel()方法内置主控互斥逻辑
注意事项 / 限制
- 命令方块无条件最高权限:
resolveCommandSourceLevel()对非玩家源直接返回MASTER_ADMIN,数据包/命令方块不受任何权限限制。 - 客户端权限不可信:
NfaPermissionClientState仅用于 UI,所有权限判断在服务端执行。 - 主控互斥是硬性的:同一时间有且仅有一个主控。设新主控会自动降级旧主控。
- 引导期可逆:降级当前主控后
masterAdminUuid置 null,系统重新进入引导期。 - 外部 API 稳定:
NfaPermissionApi位于common包,可供扩展模组引用。 - PLAYER 不持久化:默认 PLAYER 等级不写入 NBT,加载时自动返回 PLAYER。