初学者,自用笔记

基础工具

// 网络消息类型
public enum NetMsgType
{
    RpcRequest,   // RPC请求
    RpcResponse,  // RPC响应
      
    SyncEvent     // 同步事件(特效/声音)
  
     // 通用命令类
    PlayerCommand,      // 玩家操作指令(状态/帧同步通用)
    
    // 状态同步专属
    StateSnapshot,      // 全量状态快照
    StateDelta,         // 增量状态更新
    StateCorrection,    // 强制状态校正
    
    // 帧同步专属
    FrameInputBatch,    // 帧输入包
    FrameAuthPackage,     // 服务端下行:权威帧命令包
    FrameConfirm,       // 帧确认消息
      
}

// 网络消息
public class NetMessage
{
    public NetMsgType Type;// 消息类型
    public int RequestId;
    public int EntityId;// 实体ID
    public string Method;
    public byte[] Payload;// 序列化数据
}

操作指令(命令模式)

// 通用命令接口(状态/帧同步统一封装操作指令)
// 所有玩家操作均封装为命令对象,实现输入与处理解耦
public interface ICommand
{
    // 发起命令的玩家ID
    int PlayerId { get; }
    
    // 命令合法性校验(服务端执行)
    bool IsValid(ISyncContext context);
    
    // 执行命令,修改权威状态(服务端执行)
    void Execute(ISyncContext context);
}

// 命令基类(封装通用逻辑)
public abstract class CommandBase : ICommand
{
    public int PlayerId { get; set; }

    // 默认校验:玩家存在
    public virtual bool IsValid(ISyncContext context)
    {
        return context.TryGetEntity(PlayerId, out _);
    }

    // 具体执行逻辑由子类实现
    public abstract void Execute(ISyncContext context);
}
// 移动命令
public class MoveCommand : CommandBase
{
    public Vector2 MoveInput;

    public override bool IsValid(ISyncContext context)
    {
        // 先跑基类校验,再做移动特定校验(比如是否被眩晕)
        if (!base.IsValid(context)) return false;
        if (!context.TryGetEntity(PlayerId, out var player)) return false;
        return !player.IsStunned;
    }

    public override void Execute(ISyncContext context)
    {
        if (!context.TryGetEntity(PlayerId, out var player)) return;
        
        // 服务端跑物理、算最终位置(权威)
        Vector3 finalPos = PhysicsSystem.CalcValidMove(player.Position, MoveInput, 5f);
        player.Position = finalPos;
        
        // 标记脏数据,等待同步
        context.MarkDirty(PlayerId);
    }
}

// 技能命令
public class SkillCommand : CommandBase
{
    public int SkillId;

    public override bool IsValid(ISyncContext context)
    {
        if (!base.IsValid(context)) return false;
        if (!context.TryGetEntity(PlayerId, out var player)) return false;
        
        // 技能特定校验:CD、蓝量
        var skill = SkillSystem.GetSkill(SkillId);
        return player.MP >= skill.MPCost && player.SkillCDs[SkillId] <= 0;
    }

    public override void Execute(ISyncContext context)
    {
        if (!context.TryGetEntity(PlayerId, out var player)) return;
        
        // 服务端跑战斗结算(权威)
        var result = BattleSystem.CastSkill(player, SkillId);
        
        // 更新所有受影响实体
        foreach (var target in result.HitTargets)
        {
            target.HP = result.FinalHP;
            context.MarkDirty(target.EntityId);
        }
        
        // 扣蓝、刷CD
        player.MP -= result.MPCost;
        player.SkillCDs[SkillId] = result.CD;
        context.MarkDirty(PlayerId);
    }
}

状态同步

基础类


// 服务端权威状态(客户端最终认可的真相)
public struct ServerState
{
    public Vector3 Position;
    public Quaternion Rotation;
    public int HP;
}

// 增量状态(只传变化的字段)
public struct ServerDelta
{
    public bool HasPosition;
    public bool HasRotation;
    public bool HasHP;

    public Vector3 Position;
    public Quaternion Rotation;
    public int HP;

    // 增量转完整状态
    public ServerState ToServerState(NetEntity entity)
    {
        return new ServerState
        {
            Position = HasPosition ? Position : entity.Position,
            Rotation = HasRotation ? Rotation : entity.Rotation,
            HP = HasHP ? HP : entity.HP
        };
    }
}

// 网络实体:被同步的对象(玩家、怪物、NPC)
public class NetEntity
{
    public int EntityId;
    public Vector3 Position;
    public Quaternion Rotation;
    public int HP;
    public float[] SkillCDs;        // 技能CD
    public bool IsStunned;          // 受控状态
    public bool Dirty; // 脏标记:变化后需要同步
}


职责抽象

public interface IStateSync
{
    void RegisterEntity(NetEntity entity);
    void MarkDirty(int entityId);
    void TickSync();
    void OnReceive(NetMessage msg);
    event Action<int, ServerState> OnEntityStateChanged;
}

// 发送职责:什么时候发、发什么
public interface IStateSyncSender
{
    void Tick(float deltaTime, ISyncContext context, IRpcServer rpc);
}

// 接收处理职责:收到消息怎么处理
public interface INetMessageHandler
{
    NetMsgType MessageType { get; }
    void Handle(NetMessage msg, ISyncContext context);
}

// 同步上下文:实体仓库+事件
public interface ISyncContext
{
    IReadOnlyList<NetEntity> Entities { get; }
    bool TryGetEntity(int entityId, out NetEntity entity);
    void AddEntity(NetEntity entity);
    void MarkDirty(int entityId);
    void RaiseStateChanged(int entityId, ServerState state);
}

// 同步上下文实现:管理实体、事件、脏标记
public class SyncContext : ISyncContext
{
    private Dictionary<int, NetEntity> _entities = new();
    public event Action<int, ServerState> StateChanged;

    public SyncContext(ISerializer serializer) => Serializer = serializer;

    public IReadOnlyList<NetEntity> Entities => _entities.Values.ToList();

    public void AddEntity(NetEntity entity) => _entities[entity.EntityId] = entity;

    public bool TryGetEntity(int entityId, out NetEntity entity) => _entities.TryGetValue(entityId, out entity);

    public void MarkDirty(int entityId)
    {
        if (_entities.TryGetValue(entityId, out var e)) e.Dirty = true;
    }

    public void RaiseStateChanged(int entityId, ServerState state) => StateChanged?.Invoke(entityId, state);
}

发送职责层(服务端专用)

1.全量快照

// 定时发送完整状态,用于刚连接、断线重连、定期校准
public class SnapshotSender : IStateSyncSender
{
    private float _timer;
    private float _interval; // 发送间隔

    public SnapshotSender(float interval) => _interval = interval;

    public void Tick(float deltaTime, ISyncContext context, ISocket socket)
    {
        _timer += deltaTime;
        if (_timer < _interval) return;
        _timer = 0;

        // 遍历所有实体,广播全量状态
        foreach (var entity in context.Entities)
        {
            var state = new ServerState
            {
                Position = entity.Position,
                Rotation = entity.Rotation,
                HP = entity.HP,
                MP = entity.MP
            };

            var msg = new NetMessage
            {
                MsgType = NetMsgType.StateSnapshot,
                SenderId = 0,
            };
            rpc.Broadcast(msg);
        }
    }
}

