You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
TTP# 337136 - SETTINGS: Target Platform settings polish
TTP# 337652 - EDITOR: Limit Project supported Android icons down to 1
TTP# 337650 - EDITOR: There is only 1 icon for Apple for Project Supported Platforms
DesktopPlatform now contains a static array of FPlatformInfo. This can be used to query UE4 about its available platforms, even when they're not available as a target platform.
FPlatformInfo contains the information required by the editor (such as a localized display name and icon), as well as whether a platform is a variation ("flavor") of another, and if so, whether the flavor affects the build output (eg, Win32 or Win64), or the cook output (eg, Android_XYZ). This lets the editor build up nested menus for the "Package Project" and "Cook Project" options, rather than just showing everything as a flat list.
ReviewedBy Thomas.Sarkanen, Max.Preussner
[CL 2095796 by Jamie Dale in Main branch]
419 lines
14 KiB
C++
419 lines
14 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ProjectsPrivatePCH.h"
|
|
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC( LogProjectManager, Log, All );
|
|
|
|
#define LOCTEXT_NAMESPACE "ProjectManager"
|
|
|
|
|
|
FProject::FProject()
|
|
{
|
|
}
|
|
|
|
FProject::FProject( const FProjectInfo& InitProjectInfo )
|
|
: ProjectInfo(InitProjectInfo)
|
|
{
|
|
}
|
|
|
|
bool FProject::IsSignedSampleProject(const FString& FilePath) const
|
|
{
|
|
return ProjectInfo.EpicSampleNameHash == GetTypeHash(FPaths::GetCleanFilename(FilePath));
|
|
}
|
|
|
|
void FProject::SignSampleProject(const FString& FilePath, const FString& Category)
|
|
{
|
|
ProjectInfo.EpicSampleNameHash = GetTypeHash(FPaths::GetCleanFilename(FilePath));
|
|
ProjectInfo.Category = Category;
|
|
}
|
|
|
|
void FProject::UpdateSupportedTargetPlatforms(const FName& InPlatformName, const bool bIsSupported)
|
|
{
|
|
if ( bIsSupported )
|
|
{
|
|
ProjectInfo.TargetPlatforms.AddUnique(InPlatformName);
|
|
}
|
|
else
|
|
{
|
|
ProjectInfo.TargetPlatforms.Remove(InPlatformName);
|
|
}
|
|
}
|
|
|
|
void FProject::ClearSupportedTargetPlatforms()
|
|
{
|
|
ProjectInfo.TargetPlatforms.Empty();
|
|
}
|
|
|
|
bool FProject::PerformAdditionalDeserialization(const TSharedRef< FJsonObject >& FileObject)
|
|
{
|
|
ReadNumberFromJSON(FileObject, TEXT("EpicSampleNameHash"), ProjectInfo.EpicSampleNameHash);
|
|
|
|
ProjectInfo.TargetPlatforms.Empty();
|
|
if ( FileObject->HasField(TEXT("TargetPlatforms")) )
|
|
{
|
|
const TSharedPtr<FJsonValue>& TargetPlatformsValue = FileObject->GetField<EJson::Array>(TEXT("TargetPlatforms"));
|
|
const TArray<TSharedPtr<FJsonValue>>& TargetPlatformsArray = TargetPlatformsValue->AsArray();
|
|
for ( const auto& TargetPlatformsEntry : TargetPlatformsArray )
|
|
{
|
|
const FString PlatformName = TargetPlatformsEntry->AsString();
|
|
ProjectInfo.TargetPlatforms.Add(*PlatformName);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FProject::PerformAdditionalSerialization(const TSharedRef< TJsonWriter<> >& Writer) const
|
|
{
|
|
Writer->WriteValue(TEXT("EpicSampleNameHash"), FString::Printf(TEXT("%u"), ProjectInfo.EpicSampleNameHash));
|
|
|
|
if ( ProjectInfo.TargetPlatforms.Num() > 0 )
|
|
{
|
|
Writer->WriteArrayStart(TEXT("TargetPlatforms"));
|
|
for ( const FName& PlatformName : ProjectInfo.TargetPlatforms )
|
|
{
|
|
Writer->WriteValue(PlatformName.ToString());
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FProjectManager::FProjectManager()
|
|
{
|
|
|
|
}
|
|
|
|
bool FProjectManager::LoadProjectFile( const FString& InProjectFile )
|
|
{
|
|
FText FailureReason;
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject() );
|
|
if ( NewProject->LoadFromFile(InProjectFile, FailureReason) )
|
|
{
|
|
// Load successful. Set the loaded project file pointer.
|
|
CurrentlyLoadedProject = NewProject;
|
|
|
|
return true;
|
|
}
|
|
|
|
#if PLATFORM_IOS
|
|
FString UpdatedMessage = FString::Printf(TEXT("%s\n%s"), *FailureReason.ToString(), TEXT("For troubleshooting, please go to https://docs.unrealengine.com/latest/INT/Platforms/iOS/GettingStarted/index.html"));
|
|
FailureReason = FText::FromString(UpdatedMessage);
|
|
#endif
|
|
UE_LOG(LogProjectManager, Error, TEXT("%s"), *FailureReason.ToString());
|
|
FMessageDialog::Open(EAppMsgType::Ok, FailureReason);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool FProjectManager::LoadModulesForProject( const ELoadingPhase::Type LoadingPhase )
|
|
{
|
|
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Loading Game Modules"), STAT_GameModule, STATGROUP_LoadTime);
|
|
|
|
bool bSuccess = true;
|
|
|
|
if ( CurrentlyLoadedProject.IsValid() )
|
|
{
|
|
TMap<FName, ELoadModuleFailureReason::Type> ModuleLoadFailures;
|
|
CurrentlyLoadedProject->LoadModules(LoadingPhase, ModuleLoadFailures);
|
|
|
|
if ( ModuleLoadFailures.Num() > 0 )
|
|
{
|
|
FText FailureMessage;
|
|
for ( auto FailureIt = ModuleLoadFailures.CreateConstIterator(); FailureIt; ++FailureIt )
|
|
{
|
|
const ELoadModuleFailureReason::Type FailureReason = FailureIt.Value();
|
|
|
|
if( FailureReason != ELoadModuleFailureReason::Success )
|
|
{
|
|
const FText TextModuleName = FText::FromName(FailureIt.Key());
|
|
|
|
if ( FailureReason == ELoadModuleFailureReason::FileNotFound )
|
|
{
|
|
FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleNotFound", "The game module '{0}' could not be found. Please ensure that this module exists and that it is compiled."), TextModuleName );
|
|
}
|
|
else if ( FailureReason == ELoadModuleFailureReason::FileIncompatible )
|
|
{
|
|
FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleIncompatible", "The game module '{0}' does not appear to be up to date. This may happen after updating the engine. Please recompile this module and try again."), TextModuleName );
|
|
}
|
|
else if ( FailureReason == ELoadModuleFailureReason::FailedToInitialize )
|
|
{
|
|
FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleFailedToInitialize", "The game module '{0}' could not be successfully initialized after it was loaded."), TextModuleName );
|
|
}
|
|
else if ( FailureReason == ELoadModuleFailureReason::CouldNotBeLoadedByOS )
|
|
{
|
|
FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleCouldntBeLoaded", "The game module '{0}' could not be loaded. There may be an operating system error or the module may not be properly set up."), TextModuleName );
|
|
}
|
|
else
|
|
{
|
|
ensure(0); // If this goes off, the error handling code should be updated for the new enum values!
|
|
FailureMessage = FText::Format( LOCTEXT("PrimaryGameModuleGenericLoadFailure", "The game module '{0}' failed to load for an unspecified reason. Please report this error."), TextModuleName );
|
|
}
|
|
|
|
// Just report the first error
|
|
break;
|
|
}
|
|
}
|
|
|
|
FMessageDialog::Open(EAppMsgType::Ok, FailureMessage);
|
|
bSuccess = false;
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
bool FProjectManager::AreProjectModulesUpToDate()
|
|
{
|
|
return !CurrentlyLoadedProject.IsValid() || CurrentlyLoadedProject->AreModulesUpToDate();
|
|
}
|
|
|
|
const FString& FProjectManager::GetAutoLoadProjectFileName()
|
|
{
|
|
static FString RecentProjectFileName = FPaths::Combine(*FPaths::GameAgnosticSavedDir(), TEXT("AutoLoadProject.txt"));
|
|
return RecentProjectFileName;
|
|
}
|
|
|
|
const FString& FProjectManager::NonStaticGetProjectFileExtension()
|
|
{
|
|
static FString GameProjectFileExtension(TEXT("uproject"));
|
|
return GameProjectFileExtension;
|
|
}
|
|
|
|
bool FProjectManager::GenerateNewProjectFile(const FString& NewProjectFilename, const TArray<FString>& StartupModuleNames, const FString& EngineIdentifier, FText& OutFailReason)
|
|
{
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject() );
|
|
NewProject->UpdateVersionToCurrent(EngineIdentifier);
|
|
NewProject->ReplaceModulesInProject(&StartupModuleNames);
|
|
|
|
const FString& FileContents = NewProject->SerializeToJSON();
|
|
if ( FFileHelper::SaveStringToFile(FileContents, *NewProjectFilename) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT("FailedToWriteOutputFile", "Failed to write output file '{0}'. Perhaps the file is Read-Only?"), FText::FromString(NewProjectFilename) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FProjectManager::DuplicateProjectFile(const FString& SourceProjectFilename, const FString& NewProjectFilename, const FString& EngineIdentifier, FText& OutFailReason)
|
|
{
|
|
// Load the source project
|
|
TSharedRef<FProject> SourceProject = MakeShareable( new FProject() );
|
|
if ( !SourceProject->LoadFromFile(SourceProjectFilename, OutFailReason) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Duplicate the project info
|
|
FProjectInfo ProjectInfo = SourceProject->GetProjectInfo();
|
|
|
|
// Clear the sample hash
|
|
ProjectInfo.EpicSampleNameHash = 0;
|
|
|
|
// Fix up module names
|
|
const FString BaseSourceName = FPaths::GetBaseFilename(SourceProjectFilename);
|
|
const FString BaseNewName = FPaths::GetBaseFilename(NewProjectFilename);
|
|
for ( auto ModuleIt = ProjectInfo.Modules.CreateIterator(); ModuleIt; ++ModuleIt )
|
|
{
|
|
FProjectOrPluginInfo::FModuleInfo& ModuleInfo = *ModuleIt;
|
|
ModuleInfo.Name = FName(*ModuleInfo.Name.ToString().Replace(*BaseSourceName, *BaseNewName));
|
|
}
|
|
|
|
// Create new project, update version numbers (no need to replace modules here)
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject(ProjectInfo) );
|
|
NewProject->UpdateVersionToCurrent(EngineIdentifier);
|
|
|
|
// Serialize and write to disk
|
|
const FString& FileContents = NewProject->SerializeToJSON();
|
|
if ( FFileHelper::SaveStringToFile(FileContents, *NewProjectFilename) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT("FailedToWriteOutputFile", "Failed to write output file '{0}'. Perhaps the file is Read-Only?"), FText::FromString(NewProjectFilename) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FProjectManager::UpdateLoadedProjectFileToCurrent(const TArray<FString>* StartupModuleNames, const FString& EngineIdentifier, FText& OutFailReason)
|
|
{
|
|
if ( !CurrentlyLoadedProject.IsValid() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Freshen version information
|
|
CurrentlyLoadedProject->UpdateVersionToCurrent(EngineIdentifier);
|
|
|
|
// Replace the modules names, if specified
|
|
CurrentlyLoadedProject->ReplaceModulesInProject(StartupModuleNames);
|
|
|
|
// Update file on disk
|
|
const FString& FileContents = CurrentlyLoadedProject->SerializeToJSON();
|
|
if ( FFileHelper::SaveStringToFile(FileContents, *FPaths::GetProjectFilePath()) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// We failed to generate the file. Could be read only.
|
|
OutFailReason = FText::Format( LOCTEXT("FailedToWriteOutputFile", "Failed to write output file '{0}'. Perhaps the file is Read-Only?"), FText::FromString(FPaths::GetProjectFilePath()) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FProjectManager::SignSampleProject(const FString& FilePath, const FString& Category, FText& OutFailReason)
|
|
{
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject() );
|
|
if ( !NewProject->LoadFromFile(FilePath, OutFailReason) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
NewProject->SignSampleProject(FilePath, Category);
|
|
|
|
const FString& FileContents = NewProject->SerializeToJSON();
|
|
if (FFileHelper::SaveStringToFile(FileContents, *FilePath))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT("FailedToSaveSignedProject", "Failed to save signed project file {0}"), FText::FromString(FilePath) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool FProjectManager::QueryStatusForProject(const FString& FilePath, FProjectStatus& OutProjectStatus) const
|
|
{
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject() );
|
|
FText FailReason;
|
|
if ( !NewProject->LoadFromFile(FilePath, FailReason) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QueryStatusForProjectImpl(*NewProject, FilePath, OutProjectStatus);
|
|
return true;
|
|
}
|
|
|
|
bool FProjectManager::QueryStatusForCurrentProject(FProjectStatus& OutProjectStatus) const
|
|
{
|
|
if ( !CurrentlyLoadedProject.IsValid() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QueryStatusForProjectImpl(*CurrentlyLoadedProject, FPaths::GetProjectFilePath(), OutProjectStatus);
|
|
return true;
|
|
}
|
|
|
|
void FProjectManager::QueryStatusForProjectImpl(const FProject& Project, const FString& FilePath, FProjectStatus& OutProjectStatus)
|
|
{
|
|
const FProjectInfo& ProjectInfo = Project.GetProjectInfo();
|
|
OutProjectStatus.Name = ProjectInfo.Name;
|
|
OutProjectStatus.Description = ProjectInfo.Description;
|
|
OutProjectStatus.Category = ProjectInfo.Category;
|
|
OutProjectStatus.bCodeBasedProject = ProjectInfo.Modules.Num() > 0;
|
|
OutProjectStatus.bSignedSampleProject = Project.IsSignedSampleProject(FilePath);
|
|
OutProjectStatus.bRequiresUpdate = Project.RequiresUpdate();
|
|
OutProjectStatus.TargetPlatforms = ProjectInfo.TargetPlatforms;
|
|
}
|
|
|
|
void FProjectManager::UpdateSupportedTargetPlatformsForProject(const FString& FilePath, const FName& InPlatformName, const bool bIsSupported)
|
|
{
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject() );
|
|
FText FailReason;
|
|
if ( !NewProject->LoadFromFile(FilePath, FailReason) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
NewProject->UpdateSupportedTargetPlatforms(InPlatformName, bIsSupported);
|
|
|
|
const FString& FileContents = NewProject->SerializeToJSON();
|
|
FFileHelper::SaveStringToFile(FileContents, *FilePath);
|
|
|
|
// Call OnTargetPlatformsForCurrentProjectChangedEvent if this project is the same as the one we currently have loaded
|
|
const FString CurrentProjectPath = FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath());
|
|
const FString InProjectPath = FPaths::ConvertRelativePathToFull(FilePath);
|
|
if ( CurrentProjectPath == InProjectPath )
|
|
{
|
|
OnTargetPlatformsForCurrentProjectChangedEvent.Broadcast();
|
|
}
|
|
}
|
|
|
|
void FProjectManager::UpdateSupportedTargetPlatformsForCurrentProject(const FName& InPlatformName, const bool bIsSupported)
|
|
{
|
|
if ( !CurrentlyLoadedProject.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
CurrentlyLoadedProject->UpdateSupportedTargetPlatforms(InPlatformName, bIsSupported);
|
|
|
|
const FString& FileContents = CurrentlyLoadedProject->SerializeToJSON();
|
|
FFileHelper::SaveStringToFile(FileContents, *FPaths::GetProjectFilePath());
|
|
|
|
OnTargetPlatformsForCurrentProjectChangedEvent.Broadcast();
|
|
}
|
|
|
|
void FProjectManager::ClearSupportedTargetPlatformsForProject(const FString& FilePath)
|
|
{
|
|
TSharedRef<FProject> NewProject = MakeShareable( new FProject() );
|
|
FText FailReason;
|
|
if ( !NewProject->LoadFromFile(FilePath, FailReason) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
NewProject->ClearSupportedTargetPlatforms();
|
|
|
|
const FString& FileContents = NewProject->SerializeToJSON();
|
|
FFileHelper::SaveStringToFile(FileContents, *FilePath);
|
|
|
|
// Call OnTargetPlatformsForCurrentProjectChangedEvent if this project is the same as the one we currently have loaded
|
|
const FString CurrentProjectPath = FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath());
|
|
const FString InProjectPath = FPaths::ConvertRelativePathToFull(FilePath);
|
|
if ( CurrentProjectPath == InProjectPath )
|
|
{
|
|
OnTargetPlatformsForCurrentProjectChangedEvent.Broadcast();
|
|
}
|
|
}
|
|
|
|
void FProjectManager::ClearSupportedTargetPlatformsForCurrentProject()
|
|
{
|
|
if ( !CurrentlyLoadedProject.IsValid() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
CurrentlyLoadedProject->ClearSupportedTargetPlatforms();
|
|
|
|
const FString& FileContents = CurrentlyLoadedProject->SerializeToJSON();
|
|
FFileHelper::SaveStringToFile(FileContents, *FPaths::GetProjectFilePath());
|
|
|
|
OnTargetPlatformsForCurrentProjectChangedEvent.Broadcast();
|
|
}
|
|
|
|
IProjectManager& IProjectManager::Get()
|
|
{
|
|
// Single instance of manager, allocated on demand and destroyed on program exit.
|
|
static FProjectManager* ProjectManager = NULL;
|
|
if( ProjectManager == NULL )
|
|
{
|
|
ProjectManager = new FProjectManager();
|
|
}
|
|
return *ProjectManager;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |