Files
Workspace/Assets/Modules/ShrinkNetwork.Integration.EventBus/README.md
T

253 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)