using System.IO; namespace ET.Client { public static partial class LSClientHelper { public static void RunLSRollbackSystem(Entity entity) { if (entity is LSEntity) { return; } LSEntitySystemSingleton.Instance.LSRollback(entity); if (entity.ComponentsCount() > 0) { foreach (var kv in entity.Components) { RunLSRollbackSystem(kv.Value); } } if (entity.ChildrenCount() > 0) { foreach (var kv in entity.Children) { RunLSRollbackSystem(kv.Value); } } } // 回滚 public static void Rollback(Room room, int frame) { room.LSWorld.Dispose(); FrameBuffer frameBuffer = room.FrameBuffer; // 回滚 room.LSWorld = room.GetLSWorld(SceneType.LockStepClient, frame); OneFrameInputs authorityFrameInput = frameBuffer.FrameInputs(frame); // 执行AuthorityFrame room.Update(authorityFrameInput); room.SendHash(frame); // 重新执行预测的帧 for (int i = room.AuthorityFrame + 1; i <= room.PredictionFrame; ++i) { OneFrameInputs oneFrameInputs = frameBuffer.FrameInputs(i); LSClientHelper.CopyOtherInputsTo(room, authorityFrameInput, oneFrameInputs); // 重新预测消息 room.Update(oneFrameInputs); } RunLSRollbackSystem(room); } public static void SendHash(this Room self, int frame) { if (frame > self.AuthorityFrame) { return; } long hash = self.FrameBuffer.GetHash(frame); C2Room_CheckHash c2RoomCheckHash = C2Room_CheckHash.Create(); c2RoomCheckHash.Frame = frame; c2RoomCheckHash.Hash = hash; self.Root().GetComponent().Send(c2RoomCheckHash); } // 重新调整预测消息,只需要调整其他玩家的输入 public static void CopyOtherInputsTo(Room room, OneFrameInputs from, OneFrameInputs to) { long myId = room.GetComponent().MyId; foreach (var kv in from.Inputs) { if (kv.Key == myId) { continue; } to.Inputs[kv.Key] = kv.Value; } } public static void SaveReplay(Room room, string path) { if (room.IsReplay) { return; } Log.Debug($"save replay: {path} frame: {room.Replay.FrameInputs.Count}"); byte[] bytes = MemoryPackHelper.Serialize(room.Replay); File.WriteAllBytes(path, bytes); } public static void JumpReplay(Room room, int frame) { if (!room.IsReplay) { return; } if (frame >= room.Replay.FrameInputs.Count) { frame = room.Replay.FrameInputs.Count - 1; } int snapshotIndex = frame / LSConstValue.SaveLSWorldFrameCount; Log.Debug($"jump replay start {room.AuthorityFrame} {frame} {snapshotIndex}"); if (snapshotIndex != room.AuthorityFrame / LSConstValue.SaveLSWorldFrameCount || frame < room.AuthorityFrame) { room.LSWorld.Dispose(); // 回滚 byte[] memoryBuffer = room.Replay.Snapshots[snapshotIndex]; LSWorld lsWorld = MongoHelper.Deserialize(typeof (LSWorld), memoryBuffer, 0, memoryBuffer.Length) as LSWorld; room.LSWorld = lsWorld; room.AuthorityFrame = snapshotIndex * LSConstValue.SaveLSWorldFrameCount; RunLSRollbackSystem(room); } room.FixedTimeCounter.Reset(room.Fiber.TimeInfo.ServerFrameTime() - frame * LSConstValue.UpdateInterval, 0); Log.Debug($"jump replay finish {frame}"); } } }