◆ 持续开发中 · 当前为非稳定版本,如需抢先体验请通过 GitHub Issues 联系作者

帧同步 Sync

联机方案的 ECS 底层:帧同步(P1 已落地)与状态同步(P3 路线,共用同一底座)。框架负责传输抽象、输入汇流、tick 推进、确定性纪律工具;游戏层只做两件事:继承基类写逻辑、照常用 SystemAPI 取数。

命名空间 HYC.Framework.Sync,Host / Client 两端同码。

实现状态(如实)

阶段状态内容
P1✅ 已落地共享底座 + 帧同步(Driver / InputSystem / LogicSystem / Loopback 双端)
P2❌ 未做帧同步快照([SyncSnapshot] 全量二进制)、断线快照重连追赶、观战中途加入
P3❌ 未做(路线)状态同步:脏检广播 / 应用+实体映射 / B9 前插值 / spawn·despawn 原型 ID / SyncIdService 发号
P4❌ 未做确定性校验和(周期 hash 比对)+ Samples 示例
◈
P1 断链后世界默认永久等齐(MaxStallFrames=0);「重链快照追赶」是 P2 范围。

核心类

类职责
ITransport传输抽象:Send / event MessageReceived / Pump();框架不绑定任何网络库
LoopbackTransport回环传输,单机双端联调与确定性回归(CreatePair / CreateSolo)
BlitIOblittable 类型顺序 memcpy,零分配零反射
SyncRandom确定性随机:splitmix64 + 纯整数位运算
SyncBootstrap装配入口:Install(World, ITransport)
LockstepDriver齐帧推进(写 SyncTickSingleton)
LockstepInputSystem<T>输入汇流基类(SampleLocal / Submit)
LockstepLogicSystem逻辑系统基类(Step(tick) / TryGetInput<T>)

上层接入范式

// ① 输入结构:blittable(unmanaged IComponentData)
public struct MyInput : IComponentData
{
    public float2 Move;
    public uint Buttons;   //  bit0=跳 bit1=攻击 …
}

// ② 输入系统:两组特性必须自带(ILPP 不继承基类特性,见坑 1)
[UpdateInGroup(typeof(UpdateGroup_A0))]
[UpdateBefore(typeof(LockstepDriver))]
public partial class MyInputSystem : LockstepInputSystem<MyInput>
{
    protected override void SampleLocal(int localPlayerId, ref MyInput input)
    {
        input.Move = new float2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
        input.Buttons = (Input.GetKey(KeyCode.Space) ? 1u : 0u);
    }
}

// ③ 逻辑系统:组特性自带;OnUpdate 被封闭,只写 Step
[UpdateInGroup(typeof(UpdateGroup_A3))]
public partial class MySimSystem : LockstepLogicSystem
{
    protected override void Step(int tick)
    {
        float dt = 1f / 60f;                        // 固定步长,禁读 Time
        var rng = SyncRandom.ForTick(tick, 20261019); // 确定性随机
        if (TryGetInput<MyInput>(0, out var local)) { /* 本地玩家 */ }
        if (TryGetInput<MyInput>(1, out var remote)) { /* 对端 */ }
        foreach (var (hp, vel) in SystemAPI.Query<RefRW<Health>, RefRW<Velocity>>())
        {
            // 与单机完全同形的确定性逻辑
        }
    }
}

// ④ bootstrap 装配(FrameworkBootstrapBase 子类 / installer 内)
protected override void RegisterSystems(World world)
{
    HYC.Framework.Sync.SyncBootstrap.Install(world, new MyEnetTransport(...));
}

坑

  • 组特性必须游戏侧具体类自带:Entities ILPP 用 Cecil 直读类型特性、不继承泛型基类特性。缺失症状:输入系统脱离 A0(本地输入总在 Driver 检查后才提交,推进降半速)、逻辑系统脱离固定步长。SyncBootstrap.Install 会在装配期对缺失类 LogError。系统类还必须是 partial。
  • 一个玩法一个输入系统:多个 LockstepInputSystem<T> 会各自解码同一份远端字节;多路输入合并进同一个 TInput 结构。
  • playerId < 32(齐帧用位掩码)。P1 传输模型 = 一条链路两个端点。
  • MaxStallFrames 默认 0 = 永久等齐。
  • 追帧期间同渲染帧复用输入快照:这是正确行为,不要试图在 Step 内重新采样。
  • 边界铁律:同步层 = 传输 / 帧同步 / 状态同步,与输入采集严格分离。