/* ============= SV_EmitPacketEntities Writes a delta update of an entityState_t list to the message. ============= */ static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to, msg_t *msg ) { sharedEntityState_t *oldent, *newent; int oldindex, newindex; int oldnum, newnum; int from_num_entities; // generate the delta update if ( !from ) { from_num_entities = 0; } else { from_num_entities = from->num_entities; } newent = NULL; oldent = NULL; newindex = 0; oldindex = 0; while ( newindex < to->num_entities || oldindex < from_num_entities ) { if ( newindex >= to->num_entities ) { newnum = 9999; } else { newent = SV_SnapshotEntity( to->first_entity + newindex ); newnum = newent->number; } if ( oldindex >= from_num_entities ) { oldnum = 9999; } else { oldent = SV_SnapshotEntity( from->first_entity + oldindex ); oldnum = oldent->number; } if ( newnum == oldnum ) { // delta update from old position // because the force parm is qfalse, this will not result // in any bytes being emited if the entity has not changed at all MSG_WriteDeltaEntity (msg, oldent, newent, qfalse ); oldindex++; newindex++; continue; } if ( newnum < oldnum ) { // this is a new entity, send it from the baseline MSG_WriteDeltaEntity (msg, DA_ElementPointer( sv.svEntitiesBaseline, newnum ), newent, qtrue ); newindex++; continue; } if ( newnum > oldnum ) { // the old entity isn't present in the new message MSG_WriteDeltaEntity (msg, oldent, NULL, qtrue ); oldindex++; continue; } } MSG_WriteBits( msg, (MAX_GENTITIES-1), GENTITYNUM_BITS ); // end of packetentities }
/* ================== CL_ParseBaseline ================== */ void CL_ParseBaseline( msg_t *msg ) { sharedEntityState_t *es; int newnum; if ( !cgvm ) { Com_Error( ERR_DROP, "Received unexpected baseline" ); } if ( !cl.entityBaselines.pointer ) { Com_Error( ERR_DROP, "cgame needs to call trap_SetNetFields" ); } newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } es = DA_ElementPointer( cl.entityBaselines, newnum ); MSG_ReadDeltaEntity( msg, NULL, es, newnum ); }
/* ================== SV_WriteBaselineToClient ================== */ void SV_WriteBaselineToClient( client_t *client, msg_t *msg ) { sharedEntityState_t *base; int start; if ( !client->needBaseline ) { return; } client->needBaseline = qfalse; // write the baselines for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = (sharedEntityState_t *)DA_ElementPointer( sv.svEntitiesBaseline, start ); if ( !base->number ) { continue; } MSG_WriteByte( msg, svc_baseline ); MSG_WriteDeltaEntity( msg, NULL, base, qtrue ); } }
/* ============= SV_SnapshotPlayer Get pointer to beginning of player state. ============= */ sharedPlayerState_t *SV_SnapshotPlayer( clientSnapshot_t *snap, int num ) { return DA_ElementPointer( snap->playerStates, num ); }
/* ============= SV_SnapshotEntity Get pointer to beginning of entity state. ============= */ sharedEntityState_t *SV_SnapshotEntity( int num ) { return DA_ElementPointer( svs.snapshotEntities, num % svs.numSnapshotEntities ); }
/* ================== CL_ParseEntityState Client only looks at shared part of entityState_t ================== */ sharedEntityState_t *CL_ParseEntityState( int num ) { return DA_ElementPointer( cl.parseEntities, num % cl.parseEntities.maxElements ); }
/* ================ CL_ParseSnapshot If the snapshot is parsed properly, it will be copied to cl.snap and saved in cl.snapshots[]. If the snapshot is invalid for any reason, no changes to the state will be made at all. ================ */ void CL_ParseSnapshot( msg_t *msg ) { int len; clSnapshot_t *old; clSnapshot_t newSnap; sharedPlayerState_t *newPS, *oldPS; int deltaNum; int oldMessageNum; int i, packetNum; if ( !cgvm ) { Com_Error( ERR_DROP, "Received unexpected snapshot" ); } if ( !cl.cgamePlayerStateSize || !cl.cgameEntityStateSize ) { Com_Error( ERR_DROP, "cgame needs to call trap_SetNetFields" ); } // get the reliable sequence acknowledge number // NOTE: now sent with all server to client messages //clc.reliableAcknowledge = MSG_ReadLong( msg ); // read in the new snapshot to a temporary buffer // we will only copy to cl.snap if it is valid Com_Memset (&newSnap, 0, sizeof(newSnap)); // we will have read any new server commands in this // message before we got to svc_snapshot newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = MSG_ReadLong( msg ); // if we were just unpaused, we can only *now* really let the // change come into effect or the client hangs. cl_paused->modified = 0; newSnap.messageNum = clc.serverMessageSequence; deltaNum = MSG_ReadByte( msg ); if ( !deltaNum ) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = MSG_ReadByte( msg ); // If the frame is delta compressed from data that we // no longer have available, we must suck up the rest of // the frame, but not use it, then ask for a non-compressed // message if ( newSnap.deltaNum <= 0 ) { newSnap.valid = qtrue; // uncompressed frame old = NULL; clc.demowaiting = qfalse; // we can start recording now } else { old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK]; if ( !old->valid ) { // should never happen Com_Printf ("Delta from invalid frame (not supposed to happen!).\n"); } else if ( old->messageNum != newSnap.deltaNum ) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. Com_Printf ("Delta frame too old.\n"); } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > cl.parseEntities.maxElements - MAX_SNAPSHOT_ENTITIES * CL_MAX_SPLITVIEW ) { Com_Printf ("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } DA_Clear( &cl.tempSnapshotPS ); // read playerinfo SHOWNET( msg, "playerstate" ); newSnap.numPSs = MSG_ReadByte( msg ); if (newSnap.numPSs > MAX_SPLITVIEW) { Com_DPrintf(S_COLOR_YELLOW "Warning: Got numPSs as %d (max=%d)\n", newSnap.numPSs, MAX_SPLITVIEW); newSnap.numPSs = MAX_SPLITVIEW; } for (i = 0; i < MAX_SPLITVIEW; i++) { newSnap.localPlayerIndex[i] = MSG_ReadByte( msg ); newSnap.playerNums[i] = MSG_ReadByte( msg ); // -1 gets converted to 255 should be set to -1 (and so should all invalid values) if ( newSnap.localPlayerIndex[i] >= newSnap.numPSs || newSnap.playerNums[i] >= MAX_CLIENTS ) { newSnap.localPlayerIndex[i] = -1; newSnap.playerNums[i] = -1; } // read areamask len = MSG_ReadByte( msg ); if(len > sizeof(newSnap.areamask[0])) { Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len); return; } MSG_ReadData( msg, &newSnap.areamask[i], len); } for (i = 0; i < MAX_SPLITVIEW; i++) { // Read player states if (newSnap.localPlayerIndex[i] != -1) { newPS = (sharedPlayerState_t *) DA_ElementPointer( cl.tempSnapshotPS, newSnap.localPlayerIndex[i] ); if ( old && old->valid && old->localPlayerIndex[i] != -1 ) { oldPS = (sharedPlayerState_t *) DA_ElementPointer( old->playerStates, old->localPlayerIndex[i] ); MSG_ReadDeltaPlayerstate( msg, oldPS, newPS, newSnap.playerNums[i] ); } else { MSG_ReadDeltaPlayerstate( msg, NULL, newPS, newSnap.playerNums[i] ); } } // Server added or removed local player if ( old && old->playerNums[i] != newSnap.playerNums[i] ) { CL_LocalPlayerRemoved( i ); if ( newSnap.playerNums[i] != -1 ) { CL_LocalPlayerAdded( i, newSnap.playerNums[i] ); } } } // read packet entities SHOWNET( msg, "packet entities" ); CL_ParsePacketEntities( msg, old, &newSnap ); // if not valid, dump the entire thing now that it has // been properly read if ( !newSnap.valid ) { return; } // clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer oldMessageNum = cl.snap.messageNum + 1; if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) { oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 ); } for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) { cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; } // copy player states from temp to snapshot DA_Copy( cl.tempSnapshotPS, &cl.snapshots[newSnap.messageNum & PACKET_MASK].playerStates ); newSnap.playerStates = cl.snapshots[newSnap.messageNum & PACKET_MASK].playerStates; // copy to the current good spot cl.snap = newSnap; cl.snap.ping = 999; // calculate ping time for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; newPS = (sharedPlayerState_t *) DA_ElementPointer( cl.snap.playerStates, 0 ); if ( newPS->commandTime >= cl.outPackets[ packetNum ].p_serverTime ) { cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime; break; } } // save the frame off in the backup array for later delta comparisons cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap; if (cl_shownet->integer == 3) { Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum, cl.snap.deltaNum, cl.snap.ping ); } cl.newSnapshots = qtrue; }
/* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) { int newnum; sharedEntityState_t *oldstate; int oldindex, oldnum; newframe->parseEntitiesNum = cl.parseEntitiesNum; newframe->numEntities = 0; // delta from the entities present in oldframe oldindex = 0; oldstate = NULL; if (!oldframe) { oldnum = 99999; } else { if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = CL_ParseEntityState(oldframe->parseEntitiesNum + oldindex); oldnum = oldstate->number; } } while ( 1 ) { // read the entity index number newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum == (MAX_GENTITIES-1) ) { break; } if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message"); } while ( oldnum < newnum ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = CL_ParseEntityState(oldframe->parseEntitiesNum + oldindex); oldnum = oldstate->number; } } if (oldnum == newnum) { // delta from previous state if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum); } CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = CL_ParseEntityState(oldframe->parseEntitiesNum + oldindex); oldnum = oldstate->number; } continue; } if ( oldnum > newnum ) { // delta from baseline if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum); } CL_DeltaEntity( msg, newframe, newnum, DA_ElementPointer( cl.entityBaselines, newnum ), qfalse ); continue; } } // any remaining entities in the old frame are copied over while ( oldnum != 99999 ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = CL_ParseEntityState(oldframe->parseEntitiesNum + oldindex); oldnum = oldstate->number; } } }