2.增量发送

// 只发送变化的字段,节省流量,高频同步
public class DeltaSender : IStateSyncSender
{
    private float _timer;
    private float _interval;

    public DeltaSender(float interval) => _interval = interval;

    public void Tick(float deltaTime, ISyncContext context, ISocket socket)
    {
        _timer += deltaTime;
        if (_timer < _interval) return;
        _timer = 0;

        // 只同步标记为Dirty(变化)的实体
        foreach (var entity in context.Entities)
        {
            if (!entity.IsDirty) continue;

            var delta = new StateDelta
            {
                HasPosition = true,
                HasRotation = true,
                HasHP = true,
                HasMP = true,
                Position = entity.Position,
                Rotation = entity.Rotation,
                HP = entity.HP,
                MP = entity.MP
            };

            var msg = new NetMessage
            {
                MsgType = NetMsgType.StateDelta,
                SenderId = 0,
            };
            rpc.Broadcast(msg);
            entity.IsDirty = false;
        }
    }
}

3.强制矫正

// 定时强制覆盖客户端状态,防止漂移、误差累积
public class CorrectionSender : IStateSyncSender
{
    private float _timer;
    private float _interval;

    public CorrectionSender(float interval) => _interval = interval;

    public void Tick(float deltaTime, ISyncContext context, ISocket socket)
    {
        _timer += deltaTime;
        if (_timer < _interval) return;
        _timer = 0;

        // 覆盖式广播全量状态
        foreach (var entity in context.Entities)
        {
            var state = new ServerState
            {
                Position = entity.Position,
                Rotation = entity.Rotation,
                HP = entity.HP,
                MP = entity.MP
            };

            var msg = new NetMessage
            {
                MsgType = NetMsgType.StateCorrection,
                SenderId = 0,
            };
            rpc.Broadcast(msg);
        }
    }
}

接收处理层(客户端专用)

1.快照处理

// 处理全量快照:直接覆盖本地所有状态
public class SnapshotHandler : INetMessageHandler
{
    public NetMsgType MessageType => NetMsgType.Snapshot;

    public void Handle(NetMessage msg, ISyncContext context)
    {
        if (!context.TryGetEntity(msg.EntityId, out var entity)) return;


        // 强制覆盖本地
        entity.Position = state.Position;
        entity.Rotation = state.Rotation;
        entity.HP = state.HP;

        // 通知表现层更新
        context.RaiseStateChanged(msg.EntityId, state);
    }
}

2.增量处理

// 处理增量同步:只更新变化字段
public class DeltaHandler : INetMessageHandler
{
    public NetMsgType MessageType => NetMsgType.SyncDelta;

    public void Handle(NetMessage msg, ISyncContext context)
    {
        if (!context.TryGetEntity(msg.EntityId, out var entity)) return;


        // 只更新有变化的字段
        if (delta.HasPosition) entity.Position = delta.Position;
        if (delta.HasRotation) entity.Rotation = delta.Rotation;
        if (delta.HasHP) entity.HP = delta.HP;

        context.RaiseStateChanged(msg.EntityId, delta.ToServerState(entity));
    }
}
  1. 矫正处理
// 处理强制校正:立即覆盖,用于修正预测误差
public class CorrectionHandler : INetMessageHandler
{
    public NetMsgType MessageType => NetMsgType.Correction;

    public void Handle(NetMessage msg, ISyncContext context)
    {
        if (!context.TryGetEntity(msg.EntityId, out var entity)) return;

        // 强制覆盖
        entity.Position = state.Position;
        entity.Rotation = state.Rotation;
        entity.HP = state.HP;

        context.RaiseStateChanged(msg.EntityId, state);
    }
}

服务端

协调器

// 服务端同步协调器:只调度发送器,不处理业务
public class ServerSyncCoordinator
{
    private readonly List<IStateSyncSender> _senders;
    private readonly ISyncContext _context;
    private readonly IRpcServer _rpc;

    // 构造:注入所有依赖
    public ServerSyncCoordinator(List<IStateSyncSender> senders, ISyncContext context, IRpcServer rpc)
    {
        _senders = senders;
        _context = context;
        _rpc = rpc;
    }

    // 每帧驱动:按顺序执行所有发送器
    public void Tick(float deltaTime)
    {
        foreach (var sender in _senders)
        {
            sender.Tick(deltaTime, _context, _rpc);
        }
    }


    // 服务端不处理客户端上行状态
    public void OnReceive(NetMessage msg) { }
}

具体实现

// 服务端状态同步:实现IStateSync门面接口,对外提供统一调用
public class StateSyncServer : IStateSync
{
     // 门面只依赖协调器和 RPC,不直接依赖 Sender
    private readonly IRpcServer _rpc;
    private readonly ServerSyncCoordinator _coordinator;
    private readonly SyncContext _context;
    private readonly PlayerCommandHandler _commandHandler;

    public event Action<int, ServerState> OnEntityStateChanged;

    public StateSyncServer(IRpcServer rpc, ISerializer serializer)
    {
        _rpc = rpc;
        
        // 1. 初始化上下文
        _context = new SyncContext(serializer);
        _context.StateChanged += (id, s) => OnEntityStateChanged?.Invoke(id, s);

        // 2. 初始化命令处理器(服务端唯一能修改权威状态的入口)
        _commandHandler = new PlayerCommandHandler();
        _rpc.RegisterHandler(_commandHandler);

        // 3. 组合发送器(符合 OCP,新增只需加在这里)
        var senders = new List<IStateSyncSender>
        {
            new SnapshotSender(1f),
            new DeltaSender(0.1f),
            new CorrectionSender(0.5f)
        };

        // 4. 初始化协调器(门面不直接调度 Sender,全交给协调器)
        _coordinator = new ServerSyncCoordinator(senders, _context, _rpc);
    }

    // 门面只做简单转发
    public void RegisterEntity(NetEntity entity) => _context.AddEntity(entity);
    public void Tick(float deltaTime)
    {
        // 先执行命令,修改权威状态
        _commandHandler.Tick(deltaTime, _context);
        // 再交给协调器调度发送器
        _coordinator.Tick(deltaTime);
    }
    public void Start(string ip, int port) => _rpc.Start(ip, port);
    public void Stop() => _rpc.Stop();
}

客户端

协调器

// 客户端同步协调器:只调度消息处理器
public class ClientSyncCoordinator
{
    private readonly Dictionary<NetMsgType, IMessageHandler> _handlers;
    private readonly ISyncContext _context;

    // 构造:注入所有依赖
    public ClientSyncCoordinator(List<IMessageHandler> handlers, ISyncContext context)
    {
        _handlers = handlers.ToDictionary(h => h.HandledType);
        _context = context;
    }

    // 客户端协调器 Tick 为空(无需主动发送逻辑)
    public void Tick(float deltaTime) { }

    // 收到消息:自动按类型分发给对应 Handler
    public void OnReceive(NetMessage msg)
    {
        if (_handlers.TryGetValue(msg.MsgType, out var handler))
        {
            handler.Handle(msg, _context);
        }
    }

具体实现

// 客户端状态同步:实现IStateSync门面接口,对外统一调用
public class StateSyncClient : IStateSync
{
    // 门面只依赖协调器和 RPC,不直接依赖 Handler
    private readonly IRpcClient _rpc;
    private readonly ClientSyncCoordinator _coordinator;
    private readonly SyncContext _context;
    private readonly int _localPlayerId;

