feat(cordis): 接入上下文组合与模组事务热替换

This commit is contained in:
2026-08-16 23:20:40 +08:00
commit ad256f109b
676 changed files with 52168 additions and 0 deletions
@@ -0,0 +1,252 @@
# ShrinkNetwork.Integration.EventBus
`ShrinkNetwork``ShrinkEventBus` 的桥接层。目标不是“把网络生命周期抛几个普通事件出来”,而是让某些 `EventBase` 类型本身就能作为网络消息声明,并在本地与远端之间保持一致的事件语义。
## ✨ 特性概览
| 特性 | 说明 |
|------|------|
| 🌉 **事件即网络消息** | `EventBase` 可直接实现 `IShrinkNetworkMessage / IShrinkNetworkRequest` 参与网络同步 |
| 🔁 **自动双端分发** | 本地 `EventBus.TriggerEvent(...)` 后,可自动转发到远端并重新进入远端 `EventBus` |
| ✅ **HasResult 请求语义** | 请求型事件支持 `EventResult` 与取消状态回传 |
| 📉 **增量事件去重** | `IShrinkNetworkDeltaEvent``SessionId + EventType + DeltaKey` 做版本过滤 |
| 🧩 **按键接入** | `ShrinkNetworkEventBusComponent` 注入网络服务,随依赖激活和撤回 |
| 🧰 **更顺手的扩展 API** | `session.PublishEventAsync(...)``session.RequestEventAsync(...)``service.UseEventBusBridge(...)` |
## 📦 依赖
- `ShrinkNetwork`
- `ShrinkEventBus`
- Unity 2022.3+
## ⚙️ 安装前提
桥接层默认假设你已经有:
- 一套正常工作的 `ShrinkNetworkService`
- 一套正常工作的 `ShrinkEventBus`
- 事件类型明确声明 `[ShrinkNetworkEvent] + [ShrinkNetworkMessage]`
## 🚀 快速上手
### 第一步:声明广播型网络事件
```csharp
using ShrinkEventBus;
using ShrinkNetwork;
using ShrinkNetwork.Integration;
[ShrinkNetworkEvent]
[ShrinkNetworkMessage(3001, "room/player_ready")]
public sealed class PlayerReadyEvent : EventBase, IShrinkNetworkMessage
{
public string PlayerId { get; set; } = string.Empty;
public string RoomId { get; set; } = string.Empty;
}
```
### 第二步:正常启动网络服务
```csharp
var service = new ShrinkNetworkService(
new ShrinkMessagePackNetworkSerializer(),
new ShrinkNetworkMessageRegistry(),
new ShrinkNetworkRouter());
service.AutoRegisterAll();
service.BindTransport(new ShrinkTcpClientTransport("127.0.0.1", 17001));
```
ContextLoader 项目由 Starter 组合根装配 `ShrinkNetworkEventBusComponent`。Standalone 项目需要显式调用 `service.UseEventBusBridge(...)`;仅绑定传输不会再通过反射接桥。
### 第三步:像普通事件一样触发
```csharp
EventBus.TriggerEvent(new PlayerReadyEvent
{
PlayerId = "10001",
RoomId = "alpha"
});
```
注册之后:
- 本地事件先按正常 `EventBus` 流程执行
- 桥接层自动把该事件转发到当前 `ShrinkNetworkService` 的在线 session
- 远端收到后重新进入远端 `EventBus.TriggerEventAsync(...)`
## 📖 核心概念
### 事件声明规则
可桥接事件需要同时满足:
1. 继承 `EventBase`
2. 实现 `IShrinkNetworkMessage``IShrinkNetworkRequest`
3. 标记 `[ShrinkNetworkEvent]`
4. 标记 `[ShrinkNetworkMessage(opcode, route)]`
如果缺少这些条件,桥接层会忽略该事件类型。
### 广播型事件
广播型事件只需要:
- `EventBase`
- `IShrinkNetworkMessage`
- `[ShrinkNetworkEvent]`
- `[ShrinkNetworkMessage(...)]`
适合:
- 玩家就绪
- 房间状态变更
- UI 同步通知
手动定向发送时,推荐直接用扩展:
```csharp
await session.PublishEventAsync(new PlayerReadyEvent
{
PlayerId = "10001",
RoomId = "alpha"
});
```
### 增量事件
如果事件本身就是 delta,而不是完整状态,可以实现 `IShrinkNetworkDeltaEvent`
```csharp
[ShrinkNetworkEvent]
[ShrinkNetworkMessage(3002, "room/player_state_delta")]
public sealed class PlayerStateDeltaEvent : EventBase, IShrinkNetworkMessage, IShrinkNetworkDeltaEvent
{
public string PlayerId { get; set; } = string.Empty;
public int Hp { get; set; }
public string DeltaKey => PlayerId;
public long DeltaVersion { get; set; }
}
```
桥接层会按:
`SessionId + EventType + DeltaKey`
记录已应用版本:
- 新版本进入远端 `EventBus`
- 旧版本或重复版本直接丢弃
这套语义是“事件自己声明 delta 载荷”,不是自动做字段 diff。
### HasResult 请求型事件
如果事件同时满足:
1. `[ShrinkNetworkEvent]`
2. `[ShrinkNetworkMessage(...)]`
3. `[HasResult]`
4. `IShrinkNetworkRequest`
就可以走“远端裁决”语义:
```csharp
[Cancelable]
[HasResult]
[ShrinkNetworkEvent]
[ShrinkNetworkMessage(3010, "room/can_use_skill")]
public sealed class CanUseSkillEvent : EventBase, IShrinkNetworkRequest
{
public string PlayerId { get; set; } = string.Empty;
public string SkillId { get; set; } = string.Empty;
}
```
```csharp
var outcome = await session.RequestEventAsync(new CanUseSkillEvent
{
PlayerId = "10001",
SkillId = "fireball"
});
if (outcome.IsSuccess && outcome.Result == EventResult.ALLOW)
{
// 允许释放技能
}
```
当前回传内容只有:
- `EventResult`
- `IsCanceled`
- `ErrorCode / ErrorMessage`
不会自动回传整个事件对象上其他字段的最终改动。
## 🔧 API 参考
### ContextLoader 接入
```csharp
// ShrinkApp.Starter.Basic 组合根自动加入 ShrinkNetworkEventBusComponent。
// 组件注入 shrink.service.network 后注册,网络提供者撤回时注销。
```
### 显式接入
Standalone 或需要自定义 session 过滤时:
```csharp
service.UseEventBusBridge(new ShrinkNetworkEventBusBridgeOptions
{
SessionFilter = (session, evt) => session.SessionId > 0,
DispatchScheduler = new ShrinkNetworkUnityMainThreadDispatchScheduler()
});
```
如果网络事件数量较大并且需要明确的每帧预算,使用 `ShrinkNetworkDispatchQueue`,在 Unity 主线程的 `Update` 中调用 `PumpAsync(maxItems)`;队列满时默认拒绝,避免无界堆积。
### 发送扩展
```csharp
session.PublishEventAsync<TEvent>(eventArgs, route = null)
session.RequestEventAsync<TEvent>(eventArgs, options = null)
service.BroadcastEventAsync<TEvent>(eventArgs, sessionFilter = null, route = null)
```
## 🏗️ 工作流
```
本地 EventBus.TriggerEvent(evt)
├─ 本地订阅链正常执行
├─ Bridge 监听 OnEventTriggered
├─ 识别为 [ShrinkNetworkEvent]
├─ 自动转发到在线 session
└─ 远端收到后重新进入 EventBus.TriggerEventAsync(evt)
```
请求型事件则额外带回:
```
远端 EventResult / IsCanceled / ErrorCode
```
## ✅ 最佳实践
- 广播型事件和请求型事件分开设计,不要一个类型同时混两种用途
- 高实时状态优先实现 `IShrinkNetworkDeltaEvent`
- 需要“远端裁决”的事件显式标记 `[HasResult]`
- 优先走 `session.PublishEventAsync(...)` / `session.RequestEventAsync(...)`,不要在业务层到处直接调用底层 bridge
## ⚠️ 注意事项
- 核心网络程序集不反射发现桥接包;桥接生命周期只来自组合组件或显式 API
- 当前只回传 `EventResult` 与取消状态,不自动同步事件对象其它字段改动
- 桥接层会抑制“远端收到后再次回传”的回环转发
- 如果你需要按目标 session 精细控制广播范围,使用 `UseEventBusBridge(...)``BroadcastEventAsync(...)`
## 📄 License
[MIT](LICENSE)