SnapshotProcessor.cpp (20164B)
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 #pragma hdrstop 29 #include "../idlib/precompiled.h" 30 31 idCVar net_optimalSnapDeltaSize( "net_optimalSnapDeltaSize", "1000", CVAR_INTEGER, "Optimal size of snapshot delta msgs." ); 32 idCVar net_debugBaseStates( "net_debugBaseStates", "0", CVAR_BOOL, "Log out base state information" ); 33 idCVar net_skipClientDeltaAppend( "net_skipClientDeltaAppend", "0", CVAR_BOOL, "Simulate delta receive buffer overflowing" ); 34 35 /* 36 ======================== 37 idSnapshotProcessor::idSnapshotProcessor 38 ======================== 39 */ 40 idSnapshotProcessor::idSnapshotProcessor() { 41 42 //assert( mem.IsGlobalHeap() ); 43 44 jobMemory = (jobMemory_t*)Mem_Alloc( sizeof( jobMemory_t) , TAG_NETWORKING ); 45 46 assert_16_byte_aligned( jobMemory ); 47 assert_16_byte_aligned( jobMemory->objParms.Ptr() ); 48 assert_16_byte_aligned( jobMemory->headers.Ptr() ); 49 assert_16_byte_aligned( jobMemory->lzwParms.Ptr() ); 50 51 Reset( true ); 52 } 53 54 /* 55 ======================== 56 idSnapshotProcessor::idSnapshotProcessor 57 ======================== 58 */ 59 idSnapshotProcessor::~idSnapshotProcessor() { 60 //mem.PushHeap(); 61 Mem_Free( jobMemory ); 62 //mem.PopHeap(); 63 } 64 65 /* 66 ======================== 67 idSnapshotProcessor::Reset 68 ======================== 69 */ 70 void idSnapshotProcessor::Reset( bool cstor ) { 71 hasPendingSnap = false; 72 snapSequence = INITIAL_SNAP_SEQUENCE; 73 baseSequence = -1; 74 lastFullSnapBaseSequence = -1; 75 76 if ( !cstor && net_debugBaseStates.GetBool() ) { 77 idLib::Printf( "NET: Reset snapshot base"); 78 } 79 80 baseState.Clear(); 81 submittedState.Clear(); 82 pendingSnap.Clear(); 83 deltas.Clear(); 84 85 partialBaseSequence = -1; 86 87 memset( &jobMemory->lzwInOutData, 0, sizeof( jobMemory->lzwInOutData ) ); 88 } 89 90 /* 91 ======================== 92 idSnapshotProcessor::TrySetPendingSnapshot 93 ======================== 94 */ 95 bool idSnapshotProcessor::TrySetPendingSnapshot( idSnapShot & ss ) { 96 // Don't advance to the next snap until the last one was fully sent 97 if ( hasPendingSnap ) { 98 return false; 99 } 100 pendingSnap = ss; 101 hasPendingSnap = true; 102 return true; 103 } 104 105 /* 106 ======================== 107 idSnapshotProcessor::PeekDeltaSequence 108 ======================== 109 */ 110 void idSnapshotProcessor::PeekDeltaSequence( const char * deltaMem, int deltaSize, int & deltaSequence, int & deltaBaseSequence ) { 111 idSnapShot::PeekDeltaSequence( deltaMem, deltaSize, deltaSequence, deltaBaseSequence ); 112 } 113 114 /* 115 ======================== 116 idSnapshotProcessor::ApplyDeltaToSnapshot 117 ======================== 118 */ 119 bool idSnapshotProcessor::ApplyDeltaToSnapshot( idSnapShot & snap, const char * deltaMem, int deltaSize, int visIndex ) { 120 return snap.ReadDeltaForJob( deltaMem, deltaSize, visIndex, &templateStates ); 121 } 122 123 #ifdef STRESS_LZW_MEM 124 // When this defined, we'll stress the lzw compressor with the smallest possible buffer, and detect when we need to grow it to make 125 // sure we are gacefully detecting the situation. 126 static int g_maxlwMem = 100; 127 #endif 128 129 /* 130 ======================== 131 idSnapshotProcessor::SubmitPendingSnap 132 ======================== 133 */ 134 void idSnapshotProcessor::SubmitPendingSnap( int visIndex, uint8 * objMemory, int objMemorySize, lzwCompressionData_t * lzwData ) { 135 136 assert_16_byte_aligned( objMemory ); 137 assert_16_byte_aligned( lzwData ); 138 139 assert( hasPendingSnap ); 140 assert( jobMemory->lzwInOutData.numlzwDeltas == 0 ); 141 142 assert( net_optimalSnapDeltaSize.GetInteger() < jobMemory_t::MAX_LZW_MEM - 128 ); // Leave padding 143 144 jobMemory->lzwInOutData.lzwDeltas = jobMemory->lzwDeltas.Ptr(); 145 jobMemory->lzwInOutData.maxlzwDeltas = jobMemory->lzwDeltas.Num(); 146 jobMemory->lzwInOutData.lzwMem = jobMemory->lzwMem.Ptr(); 147 148 #ifdef STRESS_LZW_MEM 149 jobMemory->lzwInOutData.maxlzwMem = g_maxlwMem; 150 #else 151 jobMemory->lzwInOutData.maxlzwMem = jobMemory_t::MAX_LZW_MEM; 152 #endif 153 154 jobMemory->lzwInOutData.lzwDmaOut = jobMemory_t::MAX_LZW_MEM; 155 jobMemory->lzwInOutData.numlzwDeltas = 0; 156 jobMemory->lzwInOutData.lzwBytes = 0; 157 jobMemory->lzwInOutData.optimalLength = net_optimalSnapDeltaSize.GetInteger(); 158 jobMemory->lzwInOutData.snapSequence = snapSequence; 159 jobMemory->lzwInOutData.lastObjId = 0; 160 jobMemory->lzwInOutData.lzwData = lzwData; 161 162 idSnapShot::submitDeltaJobsInfo_t submitInfo; 163 164 submitInfo.objParms = jobMemory->objParms.Ptr(); 165 submitInfo.maxObjParms = jobMemory->objParms.Num(); 166 submitInfo.headers = jobMemory->headers.Ptr(); 167 submitInfo.maxHeaders = jobMemory->headers.Num(); 168 submitInfo.objMemory = objMemory; 169 submitInfo.maxObjMemory = objMemorySize; 170 submitInfo.lzwParms = jobMemory->lzwParms.Ptr(); 171 submitInfo.maxDeltaParms = jobMemory->lzwParms.Num(); 172 173 174 // Use a copy of base state to avoid race conditions. 175 // The main thread could change it behind the jobs backs. 176 submittedState = baseState; 177 submittedTemplateStates = templateStates; 178 179 submitInfo.templateStates = &submittedTemplateStates; 180 181 submitInfo.oldSnap = &submittedState; 182 submitInfo.visIndex = visIndex; 183 submitInfo.baseSequence = baseSequence; 184 185 submitInfo.lzwInOutData = &jobMemory->lzwInOutData; 186 187 pendingSnap.SubmitWriteDeltaToJobs( submitInfo ); 188 } 189 190 /* 191 ======================== 192 idSnapshotProcessor::GetPendingSnapDelta 193 ======================== 194 */ 195 int idSnapshotProcessor::GetPendingSnapDelta( byte * outBuffer, int maxLength ) { 196 197 assert( PendingSnapReadyToSend() ); 198 199 if ( !verify( jobMemory->lzwInOutData.numlzwDeltas == 1 ) ) { 200 jobMemory->lzwInOutData.numlzwDeltas = 0; 201 return 0; // No more deltas left to send 202 } 203 204 assert( hasPendingSnap ); 205 206 jobMemory->lzwInOutData.numlzwDeltas = 0; 207 208 int size = jobMemory->lzwDeltas[0].size; 209 210 if ( !verify( size != -1 ) ) { 211 #ifdef STRESS_LZW_MEM 212 if ( g_maxlwMem < MAX_LZW_MEM ) { 213 g_maxlwMem += 50; 214 g_maxlwMem = Min( g_maxlwMem, MAX_LZW_MEM ); 215 return 0; 216 } 217 #endif 218 219 // This can happen if there wasn't enough maxlzwMem to process one full obj in a single delta 220 idLib::Error( "GetPendingSnapDelta: Delta failed." ); 221 } 222 223 uint8 * deltaData = &jobMemory->lzwMem[jobMemory->lzwDeltas[0].offset]; 224 225 int deltaSequence = 0; 226 int deltaBaseSequence = 0; 227 PeekDeltaSequence( (const char *)deltaData, size, deltaSequence, deltaBaseSequence ); 228 // sanity check: does the compressed data we are about to send have the sequence number we expect 229 assert( deltaSequence == jobMemory->lzwDeltas[0].snapSequence ); 230 231 if ( !verify( size <= maxLength ) ) { 232 idLib::Error( "GetPendingSnapDelta: Size overflow." ); 233 } 234 235 // Copy to out buffer 236 memcpy( outBuffer, deltaData, size ); 237 238 // Set the sequence to what this delta actually belongs to 239 assert( jobMemory->lzwDeltas[0].snapSequence == snapSequence + 1 ); 240 snapSequence = jobMemory->lzwDeltas[0].snapSequence; 241 242 //idLib::Printf( "deltas Num: %i, Size: %i\n", deltas.Num(), deltas.GetDataLength() ); 243 244 // Copy to delta buffer 245 // NOTE - We don't need to save this delta off if peer has already ack'd this basestate. 246 // This can happen due to the fact that we defer the processing of snap deltas on jobs. 247 // When we start processing a delta, we use the currently ack'd basestate. If while we were processing 248 // the delta, the client acks a new basestate, we can get into this situation. In this case, we simply don't 249 // store the delta, since it will just take up space, and just get removed anyways during ApplySnapshotDelta. 250 // (and cause lots of spam when it sees the delta's basestate doesn't match the current ack'd one) 251 if ( deltaBaseSequence >= baseSequence ) { 252 if ( !deltas.Append( snapSequence, deltaData, size ) ) { 253 int resendLength = deltas.ItemLength( deltas.Num() - 1 ); 254 255 if ( !verify( resendLength <= maxLength ) ) { 256 idLib::Error( "GetPendingSnapDelta: Size overflow for resend." ); 257 } 258 259 memcpy( outBuffer, deltas.ItemData( deltas.Num() - 1 ), resendLength ); 260 size = -resendLength; 261 } 262 } 263 264 if ( jobMemory->lzwInOutData.fullSnap ) { 265 // We sent the full snap, we can stop sending this pending snap now... 266 NET_VERBOSESNAPSHOT_PRINT_LEVEL( 5, va( " wrote enough deltas to a full snapshot\n" ) ); // FIXME: peer number? 267 268 hasPendingSnap = false; 269 partialBaseSequence = -1; 270 271 } else { 272 partialBaseSequence = deltaBaseSequence; 273 } 274 275 return size; 276 } 277 278 /* 279 ======================== 280 idSnapshotProcessor::IsBusyConfirmingPartialSnap 281 ======================== 282 */ 283 bool idSnapshotProcessor::IsBusyConfirmingPartialSnap() { 284 if ( partialBaseSequence != -1 && baseSequence <= partialBaseSequence ) { 285 return true; 286 } 287 288 return false; 289 } 290 291 /* 292 ======================== 293 idSnapshotProcessor::ReceiveSnapshotDelta 294 NOTE: we use ReadDeltaForJob twice, once to build the same base as the server (based on server acks, down ApplySnapshotDelta), and another time to apply the snapshot we just received 295 could we avoid the double apply by keeping outSnap cached in memory and avoid rebuilding it from a delta when the next one comes around? 296 ======================== 297 */ 298 bool idSnapshotProcessor::ReceiveSnapshotDelta( const byte * deltaData, int deltaLength, int visIndex, int & outSeq, int & outBaseSeq, idSnapShot & outSnap, bool & fullSnap ) { 299 300 fullSnap = false; 301 302 int deltaSequence = 0; 303 int deltaBaseSequence = 0; 304 305 // Get the sequence of this delta, and the base sequence it is delta'd from 306 PeekDeltaSequence( (const char *)deltaData, deltaLength, deltaSequence, deltaBaseSequence ); 307 308 //idLib::Printf("Incoming snapshot: %i, %i\n", deltaSequence, deltaBaseSequence ); 309 310 if ( deltaSequence <= snapSequence ) { 311 NET_VERBOSESNAPSHOT_PRINT( "Rejecting old delta: %d (snapSequence: %d \n", deltaSequence, snapSequence ); 312 return false; // Completely reject older out of order deltas 313 } 314 315 // Bring the base state up to date with the basestate this delta was compared to 316 ApplySnapshotDelta( visIndex, deltaBaseSequence ); 317 318 // Once we get here, our base state should be caught up to that of the server 319 assert( baseSequence == deltaBaseSequence ); 320 321 // Save the new delta 322 if ( net_skipClientDeltaAppend.GetBool() || !deltas.Append( deltaSequence, deltaData, deltaLength ) ) { 323 // This can happen if the delta queues get desync'd between the server and client. 324 // With recent fixes, this should be extremely rare, or impossible. 325 // Just in case this happens, we can recover by assuming we didn't even receive this delta. 326 idLib::Printf( "NET: ReceiveSnapshotDelta: No room to append delta %d/%d \n", deltaSequence, deltaBaseSequence ); 327 return false; 328 } 329 330 // Update our snapshot sequence number to the newer one we just got (now that it's safe) 331 snapSequence = deltaSequence; 332 333 if ( deltas.Num() > 10 ) { 334 NET_VERBOSESNAPSHOT_PRINT("NET: ReceiveSnapshotDelta: deltas.Num() > 10: %d\n ", deltas.Num() ); 335 for ( int i=0; i < deltas.Num(); i++ ) { 336 NET_VERBOSESNAPSHOT_PRINT( "%d ", deltas.ItemSequence( i ) ); 337 } 338 NET_VERBOSESNAPSHOT_PRINT( "\n" ); 339 } 340 341 342 if ( baseSequence != deltaBaseSequence ) { 343 // NOTE - With recent fixes, this should no longer be possible unless the delta is trashed 344 // We should probably disconnect from the server when this happens now. 345 static bool failed = false; 346 if ( !failed ) { 347 idLib::Printf( "NET: incorrect base state? not sure how this can happen... baseSequence: %d deltaBaseSequence: %d \n", baseSequence, deltaBaseSequence ); 348 } 349 failed = true; 350 return false; 351 } 352 353 // Copy out the current deltas sequence values to caller 354 outSeq = deltaSequence; 355 outBaseSeq = deltaBaseSequence; 356 357 if ( baseSequence < 50 && net_debugBaseStates.GetBool() ) { 358 idLib::Printf( "NET: Proper basestate... baseSequence: %d deltaBaseSequence: %d \n", baseSequence, deltaBaseSequence ); 359 } 360 361 // Make a copy of the basestate the server used to create this delta, and then apply and return it 362 outSnap = baseState; 363 364 fullSnap = ApplyDeltaToSnapshot( outSnap, (const char *)deltaData, deltaLength, visIndex ); 365 366 // We received a new delta 367 return true; 368 } 369 370 /* 371 ======================== 372 idSnapshotProcessor::ApplySnapshotDelta 373 Apply a snapshot delta to our current basestate, and make that the new base. 374 We can remove all deltas that refer to the basetate we just removed. 375 ======================== 376 */ 377 bool idSnapshotProcessor::ApplySnapshotDelta( int visIndex, int snapshotNumber ) { 378 379 NET_VERBOSESNAPSHOT_PRINT_LEVEL( 6, va( "idSnapshotProcessor::ApplySnapshotDelta snapshotNumber: %d\n", snapshotNumber ) ); 380 381 // Sanity check deltas 382 SanityCheckDeltas(); 383 384 // dump any deltas older than the acknoweledged snapshot, which should only happen if there is packet loss 385 deltas.RemoveOlderThan( snapshotNumber ); 386 387 if ( deltas.Num() == 0 || deltas.ItemSequence( 0 ) != snapshotNumber ) { 388 // this means the snapshot was either already acknowledged or came out of order 389 // On the server, this can happen because the client is continuously/redundantly sending acks 390 // Once the server has ack'd a certain base sequence, it will need to ignore all the redundant ones. 391 // On the client, this will only happen due to out of order, or dropped packets. 392 393 if ( !common->IsServer() ) { 394 // these should be printed every time on the clients 395 // printing on server is not useful / results in tons of spam 396 if ( deltas.Num() == 0 ) { 397 NET_VERBOSESNAPSHOT_PRINT("NET: Got snapshot but ignored... deltas.Num(): %d snapshotNumber: %d \n", deltas.Num(), snapshotNumber ); 398 } else { 399 NET_VERBOSESNAPSHOT_PRINT("NET: Got snapshot but ignored... deltas.ItemSequence( 0 ): %d != snapshotNumber: %d \n ", deltas.ItemSequence( 0 ), snapshotNumber ); 400 401 for ( int i=0; i < deltas.Num(); i++ ) { 402 NET_VERBOSESNAPSHOT_PRINT("%d ", deltas.ItemSequence(i) ); 403 } 404 NET_VERBOSESNAPSHOT_PRINT("\n"); 405 406 } 407 } 408 return false; 409 } 410 411 int deltaSequence = 0; 412 int deltaBaseSequence = 0; 413 414 PeekDeltaSequence( (const char *)deltas.ItemData( 0 ), deltas.ItemLength( 0 ), deltaSequence, deltaBaseSequence ); 415 416 assert( deltaSequence == snapshotNumber ); // Make sure compressed sequence number matches that in data queue 417 assert( baseSequence == deltaBaseSequence ); // If this delta isn't based off of our currently ack'd basestate, something is trashed... 418 assert( deltaSequence > baseSequence ); 419 420 if ( baseSequence != deltaBaseSequence ) { 421 // NOTE - This should no longer happen with recent fixes. 422 // We should probably disconnect from the server if this happens. (packets are trashed most likely) 423 NET_VERBOSESNAPSHOT_PRINT( "NET: Got snapshot %d but baseSequence does not match. baseSequence: %d deltaBaseSequence: %d. \n", snapshotNumber, baseSequence, deltaBaseSequence ); 424 return false; 425 } 426 427 // Apply this delta to our base state 428 if ( ApplyDeltaToSnapshot( baseState, (const char *)deltas.ItemData( 0 ), deltas.ItemLength( 0 ), visIndex ) ) { 429 lastFullSnapBaseSequence = deltaSequence; 430 } 431 432 baseSequence = deltaSequence; // This is now our new base sequence 433 434 // Remove deltas that we no longer need 435 RemoveDeltasForOldBaseSequence(); 436 437 // Sanity check deltas 438 SanityCheckDeltas(); 439 440 return true; 441 } 442 443 /* 444 ======================== 445 idSnapshotProcessor::RemoveDeltasForOldBaseSequence 446 Remove deltas for basestate we no longer have. We know we can remove them, because we will never 447 be able to apply them, since the basestate needed to generate a full snap from these deltas is gone. 448 449 Ways we can get deltas based on basestate we no longer have: 450 1. Server sends sequence 50 based on 49. It then sends sequence 51 based on 49. 451 Client acks 50, server applies it to 49, 50 is new base state. 452 Server now has a delta sequence 51 based on 49 that it won't ever be able to apply (50 is new basestate). 453 454 This is annoying, because it makes a lot of our sanity checks incorrectly fire off for benign issues. 455 Here is a series of events that make the old ( baseSequence != deltaBaseSequence ) assert: 456 457 Server: 458 49->50, 49->51, ack 50, 50->52, ack 51 (bam), 50->53 459 460 Client 461 49->50, ack 50, 49->51, ack 51 (bam), 50->52, 50->53 462 463 The client above will ack 51, even though he can't even apply that delta. To get around this, we simply don't 464 allow delta to exist in the list, unless their basestate is the current basestate we maintain. 465 This allows us to put sanity checks in place that don't fire off during benign conditions, and allow us 466 to truly check for trashed conditions. 467 468 ======================== 469 */ 470 void idSnapshotProcessor::RemoveDeltasForOldBaseSequence() { 471 // Remove any deltas that would apply to the old base we no longer maintain 472 // (we will never be able to apply these, since we don't have that base anymore) 473 for ( int i = deltas.Num() - 1; i >= 0; i-- ) { 474 int deltaSequence = 0; 475 int deltaBaseSequence = 0; 476 baseState.PeekDeltaSequence( (const char *)deltas.ItemData( i ), deltas.ItemLength( i ), deltaSequence, deltaBaseSequence ); 477 if ( deltaBaseSequence < baseSequence ) { 478 // Remove this delta, and all deltas before this one 479 deltas.RemoveOlderThan( deltas.ItemSequence( i ) + 1 ); 480 break; 481 } 482 } 483 } 484 485 /* 486 ======================== 487 idSnapshotProcessor::SanityCheckDeltas 488 Make sure delta sequence and basesequence values are valid, and in order, etc 489 ======================== 490 */ 491 void idSnapshotProcessor::SanityCheckDeltas() { 492 int deltaSequence = 0; 493 int deltaBaseSequence = 0; 494 int lastDeltaSequence = -1; 495 int lastDeltaBaseSequence = -1; 496 497 for ( int i = 0; i < deltas.Num(); i++ ) { 498 baseState.PeekDeltaSequence( (const char *)deltas.ItemData( i ), deltas.ItemLength( i ), deltaSequence, deltaBaseSequence ); 499 assert( deltaSequence == deltas.ItemSequence( i ) ); // Make sure delta stored in compressed form matches the one stored in the data queue 500 assert( deltaSequence > lastDeltaSequence ); // Make sure they are in order (we reject out of order sequences in ApplysnapshotDelta) 501 assert( deltaBaseSequence >= lastDeltaBaseSequence ); // Make sure they are in order (they can be the same, since base sequences don't change until they've been ack'd) 502 assert( deltaBaseSequence >= baseSequence ); // We should have removed old delta's that can no longer be applied 503 assert( deltaBaseSequence == baseSequence || deltaBaseSequence == lastDeltaSequence ); // Make sure we still have a base (or eventually will have) that we can apply this delta to 504 lastDeltaSequence = deltaSequence; 505 lastDeltaBaseSequence = deltaBaseSequence; 506 } 507 } 508 509 /* 510 ======================== 511 idSnapshotProcessor::AddSnapObjTemplate 512 ======================== 513 */ 514 void idSnapshotProcessor::AddSnapObjTemplate( int objID, idBitMsg & msg ) { 515 extern idCVar net_ssTemplateDebug; 516 idSnapShot::objectState_t * state = templateStates.S_AddObject( objID, MAX_UNSIGNED_TYPE( uint32 ), msg ); 517 if ( verify( state != NULL ) ) { 518 if ( net_ssTemplateDebug.GetBool() ) { 519 idLib::PrintfIf( net_ssTemplateDebug.GetBool(), "InjectingSnapObjBaseState[%d] size: %d\n", objID, state->buffer.Size() ); 520 state->Print( "BASE STATE" ); 521 } 522 state->expectedSequence = snapSequence; 523 } 524 }