    public event Action<int, ServerState> OnEntityStateChanged;
    public event Action<bool> OnConnectStateChanged;

    public StateSyncClient(IRpcClient rpc, ISerializer serializer, int localPlayerId)
    {
        _rpc = rpc;
        _localPlayerId = localPlayerId;

        // 1. 初始化上下文
        _context = new SyncContext(serializer);
        _context.StateChanged += (id, s) => OnEntityStateChanged?.Invoke(id, s);
        _rpc.OnConnectStateChanged += (connected) => OnConnectStateChanged?.Invoke(connected);

        // 2. 组合消息处理器(符合 OCP,新增只需加在这里)
        var handlers = new List<IMessageHandler>
        {
            new SnapshotHandler(),
            new DeltaHandler(),
            new CorrectionHandler()
        };

        // 3. 初始化协调器(门面不直接分发消息,全交给协调器)
        _coordinator = new ClientSyncCoordinator(handlers, _context);

        // 4. 把协调器注册给 RPC,收到消息直接转协调器
        _rpc.RegisterHandler(new ProxyHandler(_coordinator));
    }

    // 门面只做简单转发
    public void RegisterEntity(NetEntity entity) => _context.AddEntity(entity);
    public void SendCommand(ICommand cmd)
    {
        var msg = new NetMessage
        {
            MsgType = NetMsgType.PlayerCommand,
            SenderId = _localPlayerId,
            Payload = _context.Serializer.Serialize(cmd)
        };
        _rpc.Send(msg);
    }
    public void Tick(float deltaTime) => _coordinator.Tick(deltaTime);
    public void Connect(string ip, int port) => _rpc.Connect(ip, port);
    public void Disconnect() => _rpc.Disconnect();

    // 内部代理类:把 RPC 消息转发给协调器
    private class ProxyHandler : IMessageHandler
    {
        private readonly ClientSyncCoordinator _coordinator;
        public NetMsgType HandledType => 0; // 代理所有消息
        public ProxyHandler(ClientSyncCoordinator coordinator) => _coordinator = coordinator;
        public void Handle(NetMessage msg, ISyncContext context) => _coordinator.OnReceive(msg);
    }
}

帧同步

基础类


// 迟到输入处理策略
public enum LateInputPolicy
{
    Drop,               // 直接丢弃
    TreatAsEmpty,       // 当作空操作
    RollbackIfPossible   // 允许回滚则回滚
}

// 帧同步配置
public class FrameSyncPolicy
{
    public int MaxWaitFrames = 2;          // 本帧最多等待几帧输入
    public int MaxRollbackFrames = 5;      // 最多允许回滚多少帧
    public int SnapshotInterval = 5;       // 每隔多少帧保存一次快照
    public int SnapshotKeepFrames = 30;    // 保留多少帧快照
    public LateInputPolicy LatePolicy = LateInputPolicy.RollbackIfPossible;
}

// 帧快照(回滚用)
public class FrameStateSnapshot
{
    public int FrameId;
    public Dictionary<int, FrameEntityState> EntityStates = new();
}

// 帧同步实体状态
public class FrameEntityState
{
    public int EntityId;
    public Vector3 Position;
    public Quaternion Rotation;
    public int HP;
}


// 帧命令包:一帧内单个玩家的所有命令
public class FrameCommandPackage
{
    public int FrameId;
    public int PlayerId;
    public List<ICommand> Commands = new();
}

// 权威帧包:服务端广播的一帧完整数据
public class AuthFramePackage
{
    public int FrameId;
    public List<FrameCommandPackage> AllPlayerCommands = new();
}

// 帧同步上下文接口
public interface IFrameContext
{
    IReadOnlyList<FrameEntityState> Entities { get; }
    bool TryGetEntity(int entityId, out FrameEntityState entity);
    void AddEntity(FrameEntityState entity);
    void ClearEntities();
    void RaiseStateChanged(int entityId, FrameEntityState state);
}

// 帧同步上下文默认实现
public class FrameContext : IFrameContext
{
    private Dictionary<int, FrameEntityState> _entities = new();
    public ISerializer Serializer { get; }
    public event Action<int, FrameEntityState> StateChanged;

    public FrameContext(ISerializer serializer) => Serializer = serializer;

    public IReadOnlyList<FrameEntityState> Entities => _entities.Values.ToList();

    public void AddEntity(FrameEntityState entity) => _entities[entity.EntityId] = entity;

    public bool TryGetEntity(int entityId, out FrameEntityState entity) => _entities.TryGetValue(entityId, out entity);

    public void ClearEntities() => _entities.Clear();

    public void RaiseStateChanged(int entityId, FrameEntityState state) => StateChanged?.Invoke(entityId, state);
}

职责抽象


// 服务端职责接口:定义服务端需要做的核心事情
public interface IFrameCommandCollector
{
    // 收集客户端发来的命令包
    void CollectCommandPackage(FrameCommandPackage package);
    // 获取指定帧的所有玩家命令(补全缺失)
    List<FrameCommandPackage> GetFrameCommands(int frameId, HashSet<int> allPlayers);
}

public interface IFrameAuthBroadcaster
{
    // 打包并广播权威帧给所有客户端
    void BroadcastAuthFrame(AuthFramePackage authFrame, IRpcServer rpc, ISerializer serializer);
}

// 客户端职责接口:定义客户端需要做的核心事情
public interface IFrameCommandCapturer
{
    // 采集玩家输入,封装成命令包
    FrameCommandPackage CaptureCommands(int frameId, int localPlayerId);
}

public interface IFrameCommandSender
{
    // 发送本地命令包给服务端
    void SendCommandPackage(FrameCommandPackage package, IRpcClient rpc, ISerializer serializer);
}

public interface IFrameCommandExecutor
{
    // 执行单个玩家的命令包
    void ExecuteCommandPackage(FrameCommandPackage package, IFrameContext context);
    // 执行完整的权威帧(所有玩家)
    void ExecuteAuthFrame(AuthFramePackage authFrame, IFrameContext context);
}

public interface IFrameSnapshotManager
{
    // 按间隔保存快照
    void SaveSnapshotIfNeeded(int frameId, IFrameContext context);
    // 找指定帧之前最近的快照
    FrameStateSnapshot FindLatestSnapshotBefore(int frameId);
    // 从快照恢复状态
    void RestoreSnapshot(FrameStateSnapshot snapshot, IFrameContext context);
    // 清理过期快照
    void CleanupOldSnapshots(int currentFrame);
}

public interface IFrameRollbackExecutor
{
    // 执行回滚+重放核心逻辑
    void RollbackAndReplay(
        FrameStateSnapshot baseSnapshot,
        int conflictFrameId,
        int currentFrameId,
        Dictionary<int, AuthFramePackage> authFrameCache,
        IFrameContext context,
        IFrameCommandExecutor commandExecutor);
}

职责实现

服务端职责

// 帧命令收集器:收集、补全、处理迟到输入
public class FrameCommandCollector : IFrameCommandCollector
{
    private readonly FrameSyncPolicy _policy;
    // 按帧存命令:frameId -> (playerId -> package)
    private readonly Dictionary<int, Dictionary<int, FrameCommandPackage>> _frameCommands = new();
    // 已广播的最大帧号
    private int _maxBroadcastFrameId;

