/* ============== CL_ServerInfo change serverinfo ============== */ void CL_ServerInfo( sizebuf_t *msg ) { string key, value; Q_strncpy( key, BF_ReadString( msg ), sizeof( key )); Q_strncpy( value, BF_ReadString( msg ), sizeof( value )); Info_SetValueForKey( cl.serverinfo, key, value ); }
/* ================ CL_UpdateUserinfo collect userinfo from all players ================ */ void CL_UpdateUserinfo( sizebuf_t *msg ) { int slot; qboolean active; player_info_t *player; slot = BF_ReadUBitLong( msg, MAX_CLIENT_BITS ); if( slot >= MAX_CLIENTS ) Host_Error( "CL_ParseServerMessage: svc_updateuserinfo > MAX_CLIENTS\n" ); player = &cl.players[slot]; active = BF_ReadOneBit( msg ) ? true : false; if( active ) { Q_strncpy( player->userinfo, BF_ReadString( msg ), sizeof( player->userinfo )); Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name )); Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model )); cl.playermodels[slot] = 0; player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); if( slot == cl.playernum ) Q_memcpy( &menu.playerinfo, player, sizeof( player_info_t )); } else Q_memset( player, 0, sizeof( *player )); }
/* ============== CL_ParseResourceList ============== */ void CL_ParseResourceList( sizebuf_t *msg ) { int i = 0; Q_memset( &reslist, 0, sizeof( resourcelist_t )); reslist.rescount = BF_ReadWord( msg ) - 1; for( i = 0; i < reslist.rescount; i++ ) { reslist.restype[i] = BF_ReadWord( msg ); Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE ); } cls.downloadcount = 0; for( i = 0; i < reslist.rescount; i++ ) { if( reslist.restype[i] == t_sound ) CL_CheckingSoundResFile( reslist.resnames[i] ); else CL_CheckingResFile( reslist.resnames[i] ); } if( !cls.downloadcount ) { BF_WriteByte( &cls.netchan.message, clc_stringcmd ); BF_WriteString( &cls.netchan.message, "continueloading" ); } }
/* ================= CL_ParseStatusMessage Handle a reply from a info ================= */ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) { char *s; s = BF_ReadString( msg ); UI_AddServerToList( from, s ); }
/* ================= CL_ParseStatusMessage Handle a reply from a info ================= */ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) { char *s; s = BF_ReadString( msg ); MsgDev( D_NOTE, "Got info string: %s\n", s ); UI_AddServerToList( from, s ); }
void CL_ParseStuffText( sizebuf_t *msg ) { char *s = BF_ReadString( msg ); if( cl_trace_stufftext->integer ) { Msg("^3STUFFTEXT:\n^2%s\n^3END^7\n", s); } Cbuf_AddText( s ); }
/* ============== CL_ParseCvarValue Find the client cvar value and sent it back to the server ============== */ void CL_ParseCvarValue( sizebuf_t *msg ) { const char *cvarName = BF_ReadString( msg ); convar_t *cvar = Cvar_FindVar( cvarName ); // build the answer BF_WriteByte( &cls.netchan.message, clc_requestcvarvalue ); BF_WriteString( &cls.netchan.message, cvar ? cvar->string : "Not Found" ); }
/* ================ CL_ParseLightStyle ================ */ void CL_ParseLightStyle( sizebuf_t *msg ) { int style; const char *s; style = BF_ReadByte( msg ); s = BF_ReadString( msg ); CL_SetLightstyle( style, s ); }
/* ================ CL_PrecacheEvent prceache event from server ================ */ void CL_PrecacheEvent( sizebuf_t *msg ) { int eventIndex; eventIndex = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); if( eventIndex < 0 || eventIndex >= MAX_EVENTS ) Host_Error( "CL_PrecacheEvent: bad eventindex %i\n", eventIndex ); Q_strncpy( cl.event_precache[eventIndex], BF_ReadString( msg ), sizeof( cl.event_precache[0] )); // can be set now CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex ); }
/* ================ CL_RegisterUserMessage register new user message or update existing ================ */ void CL_RegisterUserMessage( sizebuf_t *msg ) { char *pszName; int svc_num, size; svc_num = BF_ReadByte( msg ); size = BF_ReadByte( msg ); pszName = BF_ReadString( msg ); // important stuff if( size == 0xFF ) size = -1; svc_num = bound( 0, svc_num, 255 ); CL_LinkUserMessage( pszName, svc_num, size ); }
/* ================ CL_PrecacheModel prceache model from server ================ */ void CL_PrecacheModel( sizebuf_t *msg ) { int modelIndex; modelIndex = BF_ReadUBitLong( msg, MAX_MODEL_BITS ); if( modelIndex < 0 || modelIndex >= MAX_MODELS ) Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex ); Q_strncpy( cl.model_precache[modelIndex], BF_ReadString( msg ), sizeof( cl.model_precache[0] )); // when we loading map all resources is precached sequentially if( !cl.video_prepped ) return; Mod_RegisterModel( cl.model_precache[modelIndex], modelIndex ); }
/* ================ CL_PrecacheSound prceache sound from server ================ */ void CL_PrecacheSound( sizebuf_t *msg ) { int soundIndex; soundIndex = BF_ReadUBitLong( msg, MAX_SOUND_BITS ); if( soundIndex < 0 || soundIndex >= MAX_SOUNDS ) Host_Error( "CL_PrecacheSound: bad soundindex %i\n", soundIndex ); Q_strncpy( cl.sound_precache[soundIndex], BF_ReadString( msg ), sizeof( cl.sound_precache[0] )); // when we loading map all resources is precached sequentially if( !cl.audio_prepped ) return; cl.sound_index[soundIndex] = S_RegisterSound( cl.sound_precache[soundIndex] ); }
/* ============== CL_ParseResourceList ============== */ void CL_ParseResourceList( sizebuf_t *msg ) { int i = 0; Q_memset( &reslist, 0, sizeof( resourcelist_t )); reslist.rescount = BF_ReadWord( msg ) - 1; for( i = 0; i < reslist.rescount; i++ ) { reslist.restype[i] = BF_ReadWord( msg ); Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE ); } cls.downloadcount = 0; HTTP_ResetProcessState(); for( i = 0; i < reslist.rescount; i++ ) { // skip some types #if 0 if( reslist.restype[i] == t_model && !Q_strchr( download_types->latched_string, 'm' ) ) continue; if( reslist.restype[i] == t_sound && !Q_strchr( download_types->latched_string, 's' ) ) continue; if( reslist.restype[i] == t_eventscript && !Q_strchr( download_types->latched_string, 'e' ) ) continue; if( reslist.restype[i] == t_generic && !Q_strchr( download_types->latched_string, 'c' ) ) continue; #endif if( reslist.restype[i] == t_sound ) CL_CheckingSoundResFile( reslist.resnames[i] ); else CL_CheckingResFile( reslist.resnames[i] ); } if( !cls.downloadcount ) { BF_WriteByte( &cls.netchan.message, clc_stringcmd ); BF_WriteString( &cls.netchan.message, "continueloading" ); } }
/* ============================== Netchan_CopyFileFragments ============================== */ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg ) { fragbuf_t *p, *n; char filename[CS_SIZE]; int nsize; byte *buffer; int pos; if( !chan->incomingready[FRAG_FILE_STREAM] ) return false; if( !chan->incomingbufs[FRAG_FILE_STREAM] ) { MsgDev( D_WARN, "Netchan_CopyFileFragments: Called with no fragments readied\n" ); chan->incomingready[FRAG_FILE_STREAM] = false; return false; } p = chan->incomingbufs[FRAG_FILE_STREAM]; BF_Init( msg, "NetMessage", net_message_buffer, sizeof( net_message_buffer )); // copy in first chunk so we can get filename out BF_WriteBits( msg, BF_GetData( &p->frag_message ), BF_GetNumBitsWritten( &p->frag_message )); BF_SeekToBit( msg, 0 ); // rewind buffer //Q_strncpy( filename, BF_ReadString( msg ), sizeof( filename )); Q_snprintf( filename, sizeof( filename ), "downloaded/%s", BF_ReadString( msg ) ); if( Q_strlen( filename ) <= 0 ) { MsgDev( D_ERROR, "File fragment received with no filename\nFlushing input queue\n" ); // clear out bufs Netchan_FlushIncoming( chan, FRAG_FILE_STREAM ); return false; } else if( Q_strstr( filename, ".." )) { MsgDev( D_ERROR, "File fragment received with relative path, ignoring\n" ); // clear out bufs Netchan_FlushIncoming( chan, FRAG_FILE_STREAM ); return false; } Q_strncpy( chan->incomingfilename, filename, sizeof( chan->incomingfilename )); if( FS_FileExists( filename, false )) { MsgDev( D_ERROR, "Can't download %s, already exists\n", filename ); // clear out bufs Netchan_FlushIncoming( chan, FRAG_FILE_STREAM ); return true; } // create file from buffers nsize = 0; while ( p ) { nsize += BF_GetNumBytesWritten( &p->frag_message ); // Size will include a bit of slop, oh well if( p == chan->incomingbufs[FRAG_FILE_STREAM] ) { nsize -= BF_GetNumBytesRead( msg ); } p = p->next; } buffer = Mem_Alloc( net_mempool, nsize + 1 ); p = chan->incomingbufs[ FRAG_FILE_STREAM ]; pos = 0; while( p ) { int cursize; n = p->next; cursize = BF_GetNumBytesWritten( &p->frag_message ); // first message has the file name, don't write that into the data stream, // just write the rest of the actual data if( p == chan->incomingbufs[FRAG_FILE_STREAM] ) { // copy it in cursize -= BF_GetNumBytesRead( msg ); Q_memcpy( &buffer[pos], &p->frag_message.pData[BF_GetNumBytesRead( msg )], cursize ); } else { Q_memcpy( &buffer[pos], p->frag_message.pData, cursize ); } pos += cursize; Mem_Free( p ); p = n; } FS_WriteFile( filename, buffer, pos ); Mem_Free( buffer ); // clear remnants BF_Clear( msg ); chan->incomingbufs[FRAG_FILE_STREAM] = NULL; // reset flag chan->incomingready[FRAG_FILE_STREAM] = false; return true; }
/* ===================== Delta_ReadField read fields by offsets assume 'from' and 'to' is valid ===================== */ qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, void *to, float timebase ) { qboolean bSigned = ( pField->flags & DT_SIGNED ) ? true : false; float flValue, flAngle, flTime; qboolean bChanged; uint iValue; const char *pStr; char *pOut; bChanged = BF_ReadOneBit( msg ); ASSERT( pField->multiplier != 0.0f ); if( pField->flags & DT_BYTE ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); if ( pField->multiplier != 1.0f ) iValue /= pField->multiplier; } else { iValue = *(byte *)((byte *)from + pField->offset ); } *(byte *)((byte *)to + pField->offset ) = iValue; } else if( pField->flags & DT_SHORT ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); if ( pField->multiplier != 1.0f ) iValue /= pField->multiplier; } else { iValue = *(word *)((byte *)from + pField->offset ); } *(word *)((byte *)to + pField->offset ) = iValue; } else if( pField->flags & DT_INTEGER ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); if ( pField->multiplier != 1.0f ) iValue /= pField->multiplier; } else { iValue = *(uint *)((byte *)from + pField->offset ); } *(uint *)((byte *)to + pField->offset ) = iValue; } else if( pField->flags & DT_FLOAT ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); flValue = (int)iValue * ( 1.0f / pField->multiplier ); flValue = flValue * pField->post_multiplier; } else { #ifdef __arm__ memcpy(&flValue, (byte *)from + pField->offset, sizeof(float) ); #else flValue = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flValue, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flValue; #endif } else if( pField->flags & DT_ANGLE ) { if( bChanged ) { flAngle = BF_ReadBitAngle( msg, pField->bits ); } else { #ifdef __arm__ memcpy(&flAngle, (byte *)from + pField->offset, sizeof(float) ); #else flAngle = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flAngle, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flAngle; #endif } else if( pField->flags & DT_TIMEWINDOW_8 ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); flValue = (float)((int)(iValue * 0.01f )); flTime = timebase + flValue; } else { #ifdef __arm__ memcpy(&flTime, (byte *)from + pField->offset, sizeof(float) ); #else flTime = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flTime, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flTime; #endif } else if( pField->flags & DT_TIMEWINDOW_BIG ) { if( bChanged ) { iValue = BF_ReadBitLong( msg, pField->bits, bSigned ); flValue = (float)((int)iValue) * ( 1.0f / pField->multiplier ); flTime = timebase + flValue; } else { #ifdef __arm__ memcpy(&flTime, (byte *)from + pField->offset, sizeof(float) ); #else flTime = *(float *)((byte *)from + pField->offset ); #endif } #ifdef __arm__ memcpy((byte *)to + pField->offset, &flTime, sizeof(float)); #else *(float *)((byte *)to + pField->offset ) = flTime; #endif } else if( pField->flags & DT_STRING ) { if( bChanged ) { pStr = BF_ReadString( msg ); } else { pStr = (char *)((byte *)from + pField->offset ); } pOut = (char *)((byte *)to + pField->offset ); Q_strncpy( pOut, pStr, pField->size ); } return bChanged; }
/* ================= CL_ConnectionlessPacket Responses to broadcasts, etc ================= */ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { char *args; char *c, buf[MAX_SYSPATH]; int len = sizeof( buf ), i = 0; netadr_t servadr; BF_Clear( msg ); BF_ReadLong( msg ); // skip the -1 args = BF_ReadStringLine( msg ); Cmd_TokenizeString( args ); c = Cmd_Argv( 0 ); MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c ); // server connection if( !Q_strcmp( c, "client_connect" )) { if( cls.state == ca_connected ) { MsgDev( D_INFO, "Dup connect received. Ignored.\n"); return; } Netchan_Setup( NS_CLIENT, &cls.netchan, from, net_qport->integer); BF_WriteByte( &cls.netchan.message, clc_stringcmd ); BF_WriteString( &cls.netchan.message, "new" ); cls.state = ca_connected; cl.validsequence = 0; // haven't gotten a valid frame update yet cl.delta_sequence = -1; // we'll request a full delta from the baseline cls.lastoutgoingcommand = -1; // we don't have a backed up cmd history yet cls.nextcmdtime = host.realtime; // we can send a cmd right away CL_StartupDemoHeader (); } else if( !Q_strcmp( c, "info" )) { // server responding to a status broadcast CL_ParseStatusMessage( from, msg ); } else if( !Q_strcmp( c, "netinfo" )) { // server responding to a status broadcast CL_ParseNETInfoMessage( from, msg ); } else if( !Q_strcmp( c, "cmd" )) { // remote command from gui front end if( !NET_IsLocalAddress( from )) { Msg( "Command packet from remote host. Ignored.\n" ); return; } #ifdef XASH_SDL SDL_RestoreWindow( host.hWnd ); #endif args = BF_ReadString( msg ); Cbuf_AddText( args ); Cbuf_AddText( "\n" ); } else if( !Q_strcmp( c, "print" )) { // print command from somewhere Msg("remote: %s\n", BF_ReadString( msg ) ); } else if( !Q_strcmp( c, "ping" )) { // ping from somewhere Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" ); } else if( !Q_strcmp( c, "challenge" )) { // challenge from the server we are connecting to cls.challenge = Q_atoi( Cmd_Argv( 1 )); CL_SendConnectPacket(); return; } else if( !Q_strcmp( c, "echo" )) { // echo request from server Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 )); } else if( !Q_strcmp( c, "disconnect" )) { // a disconnect message from the server, which will happen if the server // dropped the connection but it is still getting packets from us CL_Disconnect(); CL_ClearEdicts(); } else if( !Q_strcmp( c, "f") ) { // serverlist got from masterserver while( !msg->bOverflow ) { servadr.type = NA_IP; // 4 bytes for IP BF_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 2 bytes for Port servadr.port = BF_ReadShort( msg ); if( !servadr.port ) break; MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr )); NET_Config( true ); // allow remote Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); } // execute at next frame preventing relation on fps Cbuf_AddText("menu_resetping\n"); } else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { // user out of band message (must be handled in CL_ConnectionlessPacket) if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, (byte *)buf ); } else MsgDev( D_ERROR, "Bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); }
/* ================= CL_ConnectionlessPacket Responses to broadcasts, etc ================= */ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) { char *args; char *c, buf[MAX_SYSPATH]; int len = sizeof( buf ); int dataoffset = 0; netadr_t servadr; BF_Clear( msg ); BF_ReadLong( msg ); // skip the -1 args = BF_ReadStringLine( msg ); Cmd_TokenizeString( args ); c = Cmd_Argv( 0 ); MsgDev( D_NOTE, "CL_ConnectionlessPacket: %s : %s\n", NET_AdrToString( from ), c ); // server connection if( !Q_strcmp( c, "client_connect" )) { if( cls.state == ca_connected ) { MsgDev( D_INFO, "dup connect received. ignored\n"); return; } Netchan_Setup( NS_CLIENT, &cls.netchan, from, Cvar_VariableValue( "net_qport" )); BF_WriteByte( &cls.netchan.message, clc_stringcmd ); BF_WriteString( &cls.netchan.message, "new" ); cls.state = ca_connected; cl.validsequence = 0; // haven't gotten a valid frame update yet cl.delta_sequence = -1; // we'll request a full delta from the baseline cls.lastoutgoingcommand = -1; // we don't have a backed up cmd history yet cls.nextcmdtime = host.realtime; // we can send a cmd right away CL_StartupDemoHeader (); UI_SetActiveMenu( false ); } else if( !Q_strcmp( c, "info" )) { // server responding to a status broadcast CL_ParseStatusMessage( from, msg ); } else if( !Q_strcmp( c, "netinfo" )) { // server responding to a status broadcast CL_ParseNETInfoMessage( from, msg ); } else if( !Q_strcmp( c, "cmd" )) { // remote command from gui front end if( !NET_IsLocalAddress( from )) { Msg( "Command packet from remote host. Ignored.\n" ); return; } ShowWindow( host.hWnd, SW_RESTORE ); SetForegroundWindow ( host.hWnd ); args = BF_ReadString( msg ); Cbuf_AddText( args ); Cbuf_AddText( "\n" ); } else if( !Q_strcmp( c, "print" )) { // print command from somewhere args = BF_ReadString( msg ); Msg( args ); } else if( !Q_strcmp( c, "ping" )) { // ping from somewhere Netchan_OutOfBandPrint( NS_CLIENT, from, "ack" ); } else if( !Q_strcmp( c, "challenge" )) { // challenge from the server we are connecting to cls.challenge = Q_atoi( Cmd_Argv( 1 )); CL_SendConnectPacket(); return; } else if( !Q_strcmp( c, "echo" )) { // echo request from server Netchan_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 )); } else if( !Q_strcmp( c, "disconnect" )) { // a disconnect message from the server, which will happen if the server // dropped the connection but it is still getting packets from us CL_Disconnect(); } else if( clgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { // user out of band message (must be handled in CL_ConnectionlessPacket) if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf ); } else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x66 && msg->pData[5] == 0x0A ) { dataoffset = 6; while( 1 ) { servadr.type = NA_IP; servadr.ip[0] = msg->pData[dataoffset + 0]; servadr.ip[1] = msg->pData[dataoffset + 1]; servadr.ip[2] = msg->pData[dataoffset + 2]; servadr.ip[3] = msg->pData[dataoffset + 3]; servadr.port = *(word *)&msg->pData[dataoffset + 4]; if( !servadr.port ) break; MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr )); NET_Config( true ); // allow remote Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); dataoffset += 6; } } else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args ); }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( sizebuf_t *msg ) { char *s; int i, j, cmd; int param1, param2; int bufStart; cls_message_debug.parsing = true; // begin parsing starting_count = BF_GetNumBytesRead( msg ); // updates each frame // parse the message while( 1 ) { if( BF_CheckOverflow( msg )) { Host_Error( "CL_ParseServerMessage: overflow!\n" ); return; } // mark start position bufStart = BF_GetNumBytesRead( msg ); // end of message if( BF_GetNumBitsLeft( msg ) < 8 ) break; cmd = BF_ReadByte( msg ); // record command for debugging spew on parse problem CL_Parse_RecordCommand( cmd, bufStart ); // other commands switch( cmd ) { case svc_bad: Host_Error( "svc_bad\n" ); break; case svc_nop: // this does nothing break; case svc_disconnect: MsgDev( D_INFO, "Disconnected from server\n" ); CL_Drop (); Host_AbortCurrentFrame (); break; case svc_changing: if( BF_ReadOneBit( msg )) { cls.changelevel = true; S_StopAllSounds(); if( cls.demoplayback ) { SCR_BeginLoadingPlaque( cl.background ); cls.changedemo = true; } } else MsgDev( D_INFO, "Server disconnected, reconnecting\n" ); CL_ClearState (); CL_InitEdicts (); // re-arrange edicts if( cls.demoplayback ) { cl.background = (cls.demonum != -1) ? true : false; cls.state = ca_connected; } else cls.state = ca_connecting; cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately break; case svc_setview: cl.refdef.viewentity = BF_ReadWord( msg ); break; case svc_sound: CL_ParseSoundPacket( msg, false ); break; case svc_time: // shuffle timestamps cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = BF_ReadFloat( msg ); break; case svc_print: i = BF_ReadByte( msg ); MsgDev( D_INFO, "^6%s", BF_ReadString( msg )); if( i == PRINT_CHAT ) S_StartLocalSound( "common/menu2.wav", VOL_NORM, false ); break; case svc_stufftext: CL_ParseStuffText( msg ); break; case svc_lightstyle: CL_ParseLightStyle( msg ); break; case svc_setangle: CL_ParseSetAngle( msg ); break; case svc_serverdata: Cbuf_Execute(); // make sure any stuffed commands are done CL_ParseServerData( msg ); break; case svc_addangle: CL_ParseAddAngle( msg ); break; case svc_clientdata: CL_ParseClientData( msg ); break; case svc_packetentities: CL_ParsePacketEntities( msg, false ); break; case svc_deltapacketentities: CL_ParsePacketEntities( msg, true ); break; case svc_updatepings: CL_UpdateUserPings( msg ); break; case svc_usermessage: CL_RegisterUserMessage( msg ); break; case svc_particle: CL_ParseParticles( msg ); break; case svc_restoresound: CL_ParseRestoreSoundPacket( msg ); break; case svc_spawnstatic: CL_ParseStaticEntity( msg ); break; case svc_ambientsound: CL_ParseSoundPacket( msg, true ); break; case svc_crosshairangle: CL_ParseCrosshairAngle( msg ); break; case svc_spawnbaseline: CL_ParseBaseline( msg ); break; case svc_temp_entity: CL_ParseTempEntity( msg ); break; case svc_setpause: cl.refdef.paused = ( BF_ReadOneBit( msg ) != 0 ); break; case svc_deltamovevars: CL_ParseMovevars( msg ); break; case svc_customization: CL_ParseCustomization( msg ); break; case svc_centerprint: CL_CenterPrint( BF_ReadString( msg ), 0.25f ); break; case svc_event: CL_ParseEvent( msg ); break; case svc_event_reliable: CL_ParseReliableEvent( msg ); break; case svc_updateuserinfo: CL_UpdateUserinfo( msg ); break; case svc_intermission: cl.refdef.intermission = true; break; case svc_modelindex: CL_PrecacheModel( msg ); break; case svc_soundindex: CL_PrecacheSound( msg ); break; case svc_soundfade: CL_ParseSoundFade( msg ); break; case svc_cdtrack: param1 = BF_ReadByte( msg ); param1 = bound( 1, param1, MAX_CDTRACKS ); // tracknum param2 = BF_ReadByte( msg ); param2 = bound( 1, param2, MAX_CDTRACKS ); // loopnum S_StartBackgroundTrack( clgame.cdtracks[param1-1], clgame.cdtracks[param2-1], 0 ); break; case svc_serverinfo: CL_ServerInfo( msg ); break; case svc_eventindex: CL_PrecacheEvent( msg ); break; case svc_deltatable: Delta_ParseTableField( msg ); break; case svc_weaponanim: param1 = BF_ReadByte( msg ); // iAnim param2 = BF_ReadByte( msg ); // body CL_WeaponAnim( param1, param2 ); break; case svc_bspdecal: CL_ParseStaticDecal( msg ); break; case svc_roomtype: param1 = BF_ReadShort( msg ); Cvar_SetFloat( "room_type", param1 ); break; case svc_chokecount: i = BF_ReadByte( msg ); j = cls.netchan.incoming_acknowledged - 1; for( ; i > 0 && j > cls.netchan.outgoing_sequence - CL_UPDATE_BACKUP; j-- ) { if( cl.frames[j & CL_UPDATE_MASK].receivedtime != -3.0 ) { cl.frames[j & CL_UPDATE_MASK].receivedtime = -2.0; i--; } } break; case svc_resourcelist: CL_ParseResourceList( msg ); break; case svc_director: CL_ParseDirector( msg ); break; case svc_studiodecal: CL_ParseStudioDecal( msg ); break; case svc_querycvarvalue: CL_ParseCvarValue( msg ); break; case svc_querycvarvalue2: CL_ParseCvarValue2( msg ); break; default: CL_ParseUserMessage( msg, cmd ); break; } } cls_message_debug.parsing = false; // done // we don't know if it is ok to save a demo message until // after we have parsed the frame if( !cls.demoplayback ) { if( cls.demorecording && !cls.demowaiting ) { CL_WriteDemoMessage( false, starting_count, msg ); } else if( cls.state != ca_active ) { CL_WriteDemoMessage( true, starting_count, msg ); } } }
/* ================== CL_ParseServerData ================== */ void CL_ParseServerData( sizebuf_t *msg ) { string gamefolder; qboolean background; int i; MsgDev( D_NOTE, "Serverdata packet received.\n" ); cls.demowaiting = false; // server is changed clgame.load_sequence++; // now all hud sprites are invalid // wipe the client_t struct if( !cls.changelevel && !cls.changedemo ) CL_ClearState (); cls.state = ca_connected; // parse protocol version number i = BF_ReadLong( msg ); cls.serverProtocol = i; if( i != PROTOCOL_VERSION ) Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); cl.servercount = BF_ReadLong( msg ); cl.checksum = BF_ReadLong( msg ); cl.playernum = BF_ReadByte( msg ); cl.maxclients = BF_ReadByte( msg ); clgame.maxEntities = BF_ReadWord( msg ); clgame.maxEntities = bound( 600, clgame.maxEntities, 4096 ); Q_strncpy( clgame.mapname, BF_ReadString( msg ), MAX_STRING ); Q_strncpy( clgame.maptitle, BF_ReadString( msg ), MAX_STRING ); background = BF_ReadOneBit( msg ); Q_strncpy( gamefolder, BF_ReadString( msg ), MAX_STRING ); host.features = (uint)BF_ReadLong( msg ); if( cl.maxclients > 1 && host.developer < 1 ) host.developer++; // set the background state if( cls.demoplayback && ( cls.demonum != -1 )) { // re-init mouse host.mouse_visible = false; cl.background = true; } else cl.background = background; if( cl.background ) // tell the game parts about background state Cvar_FullSet( "cl_background", "1", CVAR_READ_ONLY ); else Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY ); if( !cls.changelevel ) { // continue playing if we are changing level S_StopBackgroundTrack (); } #if 0 // NOTE: this is not tested as well. Use with precaution CL_ChangeGame( gamefolder, false ); #endif if( !cls.changedemo ) UI_SetActiveMenu( cl.background ); cl.refdef.viewentity = cl.playernum + 1; // always keep viewent an actual menu.globals->maxClients = cl.maxclients; Q_strncpy( menu.globals->maptitle, clgame.maptitle, sizeof( menu.globals->maptitle )); if( cl.maxclients > 1 && r_decals->value > mp_decals->value ) Cvar_SetFloat( "r_decals", mp_decals->value ); if( !cls.changelevel && !cls.changedemo ) CL_InitEdicts (); // re-arrange edicts // get splash name if( cls.demoplayback && ( cls.demonum != -1 )) Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, glState.wideScreen ? "16x9" : "4x3" )); else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, glState.wideScreen ? "16x9" : "4x3" )); Cvar_SetFloat( "scr_loading", 0.0f ); // reset progress bar if(( cl_allow_levelshots->integer && !cls.changelevel ) || cl.background ) { if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true )) Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime) } if( scr_dark->integer ) { screenfade_t *sf = &clgame.fade; client_textmessage_t *title; title = CL_TextMessageGet( "GAMETITLE" ); if( title ) { // get settings from titles.txt sf->fadeEnd = title->holdtime + title->fadeout; sf->fadeReset = title->fadeout; } else sf->fadeEnd = sf->fadeReset = 4.0f; sf->fadeFlags = FFADE_IN; sf->fader = sf->fadeg = sf->fadeb = 0; sf->fadealpha = 255; sf->fadeSpeed = (float)sf->fadealpha / sf->fadeReset; sf->fadeReset += cl.time; sf->fadeEnd += sf->fadeReset; Cvar_SetFloat( "v_dark", 0.0f ); } // need to prep refresh at next oportunity cl.video_prepped = false; cl.audio_prepped = false; Q_memset( &clgame.movevars, 0, sizeof( clgame.movevars )); Q_memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); }