using System; using System.Collections.Generic; using System.IO; using UnityEngine; namespace Ubisoft.Hotel.Package.Editor { [Serializable] public class PackageDefinition { [SerializeField] public enum EImplementation { Prod, Dummy, Mock }; [SerializeField] private string m_name; [SerializeField] private string m_version; [SerializeField] private EImplementation m_implementation; public string Name { get { return m_name; } private set { m_name = value; } } public string Version { get { return m_version; } set { m_version = value; } } public EImplementation Implementation { get { return m_implementation; } set { m_implementation = value; } } public PackageDefinition(string id, string version, EImplementation implementation) { Name = id; Version = version; Implementation = implementation; } } public class UpmBuildProperty : PackageProperty { /// /// Dictionary used as a backup to store dependencies before changing them so we can tell whether or not they've changed. /// It's used as a temporary variable. It's declared only to avoid creating over and over every time configuration changes /// private static readonly Dictionary TempDependencies = new Dictionary(); public static UpmBuildProperty CreateInstanceWithParams(string name) { UpmBuildProperty returnValue = CreateInstance(); returnValue.name = name; return returnValue; } public static string ReadManifestJson() { string path = PATH_MANIFEST; return File.Exists(path) ? File.ReadAllText(path) : null; } private const string PATH_MANIFEST = "Packages/manifest.json"; [SerializeField] private List m_packages; public void Clear() { if (m_packages != null) { m_packages.Clear(); } } public override void Apply(BuildSuite buildSuite) { if (buildSuite != null) { buildSuite.OrderProperty(this); } } public void Join(UpmBuildProperty p) { if (p != null) { if (p.m_packages != null) { foreach (PackageDefinition packageDefinition in p.m_packages) { AddPackage(packageDefinition.Name, packageDefinition.Version, packageDefinition.Implementation); } } } } public void PreBuild(Dictionary packagesHandledByConsumer, Action> onDone) { Manifest manifest = ProcessManifest(packagesHandledByConsumer, true); PackageManagerHelper.PrunePackagesResources(packagesHandledByConsumer, manifest.Dependencies, HtAssetDatabase.UnityPathToPlatformPath(AssetManager.HOTEL_ASSETS_RESOURCES_PATH)); onDone?.Invoke(manifest, GetSymbols(manifest, packagesHandledByConsumer)); } private HashSet GetSymbols(Manifest manifest, Dictionary packagesHandledByConsumer) { HashSet returnValue = null; if (manifest != null && packagesHandledByConsumer != null) { returnValue = new HashSet(); if (manifest.Dependencies != null) { // Is the package OFF? foreach (KeyValuePair pair in packagesHandledByConsumer) { if (!manifest.Dependencies.ContainsDependency(pair.Key)) { _ = returnValue.Add(UnityBuildProperty.GetPackageSymbol(pair.Key, false)); } } PackageDefinition packageDefinition; PackageDefinition.EImplementation implementation; Dictionary dependencies = manifest.Dependencies.GetDependencies(); foreach (KeyValuePair pair in dependencies) { if (UnityBuildProperty.IsDefineSymbolEnabledForPackage(pair.Key)) { packageDefinition = GetPackage(pair.Key); if (packageDefinition == null) { // We assume PROD as implementation for packages with no denition provided implementation = PackageDefinition.EImplementation.Prod; } else { implementation = packageDefinition.Implementation; } _ = returnValue.Add(UnityBuildProperty.GetPackageSymbol(pair.Key, true, implementation)); } } } } return returnValue; } public void PostBuild() { } public void AddPackage(string name, string version, PackageDefinition.EImplementation implementation) { bool validateName = ValidatePackageAttribute(name, "name"); bool validateVersion = ValidatePackageAttribute(version, "version"); if (validateName && validateVersion) { PackageDefinition package = GetPackage(name); if (package == null) { package = new PackageDefinition(name, version, implementation); if (m_packages == null) { m_packages = new List(); } m_packages.Add(package); } else { package.Version = version; } } } private bool ValidatePackageAttribute(string value, string attributeName = null) { bool returnValue = !string.IsNullOrEmpty(value); if (!returnValue && attributeName != null) { string message = $"You are attempting to add an empty package {Debug.FormatTextInUserContext(attributeName)} to " + $"{Debug.FormatTextInUserContext(Name)} PackageProperty"; message = $"[{Debug.FormatTextInUserContext(Name)}({Debug.FormatTextInCodeContext("UpmBuildProperty")})]: {message}"; Debug.EditorLogError(message); } return returnValue; } public void RemovePackage(string name) { PackageDefinition package = GetPackage(name); if (package != null) { _ = m_packages.Remove(package); } } private PackageDefinition GetPackage(string name) { PackageDefinition returnValue = null; if (m_packages != null && ValidatePackageAttribute(name, null)) { int count = m_packages.Count; for (int i = 0; i < count && returnValue == null; ++i) { if (m_packages[i].Name == name) { returnValue = m_packages[i]; } } } return returnValue; } private Manifest ProcessManifest(Dictionary packagesHandledViaProperties, bool writeManifest) { Manifest manifest = null; string json = ReadManifestJson(); if (!string.IsNullOrEmpty(json)) { manifest = Manifest.Parse(json); // Remove packages that are handled by SuiteManager if (manifest != null) { TempDependencies.Clear(); if (manifest.Dependencies != null) { Dictionary dependencies = manifest.Dependencies.GetDependencies(); if (dependencies != null) { foreach (KeyValuePair pair in dependencies) { TempDependencies.Add(pair.Key, pair.Value); } } } if (packagesHandledViaProperties != null) { foreach (KeyValuePair pair in packagesHandledViaProperties) { manifest.RemoveDependency(pair.Key); } } manifest.RemovePluggableDependencies(); if (m_packages != null) { // Add pluggable packages foreach (PackageDefinition p in m_packages) { manifest.AddDependency(p.Name, p.Version, true); } } Dictionary newDependencies = null; if (manifest.Dependencies != null) { newDependencies = manifest.Dependencies.GetDependencies(); } // Manifest is written only if dependencies have changed if (writeManifest) { int count = (TempDependencies == null) ? 0 : TempDependencies.Count; int newCount = (newDependencies == null) ? 0 : newDependencies.Count; writeManifest = count != newCount; if (!writeManifest && newDependencies != null && TempDependencies != null) { foreach (KeyValuePair pair in newDependencies) { if (!TempDependencies.ContainsKey(pair.Key) || TempDependencies[pair.Key] != pair.Value) { writeManifest = true; break; } } } } TempDependencies.Clear(); if (writeManifest) { manifest.NeedsToSave = true; // Force PackageManager to resolve in Unity 2020, otherwise packages won't get updated without leaving and returning to the Editor UnityEditor.PackageManager.Client.Resolve(); } } } return manifest; } } }