初学者,自用笔记
基础工具
// 网络消息类型
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));
}
}
- 矫正处理
// 处理强制校正:立即覆盖,用于修正预测误差
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;
}
}
}

京公网安备 11010502036488号