    public FrameCommandCollector(FrameSyncPolicy policy)
    {
        _policy = policy;
    }

    // 收到客户端命令包
    public void CollectCommandPackage(FrameCommandPackage package)
    {
        // 迟到输入:按策略处理
        if (package.FrameId <= _maxBroadcastFrameId)
        {
            HandleLateInput(package);
            return;
        }

        // 正常输入:存入缓存
        if (!_frameCommands.ContainsKey(package.FrameId))
            _frameCommands[package.FrameId] = new Dictionary<int, FrameCommandPackage>();
        
        _frameCommands[package.FrameId][package.PlayerId] = package;
    }

    // 处理服务端视角的迟到输入
    private void HandleLateInput(FrameCommandPackage package)
    {
        switch (_policy.LatePolicy)
        {
            case LateInputPolicy.Drop:
                return;
            case LateInputPolicy.TreatAsEmpty:
                return;
            case LateInputPolicy.RollbackIfPossible:
                // 在回滚范围内:存下来用于补帧
                if (package.FrameId >= _maxBroadcastFrameId - _policy.MaxRollbackFrames)
                {
                    if (!_frameCommands.ContainsKey(package.FrameId))
                        _frameCommands[package.FrameId] = new Dictionary<int, FrameCommandPackage>();
                    
                    _frameCommands[package.FrameId][package.PlayerId] = package;
                }
                break;
        }
    }

    // 获取当前帧的所有玩家命令(补全缺失)
    public List<FrameCommandPackage> GetFrameCommands(int frameId, HashSet<int> allPlayers)
    {
        // 更新已广播的最大帧号
        _maxBroadcastFrameId = Math.Max(_maxBroadcastFrameId, frameId);

        if (!_frameCommands.TryGetValue(frameId, out var playerCommands))
            playerCommands = new Dictionary<int, FrameCommandPackage>();

        // 补全没提交输入的玩家(给空命令包)
        foreach (int playerId in allPlayers)
        {
            if (!playerCommands.ContainsKey(playerId))
            {
                playerCommands[playerId] = new FrameCommandPackage
                {
                    FrameId = frameId,
                    PlayerId = playerId,
                    Commands = new List<ICommand>()
                };
            }
        }

        return playerCommands.Values.ToList();
    }
}

// 权威帧广播器:打包并广播
public class FrameAuthBroadcaster : IFrameAuthBroadcaster
{
    public void BroadcastAuthFrame(AuthFramePackage authFrame, IRpcServer rpc, ISerializer serializer)
    {
        var msg = new NetMessage
        {
            MsgType = NetMsgType.FrameAuthPackage,
            SenderId = 0,
            Payload = serializer.Serialize(authFrame)
        };
        rpc.Broadcast(msg);
    }
}

客户端职责

// 命令采集器:采集玩家输入,封装成命令
public class FrameCommandCapturer : IFrameCommandCapturer
{
    public FrameCommandPackage CaptureCommands(int frameId, int localPlayerId)
    {
        var package = new FrameCommandPackage
        {
            FrameId = frameId,
            PlayerId = localPlayerId
        };

        // 采集移动输入
        Vector2 moveInput = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
        if (moveInput != Vector2.zero)
        {
            package.Commands.Add(new MoveCommand
            {
                PlayerId = localPlayerId,
                MoveInput = moveInput
            });
        }

        // 采集攻击输入(J键)
        if (Input.GetKeyDown(KeyCode.J))
        {
            package.Commands.Add(new AttackCommand
            {
                PlayerId = localPlayerId,
                TargetId = 2 // 示例目标
            });
        }

        // 采集技能输入(K键)
        if (Input.GetKeyDown(KeyCode.K))
        {
            package.Commands.Add(new SkillCommand
            {
                PlayerId = localPlayerId,
                SkillId = 1001
            });
        }

        return package;
    }
}

// 命令发送器:发送本地命令给服务端
public class FrameCommandSender : IFrameCommandSender
{
    public void SendCommandPackage(FrameCommandPackage package, IRpcClient rpc, ISerializer serializer)
    {
        var msg = new NetMessage
        {
            MsgType = NetMsgType.FrameCommandPackage,
            SenderId = package.PlayerId,
            Payload = serializer.Serialize(package)
        };
        rpc.Send(msg);
    }
}

// 命令执行器:确定性执行命令
public class FrameCommandExecutor : IFrameCommandExecutor
{
    // 执行单个玩家的命令包
    public void ExecuteCommandPackage(FrameCommandPackage package, IFrameContext context)
    {
        // 按顺序执行一帧内的所有命令
        foreach (var command in package.Commands)
        {
            if (command.IsValid(context))
            {
                command.Execute(context);
            }
        }
    }

    // 执行完整的权威帧(所有玩家)
    public void ExecuteAuthFrame(AuthFramePackage authFrame, IFrameContext context)
    {
        foreach (var package in authFrame.AllPlayerCommands)
        {
            ExecuteCommandPackage(package, context);
        }
    }
}

// 快照管理器:保存、查找、恢复、清理
public class FrameSnapshotManager : IFrameSnapshotManager
{
    private readonly FrameSyncPolicy _policy;
    // 帧号 -> 快照
    private readonly Dictionary<int, FrameStateSnapshot> _snapshotCache = new();

    public FrameSnapshotManager(FrameSyncPolicy policy)
    {
        _policy = policy;
    }

    // 按间隔保存快照
    public void SaveSnapshotIfNeeded(int frameId, IFrameContext context)
    {
        if (frameId % _policy.SnapshotInterval != 0) return;
        SaveSnapshot(frameId, context);
        CleanupOldSnapshots(frameId);
    }

    // 保存单帧快照(深度克隆)
    private void SaveSnapshot(int frameId, IFrameContext context)
    {
        var snapshot = new FrameStateSnapshot
        {
            FrameId = frameId,
            EntityStates = context.Entities.ToDictionary(
                e => e.EntityId, 
                e => new FrameEntityState
                {
                    EntityId = e.EntityId,
                    Position = e.Position,
                    Rotation = e.Rotation,
                    HP = e.HP,
                    MP = e.MP,
                    SkillCDs = e.SkillCDs?.ToArray(),
                    IsStunned = e.IsStunned
                })
        };

        _snapshotCache[frameId] = snapshot;
    }

    // 找指定帧之前最近的快照
    public FrameStateSnapshot FindLatestSnapshotBefore(int frameId)
    {
        FrameStateSnapshot bestSnapshot = null;
        foreach (var (snapFrameId, snapshot) in _snapshotCache)
        {
            if (snapFrameId <= frameId && (bestSnapshot == null || snapFrameId > bestSnapshot.FrameId))
            {
                bestSnapshot = snapshot;
            }
        }
        return bestSnapshot;
    }

