Kzeroko KTM2

身份进阶运行时

身份图归属、解锁会话、支付顺序、转换、存档与网络约定。

更新于 2026/9/19 13 分钟阅读

进阶系统将共享图定义、玩家持久解锁历史与服务端短期会话分开保存。成功请求立即记录解锁,再在定时转换中改变 Origins 身份;这两个提交阶段之间没有回滚事务。

它做什么

internal.progression 负责图解析与校验、目录快照、玩家状态覆盖层和服务端准入检查。当前没有公开的 ProgressionAPI 门面。这些实现类用于仓库内部扩展,不是稳定的外部集成 API。

格式与玩法分别见身份进阶图数据和身份与进阶。Origins 负责身份层成员与身份能力;Isekai Core 提供 PLAYER_LEVEL 使用的玩家等级值。进阶分支不会从能力、目标名称或阶位标签中自动推导。

运行时模型

目录与玩家状态

ProgressionGraphLoader 注册为服务端数据加载器。单个文件解析失败时记录日志,并将成功定义提交给 ProgressionGraphRegistry.apply。注册表按图 ID 排序,对同层目标归属冲突整图拒绝,再原子替换 volatile 不可变快照。失败图不会保留旧定义。

版本值取有序图字段 SHA-256 的前 8 字节,作为非零 long;结果为零则改为 1。图顺序按 ID 规范化,但节点、条件和连接列表的顺序均参与摘要。展示字段变化也会改变版本。

buildState 读取各图身份层中的实际 Origins 身份,只输出匹配的图。它把当前节点加入已保存历史的副本,再在无向连通分量内计算可用性。首次解锁要求入边前置非空、全部前置均为当前或已解锁节点,并满足全部条件。已记录节点跳过这些检查。可用性计算不检查目标资格,解锁请求会检查。

状态覆盖层包含身份层 ID、图 ID、当前节点 ID、已解锁集合与可用集合。显示优先级为 CURRENT、UNLOCKED、AVAILABLE、LOCKED。生成状态不会将当前节点写入持久历史。

会话与受理

ProgressionService.open(player, layerString) 根据玩家当前身份寻找图,同步目录与状态,以随机 UUID 替换该玩家的会话,再发送打开数据包。已有转换时不能打开。会话有效期为 1200 刻(60 秒),当前刻等于到期刻时仍有效。

每位玩家在所有身份层之间只持有一个会话;切换章节会替换它。会话记录 UUID、身份层、图、版本、到期刻和已提交标记,均为临时服务端状态。

requestUnlock 按以下顺序检查结果:

结果触发条件
TRANSFORMATION_ACTIVE已有受理的转换尚未结束。
INVALID_SESSION会话不存在、已提交、UUID/图不匹配,或当前刻大于到期刻。
STALE_CATALOG请求版本不同于会话或当前目录;先确保目录已同步。
INVALID_NODE图、节点或当前身份缺失,身份层不符,或目标无资格。
ALREADY_CURRENT目标已是当前节点。
NOT_CONNECTED目标与当前节点处于不同无向连通分量。
PREREQUISITES_MISSING首次解锁无入边或缺少任一前置节点。
REQUIREMENTS_MISSING至少一项首次解锁条件不满足。
ACCEPTED消耗与历史已提交,转换进入队列;身份尚未生效。

新节点的全部条件均在任何支付之前检查,再按列表顺序扣除消耗。同一物品或经验等级的重复消耗不会汇总,消耗辅助方法也不返回成功结果供复查。创造模式仍须满足等级、进度及物品 ID 已注册的要求,但跳过物品数量与全部支付。

支付后,将当前与目标节点 ID 写入历史,标记会话已提交,并将转换加入队列。随后发送状态数据包,以及同样包含状态的 ACCEPTED 结果。重返已记录节点跳过前置、条件与支付,但仍检查会话、身份、连通性和目标资格。

身份转换

END_SERVER_TICK 驱动状态机。下表以受理时刻为基准;内部转换开始时间位于节点受理效果之后。

事件服务端时间点
开始转换+12 刻(0.6 秒)。
应用身份+38 刻(1.9 秒),即开始后 26 刻(1.3 秒)。
显现身份+50 刻(2.5 秒),即开始后 38 刻(1.9 秒)。
结束+76 刻(3.8 秒),转换本身持续 64 刻(3.2 秒)。

