/* ================ CL_TestEntities if cl_testentities is set, create 32 player models ================ */ void CL_TestEntities( void ) { int i, j; float f, r; edict_t ent, *pl; if( !cl_testentities->integer ) return; pl = CL_GetLocalPlayer(); Mem_Set( &ent, 0, sizeof( edict_t )); V_ClearScene(); for( i = 0; i < 32; i++ ) { r = 64 * ((i%4) - 1.5 ); f = 64 * (i/4) + 128; for( j = 0; j < 3; j++ ) ent.v.origin[j] = cl.refdef.vieworg[j]+cl.refdef.forward[j] * f + cl.refdef.right[j] * r; ent.v.scale = 1.0f; ent.serialnumber = pl->serialnumber; ent.v.controller[0] = ent.v.controller[1] = 90.0f; ent.v.controller[2] = ent.v.controller[3] = 180.0f; ent.v.modelindex = pl->v.modelindex; re->AddRefEntity( &ent, ED_NORMAL, -1 ); } }
/* =============== CL_AddPacketEntities =============== */ void CL_AddPacketEntities( frame_t *frame ) { cl_entity_t *ent, *clent; int i, e, entityType; clent = CL_GetLocalPlayer(); if( !clent ) return; for( i = 0; i < cl.frame.num_entities; i++ ) { e = cls.packet_entities[(cl.frame.first_entity + i) % cls.num_client_entities].number; ent = CL_GetEntityByIndex( e ); if( !ent || ent == clgame.entities ) continue; CL_UpdateEntityFields( ent ); if( ent->player ) entityType = ET_PLAYER; else if( ent->curstate.entityType == ENTITY_BEAM ) entityType = ET_BEAM; else entityType = ET_NORMAL; CL_AddVisibleEntity( ent, entityType ); } }
/* ============== SCR_DrawPos Draw local player position, angles and velocity ============== */ void SCR_DrawPos( void ) { static char msg[MAX_SYSPATH]; float speed; cl_entity_t *pPlayer; rgba_t color; if( cls.state != ca_active ) return; if( !cl_showpos->integer || cl.background ) return; pPlayer = CL_GetLocalPlayer(); speed = VectorLength( cl.frame.local.client.velocity ); Q_snprintf( msg, MAX_SYSPATH, "pos: %.2f %.2f %.2f\n" "ang: %.2f %.2f %.2f\n" "velocity: %.2f", pPlayer->origin[0], pPlayer->origin[1], pPlayer->origin[2], pPlayer->angles[0], pPlayer->angles[1], pPlayer->angles[2], speed ); MakeRGBA( color, 255, 255, 255, 255 ); Con_DrawString( scr_width->integer / 2, 4, msg, color ); }
/* =============== V_SetupRefDef update refdef values each frame =============== */ void V_SetupRefDef( void ) { cl_entity_t *clent; clent = CL_GetLocalPlayer (); clgame.entities->curstate.scale = clgame.movevars.waveHeight; VectorCopy( cl.frame.local.client.punchangle, cl.refdef.punchangle ); clgame.viewent.curstate.modelindex = cl.frame.local.client.viewmodel; clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex ); clgame.viewent.curstate.entityType = ET_NORMAL; clgame.viewent.index = cl.playernum + 1; cl.refdef.movevars = &clgame.movevars; cl.refdef.onground = ( cl.frame.local.client.flags & FL_ONGROUND ) ? 1 : 0; cl.refdef.health = cl.frame.local.client.health; cl.refdef.playernum = cl.playernum; cl.refdef.max_entities = clgame.maxEntities; cl.refdef.maxclients = cl.maxclients; cl.refdef.time = cl.time; cl.refdef.frametime = cl.time - cl.oldtime; cl.refdef.demoplayback = cls.demoplayback; cl.refdef.smoothing = cl_smooth->integer; cl.refdef.waterlevel = cl.frame.local.client.waterlevel; cl.refdef.onlyClientDraw = 0; // reset clientdraw cl.refdef.viewsize = scr_viewsize->integer; cl.refdef.hardware = true; // always true cl.refdef.spectator = cl.spectator; cl.refdef.nextView = 0; // setup default viewport cl.refdef.viewport[0] = cl.refdef.viewport[1] = 0; cl.refdef.viewport[2] = scr_width->integer; cl.refdef.viewport[3] = scr_height->integer; // calc FOV cl.refdef.fov_x = cl.data.fov; // this is a final fov value cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] ); if( CL_IsPredicted( ) && !cl.refdef.demoplayback ) { VectorCopy( cl.predicted_origin, cl.refdef.simorg ); VectorCopy( cl.predicted_velocity, cl.refdef.simvel ); VectorCopy( cl.predicted_viewofs, cl.refdef.viewheight ); } else { VectorCopy( cl.frame.local.client.origin, cl.refdef.simorg ); VectorCopy( cl.frame.local.client.view_ofs, cl.refdef.viewheight ); VectorCopy( cl.frame.local.client.velocity, cl.refdef.simvel ); } }
/* ================== CL_ParsePacketEntities An svc_packetentities has just been parsed, deal with the rest of the data stream. ================== */ void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) { frame_t *newframe, *oldframe; int oldindex, newnum, oldnum; int oldpacket, newpacket; cl_entity_t *player; entity_state_t *oldent; int i, count; // save first uncompressed packet as timestamp if( cls.changelevel && !delta && cls.demorecording ) CL_WriteDemoJumpTime(); // first, allocate packet for new frame count = BF_ReadWord( msg ); newpacket = cl.parsecountmod; newframe = &cl.frames[newpacket]; // allocate parse entities newframe->first_entity = cls.next_client_entities; newframe->num_entities = 0; newframe->valid = true; // assume valid if( delta ) { int subtracted; oldpacket = BF_ReadByte( msg ); subtracted = ((( cls.netchan.incoming_sequence & 0xFF ) - oldpacket ) & 0xFF ); if( subtracted == 0 ) { Host_Error( "CL_DeltaPacketEntities: update too old, connection dropped.\n" ); return; } if( subtracted >= CL_UPDATE_MASK ) { // we can't use this, it is too old Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return; } oldframe = &cl.frames[oldpacket & CL_UPDATE_MASK]; if(( cls.next_client_entities - oldframe->first_entity ) > ( cls.num_client_entities - 128 )) { Con_NPrintf( 2, "^3Warning:^1 delta frame is too old^7\n" ); CL_FlushEntityPacket( msg ); return; } } else { // this is a full update that we can start delta compressing from now oldframe = NULL; oldpacket = -1; // delta too old or is initial message cl.force_send_usercmd = true; // send reply cls.demowaiting = false; // we can start recording now } // mark current delta state cl.validsequence = cls.netchan.incoming_sequence; oldent = NULL; oldindex = 0; if( !oldframe ) { oldnum = MAX_ENTNUMBER; } else { if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } } while( 1 ) { newnum = BF_ReadWord( msg ); if( !newnum ) break; // end of packet entities if( BF_CheckOverflow( msg )) Host_Error( "CL_ParsePacketEntities: read overflow\n" ); while( oldnum < newnum ) { // one or more entities from the old packet are unchanged CL_DeltaEntity( msg, newframe, oldnum, oldent, true ); oldindex++; if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } } if( oldnum == newnum ) { // delta from previous state CL_DeltaEntity( msg, newframe, newnum, oldent, false ); oldindex++; if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } continue; } if( oldnum > newnum ) { // delta from baseline ? CL_DeltaEntity( msg, newframe, newnum, NULL, false ); continue; } } // any remaining entities in the old frame are copied over while( oldnum != MAX_ENTNUMBER ) { // one or more entities from the old packet are unchanged CL_DeltaEntity( msg, newframe, oldnum, oldent, true ); oldindex++; if( oldindex >= oldframe->num_entities ) { oldnum = MAX_ENTNUMBER; } else { oldent = &cls.packet_entities[(oldframe->first_entity+oldindex) % cls.num_client_entities]; oldnum = oldent->number; } } cl.frame = *newframe; if( !cl.frame.valid ) return; player = CL_GetLocalPlayer(); if( cls.state != ca_active ) { // client entered the game cls.state = ca_active; cl.force_refdef = true; cls.changelevel = false; // changelevel is done cls.changedemo = false; // changedemo is done SCR_MakeLevelShot(); // make levelshot if needs Cvar_SetFloat( "scr_loading", 0.0f ); // reset progress bar if(( cls.demoplayback || cls.disable_servercount != cl.servercount ) && cl.video_prepped ) SCR_EndLoadingPlaque(); // get rid of loading plaque } // update local player states clgame.dllFuncs.pfnTxferLocalOverrides( &player->curstate, &newframe->local.client ); // update state for all players for( i = 0; i < cl.maxclients; i++ ) { cl_entity_t *ent = CL_GetEntityByIndex( i + 1 ); if( !ent ) continue; clgame.dllFuncs.pfnProcessPlayerState( &newframe->playerstate[i], &ent->curstate ); newframe->playerstate[i].number = ent->index; } cl.frame = *newframe; }
/* =============== V_SetupRefDef update refdef values each frame =============== */ void V_SetupRefDef( void ) { cl_entity_t *clent; int size; int sb_lines; clent = CL_GetLocalPlayer (); clgame.entities->curstate.scale = clgame.movevars.waveHeight; VectorCopy( cl.frame.local.client.punchangle, cl.refdef.punchangle ); clgame.viewent.curstate.modelindex = cl.frame.local.client.viewmodel; clgame.viewent.model = Mod_Handle( clgame.viewent.curstate.modelindex ); clgame.viewent.curstate.number = cl.playernum + 1; clgame.viewent.curstate.entityType = ET_NORMAL; clgame.viewent.index = cl.playernum + 1; cl.refdef.movevars = &clgame.movevars; cl.refdef.onground = ( cl.frame.local.client.flags & FL_ONGROUND ) ? 1 : 0; cl.refdef.health = cl.frame.local.client.health; cl.refdef.playernum = cl.playernum; cl.refdef.max_entities = clgame.maxEntities; cl.refdef.maxclients = cl.maxclients; cl.refdef.time = cl.time; cl.refdef.frametime = cl.time - cl.oldtime; cl.refdef.demoplayback = cls.demoplayback; cl.refdef.smoothing = cl_smooth->integer; cl.refdef.viewsize = scr_viewsize->integer; cl.refdef.waterlevel = cl.frame.local.client.waterlevel; cl.refdef.onlyClientDraw = 0; // reset clientdraw cl.refdef.hardware = true; // always true cl.refdef.spectator = (clent->curstate.spectator != 0); cl.refdef.nextView = 0; SCR_AddDirtyPoint( 0, 0 ); SCR_AddDirtyPoint( scr_width->integer - 1, scr_height->integer - 1 ); if( cl.refdef.viewsize >= 120 ) sb_lines = 0; // no status bar at all else if( cl.refdef.viewsize >= 110 ) sb_lines = 24; // no inventory else sb_lines = 48; size = min( scr_viewsize->integer, 100 ); cl.refdef.viewport[2] = scr_width->integer * size / 100; cl.refdef.viewport[3] = scr_height->integer * size / 100; if( cl.refdef.viewport[3] > scr_height->integer - sb_lines ) cl.refdef.viewport[3] = scr_height->integer - sb_lines; if( cl.refdef.viewport[3] > scr_height->integer ) cl.refdef.viewport[3] = scr_height->integer; cl.refdef.viewport[0] = (scr_width->integer - cl.refdef.viewport[2]) / 2; cl.refdef.viewport[1] = (scr_height->integer - sb_lines - cl.refdef.viewport[3]) / 2; // calc FOV cl.refdef.fov_x = cl.data.fov; // this is a final fov value cl.refdef.fov_y = V_CalcFov( &cl.refdef.fov_x, cl.refdef.viewport[2], cl.refdef.viewport[3] ); // adjust FOV for widescreen if( glState.wideScreen && r_adjust_fov->integer ) V_AdjustFov( &cl.refdef.fov_x, &cl.refdef.fov_y, cl.refdef.viewport[2], cl.refdef.viewport[3], false ); if( CL_IsPredicted( ) && !cl.refdef.demoplayback ) { VectorCopy( cl.predicted_origin, cl.refdef.simorg ); VectorCopy( cl.predicted_velocity, cl.refdef.simvel ); VectorCopy( cl.predicted_viewofs, cl.refdef.viewheight ); } else { VectorCopy( cl.frame.local.client.origin, cl.refdef.simorg ); VectorCopy( cl.frame.local.client.view_ofs, cl.refdef.viewheight ); VectorCopy( cl.frame.local.client.velocity, cl.refdef.simvel ); } }
/* ================= 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( cls.demoplayback && cl.refdef.cmd != NULL ) { // restore viewangles from cmd.angles VectorCopy( cl.refdef.cmd->viewangles, cl.refdef.cl_viewangles ); } if( cl.refdef.paused || cls.key_dest == key_menu ) return; player = CL_GetLocalPlayer (); viewent = CL_GetEntityByIndex( cl.refdef.viewentity ); cd = &cl.frame.local.client; // 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; CL_PostRunCmd( cl.refdef.cmd, cls.lastoutgoingcommand ); 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[current_command_mod]; // motor! clgame.dllFuncs.pfnPlayerMove( clgame.pmove, false ); // run frames clgame.pmove->runfuncs = ( current_command > outgoing_command - 1 ) ? true : false; 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->velocity, cl.predicted_velocity ); }
/* ================= CL_PredictMovement Sets cl.predicted_origin and cl.predicted_angles ================= */ void CL_PredictMovement() { int frame; 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( cls.demoplayback && cl.refdef.cmd != NULL ) VectorCopy( cl.refdef.cmd->viewangles, cl.refdef.cl_viewangles ); // restore viewangles from cmd.angles if(cl.refdef.paused /*|| cls.key_dest == key_menu*/) //return; player = CL_GetLocalPlayer (); viewent = CL_GetEntityByIndex( cl.refdef.viewentity ); cd = &cl.frame.local.client; // 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 expect it clgame.pmove->runfuncs = true; CL_PostRunCmd( cl.refdef.cmd, cls.lastoutgoingcommand ); return; }; ack = cls.netchan.incoming_acknowledged; outgoing_command = cls.netchan.outgoing_sequence; // if we are too far out of date, just freeze if(outgoing_command - ack >= CL_UPDATE_BACKUP) { //if(cl_showmiss->value) //Com_Printf ("exceeded CL_UPDATE_BACKUP\n"); return; }; ASSERT( cl.refdef.cmd != NULL ); // setup initial pmove state CL_SetupPMove( clgame.pmove, cd, &player->curstate, cl.refdef.cmd ); clgame.pmove->runfuncs = false; frame = 0; while(++ack < outgoing_command) { // we've run too far forward //if( frame >= CL_UPDATE_BACKUP - 1 ) // break; frame = ack & (CL_UPDATE_BACKUP - 1); // 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 // this was the most up to date command if(current_command >= outgoing_command) break; clgame.pmove->cmd = cl.cmds[current_command_mod]; // Call the client dll player movement function (and it will call the PM_Move()) clgame.dllFuncs.pfnPlayerMove(clgame.pmove, false); // run frames clgame.pmove->runfuncs = ( current_command > outgoing_command - 1 ) ? true : false; // Prevent to play sounds/etc more that once frame++; }; CL_PostRunCmd( cl.refdef.cmd, cls.lastoutgoingcommand ); // copy results out for rendering VectorCopy( clgame.pmove->view_ofs, cl.predicted_viewofs ); VectorCopy( clgame.pmove->origin, cl.predicted_origin ); VectorCopy( clgame.pmove->velocity, cl.predicted_velocity ); };