Пример #1
0
ULevelStreaming* FWorldTileModel::CreateAssosiatedStreamingLevel()
{
	ULevelStreaming* AssociatedStreamingLevel = NULL;
	ULevel* Level = GetLevelObject();
		
	if (Level)
	{
		FName PackageName = Level->GetOutermost()->GetFName();
		UWorld* PersistentWorld = LevelCollectionModel.GetWorld();
				
		// Try to find existing object first
		int32 FoundIndex = PersistentWorld->StreamingLevels.FindMatch(ULevelStreaming::FPackageNameMatcher(PackageName));
		if (FoundIndex != INDEX_NONE)
		{
			AssociatedStreamingLevel = PersistentWorld->StreamingLevels[FoundIndex];
		}
		else
		{
			// Create new streaming level
			AssociatedStreamingLevel = Cast<ULevelStreaming>(
				StaticConstructObject(ULevelStreamingKismet::StaticClass(), PersistentWorld, NAME_None, RF_Transient, NULL)
				);

			//
			AssociatedStreamingLevel->PackageName		= PackageName;
			AssociatedStreamingLevel->DrawColor			= FColor::MakeRandomColor();
			AssociatedStreamingLevel->LevelTransform	= FTransform::Identity;
			AssociatedStreamingLevel->PackageNameToLoad	= PackageName;
			//
			PersistentWorld->StreamingLevels.Add(AssociatedStreamingLevel);
		}
	}

	return AssociatedStreamingLevel;
}
Пример #2
0
void FLevelModel::SetVisible(bool bVisible)
{
	//don't create unnecessary transactions
	if (IsVisible() == bVisible)
	{
		return;
	}

	const bool oldIsDirty = IsDirty();

	const FScopedTransaction Transaction(LOCTEXT("ToggleVisibility", "Toggle Level Visibility"));

	//this call hides all owned actors, etc
	EditorLevelUtils::SetLevelVisibility( GetLevelObject(), bVisible, false );

	if (!oldIsDirty)
	{
		// don't set the dirty flag if we're just changing the visibility of the level within the editor
		ULevel* Level = GetLevelObject();
		if (Level)
		{
			Level->GetOutermost()->SetDirtyFlag(false);
		}
	}
}
		/** Generates a sub-level Blueprints sub-menu */
		static void MakeSubLevelsMenu(FMenuBuilder& InMenuBuilder, TWeakPtr< SLevelEditor > InLvlEditor)
		{
			FSlateIcon EditBP(FEditorStyle::Get().GetStyleSetName(), TEXT("LevelEditor.OpenLevelBlueprint"));

			InMenuBuilder.BeginSection(NAME_None, LOCTEXT("SubLevelsHeading", "Sub-Level Blueprints"));
			{
				UWorld* World = InLvlEditor.Pin()->GetWorld();
				for (int32 iLevel = 0; iLevel < World->GetNumLevels(); iLevel++)
				{
					ULevel* Level = World->GetLevel(iLevel);
					if (Level != NULL && Level->GetOutermost() != NULL)
					{
						if (!Level->IsPersistentLevel())
						{
							FUIAction UIAction
								(
								FExecuteAction::CreateStatic(&FLevelEditorToolBar::OnOpenSubLevelBlueprint, Level)
								);

							FText DisplayName = FText::Format(LOCTEXT("SubLevelBlueprintItem", "Edit {LevelName}"), FText::FromString(FPaths::GetCleanFilename(Level->GetOutermost()->GetName())));
							InMenuBuilder.AddMenuEntry(DisplayName, FText::GetEmpty(), EditBP, UIAction);
						}
					}
				}
			}
			InMenuBuilder.EndSection();
		}
