From 580b2db11250c93a5bc811381e39d3efb7970525 Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Wed, 26 Jun 2024 00:00:00 +0000 Subject: [PATCH] com.unity.addressables@2.2.2 ## [2.2.2] - 2024-06-26 - Fix KeyNotFoundException when clicking on local bundles in the profiler. - Fix bundles incorrectly marked as released in the Profiler when they are still active - Improved error message when trying to load a catalog in an unexpected file format. - Fixed issue where operation that uses WaitForCompletion can timeout much earlier than it should. - The build scripts were reworked so that you can extend them or copy them outside the package without having to fork the entire package. - A Version field was added to the Addressables object for getting the package version in the Editor. - Fixed an issue where tearing down the Addressables instance could happen before user tear down code was getting called. - Sort collections to make serialized editor files deterministic - Fixed issue where labels on an addressable sub-entry are incorrectly added to the former parent entry. - Added support for calling Release() on AsyncOperationHandles directly that couldn't before. - Fixed sub-object loading from AssetReferences for types that are not Sprites in a SpriteAtlas. --- CHANGELOG.md | 18 +- Documentation~/DownloadDependenciesAsync.md | 2 + Editor/Build/AddressableAnalytics.cs | 2 +- Editor/Build/AddressablesDataBuilderInput.cs | 6 +- Editor/Build/AddressablesDataBuilders.cs | 2 +- .../BuildLayoutGenerationTask.cs | 28 +- Editor/Build/ContentUpdateScript.cs | 10 +- .../AddressableAssetsBuildContext.cs | 64 +- .../DataBuilders/BuildLayoutParameters.cs | 47 + .../BuildLayoutParameters.cs.meta | 3 + Editor/Build/DataBuilders/BuildScriptBase.cs | 137 +- .../DataBuilders/BuildScriptPackedMode.cs | 235 ++-- .../Build/FastModeInitializationOperation.cs | 2 +- Editor/Build/Layout/BuildLayout.cs | 4 +- .../AddressablesProfilerDetailsView.cs | 99 +- .../Profiler/BuildLayoutsManager.cs | 35 +- .../Profiler/EditorFrameDataStore.cs | 26 + .../Profiler/EditorFrameDataStore.cs.meta | 3 + .../Diagnostics/Profiler/FrameDataViewRef.cs | 24 + .../Profiler/FrameDataViewRef.cs.meta | 3 + .../Diagnostics/Profiler/IFrameDataStore.cs | 13 + .../Profiler/IFrameDataStore.cs.meta | 3 + Editor/GUI/AssetInspectorGUI.cs | 16 +- Editor/Settings/AddressableAssetEntry.cs | 8 +- Editor/Settings/AddressableAssetSettings.cs | 28 +- Runtime/Addressables.cs | 31 +- Runtime/AddressablesImpl.cs | 120 +- Runtime/AssetReference.cs | 4 +- .../Initialization/CheckCatalogsOperation.cs | 2 +- .../InitializationObjectsOperation.cs | 2 +- .../Initialization/InitializationOperation.cs | 24 +- .../Initialization/UpdateCatalogsOperation.cs | 4 +- .../ResourceLocators/ContentCatalogData.cs | 14 +- .../AsyncOperations/AsyncOperationBase.cs | 4 +- .../AsyncOperations/AsyncOperationHandle.cs | 37 +- .../AsyncOperations/ProviderOperation.cs | 2 - .../Diagnostics/Profiling/EngineEmitter.cs | 24 + .../Profiling/EngineEmitter.cs.meta | 3 + .../Diagnostics/Profiling/IProfilerEmitter.cs | 12 + .../Profiling/IProfilerEmitter.cs.meta | 3 + .../Diagnostics/Profiling/ProfilerRuntime.cs | 22 +- Runtime/ResourceManager/ResourceManager.cs | 13 + .../ResourceProviders/AssetBundleProvider.cs | 20 +- .../ResourceProviders/AtlasSpriteProvider.cs | 8 + .../ResourceProviders/InstanceProvider.cs | 6 +- .../ResourceProviders/SceneProvider.cs | 28 +- .../Simulation/VirtualAssetBundle.cs | 4 +- .../ResourceProviders/TextDataProvider.cs | 43 +- .../Util/ComponentSingleton.cs | 3 +- .../ResourceManager/Util/ListWithEvents.cs | 6 +- .../ContentCatalogProvider.cs | 22 +- .../AddressablesUtility.cs | 6 +- .../ComponentReference/ComponentReference.cs | 2 +- .../Editor/CustomBuild.asset | 2 +- .../Editor/CustomBuild.asset.meta | 2 +- .../Editor/CustomBuildScript.cs | 1204 +---------------- .../Editor/CustomBuildScript.cs.meta | 2 +- .../Editor/CustomPlayMode.asset | 2 +- .../Editor/CustomPlayMode.asset.meta | 2 +- .../Editor/CustomPlayModeScript.cs | 79 +- .../Editor/CustomPlayModeScript.cs.meta | 2 +- Samples~/PrefabSpawner/PrefabSpawnerSample.cs | 4 +- Samples~/Tests/SamplesTests.cs | 4 +- Tests/Editor/AddressableAssetEntryTests.cs | 124 ++ .../AddressableAssetFolderSubfolderTests.cs | 2 +- Tests/Editor/BinaryStorageBufferTests.cs | 3 +- Tests/Editor/Build/BuildScriptPackedTests.cs | 20 +- .../Editor/Build/VerifyPublicBuildScripts.cs | 89 ++ .../Build/VerifyPublicBuildScripts.cs.meta | 11 + Tests/Editor/Diagnostics/Profiler.meta | 7 +- .../AddressablesProfilerDetailsViewTests.cs | 182 ++- .../Profiler/BuildLayoutBuilder.cs | 114 ++ .../Profiler/BuildLayoutBuilder.cs.meta | 3 + .../Profiler/ProfilerEventBuilder.cs | 244 ++++ .../Profiler/ProfilerEventBuilder.cs.meta | 3 + .../Diagnostics/Profiler/TestProfiler.cs | 124 ++ .../Diagnostics/Profiler/TestProfiler.cs.meta | 3 + .../DocExampleCode/AsynchronousLoading.cs | 2 +- Tests/Editor/DocExampleCode/LoadLocation.cs | 4 +- Tests/Editor/DocExampleCode/LoadMultiple.cs | 2 +- Tests/Editor/DocExampleCode/LoadSingle.cs | 2 +- .../DocExampleCode/LoadSynchronously.cs | 4 +- .../Editor/DocExampleCode/LoadWithAddress.cs | 2 +- Tests/Editor/DocExampleCode/LoadWithEvent.cs | 4 +- .../DocExampleCode/LoadWithIEnumerator.cs | 4 +- Tests/Editor/DocExampleCode/LoadWithLabels.cs | 2 +- Tests/Editor/DocExampleCode/LoadWithTask.cs | 2 +- .../DocExampleCode/MiscellaneousTopics.cs | 10 +- .../DocExampleCode/PreloadWithProgress.cs | 2 +- .../ScriptReference/ContentBuiltCheck.cs | 2 + .../ScriptReference/UsingCleanBundleCache.cs | 4 +- .../ScriptReference/UsingInitializeAsync.cs | 2 +- .../ScriptReference/UsingInstanceProvider.cs | 4 +- .../ScriptReference/UsingInstantiateAsync.cs | 4 +- .../UsingInternalIdTransformFunc.cs | 2 +- .../ScriptReference/UsingLoadAssetAsync.cs | 6 +- ...ssables.Editor.Tests.DocExampleCode.asmdef | 55 + ...es.Editor.Tests.DocExampleCode.asmdef.meta | 7 + Tests/Editor/Fixtures/buildlayout1.json | 2 + Tests/Editor/Fixtures/buildlayout1.json.meta | 7 + .../Editor/OptionalPackages/Diagnostics.meta | 3 - .../Diagnostics/Profiler.meta | 3 - .../AddressablesProfilerDetailsViewTests.cs | 99 -- Tests/Editor/SerializationTests.cs | 6 +- Tests/Runtime/AddressablesIntegrationTests.cs | 10 + .../AddressablesIntegrationTestsImpl.cs | 255 +++- Tests/Runtime/AddressablesTestFixture.cs | 6 +- Tests/Runtime/AddressablesTestUtilities.cs | 28 +- Tests/Runtime/AssetReferenceDrawerTests.cs | 2 +- Tests/Runtime/DynamicContentUpdateTests.cs | 18 +- .../Operations/AsyncOperationHandleTests.cs | 124 ++ .../Operations/BaseOperationBehaviorTests.cs | 65 +- .../ResourceManager/ResourceManagerTests.cs | 5 +- Tests/Runtime/SceneTests.cs | 105 +- Tests/Runtime/SyncAddressableTests.cs | 6 +- package.json | 12 +- 116 files changed, 2438 insertions(+), 1963 deletions(-) create mode 100644 Editor/Build/DataBuilders/BuildLayoutParameters.cs create mode 100644 Editor/Build/DataBuilders/BuildLayoutParameters.cs.meta create mode 100644 Editor/Diagnostics/Profiler/EditorFrameDataStore.cs create mode 100644 Editor/Diagnostics/Profiler/EditorFrameDataStore.cs.meta create mode 100644 Editor/Diagnostics/Profiler/FrameDataViewRef.cs create mode 100644 Editor/Diagnostics/Profiler/FrameDataViewRef.cs.meta create mode 100644 Editor/Diagnostics/Profiler/IFrameDataStore.cs create mode 100644 Editor/Diagnostics/Profiler/IFrameDataStore.cs.meta create mode 100644 Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs create mode 100644 Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs.meta create mode 100644 Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs create mode 100644 Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs.meta create mode 100644 Tests/Editor/Build/VerifyPublicBuildScripts.cs create mode 100644 Tests/Editor/Build/VerifyPublicBuildScripts.cs.meta create mode 100644 Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs create mode 100644 Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs.meta create mode 100644 Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs create mode 100644 Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs.meta create mode 100644 Tests/Editor/Diagnostics/Profiler/TestProfiler.cs create mode 100644 Tests/Editor/Diagnostics/Profiler/TestProfiler.cs.meta create mode 100644 Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef create mode 100644 Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef.meta create mode 100644 Tests/Editor/Fixtures/buildlayout1.json create mode 100644 Tests/Editor/Fixtures/buildlayout1.json.meta delete mode 100644 Tests/Editor/OptionalPackages/Diagnostics.meta delete mode 100644 Tests/Editor/OptionalPackages/Diagnostics/Profiler.meta delete mode 100644 Tests/Editor/OptionalPackages/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 43a13110..f1186f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,24 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [2.2.2] - 2024-06-26 +- Fix KeyNotFoundException when clicking on local bundles in the profiler. +- Fix bundles incorrectly marked as released in the Profiler when they are still active +- Improved error message when trying to load a catalog in an unexpected file format. +- Fixed issue where operation that uses WaitForCompletion can timeout much earlier than it should. +- The build scripts were reworked so that you can extend them or copy them outside the package without having to fork the entire package. +- A Version field was added to the Addressables object for getting the package version in the Editor. +- Fixed an issue where tearing down the Addressables instance could happen before user tear down code was getting called. +- Sort collections to make serialized editor files deterministic +- Fixed issue where labels on an addressable sub-entry are incorrectly added to the former parent entry. +- Added support for calling Release() on AsyncOperationHandles directly that couldn't before. +- Fixed sub-object loading from AssetReferences for types that are not Sprites in a SpriteAtlas. + + ## [2.1.0] - 2024-03-19 - Fix "Unable to verify target bucket for Remote Catalog: Not Found. Object could not be found" error - Fixed caching to prevent unnecessary refreshing of bucket data. -- Sort collections on serialization to prevent unecessary merge conflicts +- Sort collections on serialization to prevent unnecessary merge conflicts - Add warnings and documentation to make it clear you need to release the handle from LoadDependenciesAsync ## [2.0.8] - 2024-01-19 @@ -63,6 +77,8 @@ CCD Manager is built when using the Build to CCD and the standard Build content - Fixed an issue where "Failed to remove scene from Addressables profiler" warning occurs when a scene is unloaded. - Fixed an exception getting thrown in the Addressables Report when drilling into a bundle chain - Fixed string deduplication in binary catalogs. Certain data sets were causing data to expand. +- Removed the Analyze Rule API and corresponding tool +- Removed the Event Viewer API and corresponding tool ## [1.21.18] - 2023-09-23 - Fixed an issue where scene InternalId collisions were very likely when using dynamic internal asset naming diff --git a/Documentation~/DownloadDependenciesAsync.md b/Documentation~/DownloadDependenciesAsync.md index 7f141d1c..bc0e3fd1 100644 --- a/Documentation~/DownloadDependenciesAsync.md +++ b/Documentation~/DownloadDependenciesAsync.md @@ -36,6 +36,8 @@ Always release the download operation handle after you have read the `Result` ob [!code-cs[sample](../Tests/Editor/DocExampleCode/Preload.cs#doc_Preload)] +To note: On the WebGL platform, this API always returns the size of the AssetBundle, even if the AssetBundle has been cached. Cached AssetBundles are not stored on the local file system, but persisted as part of the IndexedDB of the browser. [WebGL Caching](https://docs.unity3d.com/Manual/webgl-caching.html) + ### Clear the dependency cache If you want to clear any AssetBundles cached by Addressables, call [`Addressables.ClearDependencyCacheAsync`](xref:UnityEngine.AddressableAssets.Addressables.ClearDependencyCacheAsync*). This method clears the cached AssetBundles containing the assets identified by a key along with any bundles containing those assets' dependencies. diff --git a/Editor/Build/AddressableAnalytics.cs b/Editor/Build/AddressableAnalytics.cs index 9215e586..dc0bf8e8 100644 --- a/Editor/Build/AddressableAnalytics.cs +++ b/Editor/Build/AddressableAnalytics.cs @@ -467,7 +467,7 @@ internal static BuildData GenerateBuildData(AddressablesDataBuilderInput builder if (selected == -1) pathType = PathType.Custom; else - pathType = prefixToTypeMap[groupTypes[selected].GroupTypePrefix]; + pathType = prefixToTypeMap.GetValueOrDefault(groupTypes[selected].GroupTypePrefix, PathType.Custom); if (pathType == PathType.Custom) { diff --git a/Editor/Build/AddressablesDataBuilderInput.cs b/Editor/Build/AddressablesDataBuilderInput.cs index c014748d..e83a2f41 100644 --- a/Editor/Build/AddressablesDataBuilderInput.cs +++ b/Editor/Build/AddressablesDataBuilderInput.cs @@ -34,8 +34,10 @@ public class AddressablesDataBuilderInput /// public FileRegistry Registry { get; private set; } - //used only by tests to inject custom info into build... - internal string PathSuffix = string.Empty; + /// + /// can be used in testing to append a suffix to file paths + /// + public string PathSuffix = string.Empty; /// /// The name of the default Runtime Settings file. diff --git a/Editor/Build/AddressablesDataBuilders.cs b/Editor/Build/AddressablesDataBuilders.cs index 3005b1a8..76599178 100644 --- a/Editor/Build/AddressablesDataBuilders.cs +++ b/Editor/Build/AddressablesDataBuilders.cs @@ -118,7 +118,7 @@ public class BundleBuildResult /// /// True if the build was doing an update to a previous build, else false. /// - public bool IsUpdateContentBuild { get; internal set; } + public bool IsUpdateContentBuild { get; set; } /// /// Build results for AssetBundles created during the build. diff --git a/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs b/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs index 7603f2a5..c46c11f3 100644 --- a/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs +++ b/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs @@ -38,12 +38,13 @@ public int Version } /// - /// The mapping of the old to new bundle names. + /// The mapping of the old to new bundle names. Instead of using this directly inject + /// the value through IBuildLayoutParUse BuildLayoutParameters.BundleNameRemap instead /// public Dictionary BundleNameRemap { - get { return m_BundleNameRemap; } - set { m_BundleNameRemap = value; } + get { return m_BuildLayoutParameters.BundleNameRemap; } + set { m_BuildLayoutParameters.BundleNameRemap = value; } } #pragma warning disable 649 @@ -70,11 +71,12 @@ public int Version [InjectContext(ContextUsage.In)] IBundleBuildResults m_BuildBundleResults; + + [InjectContext(ContextUsage.In)] + IBuildLayoutParameters m_BuildLayoutParameters; #pragma warning restore 649 - internal Dictionary m_BundleNameRemap; internal AddressablesDataBuilderInput m_AddressablesInput; - internal ContentCatalogData m_ContentCatalogData; private bool IsContentUpdateBuild => m_AddressablesInput != null && m_AddressablesInput.PreviousContentState != null; @@ -693,14 +695,14 @@ private BuildLayout GenerateBuildLayout(AddressableAssetsBuildContext aaContext, layout.RemoteCatalogBuildPath = aaContext.Settings.RemoteCatalogBuildPath.GetValue(aaContext.Settings); AddressableAssetSettings aaSettings = aaContext.Settings; - if (m_ContentCatalogData != null) - layout.BuildResultHash = m_ContentCatalogData.m_BuildResultHash; + if (m_BuildLayoutParameters.BuildResultHash != null) + layout.BuildResultHash = m_BuildLayoutParameters.BuildResultHash; using (m_Log.ScopedStep(LogLevel.Info, "Generate Basic Information")) { SetLayoutMetaData(layout, aaSettings); layout.AddressablesEditorSettings = GetAddressableEditorSettings(aaSettings); - layout.AddressablesRuntimeSettings = GetAddressableRuntimeSettings(aaContext, m_ContentCatalogData); + layout.AddressablesRuntimeSettings = GetAddressableRuntimeSettings(aaContext); } if (IsContentUpdateBuild) @@ -847,7 +849,7 @@ void CorrelateBundleToAssetGroup(BuildLayout layout, BuildLayout.Bundle b, Layou if (aaContext.bundleToAssetGroup.TryGetValue(b.Name, out var grpName)) { var assetGroup = lookup.GroupLookup[grpName]; - b.Name = m_BundleNameRemap[b.Name]; + b.Name = m_BuildLayoutParameters.BundleNameRemap[b.Name]; b.Group = assetGroup; lookup.FilenameToBundle[b.Name] = b; var filePath = Path.Combine(lookup.GroupNameToBuildPath[assetGroup.Name], b.Name); @@ -862,7 +864,7 @@ void CorrelateBundleToAssetGroup(BuildLayout layout, BuildLayout.Bundle b, Layou { // bundleToAssetGroup doesn't contain the builtin bundles. The builtin content is built using values from the default group AddressableAssetGroup defaultGroup = aaContext.Settings.DefaultGroup; - b.Name = m_BundleNameRemap[b.Name]; + b.Name = m_BuildLayoutParameters.BundleNameRemap[b.Name]; b.Group = lookup.GroupLookup[defaultGroup.Guid]; // should this be set? lookup.FilenameToBundle[b.Name] = b; @@ -1161,7 +1163,7 @@ private static void SetLayoutMetaData(BuildLayout layoutOut, AddressableAssetSet layoutOut.PlayerBuildVersion = aaSettings.PlayerBuildVersion; } - static BuildLayout.AddressablesRuntimeData GetAddressableRuntimeSettings(AddressableAssetsBuildContext aaContext, ContentCatalogData contentCatalog) + BuildLayout.AddressablesRuntimeData GetAddressableRuntimeSettings(AddressableAssetsBuildContext aaContext) { if (aaContext.runtimeData == null) { @@ -1175,8 +1177,8 @@ static BuildLayout.AddressablesRuntimeData GetAddressableRuntimeSettings(Address runtimeSettings.CatalogLoadPaths = new List(); foreach (ResourceLocationData catalogLocation in aaContext.runtimeData.CatalogLocations) runtimeSettings.CatalogLoadPaths.Add(catalogLocation.InternalId); - if (contentCatalog != null) - runtimeSettings.CatalogHash = contentCatalog.localHash; + if (m_BuildLayoutParameters.CatalogHash != null) + runtimeSettings.CatalogHash = m_BuildLayoutParameters.CatalogHash; return runtimeSettings; } diff --git a/Editor/Build/ContentUpdateScript.cs b/Editor/Build/ContentUpdateScript.cs index 211e5fd9..0d30bf40 100644 --- a/Editor/Build/ContentUpdateScript.cs +++ b/Editor/Build/ContentUpdateScript.cs @@ -371,7 +371,7 @@ static bool HasAssetOrDependencyChanged(CachedAssetState cachedInfo) /// The server path (if any) that contains an updateable content catalog. If this is empty, updates cannot occur. /// Cached state that needs to carry over from the previous build. This mainly affects Content Update. /// True if the file is saved, false otherwise. - internal static bool SaveContentState(List locations, Dictionary> guidToCatalogLocation, string path, List entries, IDependencyData dependencyData, string playerVersion, + public static bool SaveContentState(List locations, Dictionary> guidToCatalogLocation, string path, List entries, IDependencyData dependencyData, string playerVersion, string remoteCatalogPath, List carryOverCacheState) { try @@ -477,7 +477,13 @@ public static string GetContentStateDataPath(bool browse) return GetContentStateDataPath(browse, null); } - internal static string GetContentStateDataPath(bool browse, AddressableAssetSettings settings) + /// + /// Gets the path of the cache data from a selected build. + /// + /// If true, the user is allowed to browse for a specific file. + /// The settings object to use for the build. + /// The path of the previous state .bin file used to detect changes from the previous build to the content update build. + public static string GetContentStateDataPath(bool browse, AddressableAssetSettings settings) { if (settings == null) settings = AddressableAssetSettingsDefaultObject.Settings; diff --git a/Editor/Build/DataBuilders/AddressableAssetsBuildContext.cs b/Editor/Build/DataBuilders/AddressableAssetsBuildContext.cs index cdba7af6..c74a71b3 100644 --- a/Editor/Build/DataBuilders/AddressableAssetsBuildContext.cs +++ b/Editor/Build/DataBuilders/AddressableAssetsBuildContext.cs @@ -94,68 +94,6 @@ public AddressableAssetSettings Settings /// /// A mapping of Asset GUID's to resulting ContentCatalogDataEntry entries. /// - internal Dictionary> GuidToCatalogLocation = null; - - private Dictionary> m_PrimaryKeyToDependers = null; - - internal Dictionary> PrimaryKeyToDependerLocations - { - get - { - if (m_PrimaryKeyToDependers != null) - return m_PrimaryKeyToDependers; - if (locations == null || locations.Count == 0) - { - Debug.LogError("Attempting to get Entries dependent on key, but currently no locations"); - return new Dictionary>(0); - } - - m_PrimaryKeyToDependers = new Dictionary>(locations.Count); - foreach (ContentCatalogDataEntry location in locations) - { - for (int i = 0; i < location.Dependencies.Count; ++i) - { - string dependencyKey = location.Dependencies[i] as string; - if (string.IsNullOrEmpty(dependencyKey)) - continue; - - if (!m_PrimaryKeyToDependers.TryGetValue(dependencyKey, out var dependers)) - { - dependers = new List(); - m_PrimaryKeyToDependers.Add(dependencyKey, dependers); - } - - dependers.Add(location); - } - } - - return m_PrimaryKeyToDependers; - } - } - - private Dictionary m_PrimaryKeyToLocation = null; - - internal Dictionary PrimaryKeyToLocation - { - get - { - if (m_PrimaryKeyToLocation != null) - return m_PrimaryKeyToLocation; - if (locations == null || locations.Count == 0) - { - Debug.LogError("Attempting to get Primary key to entries dependent on key, but currently no locations"); - return new Dictionary(); - } - - m_PrimaryKeyToLocation = new Dictionary(); - foreach (var loc in locations) - { - if (loc != null && loc.Keys[0] != null && loc.Keys[0] is string && !m_PrimaryKeyToLocation.ContainsKey((string)loc.Keys[0])) - m_PrimaryKeyToLocation[(string)loc.Keys[0]] = loc; - } - - return m_PrimaryKeyToLocation; - } - } + public Dictionary> GuidToCatalogLocation = null; } } diff --git a/Editor/Build/DataBuilders/BuildLayoutParameters.cs b/Editor/Build/DataBuilders/BuildLayoutParameters.cs new file mode 100644 index 00000000..afefd610 --- /dev/null +++ b/Editor/Build/DataBuilders/BuildLayoutParameters.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using UnityEditor.Build.Pipeline.Interfaces; +using UnityEngine.AddressableAssets.ResourceLocators; + +namespace UnityEditor.AddressableAssets.Build.DataBuilders +{ + public interface IBuildLayoutParameters : IContextObject + { + Dictionary BundleNameRemap { get; set; } + + string BuildResultHash { get; } + + string CatalogHash { get; } + } + + public class BuildLayoutParameters : IBuildLayoutParameters + { + private Dictionary m_BundleNameRemap; + private ContentCatalogData m_contentCatalogData; + + public BuildLayoutParameters(Dictionary bundleNameRemap) + { + m_BundleNameRemap = bundleNameRemap; + } + + public BuildLayoutParameters(Dictionary bundleNameRemap, ContentCatalogData contentCatalogData) + { + m_BundleNameRemap = bundleNameRemap; + m_contentCatalogData = contentCatalogData; + } + public Dictionary BundleNameRemap + { + get => m_BundleNameRemap; + set => m_BundleNameRemap = value; + } + + public string BuildResultHash + { + get => m_contentCatalogData?.BuildResultHash; + } + + public string CatalogHash + { + get => m_contentCatalogData?.LocalHash; + } + } +} diff --git a/Editor/Build/DataBuilders/BuildLayoutParameters.cs.meta b/Editor/Build/DataBuilders/BuildLayoutParameters.cs.meta new file mode 100644 index 00000000..d5022b4d --- /dev/null +++ b/Editor/Build/DataBuilders/BuildLayoutParameters.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5813cc79f668462b91d624cc6fec9ae3 +timeCreated: 1713381856 \ No newline at end of file diff --git a/Editor/Build/DataBuilders/BuildScriptBase.cs b/Editor/Build/DataBuilders/BuildScriptBase.cs index 052bd34e..b6324a90 100644 --- a/Editor/Build/DataBuilders/BuildScriptBase.cs +++ b/Editor/Build/DataBuilders/BuildScriptBase.cs @@ -2,10 +2,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; +#if UNITY_2022_2_OR_NEWER +using UnityEditor.AddressableAssets.BuildReportVisualizer; +#endif using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEditor.Build.Pipeline.Interfaces; using UnityEditor.Build.Pipeline.Utilities; +using UnityEditor.Experimental; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets.Initialization; @@ -13,6 +17,8 @@ using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.ResourceManagement.Util; using UnityEngine.Serialization; +using System.Reflection; + namespace UnityEditor.AddressableAssets.Build.DataBuilders { @@ -21,6 +27,9 @@ namespace UnityEditor.AddressableAssets.Build.DataBuilders /// public class BuildScriptBase : ScriptableObject, IDataBuilder { + /// + /// Static part of the builtin bundle filename. + /// public const string BuiltInBundleBaseName = "_unitybuiltinassets"; /// @@ -28,14 +37,14 @@ public class BuildScriptBase : ScriptableObject, IDataBuilder /// [FormerlySerializedAs("m_InstanceProviderType")] [SerializedTypeRestrictionAttribute(type = typeof(IInstanceProvider))] - public SerializedType instanceProviderType = new SerializedType() {Value = typeof(InstanceProvider)}; + public SerializedType instanceProviderType = new SerializedType() { Value = typeof(InstanceProvider) }; /// /// The type of scene provider to create for the addressables system. /// [FormerlySerializedAs("m_SceneProviderType")] [SerializedTypeRestrictionAttribute(type = typeof(ISceneProvider))] - public SerializedType sceneProviderType = new SerializedType() {Value = typeof(SceneProvider)}; + public SerializedType sceneProviderType = new SerializedType() { Value = typeof(SceneProvider) }; /// /// Stores the logged information of all the build tasks. @@ -155,6 +164,12 @@ protected virtual string ProcessAllGroups(AddressableAssetsBuildContext aaContex if (assetGroup == null) continue; + var error = ErrorCheckBundleSettings(assetGroup, aaContext); + if (error != string.Empty) + { + return error; + } + EditorUtility.DisplayProgressBar($"Processing Addressable Group", assetGroup.Name, (float)index / aaContext.Settings.groups.Count); var errorString = ProcessGroup(assetGroup, aaContext); if (!string.IsNullOrEmpty(errorString)) @@ -171,6 +186,47 @@ protected virtual string ProcessAllGroups(AddressableAssetsBuildContext aaContex return string.Empty; } + + internal static string ErrorCheckBundleSettings(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext) + { + if (!assetGroup.HasSchema()) + return string.Empty; + + var message = string.Empty; + var settings = aaContext.Settings; + var schema = assetGroup.GetSchema(); + + string buildPath = settings.profileSettings.GetValueById(settings.activeProfileId, schema.BuildPath.Id); + string loadPath = settings.profileSettings.GetValueById(settings.activeProfileId, schema.LoadPath.Id); + + bool buildLocal = AddressableAssetUtility.StringContains(buildPath, "[UnityEngine.AddressableAssets.Addressables.BuildPath]", StringComparison.Ordinal); + bool loadLocal = AddressableAssetUtility.StringContains(loadPath, "{UnityEngine.AddressableAssets.Addressables.RuntimePath}", StringComparison.Ordinal); + + if (buildLocal && !loadLocal) + { + message = "BuildPath for group '" + assetGroup.Name + "' is set to the dynamic-lookup version of StreamingAssets, but LoadPath is not. \n"; + } + else if (!buildLocal && loadLocal) + { + message = "LoadPath for group " + assetGroup.Name + + " is set to the dynamic-lookup version of StreamingAssets, but BuildPath is not. These paths must both use the dynamic-lookup, or both not use it. \n"; + } + + if (!string.IsNullOrEmpty(message)) + { + message += "BuildPath: '" + buildPath + "'\n"; + message += "LoadPath: '" + loadPath + "'"; + } + + if (schema.Compression == BundledAssetGroupSchema.BundleCompressionMode.LZMA && (buildLocal || loadLocal)) + { + Debug.LogWarningFormat("Bundle compression is set to LZMA, but group {0} uses local content.", assetGroup.Name); + } + + return message; + } + + /// /// Build processing of an individual group. /// @@ -276,5 +332,82 @@ public virtual bool IsDataBuilt() { return false; } + + + /// + /// Copies the content state binary file from the temp directory to its final location and registers it in the + /// file registry and build results. + /// + /// Temporary location of the content state file. + /// Destination location of the content state file. + /// The builderInput object used in the build. + /// The build data result. + public virtual void CopyAndRegisterContentState(string tempPath, string contentStatePath, AddressablesDataBuilderInput builderInput, AddressablesPlayerBuildResult addrResult) + { + try + { + string directory = Path.GetDirectoryName(contentStatePath); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + if (File.Exists(contentStatePath)) + File.Delete(contentStatePath); + + File.Copy(tempPath, contentStatePath, true); + if (addrResult != null) + addrResult.ContentStateFilePath = contentStatePath; + builderInput.Registry.AddFile(contentStatePath); + } + catch (UnauthorizedAccessException uae) + { + if (!AddressableAssetUtility.IsVCAssetOpenForEdit(contentStatePath)) + Debug.LogErrorFormat("Cannot access the file {0}. It may be locked by version control.", + contentStatePath); + else + Debug.LogException(uae); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + + /// + /// Notifies the user about the existence of the Addressables Report + /// + protected virtual void NotifyUserAboutBuildReport() + { + bool buildReportSettingCheck = ProjectConfigData.UserHasBeenInformedAboutBuildReportSettingPreBuild; + if (!buildReportSettingCheck && !Application.isBatchMode && !ProjectConfigData.GenerateBuildLayout) + { + bool turnOnBuildLayout = EditorUtility.DisplayDialog("Addressables Build Report", + "There's a new Addressables Build Report you can check out after your content build. " + + "However, this requires that 'Debug Build Layout' is turned on. The setting can be found in Edit > Preferences > Addressables. Would you like to turn it on?", + "Yes", "No"); + if (turnOnBuildLayout) + ProjectConfigData.GenerateBuildLayout = true; + ProjectConfigData.UserHasBeenInformedAboutBuildReportSettingPreBuild = true; + } + } + + /// + /// Displays the Addressables Report window + /// + protected virtual void DisplayBuildReport() + { + if (!Application.isBatchMode && ProjectConfigData.AutoOpenAddressablesReport && ProjectConfigData.GenerateBuildLayout) + { + BuildReportWindow.ShowWindowAfterBuild(); + } + } + + /// + /// Clears content update notifications from teh groups window + /// + /// A list of groups that were built + protected virtual void ClearContentUpdateNotifications(List groups) + { + foreach (var group in groups) + ContentUpdateScript.ClearContentUpdateNotifications(group); + } } } diff --git a/Editor/Build/DataBuilders/BuildScriptPackedMode.cs b/Editor/Build/DataBuilders/BuildScriptPackedMode.cs index 2cfdee98..2abbcf99 100644 --- a/Editor/Build/DataBuilders/BuildScriptPackedMode.cs +++ b/Editor/Build/DataBuilders/BuildScriptPackedMode.cs @@ -50,6 +50,60 @@ public override string Name internal List ResourceProviderData => m_ResourceProviderData.ToList(); + private Dictionary> m_PrimaryKeyToDependers = null; + private Dictionary m_PrimaryKeyToLocation = null; + private Dictionary> GetPrimaryKeyToDependerLocations(List locations) + { + if (m_PrimaryKeyToDependers != null) + return m_PrimaryKeyToDependers; + if (locations == null || locations.Count == 0) + { + Debug.LogError("Attempting to get Entries dependent on key, but currently no locations"); + return new Dictionary>(0); + } + + m_PrimaryKeyToDependers = new Dictionary>(locations.Count); + foreach (ContentCatalogDataEntry location in locations) + { + for (int i = 0; i < location.Dependencies.Count; ++i) + { + string dependencyKey = location.Dependencies[i] as string; + if (string.IsNullOrEmpty(dependencyKey)) + continue; + + if (!m_PrimaryKeyToDependers.TryGetValue(dependencyKey, out var dependers)) + { + dependers = new List(); + m_PrimaryKeyToDependers.Add(dependencyKey, dependers); + } + + dependers.Add(location); + } + } + + return m_PrimaryKeyToDependers; + } + + private Dictionary GetPrimaryKeyToLocation(List locations) + { + if (m_PrimaryKeyToLocation != null) + return m_PrimaryKeyToLocation; + if (locations == null || locations.Count == 0) + { + Debug.LogError("Attempting to get Primary key to entries dependent on key, but currently no locations"); + return new Dictionary(); + } + + m_PrimaryKeyToLocation = new Dictionary(); + foreach (var loc in locations) + { + if (loc != null && loc.Keys[0] != null && loc.Keys[0] is string && !m_PrimaryKeyToLocation.ContainsKey((string)loc.Keys[0])) + m_PrimaryKeyToLocation[(string)loc.Keys[0]] = loc; + } + + return m_PrimaryKeyToLocation; + } + /// public override bool CanBuildData() { @@ -59,22 +113,15 @@ public override bool CanBuildData() /// protected override TResult BuildDataImplementation(AddressablesDataBuilderInput builderInput) { - bool buildReportSettingCheck = ProjectConfigData.UserHasBeenInformedAboutBuildReportSettingPreBuild; - if (!buildReportSettingCheck && !Application.isBatchMode && !ProjectConfigData.GenerateBuildLayout) - { - bool turnOnBuildLayout = EditorUtility.DisplayDialog("Addressables Build Report", "There's a new Addressables Build Report you can check out after your content build. " + - "However, this requires that 'Debug Build Layout' is turned on. The setting can be found in Edit > Preferences > Addressables. Would you like to turn it on?", "Yes", "No"); - if (turnOnBuildLayout) - ProjectConfigData.GenerateBuildLayout = true; - ProjectConfigData.UserHasBeenInformedAboutBuildReportSettingPreBuild = true; - } + + NotifyUserAboutBuildReport(); TResult result = default(TResult); m_IncludedGroupsInBuild?.Clear(); InitializeBuildContext(builderInput, out AddressableAssetsBuildContext aaContext); - using (m_Log.ScopedStep(LogLevel.Info, "ProcessAllGroups")) + using (Log.ScopedStep(LogLevel.Info, "ProcessAllGroups")) { var errorString = ProcessAllGroups(aaContext); if (!string.IsNullOrEmpty(errorString)) @@ -86,29 +133,16 @@ protected override TResult BuildDataImplementation(AddressablesDataBuil result = DoBuild(builderInput, aaContext); } - if (result != null) - { - var span = DateTime.Now - aaContext.buildStartTime; - result.Duration = span.TotalSeconds; - } - - if (result != null && string.IsNullOrEmpty(result.Error)) - { - foreach (var group in m_IncludedGroupsInBuild) - ContentUpdateScript.ClearContentUpdateNotifications(group); - } - - if (result != null && string.IsNullOrEmpty(result.Error)) - { - foreach (var group in m_IncludedGroupsInBuild) - ContentUpdateScript.ClearContentUpdateNotifications(group); - } + if (result == null) + return result; - if (result != null && !Application.isBatchMode && ProjectConfigData.AutoOpenAddressablesReport && ProjectConfigData.GenerateBuildLayout) + var span = DateTime.Now - aaContext.buildStartTime; + result.Duration = span.TotalSeconds; + if (string.IsNullOrEmpty(result.Error)) { - BuildReportWindow.ShowWindowAfterBuild(); + ClearContentUpdateNotifications(m_IncludedGroupsInBuild); } - + DisplayBuildReport(); return result; } @@ -136,9 +170,11 @@ internal void InitializeBuildContext(AddressablesDataBuilderInput builderInput, throw; } #endif - m_AllBundleInputDefs = new List(); m_GroupToBundleNames = new Dictionary(); + // force these caches to be rebuilt + m_PrimaryKeyToDependers = null; + m_PrimaryKeyToLocation = null; var bundleToAssetGroup = new Dictionary(); var runtimeData = new ResourceManagerRuntimeData { @@ -153,7 +189,7 @@ internal void InitializeBuildContext(AddressablesDataBuilderInput builderInput, #if ENABLE_JSON_CATALOG IsLocalCatalogInBundle = aaSettings.BundleLocalCatalog, #endif - AddressablesVersion = PackageManager.PackageInfo.FindForAssembly(typeof(Addressables).Assembly)?.version, + AddressablesVersion = Addressables.Version, MaxConcurrentWebRequests = aaSettings.MaxConcurrentWebRequests, CatalogRequestsTimeout = aaSettings.CatalogRequestsTimeout }; @@ -300,11 +336,11 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se buildTasks.Add(extractData); IBundleBuildResults results; - using (m_Log.ScopedStep(LogLevel.Info, "ContentPipeline.BuildAssetBundles")) + using (Log.ScopedStep(LogLevel.Info, "ContentPipeline.BuildAssetBundles")) using (new SBPSettingsOverwriterScope(ProjectConfigData.GenerateBuildLayout)) // build layout generation requires full SBP write results { var buildContent = new BundleBuildContent(m_AllBundleInputDefs); - var exitCode = ContentPipeline.BuildAssetBundles(buildParams, buildContent, out results, buildTasks, aaContext, m_Log); + var exitCode = ContentPipeline.BuildAssetBundles(buildParams, buildContent, out results, buildTasks, aaContext, Log); if (exitCode < ReturnCode.Success) return CreateErrorResult("SBP Error" + exitCode, builderInput, aaContext); @@ -313,7 +349,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se var groups = aaContext.Settings.groups.Where(g => g != null); var postCatalogUpdateCallbacks = new List(); - using (m_Log.ScopedStep(LogLevel.Info, "PostProcessBundles")) + using (Log.ScopedStep(LogLevel.Info, "PostProcessBundles")) using (var progressTracker = new UnityEditor.Build.Pipeline.Utilities.ProgressTracker()) { progressTracker.UpdateTask("Post Processing AssetBundles"); @@ -324,7 +360,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se if (!aaContext.assetGroupToBundles.ContainsKey(assetGroup)) continue; - using (m_Log.ScopedStep(LogLevel.Info, assetGroup.name)) + using (Log.ScopedStep(LogLevel.Info, assetGroup.name)) { PostProcessBundles(assetGroup, results, addrResult, builderInput.Registry, aaContext, @@ -333,7 +369,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se } } - using (m_Log.ScopedStep(LogLevel.Info, "Process Catalog Entries")) + using (Log.ScopedStep(LogLevel.Info, "Process Catalog Entries")) { Dictionary locationIdToCatalogEntryMap = BuildLocationIdToCatalogEntryMap(aaContext.locations); if (builderInput.PreviousContentState != null) @@ -365,7 +401,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se ContentCatalogData contentCatalog = null; #if ENABLE_JSON_CATALOG - using (m_Log.ScopedStep(LogLevel.Info, "Generate JSON Catalog")) + using (Log.ScopedStep(LogLevel.Info, "Generate JSON Catalog")) { contentCatalog = new ContentCatalogData(ResourceManagerRuntimeData.kCatalogAddress); @@ -375,7 +411,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se for (int i = 0; i < addrResult.AssetBundleBuildResults.Count; ++i) hashingObjects[i] = addrResult.AssetBundleBuildResults[i].Hash; string buildResultHash = HashingMethods.Calculate(hashingObjects).ToString(); - contentCatalog.m_BuildResultHash = buildResultHash; + contentCatalog.BuildResultHash = buildResultHash; } contentCatalog.SetData(aaContext.locations.OrderBy(f => f.InternalId).ToList()); @@ -390,19 +426,19 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se //save catalog string contentHash = null; string jsonText = null; - using (m_Log.ScopedStep(LogLevel.Info, "Generating Json")) + using (Log.ScopedStep(LogLevel.Info, "Generating Json")) jsonText = JsonUtility.ToJson(contentCatalog); if (aaContext.Settings.BuildRemoteCatalog || ProjectConfigData.GenerateBuildLayout) { - using (m_Log.ScopedStep(LogLevel.Info, "Hashing Catalog")) + using (Log.ScopedStep(LogLevel.Info, "Hashing Catalog")) contentHash = HashingMethods.Calculate(jsonText).ToString(); - contentCatalog.localHash = contentHash; + contentCatalog.LocalHash = contentHash; } CreateCatalogFiles(jsonText, builderInput, aaContext, contentHash); } #else - using (m_Log.ScopedStep(LogLevel.Info, "Generate Binary Catalog")) + using (Log.ScopedStep(LogLevel.Info, "Generate Binary Catalog")) { contentCatalog = new ContentCatalogData(ResourceManagerRuntimeData.kCatalogAddress); @@ -412,7 +448,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se for (int i = 0; i < addrResult.AssetBundleBuildResults.Count; ++i) hashingObjects[i] = addrResult.AssetBundleBuildResults[i].Hash; string buildResultHash = HashingMethods.Calculate(hashingObjects).ToString(); - contentCatalog.m_BuildResultHash = buildResultHash; + contentCatalog.BuildResultHash = buildResultHash; } contentCatalog.ResourceProviderData.AddRange(m_ResourceProviderData); @@ -427,14 +463,14 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se var contentHash = HashingMethods.Calculate(bytes); if (aaContext.Settings.BuildRemoteCatalog || ProjectConfigData.GenerateBuildLayout) - contentCatalog.localHash = contentHash.ToString(); + contentCatalog.LocalHash = contentHash.ToString(); CreateCatalogFiles(bytes, builderInput, aaContext, contentHash.ToString()); } #endif - using (m_Log.ScopedStep(LogLevel.Info, "Generate link")) + using (Log.ScopedStep(LogLevel.Info, "Generate link")) { foreach (var pd in contentCatalog.ResourceProviderData) { @@ -466,20 +502,20 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se var settingsPath = Addressables.BuildPath + "/" + builderInput.RuntimeSettingsFilename; - using (m_Log.ScopedStep(LogLevel.Info, "Generate Settings")) + using (Log.ScopedStep(LogLevel.Info, "Generate Settings")) WriteFile(settingsPath, JsonUtility.ToJson(aaContext.runtimeData), builderInput.Registry); if (extractData.BuildCache != null && builderInput.PreviousContentState == null) { - using (m_Log.ScopedStep(LogLevel.Info, "Generate Content Update State")) + using (Log.ScopedStep(LogLevel.Info, "Generate Content Update State")) { var remoteCatalogLoadPath = aaContext.Settings.BuildRemoteCatalog ? aaContext.Settings.RemoteCatalogLoadPath.GetValue(aaContext.Settings) : string.Empty; var allEntries = new List(); - using (m_Log.ScopedStep(LogLevel.Info, "Get Assets")) - aaContext.Settings.GetAllAssets(allEntries, false, ContentUpdateScript.GroupFilter); + using (Log.ScopedStep(LogLevel.Info, "Get Assets")) + aaContext.Settings.GetAllAssets(allEntries, false, ContentUpdateScript.GroupFilterFunc); if (ContentUpdateScript.SaveContentState(aaContext.locations, aaContext.GuidToCatalogLocation, tempPath, allEntries, extractData.DependencyData, playerBuildVersion, remoteCatalogLoadPath, @@ -495,31 +531,7 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se #endif } - try - { - string directory = Path.GetDirectoryName(contentStatePath); - if (!Directory.Exists(directory)) - Directory.CreateDirectory(directory); - if (File.Exists(contentStatePath)) - File.Delete(contentStatePath); - - File.Copy(tempPath, contentStatePath, true); - if (addrResult != null) - addrResult.ContentStateFilePath = contentStatePath; - builderInput.Registry.AddFile(contentStatePath); - } - catch (UnauthorizedAccessException uae) - { - if (!AddressableAssetUtility.IsVCAssetOpenForEdit(contentStatePath)) - Debug.LogErrorFormat("Cannot access the file {0}. It may be locked by version control.", - contentStatePath); - else - Debug.LogException(uae); - } - catch (Exception e) - { - Debug.LogException(e); - } + CopyAndRegisterContentState(tempPath, contentStatePath, builderInput, addrResult); } } } @@ -535,16 +547,13 @@ internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings se using (var progressTracker = new UnityEditor.Build.Pipeline.Utilities.ProgressTracker()) { progressTracker.UpdateTask("Generating Build Layout"); - using (m_Log.ScopedStep(LogLevel.Info, "Generate Build Layout")) + using (Log.ScopedStep(LogLevel.Info, "Generate Build Layout")) { List tasks = new List(); var buildLayoutTask = new BuildLayoutGenerationTask(); - buildLayoutTask.m_BundleNameRemap = bundleRenameMap; - buildLayoutTask.m_ContentCatalogData = contentCatalog; - if (contentUpdateContext.ContentState != null) - buildLayoutTask.m_AddressablesInput = builderInput; + extractData.BuildContext.SetContextObject(new BuildLayoutParameters(bundleRenameMap, contentCatalog)); tasks.Add(buildLayoutTask); - BuildTasksRunner.Run(tasks, extractData.m_BuildContext); + BuildTasksRunner.Run(tasks, extractData.BuildContext); } } } @@ -728,7 +737,7 @@ internal ReturnCode CreateCatalogBundle(string filepath, byte[] data, Addressabl var buildParams = new BundleBuildParameters(builderInput.Target, builderInput.TargetGroup, Path.GetDirectoryName(filepath)); if (builderInput.Target == BuildTarget.WebGL) buildParams.BundleCompression = BuildCompression.LZ4Runtime; - var retCode = ContentPipeline.BuildAssetBundles(buildParams, bundleBuildContent, out IBundleBuildResults result, buildTasks, m_Log); + var retCode = ContentPipeline.BuildAssetBundles(buildParams, bundleBuildContent, out IBundleBuildResults result, buildTasks, Log); if (Directory.Exists(tempFolderPath)) { @@ -846,7 +855,7 @@ internal ReturnCode CreateCatalogBundle(string filepath, string jsonText, Addres var buildParams = new BundleBuildParameters(builderInput.Target, builderInput.TargetGroup, Path.GetDirectoryName(filepath)); if (builderInput.Target == BuildTarget.WebGL) buildParams.BundleCompression = BuildCompression.LZ4Runtime; - var retCode = ContentPipeline.BuildAssetBundles(buildParams, bundleBuildContent, out IBundleBuildResults result, buildTasks, m_Log); + var retCode = ContentPipeline.BuildAssetBundles(buildParams, bundleBuildContent, out IBundleBuildResults result, buildTasks, Log); if (Directory.Exists(tempFolderPath)) { @@ -1043,10 +1052,6 @@ protected virtual string ProcessGroupSchema(AddressableAssetGroupSchema schema, if (schema == null || !schema.IncludeInBuild || !assetGroup.entries.Any()) return string.Empty; - var errorStr = ErrorCheckBundleSettings(schema, assetGroup, aaContext.Settings); - if (!string.IsNullOrEmpty(errorStr)) - return errorStr; - m_IncludedGroupsInBuild?.Add(assetGroup); AddBundleProvider(schema); @@ -1118,48 +1123,6 @@ internal static List HandleBundleNames(List bundleInpu return generatedUniqueNames; } - internal static string ErrorCheckBundleSettings(BundledAssetGroupSchema schema, AddressableAssetGroup assetGroup, AddressableAssetSettings settings) - { - var message = string.Empty; - - string buildPath = schema.BuildPath.GetValue(settings, false); - if (buildPath == null) - { - return "BuildPath for group '" + assetGroup.Name + "' is not set."; - } - string loadPath = schema.LoadPath.GetValue(settings, false); - if (loadPath == null) - { - return "LoadPath for group '" + assetGroup.Name + "' is not set."; - } - - bool buildLocal = AddressableAssetUtility.StringContains(buildPath, "[UnityEngine.AddressableAssets.Addressables.BuildPath]", StringComparison.Ordinal); - bool loadLocal = AddressableAssetUtility.StringContains(loadPath, "{UnityEngine.AddressableAssets.Addressables.RuntimePath}", StringComparison.Ordinal); - - if (buildLocal && !loadLocal) - { - message = "BuildPath for group '" + assetGroup.Name + "' is set to the dynamic-lookup version of StreamingAssets, but LoadPath is not. \n"; - } - else if (!buildLocal && loadLocal) - { - message = "LoadPath for group " + assetGroup.Name + - " is set to the dynamic-lookup version of StreamingAssets, but BuildPath is not. These paths must both use the dynamic-lookup, or both not use it. \n"; - } - - if (!string.IsNullOrEmpty(message)) - { - message += "BuildPath: '" + buildPath + "'\n"; - message += "LoadPath: '" + loadPath + "'"; - } - - if (schema.Compression == BundledAssetGroupSchema.BundleCompressionMode.LZMA && (buildLocal || loadLocal)) - { - Debug.LogWarningFormat("Bundle compression is set to LZMA, but group {0} uses local content.", assetGroup.Name); - } - - return message; - } - internal static string CalculateGroupHash(BundledAssetGroupSchema.BundleInternalIdMode mode, AddressableAssetGroup assetGroup, IEnumerable entries) { switch (mode) @@ -1414,7 +1377,7 @@ static void MoveFileToDestinationWithTimestampIfDifferent(string srcPath, string AddressablesPlayerBuildResult.BundleBuildResult bundleResultInfo = new AddressablesPlayerBuildResult.BundleBuildResult(); bundleResultInfo.SourceAssetGroup = assetGroup; - if (aaContext.PrimaryKeyToLocation.TryGetValue(builtBundleNames[i], out ContentCatalogDataEntry dataEntry)) + if (GetPrimaryKeyToLocation(aaContext.locations).TryGetValue(builtBundleNames[i], out ContentCatalogDataEntry dataEntry)) { var info = buildResult.BundleInfos[builtBundleNames[i]]; bundleResultInfo.Crc = info.Crc; @@ -1475,7 +1438,7 @@ static void MoveFileToDestinationWithTimestampIfDifferent(string srcPath, string outputBundleNames[i] = StripHashFromBundleLocation(outputBundleNames[i]); bundleRenameMap.Add(builtBundleNames[i], outputBundleNames[i]); - MoveFileToDestinationWithTimestampIfDifferent(srcPath, targetPath, m_Log); + MoveFileToDestinationWithTimestampIfDifferent(srcPath, targetPath, Log); AddPostCatalogUpdatesInternal(assetGroup, postCatalogUpdateCallbacks, dataEntry, targetPath, registry); if (addrResult != null) @@ -1572,10 +1535,10 @@ private void SetPrimaryKey(ContentCatalogDataEntry forLocation, string newPrimar throw new ArgumentException("Invalid primary key for catalog entry " + forLocation.ToString()); forLocation.Keys[0] = newPrimaryKey; - aaContext.PrimaryKeyToLocation.Remove(originalKey); - aaContext.PrimaryKeyToLocation.Add(newPrimaryKey, forLocation); + m_PrimaryKeyToLocation.Remove(originalKey); + m_PrimaryKeyToLocation.Add(newPrimaryKey, forLocation); - if (!aaContext.PrimaryKeyToDependerLocations.TryGetValue(originalKey, out var dependers)) + if (!GetPrimaryKeyToDependerLocations(aaContext.locations).TryGetValue(originalKey, out var dependers)) return; // nothing depends on it foreach (ContentCatalogDataEntry location in dependers) @@ -1593,8 +1556,8 @@ private void SetPrimaryKey(ContentCatalogDataEntry forLocation, string newPrimar } } - aaContext.PrimaryKeyToDependerLocations.Remove(originalKey); - aaContext.PrimaryKeyToDependerLocations.Add(newPrimaryKey, dependers); + m_PrimaryKeyToDependers.Remove(originalKey); + m_PrimaryKeyToDependers.Add(newPrimaryKey, dependers); } private static long GetFileSize(string fileName) diff --git a/Editor/Build/FastModeInitializationOperation.cs b/Editor/Build/FastModeInitializationOperation.cs index e17b7d81..fb2d5baf 100644 --- a/Editor/Build/FastModeInitializationOperation.cs +++ b/Editor/Build/FastModeInitializationOperation.cs @@ -112,7 +112,7 @@ protected override void Execute() { bool success = op.Status == AsyncOperationStatus.Succeeded; Complete(locator, success, success ? "" : $"{op.DebugName}, status={op.Status}, result={op.Result} failed initialization."); - m_addressables.Release(op); + op.Release(); }; } } diff --git a/Editor/Build/Layout/BuildLayout.cs b/Editor/Build/Layout/BuildLayout.cs index 42022125..6d6b4ae4 100644 --- a/Editor/Build/Layout/BuildLayout.cs +++ b/Editor/Build/Layout/BuildLayout.cs @@ -242,7 +242,7 @@ public DateTime BuildStart internal string m_FilePath; private bool m_HeaderRead = false; - private bool m_BodyRead = false; + internal bool m_BodyRead = false; private FileStream m_FileStream = null; private StreamReader m_StreamReader = null; @@ -1023,7 +1023,7 @@ public ulong UncompressedSize public List OtherAssets = new List(); /// - /// A list of referenced explicit assets located in other AssetBundles. + /// A list of referenced explicit assets located in other AssetBundles. /// [SerializeReference] public List ExternalReferences = new List(); diff --git a/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsView.cs b/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsView.cs index 335010c0..8c80bbfb 100644 --- a/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsView.cs +++ b/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsView.cs @@ -24,25 +24,34 @@ internal class AddressablesProfilerDetailsView : IDisposable private const string k_ViewNonLoadedPreferencesKey = k_PreferencesKeyPrefix + k_ViewNonLoadedActionName; private const string k_ViewObjectsPreferencesKey = k_PreferencesKeyPrefix + k_ViewObjectsActionName; - private readonly struct FrameData + internal readonly struct FrameData { - public readonly NativeArray CatalogValues; - public readonly NativeArray BundleValues; - public readonly NativeArray AssetValues; - public readonly NativeArray SceneValues; + public readonly IEnumerable CatalogValues; + public readonly IEnumerable BundleValues; + public readonly IEnumerable AssetValues; + public readonly IEnumerable SceneValues; - public bool HasValues => CatalogValues.IsCreated && CatalogValues.Length > 0; + public bool HasValues + { + get + { + if (CatalogValues == null) + return false; + using var e = CatalogValues.GetEnumerator(); + return e.MoveNext(); + } + } public FrameData(int frame) { - using (var rawFrameData = UnityEditorInternal.ProfilerDriver.GetRawFrameDataView(frame, 0)) + using (var rawFrameDataRef = m_frameDataStore.GetRawFrameDataView(frame, 0)) { - if (rawFrameData != null && rawFrameData.valid) + if (rawFrameDataRef != null && rawFrameDataRef.valid) { - CatalogValues = rawFrameData.GetFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kCatalogTag); - BundleValues = rawFrameData.GetFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kBundleDataTag); - AssetValues = rawFrameData.GetFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kAssetDataTag); - SceneValues = rawFrameData.GetFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kSceneDataTag); + CatalogValues = m_frameDataStore.GetFrameMetaData(rawFrameDataRef, ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kCatalogTag); + BundleValues = m_frameDataStore.GetFrameMetaData(rawFrameDataRef, ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kBundleDataTag); + AssetValues = m_frameDataStore.GetFrameMetaData(rawFrameDataRef, ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kAssetDataTag); + SceneValues = m_frameDataStore.GetFrameMetaData(rawFrameDataRef, ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kSceneDataTag); } else { @@ -59,7 +68,7 @@ public FrameData(int frame) private ToolbarSearchField m_SearchField; private ContentSearch m_SearchController = new ContentSearch(); - + internal static IFrameDataStore m_frameDataStore = new EditorFrameDataStore(); private AddressablesProfilerDetailsDataInspector m_DetailsInspector; @@ -86,15 +95,24 @@ public AddressablesProfilerDetailsView(ProfilerWindow profilerWindow) public VisualElement CreateView() { - m_ViewGroups = EditorPrefs.GetBool(k_ViewGroupsPreferencesKey, false); - m_ViewAssetBundles = EditorPrefs.GetBool(k_ViewAssetBundlesPreferencesKey, true); - m_ViewAssets = EditorPrefs.GetBool(k_ViewAssetsPreferencesKey, true); - m_ViewNonLoadedAssets = EditorPrefs.GetBool(k_ViewNonLoadedPreferencesKey, false); - m_ViewObjects = EditorPrefs.GetBool(k_ViewObjectsPreferencesKey, false); - - CreateViewsWithToolbarInLeft(); - OnReinitialise(); - return m_RootSplitView; + try + { + m_ViewGroups = EditorPrefs.GetBool(k_ViewGroupsPreferencesKey, false); + m_ViewAssetBundles = EditorPrefs.GetBool(k_ViewAssetBundlesPreferencesKey, true); + m_ViewAssets = EditorPrefs.GetBool(k_ViewAssetsPreferencesKey, true); + m_ViewNonLoadedAssets = EditorPrefs.GetBool(k_ViewNonLoadedPreferencesKey, false); + m_ViewObjects = EditorPrefs.GetBool(k_ViewObjectsPreferencesKey, false); + + CreateViewsWithToolbarInLeft(); + OnReinitialise(); + return m_RootSplitView; + } + catch (Exception e) + { + // this can be caused by data issues and can be nearly impossible to track down once caught further up + Debug.LogException(e); + throw; + } } private VisualElement CreateViewsWithToolbarInLeft() @@ -213,7 +231,7 @@ void ReloadData(long selectedFrameIndex) MissingBuildReportDisplay(missingBuildHash); } } - GenerateContentDataForFrame(frameData); + m_RootGroupsContentData = GenerateContentDataForFrame(frameData); BuildTree(); } else @@ -411,16 +429,16 @@ private int CompareTreeColumnData(TreeViewItemData x, TreeViewItemD return 0; } - private void GenerateContentDataForFrame(FrameData frameData) + internal List GenerateContentDataForFrame(FrameData frameData) { Dictionary reportBundleToBundleData = new Dictionary(); List bundleContent = GenerateBundleRoots(frameData.BundleValues, reportBundleToBundleData); GenerateAssetData(bundleContent, frameData.AssetValues, frameData.SceneValues); List groupContent = CollectGroups(bundleContent); - m_RootGroupsContentData = groupContent; + return groupContent; } - List GenerateBundleRoots(in NativeArray bundleValues, in Dictionary reportBundleToBundleData) + List GenerateBundleRoots(in IEnumerable bundleValues, in Dictionary reportBundleToBundleData) { List bundleDatas = new List(); foreach (BundleFrameData frameData in bundleValues) @@ -430,18 +448,21 @@ List GenerateBundleRoots(in NativeArray bundleValue return new List(); BundleData bundleData = new BundleData(layoutBundle, frameData); bundleDatas.Add(bundleData); - reportBundleToBundleData.Add(layoutBundle, bundleData); + if (!reportBundleToBundleData.TryAdd(layoutBundle, bundleData)) + { + throw new Exception($"Unable to add bundle data for bundle {layoutBundle.Name}, duplicate data exists."); + } } GenerateBundleDependencies(bundleDatas, reportBundleToBundleData); return bundleDatas; } - void GenerateAssetData(List bundleDatas, in NativeArray assetValues, in NativeArray sceneValues) + internal void GenerateAssetData(List bundleDatas, in IEnumerable assetValues, in IEnumerable sceneValues) { Dictionary bundleCodeToData = new Dictionary(); foreach (BundleData data in bundleDatas) - bundleCodeToData.Add(data.BundleCode, data); + bundleCodeToData.TryAdd(data.BundleCode, data); List addressableLoadedAssets = new List(); // add all addressable loaded assets so later know they are fully loaded @@ -518,7 +539,7 @@ private AssetData GetAssetDataForFrameData(AssetFrameData frameData, Dictionary< return assetData; } - private void ProcessAssetReferences(List activeAddressableAssets, Dictionary bundleCodeToData) + internal void ProcessAssetReferences(List activeAddressableAssets, Dictionary bundleCodeToData) { // instead get a stack Stack assetsToBeProcessed = new Stack(activeAddressableAssets); @@ -531,7 +552,10 @@ private void ProcessAssetReferences(List activeAddressableAssets, Dic if(!processedAssets.Add(assetData)) continue; // not needed - BundleData parentBundle = bundleCodeToData[((BundleData)assetData.Parent).BundleCode]; + if (!bundleCodeToData.TryGetValue(((BundleData)assetData.Parent).BundleCode, out var parentBundle)) + { + continue; + } foreach (BuildLayout.ObjectData objectData in assetData.ReportObjects) { // get or create object for self, this may have already been made from a reference @@ -557,7 +581,7 @@ private void ProcessAssetReferences(List activeAddressableAssets, Dic } } - private AssetData ProcessReference(AssetData referencingAssetData, ObjectData referencingObjectData, + internal AssetData ProcessReference(AssetData referencingAssetData, ObjectData referencingObjectData, BuildLayout.ObjectReference objectReference, BundleData parentBundle, Dictionary bundleCodeToData) { BuildLayout.File file = referencingAssetData.IsImplicit ? referencingAssetData.ReportImplicitData.File : referencingAssetData.ReportExplicitData.File; @@ -584,7 +608,14 @@ private void ProcessAssetReferences(List activeAddressableAssets, Dic if (referencedReportAsset.Bundle != bundleContainingReferencedObj.ReportBundle) { int bundleCode = referencedReportAsset.Bundle.InternalName.GetHashCode(); - bundleContainingReferencedObj = bundleCodeToData[bundleCode]; + if (bundleCodeToData.ContainsKey(bundleCode)) + { + bundleContainingReferencedObj = bundleCodeToData[bundleCode]; + } + else + { + Debug.LogWarning($"Asset {referencedReportAsset.AddressableName} referenced bundle {referencedReportAsset.Bundle.Name} not loaded from build layout, attaching to parent {bundleContainingReferencedObj.ReportBundle.Name}"); + } } bundleContainingReferencedObj.GetOrCreateAssetData(referencedReportAsset, out referencedAssetData); @@ -628,7 +659,7 @@ private static void GenerateBundleDependencies(List bundleDatas, in { foreach (BundleData data in bundleDatas) { - foreach (BuildLayout.Bundle dependency in data.ReportBundle.Dependencies) + foreach (BuildLayout.Bundle dependency in data.ReportBundle.Dependencies ?? new List()) { if (!reportBundleToBundleData.TryGetValue(dependency, out var bundleDataIsDependantOn)) continue; diff --git a/Editor/Diagnostics/Profiler/BuildLayoutsManager.cs b/Editor/Diagnostics/Profiler/BuildLayoutsManager.cs index 826d3d7d..52c3bb7b 100644 --- a/Editor/Diagnostics/Profiler/BuildLayoutsManager.cs +++ b/Editor/Diagnostics/Profiler/BuildLayoutsManager.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using Unity.Collections; @@ -23,7 +24,8 @@ public ActiveLayout(BuildLayout layout) foreach (BuildLayout.Bundle bundle in BuildLayoutHelpers.EnumerateBundles(layout)) { int bundleCode = bundle.InternalName.GetHashCode(); - m_BundleMap.Add(bundleCode, bundle); + if (!m_BundleMap.TryAdd(bundleCode, bundle)) + throw new Exception($"Duplicate bundle code ${bundle.InternalName.GetHashCode()} found for ${bundle.Name} in layout."); Dictionary assetMap = new Dictionary(bundle.AssetCount); foreach (BuildLayout.ExplicitAsset asset in BuildLayoutHelpers.EnumerateAssets(bundle)) { @@ -55,10 +57,15 @@ public BuildLayout.ExplicitAsset GetAsset(int bundleCode, int assetCode) private Dictionary m_BuildLayouts = new Dictionary(); private readonly List m_ActiveLayouts = new List(); - public void LoadReports() + internal void ClearReports() { m_BuildLayouts.Clear(); m_ActiveLayouts.Clear(); + } + + public void LoadReports() + { + ClearReports(); if (Directory.Exists(Addressables.BuildReportPath)) { @@ -95,6 +102,22 @@ public bool LoadManualReport(string path) return true; } + internal void AddActiveLayout(string buildResultHash) + { + if (m_BuildLayouts.TryGetValue(Hash128.Parse(buildResultHash), out BuildLayout layout)) + { + AddActiveLayout(layout); + return; + } + throw new Exception($"Unable to add active layout. {buildResultHash} does not exist in loaded reports."); + } + + internal void AddActiveLayout(BuildLayout layout) + { + m_ActiveLayouts.Add(new ActiveLayout(layout)); + + } + private bool TryLoadLayoutAtPath(string path, out BuildLayout layoutOut, bool logErrors = false) { layoutOut = BuildLayout.Open(path); @@ -114,13 +137,13 @@ private bool TryLoadLayoutAtPath(string path, out BuildLayout layoutOut, bool lo return true; } - public HashSet SetActiveReportsAndGetMissingBuildHashes(NativeArray loadedRuntimeCatalogs) + public HashSet SetActiveReportsAndGetMissingBuildHashes(IEnumerable loadedRuntimeCatalogs) { HashSet oldActives = new HashSet(m_ActiveLayouts); HashSet missingBuildHashes = new HashSet(); - for (int i = 0; i < loadedRuntimeCatalogs.Length; ++i) + foreach(CatalogFrameData loadedRuntimeCatalog in loadedRuntimeCatalogs) { - var recordedHash = loadedRuntimeCatalogs[i].BuildResultHash; + var recordedHash = loadedRuntimeCatalog.BuildResultHash; var layout = m_ActiveLayouts.Find(activeLayout => activeLayout.BuildResultHash == recordedHash); if (layout != null) { @@ -129,7 +152,9 @@ public HashSet SetActiveReportsAndGetMissingBuildHashes(NativeArray GetFrameMetaData(FrameDataViewRef rawFrameDataViewRef, Guid id, int tag) where T : struct + { + return rawFrameDataViewRef.frameDataView.GetFrameMetaData(id, tag); + } + } +} diff --git a/Editor/Diagnostics/Profiler/EditorFrameDataStore.cs.meta b/Editor/Diagnostics/Profiler/EditorFrameDataStore.cs.meta new file mode 100644 index 00000000..46963fc0 --- /dev/null +++ b/Editor/Diagnostics/Profiler/EditorFrameDataStore.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 95db4c4b9616444abce778abd4ab9e2a +timeCreated: 1712949974 \ No newline at end of file diff --git a/Editor/Diagnostics/Profiler/FrameDataViewRef.cs b/Editor/Diagnostics/Profiler/FrameDataViewRef.cs new file mode 100644 index 00000000..40deeefb --- /dev/null +++ b/Editor/Diagnostics/Profiler/FrameDataViewRef.cs @@ -0,0 +1,24 @@ +using System; +using UnityEditor.Profiling; + +namespace UnityEditor.AddressableAssets.Diagnostics +{ + // this is a wrapper to make it possible to pass around context for testing + internal class FrameDataViewRef : IDisposable + { + public int frameIndex { get; set; } + public int threadIndex { get; set; } + public FrameDataView frameDataView { get; set; } + + public bool valid { get; set; } + + public FrameDataViewRef() + { + } + + public void Dispose() + { + frameDataView?.Dispose(); + } + } +} diff --git a/Editor/Diagnostics/Profiler/FrameDataViewRef.cs.meta b/Editor/Diagnostics/Profiler/FrameDataViewRef.cs.meta new file mode 100644 index 00000000..24a5ddd2 --- /dev/null +++ b/Editor/Diagnostics/Profiler/FrameDataViewRef.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8046608232ba43ce8d11c6e20c248267 +timeCreated: 1712953993 \ No newline at end of file diff --git a/Editor/Diagnostics/Profiler/IFrameDataStore.cs b/Editor/Diagnostics/Profiler/IFrameDataStore.cs new file mode 100644 index 00000000..6d25c1a9 --- /dev/null +++ b/Editor/Diagnostics/Profiler/IFrameDataStore.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using Unity.Collections; +using UnityEditor.Profiling; + +namespace UnityEditor.AddressableAssets.Diagnostics +{ + internal interface IFrameDataStore + { + public FrameDataViewRef GetRawFrameDataView(int frameIndex, int threadIndex); + public IEnumerable GetFrameMetaData(FrameDataViewRef frameDataViewRef, Guid id, int tag) where T : struct; + } +} diff --git a/Editor/Diagnostics/Profiler/IFrameDataStore.cs.meta b/Editor/Diagnostics/Profiler/IFrameDataStore.cs.meta new file mode 100644 index 00000000..9c66345c --- /dev/null +++ b/Editor/Diagnostics/Profiler/IFrameDataStore.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 303dba846ec041a2907f15fad1de0aff +timeCreated: 1712949893 \ No newline at end of file diff --git a/Editor/GUI/AssetInspectorGUI.cs b/Editor/GUI/AssetInspectorGUI.cs index 99f8c482..be5c4a48 100644 --- a/Editor/GUI/AssetInspectorGUI.cs +++ b/Editor/GUI/AssetInspectorGUI.cs @@ -434,7 +434,21 @@ internal static List GatherTargetInfos(Object[] targets, Addressable if (s_Cache == null && aaSettings != null) s_Cache = new AddressableAssetSettings.Cache>(aaSettings); if (s_Cache != null && s_Cache.TryGetCached(selectionHashCode, out targetInfos)) - return targetInfos; + { + bool isValid = true; + foreach(var targetInfo in targetInfos) + { + if (targetInfo.MainAssetEntry?.parentGroup == null) + { + isValid = false; + break; + } + } + if (isValid) + return targetInfos; + else + s_Cache.Remove(selectionHashCode); + } targetInfos = new List(targets.Length); AddressableAssetEntry entry; diff --git a/Editor/Settings/AddressableAssetEntry.cs b/Editor/Settings/AddressableAssetEntry.cs index fc2a9d62..0b927319 100644 --- a/Editor/Settings/AddressableAssetEntry.cs +++ b/Editor/Settings/AddressableAssetEntry.cs @@ -547,7 +547,7 @@ internal void GatherFolderEntries(List assets, bool recur if (entry != null) { - entry.m_Labels = m_Labels; + entry.m_Labels.UnionWith(m_Labels); if (entryFilter == null || entryFilter(entry)) assets.Add(entry); @@ -572,7 +572,7 @@ internal void GatherFolderEntries(List assets, bool recur var entry = settings.CreateSubEntryIfUnique(AssetDatabase.AssetPathToGUID(folder), address + GetRelativePath(folder, path), this); if (entry != null) { - entry.m_Labels = m_Labels; + entry.m_Labels.UnionWith(m_Labels); entry.IsFolder = true; if (entryFilter == null || entryFilter(entry)) assets.Add(entry); @@ -616,7 +616,7 @@ internal AddressableAssetEntry GetFolderSubEntry(string subAssetGuid, string sub var folderEntry = settings.CreateSubEntryIfUnique(folderGuid, address + "/" + folderPath.Remove(assetPath.Length), this); if (folderEntry != null) { - folderEntry.m_Labels = m_Labels; + folderEntry.m_Labels.UnionWith(m_Labels); folderEntry.IsFolder = true; } else @@ -627,7 +627,7 @@ internal AddressableAssetEntry GetFolderSubEntry(string subAssetGuid, string sub if (assetEntry != null) { - assetEntry.m_Labels = m_Labels; + assetEntry.m_Labels.UnionWith(m_Labels); assetEntry.IsFolder = false; } diff --git a/Editor/Settings/AddressableAssetSettings.cs b/Editor/Settings/AddressableAssetSettings.cs index 6642618f..3d08262b 100644 --- a/Editor/Settings/AddressableAssetSettings.cs +++ b/Editor/Settings/AddressableAssetSettings.cs @@ -61,6 +61,13 @@ public void Add(T1 key, T2 value) m_TargetInfoCache.Add(key, value); } + public void Remove(T1 key) + { + if (!IsValid()) + m_CurrentCacheVersion = m_Settings.currentHash; + m_TargetInfoCache.Remove(key); + } + private bool IsValid() { if (m_TargetInfoCache.Count > 0) @@ -1186,8 +1193,7 @@ public Hash128 currentHash internal void DataBuilderCompleted(IDataBuilder builder, IDataBuilderResult result) { - if (OnDataBuilderComplete != null) - OnDataBuilderComplete(this, builder, result); + OnDataBuilderComplete?.Invoke(this, builder, result); } /// @@ -2173,10 +2179,8 @@ public void SetDirty(ModificationEvent modificationEvent, object eventData, bool { if (postEvent) { - if (OnModificationGlobal != null) - OnModificationGlobal(this, modificationEvent, eventData); - if (OnModification != null) - OnModification(this, modificationEvent, eventData); + OnModificationGlobal?.Invoke(this, modificationEvent, eventData); + OnModification?.Invoke(this, modificationEvent, eventData); } if (settingsModified && IsPersisted) @@ -2254,7 +2258,12 @@ public AddressableAssetEntry FindAssetEntry(string guid, bool includeImplicit) if (m_FindAssetEntryCache != null) { if (m_FindAssetEntryCache.TryGetCached(guid, out foundEntry)) - return foundEntry; + { + if (foundEntry?.parentGroup == null) + m_FindAssetEntryCache.Remove(guid); + else + return foundEntry; + } } else m_FindAssetEntryCache = new Cache(this); @@ -3036,8 +3045,7 @@ internal AddressablesPlayerBuildResult BuildPlayerContentImpl(AddressablesDataBu else Debug.Log($"Addressable content successfully built (duration : {TimeSpan.FromSeconds(result.Duration).ToString("g")})"); - if (BuildScript.buildCompleted != null) - BuildScript.buildCompleted(result); + BuildScript.buildCompleted?.Invoke(result); AssetDatabase.Refresh(); return result; } @@ -3264,7 +3272,7 @@ public void OnBeforeSerialize() { // m_InitializationObjects, m_DataBuilders, and m_GroupTemplateObjects are serialized by array index profileSettings.profiles.Sort((a, b) => string.CompareOrdinal(a.id, b.id)); - m_GroupAssets.Sort((a, b) => string.CompareOrdinal(a.Guid, b.Guid)); + m_GroupAssets.Sort((a, b) => string.CompareOrdinal(a?.Guid, b?.Guid)); } /// diff --git a/Runtime/Addressables.cs b/Runtime/Addressables.cs index d779f817..134c48ba 100644 --- a/Runtime/Addressables.cs +++ b/Runtime/Addressables.cs @@ -568,6 +568,18 @@ static AddressablesImpl m_Addressables } } + /// + /// Returns the Addressables package version in Unity 2019.3 or newer + /// + public static string Version + { +#if (UNITY_EDITOR) + get => UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(Addressables).Assembly).version; +#else + get => ""; +#endif + } + /// /// Stores the ResourceManager associated with this Addressables instance. /// @@ -591,7 +603,16 @@ static void RegisterPlayModeStateChange() static void SetAddressablesReInitFlagOnExitPlayMode(PlayModeStateChange change) { if (change == PlayModeStateChange.EnteredEditMode || change == PlayModeStateChange.ExitingPlayMode) - reinitializeAddressables = true; + { + EditorApplication.delayCall -= EnableReinitializeAddressablesFlag; + EditorApplication.delayCall += EnableReinitializeAddressablesFlag; + } + } + + static void EnableReinitializeAddressablesFlag() + { + reinitializeAddressables = true; + EditorApplication.delayCall -= EnableReinitializeAddressablesFlag; } #endif @@ -1441,7 +1462,7 @@ public static void Release(TObject obj) /// The operation handle to release. public static void Release(AsyncOperationHandle handle) { - m_Addressables.Release(handle); + handle.Release(); } /// @@ -1450,7 +1471,7 @@ public static void Release(AsyncOperationHandle handle) /// The operation handle to release. public static void Release(AsyncOperationHandle handle) { - m_Addressables.Release(handle); + handle.Release(); } /// @@ -1470,7 +1491,7 @@ public static bool ReleaseInstance(GameObject instance) /// Returns true if the instance was successfully released. public static bool ReleaseInstance(AsyncOperationHandle handle) { - m_Addressables.Release(handle); + handle.Release(); return true; } @@ -1481,7 +1502,7 @@ public static bool ReleaseInstance(AsyncOperationHandle handle) /// Returns true if the instance was successfully released. public static bool ReleaseInstance(AsyncOperationHandle handle) { - m_Addressables.Release(handle); + handle.Release(); return true; } diff --git a/Runtime/AddressablesImpl.cs b/Runtime/AddressablesImpl.cs index 74d2786d..9d052e48 100644 --- a/Runtime/AddressablesImpl.cs +++ b/Runtime/AddressablesImpl.cs @@ -42,14 +42,7 @@ public IInstanceProvider InstanceProvider public ISceneProvider SceneProvider; public ResourceManager ResourceManager - { - get - { - if (m_ResourceManager == null) - m_ResourceManager = new ResourceManager(new DefaultAllocationStrategy()); - return m_ResourceManager; - } - } + { get => m_ResourceManager; } public int CatalogRequestsTimeout { @@ -154,7 +147,7 @@ internal void OnSceneUnloaded(Scene scene) if (sceneHandle.Result.ReleaseSceneOnSceneUnloaded) { var op = SceneProvider.ReleaseScene(m_ResourceManager, sceneHandle); - AutoReleaseHandleOnCompletion(op); + op.ReleaseHandleOnCompletion(); } break; } @@ -367,7 +360,7 @@ public AsyncOperationHandle InitializeAsync(string runtimeData return m_InitializationOperation; var completedOperation = ResourceManager.CreateCompletedOperation(m_ResourceLocators[0].Locator, errorMsg: null); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(completedOperation); + completedOperation.ReleaseHandleOnCompletion(); return completedOperation; } @@ -425,7 +418,7 @@ public AsyncOperationHandle InitializeAsync(string runtimeData if (!m_InitializationOperation.IsValid()) m_InitializationOperation = Initialization.InitializationOperation.CreateInitializationOperation(this, runtimeDataPath, providerSuffix); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(m_InitializationOperation); + m_InitializationOperation.ReleaseHandleOnCompletion(); return m_InitializationOperation; } @@ -471,11 +464,13 @@ public AsyncOperationHandle InitializeAsync(bool autoReleaseHa public ResourceLocationBase CreateCatalogLocationWithHashDependencies(string catalogPath, string hashFilePath) where T : IResourceProvider { - var catalogLoc = new ResourceLocationBase(catalogPath, catalogPath, typeof(T).FullName, typeof(IResourceLocator)); - catalogLoc.Data = new ProviderLoadRequestOptions() + var catalogLoc = new ResourceLocationBase(catalogPath, catalogPath, typeof(T).FullName, typeof(IResourceLocator)) { - IgnoreFailures = false, - WebRequestTimeout = CatalogRequestsTimeout + Data = new ProviderLoadRequestOptions() + { + IgnoreFailures = false, + WebRequestTimeout = CatalogRequestsTimeout + } }; if (!string.IsNullOrEmpty(hashFilePath)) @@ -497,14 +492,18 @@ public AsyncOperationHandle InitializeAsync(bool autoReleaseHa // The file name of the local cached catalog + hash file is the hash code of the remote hash path, without query parameters (if any). string cacheHashFilePath = ResolveInternalId(kCacheDataFolder + tmpPath.GetHashCode() + ".hash"); #endif - var hashResourceLocation = new ResourceLocationBase(hashFilePath, hashFilePath, typeof(TextDataProvider).FullName, typeof(string)); - hashResourceLocation.Data = hashOptions.Copy(); + var hashResourceLocation = new ResourceLocationBase(hashFilePath, hashFilePath, typeof(TextDataProvider).FullName, typeof(string)) + { + Data = hashOptions.Copy() + }; catalogLoc.Dependencies.Add(hashResourceLocation); - var cacheResourceLocation = new ResourceLocationBase(cacheHashFilePath, cacheHashFilePath, typeof(TextDataProvider).FullName, typeof(string)); - cacheResourceLocation.Data = hashOptions.Copy(); + var cacheResourceLocation = new ResourceLocationBase(cacheHashFilePath, cacheHashFilePath, typeof(TextDataProvider).FullName, typeof(string)) + { + Data = hashOptions.Copy() + }; catalogLoc.Dependencies.Add(cacheResourceLocation); - //If you're explicitly loading a contnet catalog, we'll just set the "local" filepath to be the same as the "cached" one + //If you're explicitly loading a content catalog, we'll just set the "local" filepath to be the same as the "cached" one catalogLoc.Dependencies.Add(cacheResourceLocation); } @@ -527,7 +526,7 @@ public AsyncOperationHandle LoadContentCatalogAsync(string cat return ResourceManager.CreateChainOperation(ChainOperation, op => LoadContentCatalogAsync(catalogPath, autoReleaseHandle, providerSuffix)); var handle = Initialization.InitializationOperation.LoadContentCatalog(this, catalogLoc, providerSuffix); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); QueueEditorUpdateIfNeeded(); return handle; } @@ -779,10 +778,9 @@ void OnSceneHandleCompleted(AsyncOperationHandle handle) if (handle.Status == AsyncOperationStatus.Succeeded) { m_SceneInstances.Add(handle); - if (!m_resultToHandle.ContainsKey(handle.Result)) + if (m_resultToHandle.TryAdd(handle.Result, handle)) { handle.Destroyed += m_OnHandleDestroyedAction; - m_resultToHandle.Add(handle.Result, handle); } } } @@ -791,10 +789,9 @@ void OnHandleCompleted(AsyncOperationHandle handle) { if (handle.Status == AsyncOperationStatus.Succeeded) { - if (!m_resultToHandle.ContainsKey(handle.Result)) + if (m_resultToHandle.TryAdd(handle.Result, handle)) { handle.Destroyed += m_OnHandleDestroyedAction; - m_resultToHandle.Add(handle.Result, handle); } } } @@ -809,7 +806,7 @@ public void Release(TObject obj) AsyncOperationHandle handle; if (m_resultToHandle.TryGetValue(obj, out handle)) - Release(handle); + handle.Release(); else { LogError("Addressables.Release was called on an object that Addressables was not previously aware of. Thus nothing is being released"); @@ -818,21 +815,6 @@ public void Release(TObject obj) public void Release(AsyncOperationHandle handle) { - if (typeof(TObject) == typeof(SceneInstance)) - { - SceneInstance sceneInstance = (SceneInstance)Convert.ChangeType(handle.Result, typeof(SceneInstance)); - if (sceneInstance.Scene.isLoaded && handle.ReferenceCount == 1) - { - if (ActiveSceneInstances == 1 && m_SceneInstances.First().Equals(handle)) - m_SceneInstances.Clear(); - UnloadSceneAsync(handle, UnloadSceneOptions.None, true); - } - else if (!sceneInstance.Scene.isLoaded && handle.ReferenceCount == 2 && !handle.UnloadSceneOpExcludeReleaseCallback) - { - AutoReleaseHandleOnCompletion(handle); - } - } - m_ResourceManager.Release(handle); } @@ -973,7 +955,7 @@ AsyncOperationHandle DownloadDependenciesAsyncWithChain(AsyncOperationHandle dep { var handle = ResourceManager.CreateChainOperation(dep, op => DownloadDependenciesAsync(key).Convert>()); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } @@ -1016,7 +998,7 @@ public AsyncOperationHandle DownloadDependenciesAsync(object key, bool autoRelea { var handle = ResourceManager.CreateCompletedOperationWithException>(null, new InvalidKeyException(key, typeof(object), this)); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } else @@ -1025,7 +1007,7 @@ public AsyncOperationHandle DownloadDependenciesAsync(object key, bool autoRelea WrapAsDownloadLocations(dlLocations); var handle = LoadAssetsAsync(dlLocations, null, true); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } } @@ -1034,7 +1016,7 @@ AsyncOperationHandle DownloadDependenciesAsyncWithChain(AsyncOperationHandle dep { var handle = ResourceManager.CreateChainOperation(dep, op => DownloadDependenciesAsync(locations).Convert>()); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } @@ -1048,7 +1030,7 @@ public AsyncOperationHandle DownloadDependenciesAsync(IList l WrapAsDownloadLocations(dlLocations); var handle = LoadAssetsAsync(dlLocations, null, true); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } @@ -1056,7 +1038,7 @@ AsyncOperationHandle DownloadDependenciesAsyncWithChain(AsyncOperationHandle dep { var handle = ResourceManager.CreateChainOperation(dep, op => DownloadDependenciesAsync(keys, mode).Convert>()); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } @@ -1071,7 +1053,7 @@ public AsyncOperationHandle DownloadDependenciesAsync(IEnumerable keys, Addressa { var handle = ResourceManager.CreateCompletedOperationWithException>(null, new InvalidKeyException(keys, typeof(object), mode, this)); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } else @@ -1080,7 +1062,7 @@ public AsyncOperationHandle DownloadDependenciesAsync(IEnumerable keys, Addressa WrapAsDownloadLocations(dlLocations); var handle = LoadAssetsAsync(dlLocations, null, true); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(handle); + handle.ReleaseHandleOnCompletion(); return handle; } } @@ -1128,29 +1110,9 @@ internal bool ClearDependencyCacheForKey(object key) return result; } - internal void AutoReleaseHandleOnCompletion(AsyncOperationHandle handle) - { - handle.Completed += op => Release(op); - } - - internal void AutoReleaseHandleOnCompletion(AsyncOperationHandle handle) - { - handle.Completed += op => Release(op); - } - - internal void AutoReleaseHandleOnCompletion(AsyncOperationHandle handle, bool unloadSceneOpExcludeReleaseCallback) - { - handle.Completed += op => - { - if (unloadSceneOpExcludeReleaseCallback) - op.UnloadSceneOpExcludeReleaseCallback = true; - Release(op); - }; - } - internal void AutoReleaseHandleOnTypelessCompletion(AsyncOperationHandle handle) { - handle.CompletedTypeless += op => Release(op); + handle.CompletedTypeless += op => op.Release(); } public AsyncOperationHandle ClearDependencyCacheAsync(object key, bool autoReleaseHandle) @@ -1161,7 +1123,7 @@ public AsyncOperationHandle ClearDependencyCacheAsync(object key, bool aut var chainOp = ResourceManager.CreateChainOperation(ChainOperation, op => ClearDependencyCacheAsync(key, autoReleaseHandle)); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(chainOp); + chainOp.ReleaseHandleOnCompletion(); return chainOp; } @@ -1169,7 +1131,7 @@ public AsyncOperationHandle ClearDependencyCacheAsync(object key, bool aut var completedOp = ResourceManager.CreateCompletedOperation(result, result ? String.Empty : "Unable to clear the cache. AssetBundle's may still be loaded for the given key."); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(completedOp); + completedOp.ReleaseHandleOnCompletion(); return completedOp; } @@ -1181,7 +1143,7 @@ public AsyncOperationHandle ClearDependencyCacheAsync(IList ClearDependencyCacheAsync(locations, autoReleaseHandle)); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(chainOp); + chainOp.ReleaseHandleOnCompletion(); return chainOp; } @@ -1191,7 +1153,7 @@ public AsyncOperationHandle ClearDependencyCacheAsync(IList ClearDependencyCacheAsync(IEnumerable keys, bo var chainOp = ResourceManager.CreateChainOperation(ChainOperation, op => ClearDependencyCacheAsync(keys, autoReleaseHandle)); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(chainOp); + chainOp.ReleaseHandleOnCompletion(); return chainOp; } @@ -1213,7 +1175,7 @@ public AsyncOperationHandle ClearDependencyCacheAsync(IEnumerable keys, bo var completedOp = ResourceManager.CreateCompletedOperation(result, result ? String.Empty : "Unable to clear the cache. AssetBundle's may still be loaded for the given key(s)."); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(completedOp); + completedOp.ReleaseHandleOnCompletion(); return completedOp; } @@ -1294,7 +1256,7 @@ public bool ReleaseInstance(GameObject instance) AsyncOperationHandle handle; if (m_resultToHandle.TryGetValue(instance, out handle)) - Release(handle); + handle.Release(); else return false; @@ -1392,7 +1354,7 @@ internal AsyncOperationHandle InternalUnloadScene(AsyncOperationH QueueEditorUpdateIfNeeded(); var relOp = SceneProvider.ReleaseScene(ResourceManager, handle, unloadOptions); if (autoReleaseHandle) - AutoReleaseHandleOnCompletion(relOp, true); + relOp.ReleaseHandleOnCompletion(); return relOp; } @@ -1409,7 +1371,7 @@ internal AsyncOperationHandle> CheckForCatalogUpdates(bool autoRele return CheckForCatalogUpdatesWithChain(autoReleaseHandle); if (m_ActiveCheckUpdateOperation.IsValid()) - Release(m_ActiveCheckUpdateOperation); + m_ActiveCheckUpdateOperation.Release(); m_ActiveCheckUpdateOperation = new CheckCatalogsOperation(this).Start(m_ResourceLocators); if (autoReleaseHandle) diff --git a/Runtime/AssetReference.cs b/Runtime/AssetReference.cs index 9df0c89a..fd8b9f10 100644 --- a/Runtime/AssetReference.cs +++ b/Runtime/AssetReference.cs @@ -617,7 +617,7 @@ public virtual void ReleaseAsset() return; } - Addressables.Release(m_Operation); + m_Operation.Release(); m_Operation = default(AsyncOperationHandle); } @@ -842,7 +842,7 @@ public virtual bool SetEditorSubObject(Object value) { if (s.name == value.name && s.GetType() == value.GetType()) { - m_SubObjectGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(value)); + m_SubObjectGUID = String.Empty; m_SubObjectName = s.name; m_SubObjectType = s.GetType().AssemblyQualifiedName; m_EditorAssetChanged = true; diff --git a/Runtime/Initialization/CheckCatalogsOperation.cs b/Runtime/Initialization/CheckCatalogsOperation.cs index 07a1e270..8555b6b9 100644 --- a/Runtime/Initialization/CheckCatalogsOperation.cs +++ b/Runtime/Initialization/CheckCatalogsOperation.cs @@ -61,7 +61,7 @@ protected override bool InvokeWaitForCompletion() protected override void Destroy() { - m_Addressables.Release(m_DepOp); + m_DepOp.Release(); } /// diff --git a/Runtime/Initialization/InitializationObjectsOperation.cs b/Runtime/Initialization/InitializationObjectsOperation.cs index 3e7c0b89..e4d6f83d 100644 --- a/Runtime/Initialization/InitializationObjectsOperation.cs +++ b/Runtime/Initialization/InitializationObjectsOperation.cs @@ -115,7 +115,7 @@ protected override void Execute() { bool success = obj.Status == AsyncOperationStatus.Succeeded; Complete(true, success, success ? "" : $"{obj.DebugName}, status={obj.Status}, result={obj.Result} failed initialization."); - m_Addressables.Release(m_DepOp); + m_DepOp.Release(); }; } else diff --git a/Runtime/Initialization/InitializationOperation.cs b/Runtime/Initialization/InitializationOperation.cs index 93ec8a3f..74cfc166 100644 --- a/Runtime/Initialization/InitializationOperation.cs +++ b/Runtime/Initialization/InitializationOperation.cs @@ -53,10 +53,12 @@ internal static AsyncOperationHandle CreateInitializationOpera var runtimeDataLocation = new ResourceLocationBase("RuntimeData", playerSettingsLocation, typeof(JsonAssetProvider).FullName, typeof(ResourceManagerRuntimeData)); - var initOp = new InitializationOperation(aa); - initOp.m_rtdOp = aa.ResourceManager.ProvideResource(runtimeDataLocation); - initOp.m_ProviderSuffix = providerSuffix; - initOp.m_InitGroupOps = new InitalizationObjectsOperation(); + var initOp = new InitializationOperation(aa) + { + m_rtdOp = aa.ResourceManager.ProvideResource(runtimeDataLocation), + m_ProviderSuffix = providerSuffix, + m_InitGroupOps = new InitalizationObjectsOperation() + }; initOp.m_InitGroupOps.Init(initOp.m_rtdOp, aa); var groupOpHandle = aa.ResourceManager.StartOperation(initOp.m_InitGroupOps, initOp.m_rtdOp); @@ -120,7 +122,7 @@ protected override void Execute() } } - m_Addressables.Release(m_rtdOp); + m_rtdOp.Release(); if (rtd.CertificateHandlerType != null) m_Addressables.ResourceManager.CertificateHandlerInstance = Activator.CreateInstance(rtd.CertificateHandlerType) as CertificateHandler; @@ -210,14 +212,16 @@ static void LoadProvider(AddressablesImpl addressables, ObjectInitializationData IResourceLocation remoteHashLocation) { var data = op.Result; - addressables.Release(op); if (data == null) { var opException = op.OperationException != null ? new Exception("Failed to load content catalog.", op.OperationException) : new Exception("Failed to load content catalog."); + op.Release(); return addressables.ResourceManager.CreateCompletedOperationWithException(null, opException); } else { + op.Release(); + if (data.ResourceProviderData != null) foreach (var providerData in data.ResourceProviderData) LoadProvider(addressables, providerData, providerSuffix); @@ -239,7 +243,7 @@ static void LoadProvider(AddressablesImpl addressables, ObjectInitializationData data.location.Dependencies[(int)ContentCatalogProvider.DependencyHashIndex.Remote] = remoteHashLocation; IResourceLocator locMap = data.CreateCustomLocator(data.location.PrimaryKey, providerSuffix); - addressables.AddResourceLocator(locMap, data.localHash, data.location); + addressables.AddResourceLocator(locMap, data.LocalHash, data.location); addressables.AddResourceLocator(new DynamicResourceLocator(addressables)); return addressables.ResourceManager.CreateCompletedOperation(locMap, string.Empty); } @@ -294,7 +298,7 @@ void LoadOpComplete(AsyncOperationHandle op, IList op, IList @@ -88,7 +88,7 @@ protected override void Execute() { var catData = m_DepOp.Result[i].Result as ContentCatalogData; locator = catData.CreateCustomLocator(catData.location.PrimaryKey); - localHash = catData.localHash; + localHash = catData.LocalHash; remoteLocation = catData.location; } diff --git a/Runtime/ResourceLocators/ContentCatalogData.cs b/Runtime/ResourceLocators/ContentCatalogData.cs index d8c2f1e2..3206ddc2 100644 --- a/Runtime/ResourceLocators/ContentCatalogData.cs +++ b/Runtime/ResourceLocators/ContentCatalogData.cs @@ -78,8 +78,11 @@ public class ContentCatalogData //used to check the version of the data in case the format needs to change in the future const int kVersion = 2; + /// + /// Stores the local catalog hash + /// [NonSerialized] - internal string localHash; + public string LocalHash; [NonSerialized] internal IResourceLocation location; @@ -90,6 +93,11 @@ public class ContentCatalogData [SerializeField] internal string m_BuildResultHash; + /// + /// Stores the hash for the build result + /// + public string BuildResultHash { get => m_BuildResultHash; set => m_BuildResultHash = value; } + /// /// Stores the id of the data provider. /// @@ -245,7 +253,7 @@ public object Deserialize(BinaryStorageBuffer.Reader reader, Type t, uint offset cd.SceneProviderData = reader.ReadObject(h.sceneProvider); cd.ResourceProviderData = reader.ReadObjectArray(h.initObjectsArray).ToList(); - cd.m_BuildResultHash = reader.ReadString(h.buildResultHash); + cd.BuildResultHash = reader.ReadString(h.buildResultHash); return cd; } @@ -276,7 +284,7 @@ public uint Serialize(BinaryStorageBuffer.Writer writer, object val) instanceProvider = writer.WriteObject(cd.InstanceProviderData, false), sceneProvider = writer.WriteObject(cd.SceneProviderData, false), initObjectsArray = writer.WriteObjects(cd.m_ResourceProviderData, false), - buildResultHash = writer.WriteString(cd.m_BuildResultHash) + buildResultHash = writer.WriteString(cd.BuildResultHash) }; writer.Write(headerOffset, in header); diff --git a/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs b/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs index 555cf30d..af6c245c 100644 --- a/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs +++ b/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs @@ -214,7 +214,7 @@ protected internal void DecrementReferenceCount() { if (m_DestroyedAction != null) { - m_DestroyedAction.Invoke(new AsyncOperationHandle(this)); + m_DestroyedAction.Invoke(Handle); m_DestroyedAction.Clear(); } @@ -384,7 +384,7 @@ internal void InvokeCompletionEvent() { if (m_CompletedActionT != null) { - m_CompletedActionT.Invoke(new AsyncOperationHandle(this)); + m_CompletedActionT.Invoke(Handle); m_CompletedActionT.Clear(); } diff --git a/Runtime/ResourceManager/AsyncOperations/AsyncOperationHandle.cs b/Runtime/ResourceManager/AsyncOperations/AsyncOperationHandle.cs index 60f30876..934b3eb0 100644 --- a/Runtime/ResourceManager/AsyncOperations/AsyncOperationHandle.cs +++ b/Runtime/ResourceManager/AsyncOperations/AsyncOperationHandle.cs @@ -15,20 +15,13 @@ public struct AsyncOperationHandle : IEnumerator, IEquatable m_Version; } internal string LocationName { get { return m_LocationName; } set { m_LocationName = value; } } - bool m_UnloadSceneOpExcludeReleaseCallback; - - internal bool UnloadSceneOpExcludeReleaseCallback - { - get { return m_UnloadSceneOpExcludeReleaseCallback; } - set { m_UnloadSceneOpExcludeReleaseCallback = value; } - } - /// /// Conversion from typed to non typed handles. This does not increment the reference count. /// To convert from non-typed back, use AsyncOperationHandle.Convert<T>() @@ -45,7 +38,6 @@ internal AsyncOperationHandle(AsyncOperationBase op) m_InternalOp = op; m_Version = op?.Version ?? 0; m_LocationName = null; - m_UnloadSceneOpExcludeReleaseCallback = false; } /// @@ -69,7 +61,6 @@ internal AsyncOperationHandle(IAsyncOperation op) m_InternalOp = (AsyncOperationBase)op; m_Version = op?.Version ?? 0; m_LocationName = null; - m_UnloadSceneOpExcludeReleaseCallback = false; } internal AsyncOperationHandle(IAsyncOperation op, int version) @@ -77,7 +68,6 @@ internal AsyncOperationHandle(IAsyncOperation op, int version) m_InternalOp = (AsyncOperationBase)op; m_Version = version; m_LocationName = null; - m_UnloadSceneOpExcludeReleaseCallback = false; } internal AsyncOperationHandle(IAsyncOperation op, string locationName) @@ -85,7 +75,6 @@ internal AsyncOperationHandle(IAsyncOperation op, string locationName) m_InternalOp = (AsyncOperationBase)op; m_Version = op?.Version ?? 0; m_LocationName = locationName; - m_UnloadSceneOpExcludeReleaseCallback = false; } internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName) @@ -93,7 +82,6 @@ internal AsyncOperationHandle(IAsyncOperation op, int version, string locationNa m_InternalOp = (AsyncOperationBase)op; m_Version = version; m_LocationName = locationName; - m_UnloadSceneOpExcludeReleaseCallback = false; } /// @@ -115,6 +103,14 @@ internal AsyncOperationHandle Acquire() remove { InternalOp.Completed -= value; } } + /// + /// Automatically release this handle upon Completed callback + /// + public void ReleaseHandleOnCompletion() + { + Completed += op => op.Release(); + } + /// /// Completion event for non-typed callback handlers. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame. /// @@ -264,7 +260,7 @@ internal int ReferenceCount /// /// Release the handle. If the internal operation reference count reaches 0, the resource will be released. /// - internal void Release() + public void Release() { InternalOp.DecrementReferenceCount(); m_InternalOp = null; @@ -337,6 +333,7 @@ public static bool IsWaitingForCompletion int m_Version; string m_LocationName; + internal int Version{ get => m_Version; } internal string LocationName { get { return m_LocationName; } @@ -374,7 +371,7 @@ internal AsyncOperationHandle(IAsyncOperation op, int version, string locationNa /// /// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released. /// - /// A new handle to the operation. This handle must also be released. + /// A new handle to the operation. This handle must also be released. internal AsyncOperationHandle Acquire() { InternalOp.IncrementReferenceCount(); @@ -390,6 +387,14 @@ internal AsyncOperationHandle Acquire() remove { InternalOp.CompletedTypeless -= value; } } + /// + /// Automatically release this handle upon Completed callback + /// + public void ReleaseHandleOnCompletion() + { + Completed += op => op.Release(); + } + /// /// Converts handle to be typed. This does not increment the reference count. /// To convert back to non-typed, implicit conversion is available. @@ -527,7 +532,7 @@ internal int ReferenceCount /// /// Release the handle. If the internal operation reference count reaches 0, the resource will be released. /// - internal void Release() + public void Release() { InternalOp.DecrementReferenceCount(); m_InternalOp = null; diff --git a/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs b/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs index 368754c5..f4cdda0d 100644 --- a/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs +++ b/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs @@ -26,8 +26,6 @@ internal interface IGenericProviderOperation internal class ProviderOperation : AsyncOperationBase, IGenericProviderOperation, ICachable { private bool m_ReleaseDependenciesOnFailure = true; - private Action m_CompletionCallback; - private Action> m_GetDepCallback; private Func m_GetProgressCallback; private Func m_GetDownloadProgressCallback; private Func m_WaitForCompletionCallback; diff --git a/Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs b/Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs new file mode 100644 index 00000000..1c19c47f --- /dev/null +++ b/Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs @@ -0,0 +1,24 @@ +using System; +using UnityEngine.Profiling; + +namespace UnityEngine.ResourceManagement.Profiling +{ + + public class EngineEmitter : IProfilerEmitter + { + public bool IsEnabled + { + get => Profiler.enabled; + } + + public void EmitFrameMetaData(Guid id, int tag, Array data) + { + Profiler.EmitFrameMetaData(id, tag, data); + } + + public void InitialiseCallbacks(Action d) + { + MonoBehaviourCallbackHooks.Instance.OnLateUpdateDelegate += d; + } + } +} diff --git a/Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs.meta b/Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs.meta new file mode 100644 index 00000000..86f3a47b --- /dev/null +++ b/Runtime/ResourceManager/Diagnostics/Profiling/EngineEmitter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 752ec69ac00b4fcbbf48c1967b5a6fa6 +timeCreated: 1712941423 \ No newline at end of file diff --git a/Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs b/Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs new file mode 100644 index 00000000..2cdc10a3 --- /dev/null +++ b/Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs @@ -0,0 +1,12 @@ +using System; + +namespace UnityEngine.ResourceManagement.Profiling +{ + internal interface IProfilerEmitter + { + public bool IsEnabled { get; } + public void EmitFrameMetaData(Guid id, int tag, Array data); + + public void InitialiseCallbacks(Action onLateUpdateDelegate); + } +} diff --git a/Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs.meta b/Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs.meta new file mode 100644 index 00000000..7ba9a0b1 --- /dev/null +++ b/Runtime/ResourceManager/Diagnostics/Profiling/IProfilerEmitter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fb6d25eccdd3465781dfe97b66e07472 +timeCreated: 1712941313 \ No newline at end of file diff --git a/Runtime/ResourceManager/Diagnostics/Profiling/ProfilerRuntime.cs b/Runtime/ResourceManager/Diagnostics/Profiling/ProfilerRuntime.cs index e9662a27..8f2faa57 100644 --- a/Runtime/ResourceManager/Diagnostics/Profiling/ProfilerRuntime.cs +++ b/Runtime/ResourceManager/Diagnostics/Profiling/ProfilerRuntime.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Unity.Profiling; -using UnityEngine.Profiling; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.ResourceProviders; @@ -12,6 +11,7 @@ namespace UnityEngine.ResourceManagement.Profiling { internal static class ProfilerRuntime { + internal static IProfilerEmitter m_profilerEmitter = new EngineEmitter(); public static readonly Guid kResourceManagerProfilerGuid = new Guid("4f8a8c93-7634-4ef7-bbbc-6c9928567fa4"); public const int kCatalogTag = 0; public const int kBundleDataTag = 1; @@ -39,7 +39,12 @@ public static void Initialise() AssetLoadCounter.Value = 0; SceneLoadCounter.Value = 0; - MonoBehaviourCallbackHooks.Instance.OnLateUpdateDelegate += InstanceOnOnLateUpdateDelegate; + m_CatalogData.Data.Clear(); + m_BundleData.Data.Clear(); + m_AssetData.Data.Clear(); + m_SceneData.Data.Clear(); + + m_profilerEmitter.InitialiseCallbacks(InstanceOnOnLateUpdateDelegate); } private static void InstanceOnOnLateUpdateDelegate(float deltaTime) @@ -202,15 +207,16 @@ public static void SceneReleased(AsyncOperationHandle handle) } } - private static void PushToProfilerStream() + internal static void PushToProfilerStream() { - if (!Profiler.enabled) + if (!m_profilerEmitter.IsEnabled) return; RefreshChangedReferenceCounts(); - Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kCatalogTag, m_CatalogData.Values); - Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kBundleDataTag, m_BundleData.Values); - Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kAssetDataTag, m_AssetData.Values); - Profiler.EmitFrameMetaData(kResourceManagerProfilerGuid, kSceneDataTag, m_SceneData.Values); + m_profilerEmitter.EmitFrameMetaData(kResourceManagerProfilerGuid, kCatalogTag, m_CatalogData.Values); + m_profilerEmitter.EmitFrameMetaData(kResourceManagerProfilerGuid, kBundleDataTag, m_BundleData.Values); + m_profilerEmitter.EmitFrameMetaData(kResourceManagerProfilerGuid, kAssetDataTag, m_AssetData.Values); + m_profilerEmitter.EmitFrameMetaData(kResourceManagerProfilerGuid, kSceneDataTag, m_SceneData.Values); + m_CatalogData.Data.Clear(); } private static void RefreshChangedReferenceCounts() diff --git a/Runtime/ResourceManager/ResourceManager.cs b/Runtime/ResourceManager/ResourceManager.cs index fe9b6df7..4c8eb530 100644 --- a/Runtime/ResourceManager/ResourceManager.cs +++ b/Runtime/ResourceManager/ResourceManager.cs @@ -147,6 +147,9 @@ private struct DeferredCallbackRegisterRequest internal bool incrementRefCount; } + internal int DeferredCompleteCallbacksCount { get => m_DeferredCompleteCallbacks.Count; } + internal int DeferredCallbackCount { get => m_DeferredCallbacksToRegister?.Count ?? 0; } + Action m_ReleaseOpNonCached; Action m_ReleaseOpCached; Action m_ReleaseInstanceOp; @@ -597,6 +600,16 @@ public void Release(AsyncOperationHandle handle) handle.Release(); } + /// + /// Increment reference count of operation handle. + /// + /// The handle to the resource to increment the reference count for. + /// A new handle to the operation. This handle must also be released. + public AsyncOperationHandle Acquire(AsyncOperationHandle handle) + { + return handle.Acquire(); + } + /// /// Increment reference count of operation handle. /// diff --git a/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs b/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs index b5f86920..aa903058 100644 --- a/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs @@ -283,6 +283,8 @@ public enum LoadType float m_TimeoutTimer = 0; int m_TimeoutOverFrames = 0; internal bool m_DownloadOnly = false; + int m_LastFrameCount = -1; + float m_TimeSecSinceLastUpdate = 0; internal Func m_RequestRetryCallback = x => x.ShouldRetryDownloadError(); @@ -434,7 +436,7 @@ private void AddBundleToProfiler(Profiling.ContentStatus status, BundleSource so if (!m_ProvideHandle.IsValid) return; - if (status == Profiling.ContentStatus.Active && m_AssetBundle == null) + if (status == Profiling.ContentStatus.Active && m_AssetBundle == null) // is this going to suggest load only are released? Profiling.ProfilerRuntime.BundleReleased(m_Options.BundleName); else Profiling.ProfilerRuntime.AddBundleOperation(m_ProvideHandle, m_Options, status, source); @@ -680,13 +682,25 @@ public void Update(float unscaledDeltaTime) m_TimeoutTimer = 0; m_TimeoutOverFrames = 0; m_LastDownloadedByteCount = operation.webRequest.downloadedBytes; + + m_LastFrameCount = -1; + m_TimeSecSinceLastUpdate = 0; } else { - m_TimeoutTimer += unscaledDeltaTime; + float updateTime = unscaledDeltaTime; + if (m_LastFrameCount == Time.frameCount) + { + updateTime = Time.realtimeSinceStartup - m_TimeSecSinceLastUpdate; + } + + m_TimeoutTimer += updateTime; if (HasTimedOut) operation.webRequest.Abort(); m_TimeoutOverFrames++; + + m_LastFrameCount = Time.frameCount; + m_TimeSecSinceLastUpdate = Time.realtimeSinceStartup; } } } @@ -734,12 +748,12 @@ private void WebRequestOperationCompleted(AsyncOperation op) { if (!m_Completed) { - AddBundleToProfiler(Profiling.ContentStatus.Active, m_Source); if (!(m_ProvideHandle.Location is DownloadOnlyLocation)) { // this loads the bundle into memory which we don't want to do with download only bundles m_AssetBundle = downloadHandler.assetBundle; } + AddBundleToProfiler(Profiling.ContentStatus.Active, m_Source); downloadHandler.Dispose(); downloadHandler = null; m_ProvideHandle.Complete(this, true, null); diff --git a/Runtime/ResourceManager/ResourceProviders/AtlasSpriteProvider.cs b/Runtime/ResourceManager/ResourceProviders/AtlasSpriteProvider.cs index 7d6cb8b1..bd43cc1a 100644 --- a/Runtime/ResourceManager/ResourceProviders/AtlasSpriteProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/AtlasSpriteProvider.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.Util; using UnityEngine.U2D; @@ -26,5 +27,12 @@ public override void Provide(ProvideHandle providerInterface) var sprite = atlas.GetSprite(spriteKey); providerInterface.Complete(sprite, sprite != null, sprite != null ? null : new System.Exception($"Sprite failed to load for location {providerInterface.Location.PrimaryKey}.")); } + + /// + public override void Release(IResourceLocation location, object obj) + { + if (obj is Sprite sprite) + Object.Destroy(sprite); + } } } diff --git a/Runtime/ResourceManager/ResourceProviders/InstanceProvider.cs b/Runtime/ResourceManager/ResourceProviders/InstanceProvider.cs index 9a747bd6..6257497d 100644 --- a/Runtime/ResourceManager/ResourceProviders/InstanceProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/InstanceProvider.cs @@ -25,6 +25,10 @@ public GameObject ProvideInstance(ResourceManager resourceManager, AsyncOperatio /// public void ReleaseInstance(ResourceManager resourceManager, GameObject instance) { + // Guard for null - note that Unity overloads equality for GameObject so `default(GameObject) == null` is true so must use explicit `is null` type guard + if (instance is null) + return; + AsyncOperationHandle resource; if (!m_InstanceObjectToPrefabHandle.TryGetValue(instance, out resource)) { @@ -32,7 +36,7 @@ public void ReleaseInstance(ResourceManager resourceManager, GameObject instance } else { - resourceManager.Release(resource); + resource.Release(); m_InstanceObjectToPrefabHandle.Remove(instance); } diff --git a/Runtime/ResourceManager/ResourceProviders/SceneProvider.cs b/Runtime/ResourceManager/ResourceProviders/SceneProvider.cs index d2c19bf0..70641ddf 100644 --- a/Runtime/ResourceManager/ResourceProviders/SceneProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/SceneProvider.cs @@ -26,10 +26,12 @@ class SceneOp : AsyncOperationBase, IUpdateReceiver int m_Priority; private AsyncOperationHandle> m_DepOp; ResourceManager m_ResourceManager; + ISceneProvider2 m_provider; - public SceneOp(ResourceManager rm) + public SceneOp(ResourceManager rm, ISceneProvider2 provider) { m_ResourceManager = rm; + m_provider = provider; } internal override DownloadStatus GetDownloadStatus(HashSet visited) @@ -44,9 +46,7 @@ public void Init(IResourceLocation location, LoadSceneMode loadSceneMode, bool a public void Init(IResourceLocation location, LoadSceneParameters loadSceneParameters, SceneReleaseMode releaseMode, bool activateOnLoad, int priority, AsyncOperationHandle> depOp) { - m_DepOp = depOp; - if (m_DepOp.IsValid()) - m_DepOp.Acquire(); + m_DepOp = depOp.IsValid() ? depOp.Acquire() : depOp; m_Location = location; m_LoadSceneParameters = loadSceneParameters; @@ -156,12 +156,17 @@ AsyncOperation InternalLoad(string path, bool loadingFromBundle, LoadSceneParame } #endif } - protected override void Destroy() { - //the scene will be unloaded via the UnloadSceneOp as it waits correctlyf or the unload to complete before releasing the load op + if (m_Inst.Scene.IsValid()) + { + var unloadOp = m_provider.ReleaseScene(m_ResourceManager, Handle, UnloadSceneOptions.None); + unloadOp.ReleaseHandleOnCompletion(); + } + if (m_DepOp.IsValid()) m_DepOp.Release(); + base.Destroy(); } @@ -208,7 +213,7 @@ class UnloadSceneOp : AsyncOperationBase public void Init(AsyncOperationHandle sceneLoadHandle, UnloadSceneOptions options) { - if (sceneLoadHandle.ReferenceCount > 0) + if (sceneLoadHandle.IsValid()) { m_sceneLoadHandle = sceneLoadHandle; m_Instance = m_sceneLoadHandle.Result; @@ -247,8 +252,13 @@ protected override bool InvokeWaitForCompletion() private void UnloadSceneCompleted(AsyncOperation obj) { Complete(m_Instance, true, ""); - if (m_sceneLoadHandle.IsValid()) + + // ReleaseSceneOnHandleRelease : ReferenceCount > 0 check necessary as operation is in process of being destroyed + if (m_sceneLoadHandle.IsValid() + && m_sceneLoadHandle.ReferenceCount > 0) + { m_sceneLoadHandle.Release(); + } } private void UnloadSceneCompletedNoRelease(AsyncOperation obj) @@ -281,7 +291,7 @@ public AsyncOperationHandle ProvideScene(ResourceManager resource if (location.HasDependencies) depOp = resourceManager.ProvideResourceGroupCached(location.Dependencies, location.DependencyHashCode, typeof(IAssetBundleResource), null); - SceneOp op = new SceneOp(resourceManager); + SceneOp op = new SceneOp(resourceManager, this); op.Init(location, loadSceneParameters, releaseMode, activateOnLoad, priority, depOp); var handle = resourceManager.StartOperation(op, depOp); diff --git a/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs b/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs index 1891a78e..06fb211a 100644 --- a/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs +++ b/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs @@ -159,6 +159,8 @@ public long Size [SerializeField] internal string m_AssetPath; + public string AssetPath { get => m_AssetPath; set => m_AssetPath = value; } + /// /// Construct a new VirtualAssetBundleEntry /// @@ -552,7 +554,7 @@ public bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaT if (!(Context is IResourceLocation)) return false; var location = Context as IResourceLocation; - var assetPath = m_AssetInfo.m_AssetPath; + var assetPath = m_AssetInfo.AssetPath; object result = null; var pt = m_provideHandle.Type; diff --git a/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs b/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs index 47489478..3b7ab3d1 100644 --- a/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs @@ -156,28 +156,35 @@ private object ConvertText(string text) protected virtual void SendWebRequest(string path) { - path = path.Replace('\\', '/'); - UnityWebRequest request = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET, new DownloadHandlerBuffer(), null); - if (m_Timeout > 0) - request.timeout = m_Timeout; - - m_PI.ResourceManager.WebRequestOverride?.Invoke(request); - m_RequestQueueOperation = WebRequestQueue.QueueRequest(request); - if (m_RequestQueueOperation.IsDone) + try { - m_RequestOperation = m_RequestQueueOperation.Result; - if (m_RequestOperation.isDone) - RequestOperation_completed(m_RequestOperation); + path = path.Replace('\\', '/'); + UnityWebRequest request = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET, new DownloadHandlerBuffer(), null); + if (m_Timeout > 0) + request.timeout = m_Timeout; + + m_PI.ResourceManager.WebRequestOverride?.Invoke(request); + m_RequestQueueOperation = WebRequestQueue.QueueRequest(request); + if (m_RequestQueueOperation.IsDone) + { + m_RequestOperation = m_RequestQueueOperation.Result; + if (m_RequestOperation.isDone) + RequestOperation_completed(m_RequestOperation); + else + m_RequestOperation.completed += RequestOperation_completed; + } else - m_RequestOperation.completed += RequestOperation_completed; + { + m_RequestQueueOperation.OnComplete += asyncOperation => + { + m_RequestOperation = asyncOperation; + m_RequestOperation.completed += RequestOperation_completed; + }; + } } - else + catch (UriFormatException e) { - m_RequestQueueOperation.OnComplete += asyncOperation => - { - m_RequestOperation = asyncOperation; - m_RequestOperation.completed += RequestOperation_completed; - }; + throw new UriFormatException("Invalid path '" + path + "'", e); } } } diff --git a/Runtime/ResourceManager/Util/ComponentSingleton.cs b/Runtime/ResourceManager/Util/ComponentSingleton.cs index 63af3379..42c0f75e 100644 --- a/Runtime/ResourceManager/Util/ComponentSingleton.cs +++ b/Runtime/ResourceManager/Util/ComponentSingleton.cs @@ -1,3 +1,4 @@ +using System.Linq; using UnityEditor; namespace UnityEngine.ResourceManagement.Util @@ -36,7 +37,7 @@ public static T Instance static T FindInstance() { #if UNITY_EDITOR - foreach (T cb in Resources.FindObjectsOfTypeAll(typeof(T))) + foreach (T cb in Resources.FindObjectsOfTypeAll(typeof(T)).Cast()) { var go = cb.gameObject; if (!EditorUtility.IsPersistent(go.transform.root.gameObject) && !(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave)) diff --git a/Runtime/ResourceManager/Util/ListWithEvents.cs b/Runtime/ResourceManager/Util/ListWithEvents.cs index 36096b0f..fbc19f70 100644 --- a/Runtime/ResourceManager/Util/ListWithEvents.cs +++ b/Runtime/ResourceManager/Util/ListWithEvents.cs @@ -13,14 +13,12 @@ internal class ListWithEvents : IList private void InvokeAdded(T element) { - if (OnElementAdded != null) - OnElementAdded(element); + OnElementAdded?.Invoke(element); } private void InvokeRemoved(T element) { - if (OnElementRemoved != null) - OnElementRemoved(element); + OnElementRemoved?.Invoke(element); } public T this[int index] diff --git a/Runtime/ResourceProviders/ContentCatalogProvider.cs b/Runtime/ResourceProviders/ContentCatalogProvider.cs index 227d003e..671a36be 100644 --- a/Runtime/ResourceProviders/ContentCatalogProvider.cs +++ b/Runtime/ResourceProviders/ContentCatalogProvider.cs @@ -57,7 +57,6 @@ public enum DependencyHashIndex public bool IsLocalCatalogInBundle = false; internal Dictionary m_LocationToCatalogLoadOpMap = new Dictionary(); - ResourceManager m_RM; /// /// Constructor for this provider. @@ -65,7 +64,6 @@ public enum DependencyHashIndex /// The resource manager to use. public ContentCatalogProvider(ResourceManager resourceManagerInstance) { - m_RM = resourceManagerInstance; m_BehaviourFlags = ProviderBehaviourFlags.CanProvideWithFailedDependencies; } @@ -174,11 +172,23 @@ internal void LoadCatalog(string idToLoad, bool loadCatalogFromLocalBundle) else { #if !ENABLE_JSON_CATALOG + if (Path.GetExtension(idToLoad) == ".json") + { + m_ProviderInterface.Complete(null, false, new Exception("Expecting to load catalogs in binary format but the catalog provided is in .json format. To load it enable Addressable Asset Settings > Catalog > Enable Json Catalog.")); + return; + } + ResourceLocationBase location = new ResourceLocationBase(idToLoad, idToLoad, typeof(BinaryAssetProvider).FullName, typeof(ContentCatalogData)); location.Data = providerLoadRequestOptions; m_ProviderInterface.ResourceManager.ResourceProviders.Add(new BinaryAssetProvider()); #else + if (Path.GetExtension(idToLoad) == ".bin") + { + m_ProviderInterface.Complete(null, false, new Exception("Expecting to load catalogs in .json format but the catalog provided is in binary format. To load it disable Addressable Asset Settings > Catalog > Enable Json Catalog.")); + return; + } + ResourceLocationBase location = new ResourceLocationBase(idToLoad, idToLoad, typeof(JsonAssetProvider).FullName, typeof(ContentCatalogData)); location.Data = providerLoadRequestOptions; @@ -196,7 +206,7 @@ internal void LoadCatalog(string idToLoad, bool loadCatalogFromLocalBundle) void CatalogLoadOpCompleteCallback(AsyncOperationHandle op) { m_ContentCatalogData = op.Result; - m_ProviderInterface.ResourceManager.Release(op); + op.Release(); OnCatalogLoaded(m_ContentCatalogData); } @@ -421,10 +431,10 @@ private void OnCatalogLoaded(ContentCatalogData ccd) Addressables.LogFormat("Addressables - Content catalog load result = {0}.", ccd); if (ccd != null) { - ResourceManagement.Profiling.ProfilerRuntime.AddCatalog(Hash128.Parse(ccd.m_BuildResultHash)); + ResourceManagement.Profiling.ProfilerRuntime.AddCatalog(Hash128.Parse(ccd.BuildResultHash)); ccd.location = m_ProviderInterface.Location; - ccd.localHash = m_LocalHashValue; + ccd.LocalHash = m_LocalHashValue; if (!string.IsNullOrEmpty(m_RemoteHashValue) && !string.IsNullOrEmpty(m_LocalDataPath)) { #if ENABLE_CACHING @@ -457,7 +467,7 @@ private void OnCatalogLoaded(ContentCatalogData ccd) return; } #endif - ccd.localHash = m_RemoteHashValue; + ccd.LocalHash = m_RemoteHashValue; } #if ENABLE_CACHING else if (string.IsNullOrEmpty(m_LocalDataPath) && string.IsNullOrEmpty(Application.persistentDataPath)) diff --git a/Samples~/AddressablesUtility/AddressablesUtility.cs b/Samples~/AddressablesUtility/AddressablesUtility.cs index e64d34a4..2e0f4cbf 100644 --- a/Samples~/AddressablesUtility/AddressablesUtility.cs +++ b/Samples~/AddressablesUtility/AddressablesUtility.cs @@ -1,4 +1,4 @@ -using UnityEngine.AddressableAssets; +using UnityEngine.AddressableAssets; /// /// A utility class for various Addressables functionality @@ -17,11 +17,11 @@ public static string GetAddressFromAssetReference(AssetReference reference) if (result.Count > 0) { string key = result[0].PrimaryKey; - Addressables.Release(loadResourceLocations); + loadResourceLocations.Release(); return key; } - Addressables.Release(loadResourceLocations); + loadResourceLocations.Release(); return string.Empty; } } diff --git a/Samples~/ComponentReference/ComponentReference.cs b/Samples~/ComponentReference/ComponentReference.cs index 33e79499..45ca923b 100644 --- a/Samples~/ComponentReference/ComponentReference.cs +++ b/Samples~/ComponentReference/ComponentReference.cs @@ -82,6 +82,6 @@ public void ReleaseInstance(AsyncOperationHandle op) } // Release the handle - Addressables.Release(op); + op.Release(); } } diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset index c91f0251..1bc1da39 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset @@ -9,7 +9,7 @@ MonoBehaviour: m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f66315e0705fa82438547ab07bbe65e8, type: 3} + m_Script: {fileID: 11500000, guid: 938da90b96dab184e82e478c9e8642a8, type: 3} m_Name: CustomBuild m_EditorClassIdentifier: Unity.Addressables.SamplesFolder:UnityEditor.AddressableAssets.Build.DataBuilders:BuildScriptPackedMode instanceProviderType: diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset.meta b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset.meta index 7bdb2da9..cdf9e09c 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset.meta +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuild.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3c8ea4c2ec27a354f9b5c8d162aff2c4 +guid: 9d9d4152ec3ee9b47a3f1b5d5c7df828 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs index 4aac457a..5ab1c7f1 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs @@ -3,83 +3,47 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; -using System.Text; using UnityEditor; -using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Build; -using UnityEditor.AddressableAssets.Build.BuildPipelineTasks; using UnityEditor.AddressableAssets.Build.DataBuilders; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; -using UnityEditor.Build.Pipeline; -using UnityEditor.Build.Pipeline.Interfaces; -using UnityEditor.Build.Pipeline.Tasks; -using UnityEditor.Build.Pipeline.Utilities; using UnityEditor.SceneManagement; using UnityEngine; -using UnityEngine.AddressableAssets; -using UnityEngine.AddressableAssets.Initialization; -using UnityEngine.AddressableAssets.ResourceLocators; -using UnityEngine.AddressableAssets.ResourceProviders; -using UnityEngine.Build.Pipeline; -using UnityEngine.ResourceManagement.ResourceProviders; -using UnityEngine.ResourceManagement.Util; using UnityEngine.SceneManagement; -using static UnityEditor.AddressableAssets.Build.ContentUpdateScript; using Debug = UnityEngine.Debug; + /// /// Build scripts used for player builds and running with bundles in the editor. /// [CreateAssetMenu(fileName = "CustomBuild.asset", menuName = "Addressables/Content Builders/Custom Build Script")] -public class CustomBuildScript : BuildScriptBase +public class CustomBuildScript : BuildScriptPackedMode { + Dictionary m_SavedIncludeInBuildState = new Dictionary(); + AddressableAssetGroup m_CurrentSceneGroup; + /// public override string Name { get { return "Custom Build Script"; } } - internal List m_ResourceProviderData; - List m_AllBundleInputDefs; - List m_OutputAssetBundleNames; - HashSet m_CreatedProviderIds; - UnityEditor.Build.Pipeline.Utilities.LinkXmlGenerator m_Linker; - Dictionary m_BundleToInternalId = new Dictionary(); - private string m_CatalogBuildPath; - - internal List ResourceProviderData => m_ResourceProviderData.ToList(); - - /// - public override bool CanBuildData() - { - return typeof(T).IsAssignableFrom(typeof(AddressablesPlayerBuildResult)); - } - /// protected override TResult BuildDataImplementation(AddressablesDataBuilderInput builderInput) { - TResult result = default(TResult); - + TResult result; var timer = new Stopwatch(); timer.Start(); - CreateCurrentSceneOnlyBuildSetup(builderInput.AddressableSettings); - - InitializeBuildContext(builderInput, out AddressableAssetsBuildContext aaContext); - - using (Log.ScopedStep(LogLevel.Info, "ProcessAllGroups")) + try { - var errorString = ProcessAllGroups(aaContext); - if (!string.IsNullOrEmpty(errorString)) - result = AddressableAssetBuildResult.CreateResult(null, 0, errorString); + CreateCurrentSceneOnlyBuildSetup(builderInput.AddressableSettings); + result = base.BuildDataImplementation(builderInput); } - - if (result == null) + finally { - result = DoBuild(builderInput, aaContext); - RevertCurrentSceneSetup(aaContext.Settings); + RevertCurrentSceneSetup(builderInput.AddressableSettings); } if (result != null) @@ -88,311 +52,22 @@ protected override TResult BuildDataImplementation(AddressablesDataBuil return result; } - internal void InitializeBuildContext(AddressablesDataBuilderInput builderInput, out AddressableAssetsBuildContext aaContext) - { - var aaSettings = builderInput.AddressableSettings; - - m_AllBundleInputDefs = new List(); - m_OutputAssetBundleNames = new List(); - var bundleToAssetGroup = new Dictionary(); - var runtimeData = new ResourceManagerRuntimeData - { - CertificateHandlerType = aaSettings.CertificateHandlerType, - BuildTarget = builderInput.Target.ToString(), - LogResourceManagerExceptions = aaSettings.buildSettings.LogResourceManagerExceptions, - DisableCatalogUpdateOnStartup = aaSettings.DisableCatalogUpdateOnStartup, -#if ENABLE_JSON_CATALOG - IsLocalCatalogInBundle = aaSettings.BundleLocalCatalog, -#endif - AddressablesVersion = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(Addressables).Assembly)?.version, - MaxConcurrentWebRequests = aaSettings.MaxConcurrentWebRequests, - CatalogRequestsTimeout = aaSettings.CatalogRequestsTimeout - }; - m_Linker = UnityEditor.Build.Pipeline.Utilities.LinkXmlGenerator.CreateDefault(); - m_Linker.AddAssemblies(new[] {typeof(Addressables).Assembly, typeof(UnityEngine.ResourceManagement.ResourceManager).Assembly}); - m_Linker.AddTypes(runtimeData.CertificateHandlerType); - - m_ResourceProviderData = new List(); - aaContext = new AddressableAssetsBuildContext - { - Settings = aaSettings, - runtimeData = runtimeData, - bundleToAssetGroup = bundleToAssetGroup, - locations = new List(), - providerTypes = new HashSet(), - assetEntries = new List() - }; - - m_CreatedProviderIds = new HashSet(); - } - - struct SBPSettingsOverwriterScope : IDisposable - { - bool m_PrevSlimResults; - - public SBPSettingsOverwriterScope(bool forceFullWriteResults) - { - m_PrevSlimResults = ScriptableBuildPipeline.slimWriteResults; - if (forceFullWriteResults) - ScriptableBuildPipeline.slimWriteResults = false; - } - - public void Dispose() - { - ScriptableBuildPipeline.slimWriteResults = m_PrevSlimResults; - } - } - - internal static string GetBuiltInShaderBundleNamePrefix(AddressableAssetsBuildContext aaContext) - { - return GetBuiltInShaderBundleNamePrefix(aaContext.Settings); - } - - internal static string GetBuiltInShaderBundleNamePrefix(AddressableAssetSettings settings) - { - string value = ""; - switch (settings.BuiltInBundleNaming) - { - case BuiltInBundleNaming.DefaultGroupGuid: - value = settings.DefaultGroup.Guid; - break; - case BuiltInBundleNaming.ProjectName: - value = Hash128.Compute(GetProjectName()).ToString(); - break; - case BuiltInBundleNaming.Custom: - value = settings.BuiltInBundleCustomNaming; - break; - } - - return value; - } - - void AddBundleProvider(BundledAssetGroupSchema schema) - { - var bundleProviderId = schema.GetBundleCachedProviderId(); - - if (!m_CreatedProviderIds.Contains(bundleProviderId)) - { - m_CreatedProviderIds.Add(bundleProviderId); - var bundleProviderType = schema.AssetBundleProviderType.Value; - var bundleProviderData = ObjectInitializationData.CreateSerializedInitializationData(bundleProviderType, bundleProviderId); - m_ResourceProviderData.Add(bundleProviderData); - } - } - - internal static string GetMonoScriptBundleNamePrefix(AddressableAssetsBuildContext aaContext) - { - return GetMonoScriptBundleNamePrefix(aaContext.Settings); - } - - internal static string GetMonoScriptBundleNamePrefix(AddressableAssetSettings settings) - { - string value = null; - switch (settings.MonoScriptBundleNaming) - { - case MonoScriptBundleNaming.ProjectName: - value = Hash128.Compute(GetProjectName()).ToString(); - break; - case MonoScriptBundleNaming.DefaultGroupGuid: - value = settings.DefaultGroup.Guid; - break; - case MonoScriptBundleNaming.Custom: - value = settings.MonoScriptBundleCustomNaming; - break; - } - - return value; - } - - /// - /// The method that does the actual building after all the groups have been processed. - /// - /// The generic builderInput of the - /// - /// - /// - protected virtual TResult DoBuild(AddressablesDataBuilderInput builderInput, AddressableAssetsBuildContext aaContext) where TResult : IDataBuilderResult + void CreateCurrentSceneOnlyBuildSetup(AddressableAssetSettings settings) { - ExtractDataTask extractData = new ExtractDataTask(); - List carryOverCachedState = new List(); - var tempPath = Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath + PlatformMappingService.GetPlatformPathSubFolder() + "/addressables_content_state.bin"; - - var playerBuildVersion = builderInput.PlayerVersion; - if (m_AllBundleInputDefs.Count > 0) + Debug.Log("HERE"); + Debug.LogError(SceneManager.GetActiveScene().name); + if (SceneManager.GetActiveScene().name == "customscene") { - if (!BuildUtility.CheckModifiedScenesAndAskToSave()) - return AddressableAssetBuildResult.CreateResult(null, 0, "Unsaved scenes"); - - var buildTarget = builderInput.Target; - var buildTargetGroup = builderInput.TargetGroup; - - var buildParams = new AddressableAssetsBundleBuildParameters( - aaContext.Settings, - aaContext.bundleToAssetGroup, - buildTarget, - buildTargetGroup, - aaContext.Settings.buildSettings.bundleBuildPath); - - var builtinBundleName = GetBuiltInShaderBundleNamePrefix(aaContext) + $"{BuiltInBundleBaseName}.bundle"; - - var schema = aaContext.Settings.DefaultGroup.GetSchema(); - AddBundleProvider(schema); - - string monoScriptBundleName = GetMonoScriptBundleNamePrefix(aaContext); - if (!string.IsNullOrEmpty(monoScriptBundleName)) - monoScriptBundleName += "_monoscripts.bundle"; - var buildTasks = RuntimeDataBuildTasks(builtinBundleName, monoScriptBundleName); - buildTasks.Add(extractData); - - IBundleBuildResults results; - using (Log.ScopedStep(LogLevel.Info, "ContentPipeline.BuildAssetBundles")) - using (new SBPSettingsOverwriterScope(ProjectConfigData.GenerateBuildLayout)) // build layout generation requires full SBP write results - { - var exitCode = ContentPipeline.BuildAssetBundles(buildParams, new BundleBuildContent(m_AllBundleInputDefs), out results, buildTasks, aaContext, Log); - - if (exitCode < ReturnCode.Success) - return AddressableAssetBuildResult.CreateResult(null, 0, "SBP Error" + exitCode); - } + throw new Exception( + "please make sure that a scene other than the customscene is currently open and make sure it doesn't have any Addressable dependencies"); - var groups = aaContext.Settings.groups.Where(g => g != null); - - var bundleRenameMap = new Dictionary(); - var postCatalogUpdateCallbacks = new List(); - using (Log.ScopedStep(LogLevel.Info, "PostProcessBundles")) - using (var progressTracker = new UnityEditor.Build.Pipeline.Utilities.ProgressTracker()) - { - progressTracker.UpdateTask("Post Processing AssetBundles"); - - Dictionary primaryKeyToCatalogEntry = new Dictionary(); - foreach (var loc in aaContext.locations) - if (loc != null && loc.Keys[0] != null && loc.Keys[0] is string && !primaryKeyToCatalogEntry.ContainsKey((string)loc.Keys[0])) - primaryKeyToCatalogEntry[(string)loc.Keys[0]] = loc; - - foreach (var assetGroup in groups) - { - if (aaContext.assetGroupToBundles.TryGetValue(assetGroup, out List buildBundles)) - { - List outputBundles = new List(); - for (int i = 0; i < buildBundles.Count; ++i) - { - var b = m_AllBundleInputDefs.FindIndex(inputDef => - buildBundles[i].StartsWith(inputDef.assetBundleName, StringComparison.Ordinal)); - outputBundles.Add(b >= 0 ? m_OutputAssetBundleNames[b] : buildBundles[i]); - } - - AddressableAssetGroup sharedBundleGroup = aaContext.Settings.GetSharedBundleGroup(); - PostProcessBundles(assetGroup, buildBundles, outputBundles, results, aaContext.runtimeData, aaContext.locations, builderInput.Registry, primaryKeyToCatalogEntry, - bundleRenameMap, postCatalogUpdateCallbacks, sharedBundleGroup); - } - } - } - - ProcessCatalogEntriesForBuild(aaContext, Log, groups, builderInput, extractData.WriteData, carryOverCachedState, m_BundleToInternalId); - foreach (var postUpdateCatalogCallback in postCatalogUpdateCallbacks) - postUpdateCatalogCallback.Invoke(); - - foreach (var r in results.WriteResults) - { - var resultValue = r.Value; - m_Linker.AddTypes(resultValue.includedTypes); - m_Linker.AddSerializedClass(resultValue.includedSerializeReferenceFQN); - } - - - if (ProjectConfigData.GenerateBuildLayout) - { - using (var progressTracker = new UnityEditor.Build.Pipeline.Utilities.ProgressTracker()) - { - progressTracker.UpdateTask("Generating Build Layout"); - List tasks = new List(); - var buildLayoutTask = new BuildLayoutGenerationTask(); - buildLayoutTask.BundleNameRemap = bundleRenameMap; - tasks.Add(buildLayoutTask); - BuildTasksRunner.Run(tasks, extractData.BuildContext); - } - } } - - var contentCatalog = new ContentCatalogData(ResourceManagerRuntimeData.kCatalogAddress); - contentCatalog.SetData(aaContext.locations.OrderBy(f => f.InternalId).ToList()); - - contentCatalog.ResourceProviderData.AddRange(m_ResourceProviderData); - foreach (var t in aaContext.providerTypes) - contentCatalog.ResourceProviderData.Add(ObjectInitializationData.CreateSerializedInitializationData(t)); - - contentCatalog.InstanceProviderData = ObjectInitializationData.CreateSerializedInitializationData(instanceProviderType.Value); - contentCatalog.SceneProviderData = ObjectInitializationData.CreateSerializedInitializationData(sceneProviderType.Value); - - //save catalog - var jsonText = JsonUtility.ToJson(contentCatalog); - CreateCatalogFiles(jsonText, builderInput, aaContext); - - foreach (var pd in contentCatalog.ResourceProviderData) - { - m_Linker.AddTypes(pd.ObjectType.Value); - m_Linker.AddTypes(pd.GetRuntimeTypes()); - } - - m_Linker.AddTypes(contentCatalog.InstanceProviderData.ObjectType.Value); - m_Linker.AddTypes(contentCatalog.InstanceProviderData.GetRuntimeTypes()); - m_Linker.AddTypes(contentCatalog.SceneProviderData.ObjectType.Value); - m_Linker.AddTypes(contentCatalog.SceneProviderData.GetRuntimeTypes()); - - foreach (var io in aaContext.Settings.InitializationObjects) - { - var provider = io as IObjectInitializationDataProvider; - if (provider != null) - { - var id = provider.CreateObjectInitializationData(); - aaContext.runtimeData.InitializationObjects.Add(id); - m_Linker.AddTypes(id.ObjectType.Value); - m_Linker.AddTypes(id.GetRuntimeTypes()); - } - } - - m_Linker.AddTypes(typeof(Addressables)); - Directory.CreateDirectory(Addressables.BuildPath + "/AddressablesLink/"); - m_Linker.Save(Addressables.BuildPath + "/AddressablesLink/link.xml"); - var settingsPath = Addressables.BuildPath + "/" + builderInput.RuntimeSettingsFilename; - WriteFile(settingsPath, JsonUtility.ToJson(aaContext.runtimeData), builderInput.Registry); - - var opResult = AddressableAssetBuildResult.CreateResult(settingsPath, aaContext.locations.Count); - if (extractData.BuildCache != null && builderInput.PreviousContentState == null) - { - var allEntries = new List(); - aaContext.Settings.GetAllAssets(allEntries, false, ContentUpdateScript.GroupFilterFunc); - var remoteCatalogLoadPath = aaContext.Settings.BuildRemoteCatalog ? aaContext.Settings.RemoteCatalogLoadPath.GetValue(aaContext.Settings) : string.Empty; - if (ContentUpdateScript.SaveContentState(aaContext.locations, tempPath, allEntries, extractData.DependencyData, playerBuildVersion, remoteCatalogLoadPath, carryOverCachedState)) - { - string contentStatePath = ContentUpdateScript.GetContentStateDataPath(false); - try - { - File.Copy(tempPath, contentStatePath, true); - builderInput.Registry.AddFile(contentStatePath); - } - catch (UnauthorizedAccessException uae) - { - Debug.LogException(uae); - } - catch (Exception e) - { - Debug.LogException(e); - } - } - } - - return opResult; - } - - Dictionary m_SavedIncludeInBuildState = new Dictionary(); - AddressableAssetGroup m_CurrentSceneGroup; - - void CreateCurrentSceneOnlyBuildSetup(AddressableAssetSettings settings) - { foreach (var group in settings.groups) { + if (!group) + continue; var schema = group.GetSchema(); - if (schema == null) + if (!schema) continue; m_SavedIncludeInBuildState.Add(group, schema.IncludeInBuild); schema.IncludeInBuild = false; @@ -418,8 +93,10 @@ void RevertCurrentSceneSetup(AddressableAssetSettings settings) { foreach (var group in settings.groups) { + if (!group) + continue; var schema = group.GetSchema(); - if (schema == null || !m_SavedIncludeInBuildState.ContainsKey(group)) + if (!schema || !m_SavedIncludeInBuildState.ContainsKey(group)) continue; schema.IncludeInBuild = m_SavedIncludeInBuildState[group]; } @@ -428,840 +105,5 @@ void RevertCurrentSceneSetup(AddressableAssetSettings settings) settings.RemoveGroup(m_CurrentSceneGroup); m_CurrentSceneGroup = null; } - - private static void ProcessCatalogEntriesForBuild(AddressableAssetsBuildContext aaContext, IBuildLogger log, - IEnumerable validGroups, AddressablesDataBuilderInput builderInput, IBundleWriteData writeData, - List carryOverCachedState, Dictionary bundleToInternalId) - { - using (log.ScopedStep(LogLevel.Info, "Catalog Entries.")) - using (var progressTracker = new UnityEditor.Build.Pipeline.Utilities.ProgressTracker()) - { - progressTracker.UpdateTask("Post Processing Catalog Entries"); - Dictionary locationIdToCatalogEntryMap = BuildLocationIdToCatalogEntryMap(aaContext.locations); - if (builderInput.PreviousContentState != null) - { - ContentUpdateContext contentUpdateContext = new ContentUpdateContext() - { - BundleToInternalBundleIdMap = bundleToInternalId, - GuidToPreviousAssetStateMap = BuildGuidToCachedAssetStateMap(builderInput.PreviousContentState, aaContext.Settings), - IdToCatalogDataEntryMap = locationIdToCatalogEntryMap, - WriteData = writeData, - ContentState = builderInput.PreviousContentState, - Registry = builderInput.Registry, - PreviousAssetStateCarryOver = carryOverCachedState - }; - - RevertUnchangedAssetsToPreviousAssetState.Run(aaContext, contentUpdateContext); - } - else - { - foreach (var assetGroup in validGroups) - SetAssetEntriesBundleFileIdToCatalogEntryBundleFileId(assetGroup.entries, bundleToInternalId, writeData, locationIdToCatalogEntryMap); - } - } - - bundleToInternalId.Clear(); - } - - private static Dictionary BuildLocationIdToCatalogEntryMap(List locations) - { - Dictionary locationIdToCatalogEntryMap = new Dictionary(); - foreach (var location in locations) - locationIdToCatalogEntryMap[location.InternalId] = location; - - return locationIdToCatalogEntryMap; - } - - private static Dictionary BuildGuidToCachedAssetStateMap(AddressablesContentState contentState, AddressableAssetSettings settings) - { - Dictionary addressableEntryToCachedStateMap = new Dictionary(); - foreach (var cachedInfo in contentState.cachedInfos) - addressableEntryToCachedStateMap[cachedInfo.asset.guid.ToString()] = cachedInfo; - - return addressableEntryToCachedStateMap; - } - - internal bool CreateCatalogFiles(string jsonText, AddressablesDataBuilderInput builderInput, AddressableAssetsBuildContext aaContext) - { - if (string.IsNullOrEmpty(jsonText) || builderInput == null || aaContext == null) - { - Addressables.LogError("Unable to create content catalog (Null arguments)."); - return false; - } - - // Path needs to be resolved at runtime. - string localLoadPath = "{UnityEngine.AddressableAssets.Addressables.RuntimePath}/" + builderInput.RuntimeCatalogFilename; - m_CatalogBuildPath = Path.Combine(Addressables.BuildPath, builderInput.RuntimeCatalogFilename); - -#if ENABLE_JSON_CATALOG - if (aaContext.Settings.BundleLocalCatalog) - { - localLoadPath = localLoadPath.Replace(".json", ".bundle"); - m_CatalogBuildPath = m_CatalogBuildPath.Replace(".json", ".bundle"); - var returnCode = CreateCatalogBundle(m_CatalogBuildPath, jsonText, builderInput); - if (returnCode != ReturnCode.Success || !File.Exists(m_CatalogBuildPath)) - { - Addressables.LogError($"An error occured during the creation of the content catalog bundle (return code {returnCode})."); - return false; - } - } - else -#endif - { - WriteFile(m_CatalogBuildPath, jsonText, builderInput.Registry); - WriteFile(m_CatalogBuildPath.Replace(".json", ".hash"), HashingMethods.Calculate(jsonText).ToString(), builderInput.Registry); - } - - string[] dependencyHashes = null; - if (aaContext.Settings.BuildRemoteCatalog) - { - dependencyHashes = CreateRemoteCatalog(jsonText, aaContext.runtimeData.CatalogLocations, aaContext.Settings, builderInput, new ProviderLoadRequestOptions() {IgnoreFailures = true}); - } - - aaContext.runtimeData.CatalogLocations.Add(new ResourceLocationData( - new[] {ResourceManagerRuntimeData.kCatalogAddress}, - localLoadPath, - typeof(ContentCatalogProvider), - typeof(ContentCatalogData), - dependencyHashes)); - - return true; - } - - internal static string GetProjectName() - { - return new DirectoryInfo(Path.GetDirectoryName(Application.dataPath)).Name; - } - - internal ReturnCode CreateCatalogBundle(string filepath, string jsonText, AddressablesDataBuilderInput builderInput) - { - if (string.IsNullOrEmpty(filepath) || string.IsNullOrEmpty(jsonText) || builderInput == null) - { - throw new ArgumentException("Unable to create catalog bundle (null arguments)."); - } - - // A bundle requires an actual asset - var tempFolderName = "TempCatalogFolder"; - - var configFolder = AddressableAssetSettingsDefaultObject.kDefaultConfigFolder; - if (builderInput.AddressableSettings != null && builderInput.AddressableSettings.IsPersisted) - configFolder = builderInput.AddressableSettings.ConfigFolder; - - var tempFolderPath = Path.Combine(configFolder, tempFolderName); - var tempFilePath = Path.Combine(tempFolderPath, Path.GetFileName(filepath).Replace(".bundle", ".json")); - if (!WriteFile(tempFilePath, jsonText, builderInput.Registry)) - { - throw new Exception("An error occured during the creation of temporary files needed to bundle the content catalog."); - } - - AssetDatabase.Refresh(); - - var bundleBuildContent = new BundleBuildContent(new[] - { - new AssetBundleBuild() - { - assetBundleName = Path.GetFileName(filepath), - assetNames = new[] {tempFilePath}, - addressableNames = new string[0] - } - }); - - var buildTasks = new List - { - new CalculateAssetDependencyData(), - new GenerateBundlePacking(), - new GenerateBundleCommands(), - new WriteSerializedFiles(), - new ArchiveAndCompressBundles() - }; - - var buildParams = new BundleBuildParameters(builderInput.Target, builderInput.TargetGroup, Path.GetDirectoryName(filepath)); - if (builderInput.Target == BuildTarget.WebGL) - buildParams.BundleCompression = BuildCompression.LZ4Runtime; - var retCode = ContentPipeline.BuildAssetBundles(buildParams, bundleBuildContent, out IBundleBuildResults result, buildTasks, Log); - - if (Directory.Exists(tempFolderPath)) - { - Directory.Delete(tempFolderPath, true); - builderInput.Registry.RemoveFile(tempFilePath); - } - - var tempFolderMetaFile = tempFolderPath + ".meta"; - if (File.Exists(tempFolderMetaFile)) - { - File.Delete(tempFolderMetaFile); - builderInput.Registry.RemoveFile(tempFolderMetaFile); - } - - if (File.Exists(filepath)) - { - builderInput.Registry.AddFile(filepath); - } - - return retCode; - } - - internal static void SetAssetEntriesBundleFileIdToCatalogEntryBundleFileId(ICollection assetEntries, Dictionary bundleNameToInternalBundleIdMap, - IBundleWriteData writeData, Dictionary locationIdToCatalogEntryMap) - { - foreach (var loc in assetEntries) - { - AddressableAssetEntry processedEntry = loc; - if (loc.IsFolder && loc.SubAssets.Count > 0) - processedEntry = loc.SubAssets[0]; - GUID guid = new GUID(processedEntry.guid); - //For every entry in the write data we need to ensure the BundleFileId is set so we can save it correctly in the cached state - if (writeData.AssetToFiles.TryGetValue(guid, out List files)) - { - string file = files[0]; - string fullBundleName = writeData.FileToBundle[file]; - string convertedLocation = bundleNameToInternalBundleIdMap[fullBundleName]; - - if (locationIdToCatalogEntryMap.TryGetValue(convertedLocation, - out ContentCatalogDataEntry catalogEntry)) - { - loc.BundleFileId = catalogEntry.InternalId; - - //This is where we strip out the temporary hash added to the bundle name for Content Update for the AssetEntry - if (loc.parentGroup?.GetSchema()?.BundleNaming == - BundledAssetGroupSchema.BundleNamingStyle.NoHash) - { - loc.BundleFileId = StripHashFromBundleLocation(loc.BundleFileId); - } - } - } - } - } - - static string StripHashFromBundleLocation(string hashedBundleLocation) - { - return hashedBundleLocation.Remove(hashedBundleLocation.LastIndexOf('_')) + ".bundle"; - } - - /// - protected override string ProcessGroup(AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext) - { - if (assetGroup == null) - return string.Empty; - - if (assetGroup.Schemas.Count == 0) - { - Addressables.LogWarning($"{assetGroup.Name} does not have any associated AddressableAssetGroupSchemas. " + - $"Data from this group will not be included in the build. " + - $"If this is unexpected the AddressableGroup may have become corrupted."); - return string.Empty; - } - - foreach (var schema in assetGroup.Schemas) - { - var errorString = ProcessGroupSchema(schema, assetGroup, aaContext); - if (!string.IsNullOrEmpty(errorString)) - return errorString; - } - - return string.Empty; - } - - /// - /// Called per group per schema to evaluate that schema. This can be an easy entry point for implementing the - /// build aspects surrounding a custom schema. Note, you should not rely on schemas getting called in a specific - /// order. - /// - /// The schema to process - /// The group this schema was pulled from - /// The general Addressables build builderInput - /// - protected virtual string ProcessGroupSchema(AddressableAssetGroupSchema schema, AddressableAssetGroup assetGroup, AddressableAssetsBuildContext aaContext) - { - var bundledAssetSchema = schema as BundledAssetGroupSchema; - if (bundledAssetSchema != null) - return ProcessBundledAssetSchema(bundledAssetSchema, assetGroup, aaContext); - return string.Empty; - } - - /// - /// The processing of the bundled asset schema. This is where the bundle(s) for a given group are actually setup. - /// - /// The BundledAssetGroupSchema to process - /// The group this schema was pulled from - /// The general Addressables build builderInput - /// The error string, if any. - protected virtual string ProcessBundledAssetSchema( - BundledAssetGroupSchema schema, - AddressableAssetGroup assetGroup, - AddressableAssetsBuildContext aaContext) - { - if (schema == null || !schema.IncludeInBuild || !assetGroup.entries.Any()) - return string.Empty; - - var errorStr = ErrorCheckBundleSettings(schema, assetGroup, aaContext.Settings); - if (!string.IsNullOrEmpty(errorStr)) - return errorStr; - - AddBundleProvider(schema); - - var assetProviderId = schema.GetAssetCachedProviderId(); - if (!m_CreatedProviderIds.Contains(assetProviderId)) - { - m_CreatedProviderIds.Add(assetProviderId); - var assetProviderType = schema.BundledAssetProviderType.Value; - var assetProviderData = ObjectInitializationData.CreateSerializedInitializationData(assetProviderType, assetProviderId); - m_ResourceProviderData.Add(assetProviderData); - } - - string buildPath = schema.BuildPath.GetValue(aaContext.Settings); - if (buildPath == AddressableAssetProfileSettings.undefinedEntryValue) - return ($"Addressable group {assetGroup.Name} build path is set to undefined. Change the path to build content."); - - string loadPath = schema.LoadPath.GetValue(aaContext.Settings); - if (loadPath == AddressableAssetProfileSettings.undefinedEntryValue) - Addressables.LogWarning($"Addressable group {assetGroup.Name} load path is set to undefined. Change the path to load content."); - - if (loadPath.StartsWith("http://", StringComparison.Ordinal) && PlayerSettings.insecureHttpOption == InsecureHttpOption.NotAllowed) - Addressables.LogWarning($"Addressable group {assetGroup.Name} uses insecure http for its load path. To allow http connections for UnityWebRequests, change your settings in Edit > Project Settings > Player > Other Settings > Configuration > Allow downloads over HTTP."); - - if (schema.Compression == BundledAssetGroupSchema.BundleCompressionMode.LZMA && aaContext.runtimeData.BuildTarget == BuildTarget.WebGL.ToString()) - Addressables.LogWarning($"Addressable group {assetGroup.Name} uses LZMA compression, which cannot be decompressed on WebGL. Use LZ4 compression instead."); - - var bundleInputDefs = new List(); - var list = PrepGroupBundlePacking(assetGroup, bundleInputDefs, schema); - aaContext.assetEntries.AddRange(list); - List uniqueNames = HandleDuplicateBundleNames(bundleInputDefs, aaContext.bundleToAssetGroup, assetGroup.Guid); - m_OutputAssetBundleNames.AddRange(uniqueNames); - m_AllBundleInputDefs.AddRange(bundleInputDefs); - return string.Empty; - } - - internal static List HandleDuplicateBundleNames(List bundleInputDefs, Dictionary bundleToAssetGroup = null, string assetGroupGuid = null) - { - var generatedUniqueNames = new List(); - var handledNames = new HashSet(); - - for (int i = 0; i < bundleInputDefs.Count; i++) - { - AssetBundleBuild bundleBuild = bundleInputDefs[i]; - string assetBundleName = bundleBuild.assetBundleName; - if (handledNames.Contains(assetBundleName)) - { - int count = 1; - var newName = assetBundleName; - while (handledNames.Contains(newName) && count < 1000) - newName = assetBundleName.Replace(".bundle", string.Format("{0}.bundle", count++)); - assetBundleName = newName; - } - - string hashedAssetBundleName = HashingMethods.Calculate(assetBundleName) + ".bundle"; - generatedUniqueNames.Add(assetBundleName); - handledNames.Add(assetBundleName); - - bundleBuild.assetBundleName = hashedAssetBundleName; - bundleInputDefs[i] = bundleBuild; - - if (bundleToAssetGroup != null) - bundleToAssetGroup.Add(hashedAssetBundleName, assetGroupGuid); - } - - return generatedUniqueNames; - } - - internal static string ErrorCheckBundleSettings(BundledAssetGroupSchema schema, AddressableAssetGroup assetGroup, AddressableAssetSettings settings) - { - var message = string.Empty; - - string buildPath = schema.BuildPath.GetValue(settings, false); - string loadPath = schema.LoadPath.GetValue(settings, false); - - bool buildLocal = buildPath.Contains("[UnityEngine.AddressableAssets.Addressables.BuildPath]"); - bool loadLocal = loadPath.Contains("{UnityEngine.AddressableAssets.Addressables.RuntimePath}"); - - if (buildLocal && !loadLocal) - { - message = "BuildPath for group '" + assetGroup.Name + "' is set to the dynamic-lookup version of StreamingAssets, but LoadPath is not. \n"; - } - else if (!buildLocal && loadLocal) - { - message = "LoadPath for group " + assetGroup.Name + - " is set to the dynamic-lookup version of StreamingAssets, but BuildPath is not. These paths must both use the dynamic-lookup, or both not use it. \n"; - } - - if (!string.IsNullOrEmpty(message)) - { - message += "BuildPath: '" + buildPath + "'\n"; - message += "LoadPath: '" + loadPath + "'"; - } - - if (schema.Compression == BundledAssetGroupSchema.BundleCompressionMode.LZMA && (buildLocal || loadLocal)) - { - Debug.LogWarningFormat("Bundle compression is set to LZMA, but group {0} uses local content.", assetGroup.Name); - } - - return message; - } - - internal static string CalculateGroupHash(BundledAssetGroupSchema.BundleInternalIdMode mode, AddressableAssetGroup assetGroup, IEnumerable entries) - { - switch (mode) - { - case BundledAssetGroupSchema.BundleInternalIdMode.GroupGuid: - return assetGroup.Guid; - case BundledAssetGroupSchema.BundleInternalIdMode.GroupGuidProjectIdHash: - return HashingMethods.Calculate(assetGroup.Guid, Application.cloudProjectId).ToString(); - case BundledAssetGroupSchema.BundleInternalIdMode.GroupGuidProjectIdEntriesHash: - return HashingMethods.Calculate(assetGroup.Guid, Application.cloudProjectId, new HashSet(entries.Select(e => e.guid))).ToString(); - } - - throw new Exception("Invalid naming mode."); - } - - /// - /// Processes an AddressableAssetGroup and generates AssetBundle input definitions based on the BundlePackingMode. - /// - /// The AddressableAssetGroup to be processed. - /// The list of bundle definitions fed into the build pipeline AssetBundleBuild - /// The BundledAssetGroupSchema of used to process the assetGroup. - /// A filter to remove AddressableAssetEntries from being processed in the build. - /// The total list of AddressableAssetEntries that were processed. - public static List PrepGroupBundlePacking(AddressableAssetGroup assetGroup, List bundleInputDefs, BundledAssetGroupSchema schema, - Func entryFilter = null) - { - var combinedEntries = new List(); - var packingMode = schema.BundleMode; - var namingMode = schema.InternalBundleIdMode; - bool ignoreUnsupportedFilesInBuild = assetGroup.Settings.IgnoreUnsupportedFilesInBuild; - - switch (packingMode) - { - case BundledAssetGroupSchema.BundlePackingMode.PackTogether: - { - var allEntries = new List(); - foreach (AddressableAssetEntry a in assetGroup.entries) - { - if (entryFilter != null && !entryFilter(a)) - continue; - a.GatherAllAssets(allEntries, true, true, false, entryFilter); - } - - combinedEntries.AddRange(allEntries); - GenerateBuildInputDefinitions(allEntries, bundleInputDefs, CalculateGroupHash(namingMode, assetGroup, allEntries), "all", ignoreUnsupportedFilesInBuild); - } - break; - case BundledAssetGroupSchema.BundlePackingMode.PackSeparately: - { - foreach (AddressableAssetEntry a in assetGroup.entries) - { - if (entryFilter != null && !entryFilter(a)) - continue; - var allEntries = new List(); - a.GatherAllAssets(allEntries, true, true, false, entryFilter); - combinedEntries.AddRange(allEntries); - GenerateBuildInputDefinitions(allEntries, bundleInputDefs, CalculateGroupHash(namingMode, assetGroup, allEntries), a.address, ignoreUnsupportedFilesInBuild); - } - } - break; - case BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel: - { - var labelTable = new Dictionary>(); - foreach (AddressableAssetEntry a in assetGroup.entries) - { - if (entryFilter != null && !entryFilter(a)) - continue; - var sb = new StringBuilder(); - foreach (var l in a.labels) - sb.Append(l); - var key = sb.ToString(); - List entries; - if (!labelTable.TryGetValue(key, out entries)) - labelTable.Add(key, entries = new List()); - entries.Add(a); - } - - foreach (var entryGroup in labelTable) - { - var allEntries = new List(); - foreach (var a in entryGroup.Value) - { - if (entryFilter != null && !entryFilter(a)) - continue; - a.GatherAllAssets(allEntries, true, true, false, entryFilter); - } - - combinedEntries.AddRange(allEntries); - GenerateBuildInputDefinitions(allEntries, bundleInputDefs, CalculateGroupHash(namingMode, assetGroup, allEntries), entryGroup.Key, ignoreUnsupportedFilesInBuild); - } - } - break; - default: - throw new Exception("Unknown Packing Mode"); - } - - return combinedEntries; - } - - internal static void GenerateBuildInputDefinitions(List allEntries, List buildInputDefs, string groupGuid, string address, - bool ignoreUnsupportedFilesInBuild) - { - var scenes = new List(); - var assets = new List(); - foreach (var e in allEntries) - { - ThrowExceptionIfInvalidFiletypeOrAddress(e, ignoreUnsupportedFilesInBuild); - if (string.IsNullOrEmpty(e.AssetPath)) - continue; - if (e.IsScene) - scenes.Add(e); - else - assets.Add(e); - } - - if (assets.Count > 0) - buildInputDefs.Add(GenerateBuildInputDefinition(assets, groupGuid + "_assets_" + address + ".bundle")); - if (scenes.Count > 0) - buildInputDefs.Add(GenerateBuildInputDefinition(scenes, groupGuid + "_scenes_" + address + ".bundle")); - } - - private static void ThrowExceptionIfInvalidFiletypeOrAddress(AddressableAssetEntry entry, bool ignoreUnsupportedFilesInBuild) - { - if (entry.guid.Length > 0 && entry.address.Contains("[") && entry.address.Contains("]")) - throw new Exception($"Address '{entry.address}' cannot contain '[ ]'."); - if (entry.MainAssetType == typeof(DefaultAsset) && !AssetDatabase.IsValidFolder(entry.AssetPath)) - { - if (ignoreUnsupportedFilesInBuild) - Debug.LogWarning($"Cannot recognize file type for entry located at '{entry.AssetPath}'. Asset location will be ignored."); - else - throw new Exception($"Cannot recognize file type for entry located at '{entry.AssetPath}'. Asset import failed for using an unsupported file type."); - } - } - - internal static AssetBundleBuild GenerateBuildInputDefinition(List assets, string name) - { - var assetInternalIds = new HashSet(); - var assetsInputDef = new AssetBundleBuild(); - assetsInputDef.assetBundleName = name.ToLower().Replace(" ", "").Replace('\\', '/').Replace("//", "/"); - assetsInputDef.assetNames = assets.Select(s => s.AssetPath).ToArray(); - assetsInputDef.addressableNames = assets.Select(s => s.GetAssetLoadPath(true, assetInternalIds)).ToArray(); - return assetsInputDef; - } - - static string[] CreateRemoteCatalog(string jsonText, List locations, AddressableAssetSettings aaSettings, AddressablesDataBuilderInput builderInput, - ProviderLoadRequestOptions catalogLoadOptions) - { - string[] dependencyHashes = null; - - var contentHash = HashingMethods.Calculate(jsonText).ToString(); - - var versionedFileName = aaSettings.profileSettings.EvaluateString(aaSettings.activeProfileId, "/catalog_" + builderInput.PlayerVersion); - var remoteBuildFolder = aaSettings.RemoteCatalogBuildPath.GetValue(aaSettings); - var remoteLoadFolder = aaSettings.RemoteCatalogLoadPath.GetValue(aaSettings); - - if (string.IsNullOrEmpty(remoteBuildFolder) || - string.IsNullOrEmpty(remoteLoadFolder) || - remoteBuildFolder == AddressableAssetProfileSettings.undefinedEntryValue || - remoteLoadFolder == AddressableAssetProfileSettings.undefinedEntryValue) - { - Addressables.LogWarning( - "Remote Build and/or Load paths are not set on the main AddressableAssetSettings asset, but 'Build Remote Catalog' is true. Cannot create remote catalog. In the inspector for any group, double click the 'Addressable Asset Settings' object to begin inspecting it. '" + - remoteBuildFolder + "', '" + remoteLoadFolder + "'"); - } - else - { - var remoteJsonBuildPath = remoteBuildFolder + versionedFileName + ".json"; - var remoteHashBuildPath = remoteBuildFolder + versionedFileName + ".hash"; - - WriteFile(remoteJsonBuildPath, jsonText, builderInput.Registry); - WriteFile(remoteHashBuildPath, contentHash, builderInput.Registry); - - dependencyHashes = new string[((int)ContentCatalogProvider.DependencyHashIndex.Count)]; - dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Remote] = ResourceManagerRuntimeData.kCatalogAddress + "RemoteHash"; - dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Cache] = ResourceManagerRuntimeData.kCatalogAddress + "CacheHash"; - - var remoteHashLoadPath = remoteLoadFolder + versionedFileName + ".hash"; - var remoteHashLoadLocation = new ResourceLocationData( - new[] {dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Remote]}, - remoteHashLoadPath, - typeof(TextDataProvider), typeof(string)); - remoteHashLoadLocation.Data = catalogLoadOptions.Copy(); - locations.Add(remoteHashLoadLocation); - - var cacheLoadPath = "{UnityEngine.Application.persistentDataPath}/com.unity.addressables" + versionedFileName + ".hash"; - var cacheLoadLocation = new ResourceLocationData( - new[] {dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Cache]}, - cacheLoadPath, - typeof(TextDataProvider), typeof(string)); - cacheLoadLocation.Data = catalogLoadOptions.Copy(); - locations.Add(cacheLoadLocation); - } - - return dependencyHashes; - } - - // Tests can set this flag to prevent player script compilation. This is the most expensive part of small builds - // and isn't needed for most tests. - internal static bool s_SkipCompilePlayerScripts = false; - - static IList RuntimeDataBuildTasks(string buildInBundleName, string monoScriptBundleName) - { - var buildTasks = new List(); - - // Setup - buildTasks.Add(new SwitchToBuildPlatform()); - buildTasks.Add(new RebuildSpriteAtlasCache()); - - // Player Scripts - if (!s_SkipCompilePlayerScripts) - buildTasks.Add(new BuildPlayerScripts()); - buildTasks.Add(new PostScriptsCallback()); - - // Dependency - buildTasks.Add(new CalculateSceneDependencyData()); - buildTasks.Add(new CalculateAssetDependencyData()); - buildTasks.Add(new AddHashToBundleNameTask()); - buildTasks.Add(new StripUnusedSpriteSources()); - buildTasks.Add(new CreateBuiltInBundle(buildInBundleName)); - if (!string.IsNullOrEmpty(monoScriptBundleName)) - buildTasks.Add(new CreateMonoScriptBundle(monoScriptBundleName)); - buildTasks.Add(new PostDependencyCallback()); - - // Packing - buildTasks.Add(new GenerateBundlePacking()); - buildTasks.Add(new UpdateBundleObjectLayout()); - buildTasks.Add(new GenerateBundleCommands()); - buildTasks.Add(new GenerateSubAssetPathMaps()); - buildTasks.Add(new GenerateBundleMaps()); - buildTasks.Add(new PostPackingCallback()); - - // Writing - buildTasks.Add(new WriteSerializedFiles()); - buildTasks.Add(new ArchiveAndCompressBundles()); - buildTasks.Add(new GenerateLocationListsTask()); - buildTasks.Add(new PostWritingCallback()); - - return buildTasks; - } - - static void MoveFileToDestinationWithTimestampIfDifferent(string srcPath, string destPath, IBuildLogger log) - { - if (srcPath == destPath) - return; - - DateTime time = File.GetLastWriteTime(srcPath); - DateTime destTime = File.Exists(destPath) ? File.GetLastWriteTime(destPath) : new DateTime(); - - if (destTime == time) - return; - - using (log.ScopedStep(LogLevel.Verbose, "Move File", $"{srcPath} -> {destPath}")) - { - var directory = Path.GetDirectoryName(destPath); - if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) - Directory.CreateDirectory(directory); - else if (File.Exists(destPath)) - File.Delete(destPath); - File.Move(srcPath, destPath); - } - } - - void PostProcessBundles(AddressableAssetGroup assetGroup, List buildBundles, List outputBundles, IBundleBuildResults buildResult, ResourceManagerRuntimeData runtimeData, - List locations, FileRegistry registry, Dictionary primaryKeyToCatalogEntry, Dictionary bundleRenameMap, - List postCatalogUpdateCallbacks, AddressableAssetGroup sharedBundleGroup) - { - var schema = assetGroup.GetSchema(); - if (schema == null) - return; - - var path = schema.BuildPath.GetValue(assetGroup.Settings); - if (string.IsNullOrEmpty(path)) - return; - - for (int i = 0; i < buildBundles.Count; ++i) - { - if (primaryKeyToCatalogEntry.TryGetValue(buildBundles[i], out ContentCatalogDataEntry dataEntry)) - { - var info = buildResult.BundleInfos[buildBundles[i]]; - var requestOptions = new AssetBundleRequestOptions - { - Crc = schema.UseAssetBundleCrc ? info.Crc : 0, - UseCrcForCachedBundle = schema.UseAssetBundleCrcForCachedBundles, - UseUnityWebRequestForLocalBundles = schema.UseUnityWebRequestForLocalBundles, - Hash = schema.UseAssetBundleCache ? info.Hash.ToString() : "", - ChunkedTransfer = schema.ChunkedTransfer, - RedirectLimit = schema.RedirectLimit, - RetryCount = schema.RetryCount, - Timeout = schema.Timeout, - BundleName = Path.GetFileNameWithoutExtension(info.FileName), - AssetLoadMode = schema.AssetLoadMode, - BundleSize = GetFileSize(info.FileName), - ClearOtherCachedVersionsWhenLoaded = schema.AssetBundledCacheClearBehavior == BundledAssetGroupSchema.CacheClearBehavior.ClearWhenWhenNewVersionLoaded - }; - dataEntry.Data = requestOptions; - - if (assetGroup == sharedBundleGroup && info.Dependencies.Length == 0 && !string.IsNullOrEmpty(info.FileName) && - (info.FileName.EndsWith($"{BuiltInBundleBaseName}.bundle", StringComparison.Ordinal) || info.FileName.EndsWith("_monoscripts.bundle", StringComparison.Ordinal))) - { - outputBundles[i] = ConstructAssetBundleName(null, schema, info, outputBundles[i]); - } - else - { - int extensionLength = Path.GetExtension(outputBundles[i]).Length; - string[] deconstructedBundleName = outputBundles[i].Substring(0, outputBundles[i].Length - extensionLength).Split('_'); - string reconstructedBundleName = string.Join("_", deconstructedBundleName, 1, deconstructedBundleName.Length - 1) + ".bundle"; - outputBundles[i] = ConstructAssetBundleName(assetGroup, schema, info, reconstructedBundleName); - } - - dataEntry.InternalId = dataEntry.InternalId.Remove(dataEntry.InternalId.Length - buildBundles[i].Length) + outputBundles[i]; - dataEntry.Keys[0] = outputBundles[i]; - ReplaceDependencyKeys(buildBundles[i], outputBundles[i], locations); - - if (!m_BundleToInternalId.ContainsKey(buildBundles[i])) - m_BundleToInternalId.Add(buildBundles[i], dataEntry.InternalId); - - if (dataEntry.InternalId.StartsWith("http:\\", StringComparison.Ordinal)) - dataEntry.InternalId = dataEntry.InternalId.Replace("http:\\", "http://").Replace("\\", "/"); - if (dataEntry.InternalId.StartsWith("https:\\", StringComparison.Ordinal)) - dataEntry.InternalId = dataEntry.InternalId.Replace("https:\\", "https://").Replace("\\", "/"); - } - else - { - Debug.LogWarningFormat("Unable to find ContentCatalogDataEntry for bundle {0}.", outputBundles[i]); - } - - var targetPath = Path.Combine(path, outputBundles[i]); - var srcPath = Path.Combine(assetGroup.Settings.buildSettings.bundleBuildPath, buildBundles[i]); - - if (assetGroup.GetSchema()?.BundleNaming == BundledAssetGroupSchema.BundleNamingStyle.NoHash) - outputBundles[i] = StripHashFromBundleLocation(outputBundles[i]); - - bundleRenameMap.Add(buildBundles[i], outputBundles[i]); - MoveFileToDestinationWithTimestampIfDifferent(srcPath, targetPath, Log); - AddPostCatalogUpdatesInternal(assetGroup, postCatalogUpdateCallbacks, dataEntry, targetPath, registry); - - registry.AddFile(targetPath); - } - } - - internal void AddPostCatalogUpdatesInternal(AddressableAssetGroup assetGroup, List postCatalogUpdates, ContentCatalogDataEntry dataEntry, string targetBundlePath, FileRegistry registry) - { - if (assetGroup.GetSchema()?.BundleNaming == - BundledAssetGroupSchema.BundleNamingStyle.NoHash) - { - postCatalogUpdates.Add(() => - { - //This is where we strip out the temporary hash for the final bundle location and filename - string bundlePathWithoutHash = StripHashFromBundleLocation(targetBundlePath); - if (File.Exists(targetBundlePath)) - { - if (File.Exists(bundlePathWithoutHash)) - File.Delete(bundlePathWithoutHash); - string destFolder = Path.GetDirectoryName(bundlePathWithoutHash); - if (!string.IsNullOrEmpty(destFolder) && !Directory.Exists(destFolder)) - Directory.CreateDirectory(destFolder); - - File.Move(targetBundlePath, bundlePathWithoutHash); - } - - if (registry != null) - { - if (!registry.ReplaceBundleEntry(targetBundlePath, bundlePathWithoutHash)) - Debug.LogErrorFormat("Unable to find registered file for bundle {0}.", targetBundlePath); - } - - if (dataEntry != null) - if (DataEntryDiffersFromBundleFilename(dataEntry, bundlePathWithoutHash)) - dataEntry.InternalId = StripHashFromBundleLocation(dataEntry.InternalId); - }); - } - } - - // if false, there is no need to remove the hash from dataEntry.InternalId - bool DataEntryDiffersFromBundleFilename(ContentCatalogDataEntry dataEntry, string bundlePathWithoutHash) - { - string dataEntryId = dataEntry.InternalId; - string dataEntryFilename = Path.GetFileName(dataEntryId); - string bundleFileName = Path.GetFileName(bundlePathWithoutHash); - - return dataEntryFilename != bundleFileName; - } - - /// - /// Creates a name for an asset bundle using the provided information. - /// - /// The asset group. - /// The schema of the group. - /// The bundle information. - /// The base name of the asset bundle. - /// Returns the asset bundle name with the provided information. - protected virtual string ConstructAssetBundleName(AddressableAssetGroup assetGroup, BundledAssetGroupSchema schema, BundleDetails info, string assetBundleName) - { - if (assetGroup != null) - { - string groupName = assetGroup.Name.Replace(" ", "").Replace('\\', '/').Replace("//", "/").ToLower(); - assetBundleName = groupName + "_" + assetBundleName; - } - - string bundleNameWithHashing = BuildUtility.GetNameWithHashNaming(schema.BundleNaming, info.Hash.ToString(), assetBundleName); - //For no hash, we need the hash temporarily for content update purposes. This will be stripped later on. - if (schema.BundleNaming == BundledAssetGroupSchema.BundleNamingStyle.NoHash) - { - bundleNameWithHashing = bundleNameWithHashing.Replace(".bundle", "_" + info.Hash.ToString() + ".bundle"); - } - - return bundleNameWithHashing; - } - - static void ReplaceDependencyKeys(string from, string to, List locations) - { - foreach (ContentCatalogDataEntry location in locations) - { - for (int i = 0; i < location.Dependencies.Count; ++i) - { - string s = location.Dependencies[i] as string; - if (string.IsNullOrEmpty(s)) - continue; - if (s == from) - location.Dependencies[i] = to; - } - } - } - - private static long GetFileSize(string fileName) - { - try - { - return new FileInfo(fileName).Length; - } - catch (Exception e) - { - Debug.LogException(e); - return 0; - } - } - - /// - public override void ClearCachedData() - { - if (Directory.Exists(Addressables.BuildPath)) - { - try - { - var catalogPath = Addressables.BuildPath + "/catalog.json"; - var settingsPath = Addressables.BuildPath + "/settings.json"; - DeleteFile(catalogPath); - DeleteFile(settingsPath); - Directory.Delete(Addressables.BuildPath, true); - } - catch (Exception e) - { - Debug.LogException(e); - } - } - } - - /// - public override bool IsDataBuilt() - { - var settingsPath = Addressables.BuildPath + "/settings.json"; - return !String.IsNullOrEmpty(m_CatalogBuildPath) && - File.Exists(m_CatalogBuildPath) && - File.Exists(settingsPath); - } } #endif diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs.meta b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs.meta index dccff1d8..c004edf8 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs.meta +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f66315e0705fa82438547ab07bbe65e8 +guid: 938da90b96dab184e82e478c9e8642a8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset index 392a6851..5bcea0fc 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset @@ -9,7 +9,7 @@ MonoBehaviour: m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 5796c4ece8a8f054dbd288aa4e18acff, type: 3} + m_Script: {fileID: 11500000, guid: 93efeac63400d5341adb09764b189dcd, type: 3} m_Name: CustomPlayMode m_EditorClassIdentifier: instanceProviderType: diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset.meta b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset.meta index a9ef2ee9..5c1c0e5a 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset.meta +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayMode.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ef55377076f186e4fb78d6f117bb52ac +guid: 6dd0323ec17b2644f9b3ed81416c2690 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs index 73fd9b47..08c1741a 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs @@ -1,92 +1,17 @@ #if UNITY_EDITOR -using System; -using System.IO; -using UnityEditor; -using UnityEditor.AddressableAssets.Build; using UnityEditor.AddressableAssets.Build.DataBuilders; using UnityEngine; -using UnityEngine.AddressableAssets; -using UnityEngine.AddressableAssets.Initialization; /// -/// Uses data built by BuildScriptPacked class. This script just sets up the correct variables and runs. +/// Uses data built by CustomBuildScript class. This script just sets up the correct variables and runs. /// [CreateAssetMenu(fileName = "CustomPlayMode.asset", menuName = "Addressables/Content Builders/Use CustomPlayMode Script")] -public class CustomPlayModeScript : BuildScriptBase +public class CustomPlayModeScript : BuildScriptPackedPlayMode { /// public override string Name { get { return "Use Custom Build (requires built groups)"; } } - - private bool m_DataBuilt; - - /// - public override void ClearCachedData() - { - m_DataBuilt = false; - } - - /// - public override bool IsDataBuilt() - { - return m_DataBuilt; - } - - /// - public override bool CanBuildData() - { - return typeof(T).IsAssignableFrom(typeof(AddressablesPlayModeBuildResult)); - } - - /// - protected override TResult BuildDataImplementation(AddressablesDataBuilderInput builderInput) - { - var timer = new System.Diagnostics.Stopwatch(); - timer.Start(); - var settingsPath = Addressables.BuildPath + "/settings.json"; - var buildLogsPath = Addressables.BuildPath + "/buildLogs.json"; - if (!File.Exists(settingsPath)) - { - IDataBuilderResult resE = new AddressablesPlayModeBuildResult() - {Error = "Player content must be built before entering play mode with packed data. This can be done from the Addressables window in the Build->Build Player Content menu command."}; - return (TResult)resE; - } - - var rtd = JsonUtility.FromJson(File.ReadAllText(settingsPath)); - if (rtd == null) - { - IDataBuilderResult resE = new AddressablesPlayModeBuildResult() - { - Error = string.Format("Unable to load initialization data from path {0}. This can be done from the Addressables window in the Build->Build Player Content menu command.", settingsPath) - }; - return (TResult)resE; - } - - PackedPlayModeBuildLogs buildLogs = new PackedPlayModeBuildLogs(); - BuildTarget dataBuildTarget = BuildTarget.NoTarget; - if (!Enum.TryParse(rtd.BuildTarget, out dataBuildTarget)) - { - buildLogs.RuntimeBuildLogs.Add(new PackedPlayModeBuildLogs.RuntimeBuildLog(LogType.Warning, - $"Unable to parse build target from initialization data: '{rtd.BuildTarget}'.")); - } - - else if (BuildPipeline.GetBuildTargetGroup(dataBuildTarget) != BuildTargetGroup.Standalone) - { - buildLogs.RuntimeBuildLogs.Add(new PackedPlayModeBuildLogs.RuntimeBuildLog(LogType.Warning, - $"Asset bundles built with build target {dataBuildTarget} may not be compatible with running in the Editor.")); - } - - if (buildLogs.RuntimeBuildLogs.Count > 0) - File.WriteAllText(buildLogsPath, JsonUtility.ToJson(buildLogs)); - - var runtimeSettingsPath = "{UnityEngine.AddressableAssets.Addressables.RuntimePath}/settings.json"; - PlayerPrefs.SetString(Addressables.kAddressablesRuntimeDataPath, runtimeSettingsPath); - PlayerPrefs.SetString(Addressables.kAddressablesRuntimeBuildLogPath, buildLogsPath); - IDataBuilderResult res = new AddressablesPlayModeBuildResult() {OutputPath = settingsPath, Duration = timer.Elapsed.TotalSeconds}; - m_DataBuilt = true; - return (TResult)res; - } } #endif diff --git a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs.meta b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs.meta index 1877a204..09d0c9f7 100644 --- a/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs.meta +++ b/Samples~/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5796c4ece8a8f054dbd288aa4e18acff +guid: 93efeac63400d5341adb09764b189dcd MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Samples~/PrefabSpawner/PrefabSpawnerSample.cs b/Samples~/PrefabSpawner/PrefabSpawnerSample.cs index 2985802a..644dabcf 100644 --- a/Samples~/PrefabSpawner/PrefabSpawnerSample.cs +++ b/Samples~/PrefabSpawner/PrefabSpawnerSample.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; @@ -60,6 +60,6 @@ IEnumerator SpawnTemporaryCube() //Release the AsyncOperationHandles which destroys the GameObject foreach (var handle in handles) - Addressables.Release(handle); + handle.Release(); } } diff --git a/Samples~/Tests/SamplesTests.cs b/Samples~/Tests/SamplesTests.cs index 19e7ea68..b1092228 100644 --- a/Samples~/Tests/SamplesTests.cs +++ b/Samples~/Tests/SamplesTests.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; using NUnit.Framework; using UnityEngine; @@ -57,7 +57,7 @@ public IEnumerator Samples_GetAddressFromAssetReference_ReturnsCorrectAddress() Assert.AreEqual(behavior.AssetReferenceAddress, returnedAddress); - m_Addressables.Release(assetReferenceHandle); + assetReferenceHandle.Release(); UnityEngine.AddressableAssets.Addressables.m_AddressablesInstance = savedImpl; } } diff --git a/Tests/Editor/AddressableAssetEntryTests.cs b/Tests/Editor/AddressableAssetEntryTests.cs index baf623bd..e2daafcc 100644 --- a/Tests/Editor/AddressableAssetEntryTests.cs +++ b/Tests/Editor/AddressableAssetEntryTests.cs @@ -387,6 +387,93 @@ public void WhenGettingFolderSubEntry_OnlyReturnsIfValidSubEntry() } } + [Test] + public void GatherFolderEntries_RecurseAll_WhenSubEntry_IsMarkedAddrAndHasLabelAdded_FolderEntryHasNoLabelAdded() + { + AddressableAssetEntry mainFolderEntry = null; + AddressableAssetEntry prefabEntry = null; + string mainFolderPath = GetAssetPath("TestFolder"); + var testGroup2 = Settings.CreateGroup("testGroup2", false, false, false, null, typeof(BundledAssetGroupSchema)); + + try + { + //Setup + string prefabPath = Path.Combine(mainFolderPath, "mainFolder.prefab").Replace('\\', '/'); + Directory.CreateDirectory(mainFolderPath); + PrefabUtility.SaveAsPrefabAsset(new GameObject("mainFolderAsset"), prefabPath); + + string mainFolderGuid = AssetDatabase.AssetPathToGUID(mainFolderPath); + string prefabGuid = AssetDatabase.AssetPathToGUID(prefabPath); + mainFolderEntry = Settings.CreateOrMoveEntry(mainFolderGuid, m_testGroup, false); + + //Test + List entries = new List(); + mainFolderEntry.GatherFolderEntries(entries, true, false, null); + Assert.AreEqual(1, entries.Count); + prefabEntry = entries[0]; + Assert.AreEqual(prefabEntry.AssetPath, prefabPath); + + prefabEntry.SetLabel("test", true, true); + Settings.MoveEntry(prefabEntry, testGroup2); + Assert.AreEqual(1, prefabEntry.labels.Count); + Assert.AreEqual(0, mainFolderEntry.labels.Count); + } + finally + { + //Cleanup + Settings.RemoveAssetEntry(mainFolderEntry, false); + Settings.RemoveAssetEntry(prefabEntry, false); + Directory.Delete(mainFolderPath, true); + + Settings.RemoveGroup(testGroup2); + } + } + + + [Test] + public void GatherFolderEntries_NoRecurseAll_WhenSubFolderEntry_IsMarkedAddrAndHasLabelAdded_FolderEntryHasNoLabelAdded() + { + AddressableAssetEntry mainFolderEntry = null; + AddressableAssetEntry subFolderEntry = null; + string mainFolderPath = GetAssetPath("TestFolder"); + var testGroup2 = Settings.CreateGroup("testGroup2", false, false, false, null, typeof(BundledAssetGroupSchema)); + + try + { + //Setup + string subFolderPath = Path.Combine(mainFolderPath, "SubFolder").Replace('\\', '/'); + string prefabPath = Path.Combine(subFolderPath, "subFolder.prefab").Replace('\\', '/'); + Directory.CreateDirectory(mainFolderPath); + Directory.CreateDirectory(subFolderPath); + PrefabUtility.SaveAsPrefabAsset(new GameObject("subFolderAsset"), prefabPath); + + string mainFolderGuid = AssetDatabase.AssetPathToGUID(mainFolderPath); + string prefabGuid = AssetDatabase.AssetPathToGUID(prefabPath); + mainFolderEntry = Settings.CreateOrMoveEntry(mainFolderGuid, m_testGroup, false); + + //Test + List entries = new List(); + mainFolderEntry.GatherFolderEntries(entries, false, false, null); + Assert.AreEqual(1, entries.Count); + subFolderEntry = entries[0]; + Assert.AreEqual(subFolderEntry.AssetPath, subFolderPath); + + subFolderEntry.SetLabel("test", true, true); + Settings.MoveEntry(subFolderEntry, testGroup2); + Assert.AreEqual(1, subFolderEntry.labels.Count); + Assert.AreEqual(0, mainFolderEntry.labels.Count); + } + finally + { + //Cleanup + Settings.RemoveAssetEntry(mainFolderEntry, false); + Settings.RemoveAssetEntry(subFolderEntry, false); + Directory.Delete(mainFolderPath, true); + + Settings.RemoveGroup(testGroup2); + } + } + [TestCase(true)] [TestCase(false)] public void WhenGatherFolderEntries_ReturnsCorrectAssetObjects(bool includeSubObjects) @@ -488,6 +575,43 @@ public void GetFolderSubEntry_SetsLabelsCorrectlyOnLongPath() } } + [Test] + public void GetFolderSubEntry_WhenFolderSubEntry_IsMarkedAddrAndHasLabelAdded_FolderEntryHasNoLabelAdded() + { + AddressableAssetEntry mainFolderEntry = null; + AddressableAssetEntry prefabEntry = null; + string testAssetFolder = GetAssetPath("TestFolder"); + var testGroup2 = Settings.CreateGroup("testGroup2", false, false, false, null, typeof(BundledAssetGroupSchema)); + + try + { + //Setup + string mainPrefabPath = Path.Combine(testAssetFolder, "mainFolder.prefab").Replace('\\', '/'); + Directory.CreateDirectory(testAssetFolder); + PrefabUtility.SaveAsPrefabAsset(new GameObject("mainFolderAsset"), mainPrefabPath); + + string mainFolderGuid = AssetDatabase.AssetPathToGUID(testAssetFolder); + string mainPrefabGuid = AssetDatabase.AssetPathToGUID(mainPrefabPath); + mainFolderEntry = Settings.CreateOrMoveEntry(mainFolderGuid, m_testGroup, false); + + //Test + prefabEntry = mainFolderEntry.GetFolderSubEntry(mainPrefabGuid, mainPrefabPath); + prefabEntry.SetLabel("test", true, true); + Settings.MoveEntry(prefabEntry, testGroup2); + Assert.AreEqual(1, prefabEntry.labels.Count); + Assert.AreEqual(0, mainFolderEntry.labels.Count); + } + finally + { + //Cleanup + Settings.RemoveAssetEntry(mainFolderEntry, false); + Settings.RemoveAssetEntry(prefabEntry, false); + Directory.Delete(testAssetFolder, true); + + Settings.RemoveGroup(testGroup2); + } + } + [Test] public void WhenClassReferencedByAddressableAssetEntryIsReloaded_CachedMainAssetTypeIsReset() { diff --git a/Tests/Editor/AddressableAssetFolderSubfolderTests.cs b/Tests/Editor/AddressableAssetFolderSubfolderTests.cs index b0caafb9..b4ee1d40 100644 --- a/Tests/Editor/AddressableAssetFolderSubfolderTests.cs +++ b/Tests/Editor/AddressableAssetFolderSubfolderTests.cs @@ -102,7 +102,7 @@ List GetValidAssetPaths(string path, AddressableAssetSettings settings) public void Build_WithAddrParentFolderAndAddrSubfolders_InSeparateGroups_Succeeds() { var context = new AddressablesDataBuilderInput(Settings); - foreach (IDataBuilder db in Settings.DataBuilders) + foreach (IDataBuilder db in Settings.DataBuilders.Cast()) { if (db.CanBuildData()) db.BuildData(context); diff --git a/Tests/Editor/BinaryStorageBufferTests.cs b/Tests/Editor/BinaryStorageBufferTests.cs index 8a521e68..44892588 100644 --- a/Tests/Editor/BinaryStorageBufferTests.cs +++ b/Tests/Editor/BinaryStorageBufferTests.cs @@ -456,6 +456,7 @@ public void TestComplexObjectDeduplication([Values(1024, 1024 * 1024)]int chunkS Assert.AreEqual(new ComplexObject(i), re.ReadObject(ids[i])); } +#if !ENABLE_JSON_CATALOG //https://jira.unity3d.com/browse/ADDR-3459 [Test] [TestCase(short.MinValue, 0)] @@ -503,7 +504,7 @@ public void ContentCatalogData_SerializesRedirectLimit_Correctly(int redirectLim Assert.AreEqual(expectedRedirectLimit, result.RedirectLimit); } - +#endif [Test] public void TestComplexObjectArray([Values(1024, 1024 * 1024)]int chunkSize, [Values(1, 32, 256, 1024)]int count, [Values(0, 10, 1024)]int cacheSize) { diff --git a/Tests/Editor/Build/BuildScriptPackedTests.cs b/Tests/Editor/Build/BuildScriptPackedTests.cs index 2fab3aa6..f9a5008c 100644 --- a/Tests/Editor/Build/BuildScriptPackedTests.cs +++ b/Tests/Editor/Build/BuildScriptPackedTests.cs @@ -490,10 +490,13 @@ public void AddPostCatalogUpdatesInternal_DoesNotAttemptToRemoveHashUnnecessaril [Test] public void ErrorCheckBundleSettings_FindsNoProblemsInDefaultScema() { + var aaContext = new AddressableAssetsBuildContext(); + aaContext.Settings = Settings; + var group = Settings.CreateGroup("PackedTest", false, false, false, null, typeof(BundledAssetGroupSchema)); var schema = group.GetSchema(); - var errorStr = BuildScriptPackedMode.ErrorCheckBundleSettings(schema, group, Settings); + var errorStr = BuildScriptBase.ErrorCheckBundleSettings(group, aaContext); LogAssert.NoUnexpectedReceived(); Assert.IsTrue(string.IsNullOrEmpty(errorStr)); } @@ -501,11 +504,14 @@ public void ErrorCheckBundleSettings_FindsNoProblemsInDefaultScema() [Test] public void ErrorCheckBundleSettings_WarnsOfMismatchedBuildPath() { + var aaContext = new AddressableAssetsBuildContext(); + aaContext.Settings = Settings; + var group = Settings.CreateGroup("PackedTest", false, false, false, null, typeof(BundledAssetGroupSchema)); var schema = group.GetSchema(); schema.BuildPath.Id = "BadPath"; - var errorStr = BuildScriptPackedMode.ErrorCheckBundleSettings(schema, group, Settings); + var errorStr = BuildScriptBase.ErrorCheckBundleSettings(group, aaContext); LogAssert.NoUnexpectedReceived(); Assert.IsTrue(errorStr.Contains("is set to the dynamic-lookup version of StreamingAssets, but BuildPath is not.")); } @@ -513,11 +519,14 @@ public void ErrorCheckBundleSettings_WarnsOfMismatchedBuildPath() [Test] public void ErrorCheckBundleSettings_WarnsOfMismatchedLoadPath() { + var aaContext = new AddressableAssetsBuildContext(); + aaContext.Settings = Settings; + var group = Settings.CreateGroup("PackedTest", false, false, false, null, typeof(BundledAssetGroupSchema)); var schema = group.GetSchema(); schema.LoadPath.Id = "BadPath"; - var errorStr = BuildScriptPackedMode.ErrorCheckBundleSettings(schema, group, Settings); + var errorStr = BuildScriptBase.ErrorCheckBundleSettings(group, aaContext); LogAssert.NoUnexpectedReceived(); Assert.IsTrue(errorStr.Contains("is set to the dynamic-lookup version of StreamingAssets, but LoadPath is not.")); } @@ -525,11 +534,14 @@ public void ErrorCheckBundleSettings_WarnsOfMismatchedLoadPath() [Test] public void WhenUsingLocalContentAndCompressionIsLZMA_ErrorCheckBundleSettings_LogsWarning() { + var aaContext = new AddressableAssetsBuildContext(); + aaContext.Settings = Settings; + var group = Settings.CreateGroup("PackedTest", false, false, false, null, typeof(BundledAssetGroupSchema)); var schema = group.GetSchema(); schema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZMA; - BuildScriptPackedMode.ErrorCheckBundleSettings(schema, group, Settings); + var errorStr = BuildScriptBase.ErrorCheckBundleSettings(group, aaContext); LogAssert.Expect(LogType.Warning, $"Bundle compression is set to LZMA, but group {group.Name} uses local content."); } diff --git a/Tests/Editor/Build/VerifyPublicBuildScripts.cs b/Tests/Editor/Build/VerifyPublicBuildScripts.cs new file mode 100644 index 00000000..a6615945 --- /dev/null +++ b/Tests/Editor/Build/VerifyPublicBuildScripts.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections; +using System.IO; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using UnityEngine.TestTools; +using File = System.IO.File; +using Path = System.IO.Path; + +/** + * This test exists because we frequently suggest that our users extend or copy and paste the builder script + * files to customize or make their own. We didn't check this on every release and so internal API usage + * had crept in and made it impossible to do this without copying the entire package. This test verifies + * that you can copy the script into your own namespace and it will compile. + */ +public class VerifyPublicBuildScripts +{ + private string m_PackagePath; + private string m_SamplePath; + private string m_FolderPath = $"Assets{Path.DirectorySeparatorChar}ScriptFolder"; + + + [SetUp] + public void SetUp() + { + if(AssetDatabase.IsValidFolder(m_FolderPath)) + { + AssetDatabase.DeleteAsset(m_FolderPath); + } + AssetDatabase.CreateFolder("Assets", "ScriptFolder"); + m_PackagePath = "Packages/com.unity.addressables"; + m_SamplePath = "Samples"; + if (Directory.Exists(String.Join($"{Path.DirectorySeparatorChar}", new [] {m_PackagePath, "Samples~"}))) + { + // when packaging the samples are moved into a hidden directory + m_SamplePath = "Samples~"; + } + + // this is a dependant class and copying is easier than an asmdef + var loadScenePath = "Samples/CustomBuildAndPlaymodeScripts/LoadSceneForCustomBuild.cs"; + var fullPath = String.Join($"{Path.DirectorySeparatorChar}", new[] { m_PackagePath, loadScenePath }); + fullPath = fullPath.Replace("Samples", m_SamplePath); + var testFilePath = String.Join($"{Path.DirectorySeparatorChar}", new[] { m_FolderPath, Path.GetFileName(loadScenePath) }); + File.Copy(fullPath, testFilePath); + } + [TearDown] + public void TearDown() + { + if(AssetDatabase.IsValidFolder(m_FolderPath)) + { + AssetDatabase.DeleteAsset(m_FolderPath); + } + } + + private static string[] BuildScripts = + { + "Editor/Build/DataBuilders/BuildScriptFastMode.cs", + "Editor/Build/DataBuilders/BuildScriptPackedMode.cs", + "Editor/Build/DataBuilders/BuildScriptPackedPlayMode.cs", + "Samples/CustomBuildAndPlaymodeScripts/Editor/CustomBuildScript.cs", + "Samples/CustomBuildAndPlaymodeScripts/Editor/CustomPlayModeScript.cs", + }; + + [UnityTest] + public IEnumerator Verify_BuildScript_HasNoInternalApis([ValueSource(nameof(BuildScripts))] string buildScriptPath) + { + var fullPath = String.Join($"{Path.DirectorySeparatorChar}", new[] { m_PackagePath, buildScriptPath }); + fullPath = fullPath.Replace("Samples", m_SamplePath); + var content = File.ReadAllText(fullPath); + content = content.Replace("namespace UnityEditor.AddressableAssets.Build.DataBuilders", "namespace TestBuildScriptNamespace"); + // this is the using statement for the package the scripts are being copied from + content = "using UnityEditor; // added by unit test\n" + content; + content = "using UnityEditor.AddressableAssets.Build; // added by unit test\n" + content; + content = "using UnityEditor.AddressableAssets.Build.DataBuilders; // added by unit test\n" + content; + content = "using UnityEditor.AddressableAssets; // added by unit test\n" + content; + // content = "compile error;" + content; + + var testFilePath = String.Join($"{Path.DirectorySeparatorChar}", new[] { m_FolderPath, Path.GetFileName(buildScriptPath) }); + Debug.Log(testFilePath); + + File.WriteAllText(testFilePath, content); + AssetDatabase.Refresh(); + yield return new WaitForDomainReload(); + + // assert we didn't get any log messages when compiling the test file + LogAssert.NoUnexpectedReceived(); + } +} diff --git a/Tests/Editor/Build/VerifyPublicBuildScripts.cs.meta b/Tests/Editor/Build/VerifyPublicBuildScripts.cs.meta new file mode 100644 index 00000000..3dd21522 --- /dev/null +++ b/Tests/Editor/Build/VerifyPublicBuildScripts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a56b68a5f782f544dbbc9eebe8692f4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Diagnostics/Profiler.meta b/Tests/Editor/Diagnostics/Profiler.meta index 2a97e5ba..5438b333 100644 --- a/Tests/Editor/Diagnostics/Profiler.meta +++ b/Tests/Editor/Diagnostics/Profiler.meta @@ -1,3 +1,8 @@ fileFormatVersion: 2 guid: 7545c2cc1adeab340b31caad93786da5 -timeCreated: 1698865046 \ No newline at end of file +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs b/Tests/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs index 2d29935c..db6cb83c 100644 --- a/Tests/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs +++ b/Tests/Editor/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs @@ -1,9 +1,24 @@ +using System; +using System.Collections; using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Threading; using NUnit.Framework; +using Unity.Collections; using UnityEditor.AddressableAssets.Build.Layout; using UnityEditor.AddressableAssets.Diagnostics; +using UnityEditor.AddressableAssets.Settings; +using UnityEditor.VersionControl; using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.Playables; +using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.Profiling; +using UnityEngine.ResourceManagement.ResourceProviders; +using UnityEngine.ResourceManagement.Util; +using UnityEngine.TestTools; +using UnityEngine.UIElements; +using TreeView = UnityEditor.IMGUI.Controls.TreeView; namespace UnityEditor.AddressableAssets.Tests.Diagnostics.Profiler { @@ -11,12 +26,19 @@ public class AddressablesProfilerDetailsViewTests { private static BuildLayout.ExplicitAsset m_TestAsset; AddressablesProfilerDetailsView m_AddressablesProfilerDetailsView; + private TestProfiler testProfiler; [SetUp] public void SetUp() { + // this is an implementation of the profiler interfaces to allow us to create profile events + // and then retrieve them for testing + testProfiler = new TestProfiler(); + ProfilerRuntime.m_profilerEmitter = testProfiler; + ProfilerRuntime.Initialise(); + AddressablesProfilerDetailsView.m_frameDataStore = testProfiler; + AddressablesProfilerViewController.LayoutsManager.ClearReports(); m_AddressablesProfilerDetailsView = new AddressablesProfilerDetailsView(null); - // this is broken. Can we use Moq? What do we want to achieve here? m_TestAsset = new BuildLayout.ExplicitAsset { @@ -32,6 +54,109 @@ public void TearDown() m_AddressablesProfilerDetailsView.Dispose(); } + [Test] + public void Test_GenerateContentData_EmptyFrameData() + { + AddressablesProfilerDetailsView.FrameData frameData = new AddressablesProfilerDetailsView.FrameData(1); + List groupData = m_AddressablesProfilerDetailsView.GenerateContentDataForFrame(frameData); + Assert.AreEqual(0, groupData.Count); + } + + [Test] + public void Test_GenerateContentData_ValidData() + { + Test_GenerateContentData_FrameData(true, true, true, true); + } + + [Test] + public void Test_GenerateContentData_MissingReferencedBundle() + { + LogAssert.Expect("Asset Assets/AddressMe/brown.bmp referenced bundle folder_assets_assets/addressme_3876ae7ae6d93912555c4c93ef6e3fd7.bundle not loaded from build layout, attaching to parent scene_scenes_all_ea5d4d944831b5303deb66b664bd2e25.bundle"); + Test_GenerateContentData_FrameData(true, true, true, false); + } + + private void Test_GenerateContentData_FrameData(bool loadTextureBundle, bool loadSceneBundle, bool loadLocalAssets, bool loadFolderBundle) + { + AddressablesProfilerViewController.LayoutsManager.LoadManualReport(AddressablesTestUtility.GetPackagePath() + "/Tests/Editor/Fixtures/buildlayout1.json"); + AddressablesProfilerViewController.LayoutsManager.AddActiveLayout("3e22efc5f159401a15e7d27179b7cc4c"); // this is the value of BuildResultHash in buildlayout1.json + // this creates the build report + // var builder = new BuildLayoutBuilder().AddGroup("Remote Content").AddBundle("a.bundle").AddAsset("test.asset", "https://example.com/a.bundle").AddToLayoutManager(); + + // texture bundle + if (loadTextureBundle) + { + var bundleEvents = new ProfilerEventBuilder(testProfiler).SetBundleName("c75e210455b9b7f32a8e902db35e6ee3") + .SendBundleEvent(1, ContentStatus.Loading) + .SendBundleEvent(2, ContentStatus.Active) + .SendBundleEvent(3, ContentStatus.Released); + + } + + // scene bundle + if (loadSceneBundle) + { + var bundleEvents2 = new ProfilerEventBuilder(testProfiler).SetBundleName("79f2518976c47599628aad579c012657") + .SendBundleEvent(1, ContentStatus.Loading) + .SendBundleEvent(2, ContentStatus.Active) + .SendBundleEvent(3, ContentStatus.Released); + } + + // default local assets + if (loadLocalAssets) + { + var bundleEvents3 = new ProfilerEventBuilder(testProfiler).SetBundleName("0113602827489280bf434abd9b49426c") + .SendBundleEvent(1, ContentStatus.Loading) + .SendBundleEvent(2, ContentStatus.Active) + .SendBundleEvent(3, ContentStatus.Released); + } + + // folder bundle + if (loadFolderBundle) + { + + var bundleEvents4 = new ProfilerEventBuilder(testProfiler).SetBundleName("6801756afe332320683f01526bb77e3d") + .SendBundleEvent(1, ContentStatus.Loading) + .SendBundleEvent(2, ContentStatus.Active) + .SendBundleEvent(3, ContentStatus.Released); + } + + var sceneEvents = new ProfilerEventBuilder(testProfiler) + .SetAssetLocation("Assets/Scenes/SampleScene.unity", "79f2518976c47599628aad579c012657") // FIXME, should this take a builder so it can look this up for us? + .SendSceneEvent(1, ContentStatus.Loading) + .SendSceneEvent(2, ContentStatus.Active) + .SendSceneEvent(3, ContentStatus.Released); + var assetEvents = new ProfilerEventBuilder(testProfiler) + .SetAssetLocation("Assets/khaki.png", "a1dd26be86668e2320052da88bcb6d39") // FIXME, should this take a builder so it can look this up for us? + .SendAssetEvent(1, ContentStatus.Loading) + .SendAssetEvent(2, ContentStatus.Active); // assets do not have an explicit released event + // so this is an internal prefab, I need one with external references + var embeddedPrefabEvents = new ProfilerEventBuilder(testProfiler) + .SetAssetLocation("Assets/Prefabs/YellowCube.prefab", "c75e210455b9b7f32a8e902db35e6ee3") // FIXME, should this take a builder so it can look this up for us? + .SendAssetEvent(1, ContentStatus.Loading) + .SendAssetEvent(2, ContentStatus.Active); // assets do not have an explicit released event + var referencePrefabEvents = new ProfilerEventBuilder(testProfiler) + .SetAssetLocation("Assets/Prefabs/Canvas.prefab", "c75e210455b9b7f32a8e902db35e6ee3") // FIXME, should this take a builder so it can look this up for us? + .SendAssetEvent(1, ContentStatus.Loading) + .SendAssetEvent(2, ContentStatus.Active); // assets do not have an explicit released event + + // this simulates clicking each frame in the UI, retrieving data and verifying what is returned + for (int i = 1; i <= 2; i++) + { + AddressablesProfilerDetailsView.FrameData frameData = new AddressablesProfilerDetailsView.FrameData(i); + List groupData = m_AddressablesProfilerDetailsView.GenerateContentDataForFrame(frameData); + Assert.AreEqual("Prefabs", groupData[0].Name); + Assert.GreaterOrEqual(groupData[0].Children.Count, 1); + Assert.AreEqual("prefabs_assets_texture_8d87ce5cdbde7dec1bdded52918590d0.bundle", groupData[0].Children[0].Name); + foreach (var child in groupData[0].Children) + { + if (child is AssetData) + { + assetEvents.VerifyFrameStatus(i, child.Status); + } + } + } + } + [Test] public void Test_GetAssetDataForFrameData_HasData() { @@ -69,5 +194,60 @@ public void Test_GetAssetDataForFrameData_NoData() { { 10, new BundleData(new BuildLayout.Bundle(), new BundleFrameData()) } }; + + [Test] + [TestCase(null)] + [TestCase("Assets/Cube.prefab")] + public void Test_ExplicitAssetsHaveUniqueTreeViewIDs(string assetPath1) + { + // These two GUID strings evaluate to the same hashcode. + var cube = new BuildLayout.ExplicitAsset + { + AssetPath = assetPath1, + Guid = "bf1daffab47bbb34587f194ac0dcc1bf" + }; + + var sphere = new BuildLayout.ExplicitAsset + { + AssetPath = "Assets/Sphere.prefab", + Guid = "61a57bae3cb0cec499b6e6b8db8f4008" + }; + + var cubeData = new AssetData(cube); + var sphereData = new AssetData(sphere); + + Assert.AreNotEqual(cubeData.TreeViewID, sphereData.TreeViewID, "Explicit assets should have unique TreeViewIds."); + } + + [Test] + public void TestHasValues() + { + testProfiler.CurrentFrame = 1; + testProfiler.EmitFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kCatalogTag, new CatalogFrameData[] {}); + testProfiler.CurrentFrame = 2; + testProfiler.EmitFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kCatalogTag, new CatalogFrameData[] {new CatalogFrameData()}); + testProfiler.CurrentFrame = 3; + testProfiler.EmitFrameMetaData(ProfilerRuntime.kResourceManagerProfilerGuid, ProfilerRuntime.kCatalogTag, new CatalogFrameData[] {new CatalogFrameData(), new CatalogFrameData()}); + + AddressablesProfilerDetailsView.FrameData frameData = new AddressablesProfilerDetailsView.FrameData(1); + Assert.False(frameData.HasValues); + AssertEnumerableSize(frameData.CatalogValues, 0); + frameData = new AddressablesProfilerDetailsView.FrameData(2); + Assert.True(frameData.HasValues); + AssertEnumerableSize(frameData.CatalogValues, 1); + frameData = new AddressablesProfilerDetailsView.FrameData(3); + Assert.True(frameData.HasValues); + AssertEnumerableSize(frameData.CatalogValues, 2); + } + + private void AssertEnumerableSize(IEnumerable values, int expected) + { + int valueCount = 0; + foreach (var data in values) + { + valueCount++; + } + Assert.AreEqual(expected, valueCount); + } } } diff --git a/Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs b/Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs new file mode 100644 index 00000000..b4026037 --- /dev/null +++ b/Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs @@ -0,0 +1,114 @@ +using System; +using System.Linq; +using UnityEditor.AddressableAssets.Build.Layout; +using UnityEditor.AddressableAssets.BuildReportVisualizer; +using UnityEditor.AddressableAssets.Diagnostics; +using UnityEngine; + +namespace UnityEditor.AddressableAssets.Tests.Diagnostics.Profiler +{ + + public class BuildLayoutBuilder + { + private BuildLayout m_BuildLayout; + private BuildLayout.Group m_GroupContext; + private BuildLayout.Bundle m_BundleContext; + private BuildLayout.File m_FileContext; + + public BuildLayoutBuilder() + { + m_BuildLayout = new BuildLayout(); + } + + public BuildLayoutBuilder AddGroup(string name, string guid = null) + { + m_BundleContext = null; + m_FileContext = null; + if (guid == null) + { + guid = new Guid().ToString(); + } + + m_GroupContext = new BuildLayout.Group { Name = name, Guid = guid }; + m_BuildLayout.Groups.Add(m_GroupContext); + return this; + } + + public BuildLayout.Group FindGroup(string name) + { + return m_BuildLayout.Groups.First((g) => g.Name == name); + } + + public BuildLayoutBuilder AddBundle(string name) + { + if (m_GroupContext == null) + throw new System.Exception("You must add a group before adding a bundle."); + + var internalName = Hash128.Compute(name); + var bundle = new BuildLayout.Bundle { Name = name, InternalName = internalName + ".bundle", Group = m_GroupContext }; + m_GroupContext.Bundles.Add(bundle); + m_BundleContext = bundle; + m_FileContext = new BuildLayout.File { Bundle = bundle, Name = name }; + m_BundleContext.Files.Add(m_FileContext); + return this; + } + + public BuildLayout.Bundle FindBundle(string groupName, string bundleName) + { + var group = FindGroup(groupName); + return group.Bundles.First((b) => b.Name == bundleName); + } + + public BuildLayout.ExplicitAsset FindExplicitAsset(string groupName, string bundleName, string addressableName) + { + var group = FindGroup(groupName); + var bundle = FindBundle(groupName, bundleName); + foreach (var file in bundle.Files) + { + var asset = file.Assets.FirstOrDefault((a) => a.AddressableName == addressableName); + if (asset != null) + { + return asset; + } + } + return null; + } + + public BuildLayoutBuilder AddAsset(string name, string internalId, AssetType assetType = AssetType.Mesh) + { + if (m_FileContext == null) + throw new System.Exception("You must add a bundle before adding an asset."); + + var guid = new Guid().ToString(); + return AddAsset(new BuildLayout.ExplicitAsset { AddressableName = name, Bundle = m_BundleContext, InternalId = internalId, Guid = guid, MainAssetType = AssetType.Mesh}); + } + + public BuildLayoutBuilder AddAsset(BuildLayout.ExplicitAsset asset) + { + if (m_FileContext == null) + throw new System.Exception("You must add a bundle before adding an asset."); + + asset.Objects.Add(new BuildLayout.ObjectData + { + AssetType = asset.MainAssetType + }); + m_FileContext.Assets.Add(asset); + return this; + } + + public BuildLayoutBuilder AddAssetReference(string addressableName) + { + return this; + } + + public BuildLayoutBuilder AddToLayoutManager() + { + var hash = new Hash128(); + m_BuildLayout.m_BodyRead = true; + m_BuildLayout.BuildResultHash = hash.ToString(); + AddressablesProfilerViewController.LayoutsManager.AddActiveLayout(m_BuildLayout); + return this; + } + } + +} diff --git a/Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs.meta b/Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs.meta new file mode 100644 index 00000000..a961bb7d --- /dev/null +++ b/Tests/Editor/Diagnostics/Profiler/BuildLayoutBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6fa9ec8edf7643209ff1f78685a14f31 +timeCreated: 1713290321 \ No newline at end of file diff --git a/Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs b/Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs new file mode 100644 index 00000000..e5f79754 --- /dev/null +++ b/Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using NUnit.Framework; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.Profiling; +using UnityEngine.ResourceManagement.ResourceLocations; +using UnityEngine.ResourceManagement.ResourceProviders; +using UnityEngine.ResourceManagement.Util; + +namespace UnityEditor.AddressableAssets.Tests.Diagnostics.Profiler +{ + internal class ProfilerEventBuilder + { + private TestProfiler m_TestProfiler; + private ProvideHandle m_Handle; + private AsyncOperationHandle m_SceneHandle; + private AssetBundleRequestOptions m_Opts; + private Dictionary m_FrameStatusMap = new Dictionary(); + + public ProfilerEventBuilder(TestProfiler testProfiler) + { + m_TestProfiler = testProfiler; + m_Handle = new ProvideHandle(Addressables.ResourceManager, new TestOp()); + m_SceneHandle = new AsyncOperationHandle(new TestSceneOp()); + m_Opts = new AssetBundleRequestOptions(); + } + + public ProfilerEventBuilder SetOperationStatus(AsyncOperationStatus status) + { + ((TestOp)m_Handle.InternalOp).Status = status; + return this; + } + + public ProfilerEventBuilder SetBundleName(string bundleName) + { + m_Opts.BundleName = bundleName; + return this; + } + + public ProfilerEventBuilder SetAssetLocation(string addressableName, string bundleName) + { + var location = new ProfilerResourceLocation(); + location.InternalId = addressableName; + location.Dependencies = new List(); + location.Dependencies.Add(new ProfilerResourceLocation + { + Data = new AssetBundleRequestOptions{BundleName = bundleName}, // fixme, strip off bundle name or tone up the api + }); + + ((TestOp)m_Handle.InternalOp).Location = location; + return this; + } + + public ProfilerEventBuilder SendBundleEvent(int frame, ContentStatus status, BundleSource source = BundleSource.Download) + { + m_FrameStatusMap[frame] = status; + m_TestProfiler.CurrentFrame = frame; + if (status == ContentStatus.Released) + { + ProfilerRuntime.BundleReleased(m_Opts.BundleName); + } + else + { + ProfilerRuntime.AddBundleOperation(m_Handle, m_Opts, status, source); + } + ProfilerRuntime.PushToProfilerStream(); + return this; + } + public ProfilerEventBuilder SendAssetEvent(int frame, ContentStatus status) + { + m_FrameStatusMap[frame] = status; + m_TestProfiler.CurrentFrame = frame; + ProfilerRuntime.AddAssetOperation(m_Handle, status); + + ProfilerRuntime.PushToProfilerStream(); + return this; + } + + public ProfilerEventBuilder SendSceneEvent(int frame, ContentStatus status) + { + m_FrameStatusMap[frame] = status; + m_TestProfiler.CurrentFrame = frame; + if (status == ContentStatus.Released) + { + ProfilerRuntime.SceneReleased(m_SceneHandle); + } + else + { + ProfilerRuntime.AddSceneOperation(m_SceneHandle, m_Handle.Location, status); + } + + ProfilerRuntime.PushToProfilerStream(); + return this; + } + + public ProfilerEventBuilder VerifyFrameStatus(int frame, ContentStatus actualStatus) + { + if(!m_FrameStatusMap.ContainsKey(frame)) + { + return this; + } + + Assert.AreEqual(m_FrameStatusMap[frame], actualStatus, $"For frame {frame}, expected status {m_FrameStatusMap[frame]} but got {actualStatus}"); + return this; + } + } + + internal class TestSceneOp : AsyncOperationBase, IAsyncOperation + { + protected override void Execute() + { + throw new NotImplementedException(); + } + } + + internal class TestOp : IGenericProviderOperation, IAsyncOperation + { + public void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle> depOp) + { + throw new NotImplementedException(); + } + + public void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle> depOp, bool releaseDependenciesOnFailure) + { + throw new NotImplementedException(); + } + + public int ProvideHandleVersion { get; } + public IResourceLocation Location { get; set; } + public int DependencyCount { get; } + + public void GetDependencies(IList dstList) + { + throw new NotImplementedException(); + } + + public TDepObject GetDependency(int index) + { + throw new NotImplementedException(); + } + + public void SetProgressCallback(Func callback) + { + throw new NotImplementedException(); + } + + public void ProviderCompleted(T result, bool status, Exception e) + { + throw new NotImplementedException(); + } + + public Type RequestedType { get; } + + public void SetDownloadProgressCallback(Func callback) + { + throw new NotImplementedException(); + } + + public void SetWaitForCompletionCallback(Func callback) + { + throw new NotImplementedException(); + } + + public object GetResultAsObject() + { + throw new NotImplementedException(); + } + + public Type ResultType { get; } + public int Version { get; } + public string DebugName { get; } + + public void DecrementReferenceCount() + { + throw new NotImplementedException(); + } + + public void IncrementReferenceCount() + { + throw new NotImplementedException(); + } + + public int ReferenceCount { get; } + public float PercentComplete { get; } + + public DownloadStatus GetDownloadStatus(HashSet visited) + { + throw new NotImplementedException(); + } + + public AsyncOperationStatus Status { get; set; } + public Exception OperationException { get; } + public bool IsDone { get; } + public Action OnDestroy { get; set; } + + public void GetDependencies(List deps) + { + throw new NotImplementedException(); + } + + public bool IsRunning { get; } + public event Action CompletedTypeless; + public event Action Destroyed; + + public void InvokeCompletionEvent() + { + throw new NotImplementedException(); + } + + public Task Task { get; } + + public void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList updateCallbacks) + { + throw new NotImplementedException(); + } + + public AsyncOperationHandle Handle { get; } + + public void WaitForCompletion() + { + throw new NotImplementedException(); + } + } + + internal class ProfilerResourceLocation : IResourceLocation { + public string InternalId { get; set; } + public string ProviderId { get; set; } + public IList Dependencies { get; set; } + public int Hash(Type resultType) + { + throw new NotImplementedException(); + } + + public int DependencyHashCode { get; set; } + public bool HasDependencies { get; set; } + public object Data { get; set; } + public string PrimaryKey { get; set; } + public Type ResourceType { get; set; } + } +} diff --git a/Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs.meta b/Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs.meta new file mode 100644 index 00000000..db5180ab --- /dev/null +++ b/Tests/Editor/Diagnostics/Profiler/ProfilerEventBuilder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ef23b19942a44437b4c81ff037bdee0d +timeCreated: 1713198784 \ No newline at end of file diff --git a/Tests/Editor/Diagnostics/Profiler/TestProfiler.cs b/Tests/Editor/Diagnostics/Profiler/TestProfiler.cs new file mode 100644 index 00000000..9e893185 --- /dev/null +++ b/Tests/Editor/Diagnostics/Profiler/TestProfiler.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using UnityEditor.AddressableAssets.Diagnostics; +using UnityEngine; +using UnityEngine.ResourceManagement.Profiling; +using static UnityEngine.ResourceManagement.Profiling.ProfilerRuntime; + +namespace UnityEditor.AddressableAssets.Tests.Diagnostics.Profiler +{ + + internal class TestProfiler : IProfilerEmitter, IFrameDataStore + { + // maybe have a key + private Dictionary> m_catalogFrames = new(); + private Dictionary> m_bundleFrames = new(); + private Dictionary> m_assetFrames = new(); + private Dictionary> m_sceneFrames = new(); + + public int CurrentThread + { + get => Thread.CurrentContext.ContextID; + } + + public bool IsEnabled { get; set; } = true; + + // public int CurrentFrame { get => Time.frameCount; } + public int CurrentFrame { get; set; } = 0; + + private string GetKey() + { + return $"{CurrentFrame}-{CurrentThread}"; + } + + /* + public void IncrementCurrentFrame() + { + // it would be cool if this hooked into the test frameworks async frame tools + CurrentFrame += 1; + } + */ + + // You can emit data for each type of tag in a given frame, but then you'll need + // to increment the frame by setting CurrentFrame or calling IncrementCurrentFrame + public void EmitFrameMetaData(Guid id, int tag, Array data) + { + if (!IsEnabled) + return; + Assert.AreEqual(kResourceManagerProfilerGuid, id, "TestProfiler only handles data for the Addressables ResourceManager"); + if (data.Length == 0) + { + return; + } + + var key = GetKey(); + switch (tag) + { + case kCatalogTag: + m_catalogFrames.TryAdd(key, new List()); + // Assert.False(m_catalogFrames.ContainsKey(GetKey()), "TestProfiler does not allow emitting data for the same frame/thread/catalog more than once. Increment CurrentFrame."); + m_catalogFrames[key].AddRange(data as CatalogFrameData[]); + break; + case kBundleDataTag: + m_bundleFrames.TryAdd(key, new List()); + // Assert.False(m_bundleFrames.ContainsKey(GetKey()), "TestProfiler does not allow emitting data for the same frame/thread/bundle more than once. Increment CurrentFrame."); + m_bundleFrames[key].AddRange(data as BundleFrameData[]); + break; + case kAssetDataTag: + m_assetFrames.TryAdd(key, new List()); + // Assert.False(m_assetFrames.ContainsKey(GetKey()), "TestProfiler does not allow emitting data for the same frame/thread/asset more than once. Increment CurrentFrame."); + m_assetFrames[key].AddRange(data as AssetFrameData[]); + break; + case kSceneDataTag: + m_sceneFrames.TryAdd(key, new List()); + // Assert.False(m_catalogFrames.ContainsKey(GetKey()), "TestProfiler does not allow emitting data for the same frame/thread/scene more than once. Increment CurrentFrame."); + m_sceneFrames[key].AddRange(data as AssetFrameData[]); + break; + } + + } + + public void InitialiseCallbacks(Action onLateUpdateDelegate) + { + // make sure we get rid of the delegate if it has already been registered, although maybe we don't want to do this explicitly? + MonoBehaviourCallbackHooks.Instance.OnLateUpdateDelegate -= onLateUpdateDelegate; + } + + public FrameDataViewRef GetRawFrameDataView(int frameIndex, int threadIndex) + { + return new FrameDataViewRef + { + frameIndex = frameIndex, + threadIndex = threadIndex, + valid = true, + }; + } + + public IEnumerable GetFrameMetaData(FrameDataViewRef rawFrameDatView, Guid id, int tag) where T : struct + { + var key = $"{rawFrameDatView.frameIndex}-{rawFrameDatView.threadIndex}"; + Assert.AreEqual(kResourceManagerProfilerGuid, id, "TestProfiler only handles data for the Addressables ResourceManager"); + switch (tag) + { + case kCatalogTag: + var catalogFrameData = m_catalogFrames.GetValueOrDefault(key, new List()); + return catalogFrameData as IEnumerable; + case kBundleDataTag: + var bundleFrameData = m_bundleFrames.GetValueOrDefault(key, new List()); + return bundleFrameData as IEnumerable; + case kAssetDataTag: + var assetFrameData = m_assetFrames.GetValueOrDefault(key, new List()); + return assetFrameData as IEnumerable; + case kSceneDataTag: + var sceneFrameData = m_sceneFrames.GetValueOrDefault(key, new List()); + return sceneFrameData as IEnumerable; + } + + Assert.Fail($"unknown tag {tag}"); + return null; + } + } +} diff --git a/Tests/Editor/Diagnostics/Profiler/TestProfiler.cs.meta b/Tests/Editor/Diagnostics/Profiler/TestProfiler.cs.meta new file mode 100644 index 00000000..94537363 --- /dev/null +++ b/Tests/Editor/Diagnostics/Profiler/TestProfiler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 38834594570740c0b7a568075072e1b7 +timeCreated: 1712947015 \ No newline at end of file diff --git a/Tests/Editor/DocExampleCode/AsynchronousLoading.cs b/Tests/Editor/DocExampleCode/AsynchronousLoading.cs index e4166a50..2907bec1 100644 --- a/Tests/Editor/DocExampleCode/AsynchronousLoading.cs +++ b/Tests/Editor/DocExampleCode/AsynchronousLoading.cs @@ -40,7 +40,7 @@ async void LoadAssetWait() private void OnDestroy() { - Addressables.Release(loadHandle); + loadHandle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadLocation.cs b/Tests/Editor/DocExampleCode/LoadLocation.cs index 8cef5600..fa9521ca 100644 --- a/Tests/Editor/DocExampleCode/LoadLocation.cs +++ b/Tests/Editor/DocExampleCode/LoadLocation.cs @@ -69,7 +69,7 @@ private void OnDestroy() { foreach (var item in operationDictionary) { - Addressables.Release(item.Value); + item.Value.Release(); } } } @@ -92,7 +92,7 @@ AsyncOperationHandle> handle //... - Addressables.Release(handle); + handle.Release(); #endregion } diff --git a/Tests/Editor/DocExampleCode/LoadMultiple.cs b/Tests/Editor/DocExampleCode/LoadMultiple.cs index c5ffe948..6f611542 100644 --- a/Tests/Editor/DocExampleCode/LoadMultiple.cs +++ b/Tests/Editor/DocExampleCode/LoadMultiple.cs @@ -43,7 +43,7 @@ public IEnumerator Start() private void OnDestroy() { - Addressables.Release(loadHandle); + loadHandle.Release(); // Release all the loaded assets associated with loadHandle // Note that if you do not make loaded addressables a child of this object, // then you will need to devise another way of releasing the handle when diff --git a/Tests/Editor/DocExampleCode/LoadSingle.cs b/Tests/Editor/DocExampleCode/LoadSingle.cs index 671014e0..6cb1e08e 100644 --- a/Tests/Editor/DocExampleCode/LoadSingle.cs +++ b/Tests/Editor/DocExampleCode/LoadSingle.cs @@ -26,7 +26,7 @@ public IEnumerator Start() void OnDestroy() { - Addressables.Release(opHandle); + opHandle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadSynchronously.cs b/Tests/Editor/DocExampleCode/LoadSynchronously.cs index dd252133..a7fe2d6c 100644 --- a/Tests/Editor/DocExampleCode/LoadSynchronously.cs +++ b/Tests/Editor/DocExampleCode/LoadSynchronously.cs @@ -22,13 +22,13 @@ void Start() } else { - Addressables.Release(opHandle); + opHandle.Release(); } } void OnDestroy() { - Addressables.Release(opHandle); + opHandle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadWithAddress.cs b/Tests/Editor/DocExampleCode/LoadWithAddress.cs index fcc8d6bb..bbdb146e 100644 --- a/Tests/Editor/DocExampleCode/LoadWithAddress.cs +++ b/Tests/Editor/DocExampleCode/LoadWithAddress.cs @@ -37,7 +37,7 @@ private void Handle_Completed(AsyncOperationHandle operation) // Release asset when parent object is destroyed private void OnDestroy() { - Addressables.Release(handle); + handle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadWithEvent.cs b/Tests/Editor/DocExampleCode/LoadWithEvent.cs index e4d90741..1bb89964 100644 --- a/Tests/Editor/DocExampleCode/LoadWithEvent.cs +++ b/Tests/Editor/DocExampleCode/LoadWithEvent.cs @@ -27,13 +27,13 @@ private void Operation_Completed(AsyncOperationHandle obj) } else { - Addressables.Release(obj); + obj.Release(); } } void OnDestroy() { - Addressables.Release(opHandle); + opHandle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadWithIEnumerator.cs b/Tests/Editor/DocExampleCode/LoadWithIEnumerator.cs index 26cf1f12..11a94054 100644 --- a/Tests/Editor/DocExampleCode/LoadWithIEnumerator.cs +++ b/Tests/Editor/DocExampleCode/LoadWithIEnumerator.cs @@ -27,13 +27,13 @@ public IEnumerator Start() } else { - Addressables.Release(opHandle); + opHandle.Release(); } } void OnDestroy() { - Addressables.Release(opHandle); + opHandle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadWithLabels.cs b/Tests/Editor/DocExampleCode/LoadWithLabels.cs index 4364e041..0c235255 100644 --- a/Tests/Editor/DocExampleCode/LoadWithLabels.cs +++ b/Tests/Editor/DocExampleCode/LoadWithLabels.cs @@ -50,7 +50,7 @@ private void LoadHandle_Completed(AsyncOperationHandle> operat private void OnDestroy() { // Release all the loaded assets associated with loadHandle - Addressables.Release(loadHandle); + loadHandle.Release(); } } diff --git a/Tests/Editor/DocExampleCode/LoadWithTask.cs b/Tests/Editor/DocExampleCode/LoadWithTask.cs index b51f88ed..7bd39c75 100644 --- a/Tests/Editor/DocExampleCode/LoadWithTask.cs +++ b/Tests/Editor/DocExampleCode/LoadWithTask.cs @@ -51,7 +51,7 @@ public async void Start() private void OnDestroy() { - Addressables.Release(loadHandle); + loadHandle.Release(); // Release all the loaded assets associated with loadHandle // Note that if you do not make loaded addressables a child of this object, // then you will need to devise another way of releasing the handle when diff --git a/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs b/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs index 7469e880..cbaa69f8 100644 --- a/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs +++ b/Tests/Editor/DocExampleCode/MiscellaneousTopics.cs @@ -34,7 +34,7 @@ AsyncOperationHandle> updateHandle = Addressables.UpdateCatalogs(); yield return updateHandle; - Addressables.Release(updateHandle); + updateHandle.Release(); } #endregion @@ -55,10 +55,10 @@ AsyncOperationHandle> checkForUpdateHandle AsyncOperationHandle> updateHandle = Addressables.UpdateCatalogs(catalogsToUpdate); yield return updateHandle; - Addressables.Release(updateHandle); + updateHandle.Release(); } - Addressables.Release(checkForUpdateHandle); + checkForUpdateHandle.Release(); } #endregion @@ -144,7 +144,7 @@ AsyncOperationHandle loadAssetHandle if (!groupOp.IsDone) yield return groupOp; - Addressables.Release(loadResourceLocationsHandle); + loadResourceLocationsHandle.Release(); //take a gander at our results. foreach (var item in _preloadedObjects) @@ -174,7 +174,7 @@ private IEnumerator PreloadAssets() Debug.LogError("Failed to download dependencies for " + key); // we need to release the download handle so the assets can be loaded - Addressables.Release(downloadDependencies); + downloadDependencies.Release(); } #endregion diff --git a/Tests/Editor/DocExampleCode/PreloadWithProgress.cs b/Tests/Editor/DocExampleCode/PreloadWithProgress.cs index 4d76d21a..e616d9df 100644 --- a/Tests/Editor/DocExampleCode/PreloadWithProgress.cs +++ b/Tests/Editor/DocExampleCode/PreloadWithProgress.cs @@ -33,7 +33,7 @@ IEnumerator Start() } CompletionEvent.Invoke(downloadHandle.Status == AsyncOperationStatus.Succeeded); - Addressables.Release(downloadHandle); //Release the operation handle + downloadHandle.Release(); //Release the operation handle } } diff --git a/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs b/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs index 9a82e520..0d6f7ad8 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/ContentBuiltCheck.cs @@ -1,5 +1,6 @@ namespace AddressableAssets.DocExampleCode { + /* #region CONTENT_BUILT_CHECK #if UNITY_EDITOR @@ -37,4 +38,5 @@ public void OnPreprocessBuild(BuildReport report) #endif #endregion + */ } diff --git a/Tests/Editor/DocExampleCode/ScriptReference/UsingCleanBundleCache.cs b/Tests/Editor/DocExampleCode/ScriptReference/UsingCleanBundleCache.cs index 045bd1ce..11042f6b 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/UsingCleanBundleCache.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/UsingCleanBundleCache.cs @@ -26,7 +26,7 @@ public void UsingCleanBundleCacheForAllCatalogs() { // during caching a reference is added to the catalogs. // release is needed to reduce the reference and allow catalog to be uncached for updating - Addressables.Release(op); + op.Release(); }; } #endregion @@ -52,7 +52,7 @@ public void UsingCleanBundleCacheWithcatalogIds() { // during caching a reference is added to the catalogs. // release is needed to reduce the reference and allow catalog to be uncached for updating - Addressables.Release(op); + op.Release(); }; } #endregion diff --git a/Tests/Editor/DocExampleCode/ScriptReference/UsingInitializeAsync.cs b/Tests/Editor/DocExampleCode/ScriptReference/UsingInitializeAsync.cs index 5b09d007..1d71cfc9 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/UsingInitializeAsync.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/UsingInitializeAsync.cs @@ -78,7 +78,7 @@ async Task UsingInitializeAsyncSampleTask() IResourceLocator locator = handle.Result; Debug.Log($"The resource locator returned has an id of {locator.LocatorId}"); } - Addressables.Release(handle); + handle.Release(); } #endregion diff --git a/Tests/Editor/DocExampleCode/ScriptReference/UsingInstanceProvider.cs b/Tests/Editor/DocExampleCode/ScriptReference/UsingInstanceProvider.cs index 0225506e..ac0dad53 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/UsingInstanceProvider.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/UsingInstanceProvider.cs @@ -51,8 +51,8 @@ void OnProvideInstanceComplete(AsyncOperationHandle handle) void ReleaseResources() { - Addressables.Release(locHandle); - Addressables.Release(instHandle); + locHandle.Release(); + instHandle.Release(); } // When ready to release the asset, call ReleaseResources(). diff --git a/Tests/Editor/DocExampleCode/ScriptReference/UsingInstantiateAsync.cs b/Tests/Editor/DocExampleCode/ScriptReference/UsingInstantiateAsync.cs index 02cfb60a..f353c924 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/UsingInstantiateAsync.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/UsingInstantiateAsync.cs @@ -89,7 +89,7 @@ void OnInstantiateCompleteLocation(AsyncOperationHandle handle) void ReleaseResourcesLocation() { - Addressables.Release(locHandle); + locHandle.Release(); Addressables.ReleaseInstance(instanceLocation); } @@ -156,7 +156,7 @@ void OnInstantiateCompleteObjectUntracked(AsyncOperationHandle handl void ReleaseUntrackedResources() { - Addressables.Release(handleUntracked); + handleUntracked.Release(); } // When ready to release the asset, call ReleaseUntrackedResources(). diff --git a/Tests/Editor/DocExampleCode/ScriptReference/UsingInternalIdTransformFunc.cs b/Tests/Editor/DocExampleCode/ScriptReference/UsingInternalIdTransformFunc.cs index e4d2d05e..16c4a445 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/UsingInternalIdTransformFunc.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/UsingInternalIdTransformFunc.cs @@ -47,7 +47,7 @@ void OnInstantiateComplete(AsyncOperationHandle handle) void ReleaseResources() { - Addressables.Release(opHandle); + opHandle.Release(); } // When ready to release the asset, call ReleaseResources(). diff --git a/Tests/Editor/DocExampleCode/ScriptReference/UsingLoadAssetAsync.cs b/Tests/Editor/DocExampleCode/ScriptReference/UsingLoadAssetAsync.cs index 16a99030..1c2e98fd 100644 --- a/Tests/Editor/DocExampleCode/ScriptReference/UsingLoadAssetAsync.cs +++ b/Tests/Editor/DocExampleCode/ScriptReference/UsingLoadAssetAsync.cs @@ -65,8 +65,8 @@ void OnLoadCompleteLocation(AsyncOperationHandle handle) void ReleaseResourcesLocation() { - Addressables.Release(locHandle); - Addressables.Release(instHandleLocation); + locHandle.Release(); + instHandleLocation.Release(); } // When ready to release the asset, call ReleaseResourcesLocation(). @@ -105,7 +105,7 @@ void OnLoadCompleteKey(AsyncOperationHandle handle) void ReleaseResourcesKey() { - Addressables.Release(handleKey); + handleKey.Release(); } // When ready to release the asset, call ReleaseResourcesKey(). diff --git a/Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef b/Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef new file mode 100644 index 00000000..fcf71bd3 --- /dev/null +++ b/Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef @@ -0,0 +1,55 @@ +{ + "name": "Unity.Addressables.Editor.Tests.DocExampleCode", + "references": [ + "Unity.Addressables.Editor", + "Unity.Addressables", + "Unity.ResourceManager", + "Unity.ScriptableBuildPipeline.Editor", + "Unity.Addressables.Tests", + "Unity.Addressables.Editor.BuildReportVisualizer", + "UnityEditor.TestRunner", + "UnityEngine.TestRunner", + "Unity.Services.Ccd.Management", + "Unity.Services.Core" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll", + "Moq.dll" + ], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [ + { + "name": "Unity", + "expression": "2020.2.0a9", + "define": "NONRECURSIVE_DEPENDENCY_DATA" + }, + { + "name": "com.unity.services.ccd.management", + "expression": "[2.0.0,4.0.0)", + "define": "ENABLE_CCD" + }, + { + "name": "com.unity.profiling.core", + "expression": "[1.0.2,3.0.0)", + "define": "ENABLE_ADDRESSABLE_PROFILER" + }, + { + "name": "nuget.moq", + "expression": "2.0.0", + "define": "ENABLE_MOQ" + }, + { + "name": "com.unity.services.ccd.management", + "expression": "[3.0.0,4.0.0)", + "define": "CCD_3_OR_NEWER" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef.meta b/Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef.meta new file mode 100644 index 00000000..9eb6b110 --- /dev/null +++ b/Tests/Editor/DocExampleCode/Unity.Addressables.Editor.Tests.DocExampleCode.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ff038353b1a72c54aa6b046914f56eb0 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/Fixtures/buildlayout1.json b/Tests/Editor/Fixtures/buildlayout1.json new file mode 100644 index 00000000..bd5c9d81 --- /dev/null +++ b/Tests/Editor/Fixtures/buildlayout1.json @@ -0,0 +1,2 @@ +{"BuildTarget":19,"BuildResultHash":"3e22efc5f159401a15e7d27179b7cc4c","BuildType":0,"BuildStartTime":"4/19/2024 9:55:37 AM","Duration":3.4737609999999998,"BuildError":"", +"UnityVersion":"2022.3.26f1","PackageVersion":"com.unity.addressables: 1.21.20","PlayerBuildVersion":"0.1","AddressablesEditorSettings":{"SettingsHash":"af1e64d7eac11eb29f9f3cb8d9b24f9f","ActiveProfile":{"Name":"Default","Id":"1e8599155ff2f7749adbb6af471d42d5","Values":[{"Key":"e0fc39e952f4986449603bafe2d5226c","Value":"[UnityEditor.EditorUserBuildSettings.activeBuildTarget]"},{"Key":"cb57b8bf18bf33a47b0815e7f27dc653","Value":"[UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget]"},{"Key":"d75c74ff448614541970d5dee1545b8f","Value":"{UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]"},{"Key":"30a0fadc254841b4980a6fc66e8289ed","Value":"ServerData/[BuildTarget]"},{"Key":"f321c8623a6ff8840a41f3e5f527d8d3","Value":"http://[PrivateIpAddress]:[HostingServicePort]"}]},"BuildRemoteCatalog":true,"RemoteCatalogLoadPath":"http://172.23.112.1:HostingServicePort","BundleLocalCatalog":false,"OptimizeCatalogSize":false,"CatalogRequestsTimeout":0,"MaxConcurrentWebRequests":3,"DisableCatalogUpdateOnStartup":false,"UniqueBundleIds":false,"NonRecursiveBuilding":true,"ContiguousBundles":true,"DisableSubAssetRepresentations":false,"ShaderBundleNaming":"ProjectName","MonoScriptBundleNaming":"Disabled","StripUnityVersionFromBundleBuild":false},"AddressablesRuntimeSettings":{"ProfilerEvents":false,"LogResourceManagerExceptions":true,"CatalogLoadPaths":["http://172.23.112.1:HostingServicePort/catalog_0.1.hash","{UnityEngine.Application.persistentDataPath}/com.unity.addressables/catalog_0.1.hash","{UnityEngine.AddressableAssets.Addressables.RuntimePath}/catalog.json"],"CatalogHash":"5ad154c31af46b0ed22510e1bd4e0b9a"},"BuildScript":"Default Build Script","DefaultGroup":{"rid":1000},"Groups":[{"rid":1001},{"rid":1000},{"rid":1002},{"rid":1003},{"rid":1004}],"BuiltInBundles":[{"rid":1005}],"DuplicatedAssets":[{"AssetGuid":"7092a5e42eb254241828c7ff32096d98","DuplicatedObjects":[{"LocalIdentifierInFile":2100000,"IncludedInBundleFiles":[{"rid":1006},{"rid":1007}]}]},{"AssetGuid":"c4904135a42b0d3468d69deded7ca5b8","DuplicatedObjects":[{"LocalIdentifierInFile":2100000,"IncludedInBundleFiles":[{"rid":1006},{"rid":1007}]}]},{"AssetGuid":"5457acd3d768cb043b8ad79c19dc24dc","DuplicatedObjects":[{"LocalIdentifierInFile":2800000,"IncludedInBundleFiles":[{"rid":1006},{"rid":1007}]}]},{"AssetGuid":"d0fa037f07425224a9117ec27f735121","DuplicatedObjects":[{"LocalIdentifierInFile":2800000,"IncludedInBundleFiles":[{"rid":1006},{"rid":1007}]}]}],"LocalCatalogBuildPath":"Library/com.unity.addressables/aa/Windows/StandaloneWindows64","RemoteCatalogBuildPath":"ServerData/StandaloneWindows64","references":{"version":2,"RefIds":[{"rid":1000,"type":{"class":"BuildLayout/Group","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"Default Local Group","Guid":"c12cce9779a49514599e18b13e90336e","PackingMode":"PackTogether","Bundles":[{"rid":1008}],"Schemas":[{"rid":1009},{"rid":1010}]}},{"rid":1001,"type":{"class":"BuildLayout/Group","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"Built In Data","Guid":"c3ca9c76081dc2041914a19d26a49ac0","PackingMode":"","Bundles":[],"Schemas":[{"rid":1011}]}},{"rid":1002,"type":{"class":"BuildLayout/Group","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"Scene","Guid":"5d8d3b81860b62b4b964bed8a1deef7d","PackingMode":"PackTogether","Bundles":[{"rid":1012}],"Schemas":[{"rid":1013},{"rid":1014}]}},{"rid":1003,"type":{"class":"BuildLayout/Group","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"Folder","Guid":"d47234d6c7bab3545a5185bb31ffbde5","PackingMode":"PackSeparately","Bundles":[{"rid":1015}],"Schemas":[{"rid":1016},{"rid":1017}]}},{"rid":1004,"type":{"class":"BuildLayout/Group","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"Prefabs","Guid":"023e6b69bad1d024c92a88d3a7e6bbc2","PackingMode":"PackTogetherByLabel","Bundles":[{"rid":1018},{"rid":1019},{"rid":1020}],"Schemas":[{"rid":1021},{"rid":1022}]}},{"rid":1005,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"31bb9849e2d5a7e99340ca79e976f156_unitybuiltinshaders_812a9cade8f0615358c4e028691e73f1.bundle","InternalName":"31bb9849e2d5a7e99340ca79e976f156_unitybuiltinshaders","FileSize":48280,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":0,"AssetCount":0,"BundleDependencies":[],"Compression":"Lz4HC","CRC":1177133543,"Hash":{"serializedVersion":"2","Hash":"812a9cade8f0615358c4e028691e73f1"},"Group":{"rid":1000},"LoadPath":"{UnityEngine.AddressableAssets.Addressables.RuntimePath}\\StandaloneWindows64\\31bb9849e2d5a7e99340ca79e976f156_unitybuiltinshaders_812a9cade8f0615358c4e028691e73f1.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1023}],"DependentBundles":[{"rid":1012},{"rid":1019}],"Dependencies":[],"ExpandedDependencies":[]}},{"rid":1006,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-c027b5d99f29eca69deb8d470fdba74d/CAB-c027b5d99f29eca69deb8d470fdba74d.sharedAssets","Bundle":{"rid":1012},"SubFiles":[{"rid":1024},{"rid":1025},{"rid":1026},{"rid":1027}],"Assets":[{"rid":1028}],"OtherAssets":[{"rid":1029},{"rid":1030},{"rid":1031},{"rid":1032},{"rid":1033}],"ExternalReferences":[{"rid":1034}],"WriteResultFilename":"","BundleObjectInfo":{"Size":352},"PreloadInfoSize":165,"MonoScriptCount":6,"MonoScriptSize":576}},{"rid":1007,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-4d4a56f8e64de1cb9f42462631c1236e/CAB-4d4a56f8e64de1cb9f42462631c1236e","Bundle":{"rid":1019},"SubFiles":[{"rid":1035},{"rid":1036}],"Assets":[{"rid":1037},{"rid":1038}],"OtherAssets":[{"rid":1039},{"rid":1040},{"rid":1041},{"rid":1042}],"ExternalReferences":[],"WriteResultFilename":"","BundleObjectInfo":{"Size":532},"PreloadInfoSize":0,"MonoScriptCount":0,"MonoScriptSize":0}},{"rid":1008,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"defaultlocalgroup_assets_all_eb2ec8e17e2efb0018054c9e4d8d236a.bundle","InternalName":"0113602827489280bf434abd9b49426c","FileSize":2508,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":0,"AssetCount":5,"BundleDependencies":[],"Compression":"Lz4HC","CRC":361533155,"Hash":{"serializedVersion":"2","Hash":"eb2ec8e17e2efb0018054c9e4d8d236a"},"Group":{"rid":1000},"LoadPath":"{UnityEngine.AddressableAssets.Addressables.RuntimePath}\\StandaloneWindows64\\defaultlocalgroup_assets_all_eb2ec8e17e2efb0018054c9e4d8d236a.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1043}],"DependentBundles":[],"Dependencies":[],"ExpandedDependencies":[]}},{"rid":1009,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"57224322f6b6507489a3d1c2f3944d54","Type":"ContentUpdateGroupSchema","SchemaDataPairs":[{"Key":"StaticContent","Value":"False"}]}},{"rid":1010,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"392d31b47e22dea4795cc1521e18fa7b","Type":"BundledAssetGroupSchema","SchemaDataPairs":[{"Key":"InternalBundleIdMode","Value":"GroupGuidProjectIdHash"},{"Key":"Compression","Value":"LZ4"},{"Key":"IncludeAddressInCatalog","Value":"True"},{"Key":"IncludeGUIDInCatalog","Value":"True"},{"Key":"IncludeLabelsInCatalog","Value":"True"},{"Key":"InternalIdNamingMode","Value":"FullPath"},{"Key":"AssetBundledCacheClearBehavior","Value":"ClearWhenSpaceIsNeededInCache"},{"Key":"IncludeInBuild","Value":"True"},{"Key":"BundledAssetProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider"},{"Key":"ForceUniqueProvider","Value":"False"},{"Key":"UseAssetBundleCache","Value":"True"},{"Key":"UseAssetBundleCrc","Value":"True"},{"Key":"UseAssetBundleCrcForCachedBundles","Value":"True"},{"Key":"UseUnityWebRequestForLocalBundles","Value":"False"},{"Key":"Timeout","Value":"0"},{"Key":"ChunkedTransfer","Value":"False"},{"Key":"RedirectLimit","Value":"-1"},{"Key":"RetryCount","Value":"0"},{"Key":"BuildPath","Value":"Library/com.unity.addressables/aa/Windows/StandaloneWindows64"},{"Key":"LoadPath","Value":"{UnityEngine.AddressableAssets.Addressables.RuntimePath}/StandaloneWindows64"},{"Key":"PackingMode","Value":"PackTogether"},{"Key":"HostingServicesContentRoot","Value":"Library/com.unity.addressables/aa/Windows/StandaloneWindows64"},{"Key":"AssetBundleProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"},{"Key":"BundleNaming","Value":"AppendHash"},{"Key":"AssetLoadMode","Value":"RequestedAssetAndDependencies"}]}},{"rid":1011,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"0559acb4252395b4886e46778e53db7c","Type":"PlayerDataGroupSchema","SchemaDataPairs":[{"Key":"IncludeResourcesFolders","Value":"True"},{"Key":"IncludeBuildSettingsScenes","Value":"True"}]}},{"rid":1012,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"scene_scenes_all_ea5d4d944831b5303deb66b664bd2e25.bundle","InternalName":"79f2518976c47599628aad579c012657","FileSize":106056,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":50805,"AssetCount":1,"BundleDependencies":[{"DependencyBundle":{"rid":1015},"AssetDependencies":[{"rootAsset":{"rid":1028},"dependencyAsset":{"rid":1034}}],"ExpandedEfficiency":0.0839603990316391,"Efficiency":0.0839603990316391}],"Compression":"Lz4HC","CRC":3773962951,"Hash":{"serializedVersion":"2","Hash":"ea5d4d944831b5303deb66b664bd2e25"},"Group":{"rid":1002},"LoadPath":"http://172.23.112.1:HostingServicePort/scene_scenes_all_ea5d4d944831b5303deb66b664bd2e25.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1006}],"DependentBundles":[],"Dependencies":[{"rid":1015},{"rid":1005}],"ExpandedDependencies":[]}},{"rid":1013,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"9c54ec463bb28fc45af9c613c5130192","Type":"BundledAssetGroupSchema","SchemaDataPairs":[{"Key":"InternalBundleIdMode","Value":"GroupGuidProjectIdHash"},{"Key":"Compression","Value":"LZ4"},{"Key":"IncludeAddressInCatalog","Value":"True"},{"Key":"IncludeGUIDInCatalog","Value":"True"},{"Key":"IncludeLabelsInCatalog","Value":"True"},{"Key":"InternalIdNamingMode","Value":"FullPath"},{"Key":"AssetBundledCacheClearBehavior","Value":"ClearWhenSpaceIsNeededInCache"},{"Key":"IncludeInBuild","Value":"True"},{"Key":"BundledAssetProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider"},{"Key":"ForceUniqueProvider","Value":"False"},{"Key":"UseAssetBundleCache","Value":"True"},{"Key":"UseAssetBundleCrc","Value":"True"},{"Key":"UseAssetBundleCrcForCachedBundles","Value":"True"},{"Key":"UseUnityWebRequestForLocalBundles","Value":"False"},{"Key":"Timeout","Value":"0"},{"Key":"ChunkedTransfer","Value":"False"},{"Key":"RedirectLimit","Value":"-1"},{"Key":"RetryCount","Value":"0"},{"Key":"BuildPath","Value":"ServerData/StandaloneWindows64"},{"Key":"LoadPath","Value":"http://172.23.112.1:HostingServicePort"},{"Key":"PackingMode","Value":"PackTogether"},{"Key":"HostingServicesContentRoot","Value":"ServerData/StandaloneWindows64"},{"Key":"AssetBundleProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"},{"Key":"BundleNaming","Value":"AppendHash"},{"Key":"AssetLoadMode","Value":"RequestedAssetAndDependencies"}]}},{"rid":1014,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"bcaeb131eb189e944baddd01bb7cdd60","Type":"ContentUpdateGroupSchema","SchemaDataPairs":[{"Key":"StaticContent","Value":"False"}]}},{"rid":1015,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"folder_assets_assets/addressme_3876ae7ae6d93912555c4c93ef6e3fd7.bundle","InternalName":"6801756afe332320683f01526bb77e3d","FileSize":2525,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":0,"AssetCount":5,"BundleDependencies":[],"Compression":"Lz4HC","CRC":2362355840,"Hash":{"serializedVersion":"2","Hash":"3876ae7ae6d93912555c4c93ef6e3fd7"},"Group":{"rid":1003},"LoadPath":"http://172.23.112.1:HostingServicePort/folder_assets_assets/addressme_3876ae7ae6d93912555c4c93ef6e3fd7.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1044}],"DependentBundles":[{"rid":1012}],"Dependencies":[],"ExpandedDependencies":[]}},{"rid":1016,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"6f4860baa9cd6324a800c2db02eb3af7","Type":"BundledAssetGroupSchema","SchemaDataPairs":[{"Key":"InternalBundleIdMode","Value":"GroupGuidProjectIdHash"},{"Key":"Compression","Value":"LZ4"},{"Key":"IncludeAddressInCatalog","Value":"True"},{"Key":"IncludeGUIDInCatalog","Value":"True"},{"Key":"IncludeLabelsInCatalog","Value":"True"},{"Key":"InternalIdNamingMode","Value":"FullPath"},{"Key":"AssetBundledCacheClearBehavior","Value":"ClearWhenSpaceIsNeededInCache"},{"Key":"IncludeInBuild","Value":"True"},{"Key":"BundledAssetProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider"},{"Key":"ForceUniqueProvider","Value":"False"},{"Key":"UseAssetBundleCache","Value":"True"},{"Key":"UseAssetBundleCrc","Value":"True"},{"Key":"UseAssetBundleCrcForCachedBundles","Value":"True"},{"Key":"UseUnityWebRequestForLocalBundles","Value":"False"},{"Key":"Timeout","Value":"0"},{"Key":"ChunkedTransfer","Value":"False"},{"Key":"RedirectLimit","Value":"-1"},{"Key":"RetryCount","Value":"0"},{"Key":"BuildPath","Value":"ServerData/StandaloneWindows64"},{"Key":"LoadPath","Value":"http://172.23.112.1:HostingServicePort"},{"Key":"PackingMode","Value":"PackSeparately"},{"Key":"HostingServicesContentRoot","Value":"ServerData/StandaloneWindows64"},{"Key":"AssetBundleProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"},{"Key":"BundleNaming","Value":"AppendHash"},{"Key":"AssetLoadMode","Value":"RequestedAssetAndDependencies"}]}},{"rid":1017,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"afcbf579ec5d9d5478cfe2aac5d304d9","Type":"ContentUpdateGroupSchema","SchemaDataPairs":[{"Key":"StaticContent","Value":"False"}]}},{"rid":1018,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"prefabs_assets_texture_8d87ce5cdbde7dec1bdded52918590d0.bundle","InternalName":"c75e210455b9b7f32a8e902db35e6ee3","FileSize":20808,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":0,"AssetCount":8,"BundleDependencies":[],"Compression":"Lz4HC","CRC":2881872656,"Hash":{"serializedVersion":"2","Hash":"8d87ce5cdbde7dec1bdded52918590d0"},"Group":{"rid":1004},"LoadPath":"http://172.23.112.1:HostingServicePort/prefabs_assets_texture_8d87ce5cdbde7dec1bdded52918590d0.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1045}],"DependentBundles":[],"Dependencies":[],"ExpandedDependencies":[]}},{"rid":1019,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"prefabs_assets_cube_c0ed6a2e50d82be0ec7c62ef24d4c263.bundle","InternalName":"4114518594e249d85cf6788078b8f2ff","FileSize":5772,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":48280,"AssetCount":2,"BundleDependencies":[],"Compression":"Lz4HC","CRC":671008525,"Hash":{"serializedVersion":"2","Hash":"c0ed6a2e50d82be0ec7c62ef24d4c263"},"Group":{"rid":1004},"LoadPath":"http://172.23.112.1:HostingServicePort/prefabs_assets_cube_c0ed6a2e50d82be0ec7c62ef24d4c263.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1007}],"DependentBundles":[],"Dependencies":[{"rid":1005}],"ExpandedDependencies":[]}},{"rid":1020,"type":{"class":"BuildLayout/Bundle","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"prefabs_assets_ui_ccbaa28767c032e98a19b4b3856368a4.bundle","InternalName":"bd605777b0bb1e6517c21ee1bf69e06f","FileSize":4968,"BuildStatus":0,"ExpandedDependencyFileSize":0,"DependencyFileSize":0,"AssetCount":1,"BundleDependencies":[],"Compression":"Lz4HC","CRC":1215849707,"Hash":{"serializedVersion":"2","Hash":"ccbaa28767c032e98a19b4b3856368a4"},"Group":{"rid":1004},"LoadPath":"http://172.23.112.1:HostingServicePort/prefabs_assets_ui_ccbaa28767c032e98a19b4b3856368a4.bundle","Provider":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider","ResultType":"IAssetBundleResource","Files":[{"rid":1046}],"DependentBundles":[],"Dependencies":[],"ExpandedDependencies":[]}},{"rid":1021,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"9d98f03e9c5c3fd4483f65c1570b2eb5","Type":"BundledAssetGroupSchema","SchemaDataPairs":[{"Key":"InternalBundleIdMode","Value":"GroupGuidProjectIdHash"},{"Key":"Compression","Value":"LZ4"},{"Key":"IncludeAddressInCatalog","Value":"True"},{"Key":"IncludeGUIDInCatalog","Value":"True"},{"Key":"IncludeLabelsInCatalog","Value":"True"},{"Key":"InternalIdNamingMode","Value":"FullPath"},{"Key":"AssetBundledCacheClearBehavior","Value":"ClearWhenSpaceIsNeededInCache"},{"Key":"IncludeInBuild","Value":"True"},{"Key":"BundledAssetProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider"},{"Key":"ForceUniqueProvider","Value":"False"},{"Key":"UseAssetBundleCache","Value":"True"},{"Key":"UseAssetBundleCrc","Value":"True"},{"Key":"UseAssetBundleCrcForCachedBundles","Value":"True"},{"Key":"UseUnityWebRequestForLocalBundles","Value":"False"},{"Key":"Timeout","Value":"0"},{"Key":"ChunkedTransfer","Value":"False"},{"Key":"RedirectLimit","Value":"-1"},{"Key":"RetryCount","Value":"0"},{"Key":"BuildPath","Value":"ServerData/StandaloneWindows64"},{"Key":"LoadPath","Value":"http://172.23.112.1:HostingServicePort"},{"Key":"PackingMode","Value":"PackTogetherByLabel"},{"Key":"HostingServicesContentRoot","Value":"ServerData/StandaloneWindows64"},{"Key":"AssetBundleProviderType","Value":"UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider"},{"Key":"BundleNaming","Value":"AppendHash"},{"Key":"AssetLoadMode","Value":"RequestedAssetAndDependencies"}]}},{"rid":1022,"type":{"class":"BuildLayout/SchemaData","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"e057ce7e0e1b887469d62b11f40fe09f","Type":"ContentUpdateGroupSchema","SchemaDataPairs":[{"Key":"StaticContent","Value":"False"}]}},{"rid":1023,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-d14568fa0ba036eb39a103e016d31964/CAB-d14568fa0ba036eb39a103e016d31964","Bundle":{"rid":1005},"SubFiles":[{"rid":1047}],"Assets":[],"OtherAssets":[{"rid":1048}],"ExternalReferences":[],"WriteResultFilename":"","BundleObjectInfo":{"Size":180},"PreloadInfoSize":0,"MonoScriptCount":0,"MonoScriptSize":0}},{"rid":1024,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-c027b5d99f29eca69deb8d470fdba74d.sharedAssets","IsSerializedFile":true,"Size":14480}},{"rid":1025,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-c027b5d99f29eca69deb8d470fdba74d.sharedAssets.resS","IsSerializedFile":false,"Size":21888}},{"rid":1026,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-c027b5d99f29eca69deb8d470fdba74d","IsSerializedFile":true,"Size":39248}},{"rid":1027,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-c027b5d99f29eca69deb8d470fdba74d.resS","IsSerializedFile":false,"Size":131232}},{"rid":1028,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"9fc0d4010bbf28b4594072e72b8655ab","AssetPath":"Assets/Scenes/SampleScene.unity","InternalId":"Assets/Scenes/SampleScene.unity","AssetHash":{"serializedVersion":"2","Hash":"e9c79b65601949217813c7618ba03248"},"Objects":[{"LocalIdentifierInFile":0,"ObjectName":"Main","ComponentName":"","AssetType":25,"SerializedSize":0,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[0]},{"AssetId":1,"ObjectIds":[0]},{"AssetId":2,"ObjectIds":[0]},{"AssetId":3,"ObjectIds":[0]},{"AssetId":4,"ObjectIds":[0]},{"AssetId":6,"ObjectIds":[0]}]},{"LocalIdentifierInFile":1,"ObjectName":"Cubemap","ComponentName":"","AssetType":29,"SerializedSize":280,"StreamedSize":131232,"References":[]},{"LocalIdentifierInFile":2,"ObjectName":"GameObject","ComponentName":"","AssetType":24,"SerializedSize":485,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":3,"ObjectName":"Transform","ComponentName":"","AssetType":36,"SerializedSize":340,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":4,"ObjectName":"Camera","ComponentName":"","AssetType":30,"SerializedSize":184,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":5,"ObjectName":"MeshRenderer","ComponentName":"","AssetType":37,"SerializedSize":312,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":6,"ObjectName":"MeshFilter","ComponentName":"","AssetType":38,"SerializedSize":48,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":7,"ObjectName":"BoxCollider","ComponentName":"","AssetType":40,"SerializedSize":136,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":8,"ObjectName":"AudioListener","ComponentName":"","AssetType":31,"SerializedSize":16,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":9,"ObjectName":"RenderSettings","ComponentName":"","AssetType":34,"SerializedSize":333,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":10,"ObjectName":"Light","ComponentName":"","AssetType":32,"SerializedSize":264,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":11,"ObjectName":"LightmapSettings","ComponentName":"","AssetType":35,"SerializedSize":72,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":12,"ObjectName":"NavMeshSettings","ComponentName":"","AssetType":33,"SerializedSize":12,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":13,"ObjectName":"SceneObject","ComponentName":"","AssetType":25,"SerializedSize":304,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":14,"ObjectName":"MonoBehaviour","ComponentName":"","AssetType":26,"SerializedSize":1300,"StreamedSize":0,"References":[]}],"MainAssetType":23,"GroupGuid":"5d8d3b81860b62b4b964bed8a1deef7d","AddressableName":"Assets/Scenes/SampleScene.unity","Labels":[],"SerializedSize":4086,"StreamedSize":131232,"File":{"rid":1006},"Bundle":{"rid":1012},"InternalReferencedOtherAssets":[{"rid":1029},{"rid":1030},{"rid":1031},{"rid":1032},{"rid":1033}],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[{"rid":1034}],"ReferencingAssets":[]}},{"rid":1029,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"0000000000000000f000000000000000","AssetPath":"Resources/unity_builtin_extra","File":{"rid":1006},"Objects":[{"LocalIdentifierInFile":10304,"ObjectName":"","ComponentName":"","AssetType":19,"SerializedSize":268,"StreamedSize":0,"References":[]}],"MainAssetType":0,"ReferencingAssets":[{"rid":1028}],"ObjectCount":1,"SerializedSize":268,"StreamedSize":0}},{"rid":1030,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"7092a5e42eb254241828c7ff32096d98","AssetPath":"Assets/Materials/implicitTeal!.mat","File":{"rid":1006},"Objects":[{"LocalIdentifierInFile":2100000,"ObjectName":"implicitTeal!","ComponentName":"","AssetType":19,"SerializedSize":912,"StreamedSize":0,"References":[{"AssetId":3,"ObjectIds":[0]}]}],"MainAssetType":19,"ReferencingAssets":[{"rid":1028}],"ObjectCount":1,"SerializedSize":912,"StreamedSize":0}},{"rid":1031,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"c4904135a42b0d3468d69deded7ca5b8","AssetPath":"Assets/Materials/implicitYellow!.mat","File":{"rid":1006},"Objects":[{"LocalIdentifierInFile":2100000,"ObjectName":"implicitYellow!","ComponentName":"","AssetType":19,"SerializedSize":912,"StreamedSize":0,"References":[{"AssetId":4,"ObjectIds":[0]}]}],"MainAssetType":19,"ReferencingAssets":[{"rid":1028}],"ObjectCount":1,"SerializedSize":912,"StreamedSize":0}},{"rid":1032,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"5457acd3d768cb043b8ad79c19dc24dc","AssetPath":"Assets/implicitTeal!.png","File":{"rid":1006},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"implicitTeal!","ComponentName":"","AssetType":13,"SerializedSize":232,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"ReferencingAssets":[{"rid":1028}],"ObjectCount":1,"SerializedSize":232,"StreamedSize":10936}},{"rid":1033,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"d0fa037f07425224a9117ec27f735121","AssetPath":"Assets/implicitYellow!.jpg","File":{"rid":1006},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"implicitYellow!","ComponentName":"","AssetType":13,"SerializedSize":232,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"ReferencingAssets":[{"rid":1028}],"ObjectCount":1,"SerializedSize":232,"StreamedSize":10936}},{"rid":1034,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"a2d7efa72e470f44fbbf5b6c62edd603","AssetPath":"Assets/AddressMe/brown.bmp","InternalId":"Assets/AddressMe/brown.bmp","AssetHash":{"serializedVersion":"2","Hash":"18a9569a3622494a6a0c90474e81fb12"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"brown","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"d47234d6c7bab3545a5185bb31ffbde5","AddressableName":"Assets/AddressMe/brown.bmp","Labels":[],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1044},"Bundle":{"rid":1015},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[{"rid":1028}]}},{"rid":1035,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-4d4a56f8e64de1cb9f42462631c1236e","IsSerializedFile":true,"Size":17435}},{"rid":1036,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-4d4a56f8e64de1cb9f42462631c1236e.resS","IsSerializedFile":false,"Size":21888}},{"rid":1037,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"744799e27e42d514c9cb2a5ea807f193","AssetPath":"Assets/Prefabs/TealCube.prefab","InternalId":"Assets/Prefabs/TealCube.prefab","AssetHash":{"serializedVersion":"2","Hash":"1ed630420ca6c5b2e4b6cec470320552"},"Objects":[{"LocalIdentifierInFile":4190784972503110528,"ObjectName":"","ComponentName":"BoxCollider","AssetType":27,"SerializedSize":68,"StreamedSize":0,"References":[{"AssetId":4,"ObjectIds":[1]}]},{"LocalIdentifierInFile":234782926575669124,"ObjectName":"TealCube","ComponentName":"GameObject","AssetType":27,"SerializedSize":71,"StreamedSize":0,"References":[{"AssetId":4,"ObjectIds":[3,0,4,2]}]},{"LocalIdentifierInFile":6765074669866141958,"ObjectName":"","ComponentName":"MeshFilter","AssetType":27,"SerializedSize":24,"StreamedSize":0,"References":[{"AssetId":4,"ObjectIds":[1]}]},{"LocalIdentifierInFile":3415956576104447772,"ObjectName":"","ComponentName":"Transform","AssetType":27,"SerializedSize":68,"StreamedSize":0,"References":[{"AssetId":4,"ObjectIds":[1]}]},{"LocalIdentifierInFile":5798894485160685379,"ObjectName":"","ComponentName":"MeshRenderer","AssetType":27,"SerializedSize":156,"StreamedSize":0,"References":[{"AssetId":4,"ObjectIds":[1]},{"AssetId":2,"ObjectIds":[0]}]}],"MainAssetType":24,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/Prefabs/TealCube.prefab","Labels":["cube"],"SerializedSize":387,"StreamedSize":0,"File":{"rid":1007},"Bundle":{"rid":1019},"InternalReferencedOtherAssets":[{"rid":1041},{"rid":1042}],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1038,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"34e42f4edf1ebc848abab0de284d7962","AssetPath":"Assets/Prefabs/YellowCube.prefab","InternalId":"Assets/Prefabs/YellowCube.prefab","AssetHash":{"serializedVersion":"2","Hash":"bf8293fede2414137142ccd0a4f82efe"},"Objects":[{"LocalIdentifierInFile":3291054788880641099,"ObjectName":"","ComponentName":"BoxCollider","AssetType":27,"SerializedSize":68,"StreamedSize":0,"References":[{"AssetId":5,"ObjectIds":[4]}]},{"LocalIdentifierInFile":7432276557790250088,"ObjectName":"","ComponentName":"Transform","AssetType":27,"SerializedSize":68,"StreamedSize":0,"References":[{"AssetId":5,"ObjectIds":[4]}]},{"LocalIdentifierInFile":81098395676439687,"ObjectName":"","ComponentName":"MeshRenderer","AssetType":27,"SerializedSize":156,"StreamedSize":0,"References":[{"AssetId":1,"ObjectIds":[0]},{"AssetId":5,"ObjectIds":[4]}]},{"LocalIdentifierInFile":3212583191424844625,"ObjectName":"","ComponentName":"MeshFilter","AssetType":27,"SerializedSize":24,"StreamedSize":0,"References":[{"AssetId":5,"ObjectIds":[4]}]},{"LocalIdentifierInFile":5365089480673652783,"ObjectName":"YellowCube","ComponentName":"GameObject","AssetType":27,"SerializedSize":75,"StreamedSize":0,"References":[{"AssetId":5,"ObjectIds":[2,3,0,1]}]}],"MainAssetType":24,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/Prefabs/YellowCube.prefab","Labels":["cube"],"SerializedSize":391,"StreamedSize":0,"File":{"rid":1007},"Bundle":{"rid":1019},"InternalReferencedOtherAssets":[{"rid":1040},{"rid":1039}],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1039,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"5457acd3d768cb043b8ad79c19dc24dc","AssetPath":"Assets/implicitTeal!.png","File":{"rid":1007},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"implicitTeal!","ComponentName":"","AssetType":13,"SerializedSize":220,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"ReferencingAssets":[{"rid":1038}],"ObjectCount":1,"SerializedSize":220,"StreamedSize":10936}},{"rid":1040,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"7092a5e42eb254241828c7ff32096d98","AssetPath":"Assets/Materials/implicitTeal!.mat","File":{"rid":1007},"Objects":[{"LocalIdentifierInFile":2100000,"ObjectName":"implicitTeal!","ComponentName":"","AssetType":19,"SerializedSize":912,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[0]}]}],"MainAssetType":19,"ReferencingAssets":[{"rid":1038}],"ObjectCount":1,"SerializedSize":912,"StreamedSize":0}},{"rid":1041,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"c4904135a42b0d3468d69deded7ca5b8","AssetPath":"Assets/Materials/implicitYellow!.mat","File":{"rid":1007},"Objects":[{"LocalIdentifierInFile":2100000,"ObjectName":"implicitYellow!","ComponentName":"","AssetType":19,"SerializedSize":912,"StreamedSize":0,"References":[{"AssetId":3,"ObjectIds":[0]}]}],"MainAssetType":19,"ReferencingAssets":[{"rid":1037}],"ObjectCount":1,"SerializedSize":912,"StreamedSize":0}},{"rid":1042,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"d0fa037f07425224a9117ec27f735121","AssetPath":"Assets/implicitYellow!.jpg","File":{"rid":1007},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"implicitYellow!","ComponentName":"","AssetType":13,"SerializedSize":220,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"ReferencingAssets":[{"rid":1037}],"ObjectCount":1,"SerializedSize":220,"StreamedSize":10936}},{"rid":1043,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-8e8d8da04f7dd3a231873dfcab1b62f3/CAB-8e8d8da04f7dd3a231873dfcab1b62f3","Bundle":{"rid":1008},"SubFiles":[{"rid":1049},{"rid":1050}],"Assets":[{"rid":1051},{"rid":1052},{"rid":1053},{"rid":1054},{"rid":1055}],"OtherAssets":[],"ExternalReferences":[],"WriteResultFilename":"","BundleObjectInfo":{"Size":412},"PreloadInfoSize":0,"MonoScriptCount":0,"MonoScriptSize":0}},{"rid":1044,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-d4e276406622396f8099dcb9c2505563/CAB-d4e276406622396f8099dcb9c2505563","Bundle":{"rid":1015},"SubFiles":[{"rid":1056},{"rid":1057}],"Assets":[{"rid":1034},{"rid":1058},{"rid":1059},{"rid":1060},{"rid":1061}],"OtherAssets":[],"ExternalReferences":[],"WriteResultFilename":"","BundleObjectInfo":{"Size":460},"PreloadInfoSize":0,"MonoScriptCount":0,"MonoScriptSize":0}},{"rid":1045,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-91b8bd4356598a7b8c45c1e31fb3ed82/CAB-91b8bd4356598a7b8c45c1e31fb3ed82","Bundle":{"rid":1018},"SubFiles":[{"rid":1062},{"rid":1063}],"Assets":[{"rid":1064},{"rid":1065},{"rid":1066},{"rid":1067},{"rid":1068},{"rid":1069},{"rid":1070},{"rid":1071}],"OtherAssets":[],"ExternalReferences":[],"WriteResultFilename":"","BundleObjectInfo":{"Size":576},"PreloadInfoSize":0,"MonoScriptCount":0,"MonoScriptSize":0}},{"rid":1046,"type":{"class":"BuildLayout/File","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"archive:/CAB-c44f37c87e67db5b898970667831d4f2/CAB-c44f37c87e67db5b898970667831d4f2","Bundle":{"rid":1020},"SubFiles":[{"rid":1072}],"Assets":[{"rid":1073}],"OtherAssets":[],"ExternalReferences":[],"WriteResultFilename":"","BundleObjectInfo":{"Size":360},"PreloadInfoSize":0,"MonoScriptCount":4,"MonoScriptSize":352}},{"rid":1047,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-d14568fa0ba036eb39a103e016d31964","IsSerializedFile":true,"Size":148872}},{"rid":1048,"type":{"class":"BuildLayout/DataFromOtherAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"AssetGuid":"0000000000000000f000000000000000","AssetPath":"Resources/unity_builtin_extra","File":{"rid":1023},"Objects":[{"LocalIdentifierInFile":46,"ObjectName":"","ComponentName":"","AssetType":9,"SerializedSize":49372,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[2]}]},{"LocalIdentifierInFile":106,"ObjectName":"","ComponentName":"","AssetType":9,"SerializedSize":6976,"StreamedSize":0,"References":[]},{"LocalIdentifierInFile":6,"ObjectName":"","ComponentName":"","AssetType":9,"SerializedSize":13120,"StreamedSize":0,"References":[]}],"MainAssetType":0,"ReferencingAssets":[],"ObjectCount":3,"SerializedSize":69468,"StreamedSize":0}},{"rid":1049,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-8e8d8da04f7dd3a231873dfcab1b62f3","IsSerializedFile":true,"Size":5912}},{"rid":1050,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-8e8d8da04f7dd3a231873dfcab1b62f3.resS","IsSerializedFile":false,"Size":54720}},{"rid":1051,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"2af53d6b0e54e1948ab772da8f3fad40","AssetPath":"Assets/teal.png","InternalId":"Assets/teal.png","AssetHash":{"serializedVersion":"2","Hash":"a9fa96a37d9a749b1e4350315c49a42f"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"teal","ComponentName":"","AssetType":13,"SerializedSize":208,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"c12cce9779a49514599e18b13e90336e","AddressableName":"Assets/teal.png","Labels":[],"SerializedSize":208,"StreamedSize":10936,"File":{"rid":1043},"Bundle":{"rid":1008},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1052,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"5c1292d7b49010844bf75a5f10dcacaa","AssetPath":"Assets/yellow.jpg","InternalId":"Assets/yellow.jpg","AssetHash":{"serializedVersion":"2","Hash":"4dad1af8488a1f3a849ee76af245eb48"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"yellow","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"c12cce9779a49514599e18b13e90336e","AddressableName":"Assets/yellow.jpg","Labels":[],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1043},"Bundle":{"rid":1008},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1053,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"8126c81bf6cf5b44095d0b6964b89802","AssetPath":"Assets/pink.bmp","InternalId":"Assets/pink.bmp","AssetHash":{"serializedVersion":"2","Hash":"ffc1c75c0b33d0629dc9054a81b2aee1"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"pink","ComponentName":"","AssetType":13,"SerializedSize":208,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"c12cce9779a49514599e18b13e90336e","AddressableName":"Assets/pink.bmp","Labels":[],"SerializedSize":208,"StreamedSize":10936,"File":{"rid":1043},"Bundle":{"rid":1008},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1054,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"6014e59ad992a31428721b3bf8c76093","AssetPath":"Assets/peuce.bmp","InternalId":"Assets/peuce.bmp","AssetHash":{"serializedVersion":"2","Hash":"b90b79ccbd789f6b3c60841c02da3336"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"peuce","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"c12cce9779a49514599e18b13e90336e","AddressableName":"Assets/peuce.bmp","Labels":[],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1043},"Bundle":{"rid":1008},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1055,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"921d94828f4ef1c4db265aa1083af440","AssetPath":"Assets/perriwinkle.png","InternalId":"Assets/perriwinkle.png","AssetHash":{"serializedVersion":"2","Hash":"3933d48d09509fc1423ee19920f73b1e"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"perriwinkle","ComponentName":"","AssetType":13,"SerializedSize":216,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"c12cce9779a49514599e18b13e90336e","AddressableName":"Assets/perriwinkle.png","Labels":[],"SerializedSize":216,"StreamedSize":10936,"File":{"rid":1043},"Bundle":{"rid":1008},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1056,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-d4e276406622396f8099dcb9c2505563","IsSerializedFile":true,"Size":5968}},{"rid":1057,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-d4e276406622396f8099dcb9c2505563.resS","IsSerializedFile":false,"Size":54720}},{"rid":1058,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"44a5ed006718c5a42b73e57b098c2d37","AssetPath":"Assets/AddressMe/green.bmp","InternalId":"Assets/AddressMe/green.bmp","AssetHash":{"serializedVersion":"2","Hash":"335e0899ed7eb7142300dc6affc707be"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"green","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"d47234d6c7bab3545a5185bb31ffbde5","AddressableName":"Assets/AddressMe/green.bmp","Labels":[],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1044},"Bundle":{"rid":1015},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1059,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"8100e0e0b0163354e9fbb038a48bd69a","AssetPath":"Assets/AddressMe/purple.png","InternalId":"Assets/AddressMe/purple.png","AssetHash":{"serializedVersion":"2","Hash":"ba61d130b3a4955cf1b7c17ace0f75da"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"purple","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"d47234d6c7bab3545a5185bb31ffbde5","AddressableName":"Assets/AddressMe/purple.png","Labels":[],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1044},"Bundle":{"rid":1015},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1060,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"0ae844215a226bb4eb75f27f5f1892d6","AssetPath":"Assets/AddressMe/red.jpg","InternalId":"Assets/AddressMe/red.jpg","AssetHash":{"serializedVersion":"2","Hash":"f97fa278fea9b02b87432ccfeef0dc2d"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"red","ComponentName":"","AssetType":13,"SerializedSize":208,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"d47234d6c7bab3545a5185bb31ffbde5","AddressableName":"Assets/AddressMe/red.jpg","Labels":[],"SerializedSize":208,"StreamedSize":10936,"File":{"rid":1044},"Bundle":{"rid":1015},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1061,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"507e6278e6c338c45a1bd84deb659df4","AssetPath":"Assets/AddressMe/stormcloud.bmp","InternalId":"Assets/AddressMe/stormcloud.bmp","AssetHash":{"serializedVersion":"2","Hash":"2bc84ab8e7da14061fb38392098c7898"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"stormcloud","ComponentName":"","AssetType":13,"SerializedSize":216,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"d47234d6c7bab3545a5185bb31ffbde5","AddressableName":"Assets/AddressMe/stormcloud.bmp","Labels":[],"SerializedSize":216,"StreamedSize":10936,"File":{"rid":1044},"Bundle":{"rid":1015},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1062,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-91b8bd4356598a7b8c45c1e31fb3ed82","IsSerializedFile":true,"Size":6792}},{"rid":1063,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-91b8bd4356598a7b8c45c1e31fb3ed82.resS","IsSerializedFile":false,"Size":87552}},{"rid":1064,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"9a97bac9c29e9a94580e8490f868dfa2","AssetPath":"Assets/khaki.png","InternalId":"Assets/khaki.png","AssetHash":{"serializedVersion":"2","Hash":"3b85922056a8d5da1134c99aaa75fab7"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"khaki","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/khaki.png","Labels":["texture"],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1065,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"b6dfecf011d612845ba1f6dbad7b7ade","AssetPath":"Assets/lavender.gif","InternalId":"Assets/lavender.gif","AssetHash":{"serializedVersion":"2","Hash":"4a8f273a54a7d33827d6836b5142079a"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"lavender","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/lavender.gif","Labels":["texture"],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1066,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"c5a39000b73cc2441b5c64d9422d48ac","AssetPath":"Assets/darkgray.png","InternalId":"Assets/darkgray.png","AssetHash":{"serializedVersion":"2","Hash":"3fa5d04e1a708679e8b4bc6a546f47bf"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"darkgray","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/darkgray.png","Labels":["texture"],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1067,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"6077ae9de566b2e46bcb03f8799e546d","AssetPath":"Assets/black.png","InternalId":"Assets/black.png","AssetHash":{"serializedVersion":"2","Hash":"bbf1904c9bf966a755ea8ca6977f1af2"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"black","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/black.png","Labels":["texture"],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1068,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"ee8816f23e98d1044af8942ccb57b5c8","AssetPath":"Assets/lightgray.jpg","InternalId":"Assets/lightgray.jpg","AssetHash":{"serializedVersion":"2","Hash":"d07495270663c141c7ccc3dd7de59ab2"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"lightgray","ComponentName":"","AssetType":13,"SerializedSize":216,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/lightgray.jpg","Labels":["texture"],"SerializedSize":216,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1069,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"a27161efb063ff94fb448096ceb633a7","AssetPath":"Assets/blue.gif","InternalId":"Assets/blue.gif","AssetHash":{"serializedVersion":"2","Hash":"a4daa30f26add9c90b11cf5e6eaee69b"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"blue","ComponentName":"","AssetType":13,"SerializedSize":208,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/blue.gif","Labels":["texture"],"SerializedSize":208,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1070,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"94c48e51d9152f143a202685b2ca4d7f","AssetPath":"Assets/maroon.png","InternalId":"Assets/maroon.png","AssetHash":{"serializedVersion":"2","Hash":"3a222e9034a0139701a8fb8a46666bea"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"maroon","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/maroon.png","Labels":["texture"],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1071,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"bbdcd91b39211ad4ca869414266bc3cc","AssetPath":"Assets/orange.jpg","InternalId":"Assets/orange.jpg","AssetHash":{"serializedVersion":"2","Hash":"87952652d81d5d6828b70e4faf1f429e"},"Objects":[{"LocalIdentifierInFile":2800000,"ObjectName":"orange","ComponentName":"","AssetType":13,"SerializedSize":212,"StreamedSize":10936,"References":[]}],"MainAssetType":13,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/orange.jpg","Labels":["texture"],"SerializedSize":212,"StreamedSize":10936,"File":{"rid":1045},"Bundle":{"rid":1018},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}},{"rid":1072,"type":{"class":"BuildLayout/SubFile","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Name":"CAB-c44f37c87e67db5b898970667831d4f2","IsSerializedFile":true,"Size":14784}},{"rid":1073,"type":{"class":"BuildLayout/ExplicitAsset","ns":"UnityEditor.AddressableAssets.Build.Layout","asm":"Unity.Addressables.Editor"},"data":{"Guid":"3dc3dcd61f360fa49a2cf87380d20b18","AssetPath":"Assets/Prefabs/Canvas.prefab","InternalId":"Assets/Prefabs/Canvas.prefab","AssetHash":{"serializedVersion":"2","Hash":"472f977d7155c4da06af06af2db9c054"},"Objects":[{"LocalIdentifierInFile":609859160564680420,"ObjectName":"","ComponentName":"GameObject","AssetType":27,"SerializedSize":59,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[9,1,3]}]},{"LocalIdentifierInFile":4273724110659978750,"ObjectName":"","ComponentName":"RectTransform","AssetType":27,"SerializedSize":108,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[0,5]}]},{"LocalIdentifierInFile":-2798622644676131048,"ObjectName":"","ComponentName":"TwoDLoader","AssetType":27,"SerializedSize":48,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[6]}]},{"LocalIdentifierInFile":5052905641891018281,"ObjectName":"","ComponentName":"CanvasRenderer","AssetType":27,"SerializedSize":13,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[0]}]},{"LocalIdentifierInFile":971179268330282317,"ObjectName":"","ComponentName":"GraphicRaycaster","AssetType":27,"SerializedSize":44,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[6]}]},{"LocalIdentifierInFile":2157239411829978522,"ObjectName":"","ComponentName":"RectTransform","AssetType":27,"SerializedSize":120,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[1,6]}]},{"LocalIdentifierInFile":4355158816877972265,"ObjectName":"Canvas","ComponentName":"GameObject","AssetType":27,"SerializedSize":83,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[2,4,8,5,7]}]},{"LocalIdentifierInFile":8997632017789578071,"ObjectName":"","ComponentName":"Canvas","AssetType":27,"SerializedSize":63,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[6]}]},{"LocalIdentifierInFile":974037602816338885,"ObjectName":"","ComponentName":"CanvasScaler","AssetType":27,"SerializedSize":80,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[6]}]},{"LocalIdentifierInFile":3314305043817816148,"ObjectName":"","ComponentName":"","AssetType":0,"SerializedSize":136,"StreamedSize":0,"References":[{"AssetId":0,"ObjectIds":[0]}]}],"MainAssetType":24,"GroupGuid":"023e6b69bad1d024c92a88d3a7e6bbc2","AddressableName":"Assets/Prefabs/Canvas.prefab","Labels":["ui"],"SerializedSize":754,"StreamedSize":0,"File":{"rid":1046},"Bundle":{"rid":1020},"InternalReferencedOtherAssets":[],"InternalReferencedExplicitAssets":[],"ExternallyReferencedAssets":[],"ReferencingAssets":[]}}]}} \ No newline at end of file diff --git a/Tests/Editor/Fixtures/buildlayout1.json.meta b/Tests/Editor/Fixtures/buildlayout1.json.meta new file mode 100644 index 00000000..675938a5 --- /dev/null +++ b/Tests/Editor/Fixtures/buildlayout1.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 61b944170264b7047862dda83989b7c5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor/OptionalPackages/Diagnostics.meta b/Tests/Editor/OptionalPackages/Diagnostics.meta deleted file mode 100644 index 507a8278..00000000 --- a/Tests/Editor/OptionalPackages/Diagnostics.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 9cdab2adb7ca4d58b15316174d7b319d -timeCreated: 1699028377 \ No newline at end of file diff --git a/Tests/Editor/OptionalPackages/Diagnostics/Profiler.meta b/Tests/Editor/OptionalPackages/Diagnostics/Profiler.meta deleted file mode 100644 index 8a9c1a87..00000000 --- a/Tests/Editor/OptionalPackages/Diagnostics/Profiler.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: a0c64c476444f864f925af4bf1b5e5cc -timeCreated: 1698865046 \ No newline at end of file diff --git a/Tests/Editor/OptionalPackages/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs b/Tests/Editor/OptionalPackages/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs deleted file mode 100644 index 47dbad4a..00000000 --- a/Tests/Editor/OptionalPackages/Diagnostics/Profiler/AddressablesProfilerDetailsViewTests.cs +++ /dev/null @@ -1,99 +0,0 @@ -#if ENABLE_ADDRESSABLE_PROFILER && UNITY_2022_2_OR_NEWER -using System.Collections.Generic; -using NUnit.Framework; -using UnityEditor.AddressableAssets.Build.Layout; -using UnityEditor.AddressableAssets.Diagnostics; -using UnityEngine; -using UnityEngine.ResourceManagement.Profiling; - -namespace UnityEditor.AddressableAssets.Tests.Editor.OptionalPackages.Diagnostics.Profiler -{ - public class AddressablesProfilerDetailsViewTests - { - private static BuildLayout.ExplicitAsset m_TestAsset; - AddressablesProfilerDetailsView m_AddressablesProfilerDetailsView; - - [SetUp] - public void SetUp() - { - m_AddressablesProfilerDetailsView = new AddressablesProfilerDetailsView(null); - // this is broken. Can we use Moq? What do we want to achieve here? - - m_TestAsset = new BuildLayout.ExplicitAsset - { - AddressableName = "MainScene", - AssetPath = "Assets/Scenes/MainScene.unity", - Guid = new Hash128().ToString() - }; - } - - [TearDown] - public void TearDown() - { - m_AddressablesProfilerDetailsView.Dispose(); - } - - [Test] - public void Test_GetAssetDataForFrameData_HasData() - { - AssetFrameData frameData = new AssetFrameData - { - AssetCode = 1, - BundleCode = 10, - Status = ContentStatus.Downloading - }; - var bundleCodeToData = GetBundleCodeToData(); - var retVal = m_AddressablesProfilerDetailsView.GetAssetDataForFrameData(m_TestAsset, frameData, bundleCodeToData); - Assert.AreEqual(ContentStatus.Downloading, retVal.Status); - Assert.AreEqual(bundleCodeToData[10], retVal.Parent); - Assert.AreEqual(m_TestAsset.AssetPath, retVal.AssetPath); - Assert.AreEqual(m_TestAsset.AddressableName, retVal.Name); - Assert.AreEqual(m_TestAsset.Guid, retVal.AssetGuid); - } - - [Test] - public void Test_GetAssetDataForFrameData_NoData() - { - // the BundleCode is used to lookup the asset frame data, so since these - // don't match we expect to get null - AssetFrameData frameData = new AssetFrameData - { - AssetCode = 1, - BundleCode = 20, - Status = ContentStatus.Downloading - }; - var retVal = m_AddressablesProfilerDetailsView.GetAssetDataForFrameData(m_TestAsset, frameData, GetBundleCodeToData()); - Assert.IsNull(retVal); - } - - private Dictionary GetBundleCodeToData() => new() - { - { 10, new BundleData(new BuildLayout.Bundle(), new BundleFrameData()) } - }; - - [Test] - [TestCase(null)] - [TestCase("Assets/Cube.prefab")] - public void Test_ExplicitAssetsHaveUniqueTreeViewIDs(string assetPath1) - { - // These two GUID strings evaluate to the same hashcode. - var cube = new BuildLayout.ExplicitAsset - { - AssetPath = assetPath1, - Guid = "bf1daffab47bbb34587f194ac0dcc1bf" - }; - - var sphere = new BuildLayout.ExplicitAsset - { - AssetPath = "Assets/Sphere.prefab", - Guid = "61a57bae3cb0cec499b6e6b8db8f4008" - }; - - var cubeData = new AssetData(cube); - var sphereData = new AssetData(sphere); - - Assert.AreNotEqual(cubeData.TreeViewID, sphereData.TreeViewID, "Explicit assets should have unique TreeViewIds."); - } - } -} -#endif diff --git a/Tests/Editor/SerializationTests.cs b/Tests/Editor/SerializationTests.cs index 3fab40d3..f034105d 100644 --- a/Tests/Editor/SerializationTests.cs +++ b/Tests/Editor/SerializationTests.cs @@ -55,11 +55,7 @@ public void Setup() var defaultGroup = Settings.DefaultGroup; // the way our package isolation tests work the tests are built into their own package // which means we have to load expected files from a different location - m_PackagePath = "Packages/com.unity.addressables/Tests/Editor"; - if (Directory.Exists("Packages/com.unity.addressables.tests")) - { - m_PackagePath = "Packages/com.unity.addressables.tests/Tests/Editor"; - } + m_PackagePath = AddressablesTestUtility.GetPackagePath() + "/Tests/Editor"; } internal string GetExpectedPath(string filename) diff --git a/Tests/Runtime/AddressablesIntegrationTests.cs b/Tests/Runtime/AddressablesIntegrationTests.cs index 81fbaf23..5670cd9f 100644 --- a/Tests/Runtime/AddressablesIntegrationTests.cs +++ b/Tests/Runtime/AddressablesIntegrationTests.cs @@ -77,10 +77,20 @@ public void TearDown() { if (m_Addressables != null) { + Assert.AreEqual(0, m_Addressables.ResourceManager.DeferredCompleteCallbacksCount); + Assert.AreEqual(0, m_Addressables.ResourceManager.DeferredCallbackCount); + Assert.AreEqual(m_StartingOpCount, m_Addressables.ResourceManager.OperationCacheCount); Assert.AreEqual(m_StartingTrackedHandleCount, m_Addressables.TrackedHandleCount, $"Starting tracked handle count [{m_StartingInstanceCount}], not equal to current tracked handle count [{m_Addressables.TrackedHandleCount}]"); Assert.AreEqual(m_StartingInstanceCount, m_Addressables.ResourceManager.InstanceOperationCount); + + //If we had left deferred callbacks left then make sure to update + if (m_Addressables.ResourceManager.DeferredCallbackCount > 0 + || m_Addressables.ResourceManager.DeferredCompleteCallbacksCount > 0) + { + m_Addressables.ResourceManager.Update(Time.unscaledDeltaTime); + } } PostTearDownEvent?.Invoke(); diff --git a/Tests/Runtime/AddressablesIntegrationTestsImpl.cs b/Tests/Runtime/AddressablesIntegrationTestsImpl.cs index 2cef9e03..44edb190 100644 --- a/Tests/Runtime/AddressablesIntegrationTestsImpl.cs +++ b/Tests/Runtime/AddressablesIntegrationTestsImpl.cs @@ -36,6 +36,23 @@ namespace AddressableAssetsIntegrationTests { internal abstract partial class AddressablesIntegrationTests : IPrebuildSetup { + class FakeTypedOperation : AsyncOperationBase + { + public FakeTypedOperation() + { + m_RM = new ResourceManager(); + } + + public object GetResultAsObject() + { + return null; + } + + protected override void Execute() + { + } + } + [UnityTest] public IEnumerator AsyncCache_IsCleaned_OnFailedOperation() { @@ -72,7 +89,12 @@ public IEnumerator LoadResourceLocations_ValidKeyDoesNotThrow() yield return Init(); //Test - Assert.DoesNotThrow(() => { m_Addressables.LoadResourceLocationsAsync(AddressablesTestUtility.GetPrefabLabel("BASE"), typeof(GameObject)); }); + Assert.DoesNotThrow(() => { + var handle = m_Addressables.LoadResourceLocationsAsync(AddressablesTestUtility.GetPrefabLabel("BASE"), typeof(GameObject)); + handle.Release(); + }); + + yield return null; //< Process deferred callback } [UnityTest] @@ -348,7 +370,6 @@ public IEnumerator InvalidKeyException_LoadAssets_MixedTypesGivesCorrectError() InvalidKeyException expectedEx = new InvalidKeyException(keys, typeof(GameObject)); string message = handle.OperationException.Message; - string types = "Types=System.String, System.Int32"; string expected = expectedEx.FormatMessage(InvalidKeyException.Format.MultipleTypesRequested); bool equalOne = message == expected; @@ -611,12 +632,48 @@ public IEnumerator CanLoadTextureAsSprite() op.Release(); } + class AtlasSpriteProviderStub : AtlasSpriteProvider + { + public bool provideWasCalled = false; + public bool releaseWasCalled = false; + public override string ProviderId => nameof(AtlasSpriteProviderStub); + public override void Provide(ProvideHandle providerInterface) + { + provideWasCalled = true; + var sprite = Sprite.Create(new Texture2D(32, 32), new Rect(0, 0, 1, 1), new Vector2(0, 0)); + providerInterface.Complete(sprite, true, null); + } + public override void Release(IResourceLocation location, object obj) + { + if (obj is Sprite sprite) + Object.Destroy(sprite); + releaseWasCalled = true; + } + } [UnityTest] - public IEnumerator CanLoadSpriteByName() + public IEnumerator AtlasSpriteProviderIsCalledForProvideAndRelease() { //Setup yield return Init(); + var rm = m_Addressables.ResourceManager; + var prov = new AtlasSpriteProviderStub(); + rm.ResourceProviders.Insert(0, prov); + var handle = rm.ProvideResource(new ResourceLocationBase("", "id", nameof(AtlasSpriteProviderStub), typeof(Sprite))); + while (!handle.IsDone) + yield return null; + + Assert.IsTrue(prov.provideWasCalled); + handle.Release(); + Assert.IsTrue(prov.releaseWasCalled); + rm.ResourceProviders.RemoveAt(0); + } + + [UnityTest] + public IEnumerator CanLoadSpriteByName() + { + //Setup + yield return Init(); var op = m_Addressables.LoadAssetAsync("sprite[botright]"); yield return op; Assert.IsNotNull(op.Result); @@ -630,6 +687,8 @@ public IEnumerator CanLoadSpriteByName() Assert.AreEqual(typeof(Sprite), op2.Result.GetType()); Assert.AreEqual("topleft", op2.Result.name); op2.Release(); + + yield return null; //< Process deferred callback } [UnityTest] @@ -840,7 +899,7 @@ public IEnumerator LoadResourceLocationsAsync_ReturnsCorrectNumberOfLocationsFor } Assert.AreEqual(3, typesSeen.Count); - m_Addressables.Release(handle); + handle.Release(); } [UnityTest] @@ -860,7 +919,7 @@ public IEnumerator LoadResourceLocationsAsync_ReturnsCorrectNumberOfLocationsFor } Assert.AreEqual(3, typesSeen.Count); - m_Addressables.Release(handle); + handle.Release(); } [UnityTest] @@ -874,7 +933,7 @@ public IEnumerator LoadResourceLocationsAsync_ReturnsCorrectNumberOfLocationsFor Assert.AreEqual(1, handle.Result.Count); Assert.AreEqual(typeof(Mesh), handle.Result[0].ResourceType); - m_Addressables.Release(handle); + handle.Release(); } [UnityTest] @@ -901,8 +960,28 @@ public IEnumerator LoadResourceLocationsAsync_ReturnsCorrectNumberOfLocationsFor Assert.AreEqual(3, typesSeen.Count); - m_Addressables.Release(assetReferenceHandle); - m_Addressables.Release(handle); + assetReferenceHandle.Release(); + handle.Release(); + } + + [UnityTest] + public IEnumerator LoadSubAssetFromAssetWithMultipleSubAssetTypes() + { + yield return Init(); + + AsyncOperationHandle assetReferenceHandle = m_Addressables.InstantiateAsync(AssetReferenceObjectKey); + yield return assetReferenceHandle; + Assert.IsNotNull(assetReferenceHandle.Result as GameObject); + AssetReferenceTestBehavior behavior = + (assetReferenceHandle.Result as GameObject).GetComponent(); + + var handle = m_Addressables.LoadAssetAsync(behavior.ReferenceWithMultiTypedSubObjectSubReference); + yield return handle; + + Assert.NotNull(handle.Result); + + assetReferenceHandle.Release(); + handle.Release(); } [UnityTest] @@ -922,8 +1001,8 @@ public IEnumerator LoadResourceLocationsAsync_ReturnsCorrectNumberOfLocationsFor Assert.AreEqual(1, handle.Result.Count); Assert.AreEqual(typeof(Material), handle.Result[0].ResourceType); - m_Addressables.Release(assetReferenceHandle); - m_Addressables.Release(handle); + assetReferenceHandle.Release(); + handle.Release(); } [UnityTest] @@ -1100,8 +1179,8 @@ public IEnumerator LoadingContentCatalogTwice_DoesNotThrowException_WhenHandleIs Assert.AreEqual(AsyncOperationStatus.Succeeded, op1.Status); Assert.AreEqual(AsyncOperationStatus.Succeeded, op2.Status); - m_Addressables.Release(op1); - m_Addressables.Release(op2); + op1.Release(); + op2.Release(); if (Directory.Exists(kCatalogFolderPath)) Directory.Delete(kCatalogFolderPath, true); } @@ -1144,8 +1223,8 @@ public IEnumerator LoadingContentCatalogWithCacheTwice_DoesNotThrowException_Whe Assert.AreEqual(AsyncOperationStatus.Succeeded, op1.Status); Assert.AreEqual(AsyncOperationStatus.Succeeded, op2.Status); - m_Addressables.Release(op1); - m_Addressables.Release(op2); + op1.Release(); + op2.Release(); if (Directory.Exists(kCatalogFolderPath)) Directory.Delete(kCatalogFolderPath, true); } @@ -1165,8 +1244,10 @@ public IEnumerator LoadingContentCatalog_WithInvalidCatalogPath_Fails() Assert.AreEqual(AsyncOperationStatus.Failed, op1.Status); - m_Addressables.Release(op1); + op1.Release(); LogAssert.ignoreFailingMessages = ignoreValue; + + yield return null; //< Process deferred callback } private const string kCatalogRemotePath = "remotecatalog" + kCatalogExt; @@ -1259,7 +1340,7 @@ public IEnumerator LoadingContentCatalog_CachesCatalogData_IfValidHashFound() Assert.IsTrue(File.Exists(cachedHashPath)); Assert.AreEqual("123", File.ReadAllText(cachedHashPath)); - m_Addressables.Release(op1); + op1.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); @@ -1295,11 +1376,103 @@ public IEnumerator LoadingContentCatalog_CachesCatalogData_IfValidHashFoundAndRe Assert.IsTrue(File.Exists(cachedHashPath)); Assert.AreEqual("123", File.ReadAllText(cachedHashPath)); - m_Addressables.Release(op1); + op1.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); } + + [UnityTest] + public IEnumerator LoadingContentCatalog_WhenJsonEnabled_LoadJsonCatalog_Suceeds() + { + yield return Init(); + + string fakeCatalogFullPath = Path.Combine(kCatalogFolderPath, "remotecatalog.json"); + + if (!CreateCatalogAtFakeRemotePath(fakeCatalogFullPath)) + Assert.Ignore($"Skipping test {TestContext.CurrentContext.Test.Name} due to missing CatalogLocation."); + WriteHashFileForCatalog(fakeCatalogFullPath, "123"); + + var catalogOp = m_Addressables.LoadContentCatalogAsync(fakeCatalogFullPath, false); + yield return catalogOp; + + Assert.AreEqual(catalogOp.Status, AsyncOperationStatus.Succeeded); + + catalogOp.Release(); + } + + [UnityTest] + public IEnumerator LoadingContentCatalog_WhenJsonEnabled_LoadBinaryCatalog_FailsWithError() + { + yield return Init(); + + string fakeCatalogFullPath = Path.Combine(kCatalogFolderPath, "remotecatalog.bin"); + + if (!CreateCatalogAtFakeRemotePath(fakeCatalogFullPath)) + Assert.Ignore($"Skipping test {TestContext.CurrentContext.Test.Name} due to missing CatalogLocation."); + WriteHashFileForCatalog(fakeCatalogFullPath, "123"); + + var catalogOp = m_Addressables.LoadContentCatalogAsync(fakeCatalogFullPath, false); + yield return catalogOp; + + Assert.AreEqual(catalogOp.Status, AsyncOperationStatus.Failed); + Assert.IsTrue(catalogOp.OperationException != null); + Assert.AreEqual("ChainOperation failed because dependent operation failed", catalogOp.OperationException.Message); + Assert.IsTrue(catalogOp.OperationException.InnerException != null); + Assert.AreEqual("Failed to load content catalog.", catalogOp.OperationException.InnerException.Message); + Assert.IsTrue(catalogOp.OperationException.InnerException.InnerException != null); + Assert.AreEqual("Expecting to load catalogs in .json format but the catalog provided is in binary format. To load it disable Addressable Asset Settings > Catalog > Enable Json Catalog.", + catalogOp.OperationException.InnerException.InnerException.Message); + + catalogOp.Release(); + } +#else + [UnityTest] + public IEnumerator LoadingContentCatalog_WhenJsonDisabled_LoadBinaryCatalog_Suceeds() + { + yield return Init(); + + string fakeCatalogFullPath = Path.Combine(kCatalogFolderPath, "remotecatalog.bin"); + + if (!CreateCatalogAtFakeRemotePath(fakeCatalogFullPath)) + Assert.Ignore($"Skipping test {TestContext.CurrentContext.Test.Name} due to missing CatalogLocation."); + WriteHashFileForCatalog(fakeCatalogFullPath, "123"); + + var catalogOp = m_Addressables.LoadContentCatalogAsync(fakeCatalogFullPath, false); + yield return catalogOp; + + Assert.AreEqual(catalogOp.Status, AsyncOperationStatus.Succeeded); + + catalogOp.Release(); + } + + [UnityTest] + public IEnumerator LoadingContentCatalog_WhenJsonDisabled_LoadJsonCatalog_FailsWithError() + { + yield return Init(); + + string fakeCatalogFullPath = Path.Combine(kCatalogFolderPath, "remotecatalog.json"); + + if (!CreateCatalogAtFakeRemotePath(fakeCatalogFullPath)) + Assert.Ignore($"Skipping test {TestContext.CurrentContext.Test.Name} due to missing CatalogLocation."); + WriteHashFileForCatalog(fakeCatalogFullPath, "123"); + + var catalogOp = m_Addressables.LoadContentCatalogAsync(fakeCatalogFullPath, false); + yield return catalogOp; + + Assert.AreEqual(catalogOp.Status, AsyncOperationStatus.Failed); + Assert.IsTrue(catalogOp.OperationException != null); + Assert.AreEqual("ChainOperation failed because dependent operation failed", catalogOp.OperationException.Message); + Assert.IsTrue(catalogOp.OperationException.InnerException != null); + Assert.AreEqual("Failed to load content catalog.", catalogOp.OperationException.InnerException.Message); + Assert.IsTrue(catalogOp.OperationException.InnerException.InnerException != null); + Assert.AreEqual("Expecting to load catalogs in binary format but the catalog provided is in .json format. To load it enable Addressable Asset Settings > Catalog > Enable Json Catalog.", + catalogOp.OperationException.InnerException.InnerException.Message); + + catalogOp.Release(); + + yield return null; //< Process deferred callback + } #endif [UnityTest] @@ -1359,8 +1532,8 @@ public IEnumerator LoadingContentCatalog_CachesCatalogData_ForTwoCatalogsWithSam Assert.AreEqual("123", File.ReadAllText(cachedHashPath)); Assert.AreEqual("123", File.ReadAllText(cachedHashPathTwo)); - m_Addressables.Release(op1); - m_Addressables.Release(op2); + op1.Release(); + op2.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); @@ -1413,7 +1586,7 @@ public IEnumerator LoadingContentCatalog_IfNoCachedHashFound_Succeeds() Assert.NotNull(op1.Result); // Cleanup - Addressables.Release(op1); + op1.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); @@ -1459,7 +1632,7 @@ public IEnumerator LoadingContentCatalog_IfNoHashFileForCatalog_DoesntThrowExcep Assert.IsFalse(File.Exists(cachedHashPath)); // Cleanup - Addressables.Release(op1); + op1.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); @@ -1513,7 +1686,7 @@ public IEnumerator LoadingContentCatalog_UpdatesCachedData_IfHashFileUpdates() var op1 = m_Addressables.LoadContentCatalogAsync(fullRemotePath, false); yield return op1; - m_Addressables.Release(op1); + op1.Release(); Assert.IsTrue(File.Exists(cachedDataPath)); Assert.IsTrue(File.Exists(cachedHashPath)); @@ -1526,13 +1699,13 @@ public IEnumerator LoadingContentCatalog_UpdatesCachedData_IfHashFileUpdates() Assert.AreEqual("456", File.ReadAllText(cachedHashPath)); - m_Addressables.Release(op2); + op2.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); } - #if ENABLE_JSON_CATALOG +#if ENABLE_JSON_CATALOG [UnityTest] public IEnumerator UpdateContentCatalog_UpdatesCachedData_IfCacheCorrupted() { @@ -1567,7 +1740,7 @@ public IEnumerator UpdateContentCatalog_UpdatesCachedData_IfCacheCorrupted() Assert.IsTrue(File.Exists(cachedHashPath)); Assert.AreEqual(File.ReadAllText(cachedDataPath), File.ReadAllText(fullRemotePath)); - m_Addressables.Release(op); + op.Release(); Directory.Delete(kCatalogFolderPath, true); File.Delete(cachedDataPath); File.Delete(cachedHashPath); @@ -1596,7 +1769,7 @@ public IEnumerator LoadingContentCatalog_NoCacheDataCreated_IfRemoteHashDoesntEx var op1 = m_Addressables.LoadContentCatalogAsync(fullRemotePath, false); yield return op1; - m_Addressables.Release(op1); + op1.Release(); Assert.IsFalse(File.Exists(cachedDataPath)); Assert.IsFalse(File.Exists(cachedHashPath)); @@ -2138,7 +2311,9 @@ public IEnumerator WhenLoadWithInvalidKey_ReturnedOpIsFailed() yield return null; Assert.IsTrue(gop.IsDone); Assert.AreEqual(AsyncOperationStatus.Failed, gop.Status); - m_Addressables.Release(gop); + gop.Release(); + + yield return null; //< Process deferred callback } [UnityTest] @@ -2154,7 +2329,7 @@ public IEnumerator CanLoadAssetsWithMultipleKeysMerged() Assert.NotNull(gop.Result); Assert.AreEqual(1, gop.Result.Count); Assert.AreEqual(AsyncOperationStatus.Succeeded, gop.Status); - m_Addressables.Release(gop); + gop.Release(); } [UnityTest] @@ -2211,7 +2386,7 @@ public IEnumerator LoadAsset_SuccessfulWhenLoadAssetMode_LoadAllAssets() Assert.IsTrue(locationHandle.Result != null, "Failed to get Location for " + label); Assert.AreEqual(1, locationHandle.Result.Count, "Failed to get Location for " + label); IResourceLocation loc = locationHandle.Result[0]; - Addressables.Release(locationHandle); + locationHandle.Release(); foreach (IResourceLocation dependency in loc.Dependencies) { @@ -2502,7 +2677,7 @@ public IEnumerator AssetReference_HandleIsInvalidated_WhenReleasingLoadOperation AsyncOperationHandle referenceHandle = behavior.Reference.OperationHandle; Assert.IsTrue(behavior.Reference.IsValid()); - m_Addressables.Release(referenceHandle); + referenceHandle.Release(); yield return referenceHandle; Assert.IsFalse(behavior.Reference.IsValid()); @@ -2546,7 +2721,7 @@ public IEnumerator CanloadAssetReferenceSubObject() AsyncOperationHandle assetRefHandle = m_Addressables.LoadAssetAsync(behavior.ReferenceWithSubObject); yield return assetRefHandle; Assert.IsNotNull(assetRefHandle.Result); - m_Addressables.Release(assetRefHandle); + assetRefHandle.Release(); handle.Release(); } @@ -2561,7 +2736,7 @@ public IEnumerator AddressablesIntegration_LoadAssetAsync_CanLoadAssetReferenceO Assert.AreEqual(assetRefHandle.Result.Length, 2); Assert.AreEqual(assetRefHandle.Result[0].name, "assetWithSubObjects"); Assert.AreEqual(assetRefHandle.Result[1].name, "sub-shown"); - m_Addressables.Release(assetRefHandle); + assetRefHandle.Release(); } [UnityTest] @@ -2767,8 +2942,8 @@ public override void Provide(ProvideHandle provideHandle) GroupOperation group = new GroupOperation(); TestInternalOp = new TestAssetBundleResourceInternalOp(new TestAssetBundleResource()); TestInternalOp.m_RM = Addressables.Instance.ResourceManager; - provideHandle.ResourceManager.Acquire(TestInternalOp.Handle); - provideHandle.ResourceManager.Acquire(TestInternalOp.Handle); + var opRef1 = provideHandle.ResourceManager.Acquire(TestInternalOp.Handle); + var opRef2 = provideHandle.ResourceManager.Acquire(TestInternalOp.Handle); group.Init(new List() {new AsyncOperationHandle(TestInternalOp)}); op.Init(provideHandle.ResourceManager, null, provideHandle.Location, group.Handle); op.m_RM = Addressables.Instance.ResourceManager; @@ -2776,8 +2951,8 @@ public override void Provide(ProvideHandle provideHandle) TestInternalOp.InvokeExecute(); base.Provide(handle); provideHandle.Complete(TestInternalOp.Result, TestInternalOp.Status == AsyncOperationStatus.Succeeded, TestInternalOp.OperationException); - provideHandle.ResourceManager.Release(TestInternalOp.Handle); - provideHandle.ResourceManager.Release(TestInternalOp.Handle); + opRef1.Release(); + opRef2.Release(); } internal class TestAssetBundleResource : IAssetBundleResource @@ -3131,7 +3306,7 @@ public IEnumerator Autorelease_False_ClearDependencyCache_WithChainOperation_Doe dumbUpdate.CallComplete(); yield return clearCache; Assert.IsTrue(clearCache.IsValid()); - m_Addressables.Release(clearCache); + clearCache.Release(); Assert.IsFalse(clearCache.IsValid()); } @@ -3161,7 +3336,7 @@ public IEnumerator Autorelease_False_ClearDependencyCacheList_WithChainOperation dumbUpdate.CallComplete(); yield return clearCache; Assert.IsTrue(clearCache.IsValid()); - m_Addressables.Release(clearCache); + clearCache.Release(); Assert.IsFalse(clearCache.IsValid()); } @@ -3190,7 +3365,7 @@ public IEnumerator Autorelease_False_ClearDependencyCacheObject_WithChainOperati dumbUpdate.CallComplete(); yield return clearCache; Assert.IsTrue(clearCache.IsValid()); - m_Addressables.Release(clearCache); + clearCache.Release(); Assert.IsFalse(clearCache.IsValid()); } @@ -3225,7 +3400,7 @@ public IEnumerator Autorelease_False_ClearDependencyCacheListIResourceLocs_WithC dumbUpdate.CallComplete(); yield return clearCache; Assert.IsTrue(clearCache.IsValid()); - m_Addressables.Release(clearCache); + clearCache.Release(); Assert.IsFalse(clearCache.IsValid()); } diff --git a/Tests/Runtime/AddressablesTestFixture.cs b/Tests/Runtime/AddressablesTestFixture.cs index 70178663..73650c89 100644 --- a/Tests/Runtime/AddressablesTestFixture.cs +++ b/Tests/Runtime/AddressablesTestFixture.cs @@ -270,13 +270,17 @@ internal static IEnumerator UnloadSceneFromHandler(AsyncOperationHandle 0); - m_Addressables.Release(updateOp); + updateOp.Release(); Directory.Delete(fakeCacheFolder, true); #else Assert.Ignore("Caching not enabled."); @@ -308,7 +308,7 @@ public IEnumerator UpdateContent_UpdatesCatalogs_WhenAutoCleanCacheEnabled_AndCa Assert.AreEqual(updateOp.OperationException.Message, "ChainOperation failed because dependent operation failed"); Assert.AreEqual(updateOp.OperationException.InnerException.Message, "CompletedOperation, status=Failed, result=False catalogs updated, but failed to clean bundle cache."); - m_Addressables.Release(updateOp); + updateOp.Release(); #else Assert.Ignore("Caching is enabled, but test expects to run on caching-disabled platforms or platform was skipped."); yield return null; diff --git a/Tests/Runtime/ResourceManager/Operations/AsyncOperationHandleTests.cs b/Tests/Runtime/ResourceManager/Operations/AsyncOperationHandleTests.cs index 3253e8a7..73f6bf34 100644 --- a/Tests/Runtime/ResourceManager/Operations/AsyncOperationHandleTests.cs +++ b/Tests/Runtime/ResourceManager/Operations/AsyncOperationHandleTests.cs @@ -204,5 +204,129 @@ public void AsyncOperationHandle_Acquire_IncrementsRefCount() actualCount = DestructiveGetRefCount(typelessHandle); Assert.AreEqual(expectedCount, actualCount); } + + [Test] + public void AsyncOperationHandle_Version_FromOp() + { + var op = new FakeTypedOperation() { m_Version = 12345 }; + var handle = new AsyncOperationHandle(op); + Assert.AreEqual(op.Version, handle.Version); + } + + [Test] + public void AsyncOperationHandle_Version_FromOp_Typeless() + { + var op = (IAsyncOperation)new FakeTypedOperation() { m_Version = 12345 }; + var handle = new AsyncOperationHandle(op); + Assert.AreEqual(op.Version, handle.Version); + } + + [Test] + public void AsyncOperationHandle_Version_ConstructParam() + { + var op = new FakeTypedOperation() { m_Version = 12345 }; + var expected = 987654; + var handle = new AsyncOperationHandle(op, expected); + Assert.AreEqual(expected, handle.Version); + } + + [Test] + public void AsyncOperationHandle_Version_ConstructParam_Typeless() + { + var op = (IAsyncOperation)new FakeTypedOperation() { m_Version = 12345 }; + var expected = 987654; + var handle = new AsyncOperationHandle(op, expected); + Assert.AreEqual(expected, handle.Version); + } + + [Test] + public void AsyncOperationHandle_Version_CopiedByAcquire() + { + var expected = 987654; + var op = new FakeTypedOperation() { m_Version = expected }; + var handle = new AsyncOperationHandle(op); + var handle2 = handle.Acquire(); + Assert.AreEqual(expected, handle2.Version); + } + + [Test] + public void AsyncOperationHandle_Version_CopiedByAcquire_Typeless() + { + var expected = 987654; + var op = (IAsyncOperation)new FakeTypedOperation() { m_Version = expected }; + var handle = new AsyncOperationHandle(op); + var handle2 = handle.Acquire(); + Assert.AreEqual(expected, handle2.Version); + } + + [Test] + public void AsyncOperationHandle_LocationName_Default() + { + var op = new FakeTypedOperation(); + var handle = new AsyncOperationHandle(op); + Assert.IsNull(handle.LocationName); + } + [Test] + public void AsyncOperationHandle_LocationName_Default_Typeless() + { + var op = new FakeTypedOperation(); + var handle = new AsyncOperationHandle(op); + Assert.IsNull(handle.LocationName); + } + + [Test] + public void AsyncOperationHandle_LocationName_Set() + { + var expected = "LocationNameSet"; + var handle = new AsyncOperationHandle(null); + handle.LocationName = expected; + Assert.AreEqual(expected, handle.LocationName); + } + [Test] + public void AsyncOperationHandle_LocationName_Set_Typeless() + { + var expected = "LocationNameSet"; + var handle = new AsyncOperationHandle(null); + handle.LocationName = expected; + Assert.AreEqual(expected, handle.LocationName); + } + + [Test] + public void AsyncOperationHandle_LocationName_ConstructParam() + { + var op = new FakeTypedOperation(); + var expected = "LocationNameConstructParam"; + var handle = new AsyncOperationHandle(op, expected); + Assert.AreEqual( expected, handle.LocationName); + } + + [Test] + public void AsyncOperationHandle_LocationName_ConstructParam_Typeless() + { + var op = (IAsyncOperation)new FakeTypedOperation(); + var expected = "LocationNameConstructParam"; + var handle = new AsyncOperationHandle(op, expected); + Assert.AreEqual(expected, handle.LocationName); + } + + [Test] + public void AsyncOperationHandle_LocationName_CopiedByAcquire() + { + var op = new FakeTypedOperation(); + var expected = "LocationNameConstructParam"; + var handle = new AsyncOperationHandle(op, expected); + var handle2 = handle.Acquire(); + Assert.AreEqual(expected, handle2.LocationName); + } + + [Test] + public void AsyncOperationHandle_LocationName_CopiedByAcquire_Typeless() + { + var op = (IAsyncOperation)new FakeTypedOperation(); + var expected = "LocationNameConstructParam"; + var handle = new AsyncOperationHandle(op, expected); + var handle2 = handle.Acquire(); + Assert.AreEqual(expected, handle2.LocationName); + } } } diff --git a/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs b/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs index 56ebf01f..d5f4acb1 100644 --- a/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs +++ b/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs @@ -276,7 +276,7 @@ public void GroupOperation_WithOpsThatImplementGetDownloadStatus_ComputesExpecte foreach (var o in mdpco) o.CompleteNow(); AssertExpectedDownloadStatus(gOp.GetDownloadStatus(), 4096, 4096, 1f); - m_RM.Release(gOp); + gOp.Release(); } [Test] @@ -293,7 +293,7 @@ public void ChainOperation_WithOpThatImplementGetDownloadStatus_ComputesExpected m_RM.Update(.1f); Assert.IsTrue(chainOp.IsDone); AssertExpectedDownloadStatus(chainOp.GetDownloadStatus(), 1024, 1024, 1f); - m_RM.Release(chainOp); + chainOp.Release(); } [Test] @@ -321,7 +321,7 @@ public void GroupOperation_WithDuplicateOpThatImplementGetDownloadStatus_DoesNot AssertExpectedDownloadStatus(gOp.GetDownloadStatus(), 512, 1024, .5f); o.CompleteNow(); AssertExpectedDownloadStatus(gOp.GetDownloadStatus(), 1024, 1024, 1f); - m_RM.Release(gOp); + gOp.Release(); } class TestOp : AsyncOperationBase @@ -361,6 +361,65 @@ public void CompletionEvents_AreInvoked_InOrderAdded() op.Complete(1, true, null); } + + [Test] + public void ReleaseHandleOnCompletion_Typed() + { + var op = new TestOp(); + + var handle = op.Handle; + handle.ReleaseHandleOnCompletion(); + + Assert.IsTrue(handle.IsValid()); + + op.Start(null, default, null); + + Assert.IsTrue(handle.IsValid()); + + op.Complete(1, true, null); + + Assert.IsFalse(handle.IsValid()); + } + + [Test] + public void ReleaseHandleOnCompletion_Typeless() + { + var op = new TestOp(); + + var handle = (AsyncOperationHandle)op.Handle; //< Typeless handle + handle.ReleaseHandleOnCompletion(); + + Assert.IsTrue(handle.IsValid()); + + op.Start(null, default, null); + + Assert.IsTrue(handle.IsValid()); + + op.Complete(1, true, null); + + Assert.IsFalse(handle.IsValid()); + } + + [Test] + public void OperationIsEqual_OnAcquire() + { + var op = new TestOp(); + var handle = op.Handle; //< Typed handle + var handle2 = m_RM.Acquire(handle); + Assert.AreEqual(handle, handle2); + } + +#if false /// TODO: Future major revision to support non-void return for `AsyncOperationHandle ResourceManager::Acquire(AsyncOperationHandle)` + [Test] + public void OperationIsEqual_OnAcquire_Typeless() + { + var op = new TestOp(); + var handle = (AsyncOperationHandle)op.Handle; //< Typeless handle + var handle2 = m_RM.Acquire(handle); + Assert.AreEqual(handle, handle2); + } +#endif + [Test] public void WhenOperationIsReused_HasExecutedIsReset() { diff --git a/Tests/Runtime/ResourceManager/ResourceManagerTests.cs b/Tests/Runtime/ResourceManager/ResourceManagerTests.cs index 1d0624e6..72b3e4f5 100644 --- a/Tests/Runtime/ResourceManager/ResourceManagerTests.cs +++ b/Tests/Runtime/ResourceManager/ResourceManagerTests.cs @@ -274,7 +274,8 @@ public void ReleaseInstance_BeforeDependencyCompletes_InstantiatesAndReleasesAft iProvider.ReleaseInstanceCallback = (rm, go) => { releaseCalled = true; }; var instHandle = m_ResourceManager.ProvideInstance(iProvider, locDep, default(InstantiationParameters)); Assert.IsFalse(instHandle.IsDone); - m_ResourceManager.Release(instHandle); + var instHandleCpy = instHandle; + instHandleCpy.Release(); Assert.IsTrue(instHandle.IsValid()); Assert.IsFalse(provideCalled); Assert.IsFalse(releaseCalled); @@ -304,7 +305,7 @@ public void ProvideInstance_CanProvide() }; iProvider.ReleaseInstanceCallback = (rm, go) => { - rm.Release(refResource[0]); + refResource[0].Release(); GameObject.Destroy(go); }; diff --git a/Tests/Runtime/SceneTests.cs b/Tests/Runtime/SceneTests.cs index 3656ef64..bac5c292 100644 --- a/Tests/Runtime/SceneTests.cs +++ b/Tests/Runtime/SceneTests.cs @@ -483,6 +483,7 @@ public IEnumerator SceneTests_UnloadSceneAsync_UnloadSceneDecreaseRefOnlyOnce() } [UnityTest] + [Obsolete] public IEnumerator SceneTests_Release_ReleaseToZeroRefCountUnloadsScene() { var op = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); @@ -497,6 +498,54 @@ public IEnumerator SceneTests_Release_ReleaseToZeroRefCountUnloadsScene() Assert.IsFalse(op.IsValid()); } + [UnityTest] + public IEnumerator SceneTests_Release_ReleaseToZeroRefCountUnloadsScene_Direct() + { + var op = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); + yield return op; + Assert.AreEqual(AsyncOperationStatus.Succeeded, op.Status); + Assert.AreEqual(sceneKeys[0], SceneManager.GetSceneByName(sceneKeys[0]).name); + + op.Release(); + yield return null; + + Assert.IsFalse(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); + Assert.IsFalse(op.IsValid()); + } + + [UnityTest] + [Obsolete] + public IEnumerator SceneTests_Release_ReleaseToZeroRefCountUnloadsScene_Typeless() + { + // implicit convert to a Typeless handle + AsyncOperationHandle op = (AsyncOperationHandle)m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); + yield return op; + Assert.AreEqual(AsyncOperationStatus.Succeeded, op.Status); + Assert.AreEqual(sceneKeys[0], SceneManager.GetSceneByName(sceneKeys[0]).name); + + m_Addressables.Release(op); + yield return null; + + Assert.IsFalse(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); + Assert.IsFalse(op.IsValid()); + } + + [UnityTest] + public IEnumerator SceneTests_Release_ReleaseToZeroRefCountUnloadsScene_DirectTypeless() + { + // implicit convert to a Typeless handle + AsyncOperationHandle op = (AsyncOperationHandle)m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); + yield return op; + Assert.AreEqual(AsyncOperationStatus.Succeeded, op.Status); + Assert.AreEqual(sceneKeys[0], SceneManager.GetSceneByName(sceneKeys[0]).name); + + op.Release(); + yield return null; + + Assert.IsFalse(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); + Assert.IsFalse(op.IsValid()); + } + [UnityTest] public IEnumerator SceneTests_Release_ReleaseToRefCountZeroWhileLoadingUnloadsAfterLoadCompletes() { @@ -504,9 +553,17 @@ public IEnumerator SceneTests_Release_ReleaseToRefCountZeroWhileLoadingUnloadsAf var op = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); // Test - op.Completed += s => Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); - m_Addressables.Release(op); + bool wasLoadCompleted = false; + op.Completed += s => + { + Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); + wasLoadCompleted = true; + }; + + var opCopy = op; + opCopy.Release(); yield return op; + Assert.IsTrue(wasLoadCompleted); Assert.IsFalse(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); Assert.IsFalse(op.IsValid()); @@ -539,25 +596,34 @@ public IEnumerator SceneTests_SceneOp_UpdateReceiverDoesNotRemainPastCompletion( { Assert.AreEqual(startingReceiversCount, m_Addressables.ResourceManager.m_UpdateReceivers.Count + m_Addressables.ResourceManager.m_UpdateCallbacks.Count); - m_Addressables.Release(op); + op.Release(); Assert.IsFalse(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); Assert.IsFalse(op.IsValid()); } + + yield return null; //< `OnSceneUnloaded` needs to trigger for `m_Addressables.ActiveSceneInstances` teardown checks } [UnityTest] public IEnumerator SceneTests_Release_ReleaseNotRefCountZeroWhileLoadingDoesntUnloadAfterLoadCompletes() { // Setup - var op = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); - Addressables.ResourceManager.Acquire(op); + var opFirst = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); + var op = Addressables.ResourceManager.Acquire(opFirst); // Test - op.Completed += s => Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); - Addressables.Release(op); + bool wasLoadCompleted = false; + opFirst.Completed += s => + { + Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); + wasLoadCompleted = true; + }; + + opFirst.Release(); yield return op; + Assert.IsTrue(wasLoadCompleted); Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); Assert.IsTrue(op.IsValid()); @@ -565,7 +631,7 @@ public IEnumerator SceneTests_Release_ReleaseNotRefCountZeroWhileLoadingDoesntUn yield return op; Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); Assert.IsTrue(op.IsValid()); - m_Addressables.Release(op); + op.Release(); yield return op; Assert.IsFalse(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); @@ -575,20 +641,20 @@ public IEnumerator SceneTests_Release_ReleaseNotRefCountZeroWhileLoadingDoesntUn [UnityTest] public IEnumerator SceneTests_Release_ReleaseNotToZeroRefCountDoesNotUnloadScene() { - var op = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); - yield return op; - Assert.AreEqual(AsyncOperationStatus.Succeeded, op.Status); + var opFirst = m_Addressables.LoadSceneAsync(sceneKeys[0], new LoadSceneParameters(LoadSceneMode.Additive)); + yield return opFirst; + Assert.AreEqual(AsyncOperationStatus.Succeeded, opFirst.Status); Assert.AreEqual(sceneKeys[0], SceneManager.GetSceneByName(sceneKeys[0]).name); - Addressables.ResourceManager.Acquire(op); + var op = Addressables.ResourceManager.Acquire(opFirst); - m_Addressables.Release(op); + opFirst.Release(); yield return null; Assert.IsTrue(SceneManager.GetSceneByName(sceneKeys[0]).isLoaded); Assert.IsTrue(op.IsValid()); // Cleanup - m_Addressables.Release(op); + op.Release(); yield return null; } @@ -635,7 +701,7 @@ public IEnumerator UnloadScene_ChainsBehindLoadOp_IfLoadOpIsRunning_TypedHandle( //Assert Assert.AreEqual(typeof(ChainOperation), unloadHandle.m_InternalOp.GetType(), "Unload a scene while a Load is in progress should have resulted in the unload being chained behind the load op, but wasn't"); - Addressables.Release(unloadHandle); + unloadHandle.Release(); } [UnityTest] @@ -651,7 +717,7 @@ public IEnumerator UnloadScene_ChainsBehindLoadOp_IfLoadOpIsRunning_TypelessHand //Assert Assert.AreEqual(typeof(ChainOperationTypelessDepedency), unloadHandle.m_InternalOp.GetType(), "Unload a scene while a Load is in progress should have resulted in the unload being chained behind the load op, but wasn't"); - Addressables.Release(unloadHandle); + unloadHandle.Release(); } [UnityTest] @@ -687,12 +753,15 @@ public IEnumerator SceneTests_UnloadSceneAsync_UnloadSceneAfterAcquireAndDoNotDe int bundleCountAfterUnload = AssetBundle.GetAllLoadedAssetBundles().Count(); Assert.AreEqual(bundleCountAfterInstantiate, bundleCountAfterUnload); - Addressables.Release(activeScene); + var activeSceneCpy = activeScene; + Assert.IsTrue(activeScene.IsValid()); + activeSceneCpy.Release(); + yield return activeScene; // Cleanup Assert.IsFalse(activeScene.IsValid()); - Addressables.Release(instOp); + instOp.Release(); AssetBundleProvider.WaitForAllUnloadingBundlesToComplete(); int bundleCountEndTest = AssetBundle.GetAllLoadedAssetBundles().Count(); Assert.AreEqual(bundleCountBeforeTest, bundleCountEndTest); diff --git a/Tests/Runtime/SyncAddressableTests.cs b/Tests/Runtime/SyncAddressableTests.cs index f812ca5d..031cfe0a 100644 --- a/Tests/Runtime/SyncAddressableTests.cs +++ b/Tests/Runtime/SyncAddressableTests.cs @@ -410,7 +410,7 @@ public void InstanceOperation_WithFailedBundleLoad_CompletesSync() LogAssert.Expect(LogType.Error, new Regex("InvalidKeyException*")); Assert.IsTrue(instanceOperation.IsDone); - m_Addressables.Release(depOp); + depOp.Release(); } [Test] @@ -423,7 +423,7 @@ public void InstantiateSync_InvalidKeyExceptionCorrectlyThrown() "Exception thrown is not of the correct type."); }; var depOp = m_Addressables.LoadAssetAsync(m_InvalidKey); - m_Addressables.Release(depOp); + depOp.Release(); ResourceManager.ExceptionHandler = prevHandler; } @@ -439,7 +439,7 @@ public void InstanceOperation_WithSuccessfulBundleLoad_CompletesSync() instanceOperation.WaitForCompletion(); Assert.IsTrue(instanceOperation.IsDone); - m_Addressables.Release(depOp); + depOp.Release(); } [TestCase(true)] diff --git a/package.json b/package.json index 27484a62..aa766dc3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.addressables", "displayName": "Addressables", - "version": "2.1.0", + "version": "2.2.2", "unity": "2023.1", "description": "The Addressable Asset System allows the developer to ask for an asset via its address. Once an asset (e.g. a prefab) is marked \"addressable\", it generates an address which can be called from anywhere. Wherever the asset resides (local or remote), the system will locate it and its dependencies, then return it.\n\nUse 'Window->Asset Management->Addressables' to begin working with the system.\n\nAddressables use asynchronous loading to support loading from any location with any collection of dependencies. Whether you have been using direct references, traditional asset bundles, or Resource folders, addressables provide a simpler way to make your game more dynamic. Addressables simultaneously opens up the world of asset bundles while managing all the complexity.\n\nFor usage samples, see github.com/Unity-Technologies/Addressables-Sample", "keywords": [ @@ -12,7 +12,7 @@ "assetbundles" ], "dependencies": { - "com.unity.scriptablebuildpipeline": "2.1.3", + "com.unity.scriptablebuildpipeline": "2.1.4", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", @@ -21,16 +21,16 @@ "com.unity.profiling.core": "1.0.2" }, "_upm": { - "changelog": "- Fix \"Unable to verify target bucket for Remote Catalog: Not Found. Object could not be found\" error\n- Fixed caching to prevent unnecessary refreshing of bucket data.\n- Sort collections on serialization to prevent unecessary merge conflicts\n- Add warnings and documentation to make it clear you need to release the handle from LoadDependenciesAsync" + "changelog": "- Fix KeyNotFoundException when clicking on local bundles in the profiler.\n- Fix bundles incorrectly marked as released in the Profiler when they are still active\n- Improved error message when trying to load a catalog in an unexpected file format.\n- Fixed issue where operation that uses WaitForCompletion can timeout much earlier than it should.\n- The build scripts were reworked so that you can extend them or copy them outside the package without having to fork the entire package.\n- A Version field was added to the Addressables object for getting the package version in the Editor.\n- Fixed an issue where tearing down the Addressables instance could happen before user tear down code was getting called.\n- Sort collections to make serialized editor files deterministic\n- Fixed issue where labels on an addressable sub-entry are incorrectly added to the former parent entry.\n- Added support for calling Release() on AsyncOperationHandles directly that couldn't before.\n- Fixed sub-object loading from AssetReferences for types that are not Sprites in a SpriteAtlas." }, "upmCi": { - "footprint": "b4114ce971e4ee913e0e90b5d8b8bb86cb39529d" + "footprint": "965c2b80d4ffb77f73ae7a066d9c7b22cd8cb8e7" }, - "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.addressables@2.1/manual/index.html", + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/Addressables.git", "type": "git", - "revision": "ce0566e3aa96bd3b1648c405187751d0306cc931" + "revision": "a5d37e7459f86f9108e521236be616077803ca7c" }, "samples": [ {