void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u) { // 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); CL_PredictUsercmd (&temp, to, &split); return; } VectorCopy (from->origin, pmove.origin); VectorCopy (u->angles, pmove.angles); VectorCopy (from->velocity, pmove.velocity); pmove.jump_msec = (cl.z_ext & Z_EXT_PM_TYPE) ? 0 : from->jump_msec; pmove.jump_held = from->jump_held; pmove.waterjumptime = from->waterjumptime; pmove.pm_type = from->pm_type; pmove.onground = from->onground; pmove.cmd = *u; #ifdef JSS_CAM if (cam_lockdir.value) { VectorCopy (saved_angles, pmove.cmd.angles); VectorCopy (saved_angles, pmove.angles); } else VectorCopy (pmove.cmd.angles, saved_angles); #endif movevars.entgravity = cl.entgravity; movevars.maxspeed = cl.maxspeed; movevars.bunnyspeedcap = cl.bunnyspeedcap; PM_PlayerMove (); to->waterjumptime = pmove.waterjumptime; to->pm_type = pmove.pm_type; to->jump_held = pmove.jump_held; to->jump_msec = pmove.jump_msec; pmove.jump_msec = 0; VectorCopy (pmove.origin, to->origin); VectorCopy (pmove.angles, to->viewangles); VectorCopy (pmove.velocity, to->velocity); to->onground = pmove.onground; to->weaponframe = from->weaponframe; }
/* === Calculate the new position of players, without other player clipping We do this to set up real player prediction. Players are predicted twice, first without clipping other players, then with clipping against them. This sets up the first phase. === */ void CL_SetUpPlayerPrediction(qboolean dopred) { int j; player_state_t *state; player_state_t exact; double playertime; int msec; frame_t *frame; struct predicted_player *pplayer; playertime = realtime - cls.latency + 0.02; if (playertime > realtime) playertime = realtime; frame = &cl.frames[cl.parsecount&UPDATE_MASK]; for (j=0, pplayer = predicted_players, state=frame->playerstate; j < MAX_CLIENTS; j++, pplayer++, state++) { pplayer->active = false; if (state->messagenum != cl.parsecount) continue; // not present this frame if (!state->modelindex) continue; pplayer->active = true; pplayer->flags = state->flags; // note that the local player is special, since he moves locally // we use his last predicted postition if (j == cl.playernum) { VectorCopy(cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playernum].origin, pplayer->origin); } else { // only predict half the move to minimize overruns msec = 500*(playertime - state->state_time); if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value) || !dopred) { VectorCopy (state->origin, pplayer->origin); //Con_DPrintf ("nopredict\n"); } else { // predict players movement if (msec > 255) msec = 255; state->command.msec = msec; //Con_DPrintf ("predict: %i\n", msec); CL_PredictUsercmd (state, &exact, &state->command, false); VectorCopy (exact.origin, pplayer->origin); } } } }
/* ============== CL_PredictUsercmd ============== */ void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u) { // 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); CL_PredictUsercmd (&temp, to, &split); return; } VectorCopy (from->origin, cl.pmove.origin); // VectorCopy (from->viewangles, pmove.angles); VectorCopy (u->angles, cl.pmove.angles); VectorCopy (from->velocity, cl.pmove.velocity); if (cl.z_ext & Z_EXT_PM_TYPE) cl.pmove.jump_msec = 0; else cl.pmove.jump_msec = from->jump_msec; cl.pmove.jump_held = from->jump_held; cl.pmove.waterjumptime = from->waterjumptime; cl.pmove.pm_type = from->pm_type; cl.pmove.onground = from->onground; cl.pmove.cmd = *u; PM_PlayerMove (&cl.pmove, &cl.movevars); to->waterjumptime = cl.pmove.waterjumptime; to->pm_type = cl.pmove.pm_type; to->jump_held = cl.pmove.jump_held; to->jump_msec = cl.pmove.jump_msec; cl.pmove.jump_msec = 0; VectorCopy (cl.pmove.origin, to->origin); VectorCopy (cl.pmove.angles, to->viewangles); VectorCopy (cl.pmove.velocity, to->velocity); to->onground = cl.pmove.onground; to->weaponframe = from->weaponframe; }
/* ============== 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.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; sprintf(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; i < UPDATE_BACKUP - 1 && cls.netchan.incoming_sequence + i < cls.netchan.outgoing_sequence; i++) { to = &cl.frames[(cls.netchan.incoming_sequence + i) & UPDATE_MASK]; CL_PredictUsercmd(&from->playerstate[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); if (f < 0) f = 0; if (f > 1) 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_LinkPlayers Create visible entities in the correct position for all current players ============= */ void CL_LinkPlayers (void) { int j; player_info_t *info; player_state_t *state; player_state_t exact; double playertime; entity_t *ent; int msec; frame_t *frame; int oldphysent; playertime = realtime - cls.latency + 0.02; if (playertime > realtime) playertime = realtime; frame = &cl.frames[cl.parsecount&UPDATE_MASK]; for (j=0, info=cl.players, state=frame->playerstate ; j < MAX_CLIENTS ; j++, info++, state++) { if (state->messagenum != cl.parsecount) continue; // not present this frame // spawn light flashes, even ones coming from invisible objects // #ifdef GLQUAKE // if (!gl_flashblend.value || j != cl.playernum) { // #endif if ((state->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 3); else if (state->effects & EF_BLUE) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 1); else if (state->effects & EF_RED) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 2); else if (state->effects & EF_BRIGHTLIGHT) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2] + 16, 400 + (rand()&31), 0.1, 0); else if (state->effects & EF_DIMLIGHT) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 0); // #ifdef GLQUAKE // } // #endif // the player object never gets added if (j == cl.playernum) continue; if (!state->modelindex) continue; if (!Cam_DrawPlayer(j)) continue; // grab an entity to fill in if (cl_numvisedicts == MAX_VISEDICTS) break; // object list is full ent = &cl_visedicts[cl_numvisedicts]; cl_numvisedicts++; ent->keynum = 0; ent->model = cl.model_precache[state->modelindex]; ent->skinnum = state->skinnum; ent->frame = state->frame; ent->colormap = info->translations; if (state->modelindex == cl_playerindex) ent->scoreboard = info; // use custom skin else ent->scoreboard = NULL; // // angles // ent->angles[PITCH] = -state->viewangles[PITCH]/3; ent->angles[YAW] = state->viewangles[YAW]; ent->angles[ROLL] = 0; ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; // only predict half the move to minimize overruns msec = 500*(playertime - state->state_time); if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value)) { VectorCopy (state->origin, ent->origin); //Con_DPrintf ("nopredict\n"); } else { // predict players movement if (msec > 255) msec = 255; state->command.msec = msec; //Con_DPrintf ("predict: %i\n", msec); oldphysent = pmove.numphysent; CL_SetSolidPlayers (j); CL_PredictUsercmd (state, &exact, &state->command, false); pmove.numphysent = oldphysent; VectorCopy (exact.origin, ent->origin); } if (state->effects & EF_FLAG1) CL_AddFlagModels (ent, 0); else if (state->effects & EF_FLAG2) CL_AddFlagModels (ent, 1); } }