/* ======================= 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_BeginClientSnapshot(client_t *client, msg_t *msg) { static byte tempSnapshotMsgBuf[0x20000]; MSG_Init( msg, tempSnapshotMsgBuf, sizeof(tempSnapshotMsgBuf) ); MSG_ClearLastReferencedEntity( msg ); MSG_WriteLong( msg, client->lastClientCommand ); if ( client->state == CS_ACTIVE || client->state == CS_ZOMBIE ) SV_UpdateServerCommandsToClient( client, msg ); }
void SV_CreateClientGameStateMessage( client_t *client, msg_t *msg ) { int start; entityState_t *base, nullstate; // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, msg ); // send the gamestate MSG_WriteByte( msg, svc_gamestate ); MSG_WriteLong( msg, client->reliableSequence ); // write the configstrings for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) { if (sv.configstrings[start][0]) { MSG_WriteByte( msg, svc_configstring ); MSG_WriteShort( msg, start ); MSG_WriteBigString( msg, sv.configstrings[start] ); } } // write the baselines Com_Memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[start].baseline; if ( !base->number ) { continue; } MSG_WriteByte( msg, svc_baseline ); MSG_WriteDeltaEntity( msg, &nullstate, base, qtrue ); } MSG_WriteByte( msg, svc_EOF ); MSG_WriteLong( msg, client - svs.clients); // write the checksum feed MSG_WriteLong( msg, sv.checksumFeed); // For old RMG system. MSG_WriteShort ( msg, 0 ); }
/* ======================= 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_SendClientMapChange( client_t *client ) { msg_t msg; byte msgBuffer[MAX_MSGLEN]; MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, &msg ); // send the gamestate MSG_WriteByte( &msg, svc_mapchange ); // deliver this to the client SV_SendMessageToClient( &msg, client ); }
/* ======================= 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 ); }
/* ================ SV_SendClientGameState Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each new map load. It will be resent if the client acknowledges a later message but has the wrong gamestate. ================ */ void SV_SendClientGameState( client_t *client ) { int start; entityState_t *base, nullstate; msg_t msg; byte msgBuffer[ MAX_MSGLEN ]; Log::Debug( "SV_SendClientGameState() for %s", client->name ); Log::Debug( "Going from CS_CONNECTED to CS_PRIMED for %s", client->name ); client->state = clientState_t::CS_PRIMED; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit client->gamestateMessageNum = client->netchan.outgoingSequence; MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, &msg ); // send the gamestate MSG_WriteByte( &msg, svc_gamestate ); MSG_WriteLong( &msg, client->reliableSequence ); // write the configstrings for ( start = 0; start < MAX_CONFIGSTRINGS; start++ ) { if ( sv.configstrings[ start ][ 0 ] ) { MSG_WriteByte( &msg, svc_configstring ); MSG_WriteShort( &msg, start ); MSG_WriteBigString( &msg, sv.configstrings[ start ] ); } } // write the baselines memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[ start ].baseline; if ( !base->number ) { continue; } MSG_WriteByte( &msg, svc_baseline ); MSG_WriteDeltaEntity( &msg, &nullstate, base, true ); } MSG_WriteByte( &msg, svc_EOF ); MSG_WriteLong( &msg, client - svs.clients ); // write the checksum feed MSG_WriteLong( &msg, sv.checksumFeed ); // NERVE - SMF - debug info Log::Debug( "Sending %i bytes in gamestate to client: %li", msg.cursize, ( long )( client - svs.clients ) ); // deliver this to the client SV_SendMessageToClient( &msg, client ); }
/* ================ SV_SendClientGameState Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each new map load. It will be resent if the client acknowledges a later message but has the wrong gamestate. ================ */ void SV_SendClientGameState( client_t *client ) { int start; entityState_t *base, nullstate; msg_t msg; byte msgBuffer[MAX_MSGLEN]; // 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() [2] for %s, writing out old fragments\n", client->name); SV_Netchan_TransmitNextFragment(&client->netchan); } Com_DPrintf ("SV_SendClientGameState() for %s\n", client->name); Com_DPrintf( "Going from CS_CONNECTED to CS_PRIMED for %s\n", client->name ); client->state = CS_PRIMED; client->pureAuthentic = 0; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit client->gamestateMessageNum = client->netchan.outgoingSequence; MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, &msg ); // send the gamestate MSG_WriteByte( &msg, svc_gamestate ); MSG_WriteLong( &msg, client->reliableSequence ); // write the configstrings for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) { if (sv.configstrings[start][0]) { MSG_WriteByte( &msg, svc_configstring ); MSG_WriteShort( &msg, start ); MSG_WriteBigString( &msg, sv.configstrings[start] ); } } // write the baselines Com_Memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[start].baseline; if ( !base->number ) { continue; } MSG_WriteByte( &msg, svc_baseline ); MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue ); } MSG_WriteByte( &msg, svc_EOF ); MSG_WriteLong( &msg, client - svs.clients); // write the checksum feed MSG_WriteLong( &msg, sv.checksumFeed); //rwwRMG - send info for the terrain if ( TheRandomMissionManager ) { z_stream zdata; // Send the height map memset(&zdata, 0, sizeof(z_stream)); deflateInit ( &zdata, Z_MAX_COMPRESSION ); unsigned char heightmap[15000]; zdata.next_out = (unsigned char*)heightmap; zdata.avail_out = 15000; zdata.next_in = TheRandomMissionManager->GetLandScape()->GetHeightMap(); zdata.avail_in = TheRandomMissionManager->GetLandScape()->GetRealArea(); deflate(&zdata, Z_SYNC_FLUSH); MSG_WriteShort ( &msg, (unsigned short)zdata.total_out ); MSG_WriteBits ( &msg, 1, 1 ); MSG_WriteData ( &msg, heightmap, zdata.total_out); deflateEnd(&zdata); // Send the flatten map memset(&zdata, 0, sizeof(z_stream)); deflateInit ( &zdata, Z_MAX_COMPRESSION ); zdata.next_out = (unsigned char*)heightmap; zdata.avail_out = 15000; zdata.next_in = TheRandomMissionManager->GetLandScape()->GetFlattenMap(); zdata.avail_in = TheRandomMissionManager->GetLandScape()->GetRealArea(); deflate(&zdata, Z_SYNC_FLUSH); MSG_WriteShort ( &msg, (unsigned short)zdata.total_out ); MSG_WriteBits ( &msg, 1, 1 ); MSG_WriteData ( &msg, heightmap, zdata.total_out); deflateEnd(&zdata); // Seed is needed for misc ents and noise MSG_WriteLong ( &msg, TheRandomMissionManager->GetLandScape()->get_rand_seed ( ) ); SV_WriteRMGAutomapSymbols ( &msg ); } else { MSG_WriteShort ( &msg, 0 ); } // deliver this to the client SV_SendMessageToClient( &msg, client ); }
/* ================ SV_SendClientGameState Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each new map load. It will be resent if the client acknowledges a later message but has the wrong gamestate. ================ */ void SV_SendClientGameState( client_t *client ) { int start; entityState_t *base, nullstate; msg_t msg; byte msgBuffer[MAX_MSGLEN]; // 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() [2] for %s, writing out old fragments\n", client->name); SV_Netchan_TransmitNextFragment(&client->netchan); } Com_DPrintf ("SV_SendClientGameState() for %s\n", client->name); Com_DPrintf( "Going from CS_CONNECTED to CS_PRIMED for %s\n", client->name ); client->state = CS_PRIMED; client->pureAuthentic = 0; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit client->gamestateMessageNum = client->netchan.outgoingSequence; MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, &msg ); // send the gamestate MSG_WriteByte( &msg, svc_gamestate ); MSG_WriteLong( &msg, client->reliableSequence ); // write the configstrings for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) { if (sv.configstrings[start][0]) { MSG_WriteByte( &msg, svc_configstring ); MSG_WriteShort( &msg, start ); MSG_WriteBigString( &msg, sv.configstrings[start] ); } } // write the baselines Com_Memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[start].baseline; if ( !base->number ) { continue; } MSG_WriteByte( &msg, svc_baseline ); MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue ); } MSG_WriteByte( &msg, svc_EOF ); MSG_WriteLong( &msg, client - svs.clients); // write the checksum feed MSG_WriteLong( &msg, sv.checksumFeed); // deliver this to the client SV_SendMessageToClient( &msg, client ); }
/* ================ SV_SendClientGameState Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each new map load. It will be resent if the client acknowledges a later message but has the wrong gamestate. ================ */ static void SV_SendClientGameState( client_t *client ) { int start; entityState_t *base, nullstate; msg_t msg; byte msgBuffer[MAX_MSGLEN]; Com_DPrintf ("SV_SendClientGameState() for %s\n", client->name); Com_DPrintf( "Going from CS_CONNECTED to CS_PRIMED for %s\n", client->name ); client->state = CS_PRIMED; client->pureAuthentic = 0; client->gotCP = qfalse; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit client->gamestateMessageNum = client->netchan.outgoingSequence; MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, &msg ); // send the gamestate MSG_WriteByte( &msg, svc_gamestate ); MSG_WriteLong( &msg, client->reliableSequence ); // write the configstrings for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) { if (sv.configstrings[start][0]) { MSG_WriteByte( &msg, svc_configstring ); MSG_WriteShort( &msg, start ); MSG_WriteBigString( &msg, sv.configstrings[start] ); } } // write the baselines Com_Memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[start].baseline; if ( !base->number ) { continue; } MSG_WriteByte( &msg, svc_baseline ); MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue ); } MSG_WriteByte( &msg, svc_EOF ); MSG_WriteLong( &msg, client - svs.clients); // write the checksum feed MSG_WriteLong( &msg, sv.checksumFeed); // There's something unknown going on here at the moment. // For now we're just going with default of adding a 0 short to keep jampded compatability. MSG_WriteShort( &msg, 0); // deliver this to the client SV_SendMessageToClient( &msg, client ); }
void SV_CreateClientGameStateMessage( client_t *client, msg_t *msg, qboolean updateServerCommands ) { int start; entityState_t *base, nullstate; // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received MSG_WriteLong( msg, client->lastClientCommand ); if ( updateServerCommands ) { // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence // with a gamestate and it sets the clc.serverCommandSequence at // the client side SV_UpdateServerCommandsToClient( client, msg ); } // send the gamestate MSG_WriteByte( msg, svc_gamestate ); MSG_WriteLong( msg, client->reliableSequence ); // write the configstrings for ( start = 0 ; start < MAX_CONFIGSTRINGS ; start++ ) { if (sv.configstrings[start][0]) { MSG_WriteByte( msg, svc_configstring ); MSG_WriteShort( msg, start ); MSG_WriteBigString( msg, sv.configstrings[start] ); } } // write the baselines Com_Memset( &nullstate, 0, sizeof( nullstate ) ); for ( start = 0 ; start < MAX_GENTITIES; start++ ) { base = &sv.svEntities[start].baseline; if ( !base->number ) { continue; } MSG_WriteByte( msg, svc_baseline ); MSG_WriteDeltaEntity( msg, &nullstate, base, qtrue ); } MSG_WriteByte( msg, svc_EOF ); MSG_WriteLong( msg, client - svs.clients); // write the checksum feed MSG_WriteLong( msg, sv.checksumFeed); //rwwRMG - send info for the terrain if ( TheRandomMissionManager ) { z_stream zdata; // Send the height map memset(&zdata, 0, sizeof(z_stream)); deflateInit ( &zdata, Z_BEST_COMPRESSION ); unsigned char heightmap[15000]; zdata.next_out = (unsigned char*)heightmap; zdata.avail_out = 15000; zdata.next_in = TheRandomMissionManager->GetLandScape()->GetHeightMap(); zdata.avail_in = TheRandomMissionManager->GetLandScape()->GetRealArea(); deflate(&zdata, Z_SYNC_FLUSH); MSG_WriteShort ( msg, (unsigned short)zdata.total_out ); MSG_WriteBits ( msg, 1, 1 ); MSG_WriteData ( msg, heightmap, zdata.total_out); deflateEnd(&zdata); // Send the flatten map memset(&zdata, 0, sizeof(z_stream)); deflateInit ( &zdata, Z_BEST_COMPRESSION ); zdata.next_out = (unsigned char*)heightmap; zdata.avail_out = 15000; zdata.next_in = TheRandomMissionManager->GetLandScape()->GetFlattenMap(); zdata.avail_in = TheRandomMissionManager->GetLandScape()->GetRealArea(); deflate(&zdata, Z_SYNC_FLUSH); MSG_WriteShort ( msg, (unsigned short)zdata.total_out ); MSG_WriteBits ( msg, 1, 1 ); MSG_WriteData ( msg, heightmap, zdata.total_out); deflateEnd(&zdata); // Seed is needed for misc ents and noise MSG_WriteLong ( msg, TheRandomMissionManager->GetLandScape()->get_rand_seed ( ) ); SV_WriteRMGAutomapSymbols ( msg ); } else { MSG_WriteShort ( msg, 0 ); } }