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