//Anything above this #include will be ignored by the compiler #include "../qcommon/exe_headers.h" // cl_parse.c -- parse a message received from the server #include "client.h" #include "../qcommon/stringed_ingame.h" #include "../ghoul2/g2_local.h" #ifdef _DONETPROFILE_ #include "../qcommon/INetProfile.h" #endif #include "../zlib32/zip.h" #ifdef _XBOX #include "../xbox/XBLive.h" #include "../xbox/XBoxCommon.h" #include "../xbox/XBVoice.h" #endif static char hiddenCvarVal[128]; char *svc_strings[256] = { "svc_bad", "svc_nop", "svc_gamestate", "svc_configstring", "svc_baseline", "svc_serverCommand", "svc_download", "svc_snapshot", "svc_setgame", "svc_mapchange", #ifdef _XBOX "svc_newpeer", "svc_removepeer", "svc_xbInfo", #endif }; void SHOWNET( msg_t *msg, char *s) { if ( cl_shownet->integer >= 2) { Com_Printf ("%3i:%s\n", msg->readcount-1, s); } } //void CL_SP_Print(const word ID, byte *Data); //, char* color) /* ========================================================================= MESSAGE PARSING ========================================================================= */ /* ================== CL_DeltaEntity Parses deltas from the given base and adds the resulting entity to the current frame ================== */ void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, qboolean unchanged) { entityState_t *state; // save the parsed entity state into the big circular buffer so // it can be used as the source for a later delta state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)]; if ( unchanged ) { *state = *old; } else { MSG_ReadDeltaEntity( msg, old, state, newnum ); } if ( state->number == (MAX_GENTITIES-1) ) { return; // entity was delta removed } cl.parseEntitiesNum++; frame->numEntities++; } /* ================== CL_ParsePacketEntities ================== */ void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) { int newnum; entityState_t *oldstate; int oldindex, oldnum; newframe->parseEntitiesNum = cl.parseEntitiesNum; newframe->numEntities = 0; // delta from the entities present in oldframe oldindex = 0; oldstate = NULL; if (!oldframe) { oldnum = 99999; } else { if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } while ( 1 ) { // read the entity index number newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum == (MAX_GENTITIES-1) ) { break; } if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message"); } while ( oldnum < newnum ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } if (oldnum == newnum) { // delta from previous state if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum); } CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } continue; } if ( oldnum > newnum ) { // delta from baseline if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum); } CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse ); continue; } } // any remaining entities in the old frame are copied over while ( oldnum != 99999 ) { // one or more entities from the old packet are unchanged if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum); } CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue ); oldindex++; if ( oldindex >= oldframe->numEntities ) { oldnum = 99999; } else { oldstate = &cl.parseEntities[ (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)]; oldnum = oldstate->number; } } } /* ================ 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 ); 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; #ifndef _XBOX // No demos on Xbox clc.demowaiting = qfalse; // we can start recording now #endif } 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 ); 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_ParseSetGame rww - Update fs_game, this message is so we can use the ext_data *_overrides.txt files for mods. ================ */ void MSG_CheckNETFPSFOverrides(qboolean psfOverrides); void FS_UpdateGamedir(void); void CL_ParseSetGame( msg_t *msg ) { char newGameDir[MAX_QPATH]; int i = 0; char next; while (i < MAX_QPATH) { next = MSG_ReadByte( msg ); if (next) { //if next is 0 then we have finished reading to the end of the message newGameDir[i] = next; } else { break; } i++; } newGameDir[i] = 0; Cvar_Set("fs_game", newGameDir); //Update the search path for the mod dir FS_UpdateGamedir(); //Now update the overrides manually #ifndef _XBOX // No mods on Xbox MSG_CheckNETFPSFOverrides(qfalse); MSG_CheckNETFPSFOverrides(qtrue); #endif } //===================================================================== int cl_connectedToPureServer; int cl_connectedGAME; int cl_connectedCGAME; int cl_connectedUI; /* ================== CL_SystemInfoChanged The systeminfo configstring has been changed, so parse new information out of it. This will happen at every gamestate, and possibly during gameplay. ================== */ void CL_SystemInfoChanged( void ) { char *systemInfo; const char *s, *t; char key[BIG_INFO_KEY]; char value[BIG_INFO_VALUE]; qboolean gameSet; systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ]; cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) ); // don't set any vars when playing a demo #ifndef _XBOX // No demos on Xbox if ( clc.demoplaying ) { return; } #endif s = Info_ValueForKey( systemInfo, "sv_cheats" ); if ( atoi(s) == 0 ) { Cvar_SetCheatState(); } // check pure server string s = Info_ValueForKey( systemInfo, "sv_paks" ); t = Info_ValueForKey( systemInfo, "sv_pakNames" ); FS_PureServerSetLoadedPaks( s, t ); s = Info_ValueForKey( systemInfo, "sv_referencedPaks" ); t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" ); FS_PureServerSetReferencedPaks( s, t ); gameSet = qfalse; // scan through all the variables in the systeminfo and locally set cvars to match s = systemInfo; while ( s ) { Info_NextPair( &s, key, value ); if ( !key[0] ) { break; } // ehw! if ( !Q_stricmp( key, "fs_game" ) ) { gameSet = qtrue; } Cvar_Set( key, value ); } // if game folder should not be set and it is set at the client side if ( !gameSet && *Cvar_VariableString("fs_game") ) { Cvar_Set( "fs_game", "" ); } cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" ); cl_connectedGAME = atoi(Info_ValueForKey( systemInfo, "vm_game" )); cl_connectedCGAME = atoi(Info_ValueForKey( systemInfo, "vm_cgame" )); cl_connectedUI = atoi(Info_ValueForKey( systemInfo, "vm_ui" )); } void CL_ParseAutomapSymbols ( msg_t* msg ) { int i; clc.rmgAutomapSymbolCount = (unsigned short) MSG_ReadShort ( msg ); for ( i = 0; i < clc.rmgAutomapSymbolCount; i ++ ) { clc.rmgAutomapSymbols[i].mType = (int)MSG_ReadByte ( msg ); clc.rmgAutomapSymbols[i].mSide = (int)MSG_ReadByte ( msg ); clc.rmgAutomapSymbols[i].mOrigin[0] = (float)MSG_ReadLong ( msg ); clc.rmgAutomapSymbols[i].mOrigin[1] = (float)MSG_ReadLong ( msg ); } } 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); 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 ); inflateEnd(&zdata); } else { MSG_ReadData ( msg, (unsigned char*)clc.rmgFlattenMap, size ); } // Read the seed clc.rmgSeed = MSG_ReadLong ( msg ); CL_ParseAutomapSymbols ( msg ); } /* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); #ifdef _DONETPROFILE_ int startBytes,endBytes; startBytes=msg->readcount; #endif // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len, start; start = msg->readcount; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); if (cl_shownet->integer >= 2) { Com_Printf("%3i: %d: %s\n", start, i, s); } /* if (i == CS_SERVERINFO) { //get the special value here char *f = strstr(s, "g_debugMelee"); if (f) { while (*f && *f != '\\') { //find the \ after it f++; } if (*f == '\\') { //got it int i = 0; f++; while (*f && *f != '\\' && i < 128) { hiddenCvarVal[i] = *f; i++; f++; } hiddenCvarVal[i] = 0; //resume here s = f; } } } */ len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); CL_ParseRMG ( msg ); //rwwRMG - get info for it from the server #ifdef _DONETPROFILE_ endBytes=msg->readcount; // ClReadProf().AddField("svc_gamestate",endBytes-startBytes); #endif // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed if( FS_ConditionalRestart( clc.checksumFeed ) ) { // don't set to true because we yet have to start downloading // enabling this can cause double loading of a map when connecting to // a server which has a different game directory set //clc.downloadRestart = qtrue; } // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); } //===================================================================== /* ===================== CL_ParseDownload A download message has been received from the server ===================== */ void CL_ParseDownload ( msg_t *msg ) { #ifdef _XBOX assert(!"Xbox received a download message. Unsupported!"); #else int size; unsigned char data[MAX_MSGLEN]; int block; // read the data block = (unsigned short)MSG_ReadShort ( msg ); 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, MSG_ReadString( msg ) ); return; } } size = (unsigned short)MSG_ReadShort ( msg ); if (size > 0) 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) { if (!*clc.downloadTempName) { Com_Printf("Server sending download, but no download was requested\n"); CL_AddReliableCommand( "stopdl" ); return; } clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName ); if (!clc.download) { Com_Printf( "Could not create %s\n", clc.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 ( clc.downloadTempName, clc.downloadName ); } *clc.downloadTempName = *clc.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 (); } #endif } int CL_GetValueForHidden(const char *s) { //string arg here just in case I want to add more sometime and make a lookup table return atoi(hiddenCvarVal); } /* ===================== CL_ParseCommandString Command strings are just saved off until cgame asks for them when it transitions a snapshot ===================== */ void CL_ParseCommandString( msg_t *msg ) { char *s; int seq; int index; #ifdef _DONETPROFILE_ int startBytes,endBytes; startBytes=msg->readcount; #endif seq = MSG_ReadLong( msg ); s = MSG_ReadString( msg ); #ifdef _DONETPROFILE_ endBytes=msg->readcount; ClReadProf().AddField("svc_serverCommand",endBytes-startBytes); #endif // see if we have already executed stored it off if ( clc.serverCommandSequence >= seq ) { return; } clc.serverCommandSequence = seq; index = seq & (MAX_RELIABLE_COMMANDS-1); /* if (s[0] == 'c' && s[1] == 's' && s[2] == ' ' && s[3] == '0' && s[4] == ' ') { //yes.. we seem to have an incoming server info. char *f = strstr(s, "g_debugMelee"); if (f) { while (*f && *f != '\\') { //find the \ after it f++; } if (*f == '\\') { //got it int i = 0; f++; while (*f && *f != '\\' && i < 128) { hiddenCvarVal[i] = *f; i++; f++; } hiddenCvarVal[i] = 0; //don't worry about backing over beginning of string I guess, //we already know we successfully strstr'd the initial string //which exceeds this length. //MSG_ReadString appears to just return a static buffer so I //can stomp over its contents safely. f--; *f = '\"'; f--; *f = ' '; f--; *f = '0'; f--; *f = ' '; f--; *f = 's'; f--; *f = 'c'; //the normal configstring gets to start here... s = f; } } } */ Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) ); } /* ===================== 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"); } MSG_Bitstream(msg); // get the reliable sequence acknowledge number clc.reliableAcknowledge = MSG_ReadLong( msg ); // if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { clc.reliableAcknowledge = clc.reliableSequence; } // // parse the message // while ( 1 ) { if ( msg->readcount > msg->cursize ) { Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message"); break; } cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF) { 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\n"); break; case svc_nop: break; case svc_serverCommand: CL_ParseCommandString( msg ); break; case svc_gamestate: CL_ParseGamestate( msg ); break; case svc_snapshot: CL_ParseSnapshot( msg ); break; case svc_setgame: CL_ParseSetGame( msg ); break; case svc_download: CL_ParseDownload( msg ); break; case svc_mapchange: if (cgvm) { VM_Call( cgvm, CG_MAP_CHANGE ); } break; #ifdef _XBOX case svc_newpeer: { //jsw// new client to add to our XBonlineInfo // We now get the index that we should use to store this from the server // That ensures that our playerlist and clientinfo stay in sync! int index = MSG_ReadLong(msg); // Sanity check - server shouldn't have us overwriting an active player // Unless, we're the server, in which case it will be active. Doh. assert( com_sv_running->integer || !xbOnlineInfo.xbPlayerList[index].isActive ); // OK. Read directly into the right place MSG_ReadData(msg, &xbOnlineInfo.xbPlayerList[index], sizeof(XBPlayerInfo)); // Need to do address conversion here, voice needs it XNetXnAddrToInAddr( &xbOnlineInfo.xbPlayerList[index].xbAddr, Net_GetXNKID(), &xbOnlineInfo.xbPlayerList[index].inAddr ); // VVFIXME - Search for g_Voice here - lots of stuff we're not doing // g_Voice.OnPlayerJoined( index ); //if(g_Voice.IsPlayerMuted(&xbOnlineInfo.xbPlayerList[index].xuid)) //{ // g_Voice.MutePlayer(&xbOnlineInfo.xbPlayerList[index].xbAddr, &xbOnlineInfo.xbPlayerList[index].xuid, false); // xbOnlineInfo.xbPlayerList[index].flags |= MUTED_PLAYER; //} //else if(Cvar_Get(CVAR_XBL_WT_ACTIVE)->integer) //{ //reset w/t in case he's on the channel // g_Voice.EnableWalkieTalkie(); //} XBL_PL_CheckHistoryList(&xbOnlineInfo.xbPlayerList[index]); break; } case svc_removepeer: { // Remove a client from our xbOnlineInfo. Our ordering is the same // as the server, so we just get an index. int index = MSG_ReadLong(msg); // Sanity check assert( xbOnlineInfo.xbPlayerList[index].isActive && index != xbOnlineInfo.localIndex ); // Clear out important information xbOnlineInfo.xbPlayerList[index].isActive = false; g_Voice.OnPlayerDisconnect( &xbOnlineInfo.xbPlayerList[index] ); XBL_PL_RemoveActivePeer(&xbOnlineInfo.xbPlayerList[index].xuid, index); break; } case svc_xbInfo: { //jsw//get XNADDR list from server MSG_ReadData(msg, &xbOnlineInfo, sizeof(XBOnlineInfo)); // Immediately convert everyone's XNADDR to an IN_ADDR for( int i = 0; i < MAX_ONLINE_PLAYERS; ++i ) { if( xbOnlineInfo.xbPlayerList[i].isActive ) XNetXnAddrToInAddr( &xbOnlineInfo.xbPlayerList[i].xbAddr, Net_GetXNKID(), &xbOnlineInfo.xbPlayerList[i].inAddr ); } //now find which entry we are if( logged_on ) { PXONLINE_USER pToUser = XOnlineGetLogonUsers(); BOOL found = false; for( int i = 0; i < MAX_ONLINE_PLAYERS && !found; i++ ) { if( xbOnlineInfo.xbPlayerList[i].isActive && XOnlineAreUsersIdentical(&pToUser->xuid, &xbOnlineInfo.xbPlayerList[i].xuid)) { xbOnlineInfo.localIndex = i; found = true; } } // OK. Time to join the chat! - VVFIXME - need to check mute lists g_Voice.JoinSession(); } else { DWORD dwStatus; IN_ADDR localAddr; XNetXnAddrToInAddr(Net_GetXNADDR( &dwStatus ), Net_GetXNKID(), &localAddr); if( dwStatus != XNET_GET_XNADDR_NONE ) { int j; for( j = 0; j < MAX_ONLINE_PLAYERS; ++j ) { if( xbOnlineInfo.xbPlayerList[j].isActive && localAddr.s_addr == xbOnlineInfo.xbPlayerList[i].inAddr.s_addr ) break; } xbOnlineInfo.localIndex = (j == MAX_ONLINE_PLAYERS) ? -1 : j; } } break; } #endif } } } extern int scr_center_y; void SCR_CenterPrint (char *str);//, PalIdx_t colour)