Esempio n. 1
0
/*
========================
idProfileMgr::OnLoadSettingsCompleted
========================
*/
void idProfileMgr::OnLoadSettingsCompleted( idSaveLoadParms* parms )
{





	// Don't process if error already detected
	if( parms->errorCode != SAVEGAME_E_NONE )
	{
		return;
	}
	
	// Serialize the loaded profile
	idFile_SaveGame** profileFileContainer = FindFromGenericPtr( parms->files, SAVEGAME_PROFILE_FILENAME );
	idFile_SaveGame* profileFile = profileFileContainer == NULL ? NULL : *profileFileContainer;
	
	bool foundProfile = profileFile != NULL && profileFile->Length() > 0;
	
	if( foundProfile )
	{
		idTempArray< byte > buffer( MAX_PROFILE_SIZE );
		
		// Serialize settings from this buffer
		profileFile->MakeReadOnly();
		unsigned int originalChecksum;
		profileFile->ReadBig( originalChecksum );
		
		int dataLength = profileFile->Length() - ( int )sizeof( originalChecksum );
		profileFile->ReadBigArray( buffer.Ptr(), dataLength );
		
		// Validate the checksum before we let the game serialize the settings
		unsigned int checksum = MD5_BlockChecksum( buffer.Ptr(), dataLength );
		if( originalChecksum != checksum )
		{
			idLib::Warning( "Checksum: 0x%08x, originalChecksum: 0x%08x, size = %d", checksum, originalChecksum, dataLength );
			parms->errorCode = SAVEGAME_E_CORRUPTED;
		}
		else
		{
			idBitMsg msg;
			msg.InitRead( buffer.Ptr(), ( int )buffer.Size() );
			idSerializer ser( msg, false );
			if( !profile->Serialize( ser ) )
			{
				parms->errorCode = SAVEGAME_E_CORRUPTED;
			}
		}
		
	}
	else
	{
		parms->errorCode = SAVEGAME_E_FILE_NOT_FOUND;
	}
}
Esempio n. 2
0
/*
============================
idFile_SaveGamePipelined::CompressBlock

Called when an uncompressed block fills up, and also to flush the final partial block.
Flushes everything from [uncompressedConsumedBytes -> uncompressedProducedBytes)

Modifies:
	dataZlib
	bytesZlib
	compressed
	compressedProducedBytes
	zStream
	zStreamEndHit
============================
*/
void idFile_SaveGamePipelined::CompressBlock()
{
	zStream.next_in = ( Bytef* )dataZlib;
	zStream.avail_in = ( uInt ) bytesZlib;
	
	dataZlib = NULL;
	bytesZlib = 0;
	
	// if this is the finish block, we may need to write
	// multiple buffers even after all input has been consumed
	while( zStream.avail_in > 0 || zLibFlushType == Z_FINISH )
	{
	
		const int zstat = deflate( &zStream, zLibFlushType );
		
		if( zstat != Z_OK && zstat != Z_STREAM_END )
		{
			idLib::FatalError( "idFile_SaveGamePipelined::CompressBlock: deflate() returned %i", zstat );
		}
		
		if( zStream.avail_out == 0 || zLibFlushType == Z_FINISH )
		{
		
			if( sgf_checksums.GetBool() )
			{
				size_t blockSize = zStream.total_out + numChecksums * sizeof( uint32 ) - compressedProducedBytes;
				uint32 checksum = MD5_BlockChecksum( zStream.next_out - blockSize, blockSize );
				zStream.next_out[0] = ( ( checksum >>  0 ) & 0xFF );
				zStream.next_out[1] = ( ( checksum >>  8 ) & 0xFF );
				zStream.next_out[2] = ( ( checksum >> 16 ) & 0xFF );
				zStream.next_out[3] = ( ( checksum >> 24 ) & 0xFF );
				numChecksums++;
			}
			
			// flush the output buffer IO
			compressedProducedBytes = zStream.total_out + numChecksums * sizeof( uint32 );
			FlushCompressedBlock();
			if( zstat == Z_STREAM_END )
			{
				assert( zLibFlushType == Z_FINISH );
				zStreamEndHit = true;
				return;
			}
			
			assert( 0 == ( compressedProducedBytes & ( COMPRESSED_BLOCK_SIZE - 1 ) ) );
			
			zStream.avail_out = COMPRESSED_BLOCK_SIZE;
			zStream.next_out = ( Bytef* )&compressed[ compressedProducedBytes & ( COMPRESSED_BUFFER_SIZE - 1 ) ];
			
			if( sgf_checksums.GetBool() )
			{
				zStream.avail_out -= sizeof( uint32 );
			}
		}
	}
Esempio n. 3
0
void rvInstance::BuildInstanceMessage( void ) {
	// Build the client join instance msg
	mapEntityMsg.BeginWriting();
	mapEntityMsg.Init( mapEntityMsgBuf, sizeof( byte ) * MAX_GAME_MESSAGE_SIZE );
	mapEntityMsg.WriteByte( GAME_RELIABLE_MESSAGE_SET_INSTANCE );

	mapEntityMsg.WriteByte( instanceID );
	mapEntityMsg.WriteShort( initialSpawnCount );
	// we need to send that down for tourney so the index will match
	mapEntityMsg.WriteLong( gameLocal.GetStartingIndexForInstance( instanceID ) );

	LittleRevBytes( mapEntityNumbers, sizeof(unsigned short ), numMapEntities ); //DAJ
	int checksum = MD5_BlockChecksum( mapEntityNumbers, sizeof( unsigned short ) * numMapEntities );
	//common->Printf( "pop: server checksum: 0x%x\n", checksum );
	mapEntityMsg.WriteLong( checksum );
}
Esempio n. 4
0
/*
========================
idSaveGameProcessorSaveProfile::InitSaveProfile
========================
*/
bool idSaveGameProcessorSaveProfile::InitSaveProfile( idPlayerProfile* profile_, const char* folder )
{
	// Serialize the profile and pass a file to the processor
	profileFile = new( TAG_SAVEGAMES ) idFile_SaveGame( SAVEGAME_PROFILE_FILENAME, SAVEGAMEFILE_BINARY | SAVEGAMEFILE_AUTO_DELETE );
	profileFile->MakeWritable();
	profileFile->SetMaxLength( MAX_PROFILE_SIZE );
	
	// Create a serialization object and let the game serialize the settings into the buffer
	const int serializeSize = MAX_PROFILE_SIZE - 8;	// -8 for checksum (all platforms) and length (on 360)
	idTempArray< byte > buffer( serializeSize );
	idBitMsg msg;
	msg.InitWrite( buffer.Ptr(), serializeSize );
	idSerializer ser( msg, true );
	profile_->Serialize( ser );
	
	// Get and write the checksum & length first
	unsigned int checksum = MD5_BlockChecksum( msg.GetReadData(), msg.GetSize() );
	profileFile->WriteBig( checksum );
	
	idLib::PrintfIf( profile_verbose.GetBool(), "checksum: 0x%08x, length: %d\n", checksum, msg.GetSize() );
	
	// Add data to the file and prepare for save
	profileFile->Write( msg.GetReadData(), msg.GetSize() );
	profileFile->MakeReadOnly();
	
	saveFileEntryList_t files;
	files.Append( profileFile );
	
	idSaveGameDetails description;
	if( !idSaveGameProcessorSaveFiles::InitSave( folder, files, description, idSaveGameManager::PACKAGE_PROFILE ) )
	{
		return false;
	}
	
	
	profile = profile_;
	
	
	return true;
}
Esempio n. 5
0
/*
========================
idSaveGameThread::SaveGame
========================
*/
int idSaveGameThread::Save() {
	idLocalUserWin * user = GetLocalUserFromSaveParms( data );
	if ( user == NULL ) {
		data.saveLoadParms->errorCode = SAVEGAME_E_INVALID_USER;
		return -1;
	}

	idSaveLoadParms * callback = data.saveLoadParms;
	idStr saveFolder = "savegame";

	saveFolder.AppendPath( callback->directory );

	// Check for the required storage space.
	int64 requiredSizeBytes = 0;
	{
		for ( int i = 0; i < callback->files.Num(); i++ ) {
			idFile_SaveGame *	file = callback->files[i];
			requiredSizeBytes += ( file->Length() + sizeof( unsigned int ) ); // uint for checksum
			if ( file->type == SAVEGAMEFILE_PIPELINED ) {
				requiredSizeBytes += MIN_SAVEGAME_SIZE_BYTES;
			}
		}
	}

	int ret = ERROR_SUCCESS;

	// Check size of previous files if needed
	// ALL THE FILES RIGHT NOW----  could use pattern later...
	idStrList filesToDelete;
	if ( ( callback->mode & SAVEGAME_MBF_DELETE_FILES ) && !callback->cancelled ) {
		if ( fileSystem->IsFolder( saveFolder.c_str(), "fs_savePath" ) == FOLDER_YES ) {
			idFileList * files = fileSystem->ListFilesTree( saveFolder.c_str(), "*.*" );
			for ( int i = 0; i < files->GetNumFiles(); i++ ) {
				requiredSizeBytes -= fileSystem->GetFileLength( files->GetFile( i ) );
				filesToDelete.Append( files->GetFile( i ) );
			}
			fileSystem->FreeFileList( files );
		}
	}

	// Inform user about size required if necessary
	if ( requiredSizeBytes > 0 && !callback->cancelled ) {
		user->StorageSizeAvailable( requiredSizeBytes, callback->requiredSpaceInBytes );
		if ( callback->requiredSpaceInBytes > 0 ) {
			// check to make sure savepath actually exists before erroring
			idStr directory = fs_savepath.GetString();
			directory += "\\";	// so it doesn't think the last part is a file and ignores in the directory creation
			fileSystem->CreateOSPath( directory );  // we can't actually check FileExists in production builds, so just try to create it
			user->StorageSizeAvailable( requiredSizeBytes, callback->requiredSpaceInBytes );

			if ( callback->requiredSpaceInBytes > 0 ) {
				callback->errorCode = SAVEGAME_E_INSUFFICIENT_ROOM;
				// safe to return, haven't written any files yet
				return -1;
			}
		}
	}

	// Delete all previous files if needed
	// ALL THE FILES RIGHT NOW----  could use pattern later...
	for ( int i = 0; i < filesToDelete.Num() && !callback->cancelled; i++ ) {
		fileSystem->RemoveFile( filesToDelete[i].c_str() );
	}

	// Save the raw files.
	for ( int i = 0; i < callback->files.Num() && ret == ERROR_SUCCESS && !callback->cancelled; i++ ) {
		idFile_SaveGame * file = callback->files[i];

		idStr fileName = saveFolder;
		fileName.AppendPath( file->GetName() );
		idStr tempFileName = va( "%s.temp", fileName.c_str() );

		idFile * outputFile = fileSystem->OpenFileWrite( tempFileName, "fs_savePath" );
		if ( outputFile == NULL ) {
			idLib::Warning( "[%s]: Couldn't open file for writing, %s. Error = %08x", __FUNCTION__, tempFileName.c_str(), GetLastError() );
			file->error = true;
			callback->errorCode = SAVEGAME_E_UNKNOWN;
			ret = -1;
			continue;
		}

		if ( ( file->type & SAVEGAMEFILE_PIPELINED ) != 0 ) {

			idFile_SaveGamePipelined * inputFile = dynamic_cast< idFile_SaveGamePipelined * >( file );
			assert( inputFile != NULL );

			blockForIO_t block;
			while ( inputFile->NextWriteBlock( & block ) ) {
				if ( (size_t)outputFile->Write( block.data, block.bytes ) != block.bytes ) {
					idLib::Warning( "[%s]: Write failed. Error = %08x", __FUNCTION__, GetLastError() );
					file->error = true;
					callback->errorCode = SAVEGAME_E_INSUFFICIENT_ROOM;
					ret = -1;
					break;
				}
			}

		} else {

			if ( ( file->type & SAVEGAMEFILE_BINARY ) || ( file->type & SAVEGAMEFILE_COMPRESSED ) ) {
				if ( saveGame_checksum.GetBool() ) {
					unsigned int checksum = MD5_BlockChecksum( file->GetDataPtr(), file->Length() );
					size_t size = outputFile->WriteBig( checksum );
					if ( size != sizeof( checksum ) ) {
						idLib::Warning( "[%s]: Write failed. Error = %08x", __FUNCTION__, GetLastError() );
						file->error = true;
						callback->errorCode = SAVEGAME_E_INSUFFICIENT_ROOM;
						ret = -1;
					}
				}
			}

			size_t size = outputFile->Write( file->GetDataPtr(), file->Length() );
			if ( size != (size_t)file->Length() ) {
				idLib::Warning( "[%s]: Write failed. Error = %08x", __FUNCTION__, GetLastError() );
				file->error = true;
				callback->errorCode = SAVEGAME_E_INSUFFICIENT_ROOM;
				ret = -1;
			} else {
				idLib::PrintfIf( saveGame_verbose.GetBool(), "Saved %s (%s)\n", fileName.c_str(), outputFile->GetFullPath() );
			}
		}

		delete outputFile;

		if ( ret == ERROR_SUCCESS ) {
			// Remove the old file
			if ( !fileSystem->RenameFile( tempFileName, fileName, "fs_savePath" ) ) {
				idLib::Warning( "Could not start to rename temporary file %s to %s.", tempFileName.c_str(), fileName.c_str() );
			}
		} else {
			fileSystem->RemoveFile( tempFileName );
			idLib::Warning( "Invalid write to temporary file %s.", tempFileName.c_str() );
		}
	}

	if ( data.saveLoadParms->cancelled ) {
		data.saveLoadParms->errorCode = SAVEGAME_E_CANCELLED;
	}

	// Removed because it seemed a bit drastic
#if 0
	// If there is an error, delete the partially saved folder
	if ( callback->errorCode != SAVEGAME_E_NONE ) {
		if ( fileSystem->IsFolder( saveFolder, "fs_savePath" ) == FOLDER_YES ) {
			idFileList * files = fileSystem->ListFilesTree( saveFolder, "/|*" );
			for ( int i = 0; i < files->GetNumFiles(); i++ ) {
				fileSystem->RemoveFile( files->GetFile( i ) );
			}
			fileSystem->FreeFileList( files );
			fileSystem->RemoveDir( saveFolder );
		}
	}
#endif

	return ret;
}
Esempio n. 6
0
/*
========================
idSessionLocal::LoadGame
========================
*/
int idSaveGameThread::Load() {
	idSaveLoadParms * callback = data.saveLoadParms;
	idStr saveFolder = "savegame";

	saveFolder.AppendPath( callback->directory );

	if ( fileSystem->IsFolder( saveFolder, "fs_savePath" ) != FOLDER_YES ) {
		callback->errorCode = SAVEGAME_E_FOLDER_NOT_FOUND;
		return -1;
	}

	int ret = ERROR_SUCCESS;
	for ( int i = 0; i < callback->files.Num() && ret == ERROR_SUCCESS && !callback->cancelled; i++ ) {
		idFile_SaveGame * file = callback->files[i];

		idStr filename = saveFolder;
		filename.AppendPath( file->GetName() );

		idFile * inputFile = fileSystem->OpenFileRead( filename.c_str() );
		if ( inputFile == NULL ) {
			file->error = true;
			if ( !( file->type & SAVEGAMEFILE_OPTIONAL ) ) {
				callback->errorCode = SAVEGAME_E_CORRUPTED;
				ret = -1;
			}
			continue;
		}

		if ( ( file->type & SAVEGAMEFILE_PIPELINED ) != 0 ) {

			idFile_SaveGamePipelined * outputFile = dynamic_cast< idFile_SaveGamePipelined * >( file );
			assert( outputFile != NULL );

			size_t lastReadBytes = 0;
			blockForIO_t block;
			while ( outputFile->NextReadBlock( &block, lastReadBytes ) && !callback->cancelled ) {
				lastReadBytes = inputFile->Read( block.data, block.bytes );
				if ( lastReadBytes != block.bytes ) {
					// Notify end-of-file to the save game file which will cause all reads on the
					// other end of the pipeline to return zero bytes after the pipeline is drained.
					outputFile->NextReadBlock( NULL, lastReadBytes );
					break;
				}
			}

		} else {

			size_t size = inputFile->Length();

			unsigned int originalChecksum = 0;
			if ( ( file->type & SAVEGAMEFILE_BINARY ) != 0 || ( file->type & SAVEGAMEFILE_COMPRESSED ) != 0 ) {
				if ( saveGame_checksum.GetBool() ) {
					if ( size >= sizeof( originalChecksum ) ) {
						inputFile->ReadBig( originalChecksum );
						size -= sizeof( originalChecksum );
					}
				}
			}

			file->SetLength( size );

			size_t sizeRead = inputFile->Read( (void *)file->GetDataPtr(), size );
			if ( sizeRead != size ) {
				file->error = true;
				callback->errorCode = SAVEGAME_E_CORRUPTED;
				ret = -1;
			}

			if ( ( file->type & SAVEGAMEFILE_BINARY ) != 0 || ( file->type & SAVEGAMEFILE_COMPRESSED ) != 0 ) {
				if ( saveGame_checksum.GetBool() ) {
					unsigned int checksum = MD5_BlockChecksum( file->GetDataPtr(), file->Length() );
					if ( checksum != originalChecksum ) {
						file->error = true;
						callback->errorCode = SAVEGAME_E_CORRUPTED;
						ret = -1;
					}
				}
			}
		}

		delete inputFile;
	}

	if ( data.saveLoadParms->cancelled ) {
		data.saveLoadParms->errorCode = SAVEGAME_E_CANCELLED;
	}

	return ret;
}
Esempio n. 7
0
/*
================
idDeclParticle::Parse
================
*/
bool idDeclParticle::Parse( const char* text, const int textLength, bool allowBinaryVersion )
{

	if( cvarSystem->GetCVarBool( "fs_buildresources" ) )
	{
		fileSystem->AddParticlePreload( GetName() );
	}
	
	idLexer src;
	idToken	token;
	
	unsigned int sourceChecksum = 0;
	idStrStatic< MAX_OSPATH > generatedFileName;
	if( allowBinaryVersion )
	{
		// Try to load the generated version of it
		// If successful,
		// - Create an MD5 of the hash of the source
		// - Load the MD5 of the generated, if they differ, create a new generated
		generatedFileName = "generated/particles/";
		generatedFileName.AppendPath( GetName() );
		generatedFileName.SetFileExtension( ".bprt" );
		
		idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
		sourceChecksum = MD5_BlockChecksum( text, textLength );
		
		if( binaryLoadParticles.GetBool() && LoadBinary( file, sourceChecksum ) )
		{
			return true;
		}
	}
	
	src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
	src.SetFlags( DECL_LEXER_FLAGS );
	src.SkipUntilString( "{" );
	
	depthHack = 0.0f;
	
	while( 1 )
	{
		if( !src.ReadToken( &token ) )
		{
			break;
		}
		
		if( !token.Icmp( "}" ) )
		{
			break;
		}
		
		if( !token.Icmp( "{" ) )
		{
			if( stages.Num() >= MAX_PARTICLE_STAGES )
			{
				src.Error( "Too many particle stages" );
				MakeDefault();
				return false;
			}
			idParticleStage* stage = ParseParticleStage( src );
			if( !stage )
			{
				src.Warning( "Particle stage parse failed" );
				MakeDefault();
				return false;
			}
			stages.Append( stage );
			continue;
		}
		
		if( !token.Icmp( "depthHack" ) )
		{
			depthHack = src.ParseFloat();
			continue;
		}
		
		src.Warning( "bad token %s", token.c_str() );
		MakeDefault();
		return false;
	}
	
	// don't calculate bounds or write binary files for defaulted ( non-existent ) particles in resource builds
	if( fileSystem->UsingResourceFiles() )
	{
		bounds = idBounds( vec3_origin ).Expand( 8.0f );
		return true;
	}
	//
	// calculate the bounds
	//
	bounds.Clear();
	for( int i = 0; i < stages.Num(); i++ )
	{
		GetStageBounds( stages[i] );
		bounds.AddBounds( stages[i]->bounds );
	}
	
	if( bounds.GetVolume() <= 0.1f )
	{
		bounds = idBounds( vec3_origin ).Expand( 8.0f );
	}
	
	if( allowBinaryVersion && binaryLoadParticles.GetBool() )
	{
		idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
		idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
		WriteBinary( outputFile, sourceChecksum );
	}
	
	return true;
}
/*
========================
idSnapShot::WriteObject
========================
*/
void idSnapShot::WriteObject( idFile* file, int visIndex, objectState_t* newState, objectState_t* oldState, int& lastobjectNum )
{
	assert( newState != NULL || oldState != NULL );
	
	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 != NULL && oldState != NULL )
	{
		// 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->buffer.Size() == 0 ) != ( oldState->buffer.Size() == 0 );
			
			if( !oldVisible && !newVisible )
			{
				// object is stale and ack'ed for this client, write nothing (see 'same object' below)
				return;
			}
			else if( oldVisible && !newVisible )
			{
				NET_VERBOSESNAPSHOT_PRINT( "object %d to client %d goes stale\n", newState->objectNum, visIndex );
				visChange = true;
				visSendState = false;
			}
			else if( !oldVisible && newVisible )
			{
				NET_VERBOSESNAPSHOT_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 && newState->buffer.Size() == oldState->buffer.Size() &&
				( ( newState->buffer.Ptr() == oldState->buffer.Ptr() ) || memcmp( newState->buffer.Ptr(), oldState->buffer.Ptr(), newState->buffer.Size() ) == 0 ) )
		{
			// same state, write nothing
			return;
		}
	}
	
	// Get the id of the object we are writing out
	uint16 objectNum;
	if( newState != NULL )
	{
		objectNum = newState->objectNum;
	}
	else if( oldState != NULL )
	{
		objectNum = oldState->objectNum;
	}
	else
	{
		objectNum = 0;
	}
	
	assert( objectNum == 0 || objectNum > lastobjectNum );
	
	// Write out object id (using delta)
	uint16 objectDelta = objectNum - lastobjectNum;
	file->WriteBig( objectDelta );
	lastobjectNum = objectNum;
	
	if( newState == NULL )
	{
		// Deleted, write 0 size
		assert( oldState != NULL );
		file->WriteBig<objectSize_t>( 0 );
	}
	else if( oldState == NULL )
	{
		// New object, write out full state
		assert( newState != NULL );
		// delta against an empty snap
		file->WriteBig( newState->buffer.Size() );
		file->Write( newState->buffer.Ptr(), newState->buffer.Size() );
	}
	else
	{
		// Compare to last object
		assert( newState != NULL && oldState != NULL );
		assert( newState->objectNum == oldState->objectNum );
		
		if( visChange )
		{
			// fake size indicates vis state change
			// NOTE: we may still send a real size and a state below, for 'no longer stale' transitions
			// TMP: send 0xFFFF for going stale and 0xFFFF - 1 for no longer stale
			file->WriteBig<objectSize_t>( visSendState ? SIZE_NOT_STALE : SIZE_STALE );
		}
		if( !visChange || visSendState )
		{
		
			objectSize_t compareSize = Min( newState->buffer.Size(), oldState->buffer.Size() );		// Get the number of bytes that overlap
			
			file->WriteBig( newState->buffer.Size() );										// Write new size
			
			// Compare bytes that overlap
			for( objectSize_t b = 0; b < compareSize; b++ )
			{
				file->WriteBig<byte>( ( 0xFF + 1 + newState->buffer[b] - oldState->buffer[b] ) & 0xFF );
			}
			
			// Write leftover
			if( newState->buffer.Size() > compareSize )
			{
				file->Write( newState->buffer.Ptr() + oldState->buffer.Size(), newState->buffer.Size() - compareSize );
			}
		}
	}
	
#ifdef SNAPSHOT_CHECKSUMS
	if( ( !visChange || visSendState ) && newState != NULL )
	{
		assert( newState->buffer.Size() > 0 );
		unsigned int checksum = MD5_BlockChecksum( newState->buffer.Ptr(), newState->buffer.Size() );
		file->WriteBig( checksum );
	}
#endif
}
/*
========================
idSnapShot::ReadDelta
========================
*/
bool idSnapShot::ReadDelta( idFile* file, int visIndex )
{

	file->ReadBig( time );
	
	int objectNum = 0;
	uint16 delta = 0;
	while( file->ReadBig( delta ) == sizeof( delta ) )
	{
		objectNum += delta;
		if( objectNum >= 0xFFFF )
		{
			// full delta
			return true;
		}
		objectState_t& state = FindOrCreateObjectByID( objectNum );
		objectSize_t newsize = 0;
		file->ReadBig( newsize );
		
		if( newsize == SIZE_STALE )
		{
			NET_VERBOSESNAPSHOT_PRINT( "read delta: object %d goes stale\n", objectNum );
			// sanity
			bool oldVisible = ( state.visMask & ( 1 << visIndex ) ) != 0;
			if( !oldVisible )
			{
				NET_VERBOSESNAPSHOT_PRINT( "ERROR: unexpected already stale\n" );
			}
			state.visMask &= ~( 1 << visIndex );
			state.stale = true;
			// We need to make sure we haven't freed stale objects.
			assert( state.buffer.Size() > 0 );
			// no more data
			continue;
		}
		else if( newsize == SIZE_NOT_STALE )
		{
			NET_VERBOSESNAPSHOT_PRINT( "read delta: object %d no longer stale\n", objectNum );
			// sanity
			bool oldVisible = ( state.visMask & ( 1 << visIndex ) ) != 0;
			if( oldVisible )
			{
				NET_VERBOSESNAPSHOT_PRINT( "ERROR: unexpected not stale\n" );
			}
			state.visMask |= ( 1 << visIndex );
			state.stale = false;
			// the latest state is packed in, get the new size and continue reading the new state
			file->ReadBig( newsize );
		}
		
		if( newsize == 0 )
		{
			// object deleted
			state.buffer._Release();
		}
		else
		{
			objectBuffer_t newbuffer( newsize );
			objectSize_t compareSize = Min( newsize, state.buffer.Size() );
			
			for( objectSize_t i = 0; i < compareSize; i++ )
			{
				uint8 delta = 0;
				file->ReadBig<byte>( delta );
				newbuffer[i] = state.buffer[i] + delta;
			}
			
			if( newsize > compareSize )
			{
				file->Read( newbuffer.Ptr() + compareSize, newsize - compareSize );
			}
			
			state.buffer = newbuffer;
			state.changedCount++;
		}
		
#ifdef SNAPSHOT_CHECKSUMS
		if( state.buffer.Size() > 0 )
		{
			unsigned int checksum = 0;
			file->ReadBig( checksum );
			assert( checksum == MD5_BlockChecksum( state.buffer.Ptr(), state.buffer.Size() ) );
		}
#endif
	}
	
	// partial delta
	return false;
}
Esempio n. 10
0
void rvInstance::Populate( int serverChecksum ) {
	gameState_t currentState = gameLocal.GameState();
	
	// disable the minSpawnIndex lock out
	int latchMinSpawnIndex = gameLocal.minSpawnIndex;
	gameLocal.minSpawnIndex = MAX_CLIENTS;

	if ( currentState != GAMESTATE_STARTUP ) {
		gameLocal.SetGameState( GAMESTATE_RESTART );
	}

	if ( gameLocal.isServer ) {
		// When populating on a server, record the entity numbers	
		numMapEntities = gameLocal.GetNumMapEntities();

		// mwhitlock: Dynamic memory consolidation
		RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
		if ( mapEntityNumbers ) {
			delete[] mapEntityNumbers;
		}
		mapEntityNumbers = new unsigned short[ numMapEntities ];
		RV_POP_HEAP();

		memset( mapEntityNumbers, -1, sizeof( unsigned short ) * numMapEntities );

		// read the index we should start populating at as transmitted by the server
		gameLocal.firstFreeIndex = gameLocal.GetStartingIndexForInstance( instanceID );
		//common->Printf( "pos: get starting index for instance %d sets firstFreeIndex to %d\n", instanceID, gameLocal.firstFreeIndex );

		// remember the spawnCount ahead of time, so that the client can accurately reconstruct its spawnIds
		gameLocal.SpawnMapEntities( spawnInstanceID, NULL, mapEntityNumbers, &initialSpawnCount );

		// only build the message in MP
		if ( gameLocal.isMultiplayer ) {
			BuildInstanceMessage();

			// force joins of anyone in our instance so they get potentially new map entitynumbers
			for( int i = 0; i < MAX_CLIENTS; i++ ) {
				PACIFIER_UPDATE;
				idPlayer* player = (idPlayer*)gameLocal.entities[ i ];
				if( player && player->GetInstance() == instanceID ) {
					networkSystem->ServerSendReliableMessage( player->entityNumber, mapEntityMsg );
				}
			}
		}
	} else {
		bool proto69 = ( gameLocal.GetCurrentDemoProtocol() == 69 );

		// When populating on a client, spawn the map using existing numbers		
		// check for good state
		// OK to spawn w/o specific entity numbers if we're in the startup process.  Otherwise, 
		// we need entity numbers from the server.
		// TTimo: only valid for backward 1.2 playback now
		assert( !proto69 || ( mapEntityNumbers || ( instanceID == 0 && gameLocal.GameState() == GAMESTATE_STARTUP ) ) );
		
		if ( !proto69 ) {
			// have the client produce a log of the entity layout so we can match it with the server's
			// this is also going to be used to issue the EV_FindTargets below
			if ( mapEntityNumbers ) {
				delete []mapEntityNumbers;
			}
			numMapEntities = gameLocal.GetNumMapEntities();
			RV_PUSH_SYS_HEAP_ID(RV_HEAP_ID_LEVEL);
			mapEntityNumbers = new unsigned short[ numMapEntities ];
			RV_POP_HEAP();
			memset( mapEntityNumbers, -1, sizeof( unsigned short ) * numMapEntities );
		}

		gameLocal.firstFreeIndex = gameLocal.GetStartingIndexForInstance( instanceID );

		gameLocal.SetSpawnCount( initialSpawnCount ); // that was transmitted through the instance msg
		if ( proto69 ) {
			gameLocal.SpawnMapEntities( spawnInstanceID, mapEntityNumbers, NULL );
		} else {
			gameLocal.SpawnMapEntities( spawnInstanceID, NULL, mapEntityNumbers );
			LittleRevBytes( mapEntityNumbers, sizeof(unsigned short ), numMapEntities ); //DAJ
			int checksum = MD5_BlockChecksum( mapEntityNumbers, sizeof( unsigned short ) * numMapEntities );
			if ( serverChecksum != 0 && checksum != serverChecksum ) {
				common->Error( "client side map populate checksum ( 0x%x ) doesn't match server's ( 0x%x )", checksum, serverChecksum );
			}
		}
	}


	for ( int i = 0; i < numMapEntities; i++ ) {
		if ( mapEntityNumbers[ i ] < 0 || mapEntityNumbers[ i ] >= MAX_GENTITIES ) {
			continue;
		}

		if ( (i % 100) == 0 ) {
			PACIFIER_UPDATE;
		}

		idEntity* ent = gameLocal.entities[ mapEntityNumbers[ i ] ];

		if ( ent ) {
			ent->PostEventMS( &EV_FindTargets, 0 );
			ent->PostEventMS( &EV_PostSpawn, 0 );
		}
	}

	if ( currentState != GAMESTATE_STARTUP ) {
		gameLocal.SetGameState( currentState );
	}

	// re-enable the min spawn index
	assert( latchMinSpawnIndex == MAX_CLIENTS || gameLocal.firstFreeIndex <= latchMinSpawnIndex );
	gameLocal.minSpawnIndex = latchMinSpawnIndex;
}