diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h index da3e42469..7015db990 100644 --- a/src/cgame/cg_local.h +++ b/src/cgame/cg_local.h @@ -3449,6 +3449,7 @@ typedef struct // cg_playerstate.c void CG_Respawn(qboolean revived); void CG_TransitionPlayerState(playerState_t *ps, playerState_t *ops); +void CG_ResetTimers(void); //=============================================== diff --git a/src/cgame/cg_playerstate.c b/src/cgame/cg_playerstate.c index 83c967983..0d89ff793 100644 --- a/src/cgame/cg_playerstate.c +++ b/src/cgame/cg_playerstate.c @@ -150,7 +150,9 @@ void CG_Respawn(qboolean revived) cg.serverRespawning = qfalse; // just in case // no error decay on player movement - cg.thisFrameTeleport = qtrue; + cg.thisFrameTeleport = qtrue; + cg.predictedErrorTime = 0; + VectorClear(cg.predictedError); // need to reset client-side weapon animations cg.predictedPlayerState.weapAnim = ((cg.predictedPlayerState.weapAnim & ANIM_TOGGLEBIT) ^ ANIM_TOGGLEBIT) | WEAP_IDLE1; // reset weapon animations @@ -208,7 +210,7 @@ void CG_Respawn(qboolean revived) cg.pmext.silencedSideArm = 2; } - cg.proneMovingTime = 0; + CG_ResetTimers(); // reset fog to world fog (if present) trap_R_SetFog(FOG_CMD_SWITCHFOG, FOG_MAP, 20, 0, 0, 0, 0); @@ -456,6 +458,7 @@ void CG_TransitionPlayerState(playerState_t *ps, playerState_t *ops) // make sure we don't get any unwanted transition effects *ops = *ps; + CG_ResetTimers(); // After Limbo, make sure and do a CG_Respawn if (ps->clientNum == cg.clientNum) @@ -463,6 +466,10 @@ void CG_TransitionPlayerState(playerState_t *ps, playerState_t *ops) ops->persistant[PERS_SPAWN_COUNT]--; } } + else + { + cg.thisFrameTeleport = qfalse; + } if (ps->eFlags & EF_FIRING) { @@ -547,7 +554,7 @@ void CG_TransitionPlayerState(playerState_t *ps, playerState_t *ops) CG_CheckPlayerstateEvents(ps, ops); // smooth the ducking viewheight change - if (ps->viewheight != ops->viewheight) + if (ps->viewheight != ops->viewheight && !cg.thisFrameTeleport) { cg.duckChange = ps->viewheight - ops->viewheight; cg.duckTime = cg.time; @@ -555,3 +562,22 @@ void CG_TransitionPlayerState(playerState_t *ps, playerState_t *ops) VectorSubtract(ops->origin, ps->origin, cg.deltaProne); } } + +/** +* @brief CG_ResetTimers resets playerstate related timers +*/ +void CG_ResetTimers(void) +{ + cg.cameraShakeScale = 0; + cg.cameraShakeLength = 0; + cg.cameraShakeTime = 0; + cg.cameraShakePhase = 0; + cg.damageTime = 0; + cg.stepTime = 0; + cg.duckTime = 0; + cg.landTime = 0; + cg.proneMovingTime = 0; + cg.v_dmg_time = 0; + cg.v_noFireTime = 0; + cg.v_fireTime = 0; +} diff --git a/src/cgame/cg_predict.c b/src/cgame/cg_predict.c index a42f555a3..3f025fba4 100644 --- a/src/cgame/cg_predict.c +++ b/src/cgame/cg_predict.c @@ -981,7 +981,7 @@ void CG_PredictPlayerState(void) { int cmdNum, current; playerState_t oldPlayerState; - qboolean moved; + qboolean moved, predictError; usercmd_t oldestCmd; usercmd_t latestCmd; vec3_t deltaAngles; @@ -1249,7 +1249,8 @@ void CG_PredictPlayerState(void) // unlagged - optimized prediction // run cmds - moved = qfalse; + moved = qfalse; + predictError = qtrue; for (cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++) { // get the command @@ -1257,34 +1258,28 @@ void CG_PredictPlayerState(void) // get the previous command trap_GetUserCmd(cmdNum - 1, &cg_pmove.oldcmd); - //if (cg_pmove.pmove_fixed - // && !BG_PlayerMounted(cg.snap->ps.eFlags) // don't update view angles - causes issues in 1st person view with weapons using special view - // && cg.predictedPlayerState.weapon != WP_MOBILE_MG42_SET && cg.predictedPlayerState.weapon != WP_MOBILE_BROWNING_SET) // see cg_view.c fov_x = 55; - //{ - // added tracemask - // PM_UpdateViewAngles(cg_pmove.ps, cg_pmove.pmext, &cg_pmove.cmd, CG_Trace, cg_pmove.tracemask); - //} - - // don't do anything if the time is before the snapshot player time - if (cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime) - { - continue; - } - - // don't do anything if the command was from a previous map_restart - if (cg_pmove.cmd.serverTime > latestCmd.serverTime) - { - continue; - } - // check for a prediction error from last frame // on a lan, this will often be the exact value // from the snapshot, but on a wan we will have // to predict several commands to get to the point // we want to compare - if (cg.predictedPlayerState.commandTime == oldPlayerState.commandTime) + + // this was moved here to fix pmove_fixed prediction error detection and + // subsequent error smoothing: + // when using pmove_fixed not all user commands will result in + // an actual move because commands are run for the duration of pmove_msec + // if commands are generated every 4ms (250fps) and pmove_msec is 8ms, + // only 1 out of those 2 commands will be run + // this will mean that in most situations, the check below would happen + // on 2nd user command which is not run, and the prediction error + // is not registered, causing more jittery game when miss prediction happens + // since there might be many user commands for same playerstate commandTime + // (as explained above) need to make sure it is only checked once per frame + + if (cg.predictedPlayerState.commandTime == oldPlayerState.commandTime && predictError) { vec3_t delta; + predictError = qfalse; if (BG_PlayerMounted(cg_pmove.ps->eFlags)) // TODO: clarify MG & Browning are locked in place too? { @@ -1354,6 +1349,19 @@ void CG_PredictPlayerState(void) } } + // don't do anything if the time is before the snapshot player time + if (cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime) + { + Com_Memcpy(&pmext, &oldpmext[cmdNum & CMD_MASK], sizeof(pmoveExt_t)); + continue; + } + + // don't do anything if the command was from a previous map_restart + if (cg_pmove.cmd.serverTime > latestCmd.serverTime) + { + continue; + } + if (cg_pmove.pmove_fixed) { cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer - 1) / pmove_msec.integer) * pmove_msec.integer; diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index cf58f832b..8427858d9 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -1429,9 +1429,7 @@ static void CG_MapRestart(void) cg.latchVictorySound = qfalse; // we really should clear more parts of cg here and stop sounds - cg.v_dmg_time = 0; - cg.v_noFireTime = 0; - cg.v_fireTime = 0; + CG_ResetTimers(); cg.filtercams = Q_atoi(CG_ConfigString(CS_FILTERCAMS)) ? qtrue : qfalse; diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c index 9e2a35719..a96ba899a 100644 --- a/src/cgame/cg_view.c +++ b/src/cgame/cg_view.c @@ -2002,14 +2002,7 @@ static void CG_DemoRewindFixEffects(void) trap_R_ClearDecals(); // bullet and explosion marks (cg_markTime) are on renderer side // reset camera view effects - cg.damageTime = 0; - cg.v_dmg_time = 0; - cg.v_noFireTime = 0; - cg.v_fireTime = 0; - cg.cameraShakeScale = 0; - cg.cameraShakeLength = 0; - cg.cameraShakeTime = 0; - cg.cameraShakePhase = 0; + CG_ResetTimers(); cgs.serverCommandSequence = cg.snap->serverCommandSequence; } diff --git a/src/game/et-antiwarp.c b/src/game/et-antiwarp.c index f832ed5bb..dd44f6ebc 100644 --- a/src/game/et-antiwarp.c +++ b/src/game/et-antiwarp.c @@ -193,7 +193,7 @@ void DoClientThinks(gentity_t *ent) serverTime = cmd->serverTime; totalDelta = latestTime - cmd->serverTime; - if (ent->client->pers.pmoveFixed) + if (pmove_fixed.integer || ent->client->pers.pmoveFixed) { serverTime = ((serverTime + pmove_msec.integer - 1) / pmove_msec.integer) * pmove_msec.integer; } diff --git a/src/game/g_active.c b/src/game/g_active.c index e7ed5d0ad..e5700521e 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -1236,7 +1236,7 @@ void ClientThink_real(gentity_t *ent) // zinx etpro antiwarp client->pers.pmoveMsec = pmove_msec.integer; - if (!G_DoAntiwarp(ent) && (pmove_fixed.integer || client->pers.pmoveFixed)) + if (pmove_fixed.integer || client->pers.pmoveFixed) { ucmd->serverTime = ((ucmd->serverTime + client->pers.pmoveMsec - 1) / client->pers.pmoveMsec) * client->pers.pmoveMsec;