242 lines
6.0 KiB
C
242 lines
6.0 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2008 ©
|
|
// cl_pred.c - client movement prediction
|
|
//=======================================================================
|
|
|
|
#include "common.h"
|
|
#include "client.h"
|
|
#include "const.h"
|
|
#include "pm_local.h"
|
|
#include "net_encode.h"
|
|
|
|
#define MAX_FORWARD 6
|
|
|
|
qboolean CL_IsPredicted( void )
|
|
{
|
|
cl_entity_t *player = CL_GetLocalPlayer();
|
|
|
|
if( !player )
|
|
return false;
|
|
|
|
if( !cl.frame.valid )
|
|
return false;
|
|
|
|
if( cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= CL_UPDATE_BACKUP - 1 )
|
|
return false;
|
|
|
|
if( !cl_predict->integer )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
CL_PostRunCmd
|
|
|
|
Done after running a player command.
|
|
===========
|
|
*/
|
|
void CL_PostRunCmd( usercmd_t *ucmd, int random_seed )
|
|
{
|
|
local_state_t *from, *to;
|
|
|
|
from = &cl.frames[cl.delta_sequence & CL_UPDATE_MASK].local;
|
|
to = &cl.frame.local;
|
|
|
|
clgame.dllFuncs.pfnPostRunCmd( from, to, ucmd, clgame.pmove->runfuncs, cl.time, random_seed );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CL_CheckPredictionError
|
|
===================
|
|
*/
|
|
void CL_CheckPredictionError( void )
|
|
{
|
|
int frame;
|
|
vec3_t delta;
|
|
float flen;
|
|
|
|
if( !CL_IsPredicted( )) return;
|
|
|
|
// calculate the last usercmd_t we sent that the server has processed
|
|
frame = cls.netchan.incoming_acknowledged;
|
|
frame &= CL_UPDATE_MASK;
|
|
|
|
// compare what the server returned with what we had predicted it to be
|
|
VectorSubtract( cl.frame.local.client.origin, cl.predicted_origins[frame], delta );
|
|
|
|
// save the prediction error for interpolation
|
|
flen = fabs( delta[0] ) + fabs( delta[1] ) + fabs( delta[2] );
|
|
|
|
if( flen > 80 )
|
|
{
|
|
// a teleport or something
|
|
VectorClear( cl.prediction_error );
|
|
}
|
|
else
|
|
{
|
|
if( cl_showmiss->integer && flen > 0.1f ) Msg( "prediction miss: %g\n", flen );
|
|
VectorCopy( cl.frame.local.client.origin, cl.predicted_origins[frame] );
|
|
|
|
// save for error itnerpolation
|
|
VectorCopy( delta, cl.prediction_error );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_SetIdealPitch
|
|
===============
|
|
*/
|
|
void CL_SetIdealPitch( cl_entity_t *ent )
|
|
{
|
|
float angleval, sinval, cosval;
|
|
pmtrace_t tr;
|
|
vec3_t top, bottom;
|
|
float z[MAX_FORWARD];
|
|
int i, j;
|
|
int step, dir, steps;
|
|
|
|
if( !( cl.frame.local.client.flags & FL_ONGROUND ))
|
|
return;
|
|
|
|
angleval = ent->angles[YAW] * M_PI * 2 / 360;
|
|
SinCos( angleval, &sinval, &cosval );
|
|
|
|
for( i = 0; i < MAX_FORWARD; i++ )
|
|
{
|
|
top[0] = ent->origin[0] + cosval * (i + 3) * 12;
|
|
top[1] = ent->origin[1] + sinval * (i + 3) * 12;
|
|
top[2] = ent->origin[2] + cl.frame.local.client.view_ofs[2];
|
|
|
|
bottom[0] = top[0];
|
|
bottom[1] = top[1];
|
|
bottom[2] = top[2] - 160;
|
|
|
|
// skip any monsters (only world and brush models)
|
|
tr = PM_PlayerTrace( clgame.pmove, top, bottom, PM_STUDIO_IGNORE, 2, -1, NULL );
|
|
if( tr.allsolid ) return; // looking at a wall, leave ideal the way is was
|
|
|
|
if( tr.fraction == 1.0f )
|
|
return; // near a dropoff
|
|
|
|
z[i] = top[2] + tr.fraction * (bottom[2] - top[2]);
|
|
}
|
|
|
|
dir = 0;
|
|
steps = 0;
|
|
for( j = 1; j < i; j++ )
|
|
{
|
|
step = z[j] - z[j-1];
|
|
if( step > -ON_EPSILON && step < ON_EPSILON )
|
|
continue;
|
|
|
|
if( dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ))
|
|
return; // mixed changes
|
|
|
|
steps++;
|
|
dir = step;
|
|
}
|
|
|
|
if( !dir )
|
|
{
|
|
cl.refdef.idealpitch = 0;
|
|
return;
|
|
}
|
|
|
|
if( steps < 2 ) return;
|
|
cl.refdef.idealpitch = -dir * cl_idealpitchscale->value;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_PredictMovement
|
|
|
|
Sets cl.predicted_origin and cl.predicted_angles
|
|
=================
|
|
*/
|
|
void CL_PredictMovement( void )
|
|
{
|
|
int frame = 1;
|
|
int ack, outgoing_command;
|
|
int current_command;
|
|
int current_command_mod;
|
|
cl_entity_t *player, *viewent;
|
|
clientdata_t *cd;
|
|
|
|
if( cls.state != ca_active ) return;
|
|
if( cl.refdef.paused || cls.key_dest == key_menu ) return;
|
|
|
|
player = CL_GetLocalPlayer ();
|
|
viewent = CL_GetEntityByIndex( cl.refdef.viewentity );
|
|
cd = &cl.frame.local.client;
|
|
|
|
CL_SetIdealPitch( player );
|
|
|
|
if( cls.demoplayback && viewent )
|
|
{
|
|
// restore viewangles from angles
|
|
cl.refdef.cl_viewangles[PITCH] = -viewent->angles[PITCH] * 3;
|
|
cl.refdef.cl_viewangles[YAW] = viewent->angles[YAW];
|
|
cl.refdef.cl_viewangles[ROLL] = 0; // roll will be computed in view.cpp
|
|
}
|
|
|
|
// unpredicted pure angled values converted into axis
|
|
AngleVectors( cl.refdef.cl_viewangles, cl.refdef.forward, cl.refdef.right, cl.refdef.up );
|
|
|
|
if( !CL_IsPredicted( ))
|
|
{
|
|
// run commands even if client predicting is disabled - client expected it
|
|
clgame.pmove->runfuncs = true;
|
|
|
|
VectorCopy( cl.refdef.cl_viewangles, cl.predicted_angles );
|
|
VectorCopy( cd->view_ofs, cl.predicted_viewofs );
|
|
|
|
CL_PostRunCmd( cl.refdef.cmd, cls.netchan.outgoing_sequence );
|
|
return;
|
|
}
|
|
|
|
ack = cls.netchan.incoming_acknowledged;
|
|
outgoing_command = cls.netchan.outgoing_sequence;
|
|
|
|
ASSERT( cl.refdef.cmd != NULL );
|
|
|
|
// setup initial pmove state
|
|
CL_SetupPMove( clgame.pmove, cd, &player->curstate, cl.refdef.cmd );
|
|
clgame.pmove->runfuncs = false;
|
|
|
|
while( 1 )
|
|
{
|
|
// we've run too far forward
|
|
if( frame >= CL_UPDATE_BACKUP - 1 )
|
|
break;
|
|
|
|
// Incoming_acknowledged is the last usercmd the server acknowledged having acted upon
|
|
current_command = ack + frame;
|
|
current_command_mod = current_command & CL_UPDATE_MASK;
|
|
|
|
// we've caught up to the current command.
|
|
if( current_command > outgoing_command )
|
|
break;
|
|
|
|
clgame.pmove->cmd = cl.cmds[frame];
|
|
|
|
// motor!
|
|
clgame.dllFuncs.pfnPlayerMove( clgame.pmove, false ); // run frames
|
|
|
|
// save for debug checking
|
|
VectorCopy( clgame.pmove->origin, cl.predicted_origins[frame-1] );
|
|
clgame.pmove->runfuncs = true;
|
|
|
|
frame++;
|
|
}
|
|
|
|
CL_PostRunCmd( cl.refdef.cmd, frame );
|
|
|
|
// copy results out for rendering
|
|
VectorCopy( clgame.pmove->view_ofs, cl.predicted_viewofs );
|
|
VectorCopy( clgame.pmove->origin, cl.predicted_origin );
|
|
VectorCopy( clgame.pmove->angles, cl.predicted_angles );
|
|
VectorCopy( clgame.pmove->velocity, cl.predicted_velocity );
|
|
} |