You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
633 lines
21 KiB
C++
633 lines
21 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ProjectsPrivatePCH.h"
|
|
#include "EngineVersion.h"
|
|
|
|
|
|
#define LOCTEXT_NAMESPACE "PluginManagerShared"
|
|
|
|
|
|
void FProjectOrPlugin::LoadModules( const ELoadingPhase::Type LoadingPhase, TMap<FName, ELoadModuleFailureReason::Type>& ModuleLoadErrors )
|
|
{
|
|
const FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
|
|
for( TArray< FProjectOrPluginInfo::FModuleInfo >::TConstIterator ModuleInfoIt( ProjectOrPluginInfo.Modules.CreateConstIterator() ); ModuleInfoIt; ++ModuleInfoIt )
|
|
{
|
|
const FProjectOrPluginInfo::FModuleInfo& ModuleInfo = *ModuleInfoIt;
|
|
|
|
// Don't need to do anything if this module is already loaded
|
|
if( !FModuleManager::Get().IsModuleLoaded( ModuleInfo.Name ) )
|
|
{
|
|
if( LoadingPhase == ModuleInfo.LoadingPhase && ShouldLoadModule(ModuleInfo) )
|
|
{
|
|
// @todo plugin: DLL search problems. Plugins that statically depend on other modules within this plugin may not be found? Need to test this.
|
|
|
|
// NOTE: Loading this module may cause other modules to become loaded, both in the engine or game, or other modules
|
|
// that are part of this project or plugin. That's totally fine.
|
|
ELoadModuleFailureReason::Type FailureReason;
|
|
const TSharedPtr<IModuleInterface>& ModuleInterface = FModuleManager::Get().LoadModuleWithFailureReason( ModuleInfo.Name, FailureReason );
|
|
if( ModuleInterface.IsValid() )
|
|
{
|
|
// Module loaded OK (or was already loaded.)
|
|
}
|
|
else
|
|
{
|
|
// The module failed to load. Note this in the ModuleLoadErrors list.
|
|
ModuleLoadErrors.Add(ModuleInfo.Name, FailureReason);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FProjectOrPlugin::LoadFromFile( const FString& FileToLoad, FText& OutFailureReason )
|
|
{
|
|
const FText FileDisplayName = FText::FromString(FileToLoad);
|
|
|
|
FString FileContents;
|
|
if (!FFileHelper::LoadFileToString(FileContents, *FileToLoad))
|
|
{
|
|
// Failed to read project file
|
|
OutFailureReason = FText::Format( LOCTEXT("FailedToLoadDescriptorFile", "Failed to open descriptor file '{0}'"), FileDisplayName );
|
|
return false;
|
|
}
|
|
|
|
if (FileContents.IsEmpty())
|
|
{
|
|
// Empty project file
|
|
OutFailureReason = FText::Format( LOCTEXT("DescriptorFileEmpty", "Descriptor file '{0}' was empty."), FileDisplayName );
|
|
return false;
|
|
}
|
|
|
|
// Serialize the JSON file contents
|
|
FText DeserializeFailReason;
|
|
if (!DeserializeFromJSON(FileContents, DeserializeFailReason))
|
|
{
|
|
OutFailureReason = FText::Format( LOCTEXT("DescriptorDeserializeFailed", "'Descriptor file '{0}' could not be loaded. {1}"), FileDisplayName, DeserializeFailReason );
|
|
return false;
|
|
}
|
|
|
|
FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
ProjectOrPluginInfo.Name = FPaths::GetBaseFilename(FileToLoad);
|
|
ProjectOrPluginInfo.LoadedFromFile = FileToLoad;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FProjectOrPlugin::DeserializeFromJSON( const FString& JSONInput, FText& OutFailReason )
|
|
{
|
|
FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
|
|
TSharedPtr< FJsonObject > FileObjectPtr;
|
|
TSharedRef< TJsonReader<> > Reader = TJsonReaderFactory<>::Create(JSONInput);
|
|
if ( !FJsonSerializer::Deserialize(Reader, FileObjectPtr) || !FileObjectPtr.IsValid() )
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT("FailedToReadDescriptorFile", "Failed to read file. {1}"), FText::FromString(Reader->GetErrorMessage()) );
|
|
return false;
|
|
}
|
|
|
|
TSharedRef< FJsonObject > FileObject = FileObjectPtr.ToSharedRef();
|
|
|
|
bool bSuccessful = true;
|
|
|
|
// FileVersion. This is not optional.
|
|
if ( ReadFileVersionFromJSON(FileObject, ProjectOrPluginInfo.FileVersion) )
|
|
{
|
|
if ( ProjectOrPluginInfo.FileVersion == INDEX_NONE )
|
|
{
|
|
OutFailReason = LOCTEXT("InvalidProjectFileVersion", "File does not have a valid 'FileVersion' number.");
|
|
bSuccessful = false;
|
|
}
|
|
|
|
if ( ProjectOrPluginInfo.FileVersion > VER_LATEST_PROJECT_FILE )
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT("ProjectFileVersionTooLarge", "File appears to be in a newer version ({0}) of the file format that we can load (max version: {1})."),
|
|
FText::FromString( FString::Printf( TEXT( "%d" ), ProjectOrPluginInfo.FileVersion ) ),
|
|
FText::FromString( FString::Printf( TEXT( "%d" ), (int32)VER_LATEST_PROJECT_FILE ) )
|
|
);
|
|
|
|
bSuccessful = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = LOCTEXT("NoProjectFileVersion", "File is missing a 'FileVersion' field. This is required in order to load the file.");
|
|
bSuccessful = false;
|
|
}
|
|
|
|
if ( bSuccessful )
|
|
{
|
|
ReadNumberFromJSON(FileObject, TEXT("Version"), ProjectOrPluginInfo.Version);
|
|
ReadStringFromJSON(FileObject, TEXT("VersionName"), ProjectOrPluginInfo.VersionName);
|
|
|
|
FString EngineVersionString;
|
|
ReadStringFromJSON(FileObject, TEXT("EngineVersion"), EngineVersionString);
|
|
FEngineVersion::Parse(EngineVersionString, ProjectOrPluginInfo.EngineVersion);
|
|
|
|
ReadNumberFromJSON(FileObject, TEXT("PackageFileUE4Version"), ProjectOrPluginInfo.PackageFileUE4Version);
|
|
ReadNumberFromJSON(FileObject, TEXT("PackageFileLicenseeUE4Version"), ProjectOrPluginInfo.PackageFileLicenseeUE4Version);
|
|
ReadStringFromJSON(FileObject, TEXT("EngineAssociation"), ProjectOrPluginInfo.EngineAssociation);
|
|
ReadStringFromJSON(FileObject, TEXT("FriendlyName"), ProjectOrPluginInfo.FriendlyName);
|
|
ReadStringFromJSON(FileObject, TEXT("Description"), ProjectOrPluginInfo.Description);
|
|
|
|
if ( !ReadStringFromJSON(FileObject, TEXT("Category"), ProjectOrPluginInfo.Category) )
|
|
{
|
|
// Category used to be called CategoryPath in .uplugin files
|
|
ReadStringFromJSON(FileObject, TEXT("CategoryPath"), ProjectOrPluginInfo.Category);
|
|
}
|
|
|
|
// Due to a difference in command line parsing between Windows and Mac, we shipped a few Mac samples containing
|
|
// a category name with escaped quotes. Remove them here to make sure we can list them in the right category.
|
|
if(ProjectOrPluginInfo.Category.Len() >= 2 && ProjectOrPluginInfo.Category.StartsWith(TEXT("\"")) && ProjectOrPluginInfo.Category.EndsWith(TEXT("\"")))
|
|
{
|
|
ProjectOrPluginInfo.Category = ProjectOrPluginInfo.Category.Mid(1, ProjectOrPluginInfo.Category.Len() - 2);
|
|
}
|
|
|
|
ReadStringFromJSON(FileObject, TEXT("CreatedBy"), ProjectOrPluginInfo.CreatedBy);
|
|
ReadStringFromJSON(FileObject, TEXT("CreatedByURL"), ProjectOrPluginInfo.CreatedByURL);
|
|
|
|
if ( ReadModulesFromJSON(FileObject, ProjectOrPluginInfo.Modules, OutFailReason) )
|
|
{
|
|
ProjectOrPluginInfo.EarliestModulePhase = FProjectAndPluginManager::GetEarliestPhaseFromModules(ProjectOrPluginInfo.Modules);
|
|
}
|
|
else
|
|
{
|
|
bSuccessful = false;
|
|
}
|
|
}
|
|
|
|
if ( bSuccessful )
|
|
{
|
|
bSuccessful = PerformAdditionalDeserialization(FileObject);
|
|
}
|
|
|
|
return bSuccessful;
|
|
}
|
|
|
|
FString FProjectOrPlugin::SerializeToJSON( ) const
|
|
{
|
|
const FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
|
|
FString JSONOutput;
|
|
TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&JSONOutput);
|
|
|
|
Writer->WriteObjectStart();
|
|
|
|
Writer->WriteValue(TEXT("FileVersion"), (float)ProjectOrPluginInfo.FileVersion);
|
|
Writer->WriteValue(TEXT("Version"), (float)ProjectOrPluginInfo.Version);
|
|
Writer->WriteValue(TEXT("VersionName"), ProjectOrPluginInfo.VersionName);
|
|
Writer->WriteValue(TEXT("EngineVersion"), ProjectOrPluginInfo.EngineVersion.ToString());
|
|
Writer->WriteValue(TEXT("PackageFileUE4Version"), (float)ProjectOrPluginInfo.PackageFileUE4Version);
|
|
Writer->WriteValue(TEXT("PackageFileLicenseeUE4Version"), (float)ProjectOrPluginInfo.PackageFileLicenseeUE4Version);
|
|
Writer->WriteValue(TEXT("EngineAssociation"), ProjectOrPluginInfo.EngineAssociation);
|
|
Writer->WriteValue(TEXT("FriendlyName"), ProjectOrPluginInfo.FriendlyName);
|
|
Writer->WriteValue(TEXT("Description"), ProjectOrPluginInfo.Description);
|
|
Writer->WriteValue(TEXT("Category"), ProjectOrPluginInfo.Category);
|
|
Writer->WriteValue(TEXT("CreatedBy"), ProjectOrPluginInfo.CreatedBy);
|
|
Writer->WriteValue(TEXT("CreatedByURL"), ProjectOrPluginInfo.CreatedByURL);
|
|
|
|
if ( ProjectOrPluginInfo.Modules.Num() > 0 )
|
|
{
|
|
Writer->WriteArrayStart(TEXT("Modules"));
|
|
for ( auto ModuleIt = ProjectOrPluginInfo.Modules.CreateConstIterator(); ModuleIt; ++ModuleIt )
|
|
{
|
|
const FProjectOrPluginInfo::FModuleInfo& Module = *ModuleIt;
|
|
Writer->WriteObjectStart();
|
|
Writer->WriteValue(TEXT("Name"), Module.Name.ToString());
|
|
Writer->WriteValue(TEXT("Type"), FString(FProjectOrPluginInfo::EModuleType::ToString(Module.Type)));
|
|
Writer->WriteValue(TEXT("LoadingPhase"), FString(ELoadingPhase::ToString(Module.LoadingPhase)));
|
|
if (Module.WhitelistPlatforms.Num() > 0)
|
|
{
|
|
Writer->WriteArrayStart(TEXT("WhitelistPlatforms"));
|
|
for ( auto PlatformIt = Module.WhitelistPlatforms.CreateConstIterator(); PlatformIt; ++PlatformIt )
|
|
{
|
|
Writer->WriteValue(*PlatformIt);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
}
|
|
if (Module.BlacklistPlatforms.Num() > 0)
|
|
{
|
|
Writer->WriteArrayStart(TEXT("BlacklistPlatforms"));
|
|
for ( auto PlatformIt = Module.BlacklistPlatforms.CreateConstIterator(); PlatformIt; ++PlatformIt )
|
|
{
|
|
Writer->WriteValue(*PlatformIt);
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
}
|
|
Writer->WriteObjectEnd();
|
|
}
|
|
Writer->WriteArrayEnd();
|
|
}
|
|
|
|
PerformAdditionalSerialization(Writer);
|
|
|
|
Writer->WriteObjectEnd();
|
|
Writer->Close();
|
|
|
|
return JSONOutput;
|
|
}
|
|
|
|
bool FProjectOrPlugin::RequiresUpdate( ) const
|
|
{
|
|
const FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
return (ProjectOrPluginInfo.FileVersion < VER_LATEST_PROJECT_FILE);
|
|
}
|
|
|
|
bool FProjectOrPlugin::AreModulesUpToDate( ) const
|
|
{
|
|
const FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
for (TArray< FProjectOrPluginInfo::FModuleInfo >::TConstIterator Iter(ProjectOrPluginInfo.Modules); Iter; ++Iter)
|
|
{
|
|
const FProjectOrPluginInfo::FModuleInfo &ModuleInfo = *Iter;
|
|
if (ShouldBuildModule(ModuleInfo) && !FModuleManager::Get().IsModuleUpToDate(ModuleInfo.Name))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FProjectOrPlugin::UpdateVersionToCurrent( const FString &EngineIdentifier )
|
|
{
|
|
FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
|
|
ProjectOrPluginInfo.FileVersion = VER_LATEST_PROJECT_FILE;
|
|
ProjectOrPluginInfo.EngineVersion = GEngineVersion;
|
|
ProjectOrPluginInfo.PackageFileUE4Version = GPackageFileUE4Version;
|
|
ProjectOrPluginInfo.PackageFileLicenseeUE4Version = GPackageFileLicenseeUE4Version;
|
|
ProjectOrPluginInfo.EngineAssociation = EngineIdentifier;
|
|
}
|
|
|
|
void FProjectOrPlugin::ReplaceModulesInProject(const TArray<FString>* StartupModuleNames)
|
|
{
|
|
if ( StartupModuleNames )
|
|
{
|
|
FProjectOrPluginInfo& ProjectOrPluginInfo = GetProjectOrPluginInfo();
|
|
|
|
ProjectOrPluginInfo.Modules.Empty();
|
|
for ( auto ModuleIt = StartupModuleNames->CreateConstIterator(); ModuleIt; ++ModuleIt )
|
|
{
|
|
FProjectOrPluginInfo::FModuleInfo ModuleInfo;
|
|
ModuleInfo.Name = FName(**ModuleIt);
|
|
ProjectOrPluginInfo.Modules.Add(ModuleInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FProjectOrPlugin::ShouldBuildModule(const FProjectOrPluginInfo::FModuleInfo& ModuleInfo) const
|
|
{
|
|
// Cache the string for the current platform
|
|
static FString UBTPlatform(FPlatformMisc::GetUBTPlatform());
|
|
|
|
// Check the platform is whitelisted
|
|
if(ModuleInfo.WhitelistPlatforms.Num() > 0 && !ModuleInfo.WhitelistPlatforms.Contains(UBTPlatform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the platform is not blacklisted
|
|
if(ModuleInfo.BlacklistPlatforms.Num() > 0 && ModuleInfo.BlacklistPlatforms.Contains(UBTPlatform))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check the module is compatible with this target. This should match UEBuildTarget.ShouldIncludePluginModule in UBT
|
|
switch (ModuleInfo.Type)
|
|
{
|
|
case FProjectOrPluginInfo::EModuleType::Runtime:
|
|
case FProjectOrPluginInfo::EModuleType::RuntimeNoCommandlet:
|
|
return true;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::Developer:
|
|
#if WITH_UNREAL_DEVELOPER_TOOLS
|
|
return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::Editor:
|
|
case FProjectOrPluginInfo::EModuleType::EditorNoCommandlet:
|
|
#if WITH_EDITOR
|
|
return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::Program:
|
|
#if IS_PROGRAM
|
|
return true;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ShouldLoadModule(const FProjectOrPluginInfo::FModuleInfo& ModuleInfo) const
|
|
{
|
|
// Check that the module is built for this configuration
|
|
if(!ShouldBuildModule(ModuleInfo))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check that the runtime environment allows it to be loaded
|
|
switch (ModuleInfo.Type)
|
|
{
|
|
case FProjectOrPluginInfo::EModuleType::Runtime:
|
|
#if WITH_ENGINE || WITH_PLUGIN_SUPPORT
|
|
return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::RuntimeNoCommandlet:
|
|
#if WITH_ENGINE || WITH_PLUGIN_SUPPORT
|
|
if(!IsRunningCommandlet()) return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::Developer:
|
|
#if WITH_UNREAL_DEVELOPER_TOOLS
|
|
return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::Editor:
|
|
#if WITH_EDITOR
|
|
if(GIsEditor) return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::EditorNoCommandlet:
|
|
#if WITH_EDITOR
|
|
if(GIsEditor && !IsRunningCommandlet()) return true;
|
|
#endif
|
|
break;
|
|
|
|
case FProjectOrPluginInfo::EModuleType::Program:
|
|
#if WITH_PLUGIN_SUPPORT && IS_PROGRAM
|
|
return true;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadNumberFromJSON(const TSharedRef< FJsonObject >& FileObject, const FString& PropertyName, int32& OutNumber ) const
|
|
{
|
|
int64 LargeNumber;
|
|
if(ReadNumberFromJSON(FileObject, PropertyName, LargeNumber))
|
|
{
|
|
OutNumber = (int32)FMath::Clamp<int64>(LargeNumber, INT_MIN, INT_MAX);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadNumberFromJSON(const TSharedRef< FJsonObject >& FileObject, const FString& PropertyName, uint32& OutNumber ) const
|
|
{
|
|
int64 LargeNumber;
|
|
if(ReadNumberFromJSON(FileObject, PropertyName, LargeNumber))
|
|
{
|
|
OutNumber = (uint32)FMath::Clamp<int64>(LargeNumber, 0, UINT_MAX);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadNumberFromJSON(const TSharedRef< FJsonObject >& FileObject, const FString& PropertyName, int64& OutNumber ) const
|
|
{
|
|
if ( FileObject->HasTypedField<EJson::Number>(PropertyName) )
|
|
{
|
|
TSharedPtr<FJsonValue> NumberValue = FileObject->GetField<EJson::Number>(PropertyName);
|
|
if ( NumberValue.IsValid() )
|
|
{
|
|
OutNumber = (int64)( NumberValue->AsNumber() + 0.5 );
|
|
return true;
|
|
}
|
|
}
|
|
// We are tolerant to number fields with quotes around them
|
|
else if ( FileObject->HasTypedField<EJson::String>(PropertyName) )
|
|
{
|
|
TSharedPtr<FJsonValue> StringValue = FileObject->GetField<EJson::String>(PropertyName);
|
|
if ( StringValue.IsValid() )
|
|
{
|
|
OutNumber = FCString::Atoi64( *StringValue->AsString() );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadStringFromJSON(const TSharedRef< FJsonObject >& FileObject, const FString& PropertyName, FString& OutString ) const
|
|
{
|
|
if ( FileObject->HasTypedField<EJson::String>(PropertyName) )
|
|
{
|
|
TSharedPtr<FJsonValue> StringValue = FileObject->GetField<EJson::String>(PropertyName);
|
|
if ( StringValue.IsValid() )
|
|
{
|
|
OutString = StringValue->AsString();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadBoolFromJSON(const TSharedRef< FJsonObject >& FileObject, const FString& PropertyName, bool& OutBool ) const
|
|
{
|
|
if ( FileObject->HasTypedField<EJson::Boolean>(PropertyName) )
|
|
{
|
|
TSharedPtr<FJsonValue> BoolValue = FileObject->GetField<EJson::Boolean>(PropertyName);
|
|
if ( BoolValue.IsValid() )
|
|
{
|
|
OutBool = BoolValue->AsBool();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadFileVersionFromJSON(const TSharedRef< FJsonObject >& FileObject, int32& OutVersion ) const
|
|
{
|
|
// Modern file version
|
|
if ( ReadNumberFromJSON(FileObject, TEXT("FileVersion"), OutVersion) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Back compat with old project files
|
|
if ( ReadNumberFromJSON(FileObject, TEXT("ProjectFileVersion"), OutVersion) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Back compat with old plugin files
|
|
if ( ReadNumberFromJSON(FileObject, TEXT("PluginFileVersion"), OutVersion) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FProjectOrPlugin::ReadModulesFromJSON(const TSharedRef< FJsonObject >& FileObject, TArray<FProjectOrPluginInfo::FModuleInfo>& OutModules, FText& OutFailReason ) const
|
|
{
|
|
bool bLoadedSuccessfully = true;
|
|
|
|
// This project or plugin might have some modules that we need to know about. Let's take a look.
|
|
if( FileObject->HasField( TEXT( "Modules" ) ) )
|
|
{
|
|
const TSharedPtr< FJsonValue >& ModulesValue = FileObject->GetField< EJson::Array >( TEXT( "Modules" ) );
|
|
const TArray< TSharedPtr< FJsonValue > >& ModulesArray = ModulesValue->AsArray();
|
|
for( auto ModuleValueIt( ModulesArray.CreateConstIterator() ); ModuleValueIt; ++ModuleValueIt )
|
|
{
|
|
const TSharedPtr< FJsonObject >& ModuleObjectPtr = ( *ModuleValueIt )->AsObject();
|
|
if( ModuleObjectPtr.IsValid() )
|
|
{
|
|
const TSharedRef< FJsonObject >& ModuleObject = ModuleObjectPtr.ToSharedRef();
|
|
FProjectOrPluginInfo::FModuleInfo ModuleInfo;
|
|
|
|
// Module name
|
|
{
|
|
// All modules require a name to be set
|
|
FString ModuleName;
|
|
if ( ReadStringFromJSON(ModuleObject, TEXT("Name"), ModuleName) )
|
|
{
|
|
ModuleInfo.Name = FName(*ModuleName);
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = LOCTEXT("ModuleWithoutAName", "Found a 'Module' entry with a missing 'Name' field");
|
|
bLoadedSuccessfully = false;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
// Module type
|
|
{
|
|
FString ModuleType;
|
|
if ( ReadStringFromJSON(ModuleObject, TEXT("Type"), ModuleType) )
|
|
{
|
|
// Check to see if this is a valid type
|
|
bool bFoundValidType = false;
|
|
for( int32 PossibleTypeIndex = 0; PossibleTypeIndex < FProjectOrPluginInfo::EModuleType::Max; ++PossibleTypeIndex )
|
|
{
|
|
const FProjectOrPluginInfo::EModuleType::Type PossibleType = (FProjectOrPluginInfo::EModuleType::Type)PossibleTypeIndex;
|
|
const TCHAR* PossibleTypeName = FProjectOrPluginInfo::EModuleType::ToString( PossibleType );
|
|
|
|
if( FCString::Stricmp( PossibleTypeName, *ModuleType ) == 0 )
|
|
{
|
|
bFoundValidType = true;
|
|
ModuleInfo.Type = PossibleType;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bFoundValidType )
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT( "ModuleWithInvalidType", "Module entry '{0}' specified an unrecognized module Type '{1}'" ), FText::FromName(ModuleInfo.Name), FText::FromString(ModuleType) );
|
|
bLoadedSuccessfully = false;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT( "ModuleWithoutAType", "Found Module entry '{0}' with a missing 'Type' field" ), FText::FromName(ModuleInfo.Name) );
|
|
bLoadedSuccessfully = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Loading phase
|
|
{
|
|
FString ModuleLoadingPhase;
|
|
if ( ReadStringFromJSON(ModuleObject, TEXT("LoadingPhase"), ModuleLoadingPhase) )
|
|
{
|
|
// Check to see if this is a valid Phase
|
|
bool bFoundValidPhase = false;
|
|
for( int32 PossiblePhaseIndex = 0; PossiblePhaseIndex < ELoadingPhase::Max; ++PossiblePhaseIndex )
|
|
{
|
|
const ELoadingPhase::Type PossiblePhase = (ELoadingPhase::Type)PossiblePhaseIndex;
|
|
const TCHAR* PossiblePhaseName = ELoadingPhase::ToString( PossiblePhase );
|
|
|
|
if( FCString::Stricmp( PossiblePhaseName, *ModuleLoadingPhase ) == 0 )
|
|
{
|
|
bFoundValidPhase = true;
|
|
ModuleInfo.LoadingPhase = PossiblePhase;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !bFoundValidPhase )
|
|
{
|
|
OutFailReason = FText::Format( LOCTEXT( "ModuleWithInvalidLoadingPhase", "Module entry '{0}' specified an unrecognized module LoadingPhase '{1}'" ), FText::FromName(ModuleInfo.Name), FText::FromString(ModuleLoadingPhase) );
|
|
bLoadedSuccessfully = false;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No 'LoadingPhase' was specified in the file. That's totally fine, we'll use the default.
|
|
}
|
|
}
|
|
|
|
// Whitelist platforms
|
|
{
|
|
if( ModuleObject->HasField( TEXT( "WhitelistPlatforms" ) ) )
|
|
{
|
|
// walk over the array values
|
|
const TSharedPtr< FJsonValue >& PlatformsValue = ModuleObject->GetField< EJson::Array >( TEXT( "WhitelistPlatforms" ) );
|
|
const TArray< TSharedPtr< FJsonValue > >& PlatformsArray = PlatformsValue->AsArray();
|
|
for( auto PlatformValueIt( PlatformsArray.CreateConstIterator() ); PlatformValueIt; ++PlatformValueIt )
|
|
{
|
|
ModuleInfo.WhitelistPlatforms.Add((*PlatformValueIt)->AsString());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Blacklist platforms
|
|
{
|
|
if( ModuleObject->HasField( TEXT( "BlacklistPlatforms" ) ) )
|
|
{
|
|
// walk over the array values
|
|
const TSharedPtr< FJsonValue >& PlatformsValue = ModuleObject->GetField< EJson::Array >( TEXT( "BlacklistPlatforms" ) );
|
|
const TArray< TSharedPtr< FJsonValue > >& PlatformsArray = PlatformsValue->AsArray();
|
|
for( auto PlatformValueIt( PlatformsArray.CreateConstIterator() ); PlatformValueIt; ++PlatformValueIt )
|
|
{
|
|
ModuleInfo.BlacklistPlatforms.Add((*PlatformValueIt)->AsString());
|
|
}
|
|
}
|
|
}
|
|
|
|
OutModules.Add(ModuleInfo);
|
|
}
|
|
else
|
|
{
|
|
OutFailReason = LOCTEXT( "ModuleWithInvalidModulesArray", "The 'Modules' array has invalid contents and was not able to be loaded." );
|
|
bLoadedSuccessfully = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bLoadedSuccessfully;
|
|
}
|
|
|
|
|
|
|
|
ELoadingPhase::Type FProjectAndPluginManager::GetEarliestPhaseFromModules(const TArray<FProjectOrPluginInfo::FModuleInfo>& Modules)
|
|
{
|
|
ELoadingPhase::Type EarliestPhase = ELoadingPhase::Default;
|
|
|
|
for ( auto ModuleIt = Modules.CreateConstIterator(); ModuleIt; ++ModuleIt )
|
|
{
|
|
EarliestPhase = ELoadingPhase::GetEarlierPhase(EarliestPhase, (*ModuleIt).LoadingPhase);
|
|
}
|
|
|
|
return EarliestPhase;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|