/* cl_pred.c Copyright (C) 1996-1997 Id Software, Inc. Portions Copyright (C) 1999,2000 Nelson Rush. Copyright (C) 1999,2000 contributors of the QuakeForge project Please see the file "AUTHORS" for a list of contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include cvar_t *cl_nopred; cvar_t *cl_pushlatency; extern frame_t *view_frame; /* CL_NudgePosition If pmove.origin is in a solid position, try nudging slightly on all axis to allow for the cut precision of the net coordinates */ void CL_NudgePosition (void) { vec3_t base; int x, y; if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) return; VectorCopy (pmove.origin, base); for (x=-1 ; x<=1 ; x++) { for (y=-1 ; y<=1 ; y++) { pmove.origin[0] = base[0] + x * 1.0/8; pmove.origin[1] = base[1] + y * 1.0/8; if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) return; } } Con_DPrintf ("CL_NudgePosition: stuck\n"); } /* CL_PredictUsercmd */ void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator) { // split up very long moves if (u->msec > 50) { player_state_t temp; usercmd_t split; split = *u; split.msec /= 2; CL_PredictUsercmd (from, &temp, &split, spectator); CL_PredictUsercmd (&temp, to, &split, spectator); return; } VectorCopy (from->origin, pmove.origin); // VectorCopy (from->viewangles, pmove.angles); VectorCopy (u->angles, pmove.angles); VectorCopy (from->velocity, pmove.velocity); pmove.oldbuttons = from->oldbuttons; pmove.waterjumptime = from->waterjumptime; pmove.dead = cl.stats[STAT_HEALTH] <= 0; pmove.spectator = spectator; pmove.flying = cl.stats[STAT_FLYMODE]; pmove.cmd = *u; PlayerMove (); //for (i=0 ; i<3 ; i++) //pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125; to->waterjumptime = pmove.waterjumptime; to->oldbuttons = pmove.cmd.buttons; VectorCopy (pmove.origin, to->origin); VectorCopy (pmove.angles, to->viewangles); VectorCopy (pmove.velocity, to->velocity); to->onground = onground; to->weaponframe = from->weaponframe; } /* CL_PredictMove */ void CL_PredictMove (void) { int i; float f; frame_t *from, *to = NULL; int oldphysent; if (cl_pushlatency->value > 0) Cvar_Set ("pushlatency", "0"); if (cl.paused) return; cl.time = realtime - cls.latency - cl_pushlatency->value*0.001; if (cl.time > realtime) cl.time = realtime; if (cl.intermission) return; if (!cl.validsequence) return; if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) return; VectorCopy (cl.viewangles, cl.simangles); // this is the last frame received from the server from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; // we can now render a frame if (cls.state == ca_onserver) { // first update is the final signon stage char text[1024]; cls.state = ca_active; snprintf(text, sizeof(text), "QuakeWorld: %s", cls.servername); #ifdef _WIN32 SetWindowText (mainwindow, text); #endif } if (cl_nopred->value) { VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel); VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg); return; } // predict forward until cl.time <= to->senttime oldphysent = pmove.numphysent; CL_SetSolidPlayers (cl.playernum); // to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; for (i=1 ; iplayerstate[cl.playernum], &to->playerstate[cl.playernum], &to->cmd, cl.spectator); if (to->senttime >= cl.time) break; from = to; } pmove.numphysent = oldphysent; if (i == UPDATE_BACKUP-1 || !to) return; // net hasn't deliver packets in a long time... // now interpolate some fraction of the final frame if (to->senttime == from->senttime) f = 0; else { f = (cl.time - from->senttime) / (to->senttime - from->senttime); f = max(0, min(f, 1)); } for (i=0 ; i<3 ; i++) if ( fabs(from->playerstate[cl.playernum].origin[i] - to->playerstate[cl.playernum].origin[i]) > 128) { // teleported, so don't lerp VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel); VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg); return; } for (i=0 ; i<3 ; i++) { cl.simorg[i] = from->playerstate[cl.playernum].origin[i] + f*(to->playerstate[cl.playernum].origin[i] - from->playerstate[cl.playernum].origin[i]); cl.simvel[i] = from->playerstate[cl.playernum].velocity[i] + f*(to->playerstate[cl.playernum].velocity[i] - from->playerstate[cl.playernum].velocity[i]); } } /* CL_InitPrediction */ void CL_InitPrediction (void) { cl_pushlatency = Cvar_Get ("pushlatency","0",CVAR_NONE,"None"); cl_nopred = Cvar_Get ("cl_nopred","0",CVAR_NONE,"None"); }