开始时重新检查目标,发送 START,以 isekaiexpansion:progression_transformation 为来源施加 64 刻(3.2 秒)操作锁,并广播 critical 优先级的 PROGRESSION_ASCENSION Particlize pulse。锁定覆盖物品使用、攻击、移动与跳跃。

身份生效时再次解析身份层与目标,并检查身份层已启用、目标属于该玩家可用成员或为特殊身份。随后依次调用 OriginsEvents.ON_CHOOSING_ORIGIN、设置 Origin、同步、协调技能配置与变身身份,再发送状态。该事件在变更前调用,没有供取消使用的返回值。

遮蔽阶段持续 12 刻(0.6 秒),显现时触发身份升华显现效果。结束时清除这一来源的操作锁,发送 COMPLETE 与完成消息,并移除待执行转换和会话。

持久化

isekaiexpansion:progression 是玩家组件,重生复制策略为 ALWAYS_COPY。它实现普通 Component,客户端通过目录与状态数据包获取信息,不使用组件自动同步。以下 NBT 是存档格式,不是数据包编写格式。

NBT 键类型默认值含义
graphsarray<object>[]非空图历史,以 compound 列表保存。
graphs[].ididentifier""图 ID;语法无效时跳过该记录。
graphs[].nodesarray<string>[]非空白的已解锁节点 ID;重复项合并为集合。

读取时清空原历史,跳过空记录;同一图 ID 重复出现时,以最后一条非空记录为准。不检查节点语法、目录成员、当前目标或消耗。写出时省略空历史,节点字符串排序。删除图或节点不会清除历史,重命名也没有迁移;复用节点 ID 会改变旧解锁的含义。

unlock 本身不校验图和节点 ID,返回集合是否变化;调用方应先确定授予资格。unlocked 返回不可变副本。会话与待执行转换均不持久化。

客户端与展示

加入时,客户端发送缓存版本。服务端仅在版本不符时发送目录,但始终发送最新玩家状态。断开连接会清空客户端玩家状态与活动界面,共享目录和版本则保留供复用。

打开界面需要缓存版本、图和身份层状态匹配。不匹配时发送版本为 0 的 hello 并显示刷新提示,不会自动重试此次打开。目录和状态更新通过 ModernUI 投递,并确认原 fragment 仍处于活动状态。

只绘制当前无向连通分量。拖动背景平移;滚轮缩放为 ×1.1 或 ×0.9,限制在 0.62–1.65;重置恢复缩放 1 和零平移。节点坐标乘以水平、垂直布局间距,只影响展示,不影响解锁。

默认长按为 30 刻(1.5 秒)。长按使用 UI 单调时钟,每刻 50,000,000 纳秒。主键按下选择并开始,移出边界或提前松开会取消;绘制更新在计时完成时立即提交,不要求松开。canHold 只在节点可用或已解锁且没有等待中的响应时允许长按。界面不会暂停。

ProgressionTransformationController 关闭界面并暂时使用 THIRD_PERSON_BACK。只有电影输入锁原先未锁定时才获取所有权,也只释放自己持有的锁。完成通知、本地计时到期或断线均可结束流程;只有镜头仍保持本控制器设置的视角时,才恢复原视角。收到的时长至少取 1 刻(0.05 秒)。

详情从所选目标的能力解析形态外观变体,而不是仅查看玩家当前能力,并缓存形态 ID;变体更新时清空缓存。实际选择变体仍要求拥有对应形态。进阶组件不保存变体状态。

约定

权限与中断

服务端操作应在逻辑服务端线程调用,玩家可变映射没有同步保护。注册表 apply 用 synchronized 串行替换;多步查询应持有同一份不可变快照。S2C 处理通过 client.execute 调度,界面更新另行投递到 UI 线程。

解锁包不包含长按时长、开始证明或松开证明。长按是客户端交互约定,不是服务端权限校验。服务端保障来自会话、版本、当前身份、目标资格、连通性、前置和条件检查。

有效目标不能为空,身份层必须启用,且目标属于该玩家可用成员或为特殊身份。unchoosable 不是进阶准入检查项。此子系统不授予无敌状态,也不会在应用已受理转换时重新检查支付或图结构。

