/*
========================
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;
	}
}
Example #2
0
/*
========================
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
}