Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

cl_parse.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // cl_parse.c  -- parse a message received from the server
00023 
00024 #include "client.h"
00025 
00026 char *svc_strings[256] = {
00027     "svc_bad",
00028 
00029     "svc_nop",
00030     "svc_gamestate",
00031     "svc_configstring",
00032     "svc_baseline", 
00033     "svc_serverCommand",
00034     "svc_download",
00035     "svc_snapshot"
00036 };
00037 
00038 void SHOWNET( msg_t *msg, char *s) {
00039     if ( cl_shownet->integer >= 2) {
00040         Com_Printf ("%3i:%s\n", msg->readcount-1, s);
00041     }
00042 }
00043 
00044 
00045 /*
00046 =========================================================================
00047 
00048 MESSAGE PARSING
00049 
00050 =========================================================================
00051 */
00052 
00053 /*
00054 ==================
00055 CL_DeltaEntity
00056 
00057 Parses deltas from the given base and adds the resulting entity
00058 to the current frame
00059 ==================
00060 */
00061 void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, 
00062                      qboolean unchanged) {
00063     entityState_t   *state;
00064 
00065     // save the parsed entity state into the big circular buffer so
00066     // it can be used as the source for a later delta
00067     state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
00068 
00069     if ( unchanged ) {
00070         *state = *old;
00071     } else {
00072         MSG_ReadDeltaEntity( msg, old, state, newnum );
00073     }
00074 
00075     if ( state->number == (MAX_GENTITIES-1) ) {
00076         return;     // entity was delta removed
00077     }
00078     cl.parseEntitiesNum++;
00079     frame->numEntities++;
00080 }
00081 
00082 /*
00083 ==================
00084 CL_ParsePacketEntities
00085 
00086 ==================
00087 */
00088 void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
00089     int         newnum;
00090     entityState_t   *oldstate;
00091     int         oldindex, oldnum;
00092 
00093     newframe->parseEntitiesNum = cl.parseEntitiesNum;
00094     newframe->numEntities = 0;
00095 
00096     // delta from the entities present in oldframe
00097     oldindex = 0;
00098     oldstate = NULL;
00099     if (!oldframe) {
00100         oldnum = 99999;
00101     } else {
00102         if ( oldindex >= oldframe->numEntities ) {
00103             oldnum = 99999;
00104         } else {
00105             oldstate = &cl.parseEntities[
00106                 (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
00107             oldnum = oldstate->number;
00108         }
00109     }
00110 
00111     while ( 1 ) {
00112         // read the entity index number
00113         newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
00114 
00115         if ( newnum == (MAX_GENTITIES-1) ) {
00116             break;
00117         }
00118 
00119         if ( msg->readcount > msg->cursize ) {
00120             Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
00121         }
00122 
00123         while ( oldnum < newnum ) {
00124             // one or more entities from the old packet are unchanged
00125             if ( cl_shownet->integer == 3 ) {
00126                 Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
00127             }
00128             CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
00129             
00130             oldindex++;
00131 
00132             if ( oldindex >= oldframe->numEntities ) {
00133                 oldnum = 99999;
00134             } else {
00135                 oldstate = &cl.parseEntities[
00136                     (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
00137                 oldnum = oldstate->number;
00138             }
00139         }
00140         if (oldnum == newnum) {
00141             // delta from previous state
00142             if ( cl_shownet->integer == 3 ) {
00143                 Com_Printf ("%3i:  delta: %i\n", msg->readcount, newnum);
00144             }
00145             CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
00146 
00147             oldindex++;
00148 
00149             if ( oldindex >= oldframe->numEntities ) {
00150                 oldnum = 99999;
00151             } else {
00152                 oldstate = &cl.parseEntities[
00153                     (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
00154                 oldnum = oldstate->number;
00155             }
00156             continue;
00157         }
00158 
00159         if ( oldnum > newnum ) {
00160             // delta from baseline
00161             if ( cl_shownet->integer == 3 ) {
00162                 Com_Printf ("%3i:  baseline: %i\n", msg->readcount, newnum);
00163             }
00164             CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
00165             continue;
00166         }
00167 
00168     }
00169 
00170     // any remaining entities in the old frame are copied over
00171     while ( oldnum != 99999 ) {
00172         // one or more entities from the old packet are unchanged
00173         if ( cl_shownet->integer == 3 ) {
00174             Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
00175         }
00176         CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
00177         
00178         oldindex++;
00179 
00180         if ( oldindex >= oldframe->numEntities ) {
00181             oldnum = 99999;
00182         } else {
00183             oldstate = &cl.parseEntities[
00184                 (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
00185             oldnum = oldstate->number;
00186         }
00187     }
00188 }
00189 
00190 
00191 /*
00192 ================
00193 CL_ParseSnapshot
00194 
00195 If the snapshot is parsed properly, it will be copied to
00196 cl.snap and saved in cl.snapshots[].  If the snapshot is invalid
00197 for any reason, no changes to the state will be made at all.
00198 ================
00199 */
00200 void CL_ParseSnapshot( msg_t *msg ) {
00201     int         len;
00202     clSnapshot_t    *old;
00203     clSnapshot_t    newSnap;
00204     int         deltaNum;
00205     int         oldMessageNum;
00206     int         i, packetNum;
00207 
00208     // get the reliable sequence acknowledge number
00209     // NOTE: now sent with all server to client messages
00210     //clc.reliableAcknowledge = MSG_ReadLong( msg );
00211 
00212     // read in the new snapshot to a temporary buffer
00213     // we will only copy to cl.snap if it is valid
00214     Com_Memset (&newSnap, 0, sizeof(newSnap));
00215 
00216     // we will have read any new server commands in this
00217     // message before we got to svc_snapshot
00218     newSnap.serverCommandNum = clc.serverCommandSequence;
00219 
00220     newSnap.serverTime = MSG_ReadLong( msg );
00221 
00222     newSnap.messageNum = clc.serverMessageSequence;
00223 
00224     deltaNum = MSG_ReadByte( msg );
00225     if ( !deltaNum ) {
00226         newSnap.deltaNum = -1;
00227     } else {
00228         newSnap.deltaNum = newSnap.messageNum - deltaNum;
00229     }
00230     newSnap.snapFlags = MSG_ReadByte( msg );
00231 
00232     // If the frame is delta compressed from data that we
00233     // no longer have available, we must suck up the rest of
00234     // the frame, but not use it, then ask for a non-compressed
00235     // message 
00236     if ( newSnap.deltaNum <= 0 ) {
00237         newSnap.valid = qtrue;      // uncompressed frame
00238         old = NULL;
00239         clc.demowaiting = qfalse;   // we can start recording now
00240     } else {
00241         old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
00242         if ( !old->valid ) {
00243             // should never happen
00244             Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
00245         } else if ( old->messageNum != newSnap.deltaNum ) {
00246             // The frame that the server did the delta from
00247             // is too old, so we can't reconstruct it properly.
00248             Com_Printf ("Delta frame too old.\n");
00249         } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
00250             Com_Printf ("Delta parseEntitiesNum too old.\n");
00251         } else {
00252             newSnap.valid = qtrue;  // valid delta parse
00253         }
00254     }
00255 
00256     // read areamask
00257     len = MSG_ReadByte( msg );
00258     MSG_ReadData( msg, &newSnap.areamask, len);
00259 
00260     // read playerinfo
00261     SHOWNET( msg, "playerstate" );
00262     if ( old ) {
00263         MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
00264     } else {
00265         MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
00266     }
00267 
00268     // read packet entities
00269     SHOWNET( msg, "packet entities" );
00270     CL_ParsePacketEntities( msg, old, &newSnap );
00271 
00272     // if not valid, dump the entire thing now that it has
00273     // been properly read
00274     if ( !newSnap.valid ) {
00275         return;
00276     }
00277 
00278     // clear the valid flags of any snapshots between the last
00279     // received and this one, so if there was a dropped packet
00280     // it won't look like something valid to delta from next
00281     // time we wrap around in the buffer
00282     oldMessageNum = cl.snap.messageNum + 1;
00283 
00284     if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
00285         oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
00286     }
00287     for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
00288         cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
00289     }
00290 
00291     // copy to the current good spot
00292     cl.snap = newSnap;
00293     cl.snap.ping = 999;
00294     // calculate ping time
00295     for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
00296         packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
00297         if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
00298             cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
00299             break;
00300         }
00301     }
00302     // save the frame off in the backup array for later delta comparisons
00303     cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
00304 
00305     if (cl_shownet->integer == 3) {
00306         Com_Printf( "   snapshot:%i  delta:%i  ping:%i\n", cl.snap.messageNum,
00307         cl.snap.deltaNum, cl.snap.ping );
00308     }
00309 
00310     cl.newSnapshots = qtrue;
00311 }
00312 
00313 
00314 //=====================================================================
00315 
00316 int cl_connectedToPureServer;
00317 
00318 /*
00319 ==================
00320 CL_SystemInfoChanged
00321 
00322 The systeminfo configstring has been changed, so parse
00323 new information out of it.  This will happen at every
00324 gamestate, and possibly during gameplay.
00325 ==================
00326 */
00327 void CL_SystemInfoChanged( void ) {
00328     char            *systemInfo;
00329     const char      *s, *t;
00330     char            key[BIG_INFO_KEY];
00331     char            value[BIG_INFO_VALUE];
00332     qboolean        gameSet;
00333 
00334     systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
00335     // NOTE TTimo:
00336     // when the serverId changes, any further messages we send to the server will use this new serverId
00337     // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
00338     // in some cases, outdated cp commands might get sent with this news serverId
00339     cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
00340 
00341     // don't set any vars when playing a demo
00342     if ( clc.demoplaying ) {
00343         return;
00344     }
00345 
00346     s = Info_ValueForKey( systemInfo, "sv_cheats" );
00347     if ( atoi(s) == 0 ) {
00348         Cvar_SetCheatState();
00349     }
00350 
00351     // check pure server string
00352     s = Info_ValueForKey( systemInfo, "sv_paks" );
00353     t = Info_ValueForKey( systemInfo, "sv_pakNames" );
00354     FS_PureServerSetLoadedPaks( s, t );
00355 
00356     s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
00357     t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
00358     FS_PureServerSetReferencedPaks( s, t );
00359 
00360     gameSet = qfalse;
00361     // scan through all the variables in the systeminfo and locally set cvars to match
00362     s = systemInfo;
00363     while ( s ) {
00364         Info_NextPair( &s, key, value );
00365         if ( !key[0] ) {
00366             break;
00367         }
00368         // ehw!
00369         if ( !Q_stricmp( key, "fs_game" ) ) {
00370             gameSet = qtrue;
00371         }
00372 
00373         Cvar_Set( key, value );
00374     }
00375     // if game folder should not be set and it is set at the client side
00376     if ( !gameSet && *Cvar_VariableString("fs_game") ) {
00377         Cvar_Set( "fs_game", "" );
00378     }
00379     cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
00380 }
00381 
00382 /*
00383 ==================
00384 CL_ParseGamestate
00385 ==================
00386 */
00387 void CL_ParseGamestate( msg_t *msg ) {
00388     int             i;
00389     entityState_t   *es;
00390     int             newnum;
00391     entityState_t   nullstate;
00392     int             cmd;
00393     char            *s;
00394 
00395     Con_Close();
00396 
00397     clc.connectPacketCount = 0;
00398 
00399     // wipe local client state
00400     CL_ClearState();
00401 
00402     // a gamestate always marks a server command sequence
00403     clc.serverCommandSequence = MSG_ReadLong( msg );
00404 
00405     // parse all the configstrings and baselines
00406     cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings
00407     while ( 1 ) {
00408         cmd = MSG_ReadByte( msg );
00409 
00410         if ( cmd == svc_EOF ) {
00411             break;
00412         }
00413         
00414         if ( cmd == svc_configstring ) {
00415             int     len;
00416 
00417             i = MSG_ReadShort( msg );
00418             if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
00419                 Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
00420             }
00421             s = MSG_ReadBigString( msg );
00422             len = strlen( s );
00423 
00424             if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
00425                 Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
00426             }
00427 
00428             // append it to the gameState string buffer
00429             cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
00430             Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
00431             cl.gameState.dataCount += len + 1;
00432         } else if ( cmd == svc_baseline ) {
00433             newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
00434             if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
00435                 Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
00436             }
00437             Com_Memset (&nullstate, 0, sizeof(nullstate));
00438             es = &cl.entityBaselines[ newnum ];
00439             MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
00440         } else {
00441             Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
00442         }
00443     }
00444 
00445     clc.clientNum = MSG_ReadLong(msg);
00446     // read the checksum feed
00447     clc.checksumFeed = MSG_ReadLong( msg );
00448 
00449     // parse serverId and other cvars
00450     CL_SystemInfoChanged();
00451 
00452     // reinitialize the filesystem if the game directory has changed
00453   FS_ConditionalRestart( clc.checksumFeed );
00454 
00455     // This used to call CL_StartHunkUsers, but now we enter the download state before loading the
00456     // cgame
00457     CL_InitDownloads();
00458 
00459     // make sure the game starts
00460     Cvar_Set( "cl_paused", "0" );
00461 }
00462 
00463 
00464 //=====================================================================
00465 
00466 /*
00467 =====================
00468 CL_ParseDownload
00469 
00470 A download message has been received from the server
00471 =====================
00472 */
00473 void CL_ParseDownload ( msg_t *msg ) {
00474     int     size;
00475     unsigned char data[MAX_MSGLEN];
00476     int block;
00477 
00478     // read the data
00479     block = MSG_ReadShort ( msg );
00480 
00481     if ( !block )
00482     {
00483         // block zero is special, contains file size
00484         clc.downloadSize = MSG_ReadLong ( msg );
00485 
00486         Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
00487 
00488         if (clc.downloadSize < 0)
00489         {
00490             Com_Error(ERR_DROP, MSG_ReadString( msg ) );
00491             return;
00492         }
00493     }
00494 
00495     size = MSG_ReadShort ( msg );
00496     if (size > 0)
00497         MSG_ReadData( msg, data, size );
00498 
00499     if (clc.downloadBlock != block) {
00500         Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
00501         return;
00502     }
00503 
00504     // open the file if not opened yet
00505     if (!clc.download)
00506     {
00507         if (!*clc.downloadTempName) {
00508             Com_Printf("Server sending download, but no download was requested\n");
00509             CL_AddReliableCommand( "stopdl" );
00510             return;
00511         }
00512 
00513         clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
00514 
00515         if (!clc.download) {
00516             Com_Printf( "Could not create %s\n", clc.downloadTempName );
00517             CL_AddReliableCommand( "stopdl" );
00518             CL_NextDownload();
00519             return;
00520         }
00521     }
00522 
00523     if (size)
00524         FS_Write( data, size, clc.download );
00525 
00526     CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
00527     clc.downloadBlock++;
00528 
00529     clc.downloadCount += size;
00530 
00531     // So UI gets access to it
00532     Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
00533 
00534     if (!size) { // A zero length block means EOF
00535         if (clc.download) {
00536             FS_FCloseFile( clc.download );
00537             clc.download = 0;
00538 
00539             // rename the file
00540             FS_SV_Rename ( clc.downloadTempName, clc.downloadName );
00541         }
00542         *clc.downloadTempName = *clc.downloadName = 0;
00543         Cvar_Set( "cl_downloadName", "" );
00544 
00545         // send intentions now
00546         // We need this because without it, we would hold the last nextdl and then start
00547         // loading right away.  If we take a while to load, the server is happily trying
00548         // to send us that last block over and over.
00549         // Write it twice to help make sure we acknowledge the download
00550         CL_WritePacket();
00551         CL_WritePacket();
00552 
00553         // get another file if needed
00554         CL_NextDownload ();
00555     }
00556 }
00557 
00558 /*
00559 =====================
00560 CL_ParseCommandString
00561 
00562 Command strings are just saved off until cgame asks for them
00563 when it transitions a snapshot
00564 =====================
00565 */
00566 void CL_ParseCommandString( msg_t *msg ) {
00567     char    *s;
00568     int     seq;
00569     int     index;
00570 
00571     seq = MSG_ReadLong( msg );
00572     s = MSG_ReadString( msg );
00573 
00574     // see if we have already executed stored it off
00575     if ( clc.serverCommandSequence >= seq ) {
00576         return;
00577     }
00578     clc.serverCommandSequence = seq;
00579 
00580     index = seq & (MAX_RELIABLE_COMMANDS-1);
00581     Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
00582 }
00583 
00584 
00585 /*
00586 =====================
00587 CL_ParseServerMessage
00588 =====================
00589 */
00590 void CL_ParseServerMessage( msg_t *msg ) {
00591     int         cmd;
00592 
00593     if ( cl_shownet->integer == 1 ) {
00594         Com_Printf ("%i ",msg->cursize);
00595     } else if ( cl_shownet->integer >= 2 ) {
00596         Com_Printf ("------------------\n");
00597     }
00598 
00599     MSG_Bitstream(msg);
00600 
00601     // get the reliable sequence acknowledge number
00602     clc.reliableAcknowledge = MSG_ReadLong( msg );
00603     // 
00604     if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
00605         clc.reliableAcknowledge = clc.reliableSequence;
00606     }
00607 
00608     //
00609     // parse the message
00610     //
00611     while ( 1 ) {
00612         if ( msg->readcount > msg->cursize ) {
00613             Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
00614             break;
00615         }
00616 
00617         cmd = MSG_ReadByte( msg );
00618 
00619         if ( cmd == svc_EOF) {
00620             SHOWNET( msg, "END OF MESSAGE" );
00621             break;
00622         }
00623 
00624         if ( cl_shownet->integer >= 2 ) {
00625             if ( !svc_strings[cmd] ) {
00626                 Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
00627             } else {
00628                 SHOWNET( msg, svc_strings[cmd] );
00629             }
00630         }
00631     
00632     // other commands
00633         switch ( cmd ) {
00634         default:
00635             Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
00636             break;          
00637         case svc_nop:
00638             break;
00639         case svc_serverCommand:
00640             CL_ParseCommandString( msg );
00641             break;
00642         case svc_gamestate:
00643             CL_ParseGamestate( msg );
00644             break;
00645         case svc_snapshot:
00646             CL_ParseSnapshot( msg );
00647             break;
00648         case svc_download:
00649             CL_ParseDownload( msg );
00650             break;
00651         }
00652     }
00653 }
00654 
00655 

Generated on Thu Aug 25 12:37:27 2005 for Quake III Arena by  doxygen 1.3.9.1