Skip to content

Decision Log ​

本文集中记录已经收敛过的设计细节和仍需拍板的问题。关闭某个决策时,必须同步更新对应 authority document。

权威文档优先级:

  1. 最终架构总纲
  2. Lua API 契约
  3. 配置运行时语义
  4. 运行时语义决策稿
  5. 官方可选模块契约
  6. 重构路线图

Closed Decisions ​

OD-001 CLI Contract ​

状态:closed。权威文档见 启动流程运行时语义。

决策:

  • --config <path> 可缺省,默认 config/app.yaml。
  • --config 可重复传入;多个文件按传入顺序合并,合并规则以 配置运行时语义 为准。
  • --node-id 属于 shield_cluster optional module。未启用 cluster 时传入必须返回 CLI 错误。
  • 旧 subcommand 只作为 legacy CLI 存在,不进入新的 shield::run 公共契约。
  • shield 可执行文件和示例可执行文件都应直接调用 shield::run(argc, argv),因此使用同一套参数集合。

OD-002 Process Lifecycle ​

状态:closed。权威文档见 启动流程运行时语义。

决策:

  • shield::run 负责通用 signal handling、runtime loop、help/version 输出和退出码。
  • Windows 使用 SetConsoleCtrlHandler,POSIX 使用 SIGINT/SIGTERM。
  • shield::run 初始化 runtime 后阻塞等待停止信号;底层 bootstrap API 只负责 initialize/shutdown。
  • 用户自定义 main 可以绕过 shield::run,直接调用 lower-level bootstrap API。
  • 退出码以 启动流程运行时语义 为准。

OD-003 Physical Directory Layout ​

状态:closed。权威文档见 目录结构设计 和 重构路线图。

决策:

  • 短期保持当前 src/core、src/net、src/lua 等物理目录,先用 CMake target 和 include 边界收敛依赖。
  • 不在 Phase 1 同时做大规模文件搬迁和语义重构。
  • optional module 的目标物理目录保留为 src/optional/shield_cluster 等形态,但只在对应模块实现时创建或迁移。
  • public include 路径保持 include/shield/...,不随 target 名做破坏性改名。

OD-004 Phase 1 Network Scope ​

状态:closed。权威文档见 网络运行时语义。

决策:

  • Phase 1 只冻结 TCP session 与 basic transport framing。
  • UDP、KCP、WebSocket 保留为目标语义和后续扩展,不作为 Phase 1 验收阻塞项。
  • HTTP 业务 gateway 不进入 core;HTTP 管理端点只在 shield_ops 显式启用时存在。

OD-005 Optional Module Failure Policy ​

状态:closed。权威文档见 官方可选模块契约 和 配置运行时语义。

决策:

  • 配置包含 optional module 配置段,但对应模块未构建或未启用时,启动失败。
  • 显式启用的 optional module 初始化失败默认 fail fast。
  • shield_cluster 第一版允许远端连接失败时 degrade 到单节点,但必须持续暴露 unhealthy 状态;配置非法、节点身份冲突和本地监听失败仍 fail fast。
  • shield_ops 启用后绑定管理端口失败必须 fail fast。
  • shield_global、shield_player、shield_server 初始化失败默认 fail fast。

OD-006 Data API Phase Split ​

状态:closed。权威文档见 数据访问运行时语义 和 Lua API 契约。

决策:

  • 数据访问不进入 shield_core,也不再通过独立 shield_data 模块暴露。
  • Phase 1 当前契约是插件系统 v1:manifest scan、显式 instance、显式 binding、C ABI interface 和插件自注册 Lua namespace。
  • SQL、文档库、缓存、队列和排行榜能力分别由 provider 插件提供。
  • 业务 Lua 使用插件 namespace + binding 逻辑名,例如 shield.database.mysql("database.default")。
  • shield.db.* / shield.redis.* 是 legacy API,已删除且不提供 public compatibility shim。
  • prepared statement、pipeline、eval、sentinel/cluster 等能力由具体插件或后续插件接口独立演进,不反向扩大 core API。

OD-007 Legacy Compatibility Window ​

状态:closed。权威文档见 重构路线图 和 Lua API 测试用例。

决策:

  • 不提供 public compatibility shim。
  • legacy Lua API 删除以 lua-api-tests.md 的负向测试为门槛。
  • legacy C++ target 删除以 examples/tests 不再引用为门槛。
  • 旧 CLI subcommand 可以作为 legacy CLI 代码短期存在,但不进入新的 shield::run 公共契约。