    // 从快照恢复状态
    public void RestoreSnapshot(FrameStateSnapshot snapshot, IFrameContext context)
    {
        if (snapshot == null) return;

        // 清空当前实体
        context.ClearEntities();

        // 从快照恢复
        foreach (var (entityId, entityState) in snapshot.EntityStates)
        {
            context.AddEntity(new FrameEntityState
            {
                EntityId = entityState.EntityId,
                Position = entityState.Position,
                Rotation = entityState.Rotation,
                HP = entityState.HP,
                MP = entityState.MP,
                SkillCDs = entityState.SkillCDs?.ToArray(),
                IsStunned = entityState.IsStunned
            });
        }
    }

    // 清理过期快照
    public void CleanupOldSnapshots(int currentFrame)
    {
        var expiredFrames = _snapshotCache.Keys
            .Where(frameId => currentFrame - frameId > _policy.SnapshotKeepFrames)
            .ToList();

        foreach (var frameId in expiredFrames)
        {
            _snapshotCache.Remove(frameId);
        }
    }
}

// 回滚执行器:执行回滚+重放
public class FrameRollbackExecutor : IFrameRollbackExecutor
{
    private readonly FrameSyncPolicy _policy;

    public FrameRollbackExecutor(FrameSyncPolicy policy)
    {
        _policy = policy;
    }

    public void RollbackAndReplay(
        FrameStateSnapshot baseSnapshot,
        int conflictFrameId,
        int currentFrameId,
        Dictionary<int, AuthFramePackage> authFrameCache,
        IFrameContext context,
        IFrameCommandExecutor commandExecutor)
    {
        // 超出回滚范围:直接放弃
        if (currentFrameId - baseSnapshot.FrameId > _policy.MaxRollbackFrames) return;

        // 1. 恢复到快照状态
        baseSnapshot.RestoreSnapshot(baseSnapshot, context);

        // 2. 从快照帧+1开始,用权威帧重放到当前帧
        for (int frameId = baseSnapshot.FrameId + 1; frameId <= currentFrameId; frameId++)
        {
            if (authFrameCache.TryGetValue(frameId, out var authFrame))
            {
                // 有权威帧:用权威帧执行
                commandExecutor.ExecuteAuthFrame(authFrame, context);
            }
            else
            {
                // 无权威帧:执行空操作
                commandExecutor.ExecuteCommandPackage(new FrameCommandPackage
                {
                    FrameId = frameId,
                    PlayerId = 0,
                    Commands = new List<ICommand>()
                }, context);
            }
        }
    }
}

服务端

职责抽象

// 服务端帧同步门面接口:给上层业务用的极简接口
public interface IFrameSyncServer
{
    void Start(string ip, int port, HashSet<int> allPlayerIds);
    void Stop();
    void Tick(float deltaTime);
    event Action<int, List<FrameCommandPackage>> OnAuthFrameBroadcast;
}

协调器

// 服务端帧同步协调器:架构大脑,只调度不处理业务
public class ServerFrameSyncCoordinator
{
    private readonly IFrameCommandCollector _commandCollector;
    private readonly IFrameAuthBroadcaster _authBroadcaster;
    private readonly IRpcServer _rpc;
    private readonly HashSet<int> _allPlayers;
    private readonly FrameSyncPolicy _policy;
    private int _currentFrame;

    // 构造:注入所有依赖
    public ServerFrameSyncCoordinator(
        IFrameCommandCollector commandCollector,
        IFrameAuthBroadcaster authBroadcaster,
        IRpcServer rpc,
        ISerializer serializer,
        HashSet<int> allPlayers,
        FrameSyncPolicy policy)
    {
        _commandCollector = commandCollector;
        _authBroadcaster = authBroadcaster;
        _rpc = rpc;
        _serializer = serializer;
        _allPlayers = allPlayers;
        _policy = policy;
    }

    // 每帧驱动:收集命令 → 打包 → 广播
    public void Tick(float deltaTime)
    {
        _currentFrame++;
        
        // 获取当前帧所有玩家命令
        var allCommands = _commandCollector.GetFrameCommands(_currentFrame, _allPlayers);
        
        // 打包权威帧
        var authFrame = new AuthFramePackage
        {
            FrameId = _currentFrame,
            AllPlayerCommands = allCommands
        };
        
        // 广播出去
        _authBroadcaster.BroadcastAuthFrame(authFrame, _rpc, _serializer);
    }

    // 收到客户端命令:转交给收集器
    public void OnReceiveCommandPackage(FrameCommandPackage package)
    {
        _commandCollector.CollectCommandPackage(package);
    }
}

具体实现

// 服务端门面实现:初始化依赖、转发调用
public class FrameSyncServer : IFrameSyncServer
{
    private readonly IRpcServer _rpc;
    private ServerFrameSyncCoordinator _coordinator;
    private readonly FrameSyncPolicy _policy;

    public event Action<int, List<FrameCommandPackage>> OnAuthFrameBroadcast;

    public FrameSyncServer(IRpcServer rpc, ISerializer serializer, FrameSyncPolicy policy)
    {
        _rpc = rpc;
        _serializer = serializer;
        _policy = policy;
    }

    // 启动服务:初始化所有组件
    public void Start(string ip, int port, HashSet<int> allPlayerIds)
    {
        var collector = new FrameCommandCollector(_policy);
        var broadcaster = new FrameAuthBroadcaster();
        
        // 创建协调器
        _coordinator = new ServerFrameSyncCoordinator(
            collector,
            broadcaster,
            _rpc,
            _serializer,
            allPlayerIds,
            _policy
        );

        // 注册网络消息处理器
        _rpc.RegisterHandler(new ServerCommandHandler(_coordinator, _serializer));
        _rpc.Start(ip, port);
    }

    public void Stop() => _rpc.Stop();

    // 每帧驱动:转发给协调器
    public void Tick(float deltaTime) => _coordinator.Tick(deltaTime);

    // 内部类:服务端命令消息处理器
    private class ServerCommandHandler : IMessageHandler
    {
        private readonly ServerFrameSyncCoordinator _coordinator;
        private readonly ISerializer _serializer;
        public NetMsgType HandledType => NetMsgType.FrameCommandPackage;

        public ServerCommandHandler(ServerFrameSyncCoordinator coordinator, ISerializer serializer)
        {
            _coordinator = coordinator;
            _serializer = serializer;
        }

        // 收到客户端命令:转交给协调器
        public void Handle(NetMessage msg, object context)
        {
            var package = _serializer.Deserialize<FrameCommandPackage>(msg.Payload);
            _coordinator.OnReceiveCommandPackage(package);
        }
    }
}

客户端

职责抽象

// 客户端帧同步门面接口:给上层业务用的极简接口
public interface IFrameSyncClient
{
    void Connect(string ip, int port);
    void Disconnect();
    void RegisterEntity(FrameEntityState entity);
    void Tick(float deltaTime);
    event Action<int, FrameEntityState> OnEntityStateChanged;
    event Action<bool> OnConnectStateChanged;
}

协调器

// 客户端帧同步协调器:架构大脑,串联所有客户端逻辑
public class ClientFrameSyncCoordinator
{
    private readonly IFrameCommandCapturer _commandCapturer;
    private readonly IFrameCommandSender _commandSender;
    private readonly IFrameCommandExecutor _commandExecutor;
    private readonly IFrameSnapshotManager _snapshotManager;
    private readonly IFrameRollbackExecutor _rollbackExecutor;
    private readonly IRpcClient _rpc;
    private readonly FrameSyncPolicy _policy;
    private readonly IFrameContext _frameContext;
    private readonly int _localPlayerId;

