/* ======================= SV_SendClientSnapshot ======================= */ void SV_SendClientSnapshot( client_t *client ) { byte msg_buf[MAX_MSGLEN]; msg_t msg; // build the snapshot SV_BuildClientSnapshot( client ); // bots need to have their snapshots build, but // the query them directly without needing to be sent if ( client->gentity && client->gentity->svFlags & SVF_BOT ) { return; } MSG_Init (&msg, msg_buf, sizeof(msg_buf)); msg.allowoverflow = qtrue; // (re)send any reliable server commands SV_UpdateServerCommandsToClient( client, &msg ); // send over all the relevant entityState_t // and the playerState_t SV_WriteSnapshotToClient( client, &msg ); // check for overflow if ( msg.overflowed ) { Com_Printf ("WARNING: msg overflowed for %s\n", client->name); MSG_Clear (&msg); } SV_SendMessageToClient( &msg, client ); }
void SV_SendClientSnapshot(client_t *cl){ msg_t msg; SV_SetServerStaticHeader(); SV_BeginClientSnapshot(cl, &msg); if(cl->state == CS_ACTIVE || cl->state == CS_ZOMBIE) SV_WriteSnapshotToClient(cl, &msg); SV_EndClientSnapshot(cl, &msg); SV_GetServerStaticHeader(); }
/* ======================= SV_SendClientSnapshot Also called by SV_FinalMessage ======================= */ void SV_SendClientSnapshot( client_t* client ) { byte msg_buf[MAX_MSGLEN]; msg_s msg; // build the snapshot SV_BuildClientSnapshot( client ); // bots need to have their snapshots build, but // the query them directly without needing to be sent //if ( client->gentity && client->gentity->r.svFlags & SVF_BOT ) { // return; //} MSG_Init( &msg, msg_buf, sizeof( msg_buf ) ); msg.allowoverflow = true; // compression byte is the first byte in all server->client messages msg.oob = true; MSG_WriteByte( &msg, 0 ); msg.oob = false; // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // (re)send any reliable server commands SV_UpdateServerCommandsToClient( client, &msg ); // send over all the relevant entityState_s // and the playerState_s SV_WriteSnapshotToClient( client, &msg ); #ifdef USE_VOIP SV_WriteVoipToClient( client, &msg ); #endif // check for overflow if ( msg.overflowed ) { Com_Printf( "WARNING: msg overflowed for %s\n", client->name ); MSG_Clear( &msg ); } SV_SendMessageToClient( &msg, client ); }
void SV_SendClientMessages( void ) { int i, freeBytes, index; msg_t msg; byte buf[0x20000]; client_t *c; byte snapClients[MAX_CLIENTS]; int numclients = 0; // NERVE - SMF - net debugging /* SV_SendClientMessagesA( ); return; */ sv.bpsTotalBytes = 0; // NERVE - SMF - net debugging sv.ubpsTotalBytes = 0; // NERVE - SMF - net debugging // send a message to each connected client for ( i = 0, c = svs.clients ; i < sv_maxclients->integer ; i++, c++ ) { if ( !c->state ) { snapClients[i] = 0; continue; // not connected } if ( svs.time < c->nextSnapshotTime ) { snapClients[i] = 0; continue; // not time yet } numclients++; // NERVE - SMF - net debugging // send additional message fragments if the last message // was too large to send at once if ( c->netchan.unsentFragments ) { c->nextSnapshotTime = svs.time + SV_RateMsec( c, c->netchan.unsentLength - c->netchan.unsentFragmentStart ); SV_Netchan_TransmitNextFragment( c ); snapClients[i] = 0; continue; } // generate a new message snapClients[i] = 1; if ( c->state == CS_ACTIVE || c->state == CS_ZOMBIE ) SV_BuildClientSnapshot( c ); } SV_SetServerStaticHeader(); for (i = 0, c = svs.clients; i < sv_maxclients->integer; i++, c++) { if(snapClients[i] == 0) continue; SV_BeginClientSnapshot( c, &msg ); if(c->state == CS_ACTIVE || c->state == CS_ZOMBIE) SV_WriteSnapshotToClient( c, &msg ); SV_EndClientSnapshot(c, &msg); SV_SendClientVoiceData( c ); } // NERVE - SMF - net debugging if ( sv_showAverageBPS->integer && numclients > 0 ) { float ave = 0, uave = 0; for ( i = 0; i < MAX_BPS_WINDOW - 1; i++ ) { sv.bpsWindow[i] = sv.bpsWindow[i + 1]; ave += sv.bpsWindow[i]; sv.ubpsWindow[i] = sv.ubpsWindow[i + 1]; uave += sv.ubpsWindow[i]; } sv.bpsWindow[MAX_BPS_WINDOW - 1] = sv.bpsTotalBytes; ave += sv.bpsTotalBytes; sv.ubpsWindow[MAX_BPS_WINDOW - 1] = sv.ubpsTotalBytes; uave += sv.ubpsTotalBytes; if ( sv.bpsTotalBytes >= sv.bpsMaxBytes ) { sv.bpsMaxBytes = sv.bpsTotalBytes; } if ( sv.ubpsTotalBytes >= sv.ubpsMaxBytes ) { sv.ubpsMaxBytes = sv.ubpsTotalBytes; } sv.bpsWindowSteps++; if ( sv.bpsWindowSteps >= MAX_BPS_WINDOW ) { float comp_ratio; sv.bpsWindowSteps = 0; ave = ( ave / (float)MAX_BPS_WINDOW ); uave = ( uave / (float)MAX_BPS_WINDOW ); comp_ratio = ( 1 - ave / uave ) * 100.f; sv.ucompAve += comp_ratio; sv.ucompNum++; Com_DPrintf( "bpspc(%2.0f) bps(%2.0f) pk(%i) ubps(%2.0f) upk(%i) cr(%2.2f) acr(%2.2f)\n", ave / (float)numclients, ave, sv.bpsMaxBytes, uave, sv.ubpsMaxBytes, comp_ratio, sv.ucompAve / sv.ucompNum ); } } // -NERVE - SMF if ( sv.state != SS_GAME ) { SV_GetServerStaticHeader(); return; } MSG_Init(&msg, buf, sizeof(buf)); SV_ArchiveSnapshot(&msg); SV_GetServerStaticHeader(); if ( msg.overflowed == qtrue ) { Com_DPrintf("SV_ArchiveSnapshot: ignoring snapshot because it overflowed.\n"); return; } svs.archiveSnaps[svs.nextArchivedSnapshotFrames % 1200].buffer = svs.nextArchivedSnapshotBuffer; svs.archiveSnaps[svs.nextArchivedSnapshotFrames % 1200].msgsize = msg.cursize; index = svs.nextArchivedSnapshotBuffer % 0x2000000; svs.nextArchivedSnapshotBuffer += msg.cursize; if ( svs.nextArchivedSnapshotBuffer >= (signed int)0x7FFFFFFE ) { Com_Error(0, "svs.nextArchivedSnapshotBuffer wrapped"); return; } freeBytes = 0x2000000 - index; if ( msg.cursize > freeBytes ) { memcpy(&svs.archiveSnapBuffer[index], msg.data, freeBytes); memcpy(svs.archiveSnapBuffer, &msg.data[freeBytes], msg.cursize - freeBytes); } else { memcpy(&svs.archiveSnapBuffer[index], msg.data, msg.cursize); } svs.nextArchivedSnapshotFrames++; if ( svs.nextArchivedSnapshotFrames >= (signed int)0x7FFFFFFE ){ Com_Error(0, "svs.nextArchivedSnapshotFrames wrapped"); } }
/* ======================= SV_SendMessageToClient Called by SV_SendClientSnapshot and SV_SendClientGameState ======================= */ void SV_SendMessageToClient( msg_t *msg, client_t *client ) { int rateMsec; // MW - my attempt to fix illegible server message errors caused by // packet fragmentation of initial snapshot. while(client->state&&client->netchan.unsentFragments) { // send additional message fragments if the last message // was too large to send at once Com_Printf ("[ISM]SV_SendClientGameState() [1] for %s, writing out old fragments\n", client->name); SV_Netchan_TransmitNextFragment(&client->netchan); } // record information about the message client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg->cursize; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSent = svs.time; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1; // send the datagram SV_Netchan_Transmit( client, msg ); //msg->cursize, msg->data ); // set nextSnapshotTime based on rate and requested number of updates // local clients get snapshots every frame if ( client->netchan.remoteAddress.type == NA_LOOPBACK || Sys_IsLANAddress (client->netchan.remoteAddress) ) { client->nextSnapshotTime = svs.time - 1; return; } // normal rate / snapshotMsec calculation rateMsec = SV_RateMsec( client, msg->cursize ); if ( rateMsec < client->snapshotMsec ) { // never send more packets than this, no matter what the rate is at rateMsec = client->snapshotMsec; client->rateDelayed = qfalse; } else { client->rateDelayed = qtrue; } client->nextSnapshotTime = svs.time + rateMsec; // don't pile up empty snapshots while connecting if ( client->state != CS_ACTIVE ) { // a gigantic connection message may have already put the nextSnapshotTime // more than a second away, so don't shorten it // do shorten if client is downloading #ifdef _XBOX // No downloads on Xbox if ( client->nextSnapshotTime < svs.time + 1000 ) { #else if ( !*client->downloadName && client->nextSnapshotTime < svs.time + 1000 ) { #endif client->nextSnapshotTime = svs.time + 1000; } } } /* ======================= SV_SendClientSnapshot Also called by SV_FinalMessage ======================= */ extern cvar_t *fs_gamedirvar; void SV_SendClientSnapshot( client_t *client ) { byte msg_buf[MAX_MSGLEN]; msg_t msg; if (!client->sentGamedir) { //rww - if this is the case then make sure there is an svc_setgame sent before this snap int i = 0; MSG_Init (&msg, msg_buf, sizeof(msg_buf)); //have to include this for each message. MSG_WriteLong( &msg, client->lastClientCommand ); MSG_WriteByte (&msg, svc_setgame); while (fs_gamedirvar->string[i]) { MSG_WriteByte(&msg, fs_gamedirvar->string[i]); i++; } MSG_WriteByte(&msg, 0); // MW - my attempt to fix illegible server message errors caused by // packet fragmentation of initial snapshot. //rww - reusing this code here while(client->state&&client->netchan.unsentFragments) { // send additional message fragments if the last message // was too large to send at once Com_Printf ("[ISM]SV_SendClientGameState() [1] for %s, writing out old fragments\n", client->name); SV_Netchan_TransmitNextFragment(&client->netchan); } // record information about the message client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg.cursize; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSent = svs.time; client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageAcked = -1; // send the datagram SV_Netchan_Transmit( client, &msg ); //msg->cursize, msg->data ); client->sentGamedir = qtrue; } // build the snapshot SV_BuildClientSnapshot( client ); // bots need to have their snapshots build, but // the query them directly without needing to be sent if ( client->gentity && client->gentity->r.svFlags & SVF_BOT ) { return; } MSG_Init (&msg, msg_buf, sizeof(msg_buf)); msg.allowoverflow = qtrue; // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // (re)send any reliable server commands SV_UpdateServerCommandsToClient( client, &msg ); // send over all the relevant entityState_t // and the playerState_t SV_WriteSnapshotToClient( client, &msg ); // Add any download data if the client is downloading #ifndef _XBOX // No downloads on Xbox SV_WriteDownloadToClient( client, &msg ); #endif // check for overflow if ( msg.overflowed ) { Com_Printf ("WARNING: msg overflowed for %s\n", client->name); MSG_Clear (&msg); } SV_SendMessageToClient( &msg, client ); }