OD-008 Player Setup API Form ​

状态:closed。权威文档见 玩家生命周期运行时语义 和 官方可选模块契约。

决策:

  • P0 只冻结 shield.player.setup(M, opts) 作为业务 Player Service 唯一推荐入口。
  • shield.player.Base 作为高级风格留待 Phase 2+;P0 不实现,P2 边界见 OD-014。
  • setup 的 opts 字段去 on_ 前缀(auth/login/client_message/disconnect/reconnect/logout/save),与 module-level on_init/on_exit 隔离命名空间。
  • 必填钩子(auth/login/client_message/disconnect/logout)缺失即返回 nil, Error{code="setup_invalid"},service 不进入 running 状态。
  • 未在 setup 中提供的可选钩子由框架按文档明列的默认实现执行,不允许"未提供即 noop"的隐式行为。
  • 业务覆盖可选钩子时,默认实现不自动执行;保留默认行为需显式调用 shield.player.defaults.* helper。

OD-009 Player Persistence Adapter Boundary ​

状态:closed。权威文档见 玩家生命周期运行时语义 和 官方可选模块契约。

决策:

  • persistence adapter 是 shield_player 拥有的轻量契约,不属于 shield_data。
  • adapter 底层必须通过插件 namespace + binding 逻辑名访问数据能力,不重新定义 SQL/Redis/文档库语义。
  • adapter 不拥有连接池;连接池归属对应插件 instance。
  • adapter 不引入 ORM、mapper、schema 工具链;只接受可 LuaPack 编码的 table 白名单字段。
  • adapter 失败复用对应数据插件错误码;player 域只新增 persistence_save_failed 等明确属于本模块的错误。
  • 未配置 persistence 时,on_save 默认实现为 no-op。

OD-010 Player Cross-Service Reference ​

状态:closed。权威文档见 玩家生命周期运行时语义 和 官方可选模块契约。

决策:

  • 跨 service 玩家引用命名为 PlayerRef,不命名为 PlayerHandle,避免与 ServiceHandle 混淆。
  • PlayerRef 是 player 模块内部引用,不是 ServiceHandle 的替代品。
  • PlayerRef 结构为 { uid, node_id, service_id, epoch };epoch 来自 shield_cluster 的 node_epoch,单节点为 0。
  • 跨 service payload 只能传 PlayerRef(或客户端 RPC 的只读 ClientContext/ClientRef 标记),禁止传裸连接句柄或完整 PlayerSession。
  • shield.player.resolve(ref) P0 仅冻结本地 resolve;远端 resolve 留 Phase 2+,由 shield_cluster + shield_player 协作定义。
  • ref 失效返回 nil, Error{code="player_not_found"},不抛错。

OD-011 Player Extended States ​

状态:closed。权威文档见 玩家生命周期运行时语义。

决策:

  • anonymous 和 spectator 都是 opt-in 状态,默认状态机仍是 connecting -> authenticating -> online。
  • auth hook 仍为必填;业务通过 auth 返回值声明匿名或旁观连接。
  • 未显式开启时返回 anonymous_disabled 或 spectator_disabled。
  • 匿名升级为正式账号必须重新认证,并生成新的 PlayerRef。

OD-012 Player Scale Model ​

状态:closed。权威文档见 玩家生命周期运行时语义。

决策:

  • 默认模型是 service_per_player。
  • player_pool 是大规模场景的可选运行策略,不改变 shield.player.* public API。
  • player_pool 下 PlayerRef.service_id 指向 pool shard;玩家级路由由 shield_player 在 shard 内完成。
  • 实现前必须补齐内存、延迟、GC、mailbox backlog 和重连窗口缓存基准。

OD-013 Player Multi-Device Policy ​

状态:closed。权威文档见 玩家生命周期运行时语义。

决策:

  • 默认策略为 single。
  • kick_old 会让旧会话以 logout reason "replaced" 退出。
  • multi 使用 (uid, device_id) 索引,shield.player.get(uid) 返回主会话,设备枚举走 PlayerManager.get_devices(uid)。
  • 未提供稳定 device_id 时只允许临时会话,不参与同设备重连。

OD-014 Player Base API Boundary ​

状态:closed。权威文档见 玩家生命周期运行时语义。