    private int _currentFrame;
    // 本地命令缓存(用于回滚)
    private readonly Dictionary<int, FrameCommandPackage> _localCommandBuffer = new();
    // 权威帧缓存(用于重放)
    private readonly Dictionary<int, AuthFramePackage> _authFrameBuffer = new();

    // 状态变更事件(给表现层用)
    public event Action<int, FrameEntityState> OnEntityStateChanged;

    // 构造:注入所有依赖
    public ClientFrameSyncCoordinator(
        IFrameCommandCapturer commandCapturer,
        IFrameCommandSender commandSender,
        IFrameCommandExecutor commandExecutor,
        IFrameSnapshotManager snapshotManager,
        IFrameRollbackExecutor rollbackExecutor,
        IRpcClient rpc,
        ISerializer serializer,
        FrameSyncPolicy policy,
        IFrameContext frameContext,
        int localPlayerId)
    {
        _commandCapturer = commandCapturer;
        _commandSender = commandSender;
        _commandExecutor = commandExecutor;
        _snapshotManager = snapshotManager;
        _rollbackExecutor = rollbackExecutor;
        _rpc = rpc;
        _serializer = serializer;
        _policy = policy;
        _frameContext = frameContext;
        _localPlayerId = localPlayerId;

        // 转发状态变更事件
        ((FrameContext)_frameContext).StateChanged += (id, s) => OnEntityStateChanged?.Invoke(id, s);
    }

    // 每帧驱动:采集 → 预测 → 存快照 → 发命令
    public void Tick(float deltaTime)
    {
        _currentFrame++;

        // 1. 采集本地输入
        var localPackage = _commandCapturer.CaptureCommands(_currentFrame, _localPlayerId);
        _localCommandBuffer[_currentFrame] = localPackage;

        // 2. 本地乐观预测执行
        _commandExecutor.ExecuteCommandPackage(localPackage, _frameContext);

        // 3. 按间隔保存快照
        _snapshotManager.SaveSnapshotIfNeeded(_currentFrame, _frameContext);

        // 4. 发送命令给服务端
        _commandSender.SendCommandPackage(localPackage, _rpc, _serializer);
    }

    // 收到服务端权威帧:校验 → 回滚(如果需要)
    public void OnReceiveAuthFrame(AuthFramePackage authFrame)
    {
        _authFrameBuffer[authFrame.FrameId] = authFrame;

        // 未来帧:先存着
        if (authFrame.FrameId > _currentFrame) return;

        // 迟到帧:按策略处理
        if (authFrame.FrameId <= _currentFrame)
        {
            HandleLateAuthFrame(authFrame);
            return;
        }

        // 正常帧:校验是否一致
        if (!CheckCommandMatch(authFrame))
        {
            // 在回滚范围内:执行回滚重放
            if (authFrame.FrameId >= _currentFrame - _policy.MaxRollbackFrames)
            {
                var snapshot = _snapshotManager.FindLatestSnapshotBefore(authFrame.FrameId);
                if (snapshot != null)
                {
                    _rollbackExecutor.RollbackAndReplay(
                        snapshot,
                        authFrame.FrameId,
                        _currentFrame,
                        _authFrameBuffer,
                        _frameContext,
                        _commandExecutor
                    );
                }
            }
            else
            {
                // 超出范围:强制恢复最新快照
                HandleTooLateAuthFrame(authFrame);
            }
        }
    }

    // 处理迟到权威帧(3种策略)
    private void HandleLateAuthFrame(AuthFramePackage authFrame)
    {
        switch (_policy.LatePolicy)
        {
            case LateInputPolicy.Drop:
                return; // 直接丢弃
            case LateInputPolicy.TreatAsEmpty:
                return; // 当作空操作
            case LateInputPolicy.RollbackIfPossible:
                // 在范围内则回滚
                if (authFrame.FrameId >= _currentFrame - _policy.MaxRollbackFrames)
                {
                    var snapshot = _snapshotManager.FindLatestSnapshotBefore(authFrame.FrameId);
                    if (snapshot != null)
                    {
                        _rollbackExecutor.RollbackAndReplay(
                            snapshot,
                            authFrame.FrameId,
                            _currentFrame,
                            _authFrameBuffer,
                            _frameContext,
                            _commandExecutor
                        );
                    }
                }
                break;
        }
    }

    // 校验本地命令与权威命令是否一致
    private bool CheckCommandMatch(AuthFramePackage authFrame)
    {
        if (!_localCommandBuffer.TryGetValue(authFrame.FrameId, out var localPackage)) 
            return false;
        
        var authPackage = authFrame.AllPlayerCommands.FirstOrDefault(p => p.PlayerId == _localPlayerId);
        if (authPackage == null) return false;

        // 简单校验:命令数量和类型一致
        if (localPackage.Commands.Count != authPackage.Commands.Count) return false;
        for (int i = 0; i < localPackage.Commands.Count; i++)
        {
            if (localPackage.Commands[i].GetType() != authPackage.Commands[i].GetType())
                return false;
        }

        return true;
    }

    // 处理超出回滚范围的过期帧
    private void HandleTooLateAuthFrame(AuthFramePackage authFrame)
    {
        var latestSnapshot = _snapshotManager.FindLatestSnapshotBefore(_currentFrame);
        if (latestSnapshot != null)
        {
            _snapshotManager.RestoreSnapshot(latestSnapshot, _frameContext);
        }
    }
}

具体实现

// 客户端门面实现:初始化依赖、转发调用
public class FrameSyncClient : IFrameSyncClient
{
    private readonly IRpcClient _rpc;
    private readonly ClientFrameSyncCoordinator _coordinator;
    private readonly FrameSyncPolicy _policy;
    private readonly IFrameContext _frameContext;
    private readonly int _localPlayerId;

    public event Action<int, FrameEntityState> OnEntityStateChanged;
    public event Action<bool> OnConnectStateChanged;

    public FrameSyncClient(IRpcClient rpc, ISerializer serializer, FrameSyncPolicy policy, int localPlayerId)
    {
        _rpc = rpc;
        _serializer = serializer;
        _policy = policy;
        _localPlayerId = localPlayerId;
        _frameContext = new FrameContext(serializer);

        // 创建所有职责组件
        var capturer = new FrameCommandCapturer();
        var sender = new FrameCommandSender();
        var executor = new FrameCommandExecutor();
        var snapshotManager = new FrameSnapshotManager(policy);
        var rollbackExecutor = new FrameRollbackExecutor(policy);

        // 创建协调器
        _coordinator = new ClientFrameSyncCoordinator(
            capturer,
            sender,
            executor,
            snapshotManager,
            rollbackExecutor,
            rpc,
            serializer,
            policy,
            _frameContext,
            localPlayerId
        );

        // 转发事件
        _coordinator.OnEntityStateChanged += (id, s) => OnEntityStateChanged?.Invoke(id, s);
        _rpc.OnConnectStateChanged += (connected) => OnConnectStateChanged?.Invoke(connected);
        // 注册网络消息处理器
        _rpc.RegisterHandler(new ClientAuthHandler(_coordinator, _serializer));
    }

