/* ================== SV_WriteEntitiesToClient ================== */ void SV_WriteEntitiesToClient( sv_client_t *cl, sizebuf_t *msg ) { edict_t *clent; edict_t *viewent; // may be NULL client_frame_t *frame; entity_state_t *state; static sv_ents_t frame_ents; int i, send_pings; clent = cl->edict; if( !SV_IsValidEdict( clent ) ) { SV_DropClient ( cl ); return; } viewent = cl->pViewEntity; // himself or trigger_camera frame = &cl->frames[cl->netchan.outgoing_sequence & SV_UPDATE_MASK]; send_pings = SV_ShouldUpdatePing( cl ); sv.net_framenum++; // now all portal-through entities are invalidate sv.hostflags &= ~SVF_PORTALPASS; // clear everything in this snapshot frame_ents.num_entities = c_fullsend = 0; // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints SV_AddEntitiesToPacket( viewent, clent, frame, &frame_ents ); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. qsort( frame_ents.entities, frame_ents.num_entities, sizeof( frame_ents.entities[0] ), SV_EntityNumbers ); // copy the entity states out frame->num_entities = 0; frame->first_entity = svs.next_client_entities; for( i = 0; i < frame_ents.num_entities; i++ ) { // add it to the circular packet_entities array state = &svs.packet_entities[svs.next_client_entities % svs.num_client_entities]; *state = frame_ents.entities[i]; svs.next_client_entities++; // this should never hit, map should always be restarted first in SV_Frame if( svs.next_client_entities >= 0x7FFFFFFE ) Host_Error( "svs.next_client_entities wrapped\n" ); frame->num_entities++; } SV_EmitPacketEntities( cl, frame, msg ); SV_EmitEvents( cl, frame, msg ); if( send_pings ) SV_EmitPings( msg ); }
/* ================== SV_WriteSnapshotToClient ================== */ static void SV_WriteSnapshotToClient(client_t *client, msg_t *msg) { clientSnapshot_t *frame, *oldframe; int lastframe; int i; int snapFlags; // this is the snapshot we are creating frame = &client->frames[client->netchan.outgoingSequence & PACKET_MASK]; // try to use a previous frame as the source for delta compressing the snapshot if (client->deltaMessage <= 0 || client->state != CS_ACTIVE) { // client is asking for a retransmit oldframe = NULL; lastframe = 0; } else if (client->netchan.outgoingSequence - client->deltaMessage >= (PACKET_BACKUP - 3)) { // client hasn't gotten a good message through in a long time Com_DPrintf("%s: Delta request from out of date packet.\n", client->name); oldframe = NULL; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = &client->frames[client->deltaMessage & PACKET_MASK]; lastframe = client->netchan.outgoingSequence - client->deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if (oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities) { Com_DPrintf("%s: Delta request from out of date entities.\n", client->name); oldframe = NULL; lastframe = 0; } } MSG_WriteByte(msg, svc_snapshot); // NOTE, MRE: now sent at the start of every message from server to client // let the client know which reliable clientCommands we have received // MSG_WriteLong( msg, client->lastClientCommand ); // send over the current server time so the client can drift // its view of time to try to match if (client->oldServerTime) { // The server has not yet got an acknowledgement of the // new gamestate from this client, so continue to send it // a time as if the server has not restarted. Note from // the client's perspective this time is strictly speaking // incorrect, but since it'll be busy loading a map at // the time it doesn't really matter. MSG_WriteLong(msg, sv.time + client->oldServerTime); } else { MSG_WriteLong(msg, sv.time); } // what we are delta'ing from MSG_WriteByte(msg, lastframe); snapFlags = svs.snapFlagServerBit; if (client->rateDelayed) { snapFlags |= SNAPFLAG_RATE_DELAYED; } if (client->state != CS_ACTIVE) { snapFlags |= SNAPFLAG_NOT_ACTIVE; } MSG_WriteByte(msg, snapFlags); // send over the areabits MSG_WriteByte(msg, frame->areabytes); MSG_WriteData(msg, frame->areabits, frame->areabytes); // delta encode the playerstate if (oldframe) { MSG_WriteDeltaPlayerstate(client->netchan.alternateProtocol, msg, &oldframe->ps, &frame->ps); } else { MSG_WriteDeltaPlayerstate(client->netchan.alternateProtocol, msg, NULL, &frame->ps); } // delta encode the entities SV_EmitPacketEntities(client->netchan.alternateProtocol, oldframe, frame, msg); // padding for rate debugging if (sv_padPackets->integer) { for (i = 0; i < sv_padPackets->integer; i++) { MSG_WriteByte(msg, svc_nop); } } }
/* ================== SV_WriteSnapshotToClient ================== */ static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) { clientSnapshot_t *frame, *oldframe; int lastframe; int snapFlags; // this is the snapshot we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // try to use a previous frame as the source for delta compressing the snapshot if ( client->deltaMessage <= 0 || client->state != CS_ACTIVE ) { // client is asking for a retransmit oldframe = NULL; lastframe = 0; } else if ( client->netchan.outgoingSequence - client->deltaMessage >= (PACKET_BACKUP - 3) ) { // client hasn't gotten a good message through in a long time Com_DPrintf ("%s: Delta request from out of date packet.\n", client->name); oldframe = NULL; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = &client->frames[ client->deltaMessage & PACKET_MASK ]; lastframe = client->netchan.outgoingSequence - client->deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if ( oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities ) { Com_DPrintf ("%s: Delta request from out of date entities.\n", client->name); oldframe = NULL; lastframe = 0; } } MSG_WriteByte (msg, svc_snapshot); // let the client know which reliable clientCommands we have received MSG_WriteLong( msg, client->lastClientCommand ); // send over the current server time so the client can drift // its view of time to try to match MSG_WriteLong (msg, sv.time); // we must write a message number, because recorded demos won't have // the same network message sequences MSG_WriteLong (msg, client->netchan.outgoingSequence ); MSG_WriteByte (msg, lastframe); // what we are delta'ing from MSG_WriteLong (msg, client->cmdNum); // we have executed up to here snapFlags = client->rateDelayed | ( client->droppedCommands << 1 ); client->droppedCommands = 0; MSG_WriteByte (msg, snapFlags); // send over the areabits MSG_WriteByte (msg, frame->areabytes); MSG_WriteData (msg, frame->areabits, frame->areabytes); // delta encode the playerstate if ( oldframe ) { MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps ); } else { MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps ); } // delta encode the entities SV_EmitPacketEntities (oldframe, frame, msg); }
/* ================== SV_WriteSnapshotToClient ================== */ static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) { clientSnapshot_t *frame, *oldframe; int lastframe; int i; int snapFlags; // this is the snapshot we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // try to use a previous frame as the source for delta compressing the snapshot if ( client->deltaMessage <= 0 || client->state != CS_ACTIVE ) { // client is asking for a retransmit oldframe = NULL; lastframe = 0; } else if ( client->netchan.outgoingSequence - client->deltaMessage >= (PACKET_BACKUP - 3) ) { // client hasn't gotten a good message through in a long time Com_DPrintf ("%s: Delta request from out of date packet.\n", client->name); oldframe = NULL; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = &client->frames[ client->deltaMessage & PACKET_MASK ]; lastframe = client->netchan.outgoingSequence - client->deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if ( oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities ) { Com_DPrintf ("%s: Delta request from out of date entities.\n", client->name); oldframe = NULL; lastframe = 0; } } MSG_WriteByte (msg, svc_snapshot); // NOTE, MRE: now sent at the start of every message from server to client // let the client know which reliable clientCommands we have received //MSG_WriteLong( msg, client->lastClientCommand ); // send over the current server time so the client can drift // its view of time to try to match MSG_WriteLong (msg, svs.time); // what we are delta'ing from MSG_WriteByte (msg, lastframe); snapFlags = svs.snapFlagServerBit; if ( client->rateDelayed ) { snapFlags |= SNAPFLAG_RATE_DELAYED; } if ( client->state != CS_ACTIVE ) { snapFlags |= SNAPFLAG_NOT_ACTIVE; } MSG_WriteByte (msg, snapFlags); // send over the areabits MSG_WriteByte (msg, frame->areabytes); MSG_WriteData (msg, frame->areabits, frame->areabytes); // delta encode the playerstate if ( oldframe ) { #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps, frame->pDeltaOneBit, frame->pDeltaNumBit ); #else MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps ); #endif if (frame->ps.m_iVehicleNum) { //then write the vehicle's playerstate too if (!oldframe->ps.m_iVehicleNum) { //if last frame didn't have vehicle, then the old vps isn't gonna delta //properly (because our vps on the client could be anything) #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, NULL, NULL, qtrue ); #else MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, qtrue ); #endif } else { #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, &oldframe->vps, &frame->vps, frame->pDeltaOneBitVeh, frame->pDeltaNumBitVeh, qtrue ); #else MSG_WriteDeltaPlayerstate( msg, &oldframe->vps, &frame->vps, qtrue ); #endif } } } else { #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps, NULL, NULL ); #else MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps ); #endif if (frame->ps.m_iVehicleNum) { //then write the vehicle's playerstate too #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, NULL, NULL, qtrue ); #else MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, qtrue ); #endif } } // delta encode the entities SV_EmitPacketEntities (oldframe, frame, msg); // padding for rate debugging if ( sv_padPackets->integer ) { for ( i = 0 ; i < sv_padPackets->integer ; i++ ) { MSG_WriteByte (msg, svc_nop); } } }
/* ================== SV_WriteSnapshotToClient ================== */ static void SV_WriteSnapshotToClient( client_t *client, msg_t *msg ) { clientSnapshot_t *frame, *oldframe; int lastframe; int i; int snapFlags; int deltaMessage; // this is the snapshot we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // bots never acknowledge, but it doesn't matter since the only use case is for serverside demos // in which case we can delta against the very last message every time deltaMessage = client->deltaMessage; if ( client->demo.isBot ) { client->deltaMessage = client->netchan.outgoingSequence; } // try to use a previous frame as the source for delta compressing the snapshot if ( deltaMessage <= 0 || client->state != CS_ACTIVE ) { // client is asking for a retransmit oldframe = NULL; lastframe = 0; } else if ( client->netchan.outgoingSequence - deltaMessage >= (PACKET_BACKUP - 3) ) { // client hasn't gotten a good message through in a long time Com_DPrintf ("%s: Delta request from out of date packet.\n", client->name); oldframe = NULL; lastframe = 0; } else if ( client->demo.demorecording && client->demo.demowaiting ) { // demo is waiting for a non-delta-compressed frame for this client, so don't delta compress oldframe = NULL; lastframe = 0; } else if ( client->demo.minDeltaFrame > deltaMessage ) { // we saved a non-delta frame to the demo and sent it to the client, but the client didn't ack it // we can't delta against an old frame that's not in the demo without breaking the demo. so send // non-delta frames until the client acks. oldframe = NULL; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = &client->frames[ deltaMessage & PACKET_MASK ]; lastframe = client->netchan.outgoingSequence - deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if ( oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities ) { Com_DPrintf ("%s: Delta request from out of date entities.\n", client->name); oldframe = NULL; lastframe = 0; } } if ( oldframe == NULL ) { if ( client->demo.demowaiting ) { // this is a non-delta frame, so we can delta against it in the demo client->demo.minDeltaFrame = client->netchan.outgoingSequence; } client->demo.demowaiting = qfalse; } MSG_WriteByte (msg, svc_snapshot); // NOTE, MRE: now sent at the start of every message from server to client // let the client know which reliable clientCommands we have received //MSG_WriteLong( msg, client->lastClientCommand ); // send over the current server time so the client can drift // its view of time to try to match if( client->oldServerTime && !( client->demo.demorecording && client->demo.isBot ) ) { // The server has not yet got an acknowledgement of the // new gamestate from this client, so continue to send it // a time as if the server has not restarted. Note from // the client's perspective this time is strictly speaking // incorrect, but since it'll be busy loading a map at // the time it doesn't really matter. MSG_WriteLong (msg, sv.time + client->oldServerTime); } else { MSG_WriteLong (msg, sv.time); } // what we are delta'ing from MSG_WriteByte (msg, lastframe); snapFlags = svs.snapFlagServerBit; if ( client->rateDelayed ) { snapFlags |= SNAPFLAG_RATE_DELAYED; } if ( client->state != CS_ACTIVE ) { snapFlags |= SNAPFLAG_NOT_ACTIVE; } MSG_WriteByte (msg, snapFlags); // send over the areabits MSG_WriteByte (msg, frame->areabytes); MSG_WriteData (msg, frame->areabits, frame->areabytes); // delta encode the playerstate if ( oldframe ) { #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps, frame->pDeltaOneBit, frame->pDeltaNumBit ); #else MSG_WriteDeltaPlayerstate( msg, &oldframe->ps, &frame->ps ); #endif if (frame->ps.m_iVehicleNum) { //then write the vehicle's playerstate too if (!oldframe->ps.m_iVehicleNum) { //if last frame didn't have vehicle, then the old vps isn't gonna delta //properly (because our vps on the client could be anything) #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, NULL, NULL, qtrue ); #else MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, qtrue ); #endif } else { #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, &oldframe->vps, &frame->vps, frame->pDeltaOneBitVeh, frame->pDeltaNumBitVeh, qtrue ); #else MSG_WriteDeltaPlayerstate( msg, &oldframe->vps, &frame->vps, qtrue ); #endif } } } else { #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps, NULL, NULL ); #else MSG_WriteDeltaPlayerstate( msg, NULL, &frame->ps ); #endif if (frame->ps.m_iVehicleNum) { //then write the vehicle's playerstate too #ifdef _ONEBIT_COMBO MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, NULL, NULL, qtrue ); #else MSG_WriteDeltaPlayerstate( msg, NULL, &frame->vps, qtrue ); #endif } } // delta encode the entities SV_EmitPacketEntities (oldframe, frame, msg); // padding for rate debugging if ( sv_padPackets->integer ) { for ( i = 0 ; i < sv_padPackets->integer ; i++ ) { MSG_WriteByte (msg, svc_nop); } } }
/* ================== SV_WriteSnapshotToClient ================== */ static void SV_WriteSnapshotToClient(client_t *client, msg_t *msg) { clientSnapshot_t *frame, *oldframe; int lastframe; int snapFlags; // this is the snapshot we are creating frame = &client->frames[client->netchan.outgoingSequence & PACKET_MASK]; // try to use a previous frame as the source for delta compressing the snapshot if (client->deltaMessage <= 0 || client->state != CS_ACTIVE) { // client is asking for a retransmit oldframe = NULL; lastframe = 0; } else if (client->netchan.outgoingSequence - client->deltaMessage >= (PACKET_BACKUP - 3)) { // client hasn't gotten a good message through in a long time Com_DPrintf("%s: Delta request from out of date packet.\n", client->name); oldframe = NULL; lastframe = 0; } else { // we have a valid snapshot to delta from oldframe = &client->frames[client->deltaMessage & PACKET_MASK]; lastframe = client->netchan.outgoingSequence - client->deltaMessage; // the snapshot's entities may still have rolled off the buffer, though if (oldframe->first_entity <= svs.nextSnapshotEntities - svs.numSnapshotEntities) { Com_DPrintf("%s: Delta request from out of date entities.\n", client->name); oldframe = NULL; lastframe = 0; } } MSG_WriteByte(msg, svc_snapshot); // NOTE, MRE: now sent at the start of every message from server to client // let the client know which reliable clientCommands we have received //MSG_WriteLong( msg, client->lastClientCommand ); // send over the current server time so the client can drift // its view of time to try to match MSG_WriteLong(msg, svs.time); // what we are delta'ing from MSG_WriteByte(msg, lastframe); snapFlags = svs.snapFlagServerBit; if (client->rateDelayed) { snapFlags |= SNAPFLAG_RATE_DELAYED; } if (client->state != CS_ACTIVE) { snapFlags |= SNAPFLAG_NOT_ACTIVE; } MSG_WriteByte(msg, snapFlags); // send over the areabits MSG_WriteByte(msg, frame->areabytes); MSG_WriteData(msg, frame->areabits, frame->areabytes); //{ //int sz = msg->cursize; //int usz = msg->uncompsize; // delta encode the playerstate if (oldframe) { MSG_WriteDeltaPlayerstate(msg, &oldframe->ps, &frame->ps); } else { MSG_WriteDeltaPlayerstate(msg, NULL, &frame->ps); } //Com_Printf( "Playerstate delta size: %f\n", ((msg->cursize - sz) * sv_fps->integer) / 8.f ); //} // delta encode the entities SV_EmitPacketEntities(oldframe, frame, msg); // padding for rate debugging if (sv_padPackets->integer) { int i; for (i = 0 ; i < sv_padPackets->integer ; i++) { MSG_WriteByte(msg, svc_nop); } } }
/* ================== SV_WriteFrameToClient ================== */ void SV_WriteFrameToClient( sv_client_t *cl, sizebuf_t *msg ) { client_frame_t *frame, *oldframe; int lastframe; // this is the frame we are creating frame = &cl->frames[sv.framenum & SV_UPDATE_MASK]; if( cl->lastframe <= 0 ) { // client is asking for a retransmit oldframe = NULL; lastframe = -1; } else if( sv.framenum - cl->lastframe >= (SV_UPDATE_BACKUP - 3)) { // client hasn't gotten a good message through in a long time oldframe = NULL; lastframe = -1; } else { // we have a valid message to delta from oldframe = &cl->frames[cl->lastframe & SV_UPDATE_MASK]; lastframe = cl->lastframe; // the snapshot's entities may still have rolled off the buffer, though if( oldframe->first_entity <= svs.next_client_entities - svs.num_client_entities ) { MsgDev( D_WARN, "%s: ^7delta request from out of date entities.\n", cl->name ); oldframe = NULL; lastframe = 0; } } // refresh physinfo if needs if( cl->physinfo_modified ) { cl->physinfo_modified = false; MSG_WriteByte( msg, svc_physinfo ); MSG_WriteString( msg, cl->physinfo ); } // delta encode the events SV_EmitEvents( cl, frame, msg ); MSG_WriteByte( msg, svc_frame ); MSG_WriteLong( msg, sv.framenum ); MSG_WriteLong( msg, sv.time ); // send a servertime each frame MSG_WriteLong( msg, sv.frametime ); MSG_WriteLong( msg, lastframe ); // what we are delta'ing from MSG_WriteByte( msg, cl->surpressCount ); // rate dropped packets MSG_WriteByte( msg, frame->index ); // send a client index cl->surpressCount = 0; // send over the areabits MSG_WriteByte( msg, frame->areabits_size ); // never more than 255 bytes MSG_WriteData( msg, frame->areabits, frame->areabits_size ); // delta encode the entities SV_EmitPacketEntities( oldframe, frame, msg ); }