决策:

  • shield.player.Base 是 P2 语法糖,必须基于 shield.player.setup。
  • Base 不引入第二套 lifecycle,不支持多继承,不恢复 setup 字段的 on_* 前缀。
  • P0/P1 测试以 setup 为准;Base 测试只验证无额外语义。

OD-015 Runtime Primitives Scope ​

状态:closed。权威文档见 基础组件与运行时适配边界、重构路线图 和 网络运行时语义。

决策:

  • shield.buffer / shield.crypto / shield.socket / shield.stream / shield.tls 这类 runtime primitives 只保留为后置方向,不进入当前主路径。
  • 不把 cosocket 风格 API 放进当前对业务用户的默认心智模型。
  • 当前推荐心智模型保持三件事:客户端入站走 gateway 单一 target + 只读 ClientContext/ClientRef,服务间走 shield.send/call,对外 HTTP 走 shield.http。
  • 若未来推进 primitives,也应作为高级能力或后置阶段能力,而不是当前默认推荐路径。
  • JWT/Auth 不是基础组件层中心能力,也不作为 Shield 官方插件发布;应由业务层基于基础原语或外部服务实现。

后续执行(2026-09-28,AD-08):shield.crypto 一期切片已落地(编解码 / SHA-256 / HMAC-SHA256 / random_bytes / constant_time_compare),让上述“业务层基于基础原语”句式可行; 参考实现为 Lua 层 scripts/lib/jwt.lua,plugins/auth_jwt 标弃用(保留一个过渡期)。其余 primitives(shield.buffer / socket / stream / tls)仍为后置方向。

OD-016 Server State Machine and API Surface ​

状态:closed。权威文档见 服务器状态运行时语义 和 官方可选模块契约。

决策:

  • ServerManager 是进程级 C++ 单例(照 PlayerManager:global()/set_global()),不是 service:没有 mailbox、没有 CAF actor、不 spawn service、不进入 Starter 编排。
  • 状态机为 starting → running ⇄ maintenance、running/maintenance/starting → shutdown;shutdown 终态;→ starting 恒非法;starting → maintenance 非法;同值 set 幂等成功且不通知;非法迁移返回错误不崩溃。
  • starting → running 由 bootstrap init complete 自动触发(mark_ready),不暴露为 API。
  • 配置段只有 server_manager.{name, info.{name,version,region}},全可选;删除草稿的 enabled(启用只由编译开关决定)、state(初始状态恒为 starting)、on_state_change(通知开关由 watch 注册行为表达)字段。
  • 状态观察者注册为 shield.server:watch(fn);投递必须走 system 消息通道(send_system)——普通 send() 静默拒绝 on_ 前缀方法,而状态通知方法名属于保留前缀域。回调签名带 dispatch ctx(fn(ctx, new_state)),与 handler dispatch 规则一致。
  • 否决"C++ 注册表持有 Lua 回调对象"方案:会让纯 C++ 的 ServerManager 链接脚本绑定层,破坏可单测性。C++ 注册表只存 {watch_id, service_id};观察者回调由每 VM 的编排层持有并安装转发 handler,观察者 service 退出时按判活自动注销。

OD-017 Server Shutdown Handover and Maintenance Boundary ​

状态:closed。权威文档见 服务器状态运行时语义。

决策:

  • server:shutdown(ms) 只做三件事:迁移到 shutdown、通知观察者、ms 毫秒后调用与 SIGINT/SIGTERM 同路径的进程停止请求。它不接管 drain 预算——超时与退出顺序仍归 bootstrap shutdown.timeout.*。
  • 重复 shutdown 返回 shutdown_already_scheduled,不重复计时;计时线程用 std::jthread + stop_token,bootstrap 两条清理路径都显式 stop。
  • 维护模式 P0 只提供 state() 真相查询;登录准入 gate 由业务 / shield_player 层自行实现,core 与 gateway 不感知维护状态。
  • 状态通知回调经 bootstrap 注入的 notify_fn 回调进入 messaging 层,ServerManager 不直接依赖 shield_lua,避免环形依赖。
  • ops 暴露并入 /ops/status 的 server 块与 console root.server 命令;不设独立 /ops/server 端点,不提供 ops 写操作入口。

Open Decisions ​

当前没有仍需设计拍板的基础语义项。后续若发现新的未决问题,先在本节记录,再同步更新对应权威文档。

Apache License 2.0