PacketProcessor.cpp 21 KB

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