Пример #1
0
        internal static string GetSceneWritePath(EntityScenesPaths.PathType type, string subsectionName, AssetImportContext ctx)
        {
            var prefix = string.IsNullOrEmpty(subsectionName) ? "" : subsectionName + ".";
            var path   = ctx.GetResultPath(prefix + EntityScenesPaths.GetExtension(type));

            return(path);
        }
Пример #2
0
        // This function is responsible for providing all the subscenes to the build.
        //
        // The way these files get generated is that we have a SceneWithBuildConfiguration file, (which is a bit of a hack to work around the inability for scriptable importers to take arguments, so
        // instead we create a different file that points to the scene we want to import, and points to the buildconfiguration we want to import it for).   The SubsceneImporter will import this file,
        // and it will make 3 (relevant) kind of files:
        // - headerfile
        // - entitybinaryformat file (the actual entities payloads)
        // - a SerializedFile that has an array of UnityEngine.Object PPtrs that are used by this entity file.
        //
        // The first two we deal with very simply: they just need to be copied into the build, and we're done.
        // the third one, we will feed as input to the Scriptable build pipeline (which is actually about creating assetbundles), and create an assetbundle that
        // has all those objects in it that the 3rd file referred to.  We do this with a batch api, first we loop through all subscenes, and register with this batch
        // api which assetbundles we'd like to see produced, and then at the end, we say "okay make them please".  this assetbundle creation api has a caching system
        // that is separate from the assetpipeline caching system, so if all goes well, the call to produce these assetbundles will return very fast and did nothing.
        //
        // The reason for the strange looking api, where a two callbacks get passed in is to make integration of the new incremental buildpipeline easier, as this code
        // needs to be compatible both with the current buildpipeline in the dots-repo, as well as with the incremental buildpipeline.  When that is merged, we can simplify this.

        public static void PrepareAdditionalFiles(string buildConfigurationGuid, Func <Type, IBuildComponent> getRequiredComponent, Action <string, string> RegisterFileCopy, string outputStreamingAssetsDirectory, string buildWorkingDirectory)
        {
            T GetRequiredComponent <T>() => (T)getRequiredComponent(typeof(T));

            var profile = GetRequiredComponent <ClassicBuildProfile>();

            if (profile.Target == BuildTarget.NoTarget)
            {
                throw new InvalidOperationException($"Invalid build target '{profile.Target.ToString()}'.");
            }

            if (profile.Target != EditorUserBuildSettings.activeBuildTarget)
            {
                throw new InvalidOperationException($"ActiveBuildTarget must be switched before the {nameof(SubSceneBuildCode)} runs.");
            }

            var content       = new BundleBuildContent(new AssetBundleBuild[0]);
            var sceneList     = GetRequiredComponent <SceneList>();
            var bundleNames   = new List <string>();
            var subSceneGuids = sceneList.GetScenePathsForBuild().SelectMany(scenePath => SceneMetaDataImporter.GetSubSceneGuids(AssetDatabase.AssetPathToGUID(scenePath))).Distinct().ToList();

            foreach (var subSceneGuid in subSceneGuids)
            {
                var hash128Guid = SceneWithBuildConfigurationGUIDs.EnsureExistsFor(subSceneGuid, new Hash128(buildConfigurationGuid));

                var hash = AssetDatabaseCompatibility.GetArtifactHash(hash128Guid.ToString(), typeof(SubSceneImporter), ImportMode.Synchronous);
                AssetDatabaseCompatibility.GetArtifactPaths(hash, out var artifactPaths);

                foreach (var artifactPath in artifactPaths)
                {
                    var ext = Path.GetExtension(artifactPath).Replace(".", "");
                    if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader))
                    {
                        var destinationFile = outputStreamingAssetsDirectory + "/" + EntityScenesPaths.RelativePathInStreamingAssetsFolderFor(subSceneGuid, EntityScenesPaths.PathType.EntitiesHeader, -1);
                        RegisterFileCopy(artifactPath, destinationFile);
                    }

                    if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary))
                    {
                        var destinationFile = outputStreamingAssetsDirectory + "/" + EntityScenesPaths.RelativePathInStreamingAssetsFolderFor(subSceneGuid, EntityScenesPaths.PathType.EntitiesBinary, EntityScenesPaths.GetSectionIndexFromPath(artifactPath));
                        RegisterFileCopy(artifactPath, destinationFile);
                    }

                    if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferences))
                    {
                        content.CustomAssets.Add(new CustomContent
                        {
                            Asset     = hash128Guid,
                            Processor = (guid, processor) =>
                            {
                                var sectionIndex = EntityScenesPaths.GetSectionIndexFromPath(artifactPath);
                                processor.GetObjectIdentifiersAndTypesForSerializedFile(artifactPath, out ObjectIdentifier[] objectIds, out Type[] types);
        public static void ConvertToBuild(GUID buildSettingSceneGuid, UnityEditor.Build.Pipeline.Tasks.CalculateCustomDependencyData task)
        {
            var buildSettingScenePath  = AssetDatabase.GUIDToAssetPath(buildSettingSceneGuid.ToString());
            var sceneWithBuildSettings = ReadSceneWithBuildSettings(buildSettingScenePath);

            var hash = UnityEditor.Experimental.AssetDatabaseExperimental.GetArtifactHash(buildSettingSceneGuid.ToString(), typeof(SubSceneImporter));

            string[] paths;
            if (!UnityEditor.Experimental.AssetDatabaseExperimental.GetArtifactPaths(hash, out paths))
            {
                return;
            }

            foreach (var path in paths)
            {
                var ext = System.IO.Path.GetExtension(path).Replace(".", "");
                if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader))
                {
                    var loadPath = EntityScenesPaths.GetLoadPath(sceneWithBuildSettings.SceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1);
                    System.IO.File.Copy(path, loadPath, true);
                    continue;
                }

                if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary))
                {
                    var sectionIndex = EntityScenesPaths.GetSectionIndexFromPath(path);
                    var loadPath     = EntityScenesPaths.GetLoadPath(sceneWithBuildSettings.SceneGUID, EntityScenesPaths.PathType.EntitiesBinary, sectionIndex);
                    System.IO.File.Copy(path, loadPath, true);
                    continue;
                }

                if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferences))
                {
                    var sectionIndex = EntityScenesPaths.GetSectionIndexFromPath(path);
                    task.GetObjectIdentifiersAndTypesForSerializedFile(path, out UnityEditor.Build.Content.ObjectIdentifier[] objectIds, out System.Type[] types);
                    var bundlePath = EntityScenesPaths.GetLoadPath(sceneWithBuildSettings.SceneGUID, EntityScenesPaths.PathType.EntitiesUnityObjectReferences, sectionIndex);
                    var bundleName = System.IO.Path.GetFileName(bundlePath);
                    task.CreateAssetEntryForObjectIdentifiers(objectIds, path, bundleName, bundleName, typeof(ReferencedUnityObjects));
                }
            }
        }