数据重载成功后,即使版本未变也会发送完整目录与状态并清空会话。待执行转换继续使用已捕获的身份层与目标,仅复查目标资格,不检查替换后的图。旧界面可能收到新图,但仍持有旧会话和版本,必须重新打开。

断线会移除服务端待执行转换,不撤销消耗或历史。开始或应用阶段失败时,会清除操作锁、结束客户端流程并移除会话,同样不退款、不回滚。因此中断后可能已记录目标解锁,实际身份却仍是旧目标。重连后,在目标恢复资格时重新激活已记录节点;系统没有恢复旧时间线的续执行队列。

网络格式与限制

下表数据包路径均使用 isekaiexpansion 命名空间。

数据包路径方向载荷顺序
progression_catalog_helloC2S版本 long。
request_open_progressionC2S身份层 ID。
request_progression_unlockC2S会话 UUID、图 ID、节点字符串(64)、版本 long。
sync_progression_catalogS2C版本 long、图数 VarInt(0–64)、图记录。
sync_progression_stateS2C玩家状态覆盖层。
open_progression_screenS2C会话 UUID、身份层 ID、图 ID、版本 long。
progression_unlock_resultS2C结果枚举字节、节点字符串(64;拒绝时为空)、玩家状态覆盖层。
progression_transformationS2C阶段字节(START、COMPLETE)、时长 VarInt;完成时发送 0。

图记录依次包含 ID、身份层、模式字节、节点数与节点,再接边数和端点字符串。节点包含 ID(64)、目标 ID、阶位(64)、x/y 浮点数、长按 VarInt、可选图标布尔值与 ID、名称和描述字符串(各 256),最后是条件列表。条件包含类型字节、可选目标布尔值与 ID、数量 VarInt、消耗布尔值。

读取限制为图数 0–64、每图节点 1–512、每节点条件 0–16、边数 0–1024。玩家状态包含 0–64 条身份层记录,每条保存 ID、当前节点(64)、已解锁集合与可用集合;各集合最多 512 个节点字符串(64)。集合按排序写出,读取时去重。无效枚举序号会抛出异常。

JSON 构造未执行全部网络限制。过大的目录或残留存档历史可能在服务端构造成功,却导致客户端解码失败;过长文本也可能在写包时失败。编写内容与修复存档时应同时遵守两层限制。

扩展方式

通过数据包添加图内容,保持图与节点 ID 稳定,并确保所需目标存在。仓库内部的服务端交互可打开现有服务,避免直接改写 Origins:

static void openRaceProgression(ServerPlayerEntity player) {
    ProgressionService.open(player, "origins:origin");
}

示例使用 internal.progression.ProgressionService 与 net.minecraft.server.network.ServerPlayerEntity,不是新增公开门面。其他身份层传入对应 ID,并确保当前 Origin 属于已注册图。

新增条件类型时,应一起更新枚举解析、校验、网络读写兼容、服务端求值与支付、客户端标签。新增转换阶段须明确中断处理以及镜头、操作锁清理。新消耗资源的合计负担能力检查应与实际支付一致;现有顺序消耗辅助方法不提供事务回滚。

验证

在可丢弃玩家进度的测试世界中使用替换图:

  • 拒绝空节点、重复 ID 或目标、自连、未知或重复边、有向环、非有限坐标、超出 10–200 刻(0.5–10 秒)的长按时长,以及不合法的 consume/type 组合。
  • 检查全部入边前置、未访问根节点、不连通目标、已记录重返、创造模式等级要求与重复消耗条件。
  • 打开两个章节后提交旧 UUID,等待超过 1200 刻(60 秒),分别重载相同与不同内容,确认旧界面不能提交。
  • 确认受理时支付并保存历史,在 +38 刻(1.9 秒)应用身份,+76 刻(3.8 秒)完成清理。在受理与应用之间移除目标资格,验证实际不会回滚。
  • 在身份变更前后分别断线;重连后检查历史、重新激活已记录节点,并检查视角与输入恢复。
  • 检查目录不匹配与复用、错误枚举或数量的数据包、残留未知历史、重生复制,以及图和文本的最大支持长度。
  • 预览尚未拥有的形态变体,获得形态后再选择,确认进阶历史与变体状态分别保存。

这些是回归检查步骤,不表示文档编写期间已执行游戏内测试。

相关页面