/* ======================== idSnapshotProcessor::AddSnapObjTemplate ======================== */ void idSnapshotProcessor::AddSnapObjTemplate( int objID, idBitMsg& msg ) { extern idCVar net_ssTemplateDebug; idSnapShot::objectState_t* state = templateStates.S_AddObject( objID, MAX_UNSIGNED_TYPE( uint32 ), msg ); if( verify( state != NULL ) ) { if( net_ssTemplateDebug.GetBool() ) { idLib::PrintfIf( net_ssTemplateDebug.GetBool(), "InjectingSnapObjBaseState[%d] size: %d\n", objID, state->buffer.Size() ); state->Print( "BASE STATE" ); } state->expectedSequence = snapSequence; } }
/* ======================== SnapshotObjectJob This job processes objects by delta comparing them, and then zrle encoding them to the dest stream The dest stream is then eventually read by the lzw job, and then lzw compressed into the final delta packet ready to be sent to peers. ======================== */ void SnapshotObjectJob( objParms_t * parms ) { int visIndex = parms->visIndex; objJobState_t & newState = parms->newState; objJobState_t & oldState = parms->oldState; objHeader_t * header = parms->destHeader; uint8 * dataStart = parms->dest; assert( newState.valid || oldState.valid ); // Setup header header->flags = 0; header->size = newState.valid ? newState.size : 0; header->csize = 0; header->objID = -1; // Default to ack header->data = dataStart; assert( header->size <= MAX_UNSIGNED_TYPE( objectSize_t ) ); // Setup checksum and tag #ifdef SNAPSHOT_CHECKSUMS header->checksum = 0; #endif idZeroRunLengthCompressor rleCompressor; bool visChange = false; // visibility changes will be signified with a 0xffff state size bool visSendState = false; // the state is sent when an entity is no longer stale // Compute visibility changes // (we need to do this before writing out object id, because we may not need to write out the id if we early out) // (when we don't write out the id, we assume this is an "ack" when we deserialize the objects) if ( newState.valid && oldState.valid ) { // Check visibility assert( newState.objectNum == oldState.objectNum ); if ( visIndex > 0 ) { bool oldVisible = ( oldState.visMask & ( 1 << visIndex ) ) != 0; bool newVisible = ( newState.visMask & ( 1 << visIndex ) ) != 0; // Force visible if we need to either create or destroy this object newVisible |= ( newState.size == 0 ) != ( oldState.size == 0 ); if ( !oldVisible && !newVisible ) { // object is stale and ack'ed for this client, write nothing (see 'same object' below) header->flags |= OBJ_SAME; return; } else if ( oldVisible && !newVisible ) { //SNAP_VERBOSE_PRINT( "object %d to client %d goes stale\n", newState->objectNum, visIndex ); visChange = true; visSendState = false; } else if ( !oldVisible && newVisible ) { //SNAP_VERBOSE_PRINT( "object %d to client %d no longer stale\n", newState->objectNum, visIndex ); visChange = true; visSendState = true; } } // Same object, write a delta (never early out during vis changes) if ( !visChange && ObjectsSame( newState, oldState ) ) { // same state, write nothing header->flags |= OBJ_SAME; return; } } // Get the id of the object we are writing out int32 objectNum = ( newState.valid ) ? newState.objectNum : oldState.objectNum; // Write out object id header->objID = objectNum; if ( !newState.valid ) { // Deleted, write 0 size assert( oldState.valid ); header->flags |= OBJ_DELETED; } else if ( !oldState.valid ) { // New object, write out full state assert( newState.valid ); // delta against an empty snap rleCompressor.Start( dataStart, NULL, OBJ_DEST_SIZE_ALIGN16( newState.size ) ); rleCompressor.WriteBytes( newState.data, newState.size ); header->csize = rleCompressor.End(); header->flags |= OBJ_NEW; if ( header->csize == -1 ) { // Not enough space, don't compress, have lzw job do zrle compression instead memcpy( dataStart, newState.data, newState.size ); } } else { // Compare to same obj id in different snapshot assert( newState.objectNum == oldState.objectNum ); header->flags |= OBJ_DIFFERENT; if ( visChange ) { header->flags |= visSendState ? OBJ_VIS_NOT_STALE : OBJ_VIS_STALE; } if ( !visChange || visSendState ) { int compareSize = Min( newState.size, oldState.size ); rleCompressor.Start( dataStart, NULL, OBJ_DEST_SIZE_ALIGN16( newState.size ) ); for ( int b = 0; b < compareSize; b++ ) { byte delta = newState.data[b] - oldState.data[b]; rleCompressor.WriteByte( ( 0xFF + 1 + delta ) & 0xFF ); } // Get leftover int leftOver = newState.size - compareSize; if ( leftOver > 0 ) { rleCompressor.WriteBytes( newState.data + compareSize, leftOver ); } header->csize = rleCompressor.End(); if ( header->csize == -1 ) { // Not enough space, don't compress, have lzw job do zrle compression instead for ( int b = 0; b < compareSize; b++ ) { *dataStart++ = ( ( 0xFF + 1 + ( newState.data[b] - oldState.data[b] ) ) & 0xFF ); } // Get leftover int leftOver = newState.size - compareSize; if ( leftOver > 0 ) { memcpy( dataStart, newState.data + compareSize, leftOver ); } } } } assert( header->csize <= OBJ_DEST_SIZE_ALIGN16( header->size ) ); #ifdef SNAPSHOT_CHECKSUMS if ( newState.valid ) { assert( newState.size ); header->checksum = SnapObjChecksum( newState.data, newState.size ); } #endif }