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