Ejemplo n.º 1
0
        public static bool MigrateAssetIfNeeded(AssetMigrationContext context, PackageLoadingAssetFile loadAsset, string dependencyName, PackageVersion untilVersion = null)
        {
            var assetFullPath = loadAsset.FilePath.FullPath;

            // Determine if asset was Yaml or not
            var assetFileExtension = Path.GetExtension(assetFullPath);

            if (assetFileExtension == null)
            {
                return(false);
            }

            assetFileExtension = assetFileExtension.ToLowerInvariant();

            var serializer = AssetSerializer.FindSerializer(assetFileExtension);

            if (!(serializer is AssetYamlSerializer))
            {
                return(false);
            }

            // We've got a Yaml asset, let's get expected and serialized versions
            var            serializedVersion = PackageVersion.Zero;
            PackageVersion expectedVersion;
            Type           assetType;

            // Read from Yaml file the asset version and its type (to get expected version)
            // Note: It tries to read as few as possible (SerializedVersion is expected to be right after Id, so it shouldn't try to read further than that)
            using (var assetStream = loadAsset.OpenStream())
                using (var streamReader = new StreamReader(assetStream))
                {
                    var yamlEventReader = new EventReader(new Parser(streamReader));

                    // Skip header
                    yamlEventReader.Expect <StreamStart>();
                    yamlEventReader.Expect <DocumentStart>();
                    var mappingStart = yamlEventReader.Expect <MappingStart>();

                    var  yamlSerializerSettings = YamlSerializer.GetSerializerSettings();
                    var  tagTypeRegistry        = yamlSerializerSettings.TagTypeRegistry;
                    bool typeAliased;
                    assetType = tagTypeRegistry.TypeFromTag(mappingStart.Tag, out typeAliased);

                    var expectedVersions = AssetRegistry.GetCurrentFormatVersions(assetType);
                    expectedVersion = expectedVersions?.FirstOrDefault(x => x.Key == dependencyName).Value ?? PackageVersion.Zero;

                    Scalar assetKey;
                    while ((assetKey = yamlEventReader.Allow <Scalar>()) != null)
                    {
                        // Only allow Id before SerializedVersion
                        if (assetKey.Value == nameof(Asset.Id))
                        {
                            yamlEventReader.Skip();
                        }
                        else if (assetKey.Value == nameof(Asset.SerializedVersion))
                        {
                            // Check for old format: only a scalar
                            var scalarVersion = yamlEventReader.Allow <Scalar>();
                            if (scalarVersion != null)
                            {
                                serializedVersion = PackageVersion.Parse("0.0." + Convert.ToInt32(scalarVersion.Value, CultureInfo.InvariantCulture));

                                // Let's update to new format
                                using (var yamlAsset = loadAsset.AsYamlAsset())
                                {
                                    yamlAsset.DynamicRootNode.RemoveChild(nameof(Asset.SerializedVersion));
                                    AssetUpgraderBase.SetSerializableVersion(yamlAsset.DynamicRootNode, dependencyName, serializedVersion);

                                    var baseBranch = yamlAsset.DynamicRootNode["~Base"];
                                    if (baseBranch != null)
                                    {
                                        var baseAsset = baseBranch["Asset"];
                                        if (baseAsset != null)
                                        {
                                            baseAsset.RemoveChild(nameof(Asset.SerializedVersion));
                                            AssetUpgraderBase.SetSerializableVersion(baseAsset, dependencyName, serializedVersion);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // New format: package => version mapping
                                yamlEventReader.Expect <MappingStart>();

                                while (!yamlEventReader.Accept <MappingEnd>())
                                {
                                    var packageName    = yamlEventReader.Expect <Scalar>().Value;
                                    var packageVersion = PackageVersion.Parse(yamlEventReader.Expect <Scalar>().Value);

                                    // For now, we handle only one dependency at a time
                                    if (packageName == dependencyName)
                                    {
                                        serializedVersion = packageVersion;
                                    }
                                }

                                yamlEventReader.Expect <MappingEnd>();
                            }
                            break;
                        }
                        else
                        {
                            // If anything else than Id or SerializedVersion, let's stop
                            break;
                        }
                    }
                }

            if (serializedVersion > expectedVersion)
            {
                // Try to open an asset newer than what we support (probably generated by a newer Xenko)
                throw new InvalidOperationException($"Asset of type {assetType} has been serialized with newer version {serializedVersion}, but only version {expectedVersion} is supported. Was this asset created with a newer version of Xenko?");
            }

            if (serializedVersion < expectedVersion)
            {
                // Perform asset upgrade
                context.Log.Verbose("{0} needs update, from version {1} to version {2}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);

                using (var yamlAsset = loadAsset.AsYamlAsset())
                {
                    var yamlRootNode = yamlAsset.RootNode;

                    // Check if there is any asset updater
                    var assetUpgraders = AssetRegistry.GetAssetUpgraders(assetType, dependencyName);
                    if (assetUpgraders == null)
                    {
                        throw new InvalidOperationException($"Asset of type {assetType} should be updated from version {serializedVersion} to {expectedVersion}, but no asset migration path was found");
                    }

                    // Instantiate asset updaters
                    var currentVersion = serializedVersion;
                    while (currentVersion != expectedVersion)
                    {
                        PackageVersion targetVersion;
                        // This will throw an exception if no upgrader is available for the given version, exiting the loop in case of error.
                        var upgrader = assetUpgraders.GetUpgrader(currentVersion, out targetVersion);

                        // Stop if the next version would be higher than what is expected
                        if (untilVersion != null && targetVersion > untilVersion)
                        {
                            break;
                        }

                        upgrader.Upgrade(context, dependencyName, currentVersion, targetVersion, yamlRootNode, loadAsset);
                        currentVersion = targetVersion;
                    }

                    // Make sure asset is updated to latest version
                    YamlNode       serializedVersionNode;
                    PackageVersion newSerializedVersion = null;
                    if (yamlRootNode.Children.TryGetValue(new YamlScalarNode(nameof(Asset.SerializedVersion)), out serializedVersionNode))
                    {
                        var newSerializedVersionForDefaultPackage = ((YamlMappingNode)serializedVersionNode).Children[new YamlScalarNode(dependencyName)];
                        newSerializedVersion = PackageVersion.Parse(((YamlScalarNode)newSerializedVersionForDefaultPackage).Value);
                    }

                    if (untilVersion == null && newSerializedVersion != expectedVersion)
                    {
                        throw new InvalidOperationException($"Asset of type {assetType} was migrated, but still its new version {newSerializedVersion} doesn't match expected version {expectedVersion}.");
                    }

                    context.Log.Info("{0} updated from version {1} to version {2}", Path.GetFullPath(assetFullPath), serializedVersion, expectedVersion);
                }

                return(true);
            }

            return(false);
        }
Ejemplo n.º 2
0
        public static void SetSerializableVersion(dynamic asset, string dependencyName, PackageVersion value)
        {
            if (asset.IndexOf(nameof(Asset.SerializedVersion)) == -1)
            {
                asset.SerializedVersion = new YamlMappingNode();

                // Ensure that it is stored right after the asset Id
                asset.MoveChild(nameof(Asset.SerializedVersion), asset.IndexOf(nameof(Asset.Id)) + 1);
            }

            asset.SerializedVersion[dependencyName] = value;
        }
Ejemplo n.º 3
0
 public PackageUpgraderAttribute(string packageName, string packageMinimumVersion, string packageUpdatedVersionRange)
 {
     PackageName           = packageName;
     PackageMinimumVersion = new PackageVersion(packageMinimumVersion);
     PackageVersionRange.TryParse(packageUpdatedVersionRange, out this.packageUpdatedVersionRange);
 }
Ejemplo n.º 4
0
        private void UpgradeBase(AssetMigrationContext context, string dependencyName, PackageVersion currentVersion, PackageVersion targetVersion, dynamic assetBase, PackageLoadingAssetFile assetFile)
        {
            var baseAsset = assetBase[nameof(AssetBase.Asset)];

            if (baseAsset != null)
            {
                UpgradeAsset(context, currentVersion, targetVersion, baseAsset, assetFile, OverrideUpgraderHint.Base);
                SetSerializableVersion(baseAsset, dependencyName, targetVersion);
            }
        }
Ejemplo n.º 5
0
 protected abstract void UpgradeAsset(AssetMigrationContext context, PackageVersion currentVersion, PackageVersion targetVersion, dynamic asset, PackageLoadingAssetFile assetFile, OverrideUpgraderHint overrideHint);
Ejemplo n.º 6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PackageVersionRange" /> class.
 /// </summary>
 /// <param name="minVersion">The minimum version.</param>
 /// <param name="minVersionInclusive">if set to <c>true</c> the minimum version is inclusive</param>
 public PackageVersionRange(PackageVersion minVersion, bool minVersionInclusive)
 {
     IsMinInclusive = minVersionInclusive;
     MinVersion     = minVersion;
 }
Ejemplo n.º 7
0
        public void Upgrade(AssetMigrationContext context, string dependencyName, PackageVersion currentVersion, PackageVersion targetVersion, YamlMappingNode yamlAssetNode, PackageLoadingAssetFile assetFile)
        {
            dynamic asset = new DynamicYamlMapping(yamlAssetNode);

            // upgrade the asset
            var baseBranch      = asset[Asset.BaseProperty];
            var basePartsBranch = asset[Asset.BasePartsProperty] as DynamicYamlArray;

            // Detect in what kind of override context we are
            var overrideHint = (baseBranch != null || (basePartsBranch != null && basePartsBranch.Node.Children.Count > 0))
                ? OverrideUpgraderHint.Derived
                : OverrideUpgraderHint.Unknown;

            // Upgrade the asset
            UpgradeAsset(context, currentVersion, targetVersion, asset, assetFile, overrideHint);
            SetSerializableVersion(asset, dependencyName, targetVersion);

            // Upgrade its base
            if (baseBranch != null)
            {
                UpgradeBase(context, dependencyName, currentVersion, targetVersion, baseBranch, assetFile);
            }

            // Upgrade base parts
            if (basePartsBranch != null)
            {
                foreach (dynamic assetBase in basePartsBranch)
                {
                    UpgradeBase(context, dependencyName, currentVersion, targetVersion, assetBase, assetFile);
                }
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Tries to parse a version dependency.
        /// </summary>
        /// <param name="value">The version dependency as a string.</param>
        /// <param name="result">The parsed result.</param>
        /// <returns><c>true</c> if successfuly parsed, <c>false</c> otherwise.</returns>
        /// <exception cref="System.ArgumentNullException">value</exception>
        public static bool TryParse(string value, out PackageVersionRange result)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            var versionSpec = new PackageVersionRange();

            value = value.Trim();

            // First, try to parse it as a plain version string
            PackageVersion version;

            if (PackageVersion.TryParse(value, out version))
            {
                // A plain version is treated as an inclusive minimum range
                result = new PackageVersionRange
                {
                    MinVersion     = version,
                    IsMinInclusive = true
                };

                return(true);
            }

            // It's not a plain version, so it must be using the bracket arithmetic range syntax

            result = null;

            // Fail early if the string is too short to be valid
            if (value.Length < 3)
            {
                return(false);
            }

            // The first character must be [ ot (
            switch (value.First())
            {
            case '[':
                versionSpec.IsMinInclusive = true;
                break;

            case '(':
                versionSpec.IsMinInclusive = false;
                break;

            default:
                return(false);
            }

            // The last character must be ] ot )
            switch (value.Last())
            {
            case ']':
                versionSpec.IsMaxInclusive = true;
                break;

            case ')':
                versionSpec.IsMaxInclusive = false;
                break;

            default:
                return(false);
            }

            // Get rid of the two brackets
            value = value.Substring(1, value.Length - 2);

            // Split by comma, and make sure we don't get more than two pieces
            string[] parts = value.Split(',');
            if (parts.Length > 2)
            {
                return(false);
            }
            if (parts.All(string.IsNullOrEmpty))
            {
                // If all parts are empty, then neither of upper or lower bounds were specified. Version spec is of the format (,]
                return(false);
            }

            // If there is only one piece, we use it for both min and max
            string minVersionString = parts[0];
            string maxVersionString = (parts.Length == 2) ? parts[1] : parts[0];

            // Only parse the min version if it's non-empty
            if (!string.IsNullOrWhiteSpace(minVersionString))
            {
                if (!PackageVersion.TryParse(minVersionString, out version))
                {
                    return(false);
                }
                versionSpec.MinVersion = version;
            }

            // Same deal for max
            if (!string.IsNullOrWhiteSpace(maxVersionString))
            {
                if (!PackageVersion.TryParse(maxVersionString, out version))
                {
                    return(false);
                }
                versionSpec.MaxVersion = version;
            }

            // Successful parse!
            result = versionSpec;
            return(true);
        }
            protected override void UpgradeAsset(AssetMigrationContext context, PackageVersion currentVersion, PackageVersion targetVersion, dynamic asset, PackageLoadingAssetFile assetFile, OverrideUpgraderHint overrideHint)
            {
                var rootPartIds = asset.Hierarchy.RootPartIds;
                int i           = 0;

                foreach (dynamic rootPartId in rootPartIds)
                {
                    rootPartIds[i++] = "ref!! " + rootPartId.ToString();
                }
                asset.Hierarchy.RootParts   = rootPartIds;
                asset.Hierarchy.RootPartIds = DynamicYamlEmpty.Default;
            }
Ejemplo n.º 10
0
        public void Upgrade(AssetMigrationContext context, string dependencyName, PackageVersion currentVersion, PackageVersion targetVersion, YamlMappingNode yamlAssetNode, PackageLoadingAssetFile assetFile)
        {
            dynamic asset = new DynamicYamlMapping(yamlAssetNode);

            // upgrade the asset
            UpgradeAsset(context, currentVersion, targetVersion, asset, assetFile);
            SetSerializableVersion(asset, dependencyName, targetVersion);

            // upgrade its base
            var baseBranch = asset[Asset.BaseProperty];

            if (baseBranch != null)
            {
                UpgradeBase(context, dependencyName, currentVersion, targetVersion, baseBranch, assetFile);
            }

            // upgrade base parts
            var basePartsBranch = asset[Asset.BasePartsProperty] as DynamicYamlArray;

            if (basePartsBranch != null)
            {
                foreach (dynamic assetBase in basePartsBranch)
                {
                    UpgradeBase(context, dependencyName, currentVersion, targetVersion, assetBase, assetFile);
                }
            }
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Initializes a new instance of the <see cref="AssetFormatVersionAttribute"/> class.
 /// </summary>
 /// <param name="name">The dependency name.</param>
 /// <param name="version">The current format version of this asset.</param>
 /// <param name="minUpgradableVersion">The minimum format version that supports upgrade for this asset.</param>
 public AssetFormatVersionAttribute(string name, string version, string minUpgradableVersion = null)
 {
     Name    = name;
     Version = PackageVersion.Parse(version);
     MinUpgradableVersion = PackageVersion.Parse(minUpgradableVersion ?? "0");
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Initializes a new instance of the <see cref="PackageStore"/> class.
        /// </summary>
        /// <exception cref="System.InvalidOperationException">Unable to find a valid Paradox installation path</exception>
        private PackageStore(string installationPath = null, string defaultPackageName = "Paradox", string defaultPackageVersion = ParadoxVersion.CurrentAsText)
        {
            // 1. Try to use the specified installation path
            if (installationPath != null)
            {
                if (!IsRootDirectory(installationPath))
                {
                    throw new ArgumentException("Invalid Paradox installation path [{0}]".ToFormat(installationPath), "installationPath");
                }

                globalInstallationPath = installationPath;
            }

            // TODO: these are currently hardcoded to Paradox
            DefaultPackageName    = defaultPackageName;
            DefaultPackageVersion = new PackageVersion(defaultPackageVersion);

            // 2. Try to resolve an installation path from the path of this assembly
            // We need to be able to use the package manager from an official Paradox install as well as from a developer folder

            // Try to determine the root package manager from the current assembly
            var thisAssemblyLocation = typeof(PackageStore).Assembly.Location;
            var binDirectory         = new FileInfo(thisAssemblyLocation).Directory;

            if (binDirectory != null && binDirectory.Parent != null && binDirectory.Parent.Parent != null)
            {
                var defaultPackageDirectoryTemp = binDirectory.Parent.Parent;

                // If we have a root directory, then store it as the default package directory
                if (IsPackageDirectory(defaultPackageDirectoryTemp.FullName, DefaultPackageName))
                {
                    defaultPackageDirectory = defaultPackageDirectoryTemp.FullName;
                }
                else
                {
                    throw new InvalidOperationException("The current assembly [{0}] is not part of the package [{1}]".ToFormat(thisAssemblyLocation, DefaultPackageName));
                }

                if (globalInstallationPath == null)
                {
                    // Check if we have a regular distribution
                    if (defaultPackageDirectoryTemp.Parent != null && IsRootDirectory(defaultPackageDirectoryTemp.Parent.FullName))
                    {
                        globalInstallationPath = defaultPackageDirectoryTemp.Parent.FullName;
                    }
                    else if (IsRootDirectory(defaultPackageDirectory))
                    {
                        // we have a dev distribution
                        globalInstallationPath = defaultPackageDirectory;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("The current assembly [{0}] must be loaded from a valid installation".ToFormat(thisAssemblyLocation));
            }

            // 3. Try from the environement variable
            if (globalInstallationPath == null)
            {
                var rootDirectory = Environment.GetEnvironmentVariable(DefaultEnvironmentSdkDir);
                if (!string.IsNullOrWhiteSpace(rootDirectory) && IsRootDirectory(rootDirectory))
                {
                    globalInstallationPath = rootDirectory;
                }
            }

            // If there is no root, this is an error
            if (globalInstallationPath == null)
            {
                throw new InvalidOperationException("Unable to find a valid Paradox installation or dev path");
            }

            // Preload default package
            var logger             = new LoggerResult();
            var defaultPackageFile = GetPackageFile(defaultPackageDirectory, DefaultPackageName);

            defaultPackage = Package.Load(logger, defaultPackageFile, GetDefaultPackageLoadParameters());
            if (defaultPackage == null)
            {
                throw new InvalidOperationException("Error while loading default package from [{0}]: {1}".ToFormat(defaultPackageFile, logger.ToText()));
            }
            defaultPackage.IsSystem = true;

            // A flag variable just to know if it is a bare bone development directory
            isDev = defaultPackageDirectory != null && IsRootDevDirectory(defaultPackageDirectory);

            // Check if we are in a root directory with store/packages facilities
            if (NugetStore.IsStoreDirectory(globalInstallationPath))
            {
                packagesDirectory = UPath.Combine(globalInstallationPath, (UDirectory)NugetStore.DefaultGamePackagesDirectory);
                store             = new NugetStore(globalInstallationPath)
                {
                    DefaultPackageId = DefaultPackageName
                };
            }
        }