Пример #1
0
    private void PredictPlayerState()
    {
        int         cmdNum, current;
        PlayerState oldPlayerState;
        bool        moved;
        UserCmd     oldestCmd;
        UserCmd     latestCmd;

        int stateIndex = 0, predictCmd = 0;
        int numPredicted = 0, numPlayedBack = 0;

        var gamestate = CDataModel.GameState;

        gamestate.hyperspace = false;

        if (!validPPS)
        {
            validPPS             = true;
            predictedPlayerState = gamestate.snap.playerState;
        }

        //如果是播回放,那么就复制移动,不做预测
        if (gamestate.demoPlayback || (gamestate.snap.playerState.pmFlags & PMoveFlags.FOLLOW) != PMoveFlags.NONE)
        {
            InterpolatePlayerState(false);
            return;
        }

        //非预测的本地移动会抓取最近的视角
        if (CConstVar.NoPredict || CConstVar.SynchronousClients)
        {
            InterpolatePlayerState(true);
            return;
        }

        pmove.playerState = predictedPlayerState;
        if (pmove.playerState.pmType == PMoveType.DEAD)
        {
            // pmove.tracemask =
        }
        else
        {
            // pmove.tracemask
        }

        // if(gamestate.snap.playerState.persistant[3] == )

        // pmove.noFootsteps = gamestate.dm

        oldPlayerState = predictedPlayerState;
        current        = CDataModel.GameState.ClActive.cmdNum;

        //如果没有紧接着snapshot之后的comands,就不能精确预测当前的位置,所以就停在最后的正确位置上
        cmdNum = current - CConstVar.CMD_BACKUP + 1;
        CDataModel.GameState.GetUserCmd(cmdNum, out oldestCmd);
        if (oldestCmd.serverTime > gamestate.snap.playerState.commandTime && oldestCmd.serverTime < gamestate.time)
        {
            if (CConstVar.ShowMiss > 0)
            {
                CLog.Info("exceeded Packet_Backup on commands");
            }
            return;
        }

        CDataModel.GameState.GetUserCmd(current, out latestCmd);

        if (gamestate.nextSnap != null && !gamestate.nextFrameTeleport && !gamestate.thisFrameTeleport)
        {
            predictedPlayerState  = gamestate.nextSnap.playerState;
            gamestate.physicsTime = gamestate.nextSnap.serverTime;
        }
        else
        {
            predictedPlayerState  = gamestate.snap.playerState;
            gamestate.physicsTime = gamestate.snap.serverTime;
        }

        if (CConstVar.PMoveMsec < 8)
        {
            CConstVar.PMoveMsec = 8;
        }
        else if (CConstVar.PMoveMsec > 33)
        {
            CConstVar.PMoveMsec = 33;
        }

        pmove.pmoveFixed = CConstVar.PMoveFixed;
        pmove.pmoveMsec  = CConstVar.PMoveMsec;
        pmove.pmoveFloat = CConstVar.PMoveFloat;
        // pmove.pmv

        if (CConstVar.OptimizePrediction)
        {
            if (gamestate.nextFrameTeleport || gamestate.thisFrameTeleport)
            {
                gamestate.lastPredictedCommand = 0;
                gamestate.stateTail            = gamestate.stateHead;
                predictCmd = current - CConstVar.CMD_BACKUP + 1;
            }
            else if (gamestate.time == gamestate.lastServerTime)
            {
                predictCmd = gamestate.lastPredictedCommand + 1;
            }
            else
            {
                bool error = true;
                for (int i = gamestate.stateHead; i != gamestate.stateTail; i = (i + 1) % CConstVar.NUM_SAVED_STATES)
                {
                    if (gamestate.savedPmoveState[i].commandTime == predictedPlayerState.commandTime)
                    {
                        int errorcode = IsUnacceptableError(predictedPlayerState, gamestate.savedPmoveState[i]);

                        if (errorcode > 0)
                        {
                            if (CConstVar.ShowMiss > 0)
                            {
                                CLog.Info("errorcode %d at %d", errorcode, gamestate.time);
                            }
                            break;
                        }

                        pmove.playerState = gamestate.savedPmoveState[i];

                        gamestate.stateHead = (i + 1) % CConstVar.NUM_SAVED_STATES;

                        predictCmd = gamestate.lastPredictedCommand + 1;

                        error = false;
                        break;
                    }
                }

                if (error)
                {
                    gamestate.lastPredictedCommand = 0;
                    gamestate.stateTail            = gamestate.stateHead;
                    predictCmd = current - CConstVar.CMD_BACKUP + 1;
                }
            }

            gamestate.lastServerTime = gamestate.physicsTime;
            stateIndex = gamestate.stateHead;
        }

        moved = false;
        for (cmdNum = current - CConstVar.CMD_BACKUP + 1; cmdNum <= current; cmdNum++)
        {
            CDataModel.GameState.GetUserCmd(current, out pmove.cmd);

            if (pmove.pmoveFixed > 0)
            {
                PMove.UpdateViewAngles(pmove.playerState, pmove.cmd);
            }

            if (pmove.cmd.serverTime <= predictedPlayerState.commandTime)
            {
                continue;
            }
            if (pmove.cmd.serverTime > latestCmd.serverTime)
            {
                continue;
            }

            if (predictedPlayerState.commandTime == oldPlayerState.commandTime)
            {
                Vector3 delta;
                float   len;

                if (gamestate.thisFrameTeleport)
                {
                    gamestate.predictedError = Vector3.zero;
                    if (CConstVar.ShowMiss > 0)
                    {
                        CLog.Info("PredictionTeleport");
                    }
                    gamestate.thisFrameTeleport = false;
                }
                else
                {
                    Vector3 adjusted, new_angles;
                    AdjustPositionForMover(predictedPlayerState.origin, predictedPlayerState.groundEntityNum, gamestate.physicsTime, gamestate.oldTime, out adjusted, predictedPlayerState.viewangles, out new_angles);

                    if (CConstVar.ShowMiss > 0)
                    {
                        if (oldPlayerState.origin != adjusted)
                        {
                            CLog.Info("prediction error");
                        }
                    }

                    delta = oldPlayerState.origin - adjusted;
                    len   = delta.magnitude;
                    if (len > 0.1)
                    {
                        if (CConstVar.ShowMiss > 0)
                        {
                            CLog.Info("Prediction miss: %d", len);
                        }
                        if (CConstVar.ErrorDecay > 0)
                        {
                            int   t = gamestate.time - gamestate.predictedErrorTime;
                            float f = (CConstVar.ErrorDecay - t) / CConstVar.ErrorDecay;
                            if (f < 0f)
                            {
                                f = 0f;
                            }
                            if (f > 0f && CConstVar.ShowMiss > 0)
                            {
                                CLog.Info("Double prediction decay: %d", f);
                            }
                            gamestate.predictedError = gamestate.predictedError * f;
                        }
                        else
                        {
                            gamestate.predictedError = Vector3.zero;
                        }

                        gamestate.predictedError     = delta + gamestate.predictedError;
                        gamestate.predictedErrorTime = gamestate.oldTime;
                    }
                }
            }

            pmove.gauntletHit = false;
            if (pmove.pmoveFixed > 0)
            {
                pmove.cmd.serverTime = ((pmove.cmd.serverTime + CConstVar.PMoveMsec - 1) / CConstVar.PMoveMsec) * CConstVar.PMoveMsec;
            }

            if (CConstVar.OptimizePrediction)
            {
                if (cmdNum >= predictCmd || (stateIndex + 1) % CConstVar.NUM_SAVED_STATES == gamestate.stateHead)
                {
                    pmove.Move();

                    numPredicted++;

                    gamestate.lastPredictedCommand = cmdNum;

                    if ((stateIndex + 1) % CConstVar.NUM_SAVED_STATES != gamestate.stateHead)
                    {
                        gamestate.savedPmoveState[stateIndex] = pmove.playerState;
                        stateIndex          = (stateIndex + 1) % CConstVar.NUM_SAVED_STATES;
                        gamestate.stateTail = stateIndex;
                    }
                }
                else
                {
                    numPlayedBack++;
                    if (CConstVar.ShowMiss > 0 && gamestate.savedPmoveState[stateIndex].commandTime != pmove.cmd.serverTime)
                    {
                        CLog.Info("saved state miss");
                    }

                    pmove.playerState = gamestate.savedPmoveState[stateIndex];
                    stateIndex        = (stateIndex + 1) % CConstVar.NUM_SAVED_STATES;
                }
            }
            else
            {
                pmove.Move();
                numPredicted++;
            }

            moved = true;
        }

        if (CConstVar.ShowMiss > 1)
        {
            CLog.Info("[%d : %d] ", pmove.cmd.serverTime, gamestate.time);
        }
        if (!moved)
        {
            if (CConstVar.ShowMiss > 0)
            {
                CLog.Info("not moved");
            }
            return;
        }
        AdjustPositionForMover(predictedPlayerState.origin, predictedPlayerState.groundEntityNum, gamestate.physicsTime, gamestate.time, out predictedPlayerState.origin, predictedPlayerState.viewangles, out predictedPlayerState.viewangles);
        if (CConstVar.ShowMiss > 0)
        {
            if (predictedPlayerState.eventSequence > oldPlayerState.eventSequence + CConstVar.MAX_PS_EVENTS)
            {
                CLog.Info("dropped event");
            }
        }
    }
