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();
        }