Home | History | Annotate | Download | only in client
      1 /*
      2 Copyright (C) 1996-1997 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 #include "quakedef.h"
     21 #include "winquake.h"
     22 
     23 cvar_t	cl_nopred = CVAR2("cl_nopred","0");
     24 cvar_t	cl_pushlatency = CVAR2("pushlatency","-999");
     25 
     26 extern	frame_t		*view_frame;
     27 
     28 /*
     29 =================
     30 CL_NudgePosition
     31 
     32 If pmove.origin is in a solid position,
     33 try nudging slightly on all axis to
     34 allow for the cut precision of the net coordinates
     35 =================
     36 */
     37 void CL_NudgePosition (void)
     38 {
     39 	vec3_t	base;
     40 	int		x, y;
     41 
     42 	if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY)
     43 		return;
     44 
     45 	VectorCopy (pmove.origin, base);
     46 	for (x=-1 ; x<=1 ; x++)
     47 	{
     48 		for (y=-1 ; y<=1 ; y++)
     49 		{
     50 			pmove.origin[0] = base[0] + x * 1.0/8;
     51 			pmove.origin[1] = base[1] + y * 1.0/8;
     52 			if (PM_HullPointContents (&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY)
     53 				return;
     54 		}
     55 	}
     56 	Con_DPrintf ("CL_NudgePosition: stuck\n");
     57 }
     58 
     59 /*
     60 ==============
     61 CL_PredictUsercmd
     62 ==============
     63 */
     64 void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectator)
     65 {
     66 	// split up very long moves
     67 	if (u->msec > 50)
     68 	{
     69 		player_state_t	temp;
     70 		usercmd_t	split;
     71 
     72 		split = *u;
     73 		split.msec /= 2;
     74 
     75 		CL_PredictUsercmd (from, &temp, &split, spectator);
     76 		CL_PredictUsercmd (&temp, to, &split, spectator);
     77 		return;
     78 	}
     79 
     80 	VectorCopy (from->origin, pmove.origin);
     81 //	VectorCopy (from->viewangles, pmove.angles);
     82 	VectorCopy (u->angles, pmove.angles);
     83 	VectorCopy (from->velocity, pmove.velocity);
     84 
     85 	pmove.oldbuttons = from->oldbuttons;
     86 	pmove.waterjumptime = from->waterjumptime;
     87 	pmove.dead = cl.stats[STAT_HEALTH] <= 0;
     88 	pmove.spectator = spectator;
     89 
     90 	pmove.cmd = *u;
     91 
     92 	PlayerMove ();
     93 //for (i=0 ; i<3 ; i++)
     94 //pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125;
     95 	to->waterjumptime = pmove.waterjumptime;
     96 	to->oldbuttons = pmove.cmd.buttons;
     97 	VectorCopy (pmove.origin, to->origin);
     98 	VectorCopy (pmove.angles, to->viewangles);
     99 	VectorCopy (pmove.velocity, to->velocity);
    100 	to->onground = onground;
    101 
    102 	to->weaponframe = from->weaponframe;
    103 }
    104 
    105 
    106 
    107 /*
    108 ==============
    109 CL_PredictMove
    110 ==============
    111 */
    112 void CL_PredictMove (void)
    113 {
    114 	int			i;
    115 	float		f;
    116 	frame_t		*from, *to = NULL;
    117 	int			oldphysent;
    118 
    119 	if (cl_pushlatency.value > 0)
    120 		Cvar_Set ("pushlatency", "0");
    121 
    122 	if (cl.paused)
    123 		return;
    124 
    125 	cl.time = realtime - cls.latency - cl_pushlatency.value*0.001;
    126 	if (cl.time > realtime)
    127 		cl.time = realtime;
    128 
    129 	if (cl.intermission)
    130 		return;
    131 
    132 	if (!cl.validsequence)
    133 		return;
    134 
    135 	if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1)
    136 		return;
    137 
    138 	VectorCopy (cl.viewangles, cl.simangles);
    139 
    140 	// this is the last frame received from the server
    141 	from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
    142 
    143 	// we can now render a frame
    144 	if (cls.state == ca_onserver)
    145 	{	// first update is the final signon stage
    146 		char		text[1024];
    147 
    148 		cls.state = ca_active;
    149 		sprintf (text, "QuakeWorld: %s", cls.servername);
    150 #ifdef _WIN32
    151 		SetWindowText (mainwindow, text);
    152 #endif
    153 	}
    154 
    155 	if (cl_nopred.value)
    156 	{
    157 		VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel);
    158 		VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg);
    159 		return;
    160 	}
    161 
    162 	// predict forward until cl.time <= to->senttime
    163 	oldphysent = pmove.numphysent;
    164 	CL_SetSolidPlayers (cl.playernum);
    165 
    166 //	to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
    167 
    168 	for (i=1 ; i<UPDATE_BACKUP-1 && cls.netchan.incoming_sequence+i <
    169 			cls.netchan.outgoing_sequence; i++)
    170 	{
    171 		to = &cl.frames[(cls.netchan.incoming_sequence+i) & UPDATE_MASK];
    172 		CL_PredictUsercmd (&from->playerstate[cl.playernum]
    173 			, &to->playerstate[cl.playernum], &to->cmd, cl.spectator);
    174 		if (to->senttime >= cl.time)
    175 			break;
    176 		from = to;
    177 	}
    178 
    179 	pmove.numphysent = oldphysent;
    180 
    181 	if (i == UPDATE_BACKUP-1 || !to)
    182 		return;		// net hasn't deliver packets in a long time...
    183 
    184 	// now interpolate some fraction of the final frame
    185 	if (to->senttime == from->senttime)
    186 		f = 0;
    187 	else
    188 	{
    189 		f = (cl.time - from->senttime) / (to->senttime - from->senttime);
    190 
    191 		if (f < 0)
    192 			f = 0;
    193 		if (f > 1)
    194 			f = 1;
    195 	}
    196 
    197 	for (i=0 ; i<3 ; i++)
    198 		if ( fabs(from->playerstate[cl.playernum].origin[i] - to->playerstate[cl.playernum].origin[i]) > 128)
    199 		{	// teleported, so don't lerp
    200 			VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel);
    201 			VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg);
    202 			return;
    203 		}
    204 
    205 	for (i=0 ; i<3 ; i++)
    206 	{
    207 		cl.simorg[i] = from->playerstate[cl.playernum].origin[i]
    208 			+ f*(to->playerstate[cl.playernum].origin[i] - from->playerstate[cl.playernum].origin[i]);
    209 		cl.simvel[i] = from->playerstate[cl.playernum].velocity[i]
    210 			+ f*(to->playerstate[cl.playernum].velocity[i] - from->playerstate[cl.playernum].velocity[i]);
    211 	}
    212 }
    213 
    214 
    215 /*
    216 ==============
    217 CL_InitPrediction
    218 ==============
    219 */
    220 void CL_InitPrediction (void)
    221 {
    222 	Cvar_RegisterVariable (&cl_pushlatency);
    223 	Cvar_RegisterVariable (&cl_nopred);
    224 }
    225 
    226