    public void Connect(string ip, int port) => _rpc.Connect(ip, port);
    public void Disconnect() => _rpc.Disconnect();
    public void RegisterEntity(FrameEntityState entity) => _frameContext.AddEntity(entity);
    public void Tick(float deltaTime) => _coordinator.Tick(deltaTime);

    // 内部类:客户端权威帧消息处理器
    private class ClientAuthHandler : IMessageHandler
    {
        private readonly ClientFrameSyncCoordinator _coordinator;
        private readonly ISerializer _serializer;
        public NetMsgType HandledType => NetMsgType.FrameAuthPackage;

        public ClientAuthHandler(ClientFrameSyncCoordinator coordinator, ISerializer serializer)
        {
            _coordinator = coordinator;
            _serializer = serializer;
        }

        // 收到权威帧:转交给协调器
        public void Handle(NetMessage msg, object context)
        {
            var authFrame = _serializer.Deserialize<AuthFramePackage>(msg.Payload);
            _coordinator.OnReceiveAuthFrame(authFrame);
        }
    }
}

同步优化(使用桥接模式)

通用结构

// 服务端发来的一帧完整状态
public struct ServerState
{
    public Vector3 Position;    // 位置
    public Quaternion Rotation; // 旋转
    public Vector3 Velocity;    // 线速度(预测用)
    public Vector3 AngularVel;  // 角速度(预测用)
}

// 客户端最终要显示的状态
public struct DisplayState
{
    public Vector3 Position;
    public Quaternion Rotation;
}

// 同步配置:阈值、平滑速度等
public class SyncConfig
{
    public float PositionErrorThreshold = 0.1f;
    public float SmoothTime = 0.15f;
    public float RollbackSpeed = 15f;
}

// 同步目标类型
public enum SyncTargetType
{
    Position,
    Rotation
}

// 同步运行时上下文(所有算法共享的环境)
public class SyncContext
{
    // 服务端状态
    public ServerState ServerState { get; set; }

    // 当前显示状态
    public DisplayState CurrentDisplay { get; set; }

    // 配置
    public SyncConfig Config { get; set; }

    // 所有同步目标:字典存储(位置、旋转、动画...)
    private readonly Dictionary<string, ISyncTarget> _targets = new();

    // 添加同步目标
    public void AddTarget(string key, ISyncTarget target) => _targets[key] = target;

    // 获取同步目标
    public ISyncTarget GetTarget(string key) => _targets.TryGetValue(key, out var t) ? t : null;
}

// 算法需要的共享数据(只传必要的)
public struct SyncInput
{
    public ServerState ServerState; // 服务端状态
    public SyncConfig Config;       // 配置
    public float DeltaTime;          // 帧时间
}

同步对象

// 同步对象接口:位置、旋转都实现它
// 同步目标接口
public interface ISyncTarget
{
    SyncTargetType Type { get; }                    // 自身类型
    DisplayState GetCurrent();                       // 获取当前状态
    void Update(DisplayState state);                 // 更新状态
    void MergeToDisplayState(ref DisplayState state); // 合并到最终状态

    // 自带算法
    IPredictionAlgorithm Prediction { get; }
    ISmoothingAlgorithm Smoothing { get; }
    IAuthorityStrategy Authority { get; }
}

// 位置同步对象
public class PositionSync : ISyncTarget
{
    private Vector3 _pos;

    public SyncTargetType Type => SyncTargetType.Position;
    // 绑定具体算法
    public IPredictionAlgorithm Prediction => new PositionVelocityPrediction();
    public ISmoothingAlgorithm Smoothing => new PositionShadowFollow();
    public IAuthorityStrategy Authority => new PositionSmoothRollback(); // 可选:PositionOverride

    public DisplayState GetCurrent() => new DisplayState { Position = _pos };
    public void Update(DisplayState s) => _pos = s.Position;
    public void MergeToDisplayState(ref DisplayState state) => state.Position = _pos;
}

// 旋转同步对象
public class RotationSync : ISyncTarget
{
    private Quaternion _rot = Quaternion.identity;

    public SyncTargetType Type => SyncTargetType.Rotation;
     public IPredictionAlgorithm Prediction => new RotationAngularPrediction();
    public ISmoothingAlgorithm Smoothing => new RotationShadowFollow();
    public IAuthorityStrategy Authority => new RotationSmoothRollback();

    public DisplayState GetCurrent() => new DisplayState { Rotation = _rot };
    public void Update(DisplayState s) => _rot = s.Rotation;
    public void MergeToDisplayState(ref DisplayState state) => state.Rotation = _rot;
}

同步算法

导航推测

// 预测算法接口
public interface IPredictionAlgorithm 
{ void Predict(ISyncTarget target, SyncInput input); }

// 1. 导航预测:基于速度的匀速插值
public class PositionVelocityPrediction : IPredictionAlgorithm
{
    public void Predict(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Position += input.ServerState.Velocity * input.DeltaTime;
        target.Update(curr);
    }
}

// 2. 导航预测:基于角速度的插值
public class RotationAngularPrediction : IPredictionAlgorithm
{
    public void Predict(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        var av = input.ServerState.AngularVel;
        float angle = av.magnitude * input.DeltaTime;
        Vector3 axis = av.normalized;
        curr.Rotation = Quaternion.AngleAxis(angle, axis) * curr.Rotation;
        target.Update(curr);
    }
}

影子追随

// 平滑/影子追随算法接口
public interface ISmoothingAlgorithm 
{ void Smooth(ISyncTarget target, SyncInput input); }

// 1. 影子追随:SmoothDamp 平滑
public class PositionShadowFollow : ISmoothingAlgorithm
{
    private Vector3 _smoothVel;
    public void Smooth(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Position = Vector3.SmoothDamp(
            curr.Position, 
            input.ServerState.Position, 
            ref _smoothVel, 
            input.Config.SmoothTime, 
            100, 
            input.DeltaTime);
        target.Update(curr);
    }
}

// 2. 影子追随:Lerp 平滑
public class RotationShadowFollow : ISmoothingAlgorithm
{
    public void Smooth(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Rotation = Quaternion.Lerp(
            curr.Rotation, 
            input.ServerState.Rotation, 
            input.DeltaTime * 8);
        target.Update(curr);
    }
}

权威冲突策略

// 权威策略接口
public interface IAuthorityStrategy 
{ void Resolve(ISyncTarget target, SyncInput input); }

// 策略1:直接覆盖
public class PositionOverride : IAuthorityStrategy
{
    public void Resolve(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Position = input.ServerState.Position;
        target.Update(curr);
    }
}


// 策略2:平滑回退
public class PositionSmoothRollback : IAuthorityStrategy
{
    private Vector3 _vel;
    public void Resolve(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Position = Vector3.SmoothDamp(
            curr.Position, 
            input.ServerState.Position, 
            ref _vel, 
            0.1f, 
            100, 
            input.DeltaTime);
        target.Update(curr);
    }
}

// 3. 权威冲突:直接覆盖
public class RotationOverride : IAuthorityStrategy
{
    public void Resolve(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Rotation = input.ServerState.Rotation;
        target.Update(curr);
    }
}

// 4. 权威冲突:平滑回退
public class RotationSmoothRollback : IAuthorityStrategy
{
    public void Resolve(ISyncTarget target, SyncInput input)
    {
        var curr = target.GetCurrent();
        curr.Rotation = Quaternion.Lerp(
            curr.Rotation, 
            input.ServerState.Rotation, 
            input.Config.RollbackSpeed * input.DeltaTime);
        target.Update(curr);
    }
}

整合管理

桥接器

// 桥接:遍历执行所有目标
public class SyncBridge
{
    private readonly SyncContext _ctx;

