diff options
-rw-r--r-- | doc/client.txt | 13 | ||||
-rw-r--r-- | src/cl_demo.c | 458 | ||||
-rw-r--r-- | src/cl_ents.c | 8 | ||||
-rw-r--r-- | src/cl_local.h | 10 | ||||
-rw-r--r-- | src/cl_main.c | 21 | ||||
-rw-r--r-- | src/cl_parse.c | 109 |
6 files changed, 504 insertions, 115 deletions
diff --git a/doc/client.txt b/doc/client.txt index 6114d84..2478252 100644 --- a/doc/client.txt +++ b/doc/client.txt @@ -829,6 +829,12 @@ cl_adjustfov:: Specifies if horizontal field of view is automatically adjusted for screens with aspect ratio different from 4/3. Default value is 0 (don't adjust FOV). +cl_demosnaps:: + Specifies time interval, in seconds, between saving ‘snapshots’ in memory + during demo playback. Snapshots enable backward seeking in demo (see ‘seek’ + command description), and speed up repeated forward seeks. Setting this + variable to 0 disables snapshotting entirely. Default value is 10. + ui_open:: Specifies if menu is automatically opened on startup, instead of full screen console. Default value is 1 (open menu). @@ -927,7 +933,12 @@ demo [/]<filename[.ext]>:: ‘demos/’ unless slash is prepended to _filename_, otherwise loads from the root of quake file system. Can be used to launch MVD playback as well, if MVD file type is detected, it will be automatically passed to the server - subsystem. + subsystem. To stop demo playback, type ‘disconnect’. + +seek [+-]<seconds>:: + During demo playback, seeks the given number of _seconds_ from current + position in demo file. Prepend with ‘+’ to seek forward, prepend with + ‘-’ to seek backward. Initial forward seek may be slow, so be patient. record [-hzes] <filename>:: Begins demo recording into ‘demos/_filename_.dm2’, or prints some diff --git a/src/cl_demo.c b/src/cl_demo.c index 5cf8c61..4a1c95f 100644 --- a/src/cl_demo.c +++ b/src/cl_demo.c @@ -27,6 +27,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. static byte demo_buffer[MAX_PACKETLEN_WRITABLE]; static int demo_extra; +static cvar_t *cl_demosnaps; + // ========================================================================= /* @@ -68,14 +70,8 @@ fail: return qfalse; } -/* -============= -CL_EmitPacketEntities - -Writes a delta update of an entity_state_t list to the message. -============= -*/ -static void CL_EmitPacketEntities( server_frame_t *from, server_frame_t *to ) { +// writes a delta update of an entity_state_t list to the message. +static void emit_packet_entities( server_frame_t *from, server_frame_t *to ) { entity_state_t *oldent, *newent; int oldindex, newindex; int oldnum, newnum; @@ -135,6 +131,27 @@ static void CL_EmitPacketEntities( server_frame_t *from, server_frame_t *to ) { MSG_WriteShort( 0 ); // end of packetentities } +static void emit_delta_frame( server_frame_t *from, server_frame_t *to, + int fromnum, int tonum ) +{ + MSG_WriteByte( svc_frame ); + MSG_WriteLong( tonum ); + MSG_WriteLong( fromnum ); // what we are delta'ing from + MSG_WriteByte( 0 ); // rate dropped packets + + // send over the areabits + MSG_WriteByte( to->areabytes ); + MSG_WriteData( to->areabits, to->areabytes ); + + // delta encode the playerstate + MSG_WriteByte( svc_playerinfo ); + MSG_WriteDeltaPlayerstate_Default( from ? &from->ps : NULL, &to->ps ); + + // delta encode the entities + MSG_WriteByte( svc_packetentities ); + emit_packet_entities( from, to ); +} + /* ==================== CL_EmitDemoFrame @@ -147,19 +164,19 @@ void CL_EmitDemoFrame( void ) { player_state_t *oldstate; int lastframe; - if( cls.demo.paused ) { + if( cls.demo.paused ) return; - } - if( cl.demoframe < 0 ) { + // the first frame is delta uncompressed + if( !cls.demo.frames_written ) { oldframe = NULL; oldstate = NULL; lastframe = -1; } else { - oldframe = &cl.frames[cl.demoframe & UPDATE_MASK]; + oldframe = &cl.frames[cls.demo.last_frame & UPDATE_MASK]; oldstate = &oldframe->ps; - lastframe = cl.demoframe + cl.demodelta; - if( oldframe->number != cl.demoframe || !oldframe->valid || + lastframe = cls.demo.frames_written; + if( oldframe->number != cls.demo.last_frame || !oldframe->valid || cl.numEntityStates - oldframe->firstEntity > MAX_PARSE_ENTITIES ) { oldframe = NULL; @@ -168,42 +185,34 @@ void CL_EmitDemoFrame( void ) { } } - MSG_WriteByte( svc_frame ); - MSG_WriteLong( cl.frame.number + cl.demodelta ); - MSG_WriteLong( lastframe ); // what we are delta'ing from - MSG_WriteByte( 0 ); // rate dropped packets - - // send over the areabits - MSG_WriteByte( cl.frame.areabytes ); - MSG_WriteData( cl.frame.areabits, cl.frame.areabytes ); - - // delta encode the playerstate - MSG_WriteByte( svc_playerinfo ); - MSG_WriteDeltaPlayerstate_Default( oldstate, &cl.frame.ps ); - - // delta encode the entities - MSG_WriteByte( svc_packetentities ); - CL_EmitPacketEntities( oldframe, &cl.frame ); + // we add 1 to frame number because frame 0 can't be used + emit_delta_frame( oldframe, &cl.frame, lastframe, cls.demo.frames_written + 1 ); if( cls.demo.buffer.cursize + msg_write.cursize > cls.demo.buffer.maxsize ) { Com_DPrintf( "Demo frame overflowed\n" ); cls.demo.frames_dropped++; } else { SZ_Write( &cls.demo.buffer, msg_write.data, msg_write.cursize ); - cl.demoframe = cl.frame.number; + cls.demo.last_frame = cl.frame.number; cls.demo.frames_written++; } SZ_Clear( &msg_write ); } -static void CL_EmitZeroFrame( void ) { - cl.demodelta++; // insert new zero frame +static void emit_zero_frame( void ) { + int lastframe; + + // the first frame is delta uncompressed + if( !cls.demo.frames_written ) + lastframe = -1; + else + lastframe = cls.demo.frames_written; MSG_WriteByte( svc_frame ); - MSG_WriteLong( cl.frame.number + cl.demodelta ); - MSG_WriteLong( cl.frame.number + cl.demodelta - 1 ); // what we are delta'ing from - MSG_WriteByte( 0 ); // rate dropped packets + MSG_WriteLong( cls.demo.frames_written + 1 ); + MSG_WriteLong( lastframe ); // what we are delta'ing from + MSG_WriteByte( 0 ); // rate dropped packets // send over the areabits MSG_WriteByte( cl.frame.areabytes ); @@ -363,10 +372,6 @@ static void CL_Record_f( void ) { demo_extra = 0; - // the first frame will be delta uncompressed - cl.demoframe = -1; - cl.demodelta = 0; - // clear dirty configstrings memset( cl.dcs, 0, sizeof( cl.dcs ) ); @@ -439,53 +444,61 @@ static void CL_Record_f( void ) { // the rest of the demo file will be individual frames } -static void CL_Suspend_f( void ) { +static void flush_dirty_configstrings( void ) { int i, j, index; - size_t length, total = 0; - - if( !cls.demo.recording ) { - Com_Printf( "Not recording a demo.\n" ); - return; - } - if( !cls.demo.paused ) { - Com_Printf( "Suspended demo.\n" ); - cls.demo.paused = qtrue; - return; - } + size_t len, total = 0; - // XXX: embed these in frame instead? for( i = 0; i < CS_BITMAP_LONGS; i++ ) { - if( (( uint32_t * )cl.dcs)[i] == 0 ) { + if( ((uint32_t *)cl.dcs)[i] == 0 ) continue; - } + index = i << 5; for( j = 0; j < 32; j++, index++ ) { - if( !Q_IsBitSet( cl.dcs, index ) ) { + if( !Q_IsBitSet( cl.dcs, index ) ) continue; - } - length = strlen( cl.configstrings[index] ); - if( length > MAX_QPATH ) { - length = MAX_QPATH; - } - if( msg_write.cursize + length + 4 > MAX_PACKETLEN_WRITABLE_DEFAULT ) { + + len = strlen( cl.configstrings[index] ); + if( len > MAX_QPATH ) + len = MAX_QPATH; + + if( msg_write.cursize + len + 4 > MAX_PACKETLEN_WRITABLE_DEFAULT ) { if( !CL_WriteDemoMessage( &msg_write ) ) return; } + MSG_WriteByte( svc_configstring ); MSG_WriteShort( index ); - MSG_WriteData( cl.configstrings[index], length ); + MSG_WriteData( cl.configstrings[index], len ); MSG_WriteByte( 0 ); - total += length + 4; + total += len + 4; } } - // write it to the demo file - if( !CL_WriteDemoMessage( &msg_write ) ) + CL_WriteDemoMessage( &msg_write ); + + Com_DPrintf( "Flushed %"PRIz" bytes\n", total ); +} + +static void CL_Suspend_f( void ) { + if( !cls.demo.recording ) { + Com_Printf( "Not recording a demo.\n" ); + return; + } + + if( !cls.demo.paused ) { + Com_Printf( "Suspended demo recording.\n" ); + cls.demo.paused = qtrue; + return; + } + + // XXX: embed these in frame instead? + flush_dirty_configstrings(); + + if( !cls.demo.recording ) return; - Com_Printf( "Resumed demo (%"PRIz" bytes flushed).\n", total ); + Com_Printf( "Resumed demo recording.\n" ); - cl.demodelta += cl.demoframe - cl.frame.number; // do not create holes cls.demo.paused = qfalse; // clear dirty configstrings @@ -585,33 +598,27 @@ static int read_next_message( qhandle_t f ) { return 1; } -static void parse_next_message( void ) { - int ret; - - ret = read_next_message( cls.demo.playback ); - if( ret <= 0 ) { - char *s = Cvar_VariableString( "nextserver" ); +static void finish_demo( int ret ) { + char *s = Cvar_VariableString( "nextserver" ); - if( !s[0] ) { - if( ret == 0 ) { - Com_Error( ERR_DISCONNECT, "Demo finished" ); - } else { - Com_Error( ERR_DROP, "Couldn't read demo: %s", Q_ErrorString( ret ) ); - } + if( !s[0] ) { + if( ret == 0 ) { + Com_Error( ERR_DISCONNECT, "Demo finished" ); + } else { + Com_Error( ERR_DROP, "Couldn't read demo: %s", Q_ErrorString( ret ) ); } + } - CL_Disconnect( ERR_RECONNECT ); - - Cvar_Set( "nextserver", "" ); + CL_Disconnect( ERR_RECONNECT ); - Cbuf_AddText( &cmd_buffer, s ); - Cbuf_AddText( &cmd_buffer, "\n" ); - Cbuf_Execute( &cmd_buffer ); - return; - } + Cvar_Set( "nextserver", "" ); - CL_ParseServerMessage(); + Cbuf_AddText( &cmd_buffer, s ); + Cbuf_AddText( &cmd_buffer, "\n" ); + Cbuf_Execute( &cmd_buffer ); +} +static void update_status( void ) { if( cls.demo.file_size ) { off_t pos = FS_Tell( cls.demo.playback ); @@ -621,6 +628,20 @@ static void parse_next_message( void ) { } } +static void parse_next_message( void ) { + int ret; + + ret = read_next_message( cls.demo.playback ); + if( ret <= 0 ) { + finish_demo( ret ); + return; + } + + CL_ParseServerMessage(); + + update_status(); +} + /* ==================== CL_PlayDemo_f @@ -675,6 +696,8 @@ static void CL_PlayDemo_f( void ) { parse_next_message(); } + memcpy( cl.baseconfigstrings, cl.configstrings, sizeof( cl.baseconfigstrings ) ); + len = FS_Length( f ); ofs = FS_Tell( f ); if( len > 0 && ofs > 0 ) { @@ -694,6 +717,223 @@ static void CL_Demo_c( genctx_t *ctx, int argnum ) { } } +#define DEMO_FPS 10 + +typedef struct { + list_t entry; + int framenum; + off_t filepos; + size_t msglen; + byte data[1]; +} demosnap_t; + +/* +==================== +CL_EmitDemoSnapshot + +Periodically builds a fake demo packet used to reconstruct delta compression +state, configstrings and layouts at the given server frame. +==================== +*/ +void CL_EmitDemoSnapshot( void ) { + demosnap_t *snap; + off_t pos; + char *from, *to; + size_t len; + server_frame_t *lastframe, *frame; + int i, j, lastnum; + + if( cl_demosnaps->integer <= 0 ) + return; + + if( cl.frame.number < cls.demo.last_snapshot + cl_demosnaps->integer * DEMO_FPS ) + return; + + if( !cls.demo.file_size ) + return; + + pos = FS_Tell( cls.demo.playback ); + if( pos < cls.demo.file_offset ) + return; + + // write all the backups, since we can't predict what frame the next + // delta will come from + lastframe = NULL; + lastnum = -1; + for( i = 0; i < UPDATE_BACKUP; i++ ) { + j = cl.frame.number - ( UPDATE_BACKUP - 1 ) + i; + frame = &cl.frames[j & UPDATE_MASK]; + if( frame->number != j || !frame->valid || + cl.numEntityStates - frame->firstEntity > MAX_PARSE_ENTITIES ) + { + continue; + } + + emit_delta_frame( lastframe, frame, lastnum, j ); + lastframe = frame; + lastnum = frame->number; + } + + // write configstrings + for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { + from = cl.baseconfigstrings[i]; + to = cl.configstrings[i]; + + if( !strcmp( from, to ) ) + continue; + + len = strlen( to ); + if( len > MAX_QPATH ) + len = MAX_QPATH; + + MSG_WriteByte( svc_configstring ); + MSG_WriteShort( i ); + MSG_WriteData( to, len ); + MSG_WriteByte( 0 ); + } + + // write layout + MSG_WriteByte( svc_layout ); + MSG_WriteString( cl.layout ); + + snap = Z_Malloc( sizeof( *snap ) + msg_write.cursize - 1 ); + snap->framenum = cl.frame.number; + snap->filepos = pos; + snap->msglen = msg_write.cursize; + memcpy( snap->data, msg_write.data, msg_write.cursize ); + List_Append( &cls.demo.snapshots, &snap->entry ); + + Com_DPrintf( "[%d] snaplen %"PRIz"\n", cl.frame.number, msg_write.cursize ); + + SZ_Clear( &msg_write ); + + cls.demo.last_snapshot = cl.frame.number; +} + +static demosnap_t *find_snapshot( int framenum ) { + demosnap_t *snap, *prev; + + if( LIST_EMPTY( &cls.demo.snapshots ) ) + return NULL; + + prev = LIST_FIRST( demosnap_t, &cls.demo.snapshots, entry ); + + LIST_FOR_EACH( demosnap_t, snap, &cls.demo.snapshots, entry ) { + if( snap->framenum > framenum ) + break; + prev = snap; + } + + return prev; +} + +static void CL_Seek_f( void ) { + demosnap_t *snap; + int i, j, ret, index, frames, dest, prev; + char *from, *to; + + if( Cmd_Argc() < 2 ) { + Com_Printf( "Usage: %s [+-]<seconds>\n", Cmd_Argv( 0 ) ); + return; + } + + if( !cls.demo.playback ) { + Com_Printf( "Not playing a demo.\n" ); + return; + } + + frames = atoi( Cmd_Argv( 1 ) ) * DEMO_FPS; + if( !frames ) + return; + + // disable effects processing + cls.demo.seeking = qtrue; + + // clear dirty configstrings + memset( cl.dcs, 0, sizeof( cl.dcs ) ); + + dest = cl.frame.number + frames; + prev = cl.frame.number; + + Com_DPrintf( "[%d] seeking to %d\n", cl.frame.number, dest ); + + // seek to the previous most recent snapshot + if( frames < 0 || cls.demo.last_snapshot > cl.frame.number ) { + snap = find_snapshot( dest ); + + if( snap ) { + Com_DPrintf( "found snap at %d\n", snap->framenum ); + ret = FS_Seek( cls.demo.playback, snap->filepos ); + if( ret < 0 ) { + Com_EPrintf( "Couldn't seek demo: %s\n", Q_ErrorString( ret ) ); + goto done; + } + + // reset configstrings + for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { + from = cl.baseconfigstrings[i]; + to = cl.configstrings[i]; + + if( !strcmp( from, to ) ) + continue; + + Q_SetBit( cl.dcs, i ); + strcpy( to, from ); + } + + SZ_Init( &msg_read, snap->data, snap->msglen ); + msg_read.cursize = snap->msglen; + + CL_SeekDemoMessage(); + Com_DPrintf( "[%d] after snap parse\n", cl.frame.number ); + } else if( frames < 0 ) { + Com_Printf( "Couldn't seek backwards without snapshots!\n" ); + goto done; + } + } + + // skip forward to destination frame + while( cl.frame.number < dest ) { + ret = read_next_message( cls.demo.playback ); + if( ret <= 0 ) { + finish_demo( ret ); + return; + } + + CL_SeekDemoMessage(); + } + + Com_DPrintf( "[%d] after skip\n", cl.frame.number ); + + // don't lerp to old + memset( &cl.oldframe, 0, sizeof( cl.oldframe ) ); + + // fix time delta + cl.serverdelta += cl.frame.number - prev; + + CL_DeltaFrame(); + + // update dirty configstrings + for( i = 0; i < CS_BITMAP_LONGS; i++ ) { + if( ((uint32_t *)cl.dcs)[i] == 0 ) + continue; + + index = i << 5; + for( j = 0; j < 32; j++, index++ ) { + if( Q_IsBitSet( cl.dcs, index ) ) + CL_UpdateConfigstring( index ); + } + } + + if( cls.demo.recording && !cls.demo.paused ) + flush_dirty_configstrings(); + + update_status(); + +done: + cls.demo.seeking = qfalse; +} + static void parse_info_string( demoInfo_t *info, int clientNum, int index, const char *string ) { size_t len; char *p; @@ -807,6 +1047,40 @@ fail: // ========================================================================= +void CL_CleanupDemos( void ) { + demosnap_t *snap, *next; + size_t total; + + if( cls.demo.recording ) { + CL_Stop_f(); + } + + if( cls.demo.playback ) { + FS_FCloseFile( cls.demo.playback ); + + if( com_timedemo->integer ) { + unsigned msec = Sys_Milliseconds(); + float sec = ( msec - cls.demo.time_start ) * 0.001f; + float fps = cls.demo.time_frames / sec; + + Com_Printf( "%u frames, %3.1f seconds: %3.1f fps\n", + cls.demo.time_frames, sec, fps ); + } + } + + total = 0; + LIST_FOR_EACH_SAFE( demosnap_t, snap, next, &cls.demo.snapshots, entry ) { + total += snap->msglen; + Z_Free( snap ); + } + + if( total ) + Com_DPrintf( "Freed %"PRIz" bytes of snaps\n", total ); + + memset( &cls.demo, 0, sizeof( cls.demo ) ); + + List_Init( &cls.demo.snapshots ); +} /* ==================== @@ -828,7 +1102,7 @@ void CL_DemoFrame( int msec ) { // for syncing with audio comments, etc demo_extra += msec; if( demo_extra > 100 ) { - CL_EmitZeroFrame(); + emit_zero_frame(); demo_extra = 0; } } @@ -855,6 +1129,7 @@ static const cmdreg_t c_demo[] = { { "record", CL_Record_f, CL_Demo_c }, { "stop", CL_Stop_f }, { "suspend", CL_Suspend_f }, + { "seek", CL_Seek_f }, { NULL } }; @@ -865,7 +1140,10 @@ CL_InitDemos ==================== */ void CL_InitDemos( void ) { + cl_demosnaps = Cvar_Get( "cl_demosnaps", "10", 0 ); + Cmd_Register( c_demo ); + List_Init( &cls.demo.snapshots ); } diff --git a/src/cl_ents.c b/src/cl_ents.c index 057f0f9..2b22547 100644 --- a/src/cl_ents.c +++ b/src/cl_ents.c @@ -174,8 +174,12 @@ static void CL_SetActiveState( void ) { cl.initialSeq = cls.netchan->outgoing_sequence; } - // set initial cl.predicted_origin and cl.predicted_angles - if( !cls.demo.playback ) { + if( cls.demo.playback ) { + // force initial snapshot + cls.demo.last_snapshot = INT_MIN; + CL_EmitDemoSnapshot(); + } else { + // set initial cl.predicted_origin and cl.predicted_angles VectorScale( cl.frame.ps.pmove.origin, 0.125f, cl.predicted_origin ); VectorScale( cl.frame.ps.pmove.velocity, 0.125f, cl.predicted_velocity ); if( cl.frame.ps.pmove.pm_type < PM_DEAD && diff --git a/src/cl_local.h b/src/cl_local.h index 8fc810e..5c4db2e 100644 --- a/src/cl_local.h +++ b/src/cl_local.h @@ -149,8 +149,6 @@ typedef struct client_state_s { int lastframe; - int demoframe; - int demodelta; byte dcs[CS_BITMAP_BYTES]; // the client maintains its own idea of view angles, which are @@ -206,6 +204,7 @@ typedef struct client_state_s { int maxclients; pmoveParams_t pmp; + char baseconfigstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; char mapname[MAX_QPATH]; // short format - q2dm1, etc @@ -371,11 +370,15 @@ typedef struct client_static_s { unsigned frames_written; unsigned frames_dropped; unsigned messages_dropped; + int last_snapshot; + int last_frame; int file_size; int file_offset; int file_percent; sizebuf_t buffer; + list_t snapshots; qboolean paused; + qboolean seeking; } demo; } client_static_t; @@ -568,6 +571,7 @@ extern mz_params_t mz; extern snd_params_t snd; void CL_ParseServerMessage (void); +void CL_SeekDemoMessage( void ); // // cl_ents.c @@ -762,9 +766,11 @@ void CL_ParticleSteamEffect2(cl_sustain_t *self); // cl_demo.c // void CL_InitDemos( void ); +void CL_CleanupDemos( void ); void CL_DemoFrame( int msec ); qboolean CL_WriteDemoMessage( sizebuf_t *buf ); void CL_EmitDemoFrame( void ); +void CL_EmitDemoSnapshot( void ); void CL_Stop_f( void ); demoInfo_t *CL_GetDemoInfo( const char *path, demoInfo_t *info ); diff --git a/src/cl_main.c b/src/cl_main.c index 7a334b8..ca01da7 100644 --- a/src/cl_main.c +++ b/src/cl_main.c @@ -661,26 +661,9 @@ void CL_Disconnect( error_type_t type ) { cls.netchan = NULL; } - // stop demo - if( cls.demo.recording ) { - CL_Stop_f(); - } - - if( cls.demo.playback ) { - FS_FCloseFile( cls.demo.playback ); - - if( com_timedemo->integer ) { - unsigned msec = Sys_Milliseconds(); - float sec = ( msec - cls.demo.time_start ) * 0.001f; - float fps = cls.demo.time_frames / sec; + // stop playback and/or recording + CL_CleanupDemos(); - Com_Printf( "%u frames, %3.1f seconds: %3.1f fps\n", - cls.demo.time_frames, sec, fps ); - } - } - - memset( &cls.demo, 0, sizeof( cls.demo ) ); - // stop download CL_CleanupDownloads(); diff --git a/src/cl_parse.c b/src/cl_parse.c index 22373eb..d88d842 100644 --- a/src/cl_parse.c +++ b/src/cl_parse.c @@ -375,7 +375,11 @@ static void CL_ParseFrame( int extrabits ) { cl.oldframe = cl.frame; cl.frame = frame; - CL_DeltaFrame(); + if( cls.demo.playback ) + CL_EmitDemoSnapshot(); + + if( !cls.demo.seeking ) + CL_DeltaFrame(); } /* @@ -407,6 +411,11 @@ static void CL_ParseConfigstring( int index ) { len = maxlen - 1; } + if( cls.demo.seeking ) { + Q_SetBit( cl.dcs, index ); + return; + } + if( cls.demo.recording && cls.demo.paused ) { Q_SetBit( cl.dcs, index ); } @@ -1234,3 +1243,101 @@ void CL_ParseServerMessage( void ) { } } +/* +===================== +CL_SeekDemoMessage + +A variant of ParseServerMessage that skips over non-important action messages, +used for seeking in demos. +===================== +*/ +void CL_SeekDemoMessage( void ) { + int cmd, extrabits; + int index; + +#ifdef _DEBUG + if( cl_shownet->integer == 1 ) { + Com_LPrintf( PRINT_DEVELOPER, "%"PRIz" ", msg_read.cursize ); + } else if( cl_shownet->integer > 1 ) { + Com_LPrintf( PRINT_DEVELOPER, "------------------\n" ); + } +#endif + +// +// parse the message +// + while( 1 ) { + if( msg_read.readcount > msg_read.cursize ) { + Com_Error( ERR_DROP, "%s: read past end of server message", __func__ ); + } + + if( ( cmd = MSG_ReadByte() ) == -1 ) { + SHOWNET( 1, "%3"PRIz":END OF MESSAGE\n", msg_read.readcount - 1 ); + break; + } + + extrabits = cmd >> SVCMD_BITS; + cmd &= SVCMD_MASK; + +#ifdef _DEBUG + if( cl_shownet->integer > 1 ) { + MSG_ShowSVC( cmd ); + } +#endif + + // other commands + switch( cmd ) { + default: + Com_Error( ERR_DROP, "%s: illegible server message: %d", __func__, cmd ); + break; + + case svc_nop: + break; + + case svc_disconnect: + case svc_reconnect: + Com_Error( ERR_DISCONNECT, "Server disconnected" ); + break; + + case svc_print: + MSG_ReadByte(); + // fall thorugh + + case svc_centerprint: + case svc_stufftext: + MSG_ReadString( NULL, 0 ); + break; + + case svc_configstring: + index = MSG_ReadShort(); + CL_ParseConfigstring( index ); + break; + + case svc_sound: + CL_ParseStartSoundPacket(); + break; + + case svc_temp_entity: + CL_ParseTEntPacket(); + break; + + case svc_muzzleflash: + case svc_muzzleflash2: + CL_ParseMuzzleFlashPacket( 0 ); + break; + + case svc_frame: + CL_ParseFrame( extrabits ); + continue; + + case svc_inventory: + CL_ParseInventory(); + break; + + case svc_layout: + CL_ParseLayout(); + break; + + } + } +} |