Пример #2
0
    private void ClientThink_real(GameEntity ent)
    {
        GameClient cl = ent.client;

        if (cl.pers.connected != ClientConnState.CONNECTED)
        {
            return;
        }

        UserCmd ucmd = ent.client.pers.cmd;

        if (ucmd.forwardmove != 0 || ucmd.rightmove != 0)
        {
            CLog.Info("cmd move {0}, {1}", ucmd.forwardmove, ucmd.rightmove);
        }
        if (ucmd.serverTime > time + 200)
        {
            ucmd.serverTime = time + 200;
        }

        if (ucmd.serverTime < time - 1000)
        {
            ucmd.serverTime = time - 1000;
        }

//unlagged - backward reconciliation #4
        //frameOffset应该是一帧内收到命令数据包的偏移毫秒数,依赖于服务器运行RunFrame的速度
        cl.frameOffset = CDataModel.InputEvent.Milliseconds() - frameStartTime;
//unlagged - backward reconciliation #4

//unlagged - lag simulation #3
        if (cl.pers.plOut > 0)
        {
            float thresh = cl.pers.plOut / 100f;
            if (CUtils.Random() < thresh)
            {
                //这是丢失了的命令,不做任何事情
                return;
            }
        }

//unlagged - lag simulation #3

//unlagged - true ping
        cl.pers.pingSamples[cl.pers.sampleHead] = prevTime + cl.frameOffset - ucmd.serverTime;
        cl.pers.sampleHead++;
        if (cl.pers.sampleHead >= CConstVar.NUM_PING_SAMPLES)
        {
            cl.pers.sampleHead -= CConstVar.NUM_PING_SAMPLES;
        }

        if (CConstVar.TruePing)
        {
            int sum = 0;
            for (int i = 0; i < CConstVar.NUM_PING_SAMPLES; i++)
            {
                sum += cl.pers.pingSamples[i];
            }
            cl.pers.realPing = sum / CConstVar.NUM_PING_SAMPLES;
        }
        else
        {
            cl.pers.realPing = cl.playerState.ping;
        }
        //unlagged - true ping

//unlagged - lag simulation #2
        //  cl.pers.cmdqueue[cl.pers.cmdhead] = cl.pers.cmd;
        //  cl.pers.cmdhead++;
        //  if(cl.pers.cmdhead >= CConstVar.MAX_LATENT_CMDS){
        //      cl.pers.cmdhead -= CConstVar.MAX_LATENT_CMDS;
        //  }

        //  if(cl.pers.latentCmds > 0){
        //      int time = ucmd.serverTime;

        //      int cmdindex = cl.pers.cmdhead - cl.pers.latentCmds - 1;
        //      while(cmdindex < 0){
        //          cmdindex += CConstVar.MAX_LATENT_CMDS;
        //      }
        //      cl.pers.cmd = cl.pers.cmdqueue[cmdindex];
        //      cl.pers.realPing += time - ucmd.serverTime;
        //  }
//unlagged - lag simulation #2

        cl.attackTime = ucmd.serverTime;

        cl.lastUpdateFrame = frameNum;

//unlagged - lag simulation #1
        // if(cl.pers.latentSnaps > 0){
        //  cl.pers.realPing += cl.pers.latentSnaps * (1000 / CConstVar.SV_FPS);

        //  cl.attackTime -= cl.pers.latentSnaps * (1000 / CConstVar.SV_FPS);
        // }
//unlagged - lag simulation #1

//unlagged - true ping

        if (cl.pers.realPing < 0)
        {
            cl.pers.realPing = 0;
        }
//unlagged - true ping

        int msec = ucmd.serverTime - cl.playerState.commandTime;

        if (msec > 200)
        {
            msec = 200;
        }

        if (CConstVar.PMoveMsec < 8)
        {
            CConstVar.PMoveMsec = 8;
        }

        if (CConstVar.PMoveMsec > 33)
        {
            CConstVar.PMoveMsec = 33;
        }

        if (CConstVar.PMoveFixed > 0 || cl.pers.pmoveFixed)
        {
            ucmd.serverTime = ((ucmd.serverTime + CConstVar.PMoveMsec - 1) / CConstVar.PMoveMsec) * CConstVar.PMoveMsec;
        }

        if (cl.sess.sessionTeam == TeamType.TEAM_SPECTATOR || cl.isEliminated)
        {
            if (cl.sess.spectatorState == SpectatorState.SCOREBOARD)
            {
                return;
            }
            SpectatorThink(ent, ucmd);
            return;
        }

        if (cl.noclip)
        {
            cl.playerState.pmType = PMoveType.NOCLIP;
        }
        else if (cl.playerState.states[CConstVar.STAT_HEALTH] <= 0)
        {
            cl.playerState.pmType = PMoveType.DEAD;
        }
        else
        {
            cl.playerState.pmType = PMoveType.NORMAL;
        }

        // cl.playerState.gravity =
        cl.playerState.speed = CConstVar.Speed;

        int   oldEventSeq = cl.playerState.eventSequence;
        PMove pm          = new PMove();

        pm.playerState = cl.playerState;
        pm.agent       = cl.agent.Model;
        ucmd.CopyTo(pm.cmd);
        if (pm.playerState.pmType == PMoveType.DEAD)
        {
            // pm.tracemask
        }
        else if ((ent.sEnt.r.svFlags & SVFlags.BOT) != SVFlags.NONE)
        {
            // pm.
        }
        else
        {
        }
        pm.pmoveFixed  = CConstVar.PMoveFixed | (cl.pers.pmoveFixed ? 1 : 0);
        pm.pmoveMsec   = CConstVar.PMoveMsec;
        pm.pmoveFloat  = CConstVar.PMoveFloat;
        pm.pmvoveFlags = CConstVar.dmFlags;

        cl.oldOrigin = cl.playerState.origin;

        pm.Move();

        if (ent.client.playerState.eventSequence != oldEventSeq)
        {
            ent.eventTime = time;
        }

        if (CConstVar.SmoothClients > 1)
        {
            CUtils.BG_PlayerStateToEntityStateExtraPolate(ent.client.playerState, ref ent.sEnt.s, ent.client.playerState.commandTime, true);
        }
        else
        {
            CUtils.PlayerStateToEntityState(ent.client.playerState, ref ent.sEnt.s, true);
        }

        SendingPredictableEvents(ent.client.playerState);
        if ((ent.client.playerState.entityFlags & EntityFlags.FIRING) == 0)
        {
            // cl.fireHeld = false;
        }
        ent.sEnt.s.pos.GetTrBase(ref ent.sEnt.r.currentOrigin);
        // ent.sEnt.r.mins = pm.mins;

        //执行客户端事件
        ClientEvents(ent, oldEventSeq);

        Server.Instance.LinkEntity(ent);
        if (!ent.client.noclip)
        {
            TouchTrigger(ent);
        }

        ent.sEnt.r.currentOrigin = ent.client.playerState.origin;

        ClientImpacts(ent, ref pm);

        //保存触发器和客户端事件
        if (ent.client.playerState.eventSequence != oldEventSeq)
        {
            ent.eventTime = time;
        }

        cl.oldButtons       = cl.buttons;
        cl.buttons          = ucmd.buttons;
        cl.latched_buttons |= cl.buttons & ~cl.oldButtons;

        if (cl.playerState.states[CConstVar.STAT_HEALTH] <= 0)
        {
            if ((time > cl.respawnTime))
            {
                ClientRespawn(ent);
            }
            return;
        }
    }