    public SyncBridge(SyncContext ctx) => _ctx = ctx;

    // 接收服务端状态 → 权威纠正
    public void SetServerState(ServerState state)
    {
        _ctx.ServerState = state;
        var input = BuildSyncInput(0); // 权威纠正不需要 deltaTime
        foreach (var target in _ctx.Targets.Values)
            target.Authority?.Resolve(target, input);
    }

    // 每帧更新 → 预测 + 平滑
    public void Tick(float deltaTime)
    {
        var input = BuildSyncInput(deltaTime);
        foreach (var target in _ctx.Targets.Values)
        {
            target.Prediction?.Predict(target, input);
            target.Smoothing?.Smooth(target, input);
        }
    }

    // 合并所有目标状态
    public DisplayState GetDisplayState()
    {
        DisplayState state = new DisplayState();
        foreach (var target in _ctx.Targets.Values)
            target.MergeToDisplayState(ref state);
        _ctx.CurrentDisplay = state;
        return state;
    }

    // 构建算法输入包
    private SyncInput BuildSyncInput(float deltaTime)
    {
        return new SyncInput
        {
            ServerState = _ctx.ServerState,
            Config = _ctx.Config,
            DeltaTime = deltaTime
        };
    }
}

桥接管理

// 全局同步管理器
public class SyncBridgeManager
{
    private Dictionary<int, SyncBridge> _bridges = new();

    // 更新服务端状态
    public void UpdateServerState(int entityId, ServerState state)
    {
        if (!_bridges.ContainsKey(entityId))
            _bridges[entityId] = CreateBridge();
        _bridges[entityId].SetServerState(state);
    }

    // 每帧更新所有实体
    public void TickAll(float deltaTime)
    {
        foreach (var bridge in _bridges.Values)
            bridge.Tick(deltaTime);
    }

    // 获取最终显示状态
    public DisplayState GetDisplay(int entityId)
    {
        return _bridges.TryGetValue(entityId, out var bridge) 
            ? bridge.GetDisplayState() 
            : default;
    }

   //**创建默认桥接(位置+旋转),之后可以通过依赖注入实现自动注册,实现优化**
    private SyncBridge CreateBridge()
    {
        var ctx = new SyncContext { Config = new SyncConfig() };
        ctx.Targets[SyncTargetType.Position] = new PositionSync();
        ctx.Targets[SyncTargetType.Rotation] = new RotationSync();
        return new SyncBridge(ctx);
    }
}

同步管理器(外观模式)

服务端

// 服务端同步门面接口
public interface IServerSyncFacade
{
    void RegisterEntity(NetEntity entity);
    void Tick(float deltaTime);
    void OnReceive(NetMessage msg);

    event Action<int, ServerState> OnEntityStateChanged;
}

// 服务端同步层门面(统一入口)
// 同时管理状态同步 + 帧同步,对外提供统一接口
public class ServerSyncFacade : IServerSyncFacade
{
    // 状态同步服务端
    private readonly IStateSyncServer _stateSync;
    // 帧同步服务端
    private readonly IFrameSyncServer _frameSync;

    // 实体状态发生变化时对外抛出(给业务/UI/战斗层使用)
    public event Action<int, ServerState> OnEntityStateChanged;

    // 注入两种同步模块
    public ServerSyncFacade(IStateSyncServer stateSync, IFrameSyncServer frameSync)
    {
        _stateSync = stateSync;
        _frameSync = frameSync;

        // 转发状态同步的变更事件
        _stateSync.OnEntityStateChanged += (id, state) => OnEntityStateChanged?.Invoke(id, state);
    }

    // 注册需要同步的网络实体
    public void RegisterEntity(NetEntity entity)
    {
        _stateSync.RegisterEntity(entity);
        _frameSync.RegisterEntity(entity);
    }

    // 驱动两个同步模块每帧更新
    public void Tick(float deltaTime)
    {
        _stateSync.Tick(deltaTime);
        _frameSync.Tick(deltaTime);
    }

    // 接收网络消息并分发给对应同步模块
    public void OnReceive(NetMessage msg)
    {
        switch (msg.Type)
        {
            // 玩家指令 / 批量帧输入 交给帧同步
            case NetMsgType.PlayerCommand:
            case NetMsgType.FrameInputBatch:
                _frameSync.OnReceive(msg);
                break;
        }
    }
}

服务端

// 客户端同步门面接口
public interface IClientSyncFacade
{
    void RegisterEntity(NetEntity entity);
    void Tick(float deltaTime);
    void OnReceive(NetMessage msg);

    event Action<int, ServerState> OnEntityStateChanged;
    event Action<int, List<FrameInput>> OnFrameReceived;
}

// 客户端同步层门面(统一入口)
// 同时管理状态同步 + 帧同步,对外提供统一接口
public class ClientSyncFacade : IClientSyncFacade
{
    // 状态同步客户端
    private readonly IStateSyncClient _stateSync;
    // 帧同步客户端
    private readonly IFrameSyncClient _frameSync;

    // 服务端状态更新事件
    public event Action<int, ServerState> OnEntityStateChanged;
    // 服务端帧数据到达事件
    public event Action<int, List<FrameInput>> OnFrameReceived;

    // 注入两个同步模块
    public ClientSyncFacade(IStateSyncClient stateSync, IFrameSyncClient frameSync)
    {
        _stateSync = stateSync;
        _frameSync = frameSync;

        // 转发状态变更事件
        _stateSync.OnEntityStateChanged += (id, state) => OnEntityStateChanged?.Invoke(id, state);
        // 转发帧数据事件
        _frameSync.OnFrameReceived += (f, inputs) => OnFrameReceived?.Invoke(f, inputs);
    }

    // 注册需要同步的实体
    public void RegisterEntity(NetEntity entity)
    {
        _stateSync.RegisterEntity(entity);
        _frameSync.RegisterEntity(entity);
    }

    // 每帧驱动两个同步模块
    public void Tick(float deltaTime)
    {
        _stateSync.Tick(deltaTime);
        _frameSync.Tick(deltaTime);
    }

    // 接收网络消息 → 分发给对应同步模块
    public void OnReceive(NetMessage msg)
    {
        switch (msg.Type)
        {
            // 状态快照、增量、回退 → 状态同步处理
            case NetMsgType.StateSnapshot:
            case NetMsgType.StateDelta:
            case NetMsgType.StateCorrection:
                _stateSync.OnReceive(msg);
                break;

            // 帧同步广播包 → 帧同步处理
            case NetMsgType.FrameAuthPackage:
                _frameSync.OnReceive(msg);
                break;
        }
    }
}