Пример #4
0
        public static void WriteRefGuids(List <ReferencedUnityObjects> referencedUnityObjects, AssetImportContext ctx)
        {
            for (var index = 0; index < referencedUnityObjects.Count; index++)
            {
                var objRefs = referencedUnityObjects[index];
                if (objRefs == null)
                {
                    continue;
                }

                var refGuidsPath           = ctx.GetResultPath($"{index}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectRefGuids)}");
                var runtimeGlobalObjectIds = ReferencedUnityObjectsToRuntimeGlobalObjectIds(objRefs);

                using (var refGuidWriter = new StreamBinaryWriter(refGuidsPath))
                {
                    refGuidWriter.Write(runtimeGlobalObjectIds.Length);
                    refGuidWriter.WriteArray(runtimeGlobalObjectIds.AsArray());
                }

                runtimeGlobalObjectIds.Dispose();
            }
        }
Пример #5
0
        static void WriteAssetDependencyGUIDs(List <ReferencedUnityObjects> referencedUnityObjects, SceneSectionData[] sectionData, AssetImportContext ctx)
        {
            for (var index = 0; index < referencedUnityObjects.Count; index++)
            {
                var sectionIndex = sectionData[index].SubSectionIndex;

                var objRefs = referencedUnityObjects[index];
                if (objRefs == null)
                {
                    continue;
                }

                var path = ctx.GetResultPath($"{sectionIndex}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesAssetDependencyGUIDs)}");
                var assetDependencyGUIDs = ReferencedUnityObjectsToGUIDs(objRefs, ctx);

                using (var writer = new StreamBinaryWriter(path))
                {
                    writer.Write(assetDependencyGUIDs.Length);
                    writer.WriteArray(assetDependencyGUIDs.AsArray());
                }

                assetDependencyGUIDs.Dispose();
            }
        }
