private void DoNormalUpdate() { //make sure client is not move ahead too much than server var maxContinueServerTick = _cmdBuffer.MaxContinueServerTick; if ((_world.Tick - maxContinueServerTick) > MaxPredictFrameCount) { return; } var minTickToBackup = (maxContinueServerTick - (maxContinueServerTick % snapshotFrameInterval)); // Pursue Server frames var deadline = LTime.realtimeSinceStartupMS + MaxSimulationMsPerFrame; while (_world.Tick < _cmdBuffer.CurTickInServer) { var tick = _world.Tick; var sFrame = _cmdBuffer.GetServerFrame(tick); if (sFrame == null) { OnPursuingFrame(); return; } _cmdBuffer.PushLocalFrame(sFrame); Simulate(sFrame, tick == minTickToBackup); if (LTime.realtimeSinceStartupMS > deadline) { OnPursuingFrame(); return; } } if (_constStateService.IsPursueFrame) { _constStateService.IsPursueFrame = false; EventHelper.Trigger(EEvent.PursueFrameDone); } // Roll back if (_cmdBuffer.IsNeedRollback) { RollbackTo(_cmdBuffer.NextTickToCheck, maxContinueServerTick); CleanUselessSnapshot(System.Math.Min(_cmdBuffer.NextTickToCheck - 1, _world.Tick)); minTickToBackup = System.Math.Max(minTickToBackup, _world.Tick + 1); while (_world.Tick <= maxContinueServerTick) { 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, _world.Tick == minTickToBackup); } } //Run frames while (_world.Tick <= TargetTick) { var curTick = _world.Tick; ServerFrame frame = null; var sFrame = _cmdBuffer.GetServerFrame(curTick); if (sFrame != null) { frame = sFrame; } else { var cFrame = _cmdBuffer.GetLocalFrame(curTick); FillInputWithLastFrame(cFrame); frame = cFrame; } _cmdBuffer.PushLocalFrame(frame); Predict(frame, true); } _hashHelper.CheckAndSendHashCodes(); }