public static LoadableScene[] GetLoadableScenes(SubScene[] scenes) { var loadables = new List <LoadableScene>(); foreach (var scene in scenes) { var sceneHeaderPath = EntityScenesPaths.GetLoadPath(scene.SceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1); var sceneHeader = AssetDatabase.LoadAssetAtPath <SubSceneHeader>(sceneHeaderPath); if (sceneHeader != null && sceneHeader.Sections != null && scene._SceneEntities.Count == sceneHeader.Sections.Length) { for (int i = 0; i != sceneHeader.Sections.Length; i++) { var name = scene.SceneAsset.name; var sectionIndex = sceneHeader.Sections[i].SubSectionIndex; if (sectionIndex != 0) { name += $" Section: {sectionIndex}"; } loadables.Add(new LoadableScene { Scene = scene._SceneEntities[i], Name = name }); } } } return(loadables.ToArray()); }
public static string GetEntitySceneWarning(SubScene[] scenes, out bool requireCacheRebuild) { requireCacheRebuild = false; foreach (var scene in scenes) { if (scene.SceneAsset == null) { return($"Please assign a valid Scene Asset"); } var sceneHeaderPath = EntityScenesPaths.GetLoadPath(scene.SceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1); var sceneHeader = AssetDatabase.LoadAssetAtPath <SubSceneHeader>(sceneHeaderPath); if (sceneHeader == null) { requireCacheRebuild = true; return($"The entity binary file header couldn't be found. Please Rebuild Entity Cache."); } //@TODO: validate header against wrong types? //@TODO: validate actual errors when loading //@TODO: validate against dependency chain being out of date } return(null); }
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); }
public void SubScene_WithoutContents_DeletingSceneAssetUnloadsScene() { var subScene = SubSceneTestsHelper.CreateSubSceneFromObjects(ref m_TempAssets, "SubScene", false, null); subScene.AutoLoadScene = false; subScene.gameObject.SetActive(false); var world = World.DefaultGameObjectInjectionWorld; var sceneSystem = world.GetOrCreateSystem <SceneSystem>(); var sceneEntity = sceneSystem.LoadSceneAsync(subScene.SceneGUID, new SceneSystem.LoadParameters { Flags = SceneLoadFlags.BlockOnImport | SceneLoadFlags.BlockOnStreamIn }); Assert.IsFalse(sceneSystem.IsSceneLoaded(sceneEntity), "Scene should not be loaded yet."); // TODO: Editor doesn't update if it doesn't have focus, so we must explicitly update the world to process the load. world.Update(); Assert.IsTrue(sceneSystem.IsSceneLoaded(sceneEntity), "Failed to load scene"); AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(subScene.SceneAsset)); // Block the import of this subscene so that we can get a single-frame result for this test var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, sceneSystem.BuildConfigurationGUID, ImportMode.Synchronous); Assert.IsTrue(hash.IsValid, "Failed to import SubScene."); LogAssert.Expect(LogType.Error, new Regex("Loading Entity Scene failed.*")); // TODO: Editor doesn't update if it doesn't have focus, so we must explicitly update the world to process the load. world.Update(); Assert.IsFalse(sceneSystem.IsSceneLoaded(sceneEntity), "Scene should not be loaded"); }
public void SubScene_WithDependencyOnAssetInScene_Reimport_EndToEnd() { var subScene = SubSceneTestsHelper.CreateSubSceneFromObjects(ref m_TempAssets, "SubScene", false, () => { var go = new GameObject(); var authoring = go.AddComponent <DependencyTestAuthoring>(); var sphereHolder = GameObject.CreatePrimitive(PrimitiveType.Sphere); authoring.Asset = sphereHolder.GetComponent <MeshFilter>().sharedMesh; UnityEngine.Object.DestroyImmediate(sphereHolder); return(new List <GameObject> { go }); }); var buildSettings = default(Unity.Entities.Hash128); var originalHash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(originalHash.IsValid); SubSceneInspectorUtility.ForceReimport(new [] { subScene }); var newHashCreated = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(newHashCreated.IsValid); Assert.AreNotEqual(originalHash, newHashCreated); SubSceneInspectorUtility.ForceReimport(new [] { subScene }); var newHashUpdated = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(newHashUpdated.IsValid); Assert.AreNotEqual(newHashCreated, newHashUpdated); Assert.AreNotEqual(originalHash, newHashUpdated); }
public void SubScene_WithDependencyOnAsset_IsInvalidatedWhenAssetChanges() { var subScene = SubSceneTestsHelper.CreateSubSceneFromObjects(ref m_TempAssets, "SubScene", false, () => { var go = new GameObject(); var authoring = go.AddComponent <DependencyTestAuthoring>(); authoring.Asset = m_Texture1; return(new List <GameObject> { go }); }); var buildSettings = default(Unity.Entities.Hash128); var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(hash.IsValid); m_Texture1.wrapMode = m_Texture1.wrapMode == TextureWrapMode.Repeat ? TextureWrapMode.Mirror : TextureWrapMode.Repeat; AssetDatabase.SaveAssets(); var newHash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.NoImport); Assert.AreNotEqual(hash, newHash); Assert.IsFalse(newHash.IsValid); }
public void TestGetSectionIndexFromPath() { Assert.AreEqual(0, EntityScenesPaths.GetSectionIndexFromPath("")); Assert.AreEqual(5, EntityScenesPaths.GetSectionIndexFromPath("somelongpath.5.entities")); Assert.AreEqual(100, EntityScenesPaths.GetSectionIndexFromPath("somelongpath.100.entities")); Assert.AreEqual(99, EntityScenesPaths.GetSectionIndexFromPath("somelongpathwith.dots.in.the.name.98.99.entities")); Assert.AreEqual(0, EntityScenesPaths.GetSectionIndexFromPath("pathwith.dots.but.no.number.entities")); }
static int WriteEntityScene(EntityManager scene, Entities.Hash128 sceneGUID, string subsection, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos = default(NativeArray <EntityRemapUtility.EntityRemapInfo>)) { k_ProfileEntitiesSceneSave.Begin(); var entitiesBinaryPath = EntityScenesPaths.GetPathAndCreateDirectory(sceneGUID, EntityScenesPaths.PathType.EntitiesBinary, subsection); var sharedDataPath = EntityScenesPaths.GetPathAndCreateDirectory(sceneGUID, EntityScenesPaths.PathType.EntitiesSharedComponents, subsection); GameObject sharedComponents; // We're going to do file writing manually, so make sure to do version control dance if needed if (Provider.isActive && !AssetDatabase.IsOpenForEdit(entitiesBinaryPath, StatusQueryOptions.UseCachedIfPossible)) { var task = Provider.Checkout(entitiesBinaryPath, CheckoutMode.Asset); task.Wait(); if (!task.success) { throw new System.Exception($"Failed to checkout entity cache file {entitiesBinaryPath}"); } } // Write binary entity file int entitySceneFileSize = 0; using (var writer = new StreamBinaryWriter(entitiesBinaryPath)) { if (entityRemapInfos.IsCreated) { SerializeUtilityHybrid.Serialize(scene, writer, out sharedComponents, entityRemapInfos); } else { SerializeUtilityHybrid.Serialize(scene, writer, out sharedComponents); } entitySceneFileSize = (int)writer.Length; } // Write shared component data prefab k_ProfileEntitiesSceneCreatePrefab.Begin(); //var oldPrefab = AssetDatabase.LoadMainAssetAtPath(sharedDataPath); //if (oldPrefab == null) // PrefabUtility.CreatePrefab(sharedDataPath, sharedComponents, ReplacePrefabOptions.ReplaceNameBased); if (sharedComponents != null) { PrefabUtility.SaveAsPrefabAsset(sharedComponents, sharedDataPath); } //else // PrefabUtility.Save //PrefabUtility.ReplacePrefab(sharedComponents, oldPrefab, ReplacePrefabOptions.ReplaceNameBased); Object.DestroyImmediate(sharedComponents); k_ProfileEntitiesSceneCreatePrefab.End(); k_ProfileEntitiesSceneSave.End(); return(entitySceneFileSize); }
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.")); } }
static void WriteHeader(Entities.Hash128 sceneGUID, SubSceneHeader header) { k_ProfileEntitiesSceneSaveHeader.Begin(); string headerPath = EntityScenesPaths.GetPathAndCreateDirectory(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, ""); AssetDatabase.CreateAsset(header, headerPath); //subscene.CacheSceneInformation(); k_ProfileEntitiesSceneSaveHeader.End(); }
public void TestGetTempCachePath() { var tempCachePath = EntityScenesPaths.GetTempCachePath(); // Verify we can make and remove this path if (!Directory.Exists(tempCachePath)) { Directory.CreateDirectory(tempCachePath); } Directory.Delete(tempCachePath); }
// 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);
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 int WriteEntityScene(EntityManager scene, Entities.Hash128 sceneGUID, string subsection, NativeArray <EntityRemapUtility.EntityRemapInfo> entityRemapInfos = default(NativeArray <EntityRemapUtility.EntityRemapInfo>)) { k_ProfileEntitiesSceneSave.Begin(); var entitiesBinaryPath = EntityScenesPaths.GetPathAndCreateDirectory(sceneGUID, EntityScenesPaths.PathType.EntitiesBinary, subsection); var sharedDataPath = EntityScenesPaths.GetPathAndCreateDirectory(sceneGUID, EntityScenesPaths.PathType.EntitiesSharedComponents, subsection); GameObject sharedComponents; // Write binary entity file int entitySceneFileSize = 0; using (var writer = new StreamBinaryWriter(entitiesBinaryPath)) { if (entityRemapInfos.IsCreated) { SerializeUtilityHybrid.Serialize(scene, writer, out sharedComponents, entityRemapInfos); } else { SerializeUtilityHybrid.Serialize(scene, writer, out sharedComponents); } entitySceneFileSize = (int)writer.Length; } // Write shared component data prefab k_ProfileEntitiesSceneCreatePrefab.Begin(); //var oldPrefab = AssetDatabase.LoadMainAssetAtPath(sharedDataPath); //if (oldPrefab == null) // PrefabUtility.CreatePrefab(sharedDataPath, sharedComponents, ReplacePrefabOptions.ReplaceNameBased); if (sharedComponents != null) { PrefabUtility.SaveAsPrefabAsset(sharedComponents, sharedDataPath); } //else // PrefabUtility.Save //PrefabUtility.ReplacePrefab(sharedComponents, oldPrefab, ReplacePrefabOptions.ReplaceNameBased); Object.DestroyImmediate(sharedComponents); k_ProfileEntitiesSceneCreatePrefab.End(); k_ProfileEntitiesSceneSave.End(); return(entitySceneFileSize); }
public void SetUpOnce() { #if UNITY_EDITOR try { BuildConfiguration.CreateAsset(m_BuildConfigPath, config => { config.SetComponent(new SceneList { SceneInfos = new List <SceneList.SceneInfo> { new SceneList.SceneInfo { Scene = GlobalObjectId.GetGlobalObjectIdSlow( AssetDatabase.LoadAssetAtPath <SceneAsset>(m_SubScenePath)) } } }); }); m_BuildConfigurationGUID = new GUID(AssetDatabase.AssetPathToGUID(m_BuildConfigPath)); m_SceneGUID = new GUID(AssetDatabase.AssetPathToGUID(m_SubScenePath)); var guid = SceneWithBuildConfigurationGUIDs.EnsureExistsFor(m_SceneGUID, m_BuildConfigurationGUID, true, out var requestRefresh); if (requestRefresh) { AssetDatabase.Refresh(); } m_SceneWithBuildSettingsPath = SceneWithBuildConfigurationGUIDs.GetSceneWithBuildSettingsPath(guid); EntityScenesPaths.GetSubSceneArtifactHash(m_SceneGUID, m_BuildConfigurationGUID, true, ImportMode.Synchronous); } catch { AssetDatabase.DeleteAsset(m_TempPath); AssetDatabase.DeleteAsset(m_SceneWithBuildSettingsPath); throw; } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); #else //TODO: Playmode test not supported yet m_SceneGUID = new Unity.Entities.Hash128(); #endif }
public void SubScene_WithDependencyOnAssetInScene_ClearCache_EndToEnd() { var subScene = SubSceneTestsHelper.CreateSubSceneFromObjects(ref m_TempAssets, "SubScene", false, () => { var go = new GameObject(); var authoring = go.AddComponent <DependencyTestAuthoring>(); var sphereHolder = GameObject.CreatePrimitive(PrimitiveType.Sphere); authoring.Asset = sphereHolder.GetComponent <MeshFilter>().sharedMesh; UnityEngine.Object.DestroyImmediate(sphereHolder); return(new List <GameObject> { go }); }); var buildSettings = default(Unity.Entities.Hash128); var originalHash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(originalHash.IsValid); // Clear Cache (First time this creates global dependency asset, so we will test both steps) EntitiesCacheUtility.UpdateEntitySceneGlobalDependency(); var newHashCreated = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(newHashCreated.IsValid); Assert.AreNotEqual(originalHash, newHashCreated); // Clear Cache (This updates existing asset) EntitiesCacheUtility.UpdateEntitySceneGlobalDependency(); var newHashUpdated = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(newHashUpdated.IsValid); Assert.AreNotEqual(newHashCreated, newHashUpdated); Assert.AreNotEqual(originalHash, newHashUpdated); // Delete created dependency, this cleans up test but also we need to verify that the scene returns to the original hash AssetDatabase.DeleteAsset(EntitiesCacheUtility.globalEntitiesDependencyDir); AssetDatabase.Refresh(); // With the dependency deleted, the hash should return to the original var finalHash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.AreEqual(originalHash, finalHash); }
public void SubScene_WithDependencyOnAssetInScene_StillImports() { var subScene = SubSceneTestsHelper.CreateSubSceneFromObjects(ref m_TempAssets, "SubScene", false, () => { var go = new GameObject(); var authoring = go.AddComponent <DependencyTestAuthoring>(); var texture = new Texture2D(64, 64); authoring.Asset = texture; return(new List <GameObject> { go }); }); var buildSettings = default(Unity.Entities.Hash128); var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(hash.IsValid); }
static bool IsSubsceneImported(SubScene subScene) { foreach (var world in World.All) { var sceneSystem = world.GetExistingSystem <SceneSystem>(); if (sceneSystem is null) { continue; } var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, sceneSystem.BuildConfigurationGUID, ImportMode.NoImport); if (!hash.IsValid) { return(false); } } return(true); }
public void SubScene_WithDependencyOnBuiltInAsset_StillImports() { var subScene = SubSceneTestsHelper.CreateSubSceneFromObjects(ref m_TempAssets, "SubScene", false, () => { var go = new GameObject(); var authoring = go.AddComponent <DependencyTestAuthoring>(); var sphereHolder = GameObject.CreatePrimitive(PrimitiveType.Sphere); authoring.Asset = sphereHolder.GetComponent <MeshFilter>().sharedMesh; UnityEngine.Object.DestroyImmediate(sphereHolder); return(new List <GameObject> { go }); }); var buildSettings = default(Unity.Entities.Hash128); var hash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.SceneGUID, buildSettings, ImportMode.Synchronous); Assert.IsTrue(hash.IsValid); }
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)); } } }
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(); } }
//TODO: There is too much code duplication here, refactor this to send general artifacts to the editor unsafe void SendSubScene(Unity.Entities.Hash128 subSceneGuid, Unity.Entities.Hash128 buildSettingsGuid, int playerId) { LiveLinkMsg.LogInfo($"Sending SubScene: 'GUID: {subSceneGuid}' with 'BuildSettings: {buildSettingsGuid}' to playerId: {playerId}"); var hash = EntityScenesPaths.GetSubSceneArtifactHash(subSceneGuid, buildSettingsGuid, AssetDatabaseExperimental.ImportSyncMode.Block); AssetDatabaseExperimental.GetArtifactPaths(hash, out var paths); var sceneHeaderPath = EntityScenesPaths.GetLoadPathFromArtifactPaths(paths, EntityScenesPaths.PathType.EntitiesHeader); if (!File.Exists(sceneHeaderPath)) { Debug.LogError("Send Entity Scene failed because the entity header file could not be found: " + sceneHeaderPath); return; } if (!BlobAssetReference <SceneMetaData> .TryRead(sceneHeaderPath, SceneMetaDataSerializeUtility.CurrentFileFormatVersion, out var sceneMetaDataRef)) { Debug.LogError("Send Entity Scene failed because the entity header file was an old version: " + sceneHeaderPath); return; } ref var sceneMetaData = ref sceneMetaDataRef.Value;
public void RequestSubSceneTargetHash(MessageEventArgs args) { using (var subScenes = args.ReceiveArray <SubSceneGUID>()) { var resolvedScenes = new HashSet <ResolvedSubSceneID>(); foreach (var subScene in subScenes) { LiveLinkMsg.LogInfo($"RequestSubSceneTargetHash => {subScene.Guid}, {subScene.BuildConfigurationGuid}"); var targetHash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.Guid, subScene.BuildConfigurationGuid, UnityEditor.Experimental.AssetDatabaseExperimental.ImportSyncMode.Queue); _UsedSubSceneTargetHash[subScene] = targetHash; if (targetHash.IsValid) { resolvedScenes.Add(new ResolvedSubSceneID { SubSceneGUID = subScene, TargetHash = targetHash }); } } TimeBasedCallbackInvoker.SetCallback(DetectChangedAssets); if (resolvedScenes.Count == 0) { return; } var resolved = new NativeArray <ResolvedSubSceneID>(resolvedScenes.Count, Allocator.Temp); int i = 0; foreach (var id in resolvedScenes) { resolved[i++] = id; } SendSubSceneTargetHash(resolved, args.playerId); } }
public void SubScene_WithDependencyOnAsset_IsInvalidatedWhenAssetChanges() { var subScene = CreateSubScene("SubScene", nameof(SubScene_WithDependencyOnAsset_IsInvalidatedWhenAssetChanges)); SubSceneInspectorUtility.EditScene(subScene); var go = new GameObject(); var authoring = go.AddComponent <DependencyTestAuthoring>(); var dependency = new GameObject(); authoring.GameObject = dependency; var texture = new Texture2D(64, 64); authoring.Asset = texture; var assetPath = Path.Combine(m_TempAssetDir, "Texture.asset"); AssetDatabase.CreateAsset(authoring.Asset, assetPath); SceneManager.MoveGameObjectToScene(dependency, subScene.EditingScene); SceneManager.MoveGameObjectToScene(go, subScene.EditingScene); EditorSceneManager.SaveScene(subScene.EditingScene); AssetDatabase.TryGetGUIDAndLocalFileIdentifier(subScene.SceneAsset, out var guid, out long _); var buildSettings = default(Unity.Entities.Hash128); var subSceneGuid = new GUID(guid); var hash = EntityScenesPaths.GetSubSceneArtifactHash(subSceneGuid, buildSettings, ImportMode.Synchronous); Assert.IsTrue(hash.IsValid); texture.wrapMode = texture.wrapMode == TextureWrapMode.Repeat ? TextureWrapMode.Mirror : TextureWrapMode.Repeat; AssetDatabase.SaveAssets(); var newHash = EntityScenesPaths.GetSubSceneArtifactHash(subSceneGuid, buildSettings, ImportMode.NoImport); Assert.AreNotEqual(hash, newHash); Assert.IsFalse(newHash.IsValid); }
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(); } }
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))); }
void RequestSubSceneTargetHash(MessageEventArgs args) { //@TODO: should be based on connection / BuildSetting var buildTarget = EditorUserBuildSettings.activeBuildTarget; using (var subScenes = args.ReceiveArray <SubSceneGUID>()) { var resolvedScenes = new HashSet <ResolvedSubSceneID>(); var assetDependencies = new HashSet <ResolvedAssetID>(); foreach (var subScene in subScenes) { LiveLinkMsg.LogInfo($"RequestSubSceneTargetHash => {subScene.Guid}, {subScene.BuildConfigurationGuid}"); var targetHash = EntityScenesPaths.GetSubSceneArtifactHash(subScene.Guid, subScene.BuildConfigurationGuid, ImportMode.Asynchronous); m_TrackedSubScenes[subScene] = targetHash; if (targetHash.IsValid) { resolvedScenes.Add(new ResolvedSubSceneID { SubSceneGUID = subScene, TargetHash = targetHash }); var sceneDependencies = LiveLinkBuildPipeline.GetSubSceneDependencies(targetHash); foreach (var sceneDependency in sceneDependencies) { assetDependencies.Add(sceneDependency); m_TrackedAssets[sceneDependency.GUID] = sceneDependency.TargetHash; if (sceneDependency.TargetHash.IsValid) { LiveLinkBuildPipeline.CalculateTargetDependencies(sceneDependency.TargetHash, buildTarget, out ResolvedAssetID[] dependencies, ImportMode.Asynchronous, sceneDependency.GUID); foreach (var dependency in dependencies) { m_TrackedAssets[dependency.GUID] = dependency.TargetHash; assetDependencies.Add(new ResolvedAssetID { GUID = dependency.GUID, TargetHash = dependency.TargetHash }); } } } } } TimeBasedCallbackInvoker.SetCallback(DetectChangedAssets); if (resolvedScenes.Count == 0) { return; } var resolved = new NativeArray <ResolvedSubSceneID>(resolvedScenes.Count, Allocator.Temp); int i = 0; foreach (var id in resolvedScenes) { resolved[i++] = id; } SendSubSceneTargetHash(resolved, args.playerId); if (assetDependencies.Count > 0) { var resolvedAssets = new NativeArray <ResolvedAssetID>(assetDependencies.Count, Allocator.Temp); int assetIndex = 0; foreach (var asset in assetDependencies) { resolvedAssets[assetIndex++] = asset; } SendAssetTargetHash(resolvedAssets, args.playerId); } } }
// 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 static bool HasEntitySceneCache(Hash128 sceneGUID) { string headerPath = EntityScenesPaths.GetPathAndCreateDirectory(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, ""); return(File.Exists(headerPath)); }
// 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);