DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

PacketProcessor.cpp (21185B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idLib/precompiled.h"
     31 
     32 idCVar net_maxRate( "net_maxRate", "50", CVAR_INTEGER, "max send rate in kilobytes per second" );
     33 
     34 idCVar net_showReliableCompression( "net_showReliableCompression", "0", CVAR_BOOL, "Show reliable compression ratio." );
     35 
     36 // we use an assert(0); return idiom in some places, which lint complains about
     37 //lint -e527	unreachable code at token 'return'
     38 
     39 /*
     40 ================================================
     41 idPacketProcessor::QueueReliableAck
     42 ================================================
     43 */
     44 void idPacketProcessor::QueueReliableAck( int lastReliable ) {
     45 	// NOTE - Even if it was the last known sequence, go ahead and ack it, in case our last ack for this sequence got dropped
     46 	if ( lastReliable >= reliableSequenceRecv ) {		
     47 		queuedReliableAck		= lastReliable;
     48 		reliableSequenceRecv	= lastReliable;
     49 	}
     50 }
     51 
     52 /*
     53 ================================================
     54 idPacketProcessor::FinalizeRead
     55 ================================================
     56 */
     57 int idPacketProcessor::FinalizeRead( idBitMsg & inMsg, idBitMsg & outMsg, int & userValue ) {
     58 	userValue = 0;
     59 	
     60 	idInnerPacketHeader header;	
     61 	header.ReadFromMsg( inMsg );
     62 	
     63 	if ( !verify( header.Type() != PACKET_TYPE_FRAGMENTED ) ) {		// We shouldn't be fragmented at this point
     64 		idLib::Printf("Received invalid fragmented packet.\n" );
     65 		return RETURN_TYPE_NONE;
     66 	}
     67 
     68 	if ( header.Type() == PACKET_TYPE_RELIABLE_ACK ) {
     69 		// Handle reliable ack
     70 		int reliableSequence = inMsg.ReadLong();
     71 		reliable.RemoveOlderThan( reliableSequence + 1 );
     72 		header.ReadFromMsg( inMsg );								// Read the new header, since the reliable ack sits on top the actual header of the message
     73 	}
     74 
     75 	if ( header.Type() == PACKET_TYPE_OOB ) {
     76 		// out-of-band packet
     77 		userValue = header.Value();
     78 	} else {
     79 		// At this point, this MUST be an in-band packet
     80 		if ( !verify( header.Type() == PACKET_TYPE_INBAND ) ) {
     81 			idLib::Printf("In-band packet expected, received type %i instead.\n", header.Type() );
     82 			return RETURN_TYPE_NONE;
     83 		}
     84 		
     85 		// Reset number of reliables received (NOTE - This means you MUST unload all reliables as they are received)
     86 		numReliable = 0;
     87 	
     88 		// Handle reliable portion of in-band packets
     89 		int numReliableRecv = header.Value();
     90 		int bufferPos = 0;
     91 		
     92 		if ( numReliableRecv > 0 ) {
     93 			// Byte align msg
     94 			inMsg.ReadByteAlign();
     95 
     96 			int compressedSize = inMsg.ReadShort();
     97 
     98 			lzwCompressionData_t	lzwData;
     99 			idLZWCompressor			lzwCompressor( &lzwData );
    100 	
    101 			lzwCompressor.Start( (uint8*)inMsg.GetReadData() + inMsg.GetReadCount(), compressedSize );		// Read from msg
    102 
    103 			int reliableSequence = 0;
    104 
    105 			lzwCompressor.ReadAgnostic< int >( reliableSequence );
    106 
    107 			for ( int r = 0; r < numReliableRecv; r++ ) {
    108 				uint8 uncompMem[ MAX_MSG_SIZE ];
    109 
    110 				uint16 reliableDataLength = 0;
    111 				lzwCompressor.ReadAgnostic< uint16 >( reliableDataLength );
    112 				lzwCompressor.Read( uncompMem, reliableDataLength );
    113 
    114 				if ( reliableSequence + r > reliableSequenceRecv ) {		// Only accept newer reliable msg's than we've currently already received
    115 					if ( !verify( bufferPos + reliableDataLength <= sizeof( reliableBuffer ) ) ) {
    116 						idLib::Printf( "Reliable msg size overflow.\n" );
    117 						return RETURN_TYPE_NONE;
    118 					}
    119 					if ( !verify( numReliable < MAX_RELIABLE_QUEUE ) ) {
    120 						idLib::Printf( "Reliable msg count overflow.\n" );
    121 						return RETURN_TYPE_NONE;
    122 					}
    123 					memcpy( reliableBuffer + bufferPos, uncompMem, reliableDataLength );
    124 					reliableMsgSize[ numReliable ] = reliableDataLength;
    125 					reliableMsgPtrs[ numReliable++ ] = &reliableBuffer[ bufferPos ];
    126 					bufferPos += reliableDataLength;					
    127 				} else {
    128 					extern idCVar net_verboseReliable;
    129 					if ( net_verboseReliable.GetBool() ) {
    130 						idLib::Printf( "Ignoring reliable msg %i because %i was already acked\n", ( reliableSequence + r ), reliableSequenceRecv );
    131 					}
    132 				}
    133 		
    134 				if ( !verify( lzwCompressor.IsOverflowed() == false ) ) {
    135 					idLib::Printf( "lzwCompressor.IsOverflowed() == true.\n" );
    136 					return RETURN_TYPE_NONE;
    137 				}
    138 			}
    139 	
    140 			inMsg.SetReadCount( inMsg.GetReadCount() + compressedSize );
    141 
    142 			QueueReliableAck( reliableSequence + numReliableRecv - 1 );
    143 		}
    144 	}
    145 	
    146 	// Load actual msg
    147 	outMsg.BeginWriting();
    148 	outMsg.WriteData( inMsg.GetReadData() + inMsg.GetReadCount(), inMsg.GetRemainingData() );
    149 	outMsg.SetSize( inMsg.GetRemainingData() );
    150 	
    151 	return ( header.Type() == PACKET_TYPE_OOB ) ? RETURN_TYPE_OOB : RETURN_TYPE_INBAND;
    152 }
    153 
    154 /*
    155 ================================================
    156 idPacketProcessor::QueueReliableMessage
    157 ================================================
    158 */
    159 bool idPacketProcessor::QueueReliableMessage( byte type, const byte * data, int dataLen ) {
    160 	return reliable.Append( reliableSequenceSend++, &type, 1, data, dataLen );
    161 }
    162 
    163 /*
    164 ========================
    165 idPacketProcessor::CanSendMoreData
    166 ========================
    167 */
    168 bool idPacketProcessor::CanSendMoreData() const {
    169 	if ( net_maxRate.GetInteger() == 0 ) {
    170 		return true;
    171 	}
    172 
    173 	return ( outgoingRateBytes <= net_maxRate.GetInteger() * 1024 );
    174 }
    175 
    176 /*
    177 ========================
    178 idPacketProcessor::UpdateOutgoingRate
    179 ========================
    180 */
    181 void idPacketProcessor::UpdateOutgoingRate( const int time, const int size ) {
    182 	outgoingBytes += size;
    183 
    184 	// update outgoing rate variables
    185 	if ( time > outgoingRateTime ) {
    186 		outgoingRateBytes -= outgoingRateBytes * (float)( time - outgoingRateTime ) / 1000.0f;
    187 		if ( outgoingRateBytes < 0.0f ) {
    188 			outgoingRateBytes = 0.0f;
    189 		}
    190 	}
    191 	
    192 	outgoingRateTime = time;
    193 	outgoingRateBytes += size;
    194 
    195 	// compute an average bandwidth at intervals
    196 	if ( time - lastOutgoingRateTime > BANDWIDTH_AVERAGE_PERIOD ) {
    197 		currentOutgoingRate = 1000 * ( outgoingBytes - lastOutgoingBytes ) / ( time - lastOutgoingRateTime );
    198 		lastOutgoingBytes = outgoingBytes;
    199 		lastOutgoingRateTime = time;
    200 	}
    201 }
    202 
    203 /*
    204 =================
    205 idPacketProcessor::UpdateIncomingRate
    206 =================
    207 */
    208 void idPacketProcessor::UpdateIncomingRate( const int time, const int size ) {
    209 	incomingBytes += size;
    210 
    211 	// update incoming rate variables
    212 	if ( time > incomingRateTime ) {
    213 		incomingRateBytes -= incomingRateBytes * (float)( time - incomingRateTime ) / 1000.0f;
    214 		if ( incomingRateBytes < 0.0f ) {
    215 			incomingRateBytes = 0.0f;
    216 		}
    217 	}
    218 	incomingRateTime = time;
    219 	incomingRateBytes += size;
    220 
    221 	// compute an average bandwidth at intervals
    222 	if ( time - lastIncomingRateTime > BANDWIDTH_AVERAGE_PERIOD ) {
    223 		currentIncomingRate = 1000 * ( incomingBytes - lastIncomingBytes ) / ( time - lastIncomingRateTime );
    224 		lastIncomingBytes = incomingBytes;
    225 		lastIncomingRateTime = time;
    226 	}
    227 }
    228 
    229 /*
    230 ================================================
    231 idPacketProcessor::ProcessOutgoing
    232 NOTE - We only compress reliables because we assume everything else has already been compressed.
    233 ================================================
    234 */
    235 bool idPacketProcessor::ProcessOutgoing( const int time, const idBitMsg & msg, bool isOOB, int userData ) {
    236 	// We can only do ONE ProcessOutgoing call, then we need to do GetSendFragment to
    237 	// COMPLETELY empty unsentMsg before calling ProcessOutgoing again.
    238 	if ( !verify( fragmentedSend == false ) ) {
    239 		idLib::Warning( "ProcessOutgoing: fragmentedSend == true!");
    240 		return false;
    241 	}
    242 	
    243 	if ( !verify( unsentMsg.GetRemainingData() == 0 ) ) {
    244 		idLib::Warning( "ProcessOutgoing: unsentMsg.GetRemainingData() > 0!");
    245 		return false;
    246 	}
    247 	
    248 	// Build the full msg to send, which could include reliable data
    249 	unsentMsg.InitWrite( unsentBuffer, sizeof( unsentBuffer ) );
    250 	unsentMsg.BeginWriting();
    251 	
    252 	// Ack reliables if we need to (NOTE - We will send this ack on both the in-band and out-of-band channels)
    253 	if ( queuedReliableAck >= 0 ) {
    254 		idInnerPacketHeader header( PACKET_TYPE_RELIABLE_ACK, 0 );
    255 		header.WriteToMsg( unsentMsg );
    256 		unsentMsg.WriteLong( queuedReliableAck );
    257 		queuedReliableAck = -1;
    258 	}
    259 
    260 	if ( isOOB ) {
    261 		if ( msg.GetSize() + unsentMsg.GetSize() > MAX_OOB_MSG_SIZE ) {		// Fragmentation not allowed for out-of-band msg's
    262 			idLib::Printf("Out-of-band packet too large %i\n", unsentMsg.GetSize() );
    263 			assert( 0 );
    264 			return false;
    265 		}
    266 		// We don't need to worry about reliable for out of band packets
    267 		idInnerPacketHeader header( PACKET_TYPE_OOB, userData );
    268 		header.WriteToMsg( unsentMsg );
    269 	} else {
    270 		// Add reliable msg's here if this is an in-band packet
    271 		idInnerPacketHeader header( PACKET_TYPE_INBAND, reliable.Num() );
    272 		header.WriteToMsg( unsentMsg );
    273 		if ( reliable.Num() > 0 ) {
    274 			// Byte align unsentMsg 
    275 			unsentMsg.WriteByteAlign();
    276 			
    277 			lzwCompressionData_t	lzwData;
    278 			idLZWCompressor			lzwCompressor( &lzwData );
    279 
    280 			lzwCompressor.Start( unsentMsg.GetWriteData() + unsentMsg.GetSize() + 2, unsentMsg.GetRemainingSpace() - 2 );		// Write to compressed mem, not exceeding MAX_MSG_SIZE (+2 to reserve space for compressed size)
    281 
    282 			int uncompressedSize = 4;
    283 			lzwCompressor.WriteAgnostic< int >( reliable.ItemSequence( 0 ) );
    284 			for ( int i = 0; i < reliable.Num(); i++ ) {
    285 				lzwCompressor.WriteAgnostic< uint16 >( reliable.ItemLength( i ) );
    286 				lzwCompressor.Write( reliable.ItemData( i ), reliable.ItemLength( i ) );
    287 				uncompressedSize += 2 + reliable.ItemLength( i );
    288 			}
    289 
    290 			lzwCompressor.End();
    291 
    292 			if ( lzwCompressor.IsOverflowed() ) {
    293 				idLib::Error( "reliable msg compressor overflow." );
    294 			}
    295 
    296 			unsentMsg.WriteShort( lzwCompressor.Length() );
    297 			unsentMsg.SetSize( unsentMsg.GetSize() + lzwCompressor.Length() );
    298 
    299 			if ( net_showReliableCompression.GetBool() ) {
    300 				static int totalUncompressed = 0;
    301 				static int totalCompressed = 0;
    302 
    303 				totalUncompressed += uncompressedSize;
    304 				totalCompressed += lzwCompressor.Length();
    305 
    306 				float ratio1 = (float)lzwCompressor.Length() / (float)uncompressedSize;
    307 				float ratio2 = (float)totalCompressed / (float)totalUncompressed;
    308 
    309 				idLib::Printf( "Uncompressed: %i, Compressed: %i, TotalUncompressed: %i, TotalCompressed: %i, (%2.2f / %2.2f )\n", uncompressedSize, lzwCompressor.Length(), totalUncompressed, totalCompressed, ratio1, ratio2 );
    310 			}
    311 		}
    312 	}
    313 	
    314 	// Fill up with actual msg
    315 	unsentMsg.WriteData( msg.GetReadData(), msg.GetSize() );
    316 
    317 	if ( unsentMsg.GetSize() > MAX_PACKET_SIZE ) {
    318 		if ( isOOB ) {
    319 			idLib::Error( "oob msg's cannot fragment" );
    320 		}
    321 		fragmentedSend = true;
    322 	}
    323 	
    324 	return true;
    325 }
    326 
    327 /*
    328 ================================================
    329 idPacketProcessor::GetSendFragment
    330 ================================================
    331 */
    332 bool idPacketProcessor::GetSendFragment( const int time, sessionId_t sessionID, idBitMsg & outMsg ) {
    333 	lastSendTime = time;
    334 
    335 	if ( unsentMsg.GetRemainingData() <= 0 ) {
    336 		return false;	// Nothing to send
    337 	}
    338 	
    339 	outMsg.BeginWriting();
    340 
    341 
    342 	idOuterPacketHeader	outerHeader( sessionID );
    343 
    344 	// Write outer packet header to the msg
    345 	outerHeader.WriteToMsg( outMsg );
    346 	
    347 	if ( !fragmentedSend ) {
    348 		// Simple case, no fragments to sent
    349 		outMsg.WriteData( unsentMsg.GetReadData(), unsentMsg.GetSize() );
    350 		unsentMsg.SetSize( 0 );
    351 	} else {
    352 		int currentSize = idMath::ClampInt( 0, MAX_PACKET_SIZE, unsentMsg.GetRemainingData() );
    353 		assert( currentSize > 0 );
    354 		assert( unsentMsg.GetRemainingData() - currentSize >= 0 );
    355 		
    356 		// See if we'll have more fragments once we subtract off how much we're about to write
    357 		bool moreFragments = ( unsentMsg.GetRemainingData() - currentSize > 0 ) ? true : false;
    358 		
    359 		if ( !unsentMsg.GetReadCount() ) {		// If this is the first read, then we know it's the first fragment
    360 			assert( moreFragments );			// If we have a first, we must have more or something went wrong
    361 			idInnerPacketHeader header( PACKET_TYPE_FRAGMENTED, FRAGMENT_START );
    362 			header.WriteToMsg( outMsg );
    363 		} else {
    364 			idInnerPacketHeader header( PACKET_TYPE_FRAGMENTED, moreFragments ? FRAGMENT_MIDDLE : FRAGMENT_END );
    365 			header.WriteToMsg( outMsg );
    366 		}
    367 		
    368 		outMsg.WriteLong( fragmentSequence );
    369 		outMsg.WriteData( unsentMsg.GetReadData() + unsentMsg.GetReadCount(), currentSize );
    370 		unsentMsg.ReadData( NULL, currentSize );
    371 
    372 		assert( moreFragments == unsentMsg.GetRemainingData() > 0 );
    373 		fragmentedSend = moreFragments;
    374 		
    375 		fragmentSequence++;				// Advance sequence
    376 
    377 		fragmentAccumulator++;			// update the counter for the net debug hud
    378 	}
    379 
    380 
    381 	// The caller needs to send this packet, so assume he did, and update rates
    382 	UpdateOutgoingRate( time, outMsg.GetSize() );
    383 	
    384 	return true;
    385 }
    386 
    387 /*
    388 ================================================
    389 idPacketProcessor::ProcessIncoming
    390 ================================================
    391 */
    392 int idPacketProcessor::ProcessIncoming( int time, sessionId_t expectedSessionID, idBitMsg & msg, idBitMsg & out, int & userData, const int peerNum ) {
    393 	assert( msg.GetSize() <= MAX_FINAL_PACKET_SIZE );
    394 	
    395 	UpdateIncomingRate( time, msg.GetSize() );
    396 
    397 
    398 	idOuterPacketHeader outerHeader;
    399 	outerHeader.ReadFromMsg( msg );
    400 
    401 	sessionId_t sessionID = outerHeader.GetSessionID();
    402 	assert( sessionID == expectedSessionID );
    403 	
    404 	if ( !verify( sessionID != SESSION_ID_CONNECTIONLESS_PARTY && sessionID != SESSION_ID_CONNECTIONLESS_GAME && sessionID != SESSION_ID_CONNECTIONLESS_GAME_STATE ) ) {
    405 		idLib::Printf( "Expected non connectionless ID, but got a connectionless one\n" );
    406 		return RETURN_TYPE_NONE;
    407 	}
    408 	
    409 	if ( sessionID != expectedSessionID ) {
    410 		idLib::Printf( "Expected session id: %8x but got %8x instead\n", expectedSessionID, sessionID );
    411 		return RETURN_TYPE_NONE;
    412 	}
    413 
    414 	int c,b;
    415 	msg.SaveReadState( c, b );
    416 
    417 	idInnerPacketHeader header;
    418 	header.ReadFromMsg( msg );
    419 
    420 	if ( header.Type() != PACKET_TYPE_FRAGMENTED ) {
    421 		// Non fragmented
    422 		msg.RestoreReadState( c, b );		// Reset since we took a byte to check the type
    423 		return FinalizeRead( msg, out, userData );
    424 	} 
    425 
    426 	// Decode fragmented packet
    427 	int readSequence = msg.ReadLong();	// Read sequence of fragment
    428 
    429 	if ( header.Value() == FRAGMENT_START ) {
    430 		msgWritePos = 0;				// Reset msg reconstruction write pos
    431 	} else if ( fragmentSequence == -1 || readSequence != fragmentSequence + 1 ) {
    432 		droppedFrags++;
    433 		idLib::Printf( "Dropped Fragments - PeerNum: %i FragmentSeq: %i, ReadSeq: %i, Total: %i\n", peerNum, fragmentSequence, readSequence, droppedFrags );
    434 
    435 		// If this is the middle or end, make sure we are reading in fragmentSequence
    436 		fragmentSequence = -1;
    437 		return RETURN_TYPE_NONE;		// Out of sequence
    438 	}
    439 	fragmentSequence = readSequence;
    440 	assert( msg.GetRemainingData() > 0 );
    441 
    442 	if ( !verify( msgWritePos + msg.GetRemainingData() < sizeof( msgBuffer ) ) ) {
    443 		idLib::Error( "ProcessIncoming: Fragmented msg buffer overflow." );
    444 	}
    445 
    446 	memcpy( msgBuffer + msgWritePos, msg.GetReadData() + msg.GetReadCount(), msg.GetRemainingData() );
    447 	msgWritePos += msg.GetRemainingData();
    448 		
    449 	if ( header.Value() == FRAGMENT_END ) {
    450 		// Done reconstructing the msg
    451 		idBitMsg msg( msgBuffer, sizeof( msgBuffer ) );
    452 		msg.SetSize( msgWritePos );
    453 		return FinalizeRead( msg, out, userData );
    454 	}
    455 		
    456 	if ( !verify( header.Value() == FRAGMENT_START || header.Value() == FRAGMENT_MIDDLE ) ) {
    457 		idLib::Printf( "ProcessIncoming: Invalid packet.\n" );
    458 	}
    459 
    460 	// If we get here, this is part (either beginning or end) of a fragmented packet.
    461 	// We return RETURN_TYPE_NONE to let the caller know they don't need to do anything yet.
    462 	return RETURN_TYPE_NONE;
    463 }
    464 		
    465 /*
    466 ================================================
    467 idPacketProcessor::ProcessConnectionlessOutgoing
    468 ================================================
    469 */
    470 bool idPacketProcessor::ProcessConnectionlessOutgoing( idBitMsg & msg, idBitMsg & out, int lobbyType, int userData ) {
    471 	sessionId_t sessionID = lobbyType + 1;
    472 
    473 	
    474 	// Write outer header
    475 	idOuterPacketHeader outerHeader( sessionID );
    476 	outerHeader.WriteToMsg( out );
    477 
    478 	// Write inner header
    479 	idInnerPacketHeader header( PACKET_TYPE_OOB, userData );
    480 	header.WriteToMsg( out );
    481 
    482 	// Write msg
    483 	out.WriteData( msg.GetReadData(), msg.GetSize() );
    484 
    485 
    486 	return true;
    487 }
    488 
    489 /*
    490 ================================================
    491 idPacketProcessor::ProcessConnectionlessIncoming
    492 ================================================
    493 */
    494 bool idPacketProcessor::ProcessConnectionlessIncoming( idBitMsg & msg, idBitMsg & out, int & userData ) {
    495 
    496 	idOuterPacketHeader outerHeader;
    497 	outerHeader.ReadFromMsg( msg );
    498 
    499 	sessionId_t sessionID = outerHeader.GetSessionID();
    500 
    501 	if ( sessionID != SESSION_ID_CONNECTIONLESS_PARTY && sessionID != SESSION_ID_CONNECTIONLESS_GAME && sessionID != SESSION_ID_CONNECTIONLESS_GAME_STATE ) {
    502 		// Not a connectionless msg (this can happen if a previously connected peer keeps sending data for whatever reason)
    503 		idLib::Printf( "ProcessConnectionlessIncoming: Invalid session ID - %d\n", sessionID );
    504 		return false;
    505 	}
    506 
    507 	idInnerPacketHeader header;
    508 	header.ReadFromMsg( msg );
    509 
    510 	if ( header.Type() != PACKET_TYPE_OOB ) {
    511 		idLib::Printf( "ProcessConnectionlessIncoming: header.Type() != PACKET_TYPE_OOB\n" );
    512 		return false;		// Only out-of-band packets supported for connectionless
    513 	}
    514 	
    515 	userData = header.Value();
    516 	
    517 	out.BeginWriting();
    518 	out.WriteData( msg.GetReadData() + msg.GetReadCount(), msg.GetRemainingData() );
    519 	out.SetSize( msg.GetRemainingData() );
    520 
    521 	return true;
    522 }
    523 
    524 /*
    525 ================================================
    526 idPacketProcessor::GetSessionID
    527 ================================================
    528 */
    529 idPacketProcessor::sessionId_t idPacketProcessor::GetSessionID( idBitMsg & msg ) {
    530 	sessionId_t sessionID;
    531 	int c,b;
    532 	msg.SaveReadState( c, b );
    533 	// Read outer header
    534 	idOuterPacketHeader outerHeader;
    535 	outerHeader.ReadFromMsg( msg );
    536 	
    537 	// Get session ID
    538 	sessionID = outerHeader.GetSessionID();
    539 
    540 	msg.RestoreReadState( c, b );
    541 	return sessionID;
    542 }
    543 
    544 /*
    545 ================================================
    546 idPacketProcessor::VerifyEmptyReliableQueue
    547 ================================================
    548 */
    549 idCVar net_verifyReliableQueue( "net_verifyReliableQueue", "2", CVAR_INTEGER, "0: warn only, 1: error, 2: fixup, 3: fixup and verbose, 4: force test" );
    550 #define RELIABLE_VERBOSE if ( net_verifyReliableQueue.GetInteger() >= 3 ) idLib::Printf
    551 void idPacketProcessor::VerifyEmptyReliableQueue( byte keepMsgBelowThis, byte replaceWithThisMsg ) {
    552 	if ( net_verifyReliableQueue.GetInteger() == 4 ) {
    553 		RELIABLE_VERBOSE( "pushing a fake game reliable\n" );
    554 		const char * garbage = "garbage";
    555 		QueueReliableMessage( keepMsgBelowThis + 4, (const byte *)garbage, 8 );
    556 		QueueReliableMessage( replaceWithThisMsg, NULL, 0 );
    557 	}
    558 	if ( reliable.Num() == 0 ) {
    559 		return;
    560 	}
    561 	if ( net_verifyReliableQueue.GetInteger() == 1 ) {
    562 		idLib::Error( "reliable queue is not empty: %d messages", reliable.Num() );
    563 		return;
    564 	}
    565 	idLib::Warning( "reliable queue is not empty: %d messages", reliable.Num() );
    566 	if ( net_verifyReliableQueue.GetInteger() == 0 ) {
    567 		return;
    568 	}
    569 	// drop some stuff that is potentially dangerous and should not transmit
    570 	idDataQueue< MAX_RELIABLE_QUEUE, MAX_MSG_SIZE > clean;
    571 	RELIABLE_VERBOSE( "rollback send sequence from %d to %d\n", reliableSequenceSend, reliable.ItemSequence( 0 ) );
    572 	for ( int i = 0; i < reliable.Num(); i++ ) {
    573 		byte peek = reliable.ItemData( i )[0];
    574 		if ( peek < keepMsgBelowThis ) {
    575 			RELIABLE_VERBOSE( "keeping %d\n", peek );
    576 			clean.Append( reliable.ItemSequence( i ), reliable.ItemData( i ), reliable.ItemLength( i ) );
    577 		} else {
    578 			// Replace with fake msg, so we retain itemsequence ordering.
    579 			// If we don't do this, it's possible we remove the last msg, then append a single msg before the next send, 
    580 			// and the client may think he already received the msg, since his last reliableSequenceRecv could be greater than our
    581 			// reliableSequenceSend if he already received the group of reliables we are mucking with
    582 			clean.Append( reliable.ItemSequence( i ), &replaceWithThisMsg, 1 );
    583 			RELIABLE_VERBOSE( "dropping %d\n", peek );
    584 		}
    585 	}
    586 
    587 	assert( reliable.Num() == clean.Num() );
    588 
    589 	reliable = clean;
    590 }