Пример #6
0
        // This function is responsible for providing all the subscenes to the build.
        //
        // The way these files get generated is that we have a SceneWithBuildConfiguration file, (which is a bit of a hack to work around the inability for scriptable importers to take arguments, so
        // instead we create a different file that points to the scene we want to import, and points to the buildconfiguration we want to import it for).   The SubsceneImporter will import this file,
        // and it will make 3 (relevant) kind of files:
        // - headerfile
        // - entitybinaryformat file (the actual entities payloads)
        // - a SerializedFile that has an array of UnityEngine.Object PPtrs that are used by this entity file.
        //
        // The first two we deal with very simply: they just need to be copied into the build, and we're done.
        // the third one, we will feed as input to the Scriptable build pipeline (which is actually about creating assetbundles), and create an assetbundle that
        // has all those objects in it that the 3rd file referred to.  We do this with a batch api, first we loop through all subscenes, and register with this batch
        // api which assetbundles we'd like to see produced, and then at the end, we say "okay make them please".  this assetbundle creation api has a caching system
        // that is separate from the assetpipeline caching system, so if all goes well, the call to produce these assetbundles will return very fast and did nothing.
        //
        // The reason for the strange looking api, where a two callbacks get passed in is to make integration of the new incremental buildpipeline easier, as this code
        // needs to be compatible both with the current buildpipeline in the dots-repo, as well as with the incremental buildpipeline.  When that is merged, we can simplify this.
        public static void PrepareAdditionalFiles(string buildConfigurationGuid, string[] scenePathsForBuild, BuildTarget target, Action <string, string> RegisterFileCopy, string outputStreamingAssetsDirectory, string buildWorkingDirectory)
        {
            if (target == BuildTarget.NoTarget)
            {
                throw new InvalidOperationException($"Invalid build target '{target.ToString()}'.");
            }

            if (target != EditorUserBuildSettings.activeBuildTarget)
            {
                throw new InvalidOperationException($"ActiveBuildTarget must be switched before the {nameof(SubSceneBuildCode)} runs.");
            }

            var content             = new BundleBuildContent(new AssetBundleBuild[0]);
            var bundleNames         = new HashSet <string>();
            var subSceneGuids       = scenePathsForBuild.SelectMany(scenePath => SceneMetaDataImporter.GetSubSceneGuids(AssetDatabase.AssetPathToGUID(scenePath))).Distinct().ToList();
            var subScenePaths       = new Dictionary <Hash128, string>();
            var dependencyInputData = new Dictionary <SceneSection, SectionDependencyInfo>();
            var refExt    = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferences);
            var headerExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader);
            var binaryExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary);

            var group      = BuildPipeline.GetBuildTargetGroup(target);
            var parameters = new BundleBuildParameters(target, @group, buildWorkingDirectory)
            {
                BundleCompression = BuildCompression.LZ4Runtime
            };

            var requiresRefresh       = false;
            var sceneBuildConfigGuids = new NativeArray <GUID>(subSceneGuids.Count, Allocator.TempJob);

            for (int i = 0; i != sceneBuildConfigGuids.Length; i++)
            {
                sceneBuildConfigGuids[i] = SceneWithBuildConfigurationGUIDs.EnsureExistsFor(subSceneGuids[i], new Hash128(buildConfigurationGuid), out var thisRequiresRefresh);
                requiresRefresh         |= thisRequiresRefresh;
            }
            if (requiresRefresh)
            {
                AssetDatabase.Refresh();
            }

            var artifactHashes = new NativeArray <UnityEngine.Hash128>(subSceneGuids.Count, Allocator.TempJob);

            AssetDatabaseCompatibility.ProduceArtifactsRefreshIfNecessary(sceneBuildConfigGuids, typeof(SubSceneImporter), artifactHashes);

            for (int i = 0; i != sceneBuildConfigGuids.Length; i++)
            {
                var sceneGuid            = subSceneGuids[i];
                var sceneBuildConfigGuid = sceneBuildConfigGuids[i];
                var artifactHash         = artifactHashes[i];

                bool foundEntityHeader = false;
                AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out var artifactPaths);
                foreach (var artifactPath in artifactPaths)
                {
                    //@TODO: This looks like a workaround. Whats going on here?
                    var ext = Path.GetExtension(artifactPath).Replace(".", "");
                    if (ext == headerExt)
                    {
                        foundEntityHeader = true;

                        if (!string.IsNullOrEmpty(artifactPaths.FirstOrDefault(a => a.EndsWith(refExt))))
                        {
                            subScenePaths[sceneGuid] = artifactPath;
                        }
                        else
                        {
                            //if there are no reference bundles, then deduplication can be skipped
                            var destinationFile = EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesHeader, -1);
                            DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, artifactPath, destinationFile);
                        }
                    }
                    else if (ext == binaryExt)
                    {
                        var destinationFile = EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesBinary, EntityScenesPaths.GetSectionIndexFromPath(artifactPath));
                        DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, artifactPath, destinationFile);
                    }

                    if (ext == refExt)
                    {
                        content.CustomAssets.Add(new CustomContent
                        {
                            Asset     = sceneBuildConfigGuid,
                            Processor = (guid, processor) =>
                            {
                                var sectionIndex = EntityScenesPaths.GetSectionIndexFromPath(artifactPath);
                                processor.GetObjectIdentifiersAndTypesForSerializedFile(artifactPath, out ObjectIdentifier[] objectIds, out Type[] types);
        // This function is responsible for providing all the subscenes to the build.
        //
        // The way these files get generated is that we have a SceneWithBuildConfiguration file, (which is a bit of a hack to work around the inability for scriptable importers to take arguments, so
        // instead we create a different file that points to the scene we want to import, and points to the buildconfiguration we want to import it for).   The SubsceneImporter will import this file,
        // and it will make 3 (relevant) kind of files:
        // - headerfile
        // - entitybinaryformat file (the actual entities payloads)
        // - a SerializedFile that has an array of UnityEngine.Object PPtrs that are used by this entity file.
        //
        // The first two we deal with very simply: they just need to be copied into the build, and we're done.
        // the third one, we will feed as input to the Scriptable build pipeline (which is actually about creating assetbundles), and create an assetbundle that
        // has all those objects in it that the 3rd file referred to.  We do this with a batch api, first we loop through all subscenes, and register with this batch
        // api which assetbundles we'd like to see produced, and then at the end, we say "okay make them please".  this assetbundle creation api has a caching system
        // that is separate from the assetpipeline caching system, so if all goes well, the call to produce these assetbundles will return very fast and did nothing.
        //
        // The reason for the strange looking api, where a two callbacks get passed in is to make integration of the new incremental buildpipeline easier, as this code
        // needs to be compatible both with the current buildpipeline in the dots-repo, as well as with the incremental buildpipeline.  When that is merged, we can simplify this.
        public static void PrepareAdditionalFiles(GUID[] sceneGuids, ArtifactKey[] entitySceneArtifacts, BuildTarget target, Action <string, string> RegisterFileCopy, string outputStreamingAssetsDirectory, string buildWorkingDirectory)
        {
            if (target == BuildTarget.NoTarget)
            {
                throw new InvalidOperationException($"Invalid build target '{target.ToString()}'.");
            }

            if (target != EditorUserBuildSettings.activeBuildTarget)
            {
                throw new InvalidOperationException($"ActiveBuildTarget must be switched before the {nameof(SubSceneBuildCode)} runs.");
            }

            Assert.AreEqual(sceneGuids.Length, entitySceneArtifacts.Length);

            var content             = new BundleBuildContent(new AssetBundleBuild[0]);
            var bundleNames         = new HashSet <string>();
            var subScenePaths       = new Dictionary <Hash128, string>();
            var dependencyInputData = new Dictionary <SceneSection, SectionDependencyInfo>();

            var    refExt    = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesUnityObjectReferences);
            var    headerExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader);
            var    binaryExt = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary);
            string conversionLogExtension = EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesConversionLog);

            var group      = BuildPipeline.GetBuildTargetGroup(target);
            var parameters = new BundleBuildParameters(target, @group, buildWorkingDirectory)
            {
                BundleCompression = BuildCompression.LZ4Runtime
            };

            var artifactHashes = new UnityEngine.Hash128[entitySceneArtifacts.Length];

            AssetDatabaseCompatibility.ProduceArtifactsRefreshIfNecessary(entitySceneArtifacts, artifactHashes);

            for (int i = 0; i != entitySceneArtifacts.Length; i++)
            {
                var sceneGuid            = sceneGuids[i];
                var sceneBuildConfigGuid = entitySceneArtifacts[i].guid;
                var artifactHash         = artifactHashes[i];

                bool foundEntityHeader = false;

                if (!artifactHash.isValid)
                {
                    throw new Exception($"Building EntityScene artifact failed: '{AssetDatabaseCompatibility.GuidToPath(sceneGuid)}' ({sceneGuid}). There were exceptions during the entity scene imports.");
                }

                AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out var artifactPaths);

                foreach (var artifactPath in artifactPaths)
                {
                    //UnityEngine.Debug.Log($"guid: {sceneGuid} artifact: '{artifactPath}'");

                    //@TODO: This looks like a workaround. Whats going on here?
                    var ext = Path.GetExtension(artifactPath).Replace(".", "");

                    if (ext == conversionLogExtension)
                    {
                        var res = ConversionLogUtils.PrintConversionLogToUnityConsole(artifactPath);

                        if (res.HasException)
                        {
                            throw new Exception("Building entity scenes failed. There were exceptions during the entity scene imports.");
                        }
                    }
                    else if (ext == headerExt)
                    {
                        foundEntityHeader = true;

                        if (!string.IsNullOrEmpty(artifactPaths.FirstOrDefault(a => a.EndsWith(refExt))))
                        {
                            subScenePaths[sceneGuid] = artifactPath;
                        }
                        else
                        {
                            //if there are no reference bundles, then deduplication can be skipped
                            var destinationFile = EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesHeader, -1);
                            DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, artifactPath, destinationFile);
                        }
                    }
                    else if (ext == binaryExt)
                    {
                        var destinationFile = EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesBinary, EntityScenesPaths.GetSectionIndexFromPath(artifactPath));
                        DoCopy(RegisterFileCopy, outputStreamingAssetsDirectory, artifactPath, destinationFile);
                    }
                    else if (ext == refExt)
                    {
                        content.CustomAssets.Add(new CustomContent
                        {
                            Asset     = sceneBuildConfigGuid,
                            Processor = (guid, processor) =>
                            {
                                var sectionIndex = EntityScenesPaths.GetSectionIndexFromPath(artifactPath);
                                processor.GetObjectIdentifiersAndTypesForSerializedFile(artifactPath, out ObjectIdentifier[] objectIds, out Type[] types);
        internal static string GetSceneWritePath(EntityScenesPaths.PathType type, string subsectionName, Hash128 sceneGUID, string outputPath)
        {
            var prefix = string.IsNullOrEmpty(subsectionName) ? "" : subsectionName + ".";

            return(Path.Combine(outputPath, sceneGUID + "." + prefix + EntityScenesPaths.GetExtension(type)));
        }
Пример #9
0
        public override BuildResult Run(BuildContext context)
        {
            var manifest               = context.BuildManifest;
            var rootAssembly           = context.GetComponentOrDefault <DotsRuntimeRootAssembly>();
            var buildScenes            = context.GetComponentOrDefault <SceneList>();
            var targetName             = rootAssembly.MakeBeeTargetName(context.BuildConfigurationName);
            var scenePaths             = buildScenes.GetScenePathsForBuild();
            var buildConfigurationGuid = context.BuildConfigurationAssetGUID;
            var dataDirectory          = WorldExport.GetOrCreateDataDirectoryFrom(rootAssembly.StagingDirectory.Combine(targetName));
            var logsDirectory          = WorldExport.GetOrCreateLogDirectoryFrom(targetName);

            var sceneGuids = scenePaths.SelectMany(scenePath =>
            {
                var guids = EditorEntityScenes.GetSubScenes(AssetDatabaseCompatibility.PathToGUID(scenePath)).ToList();
                guids.Add(AssetDatabaseCompatibility.PathToGUID(scenePath));
                return(guids);
            }).Distinct().ToList();

            //Save all unsaved scenes of the project first
            foreach (var guid in sceneGuids)
            {
                string scenePath = AssetDatabase.GUIDToAssetPath(guid.ToString());
                var    scene     = SceneManager.GetSceneByPath(scenePath);
                EditorSceneManager.SaveScene(scene);
            }

            var requiresRefresh       = false;
            var sceneBuildConfigGuids = new NativeArray <GUID>(sceneGuids.Count, Allocator.TempJob);

            for (int i = 0; i != sceneBuildConfigGuids.Length; i++)
            {
                sceneBuildConfigGuids[i] = SceneWithBuildConfigurationGUIDs.EnsureExistsFor(sceneGuids[i], new Hash128(buildConfigurationGuid), false, out var thisRequiresRefresh);
                requiresRefresh         |= thisRequiresRefresh;
            }
            if (requiresRefresh)
            {
                AssetDatabase.Refresh();
            }

            var artifactHashes = new NativeArray <UnityEngine.Hash128>(sceneGuids.Count, Allocator.TempJob);

            AssetDatabaseCompatibility.ProduceArtifactsRefreshIfNecessary(sceneBuildConfigGuids, typeof(SubSceneImporter), artifactHashes);

            bool succeeded = true;

            for (int i = 0; i != sceneBuildConfigGuids.Length; i++)
            {
                var sceneGuid    = sceneGuids[i];
                var artifactHash = artifactHashes[i];

                AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out var artifactPaths);

                List <FileInfo> exportedFiles     = new List <FileInfo>();
                bool            foundEntityHeader = false;
                foreach (var artifactPath in artifactPaths)
                {
                    var ext = Path.GetExtension(artifactPath).ToLower().Replace(".", "");
                    if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader))
                    {
                        foundEntityHeader = true;
                        var destinationFile = dataDirectory.FullName + Path.DirectorySeparatorChar + EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesHeader, -1);
                        new NPath(artifactPath).MakeAbsolute().Copy(new NPath(destinationFile).MakeAbsolute().EnsureParentDirectoryExists());
                        exportedFiles.Add(new FileInfo(destinationFile));
                    }
                    else if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary))
                    {
                        var destinationFile = dataDirectory.FullName + Path.DirectorySeparatorChar + EntityScenesPaths.RelativePathFolderFor(sceneGuid, EntityScenesPaths.PathType.EntitiesBinary, EntityScenesPaths.GetSectionIndexFromPath(artifactPath));
                        new NPath(artifactPath).MakeAbsolute().Copy(new NPath(destinationFile).MakeAbsolute().EnsureParentDirectoryExists());
                        exportedFiles.Add(new FileInfo(destinationFile));
                    }
                    else if (ext == EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesConversionLog))
                    {
                        var destinationFile = logsDirectory.FullName + Path.DirectorySeparatorChar + $"{sceneGuid}.{EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesConversionLog)}";
                        new NPath(artifactPath).MakeAbsolute().Copy(new NPath(destinationFile).MakeAbsolute().EnsureParentDirectoryExists());
                        var result = PrintConversionLogToUnityConsole(artifactPath);
                        if (result.HasError || result.HasException)
                        {
                            UnityEngine.Debug.LogError("Failed to export scene: " + Path.GetFileName(AssetDatabase.GUIDToAssetPath(sceneGuid.ToString())));
                            succeeded = false;
                        }
                    }
                    else if (new Hash128(ext).IsValid) //Asset files are exported as {artifactHash}.{assetguid}
                    {
                        var destinationFile = dataDirectory.FullName + Path.DirectorySeparatorChar + ext;
                        new NPath(artifactPath).MakeAbsolute().Copy(new NPath(destinationFile).MakeAbsolute().EnsureParentDirectoryExists());
                        exportedFiles.Add(new FileInfo(destinationFile));
                    }
                }

                if (!foundEntityHeader)
                {
                    Debug.LogError($"Failed to build EntityScene for '{AssetDatabaseCompatibility.GuidToPath(sceneGuid)}'.");
                    succeeded = false;
                }

                //UpdateManifest
                manifest.Add(new Guid(sceneGuid.ToString()), AssetDatabase.GUIDToAssetPath(sceneGuid.ToString()), exportedFiles);
            }

            var catalogPath = Path.Combine(dataDirectory.ToString(), SceneSystem.k_SceneInfoFileName);

            WriteCatalogFile(catalogPath, buildScenes);
            manifest.AddAdditionalFilesToDeploy(new FileInfo(catalogPath.ToString()));

            sceneBuildConfigGuids.Dispose();
            artifactHashes.Dispose();

            if (succeeded)
            {
                return(context.Success());
            }
            return(context.Failure($"Failed to export scenes"));
        }
        internal static void UpdateManifest(BuildManifest manifest, string scenePath, Hash128 subSceneGuid, SceneSectionData[] sections, DirectoryInfo dataDirectory, string outputDirectory)
        {
            //Add the entity header file and all the sections to the manifest
            List <FileInfo> exportedFiles = new List <FileInfo>();
            var             headerFile    = dataDirectory.GetFile(Path.Combine(outputDirectory, subSceneGuid + "." + EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesHeader)));

            exportedFiles.Add(headerFile);
            foreach (var section in sections)
            {
                var entityFile = dataDirectory.GetFile(Path.Combine(outputDirectory, section.SceneGUID + "." + section.SubSectionIndex + "." + EntityScenesPaths.GetExtension(EntityScenesPaths.PathType.EntitiesBinary)));
                exportedFiles.Add(entityFile);
            }
            manifest.Add(new Guid(subSceneGuid.ToString()), scenePath, exportedFiles);
        }