Skip to content

警报系统

文档版本:基于 2026-06-01 代码分析

概述

警报系统负责在特定玩家位置周期性播放警报音效,暴露其所在位置。支持两种警报管理模型:列表管理(管理员通过指令设置警报列表,启动后定时响铃)和侦测管理(侦测方块检测到进入范围的逃走者,自动开始警报)。

警报音效以 1.5 倍音量按RECORDS分类播放,每 2 秒循环一次,形成持续的"暴露"压力。


核心概念

两种管理模型

属性列表管理(listManaged)侦测管理(detectManaged)
触发方式指令 /nfa rfm alarm start侦测方块自动检测
持续时间有时间限制(指令指定)Long.MAX_VALUE(永不超时)
持久化列表受列表控制(tick时反向检查)不受列表控制
玩家淘汰时自动从列表和活动任务中移除仅从活动任务中移除
停止方式指令停止 / 超时 / 淘汰离开侦测范围 / 淘汰

循环参数

  • 间隔LOOP_INTERVAL_MILLIS = 2000(每 2 秒播放一次)
  • 音量ALARM_VOLUME = 1.524.0F / 16.0F
  • 音效分类SoundSource.RECORDS(强制覆盖普通音量限制)

架构设计

警报生命周期

mermaid
sequenceDiagram
    participant Admin as 管理员
    participant Cmd as 命令
    participant AM as RfmAlarmManager
    participant Data as PersistentRfmAlarmSavedData
    participant Task as AlarmLoopTask
    participant Sound as 音效系统

    Admin->>Cmd: /nfa rfm alarm list add @p
    Cmd->>AM: addToList(targets)
    AM->>Data: addPlayers(uuids)

    Admin->>Cmd: /nfa rfm alarm start 1 30
    Cmd->>AM: startFromList(server, 90)
    AM->>AM: 清空 ACTIVE_ALARMS
    AM->>Data: 获取持久化列表
    loop 每个存活逃走者
        AM->>Task: 创建 listManaged(endAt=now+90000)
    end

    loop tick() 每tick
        AM->>Task: 检查超时/列表一致性/玩家存活
        alt 每2秒
            AM->>Sound: playAlarmAround(target)
            Note over Sound: 目标位置播放 alarm.ogg<br/>音量1.5 × RECORDS分类
        end
        alt 玩家淘汰
            AM->>Data: 自动从列表移除
            AM->>Task: 移除任务
        end
    end

    Note over Task: 超时 → 任务移除
    Note over Cmd: /nfa rfm alarm stop → 全部清空

两种模型的任务并存

ACTIVE_ALARMS 是一个共享的 LinkedHashMap<UUID, AlarmLoopTask>,同时容纳 listManageddetectManaged 两种任务。同一个玩家可能同时有列表警报和侦测警报(但 UUID 唯一,后创建的会覆盖先前的)。


关键文件

文件说明
com/chenxi/chenxi_rfm/server/rfm/RfmAlarmManager.java警报管理核心(含 AlarmLoopTask)
com/chenxi/chenxi_rfm/server/config/rfm/PersistentRfmAlarmSavedData.java警报列表持久化
com/chenxi/chenxi_rfm/server/rfm/RfmDetectBlockManager.java侦测方块联动(startDetectAlarm/stopDetectAlarm)
com/chenxi/chenxi_rfm/server/command/rfm/RfmAlarmCommand.java/nfa rfm alarm 入口
com/chenxi/chenxi_rfm/server/command/rfm/RfmAlarmStartCommand.javaalarm start
com/chenxi/chenxi_rfm/server/command/rfm/RfmAlarmStopCommand.javaalarm stop
com/chenxi/chenxi_rfm/server/command/rfm/RfmAlarmListCommand.javaalarm list add/delete/clear
com/chenxi/chenxi_rfm/common/registry/ModSounds.javaALARM 音效注册
com/chenxi/chenxi_rfm/server/event/ServerRfmGameEvents.javatick 调度入口

关键流程

添加玩家到列表

/nfa rfm alarm list add <targets>

  1. 选择在线玩家
  2. 如果选择了所有在线玩家(@a) → 要求 confirm 二次确认(命令方块除外)
  3. @a 时自动过滤为"存活的逃走者"(filterAliveRunners
  4. 非全选时直接按传入的玩家集合添加,不做存活过滤

启动警报

/nfa rfm alarm start <minutes> <seconds>

  1. 清空 ACTIVE_ALARMS
  2. 计算 endAt = now + totalSeconds * 1000
  3. 遍历持久化列表中所有玩家,仅为存活逃走者创建 listManaged 任务
  4. 提示实际启动的警报数量

tick 循环

每 tick 执行:

  1. 遍历 ACTIVE_ALARMS 的所有条目
  2. 超时检查now >= endAtMillis → 移除
  3. 列表一致性检查listManaged 但目标已不在持久化列表 → 移除
  4. 存活检查:玩家不存活 → 如果是 listManaged 则同步从持久化列表删除,移除任务
  5. 间隔检查nextPlayMillis > now → 跳过
  6. 播放音效playAlarmAround(server, targetUuid),失败(玩家下线)→ 移除
  7. 设置下次播放时间:nextPlayMillis = now + 2000

侦测方块联动

  • startDetectAlarm(uuid) → 创建 detectManaged 任务(endAtMillis=Long.MAX_VALUE, nextPlayMillis=0 立即播放)
  • stopDetectAlarm(uuid) → 从 ACTIVE_ALARMS 移除

数据持久化

PersistentRfmAlarmSavedData

文件:<world>/data/nfa_rfm/rfm_alarm.dat

json
{
  "Players": [
    { "Uuid": "uuid-1" },
    { "Uuid": "uuid-2" }
  ]
}
  • 仅存储目标 UUID 列表,不存储任务状态
  • 任务运行时状态(超时、下次播放时间、管理类型)全在内存中的 ACTIVE_ALARMS
  • setDirty() 标记持久化,在 Minecraft 自动保存时写入

注意事项 / 限制

  1. 不跨维度:音效仅在目标玩家所在维度播放,其他维度的玩家听不到。
  2. 距离衰减有效playSound(null, x, y, z, ...) 的 null 接收者意味着所有玩家都能听到,但受 Minecraft 原生的声音距离衰减影响。
  3. 1.5倍音量:通过 RECORDS 分类 + 高音量设计确保可听性。
  4. 主动移除淘汰者:tick 中自动检测玩家是否存活,不存活则同步清理持久化列表和活动任务。
  5. listManaged 的一致性校验:tick 中会检查目标是否仍在持久化列表中,防止数据不一致。
  6. detectManaged 不受列表管理:侦测触发的警报不检查持久化列表一致性。
  7. 全选时自动过滤@a 全选时会自动过滤为存活逃走者;需要添加所有玩家时需逐个指定。

相关文档