Appearance
游戏会话管理
文档版本:基于 2026-06-01 代码分析
概述
游戏会话管理是逃走中MOD的核心调度器,负责管理游戏的完整生命周期。通过 RfmGameSessionManager 实现 IDLE → RUNNING → IDLE 的状态转换,并在 RUNNING 状态下管理五个子阶段(NORMAL / PAUSED / COUNTDOWN_FROZEN / TIME_REWIND / TIME_CHAOS)的切换。每次服务器 tick 驱动倒计时、逃走币产出和猎人生效检测。
核心概念
状态与阶段双维度
| 维度 | 枚举 | 值 | 含义 |
|---|---|---|---|
| State(状态) | RfmGameState | idle / running | 是否正在进行一局游戏 |
| Phase(阶段) | RfmGamePhase | normal / paused / countdown_frozen / time_rewind / time_chaos | 游戏进行中的子状态 |
游戏状态由 State 和 Phase 共同决定:
| State | Phase | 语义 | 倒计时 | 逃走币产出 | 玩家行动 |
|---|---|---|---|---|---|
idle | normal | 未进行 | 停止 | 无 | 限制 |
running | normal | 正常运行 | 递减 | 正常发放 | 允许 |
running | paused | 已暂停 | 冻结 | 停止 | 禁止 |
running | countdown_frozen | 倒计时冻结 | 冻结 | 停止 | 允许 |
running | time_rewind | 时光倒流 | 递增(倒流) | 按速率扣除 | 允许 |
running | time_chaos | 时光混乱 | 锁定 + 手机随机 | 锁定 | 允许 |
存活时间计算
游戏通过两个字段追踪时间流逝:
TotalSeconds:总倒计时秒数(默认 3600,范围 60-86400)ElapsedBeforeRunSeconds:暂停/冻结时锁定的已消耗秒数RunStartMillis:当前运行段开始的系统时间戳
已消耗秒数 = ElapsedBeforeRunSeconds + (当前毫秒 - RunStartMillis) / 1000
剩余秒数 = TotalSeconds - 已消耗秒数架构设计
状态机图
mermaid
stateDiagram-v2
[*] --> IDLE_NORMAL : 初始状态
IDLE_NORMAL --> RUNNING_NORMAL : startGame()
state RUNNING_NORMAL {
[*] --> Normal
Normal --> Paused : pauseGame()
Paused --> Normal : resumeGame()
Normal --> Frozen : startCountdownFreeze()
Frozen --> Normal : stopCountdownFreeze()
Normal --> Rewind : startTimeRewind()
Rewind --> Normal : stopTimeRewind()
Normal --> Chaos : startTimeChaos()
Chaos --> Normal : stopTimeChaos()
}
RUNNING_NORMAL --> IDLE_NORMAL : terminateGame() / 超时 / 团灭 / 单一胜利者
note right of IDLE_NORMAL : State=IDLE, Phase=NORMAL
note right of RUNNING_NORMAL : State=RUNNING
note right of Paused : 倒计时冻结<br/>玩家行动禁止<br/>逃走币停止发放
note right of Frozen : 倒计时冻结<br/>玩家行动允许<br/>猎人效果持续
note right of Rewind : 倒计时反向流动<br/>逃走币每秒扣除<br/>已淘汰者不受影响
note right of Chaos : 真实时间锁定<br/>逃走币锁定<br/>手机APP时间乱跳<br/>支持单一胜利者游戏生命周期时序
mermaid
sequenceDiagram
participant Admin as 管理员
participant Cmd as BasicCommand
participant GSM as RfmGameSessionManager
participant HS as RfmHunterSystemManager
participant Data as PersistentRfmGameSavedData
participant All as 全服玩家
Admin->>Cmd: /nfa basic start
Cmd->>GSM: startGame(server)
GSM->>Data: resetToIdle() 安全重置
GSM->>Data: snapshotOnlineRunners()
GSM->>Data: startRunning(now)
Data-->>GSM: State=RUNNING, Phase=NORMAL
GSM->>HS: onGameStarted() 重置状态
GSM->>All: syncToAll() + syncHud()
Note over GSM: tick() 循环开始
Admin->>Cmd: /nfa basic pause
Cmd->>GSM: pauseGame(server)
GSM->>HS: onGamePaused() 结算逃走币
GSM->>Data: pauseAtElapsed(elapsed)
Data-->>GSM: Phase=PAUSED
Admin->>Cmd: /nfa basic resume
Cmd->>GSM: resumeGame(server)
GSM->>Data: resumeCountdown(now)
Data-->>GSM: Phase=NORMAL
GSM->>HS: onGameResumed(now)
Note over GSM: 倒计时归零 → 自动终止
GSM->>HS: handleTimeExpired()
GSM->>GSM: executeGameEnd(server)
GSM->>Data: resetToIdle()
GSM->>All: syncToAll() + syncHud()关键文件
| 文件 | 说明 |
|---|---|
com/chenxi/chenxi_rfm/server/rfm/RfmGameSessionManager.java | 游戏会话管理核心类 |
com/chenxi/chenxi_rfm/common/rfm/RfmGameState.java | 游戏状态枚举(IDLE/RUNNING) |
com/chenxi/chenxi_rfm/common/rfm/RfmGamePhase.java | 游戏阶段枚举(NORMAL/PAUSED/COUNTDOWN_FROZEN/TIME_REWIND/TIME_CHAOS) |
com/chenxi/chenxi_rfm/common/rfm/RfmGameStateHelper.java | 状态/阶段辅助判断工具类 |
com/chenxi/chenxi_rfm/server/config/rfm/PersistentRfmGameSavedData.java | 游戏状态持久化(NBT) |
com/chenxi/chenxi_rfm/server/config/rfm/PersistentRfmRoundSavedData.java | 回合参与者 UUID 快照 |
com/chenxi/chenxi_rfm/server/config/rfm/PersistentRfmHunterRoundSavedData.java | 猎人对局状态(含混乱金币快照和单一胜利者) |
com/chenxi/chenxi_rfm/server/event/ServerRfmGameEvents.java | 服务端 tick 事件驱动 |
com/chenxi/chenxi_rfm/server/command/basic/BasicStartCommand.java | /nfa basic start |
com/chenxi/chenxi_rfm/server/command/basic/BasicPauseCommand.java | /nfa basic pause |
com/chenxi/chenxi_rfm/server/command/basic/BasicResumeCommand.java | /nfa basic resume |
com/chenxi/chenxi_rfm/server/command/basic/BasicStopCommand.java | /nfa basic stop |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundCountdownFreezeStartCommand.java | 手动冻结倒计时 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundCountdownFreezeStopCommand.java | 手动解冻倒计时 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundTimeRewindStartCommand.java | 开启时光倒流 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundTimeRewindStopCommand.java | 解除时光倒流 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundTimeChaosStartCommand.java | 开启时光混乱 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundTimeChaosStopCommand.java | 解除时光混乱 |
com/chenxi/chenxi_rfm/server/command/rfm/RfmRoundTimeChaosWinnerCommand.java | 设置混乱阶段单一胜利者 |
关键流程
startGame() 执行步骤
- 前置检查:
state != IDLE则拒绝 data.resetToIdle()— 冗余安全重置PersistentRfmRoundSavedData.snapshotOnlineRunners(server)— 快照在线逃走者 UUIDRfmAlarmManager.clearForGameLifecycle(server)— 清空警报RfmDetectBlockManager.resetForNewGame(server)— 重置侦测方块data.startRunning(System.currentTimeMillis())— State=RUNNING, Phase=NORMALRfmHunterSystemManager.onGameStarted(server)— 恢复逃走者状态、重置猎人回合数据、重置逃走币计时器RfmHunterSpawnManager.applyOnGameStarted(server)— 随机分配猎人出生点RfmRedstoneTriggerManager.resetAllOutputs(server)— 触发器断电syncToAll(server)+RfmHudSyncManager.syncToAll(server)— 全服同步
tick() 调度逻辑
每 tick(ServerTickEvent.Post)调用 RfmGameSessionManager.tick(server):
if (state != RUNNING) return;
if (phase == COUNTDOWN_FROZEN):
→ tickRunningWithoutCoin() // 仅猎人效果+待处理淘汰+冷却,无逃走币
return;
if (phase == PAUSED):
return; // 完全静止
if (phase == TIME_REWIND):
→ tickRewind() // 猎人效果+待处理淘汰+冷却+每秒扣除逃走币
if (elapsed <= 0) → elapsed 保持为 0(不会变负)
→ 每分钟 syncToAll()
return;
if (phase == TIME_CHAOS):
→ tickChaos() // 猎人效果+待处理淘汰+冷却(不使用真实时间,使用锁定时间)
→ checkSingleWinnerGameEnd() // 检查单一胜利者模式下是否全部其他玩家已淘汰
return;
if (elapsed >= totalSeconds):
→ onGamePaused() 结算最后逃走币
→ handleTimeExpired() 触发结算
→ executeGameEnd() 清理
return;
→ tickRunning() // 正常逃走币发放+猎人效果+冷却
→ 每分钟 syncToAll()tick 中调用的子管理器
按 ServerRfmGameEvents.onServerTick() 中的调用顺序:
| 顺序 | 子管理器 | 方法 | 作用 |
|---|---|---|---|
| 1 | RfmGameSessionManager | tick(server) | 游戏主循环 |
| 2 | RfmCoinStorageService | tick(server) | 每10分钟自动同步云存储 |
| 3 | RfmDetectBlockManager | tick(server) | 每5tick检测侦测方块范围 |
| 4 | RfmRedstoneTriggerManager | tick(server) | 每5tick检测触发器阈值 |
| 5 | RfmAlarmManager | tick(server) | 每tick遍历警报响铃 |
| 6 | RfmLocatorManager | tick(server) | 每5秒采样坐标 |
| 7 | RfmHunterEntityManager | tick(server) | A型猎人AI |
| 8 | RfmAnnounceHunterEntityManager | tick(server) | B型通报猎人AI |
| 9 | RfmHunterSystemManager | tickHunterDeviceRevival(server) | 猎人装置复活阶段 |
gameEnd() 终止途径
| 途径 | 触发条件 |
|---|---|
| 自然超时 | tick 中 elapsed >= totalSeconds |
| 命令终止 | /nfa basic stop |
| 团灭 | 所有逃走者被淘汰 |
| 叛节者胜利 | 仅剩叛节者存活 |
| 单一胜利者 | 时光混乱中除胜利者外全部被捕 |
结算流程(settleRoundAndBroadcast)
- 获取所有参与者 UUID
- 读取每人的回合币
- 若有
eliminatedReward则使用淘汰补偿,否则用全量回合币 RfmPairingManager.applySettlementAdjustments()— 结对调整(COIN取高/LIFE相加)RfmCoinStorageService.addCoinsByMap()— 回合币→总币RfmCoinFileSyncService.appendRoundSettlement()— 写入CSV结算文件- 全服广播结算排名
数据持久化
PersistentRfmGameSavedData
| NBT 键 | 类型 | 说明 |
|---|---|---|
State | String | "idle" / "running" |
Phase | String | "normal" / "paused" / "countdown_frozen" / "time_rewind" / "time_chaos" |
TotalSeconds | int | 总游戏时间秒数(默认3600,范围60-86400) |
ElapsedBeforeRunSeconds | int | 暂停/冻结/倒流/混乱时刻锁定的已消耗秒数 |
RunStartMillis | long | 当前 NORMAL 运行段开始的系统时间戳 |
RewindStartMillis | long | 时光倒流开始的系统时间戳(用于反向计算) |
ChaosLockedElapsedSeconds | int | 时光混乱锁定时的已消耗秒数(用于 HUD 显示冻结时间) |
PersistentRfmRoundSavedData
| NBT 键 | 类型 | 说明 |
|---|---|---|
Participants | List<UUID> | 本轮参与者的 UUID 列表 |
注意事项 / 限制
- IDLE→RUNNING 是唯一合法启动:无法从 PAUSED/FROZEN/REWIND/CHAOS 状态直接开始,必须经过 IDLE。
- PAUSED 完全静止:暂停期间不执行任何 tick 子逻辑(包括猎人效果和冷却恢复),只有红石触发器的 SUSTAINED 模式例外。
- FROZEN 仅停逃走币:
COUNTDOWN_FROZEN阶段玩家仍可自由行动,猎人冷却/效果/淘汰队列继续处理,仅逃走币产出和倒计时停止。 - TIME_REWIND 倒流到 0 停止:倒计时倒流到 0 秒时不会变成负数,保持在 0 秒。需通过指令手动解除。
- TIME_CHAOS 锁定金币淘汰:进入混乱阶段时对存活逃走者的当前回合币做快照,混乱期间被捕的逃走者以该快照值(而非实时值)计算淘汰奖励。单一胜利者模式下胜利者不参与结算分配。
- 单一胜利者不可被捕:设置单一胜利者后,胜利者的
canBeCaptured设为 false,游戏结束后重置为 true。 - 逃走币结算时机:暂停或阶段切换前会先调用
onGamePaused()结算该时段已产生的逃走币,避免玩家损失。 - 同步频率:游戏状态每分钟自动同步一次到全服,HUD 数据同步频率更高。
- 数据持久化保存时机:
setDirty()后依赖 Minecraft 的 DataStorage 在世界自动保存时写入,非立即落盘。