// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; namespace UnrealBuildTool { /// /// The version format for .uplugin files. This rarely changes now; plugin descriptors should maintain backwards compatibility automatically. /// public enum PluginDescriptorVersion { /// /// Invalid /// Invalid = 0, /// /// Initial version /// Initial = 1, /// /// Adding SampleNameHash /// NameHash = 2, /// /// Unifying plugin/project files (since abandoned, but backwards compatibility maintained) /// ProjectPluginUnification = 3, /// /// This needs to be the last line, so we can calculate the value of Latest below /// LatestPlusOne, /// /// The latest plugin descriptor version /// Latest = LatestPlusOne - 1 } /// /// In-memory representation of a .uplugin file /// public class PluginDescriptor { /// /// Descriptor version number /// public int FileVersion; /// /// Version number for the plugin. The version number must increase with every version of the plugin, so that the system /// can determine whether one version of a plugin is newer than another, or to enforce other requirements. This version /// number is not displayed in front-facing UI. Use the VersionName for that. /// public int Version; /// /// Name of the version for this plugin. This is the front-facing part of the version number. It doesn't need to match /// the version number numerically, but should be updated when the version number is increased accordingly. /// public string VersionName; /// /// Friendly name of the plugin /// public string FriendlyName; /// /// Description of the plugin /// public string Description; /// /// The name of the category this plugin /// public string Category; /// /// The company or individual who created this plugin. This is an optional field that may be displayed in the user interface. /// public string CreatedBy; /// /// Hyperlink URL string for the company or individual who created this plugin. This is optional. /// public string CreatedByURL; /// /// Documentation URL string. /// public string DocsURL; /// /// Marketplace URL for this plugin. This URL will be embedded into projects that enable this plugin, so we can redirect to the marketplace if a user doesn't have it installed. /// public string MarketplaceURL; /// /// Support URL/email for this plugin. /// public string SupportURL; /// /// Sets the version of the engine that this plugin is compatible with. /// public string EngineVersion; /// /// For packaged plugins, contains the changelist that this plugin is compatible with /// public int CompatibleChangelist; /// /// List of all modules associated with this plugin /// public ModuleDescriptor[] Modules; /// /// List of all localization targets associated with this plugin /// public LocalizationTargetDescriptor[] LocalizationTargets; /// /// Whether this plugin should be enabled by default for all projects /// public bool bEnabledByDefault; /// /// Can this plugin contain content? /// public bool bCanContainContent; /// /// Marks the plugin as beta in the UI /// public bool bIsBetaVersion; /// /// Whether this plugin is a mod /// public bool bIsMod; /// /// Whether this plugin can be used by UnrealHeaderTool /// public bool bCanBeUsedWithUnrealHeaderTool; /// /// Set for plugins which are installed /// public bool bInstalled; /// /// For plugins that are under a platform folder (eg. /PS4/), determines whether compiling the plugin requires the build platform and/or SDK to be available /// public bool bRequiresBuildPlatform; /// /// Set of pre-build steps to execute, keyed by host platform name. /// public CustomBuildSteps PreBuildSteps; /// /// Set of post-build steps to execute, keyed by host platform name. /// public CustomBuildSteps PostBuildSteps; /// /// Private constructor. This object should not be created directly; read it from disk using FromFile() instead. /// private PluginDescriptor() { FileVersion = (int)PluginDescriptorVersion.Latest; bRequiresBuildPlatform = true; } /// /// Creates a plugin descriptor from a file on disk /// /// The filename to read /// Whether this plugin should be enabled by default based on its location /// New plugin descriptor public static PluginDescriptor FromFile(FileReference FileName, bool bPluginTypeEnabledByDefault) { JsonObject RawObject = JsonObject.Read(FileName.FullName); try { PluginDescriptor Descriptor = new PluginDescriptor(); // Read the version if (!RawObject.TryGetIntegerField("FileVersion", out Descriptor.FileVersion)) { if (!RawObject.TryGetIntegerField("PluginFileVersion", out Descriptor.FileVersion)) { throw new BuildException("Plugin descriptor file '{0}' does not contain a valid FileVersion entry", FileName); } } // Check it's not newer than the latest version we can parse if (Descriptor.FileVersion > (int)PluginDescriptorVersion.Latest) { throw new BuildException("Plugin descriptor file '{0}' appears to be in a newer version ({1}) of the file format that we can load (max version: {2}).", FileName, Descriptor.FileVersion, (int)PluginDescriptorVersion.Latest); } // Read the other fields RawObject.TryGetIntegerField("Version", out Descriptor.Version); RawObject.TryGetStringField("VersionName", out Descriptor.VersionName); RawObject.TryGetStringField("FriendlyName", out Descriptor.FriendlyName); RawObject.TryGetStringField("Description", out Descriptor.Description); if (!RawObject.TryGetStringField("Category", out Descriptor.Category)) { // Category used to be called CategoryPath in .uplugin files RawObject.TryGetStringField("CategoryPath", out Descriptor.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 (Descriptor.Category != null && Descriptor.Category.Length >= 2 && Descriptor.Category.StartsWith("\"") && Descriptor.Category.EndsWith("\"")) { Descriptor.Category = Descriptor.Category.Substring(1, Descriptor.Category.Length - 2); } RawObject.TryGetStringField("CreatedBy", out Descriptor.CreatedBy); RawObject.TryGetStringField("CreatedByURL", out Descriptor.CreatedByURL); RawObject.TryGetStringField("DocsURL", out Descriptor.DocsURL); RawObject.TryGetStringField("MarketplaceURL", out Descriptor.MarketplaceURL); RawObject.TryGetStringField("SupportURL", out Descriptor.SupportURL); RawObject.TryGetStringField("EngineVersion", out Descriptor.EngineVersion); RawObject.TryGetIntegerField("CompatibleChangelist", out Descriptor.CompatibleChangelist); JsonObject[] ModulesArray; if (RawObject.TryGetObjectArrayField("Modules", out ModulesArray)) { Descriptor.Modules = Array.ConvertAll(ModulesArray, x => ModuleDescriptor.FromJsonObject(x)); } JsonObject[] LocalizationTargetsArray; if (RawObject.TryGetObjectArrayField("LocalizationTargets", out LocalizationTargetsArray)) { Descriptor.LocalizationTargets = Array.ConvertAll(LocalizationTargetsArray, x => LocalizationTargetDescriptor.FromJsonObject(x)); } if(!RawObject.TryGetBoolField("EnabledByDefault", out Descriptor.bEnabledByDefault)) { Descriptor.bEnabledByDefault = bPluginTypeEnabledByDefault; } RawObject.TryGetBoolField("CanContainContent", out Descriptor.bCanContainContent); RawObject.TryGetBoolField("IsBetaVersion", out Descriptor.bIsBetaVersion); RawObject.TryGetBoolField("IsMod", out Descriptor.bIsMod); RawObject.TryGetBoolField("Installed", out Descriptor.bInstalled); RawObject.TryGetBoolField("CanBeUsedWithUnrealHeaderTool", out Descriptor.bCanBeUsedWithUnrealHeaderTool); RawObject.TryGetBoolField("RequiresBuildPlatform", out Descriptor.bRequiresBuildPlatform); CustomBuildSteps.TryRead(RawObject, "PreBuildSteps", out Descriptor.PreBuildSteps); CustomBuildSteps.TryRead(RawObject, "PostBuildSteps", out Descriptor.PostBuildSteps); return Descriptor; } catch (JsonParseException ParseException) { throw new JsonParseException("{0} (in {1})", ParseException.Message, FileName); } } /// /// Saves the descriptor to disk /// /// The filename to write to /// Whether the plugin is enabled by default based on its location public void Save(string FileName, bool bPluginTypeEnabledByDefault) { using (JsonWriter Writer = new JsonWriter(FileName)) { Writer.WriteObjectStart(); Writer.WriteValue("FileVersion", (int)ProjectDescriptorVersion.Latest); Writer.WriteValue("Version", Version); Writer.WriteValue("VersionName", VersionName); Writer.WriteValue("FriendlyName", FriendlyName); Writer.WriteValue("Description", Description); Writer.WriteValue("Category", Category); Writer.WriteValue("CreatedBy", CreatedBy); Writer.WriteValue("CreatedByURL", CreatedByURL); Writer.WriteValue("DocsURL", DocsURL); Writer.WriteValue("MarketplaceURL", MarketplaceURL); Writer.WriteValue("SupportURL", SupportURL); if(!String.IsNullOrEmpty(EngineVersion)) { Writer.WriteValue("EngineVersion", EngineVersion); } if(CompatibleChangelist != 0) { Writer.WriteValue("CompatibleChangelist", CompatibleChangelist); } if(bEnabledByDefault != bPluginTypeEnabledByDefault) { Writer.WriteValue("EnabledByDefault", bEnabledByDefault); } Writer.WriteValue("CanContainContent", bCanContainContent); Writer.WriteValue("IsBetaVersion", bIsBetaVersion); if(bIsMod) { Writer.WriteValue("IsMod", bIsMod); } Writer.WriteValue("Installed", bInstalled); Writer.WriteValue("RequiresBuildPlatform", bRequiresBuildPlatform); ModuleDescriptor.WriteArray(Writer, "Modules", Modules); if(PreBuildSteps != null) { PreBuildSteps.Write(Writer, "PreBuildSteps"); } if(PostBuildSteps != null) { PostBuildSteps.Write(Writer, "PostBuildSteps"); } Writer.WriteObjectEnd(); } } } /// /// Representation of a reference to a plugin from a project file /// [DebuggerDisplay("Name={Name}")] public class PluginReferenceDescriptor { /// /// Name of the plugin /// public string Name; /// /// Whether it should be enabled by default /// public bool bEnabled; /// /// Whether this plugin is optional, and the game should silently ignore it not being present /// public bool bOptional; /// /// Description of the plugin for users that do not have it installed. /// public string Description; /// /// URL for this plugin on the marketplace, if the user doesn't have it installed. /// public string MarketplaceURL; /// /// If enabled, list of platforms for which the plugin should be enabled (or all platforms if blank). /// UnrealTargetPlatform[] WhitelistPlatforms; /// /// If enabled, list of platforms for which the plugin should be disabled. /// UnrealTargetPlatform[] BlacklistPlatforms; /// /// If enabled, list of targets for which the plugin should be enabled (or all targets if blank). /// TargetType[] WhitelistTargets; /// /// If enabled, list of targets for which the plugin should be disabled. /// TargetType[] BlacklistTargets; /// /// Constructor /// /// Name of the plugin /// The marketplace URL for plugins which are not installed /// Whether the plugin is enabled public PluginReferenceDescriptor(string InName, string InMarketplaceURL, bool bInEnabled) { Name = InName; MarketplaceURL = InMarketplaceURL; bEnabled = bInEnabled; } /// /// Construct a PluginReferenceDescriptor from a Json object /// /// The Json object containing a plugin reference descriptor /// New PluginReferenceDescriptor object public static PluginReferenceDescriptor FromJsonObject(JsonObject RawObject) { PluginReferenceDescriptor Descriptor = new PluginReferenceDescriptor(RawObject.GetStringField("Name"), null, RawObject.GetBoolField("Enabled")); RawObject.TryGetBoolField("Optional", out Descriptor.bOptional); RawObject.TryGetStringField("Description", out Descriptor.Description); RawObject.TryGetStringField("MarketplaceURL", out Descriptor.MarketplaceURL); RawObject.TryGetEnumArrayField("WhitelistPlatforms", out Descriptor.WhitelistPlatforms); RawObject.TryGetEnumArrayField("BlacklistPlatforms", out Descriptor.BlacklistPlatforms); RawObject.TryGetEnumArrayField("WhitelistTargets", out Descriptor.WhitelistTargets); RawObject.TryGetEnumArrayField("BlacklistTargets", out Descriptor.BlacklistTargets); return Descriptor; } /// /// Determines if this reference enables the plugin for a given platform /// /// The platform to check /// True if the plugin should be enabled public bool IsEnabledForPlatform(UnrealTargetPlatform Platform) { if (!bEnabled) { return false; } if (WhitelistPlatforms != null && WhitelistPlatforms.Length > 0 && !WhitelistPlatforms.Contains(Platform)) { return false; } if (BlacklistPlatforms != null && BlacklistPlatforms.Contains(Platform)) { return false; } return true; } /// /// Determines if this reference enables the plugin for a given target /// /// The target to check /// True if the plugin should be enabled public bool IsEnabledForTarget(TargetType Target) { if (!bEnabled) { return false; } if (WhitelistTargets != null && WhitelistTargets.Length > 0 && !WhitelistTargets.Contains(Target)) { return false; } if (BlacklistTargets != null && BlacklistTargets.Contains(Target)) { return false; } return true; } } }