void demoCutParseRMG(msg_t *msg, clientConnection_t *clcCut, clientActive_t *clCut) { int i; clcCut->rmgHeightMapSize = (unsigned short)MSG_ReadShort(msg); if (clcCut->rmgHeightMapSize == 0) { return; } z_stream zdata; int flatDataSize; unsigned char heightmap1[15000]; // height map if (MSG_ReadBits(msg, 1)) { memset(&zdata, 0, sizeof(z_stream)); inflateInit(&zdata/*, Z_SYNC_FLUSH*/); MSG_ReadData (msg, heightmap1, clcCut->rmgHeightMapSize); zdata.next_in = heightmap1; zdata.avail_in = clcCut->rmgHeightMapSize; zdata.next_out = (unsigned char*)clcCut->rmgHeightMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate(&zdata,Z_SYNC_FLUSH); clcCut->rmgHeightMapSize = zdata.total_out; inflateEnd(&zdata); } else { MSG_ReadData(msg, (unsigned char*)clcCut->rmgHeightMap, clcCut->rmgHeightMapSize); } // Flatten map flatDataSize = MSG_ReadShort(msg); if (MSG_ReadBits(msg, 1)) { // Read the flatten map memset(&zdata, 0, sizeof(z_stream)); inflateInit(&zdata/*, Z_SYNC_FLUSH*/); MSG_ReadData ( msg, heightmap1, flatDataSize); zdata.next_in = heightmap1; zdata.avail_in = clcCut->rmgHeightMapSize; zdata.next_out = (unsigned char*)clcCut->rmgFlattenMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate(&zdata, Z_SYNC_FLUSH); inflateEnd(&zdata); } else { MSG_ReadData(msg, (unsigned char*)clcCut->rmgFlattenMap, flatDataSize); } // Seed clcCut->rmgSeed = MSG_ReadLong(msg); // Automap symbols clcCut->rmgAutomapSymbolCount = (unsigned short)MSG_ReadShort(msg); for(i = 0; i < clcCut->rmgAutomapSymbolCount; i++) { clcCut->rmgAutomapSymbols[i].mType = (int)MSG_ReadByte(msg); clcCut->rmgAutomapSymbols[i].mSide = (int)MSG_ReadByte(msg); clcCut->rmgAutomapSymbols[i].mOrigin[0] = (float)MSG_ReadLong(msg); clcCut->rmgAutomapSymbols[i].mOrigin[1] = (float)MSG_ReadLong(msg); } }
void CL_ParseZPacket (void) { #ifndef NO_ZLIB byte buff_in[MAX_MSGLEN]; byte buff_out[0xFFFF]; sizebuf_t sb, old; int16 compressed_len = (int16)MSG_ReadShort (&net_message); int16 uncompressed_len = (int16)MSG_ReadShort (&net_message); if (uncompressed_len <= 0) Com_Error (ERR_DROP, "CL_ParseZPacket: uncompressed_len <= 0"); if (compressed_len <= 0) Com_Error (ERR_DROP, "CL_ParseZPacket: compressed_len <= 0"); MSG_ReadData (&net_message, buff_in, compressed_len); SZ_Init (&sb, buff_out, uncompressed_len); sb.cursize = ZLibDecompress (buff_in, compressed_len, buff_out, uncompressed_len, -15); old = net_message; net_message = sb; CL_ParseServerMessage (); net_message = old; Com_DPrintf ("Got a ZPacket, %d->%d\n", uncompressed_len + 4, compressed_len); #else Com_Error (ERR_DROP, "Receied a zPacket but no zlib in this binary"); #endif }
static int read_level_file(void) { char name[MAX_OSPATH]; size_t len, maxlen; int index; len = Q_snprintf(name, MAX_QPATH, "save/" SAVE_CURRENT "/%s.sv2", sv.name); if (len >= MAX_QPATH) return -1; if (read_binary_file(name)) return -1; if (MSG_ReadLong() != SAVE_MAGIC2) return -1; if (MSG_ReadLong() != SAVE_VERSION) return -1; // any error will drop from this point // the rest can't underflow msg_read.allowunderflow = qfalse; // read all configstrings while (1) { index = MSG_ReadShort(); if (index == MAX_CONFIGSTRINGS) break; if (index < 0 || index > MAX_CONFIGSTRINGS) Com_Error(ERR_DROP, "Bad savegame configstring index"); maxlen = CS_SIZE(index); len = MSG_ReadString(sv.configstrings[index], maxlen); if (len >= maxlen) Com_Error(ERR_DROP, "Savegame configstring too long"); } len = MSG_ReadByte(); if (len > MAX_MAP_PORTAL_BYTES) Com_Error(ERR_DROP, "Savegame portalbits too long"); SV_ClearWorld(); CM_SetPortalStates(&sv.cm, MSG_ReadData(len), len); // read game level len = Q_snprintf(name, MAX_OSPATH, "%s/save/" SAVE_CURRENT "/%s.sav", fs_gamedir, sv.name); if (len >= MAX_OSPATH) Com_Error(ERR_DROP, "Savegame path too long"); ge->ReadLevel(name); return 0; }
void CL_ParseZPacket (sizebuf_t *msg) { byte buff_in[MAX_MSGLEN]; byte buff_out[0xFFFF]; sizebuf_t sb; uint16 compressed_len = MSG_ReadShort (msg); uint16 uncompressed_len = MSG_ReadShort (msg); if (uncompressed_len <= 0) Com_Error (ERR_DROP, "CL_ParseZPacket: uncompressed_len <= 0"); if (compressed_len <= 0) Com_Error (ERR_DROP, "CL_ParseZPacket: compressed_len <= 0"); MSG_ReadData (msg, buff_in, compressed_len); SZ_Init (&sb, buff_out, uncompressed_len); sb.cursize = ZLibDecompress (buff_in, compressed_len, buff_out, uncompressed_len, -15); CL_ParseServerMessage(&sb); Com_DPrintf ("Got a ZPacket, %d->%d\n", uncompressed_len + 4, compressed_len); }
/* ================ CL_ParseSnapshot If the snapshot is parsed properly, it will be copied to cl.frame and saved in cl.frames[]. 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; int deltaNum; int oldMessageNum; int i, packetNum; // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong( msg ); // read in the new snapshot to a temporary buffer // we will only copy to cl.frame if it is valid memset (&newSnap, 0, sizeof(newSnap)); newSnap.serverCommandNum = clc.serverCommandSequence; newSnap.serverTime = MSG_ReadLong( msg ); newSnap.messageNum = MSG_ReadLong( msg ); deltaNum = MSG_ReadByte( msg ); if ( !deltaNum ) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.cmdNum = MSG_ReadLong( msg ); 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; } else { old = &cl.frames[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 > MAX_PARSE_ENTITIES ) { Com_Printf ("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte( msg ); MSG_ReadData( msg, &newSnap.areamask, len); // read playerinfo SHOWNET( msg, "playerstate" ); if ( old ) { MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); } else { MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); } // 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 oldMessageNum = cl.frame.messageNum + 1; if ( cl.frame.messageNum - oldMessageNum >= PACKET_BACKUP ) { oldMessageNum = cl.frame.messageNum - ( PACKET_BACKUP - 1 ); } for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) { cl.frames[oldMessageNum & PACKET_MASK].valid = qfalse; } // copy to the current good spot cl.frame = newSnap; // calculate ping time for ( i = 0 ; i < PACKET_BACKUP ; i++ ) { packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK; if ( cl.frame.cmdNum == cl.packetCmdNumber[ packetNum ] ) { cl.frame.ping = cls.realtime - cl.packetTime[ packetNum ]; break; } } // save the frame off in the backup array for later delta comparisons cl.frames[cl.frame.messageNum & PACKET_MASK] = cl.frame; if (cl_shownet->integer == 3) { Com_Printf (" frame:%i delta:%i\n", cl.frame.messageNum, cl.frame.deltaNum); } // actions for valid frames cl.newSnapshots = qtrue; }
static void SV_UserVoip( client_t *cl, msg_t *msg ) { const int sender = (int) (cl - svs.clients); const int generation = MSG_ReadByte(msg); const int sequence = MSG_ReadLong(msg); const int frames = MSG_ReadByte(msg); const int recip1 = MSG_ReadLong(msg); const int recip2 = MSG_ReadLong(msg); const int recip3 = MSG_ReadLong(msg); const int packetsize = MSG_ReadShort(msg); byte encoded[sizeof (cl->voipPacket[0].data)]; client_t *client = NULL; voipServerPacket_t *packet = NULL; int i; if (generation < 0) return; // short/invalid packet, bail. else if (sequence < 0) return; // short/invalid packet, bail. else if (frames < 0) return; // short/invalid packet, bail. else if (recip1 < 0) return; // short/invalid packet, bail. else if (recip2 < 0) return; // short/invalid packet, bail. else if (recip3 < 0) return; // short/invalid packet, bail. else if (packetsize < 0) return; // short/invalid packet, bail. if (packetsize > sizeof (encoded)) { // overlarge packet? int bytesleft = packetsize; while (bytesleft) { int br = bytesleft; if (br > sizeof (encoded)) br = sizeof (encoded); MSG_ReadData(msg, encoded, br); bytesleft -= br; } return; // overlarge packet, bail. } MSG_ReadData(msg, encoded, packetsize); if (SV_ShouldIgnoreVoipSender(cl)) return; // Blacklisted, disabled, etc. // !!! FIXME: see if we read past end of msg... // !!! FIXME: reject if not speex narrowband codec. // !!! FIXME: decide if this is bogus data? // (the three recip* values are 31 bits each (ignores sign bit so we can // get a -1 error from MSG_ReadLong() ... ), allowing for 93 clients.) assert( sv_maxclients->integer < 93 ); // decide who needs this VoIP packet sent to them... for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { if (client->state != CS_ACTIVE) continue; // not in the game yet, don't send to this guy. else if (i == sender) continue; // don't send voice packet back to original author. else if (!client->hasVoip) continue; // no VoIP support, or support disabled. else if (client->muteAllVoip) continue; // client is ignoring everyone. else if (client->ignoreVoipFromClient[sender]) continue; // client is ignoring this talker. else if (*cl->downloadName) // !!! FIXME: possible to DoS? continue; // no VoIP allowed if downloading, to save bandwidth. else if ( ((i >= 0) && (i < 31)) && ((recip1 & (1 << (i-0))) == 0) ) continue; // not addressed to this player. else if ( ((i >= 31) && (i < 62)) && ((recip2 & (1 << (i-31))) == 0) ) continue; // not addressed to this player. else if ( ((i >= 62) && (i < 93)) && ((recip3 & (1 << (i-62))) == 0) ) continue; // not addressed to this player. // Transmit this packet to the client. // !!! FIXME: I don't like this queueing system. if (client->queuedVoipPackets >= (sizeof (client->voipPacket) / sizeof (client->voipPacket[0]))) { Com_Printf("Too many VoIP packets queued for client #%d\n", i); continue; // no room for another packet right now. } packet = &client->voipPacket[client->queuedVoipPackets]; packet->sender = sender; packet->frames = frames; packet->len = packetsize; packet->generation = generation; packet->sequence = sequence; memcpy(packet->data, encoded, packetsize); client->queuedVoipPackets++; } }
/* * CL_ParseGetServersResponseMessage * Handle a reply from getservers message to master server */ static void CL_ParseGetServersResponseMessage( msg_t *msg, qboolean extended ) { const char *header; char adrString[64]; qbyte addr[16]; unsigned short port; netadr_t adr; MSG_BeginReading( msg ); MSG_ReadLong( msg ); // skip the -1 //jump over the command name header = ( extended ? "getserversExtResponse" : "getserversResponse" ); if( !MSG_SkipData( msg, strlen( header ) ) ) { Com_Printf( "Invalid master packet ( missing %s )\n", header ); return; } while( msg->readcount + 7 <= msg->cursize ) { char prefix = MSG_ReadChar( msg ); switch( prefix ) { case '\\': MSG_ReadData( msg, addr, 4 ); port = ShortSwap( MSG_ReadShort( msg ) ); // both endians need this swapped. Q_snprintfz( adrString, sizeof( adrString ), "%u.%u.%u.%u:%u", addr[0], addr[1], addr[2], addr[3], port ); break; case '/': if( extended ) { MSG_ReadData( msg, addr, 16 ); port = ShortSwap( MSG_ReadShort( msg ) ); // both endians need this swapped. Q_snprintfz( adrString, sizeof( adrString ), "[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%hu", addr[ 0], addr[ 1], addr[ 2], addr[ 3], addr[ 4], addr[ 5], addr[ 6], addr[ 7], addr[ 8], addr[ 9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15], port ); } else { Com_Printf( "Invalid master packet ( IPv6 prefix in a non-extended response )\n" ); return; } break; default: Com_Printf( "Invalid master packet ( missing separator )\n" ); return; } if( port == 0 ) // last server seen return; Com_DPrintf( "%s\n", adrString ); if( !NET_StringToAddress( adrString, &adr ) ) { Com_Printf( "Bad address: %s\n", adrString ); continue; } CL_AddServerToList( &masterList, adrString, 0 ); } }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload( msg_t *msg ) { int size; unsigned char data[ MAX_MSGLEN ]; int block; if ( !*cls.downloadTempName ) { Com_Printf("%s", _( "Server sending download, but no download was requested\n" )); CL_AddReliableCommand( "stopdl" ); return; } // read the data block = MSG_ReadShort( msg ); // TTimo - www dl // if we haven't acked the download redirect yet if ( block == -1 ) { if ( !clc.bWWWDl ) { // server is sending us a www download Q_strncpyz( cls.originalDownloadName, cls.downloadName, sizeof( cls.originalDownloadName ) ); Q_strncpyz( cls.downloadName, MSG_ReadString( msg ), sizeof( cls.downloadName ) ); clc.downloadSize = MSG_ReadLong( msg ); clc.downloadFlags = MSG_ReadLong( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); Com_DPrintf( "Server redirected download: %s\n", cls.downloadName ); clc.bWWWDl = qtrue; // activate wwwdl client loop CL_AddReliableCommand( "wwwdl ack" ); // make sure the server is not trying to redirect us again on a bad checksum if ( strstr( clc.badChecksumList, va( "@%s", cls.originalDownloadName ) ) ) { Com_Printf(_( "refusing redirect to %s by server (bad checksum)\n"), cls.downloadName ); CL_AddReliableCommand( "wwwdl fail" ); clc.bWWWDlAborting = qtrue; return; } // make downloadTempName an OS path Q_strncpyz( cls.downloadTempName, FS_BuildOSPath( Cvar_VariableString( "fs_homepath" ), cls.downloadTempName, "" ), sizeof( cls.downloadTempName ) ); cls.downloadTempName[ strlen( cls.downloadTempName ) - 1 ] = '\0'; if ( !DL_BeginDownload( cls.downloadTempName, cls.downloadName, com_developer->integer ) ) { // setting bWWWDl to false after sending the wwwdl fail doesn't work // not sure why, but I suspect we have to eat all remaining block -1 that the server has sent us // still leave a flag so that CL_WWWDownload is inactive // we count on server sending us a gamestate to start up clean again CL_AddReliableCommand( "wwwdl fail" ); clc.bWWWDlAborting = qtrue; Com_Printf(_( "Failed to initialize download for '%s'\n"), cls.downloadName ); } // Check for a disconnected download // we'll let the server disconnect us when it gets the bbl8r message if ( clc.downloadFlags & ( 1 << DL_FLAG_DISCON ) ) { CL_AddReliableCommand( "wwwdl bbl8r" ); cls.bWWWDlDisconnected = qtrue; } return; } else { // server keeps sending that message till we ack it, eat and ignore //MSG_ReadLong( msg ); MSG_ReadString( msg ); MSG_ReadLong( msg ); MSG_ReadLong( msg ); return; } } if ( !block ) { // block zero is special, contains file size clc.downloadSize = MSG_ReadLong( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); if ( clc.downloadSize < 0 ) { Com_Error( ERR_DROP, "%s", MSG_ReadString( msg ) ); } } size = MSG_ReadShort( msg ); if ( size < 0 || size > sizeof( data ) ) { Com_Error( ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk.", size ); } MSG_ReadData( msg, data, size ); if ( clc.downloadBlock != block ) { Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block ); return; } // open the file if not opened yet if ( !clc.download ) { clc.download = FS_SV_FOpenFileWrite( cls.downloadTempName ); if ( !clc.download ) { Com_Printf(_( "Could not create %s\n"), cls.downloadTempName ); CL_AddReliableCommand( "stopdl" ); CL_NextDownload(); return; } } if ( size ) { FS_Write( data, size, clc.download ); } CL_AddReliableCommand( va( "nextdl %d", clc.downloadBlock ) ); clc.downloadBlock++; clc.downloadCount += size; // So UI gets access to it Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); if ( !size ) { // A zero length block means EOF if ( clc.download ) { FS_FCloseFile( clc.download ); clc.download = 0; // rename the file FS_SV_Rename( cls.downloadTempName, cls.downloadName ); } *cls.downloadTempName = *cls.downloadName = 0; Cvar_Set( "cl_downloadName", "" ); // send intentions now // We need this because without it, we would hold the last nextdl and then start // loading right away. If we take a while to load, the server is happily trying // to send us that last block over and over. // Write it twice to help make sure we acknowledge the download CL_WritePacket(); CL_WritePacket(); // get another file if needed CL_NextDownload(); } }
/* ===================== CL_ParseVoip A VoIP message has been received from the server ===================== */ static void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) { static short decoded[VOIP_MAX_PACKET_SAMPLES*4]; // !!! FIXME: don't hard code const int sender = MSG_ReadShort(msg); const int generation = MSG_ReadByte(msg); const int sequence = MSG_ReadLong(msg); const int frames = MSG_ReadByte(msg); const int packetsize = MSG_ReadShort(msg); const int flags = MSG_ReadBits(msg, VOIP_FLAGCNT); unsigned char encoded[4000]; int numSamples; int seqdiff; int written = 0; int i; Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender); if (sender < 0) return; // short/invalid packet, bail. else if (generation < 0) return; // short/invalid packet, bail. else if (sequence < 0) return; // short/invalid packet, bail. else if (frames < 0) return; // short/invalid packet, bail. else if (packetsize < 0) return; // short/invalid packet, bail. if (packetsize > sizeof (encoded)) { // overlarge packet? int bytesleft = packetsize; while (bytesleft) { int br = bytesleft; if (br > sizeof (encoded)) br = sizeof (encoded); MSG_ReadData(msg, encoded, br); bytesleft -= br; } return; // overlarge packet, bail. } MSG_ReadData(msg, encoded, packetsize); if (ignoreData) { return; // just ignore legacy speex voip data } else if (!clc.voipCodecInitialized) { return; // can't handle VoIP without libopus! } else if (sender >= MAX_CLIENTS) { return; // bogus sender. } else if (CL_ShouldIgnoreVoipSender(sender)) { return; // Channel is muted, bail. } // !!! FIXME: make sure data is narrowband? Does decoder handle this? Com_DPrintf("VoIP: packet accepted!\n"); seqdiff = sequence - clc.voipIncomingSequence[sender]; // This is a new "generation" ... a new recording started, reset the bits. if (generation != clc.voipIncomingGeneration[sender]) { Com_DPrintf("VoIP: new generation %d!\n", generation); opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); clc.voipIncomingGeneration[sender] = generation; seqdiff = 0; } else if (seqdiff < 0) { // we're ahead of the sequence?! // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[sender]); // reset the decoder just in case. opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); seqdiff = 0; } else if (seqdiff * VOIP_MAX_PACKET_SAMPLES*2 >= sizeof (decoded)) { // dropped more than we can handle? // just start over. Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender); opus_decoder_ctl(clc.opusDecoder[sender], OPUS_RESET_STATE); seqdiff = 0; } if (seqdiff != 0) { Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", seqdiff, sender); // tell opus that we're missing frames... for (i = 0; i < seqdiff; i++) { assert((written + VOIP_MAX_PACKET_SAMPLES) * 2 < sizeof (decoded)); numSamples = opus_decode(clc.opusDecoder[sender], NULL, 0, decoded + written, VOIP_MAX_PACKET_SAMPLES, 0); if ( numSamples <= 0 ) { Com_DPrintf("VoIP: Error decoding frame %d from client #%d\n", i, sender); continue; } written += numSamples; } } numSamples = opus_decode(clc.opusDecoder[sender], encoded, packetsize, decoded + written, ARRAY_LEN(decoded) - written, 0); if ( numSamples <= 0 ) { Com_DPrintf("VoIP: Error decoding voip data from client #%d\n", sender); numSamples = 0; } #if 0 static FILE *encio = NULL; if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } static FILE *decio = NULL; if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } #endif written += numSamples; Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, frames); if(written > 0) CL_PlayVoip(sender, written, (const byte *) decoded, flags); clc.voipIncomingSequence[sender] = sequence + frames; }
void CL_ParseFrame(void) { int cmd; int len; frame_t *old; memset(&cl.frame, 0, sizeof(cl.frame)); cl.frame.serverframe = MSG_ReadLong(&net_message); cl.frame.deltaframe = MSG_ReadLong(&net_message); cl.frame.servertime = cl.frame.serverframe * 100; /* BIG HACK to let old demos continue to work */ if (cls.serverProtocol != 26) { cl.surpressCount = MSG_ReadByte(&net_message); } if (cl_shownet->value == 3) { Com_Printf(" frame:%i delta:%i\n", cl.frame.serverframe, cl.frame.deltaframe); } /* 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 (cl.frame.deltaframe <= 0) { cl.frame.valid = true; /* uncompressed frame */ old = NULL; cls.demowaiting = false; /* we can start recording now */ } else { old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK]; if (!old->valid) { /* should never happen */ Com_Printf("Delta from invalid frame (not supposed to happen!).\n"); } if (old->serverframe != cl.frame.deltaframe) { /* 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.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES - 128) { Com_Printf("Delta parse_entities too old.\n"); } else { cl.frame.valid = true; /* valid delta parse */ } } /* clamp time */ if (cl.time > cl.frame.servertime) { cl.time = cl.frame.servertime; } else if (cl.time < cl.frame.servertime - 100) { cl.time = cl.frame.servertime - 100; } /* read areabits */ len = MSG_ReadByte(&net_message); MSG_ReadData(&net_message, &cl.frame.areabits, len); /* read playerinfo */ cmd = MSG_ReadByte(&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_playerinfo) { Com_Error(ERR_DROP, "CL_ParseFrame: 0x%X not playerinfo", cmd); } CL_ParsePlayerstate(old, &cl.frame); /* read packet entities */ cmd = MSG_ReadByte(&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_packetentities) { Com_Error(ERR_DROP, "CL_ParseFrame: 0x%X not packetentities", cmd); } CL_ParsePacketEntities(old, &cl.frame); /* save the frame off in the backup array for later delta comparisons */ cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame; if (cl.frame.valid) { /* getting a valid frame message ends the connection process */ if (cls.state != ca_active) { cls.state = ca_active; cl.force_refdef = true; cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0] * 0.125f; cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1] * 0.125f; cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2] * 0.125f; VectorCopy(cl.frame.playerstate.viewangles, cl.predicted_angles); if ((cls.disable_servercount != cl.servercount) && cl.refresh_prepped) { SCR_EndLoadingPlaque(); /* get rid of loading plaque */ } cl.sound_prepped = true; } /* fire entity events */ CL_FireEntityEvents(&cl.frame); if (!(!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))) { CL_CheckPredictionError(); } } }
static void SV_UserVoip(client_t *cl, msg_t *msg) { int sender, generation, sequence, frames, packetsize; uint8_t recips[(MAX_CLIENTS + 7) / 8]; int flags; byte encoded[sizeof(cl->voipPacket[0]->data)]; client_t *client = NULL; voipServerPacket_t *packet = NULL; int i; sender = cl - svs.clients; generation = MSG_ReadByte(msg); sequence = MSG_ReadLong(msg); frames = MSG_ReadByte(msg); MSG_ReadData(msg, recips, sizeof(recips)); flags = MSG_ReadByte(msg); packetsize = MSG_ReadShort(msg); if (msg->readcount > msg->cursize) return; // short/invalid packet, bail. if (packetsize > sizeof (encoded)) { // overlarge packet? int bytesleft = packetsize; while (bytesleft) { int br = bytesleft; if (br > sizeof (encoded)) br = sizeof (encoded); MSG_ReadData(msg, encoded, br); bytesleft -= br; } return; // overlarge packet, bail. } MSG_ReadData(msg, encoded, packetsize); if (SV_ShouldIgnoreVoipSender(cl)) return; // Blacklisted, disabled, etc. // !!! FIXME: see if we read past end of msg... // !!! FIXME: reject if not speex narrowband codec. // !!! FIXME: decide if this is bogus data? // decide who needs this VoIP packet sent to them... for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { if (client->state != CS_ACTIVE) continue; // not in the game yet, don't send to this guy. else if (i == sender) continue; // don't send voice packet back to original author. else if (!client->hasVoip) continue; // no VoIP support, or unsupported protocol else if (client->muteAllVoip) continue; // client is ignoring everyone. else if (client->ignoreVoipFromClient[sender]) continue; // client is ignoring this talker. else if (*cl->downloadName) // !!! FIXME: possible to DoS? continue; // no VoIP allowed if downloading, to save bandwidth. if(Com_IsVoipTarget(recips, sizeof(recips), i)) flags |= VOIP_DIRECT; else flags &= ~VOIP_DIRECT; if (!(flags & (VOIP_SPATIAL | VOIP_DIRECT))) continue; // not addressed to this player. // Transmit this packet to the client. if (client->queuedVoipPackets >= ARRAY_LEN(client->voipPacket)) { Com_Printf("Too many VoIP packets queued for client #%d\n", i); continue; // no room for another packet right now. } packet = Z_Malloc(sizeof(*packet)); packet->sender = sender; packet->frames = frames; packet->len = packetsize; packet->generation = generation; packet->sequence = sequence; packet->flags = flags; memcpy(packet->data, encoded, packetsize); client->voipPacket[(client->queuedVoipIndex + client->queuedVoipPackets) % ARRAY_LEN(client->voipPacket)] = packet; client->queuedVoipPackets++; } }
static void demoFrameUnpack( msg_t *msg, demoFrame_t *oldFrame, demoFrame_t *newFrame ) { int last; qboolean isDelta = MSG_ReadBits( msg, 1 ) ? qfalse : qtrue; if (!isDelta) oldFrame = 0; newFrame->serverTime = MSG_ReadLong( msg ); /* Read config strings */ newFrame->string.data[0] = 0; newFrame->string.used = 1; last = 0; /* Extract config strings */ while ( 1 ) { int i, num = MSG_ReadShort( msg ); if (!isDelta ) { for (i = last;i<num;i++) newFrame->string.offsets[i] = 0; } else { for (i = last;i<num;i++) demoFrameAddString( &newFrame->string, i, oldFrame->string.data + oldFrame->string.offsets[i] ); } if (num < MAX_CONFIGSTRINGS) { demoFrameAddString( &newFrame->string, num, MSG_ReadBigString( msg ) ); } else { break; } last = num + 1; } /* Extract player states */ Com_Memset( newFrame->clientData, 0, sizeof( newFrame->clientData )); last = MSG_ReadByte( msg ); while (last < MAX_CLIENTS) { playerState_t *oldPlayer, *newPlayer; newFrame->clientData[last] = 1; oldPlayer = isDelta && oldFrame->clientData[last] ? &oldFrame->clients[last] : &demoNullPlayerState; newPlayer = &newFrame->clients[last]; MSG_ReadDeltaPlayerstate( msg, oldPlayer, newPlayer ); last = MSG_ReadByte( msg ); } /* Extract entity states */ last = 0; while ( 1 ) { int i, num = MSG_ReadBits( msg, GENTITYNUM_BITS ); entityState_t *oldEntity, *newEntity; if ( isDelta ) { for (i = last;i<num;i++) if (oldFrame->entities[i].number == i) newFrame->entities[i] = oldFrame->entities[i]; else newFrame->entities[i].number = MAX_GENTITIES - 1; } else { for (i = last;i<num;i++) newFrame->entities[i].number = MAX_GENTITIES - 1; } if (num < MAX_GENTITIES - 1) { if (isDelta) { oldEntity = &oldFrame->entities[num]; if (oldEntity->number != num) oldEntity = &demoNullEntityState; } else { oldEntity = &demoNullEntityState; } newEntity = &newFrame->entities[i]; MSG_ReadDeltaEntity( msg, oldEntity, newEntity, num ); } else break; last = num + 1; } /* Read the area mask */ newFrame->areaUsed = MSG_ReadByte( msg ); MSG_ReadData( msg, newFrame->areamask, newFrame->areaUsed ); /* Read the command string data */ newFrame->commandUsed = MSG_ReadLong( msg ); MSG_ReadData( msg, newFrame->commandData, newFrame->commandUsed ); }
void demoConvert( const char *oldName, const char *newBaseName, qboolean smoothen ) { fileHandle_t oldHandle = 0; fileHandle_t newHandle = 0; int temp; int oldSize; int msgSequence; msg_t oldMsg; byte oldData[ MAX_MSGLEN ]; int oldTime, nextTime, fullTime; int clientNum; demoFrame_t *workFrame; int parseEntitiesNum = 0; demoConvert_t *convert; char bigConfigString[BIG_INFO_STRING]; int bigConfigNumber; const char *s; clSnapshot_t *oldSnap = 0; clSnapshot_t *newSnap; int levelCount = 0; char newName[MAX_OSPATH]; oldSize = FS_FOpenFileRead( oldName, &oldHandle, qtrue ); if (!oldHandle) { Com_Printf("Failed to open %s for conversion.", oldName); return; } /* Alloc some memory */ convert = Z_Malloc( sizeof( demoConvert_t) ); /* Initialize the first workframe's strings */ while (oldSize > 0) { MSG_Init( &oldMsg, oldData, sizeof( oldData ) ); /* Read the sequence number */ if (FS_Read( &convert->messageNum, 4, oldHandle) != 4) goto conversionerror; convert->messageNum = LittleLong( convert->messageNum ); oldSize -= 4; /* Read the message size */ if (FS_Read( &oldMsg.cursize,4, oldHandle) != 4) goto conversionerror; oldSize -= 4; oldMsg.cursize = LittleLong( oldMsg.cursize ); /* Negative size signals end of demo */ if (oldMsg.cursize < 0) break; if ( oldMsg.cursize > oldMsg.maxsize ) goto conversionerror; /* Read the actual message */ if (FS_Read( oldMsg.data, oldMsg.cursize, oldHandle ) != oldMsg.cursize) goto conversionerror; oldSize -= oldMsg.cursize; // init the bitstream MSG_BeginReading( &oldMsg ); // Skip the reliable sequence acknowledge number MSG_ReadLong( &oldMsg ); // // parse the message // while ( 1 ) { byte cmd; if ( oldMsg.readcount > oldMsg.cursize ) { Com_Printf ("Demo conversion, read past end of server message.\n"); goto conversionerror; } cmd = MSG_ReadByte( &oldMsg ); if ( cmd == svc_EOF) { break; } workFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES ]; // other commands switch ( cmd ) { default: Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_serverCommand: temp = MSG_ReadLong( &oldMsg ); s = MSG_ReadString( &oldMsg ); if (temp<=msgSequence) break; // Com_Printf( " server command %s\n", s ); msgSequence = temp; Cmd_TokenizeString( s ); if ( !Q_stricmp( Cmd_Argv(0), "bcs0" ) ) { bigConfigNumber = atoi( Cmd_Argv(1) ); Q_strncpyz( bigConfigString, Cmd_Argv(2), sizeof( bigConfigString )); break; } if ( !Q_stricmp( Cmd_Argv(0), "bcs1" ) ) { Q_strcat( bigConfigString, sizeof( bigConfigString ), Cmd_Argv(2)); break; } if ( !Q_stricmp( Cmd_Argv(0), "bcs2" ) ) { Q_strcat( bigConfigString, sizeof( bigConfigString ), Cmd_Argv(2)); demoFrameAddString( &workFrame->string, bigConfigNumber, bigConfigString ); break; } if ( !Q_stricmp( Cmd_Argv(0), "cs" ) ) { int num = atoi( Cmd_Argv(1) ); s = Cmd_ArgsFrom( 2 ); demoFrameAddString( &workFrame->string, num, Cmd_ArgsFrom( 2 ) ); break; } if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) { int len = strlen( s ) + 1; char *dst; if (workFrame->commandUsed + len + 1 > sizeof( workFrame->commandData)) { Com_Printf("Overflowed state command data.\n"); goto conversionerror; } dst = workFrame->commandData + workFrame->commandUsed; *dst = clientNum; Com_Memcpy( dst+1, s, len ); workFrame->commandUsed += len + 1; } break; case svc_gamestate: if (newHandle) { FS_FCloseFile( newHandle ); newHandle = 0; } if (levelCount) { Com_sprintf( newName, sizeof( newName ), "%s.%d.mme", newBaseName, levelCount ); } else { Com_sprintf( newName, sizeof( newName ), "%s.mme", newBaseName ); } fullTime = -1; clientNum = -1; oldTime = -1; Com_Memset( convert, 0, sizeof( *convert )); convert->frames[0].string.used = 1; levelCount++; newHandle = FS_FOpenFileWrite( newName ); if (!newHandle) { Com_Printf("Failed to open %s for target conversion target.\n", newName); goto conversionerror; return; } else { FS_Write ( demoHeader, strlen( demoHeader ), newHandle ); } Com_sprintf( newName, sizeof( newName ), "%s.txt", newBaseName ); workFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES ]; msgSequence = MSG_ReadLong( &oldMsg ); while( 1 ) { cmd = MSG_ReadByte( &oldMsg ); if (cmd == svc_EOF) break; if ( cmd == svc_configstring) { int num; const char *s; num = MSG_ReadShort( &oldMsg ); s = MSG_ReadBigString( &oldMsg ); demoFrameAddString( &workFrame->string, num, s ); } else if ( cmd == svc_baseline ) { int num = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); if ( num < 0 || num >= MAX_GENTITIES ) { Com_Printf( "Baseline number out of range: %i.\n", num ); goto conversionerror; } MSG_ReadDeltaEntity( &oldMsg, &demoNullEntityState, &convert->entityBaselines[num], num ); } else { Com_Printf( "Unknown block while converting demo gamestate.\n" ); goto conversionerror; } } clientNum = MSG_ReadLong( &oldMsg ); /* Skip the checksum feed */ MSG_ReadLong( &oldMsg ); break; case svc_snapshot: nextTime = MSG_ReadLong( &oldMsg ); /* Delta number, not needed */ newSnap = &convert->snapshots[convert->messageNum & PACKET_MASK]; Com_Memset (newSnap, 0, sizeof(*newSnap)); newSnap->deltaNum = MSG_ReadByte( &oldMsg ); newSnap->messageNum = convert->messageNum; if (!newSnap->deltaNum) { newSnap->deltaNum = -1; newSnap->valid = qtrue; // uncompressed frame oldSnap = NULL; } else { newSnap->deltaNum = newSnap->messageNum - newSnap->deltaNum; oldSnap = &convert->snapshots[newSnap->deltaNum & PACKET_MASK]; if (!oldSnap->valid) { Com_Printf( "Delta snapshot without base.\n" ); goto conversionerror; } else if (oldSnap->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 ( parseEntitiesNum - oldSnap->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) { Com_Printf ("Delta parseEntitiesNum too old.\n"); } else { newSnap->valid = qtrue; // valid delta parse } } /* Snapflags, not needed */ newSnap->snapFlags = MSG_ReadByte( &oldMsg ); // read areamask workFrame->areaUsed = MSG_ReadByte( &oldMsg ); MSG_ReadData( &oldMsg, workFrame->areamask, workFrame->areaUsed ); if (clientNum <0 || clientNum >= MAX_CLIENTS) { Com_Printf("Got snapshot with invalid client.\n"); goto conversionerror; } MSG_ReadDeltaPlayerstate( &oldMsg, oldSnap ? &oldSnap->ps : &demoNullPlayerState, &newSnap->ps ); /* Read the individual entities */ newSnap->parseEntitiesNum = parseEntitiesNum; newSnap->numEntities = 0; Com_Memset( workFrame->entityData, 0, sizeof( workFrame->entityData )); /* The beast that is entity parsing */ { int newnum; entityState_t *oldstate, *newstate; int oldindex = 0; int oldnum; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); while ( 1 ) { // read the entity index number if (oldSnap && oldindex < oldSnap->numEntities) { oldstate = &convert->parseEntities[(oldSnap->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } else { oldstate = 0; oldnum = 99999; } newstate = &convert->parseEntities[parseEntitiesNum]; if ( !oldstate && (newnum == (MAX_GENTITIES-1))) { break; } else if ( oldnum < newnum ) { *newstate = *oldstate; oldindex++; } else if (oldnum == newnum) { oldindex++; MSG_ReadDeltaEntity( &oldMsg, oldstate, newstate, newnum ); if ( newstate->number != MAX_GENTITIES-1) workFrame->entityData[ newstate->number ] = 1; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); } else if (oldnum > newnum) { MSG_ReadDeltaEntity( &oldMsg, &convert->entityBaselines[newnum], newstate , newnum ); if ( newstate->number != MAX_GENTITIES-1) workFrame->entityData[ newstate->number ] = 1; newnum = MSG_ReadBits( &oldMsg, GENTITYNUM_BITS ); } if (newstate->number == MAX_GENTITIES-1) continue; parseEntitiesNum++; parseEntitiesNum &= (MAX_PARSE_ENTITIES-1); newSnap->numEntities++; }} /* Stop processing this further since it's an invalid snap due to lack of delta data */ if (!newSnap->valid) break; /* Skipped snapshots will be set invalid in the circular buffer */ if ( newSnap->messageNum - convert->lastMessageNum >= PACKET_BACKUP ) { convert->lastMessageNum = newSnap->messageNum - ( PACKET_BACKUP - 1 ); } for ( ; convert->lastMessageNum < newSnap->messageNum ; convert->lastMessageNum++ ) { convert->snapshots[convert->lastMessageNum & PACKET_MASK].valid = qfalse; } convert->lastMessageNum = newSnap->messageNum + 1; /* compress the frame into the new format */ if (nextTime > oldTime) { demoFrame_t *cleanFrame; int writeIndex; for (temp = 0;temp<newSnap->numEntities;temp++) { int p = (newSnap->parseEntitiesNum+temp) & (MAX_PARSE_ENTITIES-1); entityState_t *newState = &convert->parseEntities[p]; workFrame->entities[newState->number] = *newState; } workFrame->clientData[clientNum] = 1; workFrame->clients[clientNum] = newSnap->ps; workFrame->serverTime = nextTime; /* Which frame from the cache to save */ writeIndex = convert->frameIndex - (DEMOCONVERTFRAMES/2); if (writeIndex >= 0) { const demoFrame_t *newFrame; msg_t writeMsg; // init the message MSG_Init( &writeMsg, demoBuffer, sizeof (demoBuffer)); MSG_Clear( &writeMsg ); MSG_Bitstream( &writeMsg ); newFrame = &convert->frames[ writeIndex % DEMOCONVERTFRAMES]; if ( smoothen ) demoFrameInterpolate( convert->frames, DEMOCONVERTFRAMES, writeIndex ); if ( nextTime > fullTime || writeIndex <= 0 ) { /* Plan the next time for a full write */ fullTime = nextTime + 2000; demoFramePack( &writeMsg, newFrame, 0 ); } else { const demoFrame_t *oldFrame = &convert->frames[ ( writeIndex -1 ) % DEMOCONVERTFRAMES]; demoFramePack( &writeMsg, newFrame, oldFrame ); } /* Write away the new data in the msg queue */ temp = LittleLong( writeMsg.cursize ); FS_Write (&temp, 4, newHandle ); FS_Write ( writeMsg.data , writeMsg.cursize, newHandle ); } /* Clean up the upcoming frame for all new changes */ convert->frameIndex++; cleanFrame = &convert->frames[ convert->frameIndex % DEMOCONVERTFRAMES]; cleanFrame->serverTime = 0; for (temp = 0;temp<MAX_GENTITIES;temp++) cleanFrame->entities[temp].number = MAX_GENTITIES-1; Com_Memset( cleanFrame->clientData, 0, sizeof ( cleanFrame->clientData )); Com_Memcpy( cleanFrame->string.data, workFrame->string.data, workFrame->string.used ); Com_Memcpy( cleanFrame->string.offsets, workFrame->string.offsets, sizeof( workFrame->string.offsets )); cleanFrame->string.used = workFrame->string.used; cleanFrame->commandUsed = 0; /* keep track of this last frame's time */ oldTime = nextTime; } break; case svc_download: // read block number temp = MSG_ReadShort ( &oldMsg ); if (!temp) //0 block, read file size MSG_ReadLong( &oldMsg ); // read block size temp = MSG_ReadShort ( &oldMsg ); // read the data block for ( ;temp>0;temp--) MSG_ReadByte( &oldMsg ); break; } } } conversionerror: FS_FCloseFile( oldHandle ); FS_FCloseFile( newHandle ); Z_Free( convert ); return; }
// Called when data is received from server // TODO: store the voip data and then process in the main S_Update() call void S_Voip_Parse (void) { unsigned int sender; int bytes; unsigned char data[1024], *start; short decodebuf[1024]; unsigned int decodesamps, len, newseq, drops; unsigned char seq, gen; float amp = 1; unsigned int i; sender = MSG_ReadByte (); gen = MSG_ReadByte (); seq = MSG_ReadByte (); bytes = MSG_ReadShort (); if (bytes > sizeof (data) || !cl_voip_play.integer || !S_Speex_Init () || (gen & 0xf0)) { Com_DPrintf ("skip data: %d\n", bytes); MSG_ReadSkip (bytes); return; } MSG_ReadData (data, bytes); sender &= MAX_CLIENTS - 1; amp = s_speex.decamp[sender]; decodesamps = 0; newseq = 0; drops = 0; start = data; s_speex.lastspoke[sender] = cls.realtime + 0.5; if (s_speex.decgen[sender] != gen) { speex_bits_reset (&s_speex.decbits[sender]); s_speex.decgen[sender] = gen; s_speex.decseq[sender] = seq; } while (bytes > 0) { if (decodesamps + s_speex.framesize > sizeof (decodebuf) / sizeof (decodebuf[0])) { S_RawAudio (sender, (byte*)decodebuf, s_speex.samplerate, decodesamps, 1, 2); decodesamps = 0; } if (s_speex.decseq[sender] != seq) { speex_decode_int (s_speex.decoder[sender], NULL, decodebuf + decodesamps); s_speex.decseq[sender]++; drops++; } else { bytes--; len = *start++; speex_bits_read_from (&s_speex.decbits[sender], (char *)start, len); bytes -= len; start += len; speex_decode_int (s_speex.decoder[sender], &s_speex.decbits[sender], decodebuf + decodesamps); newseq++; } if (amp != 1) { for (i = decodesamps; i < decodesamps + s_speex.framesize; i++) decodebuf[i] *= amp; } decodesamps += s_speex.framesize; } s_speex.decseq[sender] += newseq; if (drops) Con_DPrintf ("%i dropped audio frames\n", drops); if (decodesamps > 0) { S_RawAudio (sender, (byte*)decodebuf, s_speex.samplerate, decodesamps, 1, 2); } }
/* * CL_ParseServerMessage */ void CL_ParseServerMessage( msg_t *msg ) { int cmd; if( cl_shownet->integer == 1 ) { Com_Printf( "%i ", msg->cursize ); } else if( cl_shownet->integer >= 2 ) { Com_Printf( "------------------\n" ); } // parse the message while( 1 ) { if( msg->readcount > msg->cursize ) { Com_Error( ERR_DROP, "CL_ParseServerMessage: Bad server message" ); break; } cmd = MSG_ReadByte( msg ); if( cl_debug_serverCmd->integer & 4 ) { if( cmd == -1 ) Com_Printf( "%3i:CMD %i %s\n", msg->readcount-1, cmd, "EOF" ); else Com_Printf( "%3i:CMD %i %s\n", msg->readcount-1, cmd, !svc_strings[cmd] ? "bad" : svc_strings[cmd] ); } if( cmd == -1 ) { SHOWNET( msg, "END OF MESSAGE" ); break; } if( cl_shownet->integer >= 2 ) { if( !svc_strings[cmd] ) Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); else SHOWNET( msg, svc_strings[cmd] ); } // other commands switch( cmd ) { default: Com_Error( ERR_DROP, "CL_ParseServerMessage: Illegible server message" ); break; case svc_nop: // Com_Printf( "svc_nop\n" ); break; case svc_servercmd: if( !cls.reliable ) { int cmdNum = MSG_ReadLong( msg ); if( cmdNum < 0 ) { Com_Error( ERR_DROP, "CL_ParseServerMessage: Invalid cmdNum value received: %i\n", cmdNum ); return; } if( cmdNum <= cls.lastExecutedServerCommand ) { MSG_ReadString( msg ); // read but ignore break; } cls.lastExecutedServerCommand = cmdNum; } // fall through case svc_servercs: // configstrings from demo files. they don't have acknowledge CL_ParseServerCommand( msg ); break; case svc_serverdata: if( cls.state == CA_HANDSHAKE ) { Cbuf_Execute(); // make sure any stuffed commands are done CL_ParseServerData( msg ); } else { return; // ignore rest of the packet (serverdata is always sent alone) } break; case svc_spawnbaseline: CL_ParseBaseline( msg ); break; case svc_download: CL_ParseDownload( msg ); break; case svc_clcack: if( cls.reliable ) { Com_Error( ERR_DROP, "CL_ParseServerMessage: clack message for reliable client\n" ); return; } cls.reliableAcknowledge = (unsigned)MSG_ReadLong( msg ); cls.ucmdAcknowledged = (unsigned)MSG_ReadLong( msg ); if( cl_debug_serverCmd->integer & 4 ) Com_Printf( "svc_clcack:reliable cmd ack:%i ucmdack:%i\n", cls.reliableAcknowledge, cls.ucmdAcknowledged ); break; case svc_frame: CL_ParseFrame( msg ); break; case svc_demoinfo: assert( cls.demo.playing ); { size_t meta_data_maxsize; MSG_ReadLong( msg ); MSG_ReadLong( msg ); cls.demo.meta_data_realsize = (size_t)MSG_ReadLong( msg ); meta_data_maxsize = (size_t)MSG_ReadLong( msg ); // sanity check if( cls.demo.meta_data_realsize > meta_data_maxsize ) { cls.demo.meta_data_realsize = meta_data_maxsize; } if( cls.demo.meta_data_realsize > sizeof( cls.demo.meta_data ) ) { cls.demo.meta_data_realsize = sizeof( cls.demo.meta_data ); } MSG_ReadData( msg, cls.demo.meta_data, cls.demo.meta_data_realsize ); MSG_SkipData( msg, meta_data_maxsize - cls.demo.meta_data_realsize ); } break; case svc_playerinfo: case svc_packetentities: case svc_match: Com_Error( ERR_DROP, "Out of place frame data" ); break; case svc_extension: if( 1 ) { int ext, len; ext = MSG_ReadByte( msg ); // extension id MSG_ReadByte( msg ); // version number len = MSG_ReadShort( msg ); // command length switch( ext ) { default: // unsupported MSG_SkipData( msg, len ); break; } } break; } } CL_AddNetgraph(); // // if recording demos, copy the message out // // // we don't know if it is ok to save a demo message until // after we have parsed the frame // if( cls.demo.recording && !cls.demo.waiting ) CL_WriteDemoMessage( msg ); }
/* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload ( msg_t *msg ) { int size; unsigned char data[MAX_MSGLEN]; uint16_t block; if (!*clc.downloadTempName) { Com_Printf("Server sending download, but no download was requested\n"); CL_AddReliableCommand("stopdl", qfalse); return; } // read the data block = MSG_ReadShort ( msg ); if(!block && !clc.downloadBlock) { // block zero is special, contains file size clc.downloadSize = MSG_ReadLong ( msg ); Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); if (clc.downloadSize < 0) { Com_Error( ERR_DROP, "%s", MSG_ReadString( msg ) ); return; } } size = MSG_ReadShort ( msg ); if (size < 0 || size > sizeof(data)) { Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk", size); return; } MSG_ReadData(msg, data, size); if((clc.downloadBlock & 0xFFFF) != block) { Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", (clc.downloadBlock & 0xFFFF), block); return; } // open the file if not opened yet if (!clc.download) { clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName ); if (!clc.download) { Com_Printf( "Could not create %s\n", clc.downloadTempName ); CL_AddReliableCommand("stopdl", qfalse); CL_NextDownload(); return; } } if (size) FS_Write( data, size, clc.download ); CL_AddReliableCommand(va("nextdl %d", clc.downloadBlock), qfalse); clc.downloadBlock++; clc.downloadCount += size; // So UI gets access to it Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); if (!size) { // A zero length block means EOF if (clc.download) { FS_FCloseFile( clc.download ); clc.download = 0; // rename the file FS_SV_Rename ( clc.downloadTempName, clc.downloadName, qfalse ); } // send intentions now // We need this because without it, we would hold the last nextdl and then start // loading right away. If we take a while to load, the server is happily trying // to send us that last block over and over. // Write it twice to help make sure we acknowledge the download CL_WritePacket(); CL_WritePacket(); // get another file if needed CL_NextDownload (); } }
/* ================ CL_ParseFrame ================ */ void CL_ParseFrame (void) { int cmd; int len; frame_t *old; memset (&cl.frame, 0, sizeof(cl.frame)); cl.frame.serverframe = MSG_ReadLong(&net_message); cl.frame.deltaframe = MSG_ReadLong(&net_message); cl.frame.servertime = cl.frame.serverframe*100; // BIG HACK to let old demos continue to work if (cls.serverProtocol != 26) cl.surpressCount = MSG_ReadByte(&net_message); if (cl_shownet->value == 3) Com_Printf (" frame:%i delta:%i\n", cl.frame.serverframe, cl.frame.deltaframe); // 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 (cl.frame.deltaframe <= 0) { cl.frame.valid = true; // uncompressed frame old = NULL; cls.demowaiting = false; // we can start recording now } else { old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK]; if (!old->valid) { // should never happen Com_Printf ("Delta from invalid frame (not supposed to happen!).\n"); } if (old->serverframe != cl.frame.deltaframe) { // 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.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES-128) { Com_Printf ("Delta parse_entities too old.\n"); } else { cl.frame.valid = true; // valid delta parse } } // clamp time if (cl.time > cl.frame.servertime) cl.time = cl.frame.servertime; else if (cl.time < cl.frame.servertime - 100) cl.time = cl.frame.servertime - 100; // read areabits len = MSG_ReadByte(&net_message); MSG_ReadData(&net_message, &cl.frame.areabits, len); // read playerinfo cmd = MSG_ReadByte(&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_playerinfo) Com_Error(ERR_DROP, "CL_ParseFrame: not playerinfo"); CL_ParsePlayerstate(old, &cl.frame); // read packet entities cmd = MSG_ReadByte(&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_packetentities) Com_Error(ERR_DROP, "CL_ParseFrame: not packetentities"); CL_ParsePacketEntities(old, &cl.frame); // save the frame off in the backup array for later delta comparisons cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame; if (cl.frame.valid) { // getting a valid frame message ends the connection process if (cls.state != ca_active) { cls.state = ca_active; //cl_scores_setinuse_all(false); // jitscores - clear scoreboard cl.force_refdef = true; cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0]*0.125; cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1]*0.125; cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2]*0.125; VectorCopy(cl.frame.playerstate.viewangles, cl.predicted_angles); if (cls.disable_servercount != cl.servercount && cl.refresh_prepped) SCR_EndLoadingPlaque(); // get rid of loading plaque } cl.sound_prepped = true; // can start mixing ambient sounds // fire entity events CL_FireEntityEvents(&cl.frame); CL_CheckPredictionError(); } }
/* ================ 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; int deltaNum; int oldMessageNum; int i, packetNum; // 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; if ( clc.demorecording ) { clc.demowaiting = qfalse; // we can start recording now // if(cl_autorecord->integer) { // Cvar_Set( "g_synchronousClients", "0" ); // } } else { if ( cl_autorecord->integer /*&& Cvar_VariableValue( "g_synchronousClients") */ ) { char name[ 256 ]; char mapname[ MAX_QPATH ]; char *period; qtime_t time; Com_RealTime( &time ); Q_strncpyz( mapname, cl.mapname, MAX_QPATH ); for ( period = mapname; *period; period++ ) { if ( *period == '.' ) { *period = '\0'; break; } } for ( period = mapname; *period; period++ ) { if ( *period == '/' ) { break; } } if ( *period ) { period++; } Com_sprintf( name, sizeof( name ), "demos/%s_%04i-%02i-%02i_%02i%02i%02i.dm_%d", period, 1900 + time.tm_year, time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec, PROTOCOL_VERSION ); CL_Record( name ); } } } 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_DPrintf( "Delta frame too old.\n" ); } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES - 128 ) { Com_DPrintf( "Delta parseEntitiesNum too old.\n" ); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte( msg ); if ( len > sizeof( newSnap.areamask ) ) { Com_Error( ERR_DROP, "CL_ParseSnapshot: Invalid size %d for areamask.", len ); } MSG_ReadData( msg, &newSnap.areamask, len ); // read playerinfo SHOWNET( msg, "playerstate" ); if ( old ) { MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); } else { MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); } // 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 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; if ( cl.snap.ps.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_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; int deltaNum; int oldMessageNum; int i, packetNum; // 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 = qfalse; 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 > MAX_PARSE_ENTITIES-128 ) { Com_DPrintf ("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte( msg ); if((unsigned)len > sizeof(newSnap.areamask)) { Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len); return; } MSG_ReadData( msg, &newSnap.areamask, len); // read playerinfo SHOWNET( msg, "playerstate" ); if ( old ) { MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps ); if (newSnap.ps.m_iVehicleNum) { //this means we must have written our vehicle's ps too MSG_ReadDeltaPlayerstate( msg, &old->vps, &newSnap.vps, qtrue ); } } else { MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps ); if (newSnap.ps.m_iVehicleNum) { //this means we must have written our vehicle's ps too MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.vps, qtrue ); } } // 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 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; if ( cl.snap.ps.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_ParseVoip A VoIP message has been received from the server ===================== */ static void CL_ParseVoip( msg_t *msg ) { static short decoded[ 4096 ]; // !!! FIXME: don't hardcode. const int sender = MSG_ReadShort( msg ); const int generation = MSG_ReadByte( msg ); const int sequence = MSG_ReadLong( msg ); const int frames = MSG_ReadByte( msg ); const int packetsize = MSG_ReadShort( msg ); const int flags = MSG_ReadBits( msg, VOIP_FLAGCNT ); char encoded[ 1024 ]; int seqdiff = sequence - clc.voipIncomingSequence[ sender ]; int written = 0; int i; Com_DPrintf( "VoIP: %d-byte packet from client %d\n", packetsize, sender ); if ( sender < 0 ) { return; // short/invalid packet, bail. } else if ( generation < 0 ) { return; // short/invalid packet, bail. } else if ( sequence < 0 ) { return; // short/invalid packet, bail. } else if ( frames < 0 ) { return; // short/invalid packet, bail. } else if ( packetsize < 0 ) { return; // short/invalid packet, bail. } if ( packetsize > sizeof( encoded ) ) // overlarge packet? { int bytesleft = packetsize; while ( bytesleft ) { int br = bytesleft; if ( br > sizeof( encoded ) ) { br = sizeof( encoded ); } MSG_ReadData( msg, encoded, br ); bytesleft -= br; } return; // overlarge packet, bail. } if ( !clc.speexInitialized ) { MSG_ReadData( msg, encoded, packetsize ); // skip payload. return; // can't handle VoIP without libspeex! } else if ( sender >= MAX_CLIENTS ) { MSG_ReadData( msg, encoded, packetsize ); // skip payload. return; // bogus sender. } else if ( CL_ShouldIgnoreVoipSender( sender ) ) { MSG_ReadData( msg, encoded, packetsize ); // skip payload. return; // Channel is muted, bail. } // !!! FIXME: make sure data is narrowband? Does decoder handle this? Com_DPrintf( "VoIP: packet accepted!\n" ); // This is a new "generation" ... a new recording started, reset the bits. if ( generation != clc.voipIncomingGeneration[ sender ] ) { Com_DPrintf( "VoIP: new generation %d!\n", generation ); speex_bits_reset( &clc.speexDecoderBits[ sender ] ); clc.voipIncomingGeneration[ sender ] = generation; seqdiff = 0; } else if ( seqdiff < 0 ) // we're ahead of the sequence?! { // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf( "VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[ sender ] ); // reset the bits just in case. speex_bits_reset( &clc.speexDecoderBits[ sender ] ); seqdiff = 0; } else if ( seqdiff > 100 ) // more than 2 seconds of audio dropped? { // just start over. Com_DPrintf( "VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender ); speex_bits_reset( &clc.speexDecoderBits[ sender ] ); seqdiff = 0; } if ( seqdiff != 0 ) { Com_DPrintf( "VoIP: Dropped %d frames from client #%d\n", seqdiff, sender ); // tell speex that we're missing frames... for ( i = 0; i < seqdiff; i++ ) { assert( ( written + clc.speexFrameSize ) * 2 < sizeof( decoded ) ); speex_decode_int( clc.speexDecoder[ sender ], NULL, decoded + written ); written += clc.speexFrameSize; } } for ( i = 0; i < frames; i++ ) { const int len = MSG_ReadByte( msg ); if ( len < 0 ) { Com_DPrintf( "VoIP: Short packet!\n" ); break; } MSG_ReadData( msg, encoded, len ); // shouldn't happen, but just in case... if ( ( written + clc.speexFrameSize ) * 2 > sizeof( decoded ) ) { Com_DPrintf( "VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i ); CL_PlayVoip( sender, written, ( const byte * ) decoded, flags ); written = 0; } speex_bits_read_from( &clc.speexDecoderBits[ sender ], encoded, len ); speex_decode_int( clc.speexDecoder[ sender ], &clc.speexDecoderBits[ sender ], decoded + written ); #if 0 static FILE *encio = NULL; if ( encio == NULL ) { encio = fopen( "voip-incoming-encoded.bin", "wb" ); } if ( encio != NULL ) { fwrite( encoded, len, 1, encio ); fflush( encio ); } static FILE *decio = NULL; if ( decio == NULL ) { decio = fopen( "voip-incoming-decoded.bin", "wb" ); } if ( decio != NULL ) { fwrite( decoded + written, clc.speexFrameSize * 2, 1, decio ); fflush( decio ); } #endif written += clc.speexFrameSize; } Com_DPrintf( "VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i ); if ( written > 0 ) { CL_PlayVoip( sender, written, ( const byte * ) decoded, flags ); } clc.voipIncomingSequence[ sender ] = sequence + frames; }
void CL_ParseRMG ( msg_t* msg ) { clc.rmgHeightMapSize = (unsigned short)MSG_ReadShort ( msg ); if ( !clc.rmgHeightMapSize ) { return; } z_stream zdata; int size; unsigned char heightmap1[15000]; if ( MSG_ReadBits ( msg, 1 ) ) { // Read the heightmap memset(&zdata, 0, sizeof(z_stream)); inflateInit ( &zdata/*, Z_SYNC_FLUSH*/ ); MSG_ReadData ( msg, heightmap1, clc.rmgHeightMapSize ); zdata.next_in = heightmap1; zdata.avail_in = clc.rmgHeightMapSize; zdata.next_out = (unsigned char*)clc.rmgHeightMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate (&zdata,Z_SYNC_FLUSH ); clc.rmgHeightMapSize = zdata.total_out; inflateEnd(&zdata); } else { MSG_ReadData ( msg, (unsigned char*)clc.rmgHeightMap, clc.rmgHeightMapSize ); } size = (unsigned short)MSG_ReadShort ( msg ); if ( MSG_ReadBits ( msg, 1 ) ) { // Read the flatten map memset(&zdata, 0, sizeof(z_stream)); inflateInit ( &zdata/*, Z_SYNC_FLUSH*/ ); MSG_ReadData ( msg, heightmap1, size ); zdata.next_in = heightmap1; zdata.avail_in = clc.rmgHeightMapSize; zdata.next_out = (unsigned char*)clc.rmgFlattenMap; zdata.avail_out = MAX_HEIGHTMAP_SIZE; inflate (&zdata, Z_SYNC_FLUSH); inflateEnd(&zdata); } else { MSG_ReadData ( msg, (unsigned char*)clc.rmgFlattenMap, size ); } // Read the seed clc.rmgSeed = MSG_ReadLong ( msg ); CL_ParseAutomapSymbols ( msg ); }
qboolean demoCutParseSnapshot(msg_t *msg, clientConnection_t *clcCut, clientActive_t *clCut) { int len; clSnapshot_t *oldSnap; clSnapshot_t newSnap; int deltaNum; int oldMessageNum; int i, packetNum; Com_Memset(&newSnap, 0, sizeof(newSnap)); newSnap.serverCommandNum = clcCut->serverCommandSequence; newSnap.serverTime = MSG_ReadLong(msg); cl_paused->modified = qfalse; newSnap.messageNum = clcCut->serverMessageSequence; deltaNum = MSG_ReadByte(msg); newSnap.snapFlags = MSG_ReadByte(msg); if (!deltaNum) { newSnap.deltaNum = -1; newSnap.valid = qtrue; // uncompressed frame oldSnap = NULL; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; oldSnap = &clCut->snapshots[newSnap.deltaNum & PACKET_MASK]; if (!oldSnap->valid) { // should never happen Com_Printf("Delta from invalid frame (not supposed to happen!).\n"); } else if (oldSnap->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 (clCut->parseEntitiesNum - oldSnap->parseEntitiesNum > MAX_PARSE_ENTITIES-128) { Com_DPrintf("Delta parseEntitiesNum too old.\n"); } else { newSnap.valid = qtrue; // valid delta parse } } // read areamask len = MSG_ReadByte(msg); if(len > sizeof(newSnap.areamask)) { Com_Printf("demoCutParseSnapshot: Invalid size %d for areamask", len); return qfalse; } MSG_ReadData(msg, &newSnap.areamask, len); // read playerinfo MSG_ReadDeltaPlayerstate(msg, oldSnap ? &oldSnap->ps : NULL, &newSnap.ps); if(newSnap.ps.m_iVehicleNum) MSG_ReadDeltaPlayerstate(msg, oldSnap ? &oldSnap->vps : NULL, &newSnap.vps, qtrue); // read packet entities demoCutParsePacketEntities(msg, oldSnap, &newSnap, clCut); // if not valid, dump the entire thing now that it has // been properly read if (!newSnap.valid) { return qtrue; } // 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 = clCut->snap.messageNum + 1; if (newSnap.messageNum - oldMessageNum >= PACKET_BACKUP) { oldMessageNum = newSnap.messageNum - (PACKET_BACKUP - 1); } for (;oldMessageNum < newSnap.messageNum; oldMessageNum++) { clCut->snapshots[oldMessageNum & PACKET_MASK].valid = qfalse; } // copy to the current good spot clCut->snap = newSnap; clCut->snap.ping = 999; // calculate ping time for (i = 0; i < PACKET_BACKUP; i++) { packetNum = (clcCut->netchan.outgoingSequence-1-i) & PACKET_MASK; if (clCut->snap.ps.commandTime >= clCut->outPackets[packetNum].p_serverTime) { clCut->snap.ping = cls.realtime - clCut->outPackets[packetNum].p_realtime; break; } } // save the frame off in the backup array for later delta comparisons clCut->snapshots[clCut->snap.messageNum & PACKET_MASK] = clCut->snap; clCut->newSnapshots = qtrue; return qtrue; }
/* ================ 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; }