FSlateTextureDataPtr FTileThumbnail::UpdateThumbnail()
{
	// No need images for persistent and always loaded levels
	if (TileModel.IsPersistent())
	{
		return ToSlateTextureData(nullptr);
	}
	
	// Load image from a package header
	if (!TileModel.IsVisible() || TileModel.IsSimulating())
	{
		const FName LevelAssetName = TileModel.GetAssetName();
		TSet<FName> ObjectFullNames;
		ObjectFullNames.Add(LevelAssetName);
		FThumbnailMap ThumbnailMap;

		if (ThumbnailTools::ConditionallyLoadThumbnailsFromPackage(TileModel.GetPackageFileName(), ObjectFullNames, ThumbnailMap))
		{
			const FObjectThumbnail* ObjectThumbnail = ThumbnailMap.Find(LevelAssetName);
			return ToSlateTextureData(ObjectThumbnail);
		}
	}
	// Render image from a visible level
	else
	{
		ULevel* TargetLevel = TileModel.GetLevelObject();
		if (TargetLevel)
		{
			FIntPoint RTSize = ThumbnailRenderTarget->GetSizeXY();
			
			// Set persistent world package as transient to avoid package dirtying during thumbnail rendering
			FUnmodifiableObject ImmuneWorld(TargetLevel->OwningWorld);
			
			FObjectThumbnail NewThumbnail;
			// Generate the thumbnail
			ThumbnailTools::RenderThumbnail(
				TargetLevel,
				RTSize.X,
				RTSize.Y,
				ThumbnailTools::EThumbnailTextureFlushMode::NeverFlush,
				ThumbnailRenderTarget,
				&NewThumbnail
				);

			UPackage* MyOutermostPackage = CastChecked<UPackage>(TargetLevel->GetOutermost());
			ThumbnailTools::CacheThumbnail(TileModel.GetAssetName().ToString(), &NewThumbnail, MyOutermostPackage);
			return ToSlateTextureData(&NewThumbnail);
		}
	}

	return ToSlateTextureData(nullptr);
}
Пример #5
0
TArray<UPackage*> FLevelCollectionModel::GetPackagesList(const FLevelModelList& InList)
{
	TArray<UPackage*> ResultList;
	
	for (auto It = InList.CreateConstIterator(); It; ++It)
	{
		ULevel* Level = (*It)->GetLevelObject();
		if (Level)
		{
			ResultList.Add(Level->GetOutermost());
		}
	}

	return ResultList;
}
Пример #6
0
FString FLevelViewModel::GetFileSizeString() const
{
	FString MemorySizeString;
	ULevel* CurLevel = Level.Get();

	if ( CurLevel && GetDefault<ULevelBrowserSettings>()->bDisplayFileSize  )
	{
		// Update metrics
		static const float ByteConversion = 1.0f / 1024.0f;
		float FileSize = CurLevel->GetOutermost()->GetFileSize() * ByteConversion * ByteConversion;
		
		MemorySizeString += FString::Printf( TEXT( "%.2f" ), FileSize );
	}

	return MemorySizeString;
}
Пример #7
0
FString FLevelModel::GetFileSizeString() const
{
	FString MemorySizeString;
	ULevel* Level = GetLevelObject();

	if (Level && Editor->AccessEditorUserSettings().bDisplayFileSizeInLevelBrowser)
	{
		// Update metrics
		static const float ByteConversion = 1.0f / 1024.0f;
		float FileSize = Level->GetOutermost()->GetFileSize() * ByteConversion * ByteConversion;
		
		MemorySizeString += FString::Printf(TEXT( "%.2f" ), FileSize);
	}

	return MemorySizeString;
}
Пример #8
0
void FLevelCollectionModel::CacheCanExecuteSourceControlVars() const
{
	bCanExecuteSCCCheckOut = false;
	bCanExecuteSCCOpenForAdd = false;
	bCanExecuteSCCCheckIn = false;
	bCanExecuteSCC = false;

	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
	for (auto It = SelectedLevelsList.CreateConstIterator(); It; ++It)
	{
		if (ISourceControlModule::Get().IsEnabled() && SourceControlProvider.IsAvailable())
		{
			bCanExecuteSCC = true;
			
			ULevel* Level = (*It)->GetLevelObject();
			if (Level)
			{
				// Check the SCC state for each package in the selected paths
				FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Level->GetOutermost(), EStateCacheUsage::Use);

				if (SourceControlState.IsValid())
				{
					if (SourceControlState->CanCheckout())
					{
						bCanExecuteSCCCheckOut = true;
					}
					else if (!SourceControlState->IsSourceControlled())
					{
						bCanExecuteSCCOpenForAdd = true;
					}
					else if (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded())
					{
						bCanExecuteSCCCheckIn = true;
					}
				}
			}
		}

		if (bCanExecuteSCCCheckOut && 
			bCanExecuteSCCOpenForAdd && 
			bCanExecuteSCCCheckIn)
		{
			// All options are available, no need to keep iterating
			break;
		}
	}
}
Пример #9
0
	/** Check if the object is in a visible level */
	bool IsInVisibleLevel( UObject* InObject, UWorld* InWorld ) const
	{
		check( InObject );
		check( InWorld );

		UObject* ObjectPackage = InObject->GetOutermost();

		for( int32 LevelIndex=0; LevelIndex<InWorld->GetNumLevels(); LevelIndex++ )
		{
			ULevel* Level = InWorld->GetLevel(LevelIndex);
			if( Level && Level->GetOutermost() == ObjectPackage )
			{
				return true;
			}
		}
		return false;
	}
