// Recursive until new SBP APIs land in 2020.1 internal static Hash128[] GetDependenciesInternal(Hash128 artifactHash) { try { AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out string[] paths); var metaPath = paths.First(o => o.EndsWith(k_DependenciesExtension)); if (metaPath == null) { return(Array.Empty <Hash128>()); } BlobAssetReference <BuildMetaData> buildMetaData; if (!BlobAssetReference <BuildMetaData> .TryRead(metaPath, k_CurrentFileFormatVersion, out buildMetaData)) { return(new Hash128[0]); } Hash128[] guids = buildMetaData.Value.Dependencies.ToArray(); buildMetaData.Dispose(); return(guids); } catch (Exception e) { UnityEngine.Debug.LogError($"Exception thrown getting dependencies for '{artifactHash}'.\n{e.Message}"); } return(new Hash128[0]); }
static unsafe TypeDependencyCache() { //TODO: Find a better way to enforce Version 2 compatibility bool v2Enabled = (bool)typeof(AssetDatabase).GetMethod("IsV2Enabled", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null); if (!v2Enabled) { throw new System.InvalidOperationException("com.unity.entities requires Asset Pipeline Version 2. Please enable Version 2 in Project Settings / Editor / Asset Pipeline / Mode"); } // Custom dependencies are transmitted to the import worker so dont spent time on registering them if (AssetDatabaseCompatibility.IsAssetImportWorkerProcess()) { return; } using (kRegisterComponentTypes.Auto()) RegisterComponentTypes(); using (kRegisterConversionSystemVersions.Auto()) RegisterConversionSystems(); int fileFormatVersion = SerializeUtility.CurrentFileFormatVersion; UnityEngine.Hash128 fileFormatHash = default; HashUnsafeUtilities.ComputeHash128(&fileFormatVersion, sizeof(int), &fileFormatHash); AssetDatabaseCompatibility.RegisterCustomDependency("EntityBinaryFileFormatVersion", fileFormatHash); int sceneFileFormatVersion = SceneMetaDataSerializeUtility.CurrentFileFormatVersion; UnityEngine.Hash128 sceneFileFormatHash = default; HashUnsafeUtilities.ComputeHash128(&sceneFileFormatVersion, sizeof(int), &sceneFileFormatHash); AssetDatabaseCompatibility.RegisterCustomDependency("SceneMetaDataFileFormatVersion", sceneFileFormatHash); }
public static void Check(string path) { var guid = AssetDatabaseCompatibility.PathToGUID(path); Assert.IsFalse(guid.Empty()); var configGUID = SceneWithBuildConfigurationGUIDs.EnsureExistsFor(guid, default, true, out var requireRefresh);
internal static Hash128 GetHash(string guid, BuildTarget target, ImportMode importMode) { LiveLinkBuildPipeline.RemapBuildInAssetGuid(ref guid); AssetBundleTypeCache.RegisterMonoScripts(); // TODO: GetArtifactHash needs to take BuildTarget so we can get Artifacts for other than the ActiveBuildTarget return(AssetDatabaseCompatibility.GetArtifactHash(guid, typeof(LiveLinkBuildImporter), importMode)); }
static unsafe void RegisterConversionSystems() { var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.GameObjectConversion | WorldSystemFilterFlags.EntitySceneOptimizations); var behaviours = TypeCache.GetTypesDerivedFrom <IConvertGameObjectToEntity>(); var nameAndVersion = new NameAndVersion[systems.Count + behaviours.Count]; int count = 0; // System versions for (int i = 0; i != systems.Count; i++) { var fullName = systems[i].FullName; if (fullName == null) { continue; } nameAndVersion[count++].Init(systems[i], fullName); } // IConvertGameObjectToEntity versions for (int i = 0; i != behaviours.Count; i++) { var fullName = behaviours[i].FullName; if (fullName == null) { continue; } nameAndVersion[count++].Init(behaviours[i], fullName); } Array.Sort(nameAndVersion, 0, count); UnityEngine.Hash128 hash = default; for (int i = 0; i != count; i++) { var fullName = nameAndVersion[i].FullName; fixed(char *str = fullName) { HashUnsafeUtilities.ComputeHash128(str, (ulong)(sizeof(char) * fullName.Length), &hash); } var userName = nameAndVersion[i].UserName; if (userName != null) { fixed(char *str = userName) { HashUnsafeUtilities.ComputeHash128(str, (ulong)(sizeof(char) * userName.Length), &hash); } } int version = nameAndVersion[i].Version; HashUnsafeUtilities.ComputeHash128(&version, sizeof(int), &hash); } AssetDatabaseCompatibility.RegisterCustomDependency(SystemsVersion, hash); }
static void RegisterComponentTypes() { TypeManager.Initialize(); AssetDatabaseCompatibility.UnregisterCustomDependencyPrefixFilter("DOTSType/"); int typeCount = TypeManager.GetTypeCount(); for (int i = 1; i < typeCount; ++i) { ref readonly var typeInfo = ref TypeManager.GetTypeInfo(i);
public void SectionMetadata() { using (var world = TestWorldSetup.CreateEntityWorld("World", false)) { var resolveParams = new SceneSystem.LoadParameters { Flags = SceneLoadFlags.BlockOnImport | SceneLoadFlags.DisableAutoLoad }; var sceneSystem = world.GetOrCreateSystem <SceneSystem>(); var sceneEntity = sceneSystem.LoadSceneAsync(SceneGUID, resolveParams); world.Update(); var manager = world.EntityManager; var sectionEntities = manager.GetBuffer <ResolvedSectionEntity>(sceneEntity); Assert.AreEqual(3, sectionEntities.Length); Assert.IsTrue(manager.HasComponent <TestMetadata>(sectionEntities[0].SectionEntity)); Assert.IsFalse(manager.HasComponent <TestMetadata>(sectionEntities[1].SectionEntity)); Assert.IsTrue(manager.HasComponent <TestMetadata>(sectionEntities[2].SectionEntity)); Assert.IsTrue(manager.HasComponent <TestMetadataTag>(sectionEntities[0].SectionEntity)); Assert.IsFalse(manager.HasComponent <TestMetadataTag>(sectionEntities[1].SectionEntity)); Assert.IsTrue(manager.HasComponent <TestMetadataTag>(sectionEntities[2].SectionEntity)); // These components should not be added, instead an error is logged that meta info components can't contain entities or blob assets var filteredTypes = new[] { typeof(TestMetadataWithEntity), typeof(TestMetadataWithBlobAsset), typeof(EcsTestSharedComp), typeof(EcsIntElement), typeof(EcsState1), #if !UNITY_DISABLE_MANAGED_COMPONENTS typeof(EcsTestManagedComponent) #endif }; foreach (var type in filteredTypes) { var componentType = ComponentType.FromTypeIndex(TypeManager.GetTypeIndex(type)); Assert.IsFalse(manager.HasComponent(sectionEntities[0].SectionEntity, componentType)); } Assert.AreEqual(0, manager.GetComponentData <TestMetadata>(sectionEntities[0].SectionEntity).SectionIndex); Assert.AreEqual(13, manager.GetComponentData <TestMetadata>(sectionEntities[0].SectionEntity).Value); Assert.AreEqual(42, manager.GetComponentData <TestMetadata>(sectionEntities[2].SectionEntity).SectionIndex); Assert.AreEqual(100, manager.GetComponentData <TestMetadata>(sectionEntities[2].SectionEntity).Value); var hash = EntityScenesPaths.GetSubSceneArtifactHash(SceneGUID, sceneSystem.BuildConfigurationGUID, true, ImportMode.Synchronous); Assert.IsTrue(hash.IsValid); AssetDatabaseCompatibility.GetArtifactPaths(hash, out var paths); var logPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesConversionLog); Assert.NotNull(logPath); var log = System.IO.File.ReadAllText(logPath); Assert.IsTrue(log.Contains("The component type must contains only blittable/basic data types")); Assert.IsFalse(log.Contains("entities in the scene 'TestSubSceneWithSectionMetadata' had no SceneSection and as a result were not serialized at all.")); } }
// 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);
internal static string GetBundlePathInternal(Hash128 artifactHash, GUID guid) { try { AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out string[] paths); return(paths.First(o => o.EndsWith(k_BundleExtension))); } catch (Exception e) { UnityEngine.Debug.LogError($"Exception thrown getting bundle path for '{guid}'.\n{e.Message}"); } return(""); }
bool CheckConversionLog(SubScene subScene) { var pendingWork = false; foreach (var world in World.All) { var sceneSystem = world.GetExistingSystem <SceneSystem>(); if (sceneSystem is null) { continue; } if (!m_ConversionLogLoaded.TryGetValue(sceneSystem.BuildConfigurationGUID, out var loaded)) { m_ConversionLogLoaded.Add(sceneSystem.BuildConfigurationGUID, false); } else if (loaded) { continue; } var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, sceneSystem.BuildConfigurationGUID, ImportMode.Asynchronous); if (!hash.IsValid) { pendingWork = true; continue; } m_ConversionLogLoaded[sceneSystem.BuildConfigurationGUID] = true; AssetDatabaseCompatibility.GetArtifactPaths(hash, out var paths); var logPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesConversionLog); if (logPath == null) { continue; } var log = File.ReadAllText(logPath); if (log.Trim().Length != 0) { if (m_ConversionLog.Length != 0) { m_ConversionLog += "\n\n"; } m_ConversionLog += log; } } return(pendingWork); }
static void RegisterComponentTypes() { TypeManager.Initialize(); AssetDatabaseCompatibility.UnregisterCustomDependencyPrefixFilter("DOTSType/"); int typeCount = TypeManager.GetTypeCount(); for (int i = 1; i < typeCount; ++i) { var typeInfo = TypeManager.GetTypeInfo(i); var hash = typeInfo.StableTypeHash; AssetDatabaseCompatibility.RegisterCustomDependency(TypeString(typeInfo.Type), new UnityEngine.Hash128(hash, hash)); } }
public static Hash128[] GetSubSceneGuids(string guid) { var hash = AssetDatabaseCompatibility.GetArtifactHash(guid, typeof(SceneMetaDataImporter), ImportMode.Synchronous); AssetDatabaseCompatibility.GetArtifactPaths(hash, out string[] paths); var metaPath = paths.First(o => o.EndsWith("scenemeta")); BlobAssetReference <SceneMetaData> sceneMetaDataRef; if (!BlobAssetReference <SceneMetaData> .TryRead(metaPath, SceneMetaDataImporter.CurrentFileFormatVersion, out sceneMetaDataRef)) { return(new Hash128[0]); } Hash128[] guids = sceneMetaDataRef.Value.SubScenes.ToArray(); sceneMetaDataRef.Dispose(); return(guids); }
static void AddEntityBinaryFiles(Hash128 buildConfigurationGuid, string[] scenePathsForBuild, EntitySectionBundlesInBuild sectionBundlesInBuild) { var subSceneGuids = scenePathsForBuild.SelectMany(scenePath => EditorEntityScenes.GetSubScenes(AssetDatabaseCompatibility.PathToGUID(scenePath))).Distinct().ToList(); 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], buildConfigurationGuid, false, out var thisRequiresRefresh); requiresRefresh |= thisRequiresRefresh; sectionBundlesInBuild.Add(subSceneGuids[i], sceneBuildConfigGuids[i], typeof(SubSceneImporter)); } if (requiresRefresh) { AssetDatabase.Refresh(); } AssetDatabaseCompatibility.ProduceArtifactsAsync(sceneBuildConfigGuids, typeof(SubSceneImporter)); sceneBuildConfigGuids.Dispose(); }
public static void RegisterMonoScripts() { if (AssetDatabaseCompatibility.IsAssetImportWorkerProcess() || s_Initialized) { return; } s_Initialized = true; AssetDatabaseCompatibility.UnregisterCustomDependencyPrefixFilter("UnityEngineType/"); var behaviours = TypeCache.GetTypesDerivedFrom <UnityEngine.MonoBehaviour>(); var scripts = TypeCache.GetTypesDerivedFrom <UnityEngine.ScriptableObject>(); for (int i = 0; i != behaviours.Count; i++) { var type = behaviours[i]; if (type.IsGenericType) { continue; } var hash = TypeHash.CalculateStableTypeHash(type); AssetDatabaseCompatibility.RegisterCustomDependency(TypeString(type), new UnityEngine.Hash128(hash, hash)); } for (int i = 0; i != scripts.Count; i++) { var type = scripts[i]; if (type.IsGenericType) { continue; } var hash = TypeHash.CalculateStableTypeHash(type); AssetDatabaseCompatibility.RegisterCustomDependency(TypeString(type), new UnityEngine.Hash128(hash, hash)); } }
public NativeArray <Hash128> GetInitialScenes(int playerId, Allocator allocator) { var sceneList = _BuildConfiguration.GetComponent <SceneList>(); var nonEmbeddedStartupScenes = new List <string>(); foreach (var path in sceneList.GetScenePathsToLoad()) { if (SceneImporterData.CanLiveLinkScene(path)) { nonEmbeddedStartupScenes.Add(path); } } if (nonEmbeddedStartupScenes.Count > 0) { var sceneIds = new NativeArray <Hash128>(nonEmbeddedStartupScenes.Count, allocator); for (int i = 0; i < nonEmbeddedStartupScenes.Count; i++) { sceneIds[i] = AssetDatabaseCompatibility.PathToGUID(nonEmbeddedStartupScenes[i]); } return(sceneIds); } return(new NativeArray <Hash128>(0, allocator)); }
public override void OnImportAsset(AssetImportContext ctx) { try { var sceneWithBuildConfiguration = SceneWithBuildConfigurationGUIDs.ReadFromFile(ctx.assetPath); // Ensure we have as many dependencies as possible registered early in case an exception is thrown EditorEntityScenes.AddEntityBinaryFileDependencies(ctx, sceneWithBuildConfiguration.BuildConfiguration); EditorEntityScenes.DependOnSceneGameObjects(sceneWithBuildConfiguration.SceneGUID, ctx); var config = BuildConfiguration.LoadAsset(sceneWithBuildConfiguration.BuildConfiguration); var scenePath = AssetDatabaseCompatibility.GuidToPath(sceneWithBuildConfiguration.SceneGUID); var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive); try { EditorSceneManager.SetActiveScene(scene); var settings = new GameObjectConversionSettings(); settings.SceneGUID = sceneWithBuildConfiguration.SceneGUID; if (!sceneWithBuildConfiguration.IsBuildingForEditor) { settings.ConversionFlags |= GameObjectConversionUtility.ConversionFlags.IsBuildingForPlayer; } settings.BuildConfiguration = config; settings.AssetImportContext = ctx; settings.FilterFlags = WorldSystemFilterFlags.HybridGameObjectConversion; WriteEntitySceneSettings writeEntitySettings = new WriteEntitySceneSettings(); if (config != null && config.TryGetComponent <DotsRuntimeBuildProfile>(out var profile)) { if (config.TryGetComponent <DotsRuntimeRootAssembly>(out var rootAssembly)) { writeEntitySettings.Codec = Codec.LZ4; writeEntitySettings.IsDotsRuntime = true; writeEntitySettings.BuildAssemblyCache = new BuildAssemblyCache() { BaseAssemblies = rootAssembly.RootAssembly.asset, PlatformName = profile.Target.UnityPlatformName }; settings.FilterFlags = WorldSystemFilterFlags.DotsRuntimeGameObjectConversion; //Updating the root asmdef references or its references should re-trigger conversion ctx.DependsOnArtifact(AssetDatabase.GetAssetPath(rootAssembly.RootAssembly.asset)); foreach (var assemblyPath in writeEntitySettings.BuildAssemblyCache.AssembliesPath) { ctx.DependsOnArtifact(assemblyPath); } } } var sectionRefObjs = new List <ReferencedUnityObjects>(); var sectionData = EditorEntityScenes.ConvertAndWriteEntityScene(scene, settings, sectionRefObjs, writeEntitySettings); WriteAssetDependencyGUIDs(sectionRefObjs, sectionData, ctx); foreach (var objRefs in sectionRefObjs) { DestroyImmediate(objRefs); } } finally { EditorSceneManager.CloseScene(scene, true); } } // Currently it's not acceptable to let the asset database catch the exception since it will create a default asset without any dependencies // This means a reimport will not be triggered if the scene is subsequently modified catch (Exception e) { Debug.Log($"Exception thrown during SubScene import: {e}"); } }
// 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);
static bool GetMetaDataArtifactPath(Hash128 artifactHash, out string metaDataPath) { metaDataPath = default; if (!AssetDatabaseCompatibility.GetArtifactPaths(artifactHash, out string[] paths))
// 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);
public void Update(List <LiveLinkChangeSet> changeSets, NativeList <Hash128> loadScenes, NativeList <Hash128> unloadScenes, LiveLinkMode mode) { if (_LoadedScenes.Count == 0 && _SceneGUIDToLiveLink.Count == 0 && _RemovedScenes.Length == 0) { return; } // If build configuration changed, we need to trigger a full conversion if (_BuildConfigurationGUID != default) { var buildConfigurationDependencyHash = AssetDatabaseCompatibility.GetAssetDependencyHash(_BuildConfigurationGUID); if (_BuildConfigurationArtifactHash != buildConfigurationDependencyHash) { _BuildConfigurationArtifactHash = buildConfigurationDependencyHash; RequestCleanConversion(); } } if (_PreviousGlobalDirtyID != GlobalDirtyID) { RequestCleanConversion(); _PreviousGlobalDirtyID = GlobalDirtyID; } // By default all scenes need to have m_GameObjectSceneCullingMask, otherwise they won't show up in game view _GUIDToEditScene.Clear(); for (int i = 0; i != EditorSceneManager.sceneCount; i++) { var scene = EditorSceneManager.GetSceneAt(i); var sceneGUID = AssetDatabaseCompatibility.PathToGUID(scene.path); if (_LoadedScenes.Contains(sceneGUID)) { if (scene.isLoaded && sceneGUID != default(GUID)) { _GUIDToEditScene.Add(sceneGUID, scene); } } } foreach (var scene in _SceneGUIDToLiveLink) { if (!_GUIDToEditScene.ContainsKey(scene.Key)) { unloadScenes.Add(scene.Key); } } // Process scenes that are no longer loaded foreach (var scene in unloadScenes) { var liveLink = _SceneGUIDToLiveLink[scene]; liveLink.Dispose(); _SceneGUIDToLiveLink.Remove(scene); _SentLoadScenes.Remove(scene); } foreach (var scene in _RemovedScenes) { if (_SceneGUIDToLiveLink.TryGetValue(scene, out var liveLink)) { liveLink.Dispose(); _SceneGUIDToLiveLink.Remove(scene); } unloadScenes.Add(scene); _SentLoadScenes.Remove(scene); } _RemovedScenes.Clear(); _SentLoadScenes.RemoveWhere(scene => !_LoadedScenes.Contains(scene)); // Process all scenes that the player needs var conversionMode = LiveConversionSettings.Mode; foreach (var sceneGuid in _LoadedScenes) { var isLoaded = _GUIDToEditScene.TryGetValue(sceneGuid, out var scene); // We are editing with live link. Ensure it is active & up to date if (isLoaded) { var liveLink = GetLiveLink(sceneGuid); if (liveLink == null || liveLink.DidRequestUpdate()) { AddLiveLinkChangeSet(ref liveLink, sceneGuid, changeSets, mode); } #if !UNITY_2020_2_OR_NEWER else if (liveLink.LiveLinkDirtyID != GetSceneDirtyID(scene)) { AddLiveLinkChangeSet(ref liveLink, sceneGuid, changeSets, mode); } #endif #if UNITY_2020_2_OR_NEWER if (conversionMode == LiveConversionSettings.ConversionMode.IncrementalConversionWithDebug) { if (liveLink != null && liveLink.DidRequestDebugConversion()) { if (IsHotControlActive()) { EditorUpdateUtility.EditModeQueuePlayerLoopUpdate(); } else { liveLink.DebugIncrementalConversion(); } } } #endif } else { if (_SentLoadScenes.Add(sceneGuid)) { loadScenes.Add(sceneGuid); } } } }
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")); }