Nythros 架构(Architecture)¶
本文描述 Nythros 引擎的分层结构、服务拓扑与依赖铁律。设计依据:
blueprint/01-架构规范.md、ADR-020(三层产品定位与结构重划)、ADR-021(移除 gateway-worker 统一自研网关栈)、ADR-018/019(阶段 6 发布形态)。
1. 三层结构¶
组装层(用户项目的形态示例,两个)
├─ nythros/skeleton:create-project 入门套件——最小可运行 Map 服务器(Packagist 发布)
└─ nythros/demo:参考实现(对内验收)——deploy.yaml 拓扑、SocialServer/MapServer 全功能装配、
Protocol 词表、怪物/掉落示例、verify-* 脚本族(不对外发布)
Framework(三基类 + 业务模块 + 插件 + 脚手架)
└─ nythros/framework:BasePlayer/BaseMonster/BaseNPC(继承 engine 的 Actor\BaseActor)+ Damageable + Combat/Inventory/Social/Actor/Auth 模块 + Plugin + make CLI
Engine(契约 + 核心实现)
└─ nythros/engine:Contracts 接口 + 内核/世界/实体/Actor/AOI/事件/网络/协议/安全/持久化/集群实现
1.1 Engine:接口 + 实现¶
- Contracts(
Nythros\Contracts):引擎只暴露接口,跨层通信一律走接口。v0.1 冻结的契约包括ClockInterface、SchedulerInterface、WorldInterface、EntityInterface、ActorInterface、EntityManagerInterface、ActorSystemInterface、AOIProviderInterface、EventBusInterface、EventEnvelope、TimerInterface。 - 核心实现:
World(运行时聚合根,驱动 Actor → AOI → 调度器)、GridAOI(空间索引)、SimpleEntityManager/SimpleActorSystem/SimpleEventBus、SystemClock/SimpleScheduler/RegionScheduler、BaseEntity/Position、BaseActor,以及网络(WorkermanWebSocketServer)、协议(JsonSerializer/BinaryBatchSerializer二进制批量 +ProtocolVocabulary枚举压缩词表,Map 频道走二进制、社交层走 JSON,见docs/protocol.md)、安全(RedisTokenStore/TokenManager)、持久化(InMemoryStorage)、集群(RedisServiceRegistry)等实现。 - 实现细节标记
@internal:具体实现类不构成 API 承诺,业务层只依赖 Contracts 接口。
1.2 Framework:开箱即用层¶
- Actor 基座在 engine:
BaseActor(绑实体 + 抽象 update);framework 在其上给三个基类:BasePlayer(连接/uid/血量 + 钩子)、BaseMonster(AI 状态机 + 钩子)、BaseNPC(静态实体 + 交互)。 - 战斗契约
Damageable:玩家与怪物共同实现的最小战斗面(hp / maxHp / takeDamage / heal / isDead),使战斗服务以统一签名承载双向攻击;血量生命周期收敛在共享 traitActor\Vitals(hp ≤ maxHp 等数值不变量单点定义,合成 maxHp 口径由 BasePlayer 覆写)。 - 业务模块(ADR-020 §3.1 上移):
Combat(CombatService/MonsterActor/掉落)、Inventory、Social(SocialService 门面 + @internal 域响应器 Chat/Team/Guild/FriendResponder + 共享底座 SocialContext/ChannelSelector + ConnectionHub/TeamStore/GuildStore/LocationStore)、Actor(PlayerActor)、Auth(Identity)。 - 插件机制
Nythros\Framework\Plugin:官方插件(Skill / Item / Buff)经PluginRegistry::load走 register → enable 生命周期,数据定义经 Container 注入。 - 脚手架
makeCLI:make:actor/make:skill/make:event/make:map+ 能力报告make:capabilities(CapabilityCatalog数据源,入口vendor/bin/make)。
1.3 组装层:skeleton(入门套件)与 demo(参考实现)¶
- skeleton:
composer create-project nythros/skeleton的模板——最小但功能完整的单 Map 进程世界(认证直通/移动/AOI 广播/NPC 巡游),依赖仅 engine + framework,随稳定 tag 从 monorepo 同步发布。 - demo:全功能参考实现与对内验收场,不对外发布。拓扑事实源:
packages/demo/config/deploy.yaml(描述全部部署单元:social 三角色 + 地图/副本频道,ADR-021 自研单栈)。 - demo 组装脚本:
bin/server(根编排壳,读 deploy.yaml 分组 spawn)→ 社交组逐角色 spawnrun-worker.php --service=<type>、地图组 spawnbin/start-maps.php;launch.php保留为只起地图的便捷入口。 - 游戏示例:
MapServer(认证/移动/战斗/经济/GM 路由面)、SocialServer(社交三角色共用装配壳)、StaticAuthenticator(演示账号占位)、Protocol/*(演示自有协议词表);战斗闭环与社交业务逻辑在 framework(见 1.2)。
2. 服务拓扑¶
2.0 拓扑图¶
flowchart LR
subgraph Client["客户端(@nythros/client / Unity / 自研)"]
C1["控制线(JSON)"]
C2["实时线(二进制批量包)"]
end
subgraph SocialUnit["Social 单元(无状态会话,对称直连)"]
GW["gateway :18285<br/>登录 / 签发多 scope token"]
CH["chat :18286<br/>聊天五语义"]
TM["team :18287<br/>组队 / 帮派"]
end
subgraph MapUnit["Map 单元(有状态,一频道一进程一 World)"]
M1["map-1#ch-1 :18081"]
M2["map-1#ch-2 :18082"]
M3["map-2#ch-1 :18083"]
M4["dungeon-A#pool-1 :18084"]
end
C1 --> GW & CH & TM
C2 --> M1 & M2 & M3 & M4
GW -- "auth_ok 下发三地址" --> C1
GW & CH & TM & M1 & M2 & M3 & M4 <-- "token / 注册发现 / 快照(TTL) / 背包权威(bag:*) / 导出 Stream / PerfSampler" --> Redis[("Redis :6379")]
subgraph StorageUnit["Storage 单元(导出进程,type: storage)"]
EX["storage-exporter<br/>消费组单消费者"]
end
Redis -- "XREADGROUP nythros:export:players" --> EX
EX -- "落盘 upsert" --> DB[("MySQL :3306")]
2.0.1 分层结构图¶
flowchart TD
subgraph Demo["nythros/demo(组装 + 游戏示例)"]
D["MapServer / SocialServer 装配 · deploy.yaml · Protocol 词表 · verify-* E2E"]
end
subgraph Framework["nythros/framework(开箱即用层)"]
F["三基类 + Damageable · Combat/Inventory/Social/Mail/Quest/Auction/Matching/Leaderboard/GM · Plugin + make CLI"]
end
subgraph Engine["nythros/engine(契约 + 核心实现)"]
E["Contracts(冻结契约)<br/>World/Actor/AOI/Scheduler/Event/Network/Protocol/Security/Persistence/Cluster 实现(@internal)"]
end
D --> F
D --> E
F --> E
2.0.2 连接拓扑(文字版)¶
客户端 ── 社交连接(18285/18286/18287) ──> Social 单元(自研单栈,对称直连,ADR-021)
│ ├─ gateway(18285):登录入口,auth_ok 下发 map/chat/team 三地址
│ ├─ chat(18286) / team(18287):聊天五语义、组队、帮派
│ └─ 三角色共用 SocialServer 类,连接表进程内独立
│
├─ 战斗连接(18081~18084) ──> Map 单元(自研,有状态,一频道一进程一 World,客户端直连)
│
└─ 协调:Redis(6379)(token 多 scope / 服务注册与发现 / 组队·位置·帮派快照 TTL)
2.1 两条通信线路¶
- 控制线:Client → Social 单元(gateway/chat/team 对称直连)。承载登录、鉴权、聊天、组队、帮派、全局通知。
- 实时数据线:Client → Token → Map Server。承载移动、战斗、技能、AOI 广播、高频状态同步。
铁律:战斗帧同步必须客户端直连 Map,不经社交层转发(对称直连,避免高频多一跳开销,ADR-013/ADR-021)。
2.2 服务角色¶
| 角色 | 进程 | 状态特征 | 职责 |
|---|---|---|---|
| Social gateway | 自研 run-worker | 在线会话(进程内连接表) | 登录签发、bindUid/joinGroup、auth_ok 下发三地址 |
| Social chat/team | 自研 run-worker | 在线会话(进程内连接表) | 聊天五语义、组队状态机、帮派 |
| Map | 自研 run-worker / start-maps | 帧级(战斗状态) | AOI/位置/血量、掉落 |
| Redis | 外部 | 持久 + TTL | token 多 scope、服务注册表、队伍/位置快照 |
2.3 数据边界¶
| 状态层 | 落点 | 生命周期 |
|---|---|---|
| 持久状态 | Redis/DB | 永久(账号/角色/货币) |
| 临时共享状态 | Redis(TTL) | 掉线超时(组队/token/掉线标记) |
| 分组状态 | Social 连接表 | 在线会话(谁在哪个分组,进程内,onClose 自动清除) |
| 战斗状态 | Map | 帧级(AOI/位置/血量,进程内) |
判定口诀:「要保留」→ 永久进 DB/Redis、临时带 TTL 进 Redis;「要跨在线共享」→ 分组进 Social 连接表;「帧级高频」→ Map。
2.4 启动顺序与部署¶
- 启动铁序(ADR-021 §3.3):Redis(外部)→ social 单元 → map 单元 → storage 单元。
php bin/server start一条命令完成(--parts=social|maps|storage可单独部署一个单元)。 - 部署单元:
deploy.yaml中每个process块 = 一个部署单元;social块声明 gateway/chat/team 三角色,每个map服务声明mapId + channelId(serviceId 编码{mapId}#{channelId},一频道一进程一 World),storage块声明导出进程(port 仅占位),count字段可展开多个 worker。 - Map 有状态不能 reload,采用「滚动更新」:新实例 serving → 旧实例在 Redis 标记 stopping → 社交层 discover 过滤不再分配新玩家 → 旧实例自然退出。
3. 依赖方向铁律¶
允许:
Framework → Engine (framework 依赖 engine 的 contracts + actor)
Demo → Framework (demo 依赖 framework 基类)
Demo → Engine (demo 依赖 engine 接口与实现)
禁止:
Engine → Framework (引擎不知道框架层存在)
Engine → Demo
Cell → 业务模块(RPG/Skill/Inventory)
具体到代码:业务类只依赖 Nythros\Contracts 接口 + Nythros\Framework 公开类,绝不 import 引擎 @internal 实现(铁律 1,CI composer internal 对 framework/src 强制 use 扫描);引擎 @internal 装配类只允许出现在组装入口(skeleton 的 bin/ 与 demo 的 MapChannelFactory/run-worker 等组装层,门禁豁免)。
4. 包结构与发布形态¶
monorepo 开发、独立仓镜像发布(ADR-019 决策 B,已落地):
| 包 | 内容 | 依赖 | 发布 |
|---|---|---|---|
nythros/engine |
15 个核心模块物理合并为单包:contracts、kernel、kernel-workerman、scheduler、world、entity、actor、aoi、event、network、network-workerman、protocol、security、persistence、cluster | workerman/workerman ^5.2.2 |
Packagist + Nythros/engine 镜像仓 |
nythros/framework |
三基类 + Damageable + 业务模块 + 插件机制 + make CLI | nythros/engine |
Packagist + Nythros/framework 镜像仓 |
nythros/skeleton |
create-project 入门套件:最小可运行 Map 服务器骨架(type: project) | nythros/engine + nythros/framework |
Packagist + Nythros/skeleton 镜像仓(v* tag subsplit 强推) |
nythros/demo |
参考实现(对内验收):全功能装配 + verify-* 脚本族(type: library) | nythros/engine + nythros/framework |
不发布(monorepo 内随开发演进) |
要点:
- namespace 不统一:合并只动 composer.json 与目录,保留 15 个命名空间(
Nythros\Contracts\、Nythros\Kernel\、Nythros\World\等),源码零改动(ADR-019)。 - 用户安装心智是「一个 engine + 一个 framework」;15 个内部包的边界是工程实现细节,不暴露给最终用户。新用户从
composer create-project nythros/skeleton起步。 - 版本策略:已发布 v0.1.0(CHANGELOG 为准,契约自 v0.1 冻结);0.x 次版本内允许装配层破坏性调整,
v1.0.0前公开面(Contracts + 白名单 + framework 公开类)不再破坏。仓库形态与发布流水线详见部署指南「发布与仓库形态」节。
5. 运行时数据流(关键流程)¶
5.0 一次「登录 → 进图 → 攻击」的消息流¶
sequenceDiagram
participant C as 客户端
participant GW as Social gateway(18285)
participant R as Redis
participant M as Map(18081)
C->>GW: auth{username, password, mapId}
GW->>R: 校验账号 / 查位置快照
GW-->>R: 签发多 scope token(map/chat/team 各消费己 scope)
GW-->>C: auth_ok{token, map, endpoints, version, manifestVersion}
C->>M: 直连 auth{token}(map scope 一次性、防重放)
M-->>C: auth_ok{entityId, version, manifestVersion} + 视野全量 entity_enter
loop 对局
C->>M: attack{targetId}(前置校验:目标/距离/冷却)
M-->>C: combat:hit / entity_dead / drop:spawned(视野广播)
C->>M: pickup{dropId}
M-->>C: item:added(定向)+ drop:removed(视野)
M->>M: 管线 markDirty → 冲刷点 Redis 权威 + 导出 Stream → exporter 落 MySQL
end
5.1 登录 → 进图¶
- 客户端连 Social gateway(18285)发送
auth{username,password,mapId}。 - SocialService 校验账号、签发多 scope token(map/chat/team 各服务消费自己的 scope,ADR-021 §3.2)、查 Redis 位置快照;连接表 bindUid + 下发
auth_ok{token, map:{wsAddress}, endpoints:{chat,team}}。 - 客户端拿到三地址后按需直连:Map(18081~18084)发送
auth{token}进图,chat/team 地址用于社交角色直连。 - Map 消费 token 的 map scope(一次性、防重放)、按
aoi->query初始快照下发视野entity_enter,完成进图。
5.2 战斗(Map 进程内,零转发)¶
- 客户端发
attack{targetId}→ MapServer 前置校验(目标有效/存活/非自身/九宫格距离/冷却)→combat->attack结算。 - 视野广播
combat:hit;怪物死亡 →entity_dead+ 掉落drop:spawned。 - 拾取
pickup{dropId}→ 定向item:added+ 视野drop:removed→ 管线markDirty(export 缺省:冲刷点 Redis 权威 + Stream 导出;mysql 回退:worker 直写归档)。
5.3 每帧 Tick 顺序(蓝图 §9)¶
1. Clock tick
2. Scheduler 分配 Region CPU Budget
3. ActorSystem 执行 Actor update
4. World 处理 Entity 状态 / 位置变更
5. AOI 更新空间索引(World::update 全量刷新,发布 enter/leave 信封)
6. EventBus flush(demo 层帧末统一触发)
7. Network outbound flush(Outbox 批量发送)
6. 演进方向¶
- 文档分级(ADR-018 决策 3):Quick Start(门禁必需)→ Architecture / Actor Guide / Cell Guide(本组四篇)→ Protocol / Security / Cluster / Framework Guide(渐进)。
- ✅ 发布流水线已落地:v* tag → subsplit 三镜像仓(engine/framework/skeleton)→ Packagist → GitHub Release + npm(
.github/workflows/release.yml)。 - Cluster 能力(跨进程/跨服务器)明确后置:演进顺序 单进程 → 多 Actor → 多地图 → 多进程 → 跨进程 → 跨服务器。
- 社交扩展的正解 = 按消息职能竖切,不是同角色横扩(评审结论,取代早期 presence 专项草案):
三角色(gateway/chat/team)本就是"按职能分进程"的形态,沿同一维度继续切即可——当世界频道的高频
扇出实测拖慢 chat(判据:
network.dispatch_msp99 因世界消息越桶,perf-stats/stress-hotzone 可测), 新增broadcast角色专吃世界/频道广播,按 mapId 静态分片:一个世界的玩家进出同一分片进程, 天然无跨实例扇出——跨进程 presence 层(uid→实例路由 + 群消息总线)因此整个不需要。 登录侧保持单 gateway(bcrypt cost 9 ≈45/s 单实例够用,见 security.md §2 三级旋钮),从源头消除 "同一 uid 散落多实例"的踢旧问题。多实例横扩 + presence 仅在全服级频道成为产品需求时才重新立项。