/* * TV_Upstream_ParseServerData */ static void TV_Upstream_ParseServerData( upstream_t *upstream, msg_t *msg ) { int i, numpure; TV_Upstream_ClearState( upstream ); upstream->state = CA_CONNECTED; // parse protocol version number i = MSG_ReadLong( msg ); if( i != APP_PROTOCOL_VERSION ) TV_Upstream_Error( upstream, "Server returned version %i, not %i", i, APP_PROTOCOL_VERSION ); upstream->servercount = MSG_ReadLong( msg ); upstream->snapFrameTime = (unsigned int) MSG_ReadShort( msg ); Q_strncpyz( upstream->basegame, MSG_ReadString( msg ), sizeof( upstream->basegame ) ); Q_strncpyz( upstream->game, MSG_ReadString( msg ), sizeof( upstream->game ) ); // parse player entity number upstream->playernum = MSG_ReadShort( msg ); // get the full level name Q_strncpyz( upstream->levelname, MSG_ReadString( msg ), sizeof( upstream->levelname ) ); upstream->sv_bitflags = MSG_ReadByte( msg ); upstream->reliable = ( ( upstream->sv_bitflags & SV_BITFLAGS_RELIABLE ) ? true : false ); if( ( upstream->sv_bitflags & SV_BITFLAGS_HTTP ) != 0 ) { if( ( upstream->sv_bitflags & SV_BITFLAGS_HTTP_BASEURL ) != 0 ) { // read base upstream url MSG_ReadString( msg ); } else { // http port number MSG_ReadShort( msg ); } } // pure list // clean old, if necessary Com_FreePureList( &upstream->purelist ); // add new numpure = MSG_ReadShort( msg ); while( numpure > 0 ) { const char *pakname = MSG_ReadString( msg ); const unsigned checksum = MSG_ReadLong( msg ); Com_AddPakToPureList( &upstream->purelist, pakname, checksum, upstream->mempool ); numpure--; } TV_Upstream_AddReliableCommand( upstream, va( "configstrings %i 0", upstream->servercount ) ); }
/* * TV_Upstream_ServerDisconnect_f * * The server is changing levels */ static void TV_Upstream_ServerDisconnect_f( upstream_t *upstream ) { int type; type = atoi( Cmd_Argv( 1 ) ); if( type < 0 || type >= DROP_TYPE_TOTAL ) type = DROP_TYPE_GENERAL; TV_Upstream_Error( upstream, "Server disconnected: %s", Cmd_Argv( 2 ) ); }
/* * TV_Upstream_Reject */ static void TV_Upstream_Reject_f( upstream_t *upstream, msg_t *msg ) { int rejecttype, rejectflag; char rejectmessage[MAX_STRING_CHARS]; rejecttype = atoi( MSG_ReadStringLine( msg ) ); if( rejecttype < 0 || rejecttype >= DROP_TYPE_TOTAL ) { rejecttype = DROP_TYPE_GENERAL; } rejectflag = atoi( MSG_ReadStringLine( msg ) ); Q_strncpyz( rejectmessage, MSG_ReadStringLine( msg ), sizeof( rejectmessage ) ); Com_Printf( "%s" S_COLOR_WHITE ": Upstream refused: %s\n", upstream->name, rejectmessage ); if( rejectflag & DROP_FLAG_AUTORECONNECT ) { Com_Printf( "Automatic reconnecting allowed.\n" ); } else { Com_Printf( "Automatic reconnecting not allowed.\n" ); } TV_Upstream_Error( upstream, "Upstream refused: %s", rejectmessage ); }
/* * TV_Upstream_ParseConfigstringCommand_f */ static void TV_Upstream_HandleConfigstring( upstream_t *upstream, int index, const char *val ) { char hostname[MAX_CONFIGSTRING_CHARS]; msg_t msg; qbyte msgbuf[MAX_MSGLEN]; if( !val || !val[0] ) return; if( index < 0 || index >= MAX_CONFIGSTRINGS ) TV_Upstream_Error( upstream, "configstring > MAX_CONFIGSTRINGS" ); Q_strncpyz( upstream->configstrings[index], val, sizeof( upstream->configstrings[index] ) ); if( index == CS_AUTORECORDSTATE ) { // don't do a thing until we receive a "precache" command if( upstream->precacheDone ) TV_Upstream_AutoRecordAction( upstream, upstream->configstrings[CS_AUTORECORDSTATE] ); return; } if( index != CS_HOSTNAME ) return; if( !upstream->demo.playing ) { TV_Upstream_SetName( upstream, val ); return; } // demos often come with generic hostnames, attempt to workaround that if( !Q_stricmp( val, APPLICATION " server" ) ) { char *temp; size_t temp_size; const char *filebase; filebase = COM_FileBase( upstream->demo.filename ); temp_size = strlen( filebase ) + strlen( APP_DEMO_EXTENSION_STR ) + 1; temp = Mem_TempMalloc( temp_size ); Q_strncpyz( temp, filebase, temp_size ); COM_ReplaceExtension( temp, APP_DEMO_EXTENSION_STR, temp_size ); if( Com_GlobMatch( "*_auto[0-9][0-9][0-9][0-9]" APP_DEMO_EXTENSION_STR, temp, qfalse ) || Com_GlobMatch( "*_mvd" APP_DEMO_EXTENSION_STR, temp, qfalse ) ) temp[strrchr( temp, '_' ) - temp] = '\0'; else COM_StripExtension( temp ); Q_strncpyz( hostname, va( S_COLOR_ORANGE "R: " S_COLOR_WHITE "%s", temp ), sizeof( hostname ) ); Mem_TempFree( temp ); } else { Q_strncpyz( hostname, va( S_COLOR_ORANGE "R: " S_COLOR_WHITE "%s", val ), sizeof( hostname ) ); } TV_Upstream_SetName( upstream, hostname ); // override CS_HOSTNAME in next packet MSG_Init( &msg, msgbuf, sizeof( msgbuf ) ); MSG_WriteByte( &msg, svc_servercs ); MSG_WriteString( &msg, va( "cs %i \"%s\"", CS_HOSTNAME, hostname ) ); TV_Upstream_SavePacket( upstream, &msg, 0 ); }
/* * TV_Upstream_ParseServerMessage */ void TV_Upstream_ParseServerMessage( upstream_t *upstream, msg_t *msg ) { int cmd; assert( upstream && upstream->state >= CA_HANDSHAKE ); assert( msg ); // parse the message while( upstream->state >= CA_HANDSHAKE ) { if( msg->readcount > msg->cursize ) TV_Upstream_Error( upstream, "Bad server message" ); cmd = MSG_ReadByte( msg ); if( cmd == -1 ) break; // other commands switch( cmd ) { default: TV_Upstream_Error( upstream, "Illegible server message" ); case svc_nop: break; case svc_servercmd: if( !upstream->reliable ) { int cmdNum = MSG_ReadLong( msg ); if( cmdNum < 0 ) TV_Upstream_Error( upstream, "Invalid cmdNum value" ); if( cmdNum <= upstream->lastExecutedServerCommand ) { MSG_ReadString( msg ); // read but ignore break; } upstream->lastExecutedServerCommand = cmdNum; } // fall trough case svc_servercs: // configstrings from demo files. they don't have acknowledge TV_Upstream_ParseServerCommand( upstream, msg ); break; case svc_serverdata: if( upstream->state == CA_HANDSHAKE ) { Cbuf_Execute(); // make sure any stuffed commands are done FS_Rescan(); // FIXME? TV_Upstream_ParseServerData( upstream, msg ); } else { return; // ignore rest of the packet (serverdata is always sent alone) } break; case svc_spawnbaseline: TV_Upstream_ParseBaseline( upstream, msg ); break; case svc_download: //CL_ParseDownload( msg ); break; case svc_clcack: if( upstream->reliable ) TV_Upstream_Error( upstream, "clack message while reliable" ); upstream->reliableAcknowledge = (unsigned)MSG_ReadLong( msg ); MSG_ReadLong( msg ); // ucmdAcknowledged break; case svc_frame: TV_Upstream_ParseFrame( upstream, msg ); break; case svc_demoinfo: { int length; assert( upstream->demo.playing ); length = MSG_ReadLong( msg ); MSG_SkipData( msg, length ); } break; case svc_playerinfo: case svc_packetentities: case svc_match: TV_Upstream_Error( upstream, "Out of place frame data" ); break; case svc_extension: if( 1 ) { int len; MSG_ReadByte( msg ); // extension id MSG_ReadByte( msg ); // version number len = MSG_ReadShort( msg ); // command length MSG_SkipData( msg, len ); // command data } break; } } // if recording demos, copy the message out if( upstream->demo.recording && !upstream->demo.waiting ) TV_Upstream_WriteDemoMessage( upstream, msg ); }