private bool _CheckBorderServerFrame(bool isForce = false) { if (State != EGameState.Playing) { return(false); } var frame = GetOrCreateFrame(Tick); var inputs = frame.Inputs; if (!isForce) { //是否所有的输入 都已经等到 for (int i = 0; i < inputs.Length; i++) { if (inputs[i] == null) { return(false); } } } //将所有未到的包 给予默认的输入 for (int i = 0; i < inputs.Length; i++) { if (inputs[i] == null) { inputs[i] = new Msg_PlayerInput(Tick, (byte)i) { IsMiss = true }; } } //Debug.Log($" Border input {Tick} isUpdate:{isForce} _tickSinceGameStart:{_tickSinceGameStart}"); var msg = new Msg_ServerFrames(); int count = Tick < 2 ? Tick + 1 : 3; var frames = new ServerFrame[count]; for (int i = 0; i < count; i++) { frames[count - i - 1] = _allHistoryFrames[Tick - i]; } msg.startTick = frames[0].tick; msg.frames = frames; BorderUdp(EMsgSC.G2C_FrameData, msg); if (_firstFrameTimeStamp <= 0) { _firstFrameTimeStamp = _timeSinceLoaded; } if (_gameStartTimestampMs < 0) { _gameStartTimestampMs = LTime.realtimeSinceStartupMS + NetworkDefine.UPDATE_DELTATIME * _ServerTickDealy; } Tick++; return(true); }
void OnNet_ReqMissFrame(Player player, BaseMsg data) { var reqMsg = data as Msg_ReqMissFrame; var nextCheckTick = reqMsg.StartTick; Log($"OnNet_ReqMissFrame nextCheckTick id:{player.LocalId}:{nextCheckTick}"); int count = System.Math.Min(System.Math.Min((Tick - 1), allHistoryFrames.Count) - nextCheckTick, MaxRepMissFrameCountPerPack); if (count <= 0) { return; } var msg = new Msg_RepMissFrame(); var frames = new ServerFrame[count]; for (int i = 0; i < count; i++) { frames[i] = allHistoryFrames[nextCheckTick + i]; Logging.Debug.Assert(frames[i] != null); } msg.startTick = frames[0].tick; msg.frames = frames; SendUdp(player, EMsgSC.G2C_RepMissFrame, msg, true); }
private ServerFrame _GetFrame(ServerFrame[] buffer, int tick) { int num = tick % this._bufferSize; ServerFrame serverFrame = buffer[num]; bool flag = serverFrame == null; ServerFrame result; if (flag) { result = null; } else { bool flag2 = serverFrame.Tick != tick; if (flag2) { result = null; } else { result = serverFrame; } } return(result); }
public void PushLocalFrame(ServerFrame frame) { var tick = frame.tick; if (tick != nextClientTic) { UnityEngine.Debug.LogError($"PushLocalFrame error tick: {tick} :nextClientTic:{nextClientTic}"); } Logging.Debug.Assert(tick == nextClientTic); Logging.Debug.Assert(((int)nextClientTic - (int)waitCheckTick) < MAX_OVERRIDE_COUNT, $"ring out of range cTick:{nextClientTic} waitCheck:{waitCheckTick} "); var sIdx = nextClientTic % MAX_FRAME_BUFFER_COUNT; clientFrames[sIdx] = frame; nextClientTic++; #if DEBUG_FRAME_DELAY var time = 0; foreach (var input in frame.inputs) { if (input != null && input.ActorId == DebugMainActorID) { input.timeSinceStartUp = Time.realtimeSinceStartup; } } #endif }
public void PushLocalFrame(ServerFrame frame) { int num = frame.Tick % this._bufferSize; Debug.Assert(this._clientBuffer[num] == null || this._clientBuffer[num].Tick <= frame.Tick, "Push local frame error!"); this._clientBuffer[num] = frame; }
private void DumpGameFrames() { var msg = new MultiFrames(); int count = System.Math.Min((Tick - 1), _allHistoryFrames.Count); if (count <= 0) { return; } var frames = new ServerFrame[count]; for (int i = 0; i < count; i++) { frames[i] = _allHistoryFrames[i]; Logger.Debug.Assert(frames[i] != null, "!!!!!!!!!!!!!!!!!"); } msg.StartTick = frames[0].tick; msg.frames = frames; var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../Record/" + System.DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + GameType + "_" + GameId + ".record"); var dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } Log("Create Record " + path); //File.WriteAllBytes(path, bytes); }
public void ForcePushDebugFrame(ServerFrame data) { int num = data.Tick % this._bufferSize; this._serverBuffer[num] = data; this._clientBuffer[num] = data; }
private void ProcessInputQueue(ServerFrame frame) { var inputs = frame.Inputs; foreach (var playerInput in _playerInputs) { playerInput.Reset(); } foreach (var input in inputs) { if (input.Commands == null) { continue; } if (input.ActorId >= _playerInputs.Length) { continue; } var inputEntity = _playerInputs[input.ActorId]; foreach (var command in input.Commands) { Logger.Trace(this, input.ActorId + " >> " + input.Tick + ": " + input.Commands.Count()); _inputService.Execute(command, inputEntity); } } }
void OnNet_PlayerInput(Player player, BaseFormater data) { if (State != ERoomState.PartLoaded && State != ERoomState.Playing) { return; } if (State == ERoomState.PartLoaded) { State = ERoomState.Playing; } var input = data as Msg_PlayerInput; //Debug.Log($"RecvInput actorID:{input.ActorId} inputTick:{input.Tick} Tick{Tick} count = {input.Commands.Length}"); if (input.Tick < Tick) { return; } var tick = input.Tick; var iTick = (int)tick; //扩充帧队列 var frameCount = allHistoryFrames.Count; if (frameCount <= iTick) { var count = iTick - allHistoryFrames.Count + 1; for (int i = 0; i < count; i++) { allHistoryFrames.Add(null); } } if (allHistoryFrames[iTick] == null) { allHistoryFrames[iTick] = new ServerFrame() { Tick = tick }; } var frame = allHistoryFrames[iTick]; if (frame.Inputs == null || frame.Inputs.Length != MaxPlayerCount) { frame.Inputs = new Msg_PlayerInput[MaxPlayerCount]; } var id = input.ActorId; if (!allNeedWaitInputPlayerIds.Contains(id)) { allNeedWaitInputPlayerIds.Add(id); } frame.Inputs[id] = input; //if (input.Commands.Count > 0) { // Debug.Log($"RecvInput actorID:{input.ActorId} inputTick:{input.Tick} cmd:{(ECmdType)(input.Commands[0].type)}"); //} }
public void ForcePushDebugFrame(ServerFrame data) { var targetIdx = data.tick % _bufferSize; _serverBuffer[targetIdx] = data; _clientBuffer[targetIdx] = data; }
ServerFrame GetOrCreateFrame(int tick) { //扩充帧队列 var frameCount = _allHistoryFrames.Count; if (frameCount <= tick) { var count = tick - _allHistoryFrames.Count + 1; for (int i = 0; i < count; i++) { _allHistoryFrames.Add(null); } } if (_allHistoryFrames[tick] == null) { _allHistoryFrames[tick] = new ServerFrame() { tick = tick }; } var frame = _allHistoryFrames[tick]; if (frame.Inputs == null || frame.Inputs.Length != MaxPlayerCount) { frame.Inputs = new Msg_PlayerInput[MaxPlayerCount]; } return(frame); }
public void PushLocalFrame(ServerFrame frame) { var sIdx = frame.tick % _bufferSize; Debug.Assert(_clientBuffer[sIdx] == null || _clientBuffer[sIdx].tick <= frame.tick, "Push local frame error!"); _clientBuffer[sIdx] = frame; }
private void DoClientUpdate() { int maxRollbackCount = 5; if (_isDebugRollback && _world.Tick > maxRollbackCount && _world.Tick % maxRollbackCount == 0) { var rawTick = _world.Tick; var revertCount = LRandom.Range(1, maxRollbackCount); for (int i = 0; i < revertCount; i++) { var input = new Msg_PlayerInput(_world.Tick, LocalActorId, _inputService.GetDebugInputCmds()); var frame = new ServerFrame() { tick = rawTick - i, _inputs = new Msg_PlayerInput[] { input } }; _cmdBuffer.ForcePushDebugFrame(frame); } _debugService.Trace("RollbackTo " + (_world.Tick - revertCount)); if (!RollbackTo(_world.Tick - revertCount, _world.Tick)) { _commonStateService.IsPause = true; return; } while (_world.Tick < rawTick) { var sFrame = _cmdBuffer.GetServerFrame(_world.Tick); Logging.Debug.Assert(sFrame != null && sFrame.tick == _world.Tick, $" logic error: server Frame must exist tick {_world.Tick}"); _cmdBuffer.PushLocalFrame(sFrame); Simulate(sFrame); if (_commonStateService.IsPause) { return; } } } while (_world.Tick < TargetTick) { FramePredictCount = 0; var input = new Msg_PlayerInput(_world.Tick, LocalActorId, _inputService.GetInputCmds()); var frame = new ServerFrame() { tick = _world.Tick, _inputs = new Msg_PlayerInput[] { input } }; _cmdBuffer.PushLocalFrame(frame); _cmdBuffer.PushServerFrames(new ServerFrame[] { frame }); Simulate(_cmdBuffer.GetFrame(_world.Tick)); if (_commonStateService.IsPause) { return; } } }
private void Simulate(ServerFrame frame, bool isNeedGenSnap = true) { ProcessInputQueue(frame); _world.Simulate(isNeedGenSnap); var tick = _world.Tick; cmdBuffer.SetClientTick(tick); SetHashCode(); if (isNeedGenSnap && tick % FrameBuffer.SnapshotFrameInterval == 0) { _world.CleanUselessSnapshot(System.Math.Min(cmdBuffer.nextTickToCheck - 1, _world.Tick)); } }
public void DoUpdate(float deltaTime) { this._networkService.SendPing(this._simulatorService.LocalActorId, LTime.realtimeSinceStartupMS); this._predictHelper.DoUpdate(deltaTime); int tick = this._simulatorService.World.Tick; this.UpdatePingVal(deltaTime); this.IsNeedRollback = false; while (this.NextTickToCheck <= this.MaxServerTickInBuffer && this.NextTickToCheck < tick) { int num = this.NextTickToCheck % this._bufferSize; ServerFrame serverFrame = this._clientBuffer[num]; ServerFrame serverFrame2 = this._serverBuffer[num]; bool flag = serverFrame == null || serverFrame.Tick != this.NextTickToCheck || serverFrame2 == null || serverFrame2.Tick != this.NextTickToCheck; if (flag) { break; } bool flag2 = serverFrame2 == serverFrame || serverFrame2.Equals(serverFrame); if (!flag2) { this.IsNeedRollback = true; break; } int nextTickToCheck = this.NextTickToCheck; this.NextTickToCheck = nextTickToCheck + 1; } int i; for (i = this.NextTickToCheck; i <= this.MaxServerTickInBuffer; i++) { int num2 = i % this._bufferSize; bool flag3 = this._serverBuffer[num2] == null || this._serverBuffer[num2].Tick != i; if (flag3) { break; } } this.MaxContinueServerTick = i - 1; bool flag4 = this.MaxContinueServerTick <= 0; if (!flag4) { bool flag5 = this.MaxContinueServerTick <this.CurTickInServer || this._nextClientTick> this.MaxContinueServerTick + (this._maxClientPredictFrameCount - 3); if (flag5) { Debug.Log("SendMissFrameReq " + this.MaxContinueServerTick, Array.Empty <object>()); this._networkService.SendMissFrameReq(this.MaxContinueServerTick); } } }
public (ServerFrame ServerFrame, List <UnitFrame> UnitFrame) GetServerData(Player player) { float viewRange = Math.Max(player.ViewZone.ViewRange + 2f, _config.MaxViewRange); bool FilterRect(Vector2 pos) => (pos.X > player.ViewZone.MinX - 1f && pos.X < player.ViewZone.MaxX + 1f) && (pos.Y > player.ViewZone.MinY - 1f && pos.Y < player.ViewZone.MaxY + 1f); var visiblePlayers = _players.Where((p) => p.Value == player || (p.Value.IsAlive && (p.Value.UnitsEffect?.StatsChange.UnitVisible ?? true) && (p.Value.AvgPosition - player.AvgPosition).SqrLength() < viewRange * viewRange) && FilterRect(p.Value.AvgPosition)) .Select((p) => p.Value.GetPlayerCameraInfo()).ToArray(); var visibleUnits = OverlapUnits(player.AvgPosition, viewRange) .Where((u) => (u.CurrentStats.UnitVisible || u.Owner == player) && FilterRect(u.Position)); var visibleUnitsData = visibleUnits.Select((u) => u.GetUnitInfo(this, player)).ToList(); int[] visibleUnitsMap = new int[_units.Length / 32 + 1]; foreach (var unit in visibleUnitsData) { visibleUnitsMap.SetBit(unit.UnitId); } var visibleRunes = _runesTree.Overlap <Rune>(player.AvgPosition, viewRange) .Select((r) => r.GetRuneInfo()).ToArray(); var sFrame = new ServerFrame() { State = _worldState, PlayTime = _timeToEnd, ZoneSize = ZoneRadius, AlivePlayers = _alivePlayers, Cooldown = player.GetCooldown(), VisualEffect = player.UnitsEffect?.VisualEffect ?? VisualEffect.None, VisualEffectTime = (float)((player.UnitsEffect?.EndTime ?? DateTime.Now) - DateTime.Now).TotalSeconds, PlayerCameras = visiblePlayers, EnabledUnits = visibleUnitsMap, UnitKill = player.PlayerStatus.UnitKill, PlayerUnitCount = player.Units.Count, Runes = visibleRunes }; var unitFrames = SplitList.Split(visibleUnitsData, visibleUnitsData.Count < 45 ? 12 : 24) .Select((lst) => new UnitFrame() { Units = lst.ToArray(), PacketId = UnitFrame.UnitsPacketId++ }).ToList(); return(sFrame, unitFrames); }
private void FillInputWithLastFrame(ServerFrame frame) { int tick = frame.tick; var inputs = frame.Inputs; var lastFrameInputs = tick == 0 ? null : cmdBuffer.GetFrame(tick - 1)?.Inputs; var curFrameInput = inputs[_localActorId]; //将所有角色 给予默认的输入 for (int i = 0; i < _actorCount; i++) { inputs[i] = new Msg_PlayerInput(tick, _allActors[i], lastFrameInputs?[i]?.Commands?.ToList()); } inputs[_localActorId] = curFrameInput; }
private void FillInputWithLastFrame(ServerFrame frame) { int tick = frame.tick; var inputs = frame.Inputs; var lastServerInputs = tick == 0 ? null : _cmdBuffer.GetFrame(tick - 1)?.Inputs; var myInput = inputs[LocalActorId]; //fill inputs with last frame's input (Input predict) for (int i = 0; i < _actorCount; i++) { inputs[i] = new Msg_PlayerInput(tick, _allActors[i], lastServerInputs?[i]?.Commands); } inputs[LocalActorId] = myInput; }
private void ProcessInputQueue(ServerFrame frame) { Msg_PlayerInput[] inputs = frame.Inputs; foreach (Msg_PlayerInput msg_PlayerInput in inputs) { bool flag = msg_PlayerInput.Commands == null; if (!flag) { byte actorId = msg_PlayerInput.ActorId; foreach (InputCmd cmd in msg_PlayerInput.Commands) { this._world.ProcessInputQueue(actorId, cmd); } } } }
public ServerFrame GetFrame(int tick) { ServerFrame serverFrame = this.GetServerFrame(tick); bool flag = serverFrame != null; ServerFrame result; if (flag) { result = serverFrame; } else { result = this.GetLocalFrame(tick); } return(result); }
private void DoClientUpdate() { while (_world.Tick < TargetTick) { FramePredictCount = 0; var input = new Msg_PlayerInput(_world.Tick, LocalActorId, _inputService.GetInputCmds()); var frame = new ServerFrame() { tick = _world.Tick, _inputs = new Msg_PlayerInput[] { input } }; _cmdBuffer.PushLocalFrame(frame); _cmdBuffer.PushServerFrames(new ServerFrame[] { frame }); Simulate(_cmdBuffer.GetFrame(_world.Tick)); } }
public void PushServerFrames(ServerFrame[] frames, bool isNeedDebugCheck = true) { int num = frames.Length; for (int i = 0; i < num; i++) { ServerFrame serverFrame = frames[i]; long num2; bool flag = this._tick2SendTimestamp.TryGetValue(serverFrame.Tick, out num2); if (flag) { long item = LTime.realtimeSinceStartupMS - num2; this._delays.Add(item); this._tick2SendTimestamp.Remove(serverFrame.Tick); } bool flag2 = serverFrame.Tick < this.NextTickToCheck; if (!flag2) { bool flag3 = serverFrame.Tick > this.CurTickInServer; if (flag3) { this.CurTickInServer = serverFrame.Tick; } bool flag4 = serverFrame.Tick >= this.NextTickToCheck + this._maxServerOverFrameCount - 1; if (flag4) { break; } bool flag5 = serverFrame.Tick > this.MaxServerTickInBuffer; if (flag5) { this.MaxServerTickInBuffer = serverFrame.Tick; } int num3 = serverFrame.Tick % this._bufferSize; bool flag6 = this._serverBuffer[num3] == null || this._serverBuffer[num3].Tick != serverFrame.Tick; if (flag6) { this._serverBuffer[num3] = serverFrame; bool flag7 = serverFrame.Tick > this._predictHelper.nextCheckMissTick && serverFrame.Inputs[(int)this.LocalId].IsMiss && this._predictHelper.missTick == -1; if (flag7) { this._predictHelper.missTick = serverFrame.Tick; } } } } }
private void SendInputs(int curTick) { Msg_PlayerInput msg_PlayerInput = new Msg_PlayerInput(curTick, this.LocalActorId, this._inputService.GetInputCmds()); ServerFrame serverFrame = new ServerFrame(); Msg_PlayerInput[] array = new Msg_PlayerInput[this._actorCount]; array[(int)this.LocalActorId] = msg_PlayerInput; serverFrame.Inputs = array; serverFrame.Tick = curTick; this.FillInputWithLastFrame(serverFrame); this._cmdBuffer.PushLocalFrame(serverFrame); bool flag = curTick > this._cmdBuffer.MaxServerTickInBuffer; if (flag) { this._cmdBuffer.SendInput(msg_PlayerInput); } }
public void JumpTo(int tick) { bool flag = tick + 1 == this._world.Tick || tick == this._world.Tick; if (!flag) { tick = LMath.Min(tick, this._videoFrames.frames.Length - 1); float num = (float)LTime.realtimeSinceStartupMS + 0.05f; bool flag2 = !this._isInitVideo; if (flag2) { this._globalStateService.IsVideoLoading = true; while (this._world.Tick < this._videoFrames.frames.Length) { ServerFrame frame = this._videoFrames.frames[this._world.Tick]; this.Simulate(frame, true); bool flag3 = (float)LTime.realtimeSinceStartupMS > num; if (flag3) { EventHelper.Trigger(EEvent.VideoLoadProgress, (float)this._world.Tick * 1f / (float)this._videoFrames.frames.Length); return; } } this._globalStateService.IsVideoLoading = false; EventHelper.Trigger(EEvent.VideoLoadDone, null); this._isInitVideo = true; } bool flag4 = this._world.Tick > tick; if (flag4) { this.RollbackTo(tick, this._videoFrames.frames.Length, false); } while (this._world.Tick <= tick) { ServerFrame frame2 = this._videoFrames.frames[this._world.Tick]; this.Simulate(frame2, false); } this._viewService.RebindAllEntities(); this._timestampOnLastJumpToMs = LTime.realtimeSinceStartupMS; this._tickOnLastJumpTo = tick; } }
private void DumpGameFrames() { var msg = new Msg_RepMissFrame(); int count = Math.Min((Tick - 1), allHistoryFrames.Count); if (count <= 0) { return; } var frames = new ServerFrame[count]; for (int i = 0; i < count; i++) { frames[i] = allHistoryFrames[i]; Debug.Assert(frames[i] != null, "!!!!!!!!!!!!!!!!!"); } msg.StartTick = frames[0].Tick; msg.Frames = frames; var writer = new Serializer(); writer.PutInt32(TypeId); writer.PutInt32(RoomId); writer.PutInt32(seed); writer.PutBytes_255(_playerId2LocalId.Values.ToArray()); msg.Serialize(writer); var bytes = Compressor.Compress(writer); var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../Record/" + System.DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + TypeId + "_" + RoomId + ".record"); var dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } Debug.Log("Create Record " + path); File.WriteAllBytes(path, bytes); }
void Step(ServerFrame frame, bool isNeedGenSnap = true) { //Debug.Log("Step: " + _world.Tick + " TargetTick: " + TargetTick); _commonStateService.SetTick(_world.Tick); var hash = _hashHelper.CalcHash(); _commonStateService.Hash = hash; _timeMachineService.Backup(_world.Tick); _hashHelper.SetHash(_world.Tick, hash); DumpFrame(hash); ProcessInputQueue(frame); _world.Step(isNeedGenSnap); _dumpHelper.OnFrameEnd(); var tick = _world.Tick; _cmdBuffer.SetClientTick(tick); //clean useless snapshot if (isNeedGenSnap && tick % snapshotFrameInterval == 0) { CleanUselessSnapshot(System.Math.Min(_cmdBuffer.NextTickToCheck - 1, _world.Tick)); } }
void SendInputs(int curTick) { var input = new Msg_PlayerInput(curTick, LocalActorId, _inputService.GetInputCmds()); var cFrame = new ServerFrame(); var inputs = new Msg_PlayerInput[_actorCount]; inputs[LocalActorId] = input; cFrame.Inputs = inputs; cFrame.tick = curTick; FillInputWithLastFrame(cFrame); _cmdBuffer.PushLocalFrame(cFrame); //if (input.Commands != null) { // var playerInput = new Deserializer(input.Commands[0].content).Parse<Lockstep.Game.PlayerInput>(); // Debug.Log($"SendInput curTick{curTick} maxSvrTick{_cmdBuffer.MaxServerTickInBuffer} _tickSinceGameStart {_tickSinceGameStart} uv {playerInput.inputUV}"); //} if (curTick > _cmdBuffer.MaxServerTickInBuffer) { //TODO combine all history inputs into one Msg //Debug.Log("SendInput " + curTick +" _tickSinceGameStart " + _tickSinceGameStart); _cmdBuffer.SendInput(input); } }
private void ProcessInputQueue(ServerFrame frame) { var inputs = frame.Inputs; foreach (var input in inputs) { GameLog.Add(frame.tick, input); if (input.Commands == null) { continue; } foreach (var command in input.Commands) { Log.Trace(this, input.ActorId + " >> " + input.Tick + ": " + input.Commands.Count()); var inputEntity = _context.input.CreateEntity(); _inputService.Execute(command, inputEntity); inputEntity.AddTick(input.Tick); inputEntity.AddActorId(input.ActorId); inputEntity.isDestroyed = true; } } }
private void FillInputWithLastFrame(ServerFrame frame) { int tick = frame.Tick; Msg_PlayerInput[] inputs = frame.Inputs; Msg_PlayerInput[] array; if (tick != 0) { ServerFrame frame2 = this._cmdBuffer.GetFrame(tick - 1); array = ((frame2 != null) ? frame2.Inputs : null); } else { array = null; } Msg_PlayerInput[] array2 = array; Msg_PlayerInput msg_PlayerInput = inputs[(int)this.LocalActorId]; for (int i = 0; i < this._actorCount; i++) { Msg_PlayerInput[] array3 = inputs; int num = i; int tick2 = tick; byte actorID = this._allActors[i]; InputCmd[] inputs2; if (array2 == null) { inputs2 = null; } else { Msg_PlayerInput msg_PlayerInput2 = array2[i]; inputs2 = ((msg_PlayerInput2 != null) ? msg_PlayerInput2.Commands : null); } array3[num] = new Msg_PlayerInput(tick2, actorID, inputs2); } inputs[(int)this.LocalActorId] = msg_PlayerInput; }
public void RunVideo() { bool flag = this._tickOnLastJumpTo == this._world.Tick; if (flag) { this._timestampOnLastJumpToMs = LTime.realtimeSinceStartupMS; this._tickOnLastJumpTo = this._world.Tick; } float num = (LTime.timeSinceLevelLoad - (float)this._timestampOnLastJumpToMs) * 1000f; double num2 = System.Math.Ceiling((double)(num / 30f)) + (double)this._tickOnLastJumpTo; while ((double)this._world.Tick <= num2) { bool flag2 = this._world.Tick < this._videoFrames.frames.Length; if (!flag2) { break; } ServerFrame frame = this._videoFrames.frames[this._world.Tick]; this.Simulate(frame, false); } }