Пример #10
0
void UUnrealEdEngine::ConvertMatinees()
{
	FVector StartLocation= FVector::ZeroVector;
	UWorld* World = GWorld;
	if( World )
	{
		ULevel* Level = World->GetCurrentLevel();
		if( !Level )
		{
			Level = World->PersistentLevel;
		}
		check(Level);
		for( TObjectIterator<UInterpData> It; It; ++It )
		{
			UInterpData* InterpData = *It;
			if( InterpData->IsIn( Level ) ) 
			{
				// We dont care about renaming references or adding redirectors.  References to this will be old seqact_interps
				GEditor->RenameObject( InterpData, Level->GetOutermost(), *InterpData->GetName() );

				AMatineeActor* MatineeActor = Level->OwningWorld->SpawnActor<AMatineeActor>(StartLocation, FRotator::ZeroRotator);
				StartLocation.Y += 50;
								
				MatineeActor->MatineeData = InterpData;
				UProperty* MatineeDataProp = NULL;
				for( UProperty* Property = MatineeActor->GetClass()->PropertyLink; Property != NULL; Property = Property->PropertyLinkNext )
				{
					if( Property->GetName() == TEXT("MatineeData") )
					{
						MatineeDataProp = Property;
						break;
					}
				}

				FPropertyChangedEvent PropertyChangedEvent( MatineeDataProp ); 
				MatineeActor->PostEditChangeProperty( PropertyChangedEvent );
			}
		}
	}

}
Пример #11
0
void FShadowMapPendingTexture::StartEncoding(UWorld* InWorld)
{
	// Create the shadow-map texture.
	UShadowMapTexture2D* Texture = new(Outer) UShadowMapTexture2D(FPostConstructInitializeProperties());
	Texture->Filter			= GUseBilinearLightmaps ? TF_Default : TF_Nearest;
	// Signed distance field textures get stored in linear space, since they need more precision near .5.
	Texture->SRGB			= false;
	Texture->LODGroup		= TEXTUREGROUP_Shadowmap;
	Texture->ShadowmapFlags	= ShadowmapFlags;
	Texture->Source.Init2DWithMipChain(GetSizeX(), GetSizeY(), TSF_BGRA8);
	Texture->MipGenSettings = TMGS_LeaveExistingMips;
	Texture->CompressionNone = true;

	int32 TextureSizeX = Texture->Source.GetSizeX();
	int32 TextureSizeY = Texture->Source.GetSizeY();

	{
		// Create the uncompressed top mip-level.
		TArray< TArray<FFourDistanceFieldSamples> > MipData;
		FShadowMap2D::EncodeSingleTexture(*this, Texture, MipData);

		// Copy the mip-map data into the UShadowMapTexture2D's mip-map array.
		for(int32 MipIndex = 0;MipIndex < MipData.Num();MipIndex++)
		{
			FColor* DestMipData = (FColor*)Texture->Source.LockMip(MipIndex);
			uint32 MipSizeX = FMath::Max(1,TextureSizeX >> MipIndex);
			uint32 MipSizeY = FMath::Max(1,TextureSizeY >> MipIndex);

			for(uint32 Y = 0;Y < MipSizeY;Y++)
			{
				for(uint32 X = 0;X < MipSizeX;X++)
				{
					const FFourDistanceFieldSamples& SourceSample = MipData[MipIndex][Y * MipSizeX + X];
					DestMipData[ Y * MipSizeX + X ] = FColor(SourceSample.Samples[0].Distance, SourceSample.Samples[1].Distance, SourceSample.Samples[2].Distance, SourceSample.Samples[3].Distance);
				}
			}

			Texture->Source.UnlockMip(MipIndex);
		}
	}

	// Update stats.
	int32 TextureSize			= Texture->CalcTextureMemorySizeEnum( TMC_AllMips );
	GNumShadowmapTotalTexels	+= TextureSizeX * TextureSizeY;
	GNumShadowmapTextures++;
	GShadowmapTotalSize			+= TextureSize;
	GShadowmapTotalStreamingSize += (ShadowmapFlags & SMF_Streamed) ? TextureSize : 0;
	UPackage* TexturePackage = Texture->GetOutermost();

	for ( int32 LevelIndex=0; TexturePackage && LevelIndex < InWorld->GetNumLevels(); LevelIndex++ )
	{
		ULevel* Level = InWorld->GetLevel(LevelIndex);
		UPackage* LevelPackage = Level->GetOutermost();
		if ( TexturePackage == LevelPackage )
		{
			Level->ShadowmapTotalSize += float(TextureSize) / 1024.0f;
			break;
		}
	}

	// Update the texture resource.
	Texture->BeginCachePlatformData();
	Texture->FinishCachePlatformData();
	Texture->UpdateResource();
}
Пример #12
0
void FLevelCollectionModel::SaveLevels(const FLevelModelList& InLevelList)
{
	if (IsReadOnly())
	{
		return;
	}

		
	FLevelModelList		LevelModelsToSave;
	TArray<ULevel*>		LevelsToSave;
	for (auto It = InLevelList.CreateConstIterator(); It; ++It)
	{
		if ((*It)->GetLevelObject())
		{
			if (!(*It)->IsVisible())
			{
				FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "UnableToSaveInvisibleLevels", "Save aborted.  Levels must be made visible before they can be saved.") );
				return;
			}
			else if ((*It)->IsLocked())
			{
				FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "UnableToSaveLockedLevels", "Save aborted.  Level must be unlocked before it can be saved.") );
				return;
			}
						
			LevelModelsToSave.Add(*It);
			LevelsToSave.Add((*It)->GetLevelObject());
		}
	}

	TArray< UPackage* > PackagesNotNeedingCheckout;
	// Prompt the user to check out the levels from source control before saving
	if (FEditorFileUtils::PromptToCheckoutLevels(false, LevelsToSave, &PackagesNotNeedingCheckout))
	{
		for (auto It = LevelsToSave.CreateIterator(); It; ++It)
		{
			FEditorFileUtils::SaveLevel(*It);
		}
	}
	else if (PackagesNotNeedingCheckout.Num() > 0)
	{
		// The user canceled the checkout dialog but some packages didn't need to be checked out in order to save
		// For each selected level if the package its in didn't need to be saved, save the level!
		for (int32 LevelIdx = 0; LevelIdx < LevelsToSave.Num(); ++LevelIdx)
		{
			ULevel* Level = LevelsToSave[LevelIdx];
			if (PackagesNotNeedingCheckout.Contains(Level->GetOutermost()))
			{
				FEditorFileUtils::SaveLevel(Level);
			}
			else
			{
				//remove it from the list, so that only successfully saved levels are highlighted when saving is complete
				LevelModelsToSave.RemoveAt(LevelIdx);
				LevelsToSave.RemoveAt(LevelIdx);
			}
		}
	}

	// Select tiles that were saved successfully
	SetSelectedLevels(LevelModelsToSave);
}
Пример #13
0
void FLevelCollectionModel::SCCDiffAgainstDepot(const FLevelModelList& InList, UEditorEngine* InEditor)
{
	// Load the asset registry module
	FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");

	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();

	// Iterate over each selected asset
	for (auto It = InList.CreateConstIterator(); It; ++It)
	{
		ULevel* Level = (*It)->GetLevelObject();
		if (Level == NULL)
		{
			return;
		}
		
		UPackage* OriginalPackage = Level->GetOutermost();
		FString PackageName = OriginalPackage->GetName();

		// Make sure our history is up to date
		auto UpdateStatusOperation = ISourceControlOperation::Create<FUpdateStatus>();
		UpdateStatusOperation->SetUpdateHistory(true);
		SourceControlProvider.Execute(UpdateStatusOperation, OriginalPackage);

		// Get the SCC state
		FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(
			OriginalPackage, EStateCacheUsage::Use
			);

		// If the level is in SCC.
		if (SourceControlState.IsValid() && SourceControlState->IsSourceControlled())
		{
			// Get the file name of package
			FString RelativeFileName;
			if(FPackageName::DoesPackageExist(PackageName, NULL, &RelativeFileName))
			{
				if (SourceControlState->GetHistorySize() > 0)
				{
					auto Revision = SourceControlState->GetHistoryItem(0);
					check(Revision.IsValid());

					// Get the head revision of this package from source control
					FString AbsoluteFileName = FPaths::ConvertRelativePathToFull(RelativeFileName);
					FString TempFileName;
					if (Revision->Get(TempFileName))
					{
						// Forcibly disable compile on load in case we are loading old blueprints that might try to update/compile
						TGuardValue<bool> DisableCompileOnLoad(GForceDisableBlueprintCompileOnLoad, true);

						// Try and load that package
						FText NotMapReason;
						UPackage* OldPackage = LoadPackage(NULL, *TempFileName, LOAD_None);
						if(OldPackage != NULL && InEditor->PackageIsAMapFile(*TempFileName, NotMapReason))
						{
							/* Set the revision information*/
							UPackage* Package = OriginalPackage;

							FRevisionInfo OldRevision;
							OldRevision.Changelist = Revision->GetCheckInIdentifier();
							OldRevision.Date = Revision->GetDate();
							OldRevision.Revision = Revision->GetRevision();

							FRevisionInfo NewRevision; 
							NewRevision.Revision = TEXT("");

							// Dump assets to temp text files
							FString OldTextFilename = AssetToolsModule.Get().DumpAssetToTempFile(OldPackage);
							FString NewTextFilename = AssetToolsModule.Get().DumpAssetToTempFile(OriginalPackage);
							FString DiffCommand = GetDefault<UEditorLoadingSavingSettings>()->TextDiffToolPath.FilePath;

							AssetToolsModule.Get().CreateDiffProcess(DiffCommand, OldTextFilename, NewTextFilename);
							AssetToolsModule.Get().DiffAssets(OldPackage, OriginalPackage, OldRevision, NewRevision);
						}
					}
				}
			} 
		}
	}
}
Пример #14
0
/** This will set the StreamingLevels TMap with the current Streaming Level Status and also set which level the player is in **/
void GetLevelStreamingStatus( UWorld* World, TMap<FName,int32>& StreamingLevels, FString& LevelPlayerIsInName )
{
	FWorldContext &Context = GEngine->WorldContextFromWorld(World);

	// Iterate over the world info's level streaming objects to find and see whether levels are loaded, visible or neither.
	for( int32 LevelIndex=0; LevelIndex<World->StreamingLevels.Num(); LevelIndex++ )
	{
		ULevelStreaming* LevelStreaming = World->StreamingLevels[LevelIndex];

		if( LevelStreaming 
			&&  LevelStreaming->PackageName != NAME_None 
			&&	LevelStreaming->PackageName != World->GetOutermost()->GetFName() )
		{
			ULevel* Level = LevelStreaming->GetLoadedLevel();
			if( Level != NULL )
			{
				if( World->ContainsLevel( Level ) == true )
				{
					if( World->CurrentLevelPendingVisibility == Level )
					{
						StreamingLevels.Add( LevelStreaming->PackageName, LEVEL_MakingVisible );
					}
					else
					{
						StreamingLevels.Add( LevelStreaming->PackageName, LEVEL_Visible );
					}
				}
				else
				{
					StreamingLevels.Add( LevelStreaming->PackageName, LEVEL_Loaded );
				}
			}
			else
			{
				// See whether the level's world object is still around.
				UPackage* LevelPackage	= Cast<UPackage>(StaticFindObjectFast( UPackage::StaticClass(), NULL, LevelStreaming->PackageName ));
				UWorld*	  LevelWorld	= NULL;
				if( LevelPackage )
				{
					LevelWorld = UWorld::FindWorldInPackage(LevelPackage);
				}

				if( LevelWorld )
				{
					StreamingLevels.Add( LevelStreaming->PackageName, LEVEL_UnloadedButStillAround );
				}
				else if( GetAsyncLoadPercentage( *LevelStreaming->PackageName.ToString() ) >= 0 )
				{
					StreamingLevels.Add( LevelStreaming->PackageName, LEVEL_Loading );
				}
				else
				{
					StreamingLevels.Add( LevelStreaming->PackageName, LEVEL_Unloaded );
				}
			}
		}
	}

	
	// toss in the levels being loaded by PrepareMapChange
	for( int32 LevelIndex=0; LevelIndex < Context.LevelsToLoadForPendingMapChange.Num(); LevelIndex++ )
	{
		const FName LevelName = Context.LevelsToLoadForPendingMapChange[LevelIndex];
		StreamingLevels.Add(LevelName, LEVEL_Preloading);
	}


	ULevel* LevelPlayerIsIn = NULL;

	for( FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator )
	{
		APlayerController* PlayerController = *Iterator;

		if( PlayerController->GetPawn() != NULL )
		{
			// need to do a trace down here
			//TraceActor = Trace( out_HitLocation, out_HitNormal, TraceDest, TraceStart, false, TraceExtent, HitInfo, true );
			FHitResult Hit(1.f);

			// this will not work for flying around :-(
			static FName NAME_FindLevel = FName(TEXT("FindLevel"), true);			
			PlayerController->GetWorld()->LineTraceSingle(Hit,PlayerController->GetPawn()->GetActorLocation(), (PlayerController->GetPawn()->GetActorLocation()-FVector(0.f, 0.f, 256.f)), FCollisionQueryParams(NAME_FindLevel, true, PlayerController->GetPawn()), FCollisionObjectQueryParams(ECC_WorldStatic));

			/** @todo UE4 FIXME
			if( Hit.Level != NULL )
			{
				LevelPlayerIsIn = Hit.Level;
			}
			else 
			*/
			if( Hit.GetActor() != NULL )
			{
				LevelPlayerIsIn = Hit.GetActor()->GetLevel();
			}
			else if( Hit.Component != NULL && Hit.Component->GetOwner() != NULL )
			{
				AActor* Owner = Hit.Component->GetOwner();
				if (Owner)
				{
					LevelPlayerIsIn = Owner->GetLevel();
				}
				else
				{
					// This happens for BSP where the ModelComponent's outer is the level
					LevelPlayerIsIn = Hit.Component->GetTypedOuter<ULevel>();
				}
			}
		}
	}

	// this no longer seems to be getting the correct level name :-(
	LevelPlayerIsInName = LevelPlayerIsIn != NULL ? LevelPlayerIsIn->GetOutermost()->GetName() : TEXT("None");
}
Пример #15
0
bool FEditorBuildUtils::EditorAutomatedBuildAndSubmit( const FEditorAutomatedBuildSettings& BuildSettings, FText& OutErrorMessages )
{
	// Assume the build is successful to start
	bool bBuildSuccessful = true;
	
	// Keep a set of packages that should be submitted to source control at the end of a successful build. The build preparation and processing
	// will add and remove from the set depending on build settings, errors, etc.
	TSet<UPackage*> PackagesToSubmit;

	// Perform required preparations for the automated build process
	bBuildSuccessful = PrepForAutomatedBuild( BuildSettings, PackagesToSubmit, OutErrorMessages );

	// If the preparation went smoothly, attempt the actual map building process
	if ( bBuildSuccessful )
	{
		bBuildSuccessful = EditorBuild( GWorld, EBuildOptions::BuildAllSubmit );

		// If the map build failed, log the error
		if ( !bBuildSuccessful )
		{
			LogErrorMessage( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_BuildFailed", "The map build failed or was canceled."), OutErrorMessages );
		}
	}

	// If any map errors resulted from the build, process them according to the behavior specified in the build settings
	if ( bBuildSuccessful && FMessageLog("MapCheck").NumMessages( EMessageSeverity::Warning ) > 0 )
	{
		bBuildSuccessful = ProcessAutomatedBuildBehavior( BuildSettings.BuildErrorBehavior, NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_MapErrors", "Map errors occurred while building.\n\nAttempt to continue the build?"), OutErrorMessages );
	}

	// If it's still safe to proceed, attempt to save all of the level packages that have been marked for submission
	if ( bBuildSuccessful )
	{
		UPackage* CurOutermostPkg = GWorld->PersistentLevel->GetOutermost();
		FString PackagesThatFailedToSave;

		// Try to save the p-level if it should be submitted
		if ( PackagesToSubmit.Contains( CurOutermostPkg ) && !FEditorFileUtils::SaveLevel( GWorld->PersistentLevel ) )
		{
			// If the p-level failed to save, remove it from the set of packages to submit
			PackagesThatFailedToSave += FString::Printf( TEXT("%s\n"), *CurOutermostPkg->GetName() );
			PackagesToSubmit.Remove( CurOutermostPkg );
		}
		
		// Try to save each streaming level (if they should be submitted)
		for ( TArray<ULevelStreaming*>::TIterator LevelIter( GWorld->StreamingLevels ); LevelIter; ++LevelIter )
		{
			ULevelStreaming* CurStreamingLevel = *LevelIter;
			if ( CurStreamingLevel != NULL )
			{
				ULevel* Level = CurStreamingLevel->GetLoadedLevel();
				if ( Level != NULL )
				{
					CurOutermostPkg = Level->GetOutermost();
					if ( PackagesToSubmit.Contains( CurOutermostPkg ) && !FEditorFileUtils::SaveLevel( Level ) )
					{
						// If a save failed, remove the streaming level from the set of packages to submit
						PackagesThatFailedToSave += FString::Printf( TEXT("%s\n"), *CurOutermostPkg->GetName() );
						PackagesToSubmit.Remove( CurOutermostPkg );
					}
				}
			}
		}

		// If any packages failed to save, process the behavior specified by the build settings to see how the process should proceed
		if ( PackagesThatFailedToSave.Len() > 0 )
		{
			bBuildSuccessful = ProcessAutomatedBuildBehavior( BuildSettings.FailedToSaveBehavior,
				FText::Format( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_FilesFailedSave", "The following assets failed to save and cannot be submitted:\n\n{0}\n\nAttempt to continue the build?"), FText::FromString(PackagesThatFailedToSave) ),
				OutErrorMessages );
		}
	}

	// If still safe to proceed, make sure there are actually packages remaining to submit
	if ( bBuildSuccessful )
	{
		bBuildSuccessful = PackagesToSubmit.Num() > 0;
		if ( !bBuildSuccessful )
		{
			LogErrorMessage( NSLOCTEXT("UnrealEd", "AutomatedBuild_Error_NoValidLevels", "None of the current levels are valid for submission; automated build aborted."), OutErrorMessages );
		}
	}

	// Finally, if everything has gone smoothly, submit the requested packages to source control
	if ( bBuildSuccessful )
	{
		SubmitPackagesForAutomatedBuild( PackagesToSubmit, BuildSettings );
	}

	// Check if the user requested the editor shutdown at the conclusion of the automated build
	if ( BuildSettings.bShutdownEditorOnCompletion )
	{
		FPlatformMisc::RequestExit( false );
	}

	return bBuildSuccessful;
}