From 357035d9db26626d47c5513213173f4d4d341eae Mon Sep 17 00:00:00 2001 From: Unity Technologies <@unity> Date: Fri, 4 Jun 2021 00:00:00 +0000 Subject: [PATCH] com.unity.addressables@1.18.9 ## [1.18.9] - 2021-06-04 - Added "Select" button for Addressable Asset in Inspector to select the Asset in the Addressables Groups Window. - Reduced the number of file copies required during building Addressables and moving Addressables content during Player build. - Fixed issue with AssetReferenceUIRestriction not working with Lists and Arrays. - Optimised loading AssetBundles to avoid redundent existing file checks. - Fixed issue with folder asset entries throwing null ref exceptions when doing a Check for Content Update Restriction - Added documentation about how to implement custom operations with synchronous behavior - Added option on AddressableAssetSettings to strip the Unity version from the AssetBundle hash during build. - Added documentation about useful tools you can use when building Addressables content with a CI pipeline - Added Import Groups tool to Samples folder. - Updated documentation for setting up and importing addressable assets in packages." - Fixed issue where multi-group drag and drop places groups in reverse order. - Fixed issue where an asset entry is no longer selected in the Project window after it is modified on disk. - Fixed simulated play mode when "Internal Asset Naming Mode" was set to something other than "Full Path" - Fixed issues with WaitForCompletion getting stuck in infinite loop during failed operations - Organised AddressableAssetSettings GUI into more distint setting types. - Fixed issue where the wrong operation would sometimes be returned by the cache when a project contains over 10K addressable assets - Added path pairs feature - Fixed issue where AsyncOperationBase.HasExecuted isn't being reset when the operation is reused. - Added check to ensure that ResourceManager.Update() is never called from within its own callstack. - Added ability to rename labels from the label window. - Added the DisableVisibleSubAssetRepresentations option in Settings. --- CHANGELOG.md | 23 + Documentation~/AddressableAssetSettings.md | 30 +- .../AddressableAssetsDevelopmentCycle.md | 4 + .../AddressableAssetsGettingStarted.md | 39 +- .../AddressableAssetsHostingServices.md | 2 +- Documentation~/AddressableAssetsProfiles.md | 23 +- Documentation~/AddressablesAPI.md | 1 + Documentation~/AddressablesFAQ.md | 26 +- Documentation~/ContinuousIntegration.md | 37 + Documentation~/DiagnosticTools.md | 2 +- Documentation~/SynchronousAddressables.md | 6 +- Documentation~/TableOfContents.md | 2 + .../images/AddressableAssetSettings.png | Bin 119756 -> 126682 bytes .../images/ProfilesWithPathPairsExample1.png | Bin 0 -> 38707 bytes .../images/ProfilesWithPathPairsExample2.png | Bin 0 -> 38695 bytes Documentation~/images/inspectorcheckbox.png | Bin 7282 -> 20041 bytes .../Build/AddressableAssetSettingsLocator.cs | 14 +- .../Build/AddressablesPlayerBuildProcessor.cs | 20 +- .../BuildLayoutGenerationTask.cs | 35 +- .../GenerateLocationListsTask.cs | 26 +- .../AddressableAssetsBundleBuildParameters.cs | 9 + .../DataBuilders/BuildScriptPackedMode.cs | 23 +- .../DataBuilders/BuildScriptVirtualMode.cs | 10 +- Editor/Build/DirectoryUtility.cs | 26 + .../GUI/AddressableAssetSettingsInspector.cs | 118 +- .../AddressableAssetsSettingsGroupEditor.cs | 83 +- .../AddressableAssetsSettingsGroupTreeView.cs | 71 +- Editor/GUI/AddressableAssetsWindow.cs | 10 +- Editor/GUI/AssetInspectorGUI.cs | 44 +- Editor/GUI/LabelWindow.cs | 65 +- Editor/GUI/ProfileWindow.cs | 148 +- Editor/Settings/AddressableAssetEntry.cs | 10 +- Editor/Settings/AddressableAssetSettings.cs | 56 +- .../Settings/AssetReferenceDrawerUtilities.cs | 20 +- Editor/Settings/LabelTable.cs | 18 + Editor/Settings/ProfileGroupType.cs | 152 +++ .../Settings/ProfileGroupType.cs.meta | 2 +- Runtime/Addressables.cs | 23 +- Runtime/AddressablesImpl.cs | 6 + .../Initialization/InitializationOperation.cs | 18 +- .../AsyncOperations/AsyncOperationBase.cs | 13 +- .../AsyncOperations/GroupOperation.cs | 2 +- .../AsyncOperations/ProviderOperation.cs | 2 +- Runtime/ResourceManager/ResourceManager.cs | 81 +- .../ResourceProviders/AssetBundleProvider.cs | 89 +- .../AssetDatabaseProvider.cs | 157 +-- .../Simulation/VirtualAssetBundle.cs | 8 +- .../Simulation/VirtualBundledAssetProvider.cs | 5 - .../ResourceProviders/TextDataProvider.cs | 1 + .../Util/OperationCacheKeys.cs | 131 ++ .../Util/OperationCacheKeys.cs.meta | 11 + Samples~/ImportExistingGroup.meta | 8 + Samples~/ImportExistingGroup/.sample.json | 4 + Samples~/ImportExistingGroup/Editor.meta | 8 + .../Editor/ImportExistingGroup.cs | 155 +++ .../Editor/ImportExistingGroup.cs.meta | 11 + .../Unity.Addressables.SamplesFolder.asmdef | 9 +- Tests/Editor/AddressableAssetEntryTests.cs | 22 + .../AddressableAssetSettingsLocatorTests.cs | 12 +- Tests/Editor/AddressableAssetSettingsTests.cs | 59 + Tests/Editor/AddressableAssetUtilityTests.cs | 40 - ...essableAssetsBundleBuildParametersTests.cs | 46 +- Tests/Editor/AddressableAssetsWindowTests.cs | 45 +- Tests/Editor/AssetReferenceDrawerTests.cs | 1189 ----------------- Tests/Editor/Build/BuildScriptTests.cs | 113 +- .../Build/GenerateLocationListsTaskTests.cs | 20 + .../HttpHostingServiceTests.cs | 15 +- Tests/Editor/ProfileGroupTypeTests.cs | 99 ++ Tests/Editor/ProfileGroupTypeTests.cs.meta | 11 + Tests/Runtime/AddressablesImplTests.cs | 194 --- .../AddressablesIntegrationTestsImpl.cs | 20 +- Tests/Runtime/AssetBundleProviderTests.cs | 59 + .../Operations/BaseOperationBehaviorTests.cs | 19 +- .../ResourceManager/OperationsCacheTests.cs | 85 ++ .../OperationsCacheTests.cs.meta | 3 + .../ResourceManagerBaseTests.cs | 4 +- .../ResourceManager/ResourceManagerTests.cs | 41 + .../ResourceManagerVirtualModeTests.cs | 6 +- Tests/Runtime/SyncAddressableTests.cs | 43 + Tests/Runtime/TextDataProviderTests.cs | 65 + Tests/Runtime/TextDataProviderTests.cs.meta | 3 + ValidationExceptions.json | 2 +- package.json | 13 +- 83 files changed, 2161 insertions(+), 1964 deletions(-) create mode 100644 Documentation~/ContinuousIntegration.md create mode 100644 Documentation~/images/ProfilesWithPathPairsExample1.png create mode 100644 Documentation~/images/ProfilesWithPathPairsExample2.png create mode 100644 Editor/Settings/ProfileGroupType.cs rename Tests/Editor/AssetReferenceDrawerTests.cs.meta => Editor/Settings/ProfileGroupType.cs.meta (80%) create mode 100644 Runtime/ResourceManager/Util/OperationCacheKeys.cs create mode 100644 Runtime/ResourceManager/Util/OperationCacheKeys.cs.meta create mode 100644 Samples~/ImportExistingGroup.meta create mode 100644 Samples~/ImportExistingGroup/.sample.json create mode 100644 Samples~/ImportExistingGroup/Editor.meta create mode 100644 Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs create mode 100644 Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs.meta delete mode 100644 Tests/Editor/AssetReferenceDrawerTests.cs create mode 100644 Tests/Editor/ProfileGroupTypeTests.cs create mode 100644 Tests/Editor/ProfileGroupTypeTests.cs.meta create mode 100644 Tests/Runtime/ResourceManager/OperationsCacheTests.cs create mode 100644 Tests/Runtime/ResourceManager/OperationsCacheTests.cs.meta create mode 100644 Tests/Runtime/TextDataProviderTests.cs create mode 100644 Tests/Runtime/TextDataProviderTests.cs.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index a874ff2b..0c57817c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ 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). +## [1.18.9] - 2021-06-04 +- Added "Select" button for Addressable Asset in Inspector to select the Asset in the Addressables Groups Window. +- Reduced the number of file copies required during building Addressables and moving Addressables content during Player build. +- Fixed issue with AssetReferenceUIRestriction not working with Lists and Arrays. +- Optimised loading AssetBundles to avoid redundent existing file checks. +- Fixed issue with folder asset entries throwing null ref exceptions when doing a Check for Content Update Restriction +- Added documentation about how to implement custom operations with synchronous behavior +- Added option on AddressableAssetSettings to strip the Unity version from the AssetBundle hash during build. +- Added documentation about useful tools you can use when building Addressables content with a CI pipeline +- Added Import Groups tool to Samples folder. +- Updated documentation for setting up and importing addressable assets in packages." +- Fixed issue where multi-group drag and drop places groups in reverse order. +- Fixed issue where an asset entry is no longer selected in the Project window after it is modified on disk. +- Fixed simulated play mode when "Internal Asset Naming Mode" was set to something other than "Full Path" +- Fixed issues with WaitForCompletion getting stuck in infinite loop during failed operations +- Organised AddressableAssetSettings GUI into more distint setting types. +- Fixed issue where the wrong operation would sometimes be returned by the cache when a project contains over 10K addressable assets +- Added path pairs feature +- Fixed issue where AsyncOperationBase.HasExecuted isn't being reset when the operation is reused. +- Added check to ensure that ResourceManager.Update() is never called from within its own callstack. +- Added ability to rename labels from the label window. +- Added the DisableVisibleSubAssetRepresentations option in Settings. + ## [1.18.4] - 2021-05-06 - EditorOnly tagged GameObjects in Scenes are no longer detected as duplicates for Scene Analyze results. - Fixed issue when dragging multiple groups around within the groups window to set their display order. diff --git a/Documentation~/AddressableAssetSettings.md b/Documentation~/AddressableAssetSettings.md index 8d0f872e..6cbaffaf 100644 --- a/Documentation~/AddressableAssetSettings.md +++ b/Documentation~/AddressableAssetSettings.md @@ -22,35 +22,47 @@ For more information see [Getting Started](AddressableAssetsGettingStarted.md). The **Manage Profiles** button opens the Profiles window. You can also open the Groups window from the menu **Window** > **Asset Management** > **Addressables** > **Profiles**. For more information see [Addressable Assets Profiles](AddressableAssetsProfiles.md). +#### Diagnostics +| **Property:** | **Function:** | +|:---|:---| +| **Send Profiler Events** | Turning this on enables the use of the [Addressables Event Viewer](MemoryManagement.md#the-addressables-event-viewer). | +| **Log Runtime Exceptions** | When enabled, the Addressables system logs runtime exceptions to the [Unity console](xref:Console). Note that the Addressables system does not throw exceptions at runtime when it encounters loading issues, instead it adds to the error state of the IAsyncOperation and, if this flag is enabled, logs the exception. | + #### Catalog | **Property:** | **Function:** | |:---|:---| -| **Disable Catalog Update on Startup** | Whether the Addressables system should skip the check for an updated content catalog when the Addressables system [initializes](InitializeAsync.md).
Note that you can update the catalog later using [Addressables.UpdateCatalogs](UpdateCatalogs.md). | | **Player Version Override** | Overrides the player version used to generated catalog names.
If left blank, the catalog version name is a timestamp of the current UTC time in the form of "Year.Month.Day.Hour.Minute.Second". | | **Compress Local Catalog** | Whether the local catalog should be serialized in an asset bundle (compressed) or as json (uncompressed). A compressed catalog uses less space on disk, but must be decompressed when loaded, which can increase CPU usage. | | **Optimize Catalog Size** | Enables size optimization of content catalogs. This can increase CPU usage when loading the catalog. | + +#### Content Update +| **Property** | **Function:** | +|:---|:---| +| **Disable Catalog Update on Startup** | Whether the Addressables system should skip the check for an updated content catalog when the Addressables system [initializes](InitializeAsync.md).
Note that you can update the catalog later using [Addressables.UpdateCatalogs](UpdateCatalogs.md). | +| **Content State Build Path** | The path to the folder in which to save the addressables_content_state.bin file. If empty, the file is saved to Assets/AddressableAssetsData. | | **Build Remote Catalog** | Whether a remote catalog should be built-for and loaded-by the app. When enabled, content builds generate .json and .hash files for the catalog to **Build Path** and the Addressables system loads these files from **Load Path** at runtime. The system caches the catalog and compares the remote .hash file to the cached version to determine if the catalog itself should be updated (along with any changed AssetBundles). In order to update content in an existing, built app, you must build and host a remote catalog. Overwriting the catalog is how the app gets informed of the updated content. See [Profiles](AddressableAssetsProfiles.md) for more information of configuring build and load paths.| | **- Build Path** | The path at which to build the content catalog for online retrieval. Typically, this path should be the same as the build path that you use for your remote Addressables groups, such as the RemoteBuildPath profile variable.| | **- Load Path** | The path or URL from which to load the remote content catalog. Typically, this path should be the same as the load path that you use for your remote Addressables groups, such as the RemoteLoadPath profile variable. It is your responsibility to copy or upload the remote catalog files so that your app can access them at the specified location. | -| **Catalog Download Timeout** | The number of seconds to wait before a catalog .hash or .json download times out. Set to 0 for no timeout. See [UnityWebRequest.timeout](xref:UnityWebRequest.timeout) for more information.| -#### General +#### Downloads | **Property:** | **Function:** | |:---|:---| -| **Send Profiler Events** | Turning this on enables the use of the [Addressables Event Viewer](MemoryManagement.md#the-addressables-event-viewer). | -| **Log Runtime Exceptions** | When enabled, the Addressables system logs runtime exceptions to the [Unity console](xref:Console). Note that the Addressables system does not throw exceptions at runtime when it encounters loading issues, instead it adds to the error state of the IAsyncOperation and, if this flag is enabled, logs the exception. | | **Custom Certificate Handler** | The class to use for custom certificate handling. This type must inherit from [UnityEngine.Networking.CertificateHandler](xref:Networking.CertificateHandler). | +| **Max Concurrent Web Requests** | The maximum number of concurrent web requests. This value is be clamped from 1 to 1024. | +| **Catalog Download Timeout** | The number of seconds to wait before a catalog .hash or .json download times out. Set to 0 for no timeout. See [UnityWebRequest.timeout](xref:UnityWebRequest.timeout) for more information.| + +#### Build +| **Property:** | **Function:** | +|:---|:---| +| **Ignore Invalid/Unsupported Files in Build** | Whether unsupported files during build should be ignored or treated as an error. | | **Unique Bundle IDs** | When enabled, AssetBundles are assigned unique, more complex internal identifiers. This may result in more bundles being rebuilt. See [Content Update Workflow](ContentUpdateWorkflow.md#unique-bundle-ids) for more information. | | **Contiguous Bundles** | When enabled, the Addressables build script packs assets in bundles contiguously based on the ordering of the source asset, which results in improved asset loading times. Unity recommends that you enable this option. However, enabling this option does result in binary differences in the bundles produced. Disable this option if you've built bundles with a version of Addressables older than 1.12.1 and you want to minimize bundle changes. | | **Non-recursive Dependency Calculation** | Calculate and build asset bundles using Non-Recursive Dependency calculation methods. This approach helps reduce asset bundle rebuilds and runtime memory consumption. Unity recommends that you enable this option. However, enabling this option does result in binary differences in the bundles produced.
**Requires Unity 2020.2.1 or above** | -| **Max Concurrent Web Requests** | The maximum number of concurrent web requests. This value is be clamped from 1 to 1024. | -| **Group Hierarchy with Dashes** | If enabled, group names are parsed as if a '-' represented a child in hierarchy. So a group called 'a-b-c' would be displayed as if it were in a folder called 'b' that lived in a folder called 'a'. In this mode, only groups without '-' can be rearranged within the groups window. | -| **Ignore Invalid/Unsupported Files in Build** | Whether unsupported files during build should be ignored or treated as an error. | -| **Content State Build Path** | The path to the folder in which to save the addressables_content_state.bin file. If empty, the file is saved to Assets/AddressableAssetsData. | | **Shader Bundle Naming Prefix** | Sets the naming convention used for the Unity built in shader bundle at build time.
The recommended setting is Project Name. | | **- Shader Bundle Custom Prefix** | Custom Unity built-in shader bundle prefix that is used if AddressableAssetSettings.ShaderBundleNaming is set to ShaderBundleNaming.Custom. | | **Mono Bundle Naming Prefix** | Sets the naming convention used for the MonoScript bundle at build time. A MonoScript contains information for loading the corresponding runtime class.
The recommended setting is Project Name | | **- Mono Bundle Custom Prefix** | Custom MonoScript bundle prefix that is used if AddressableAssetSettings.MonoScriptBundleNaming is set to MonoScriptBundleNaming.Custom. | +| **Strip Unity Version from AssetBundles** | When enabled, Unity Editor Version is stripped from the header of the AssetBundle during a build. | #### Build and Play Mode Scripts Displays an orderable list of Build Scripts available to use for building Addressables. When Addressables is first set up for a Project, four Build Scripts are included: diff --git a/Documentation~/AddressableAssetsDevelopmentCycle.md b/Documentation~/AddressableAssetsDevelopmentCycle.md index 3edfb069..d5c4b6ed 100644 --- a/Documentation~/AddressableAssetsDevelopmentCycle.md +++ b/Documentation~/AddressableAssetsDevelopmentCycle.md @@ -22,11 +22,15 @@ The Addressable Assets package has three build scripts that create Play mode dat #### Use Asset Database (faster) Use Asset Database mode ([`BuildScriptFastMode`](xref:UnityEditor.AddressableAssets.Build.DataBuilders.BuildScriptFastMode)) allows you to run the game quickly as you work through the flow of your game. It loads Assets directly through the Asset database for quick iteration with no analysis or AssetBundle creation. +****Please Note****: Modification's to Serialised Assets (Such as Prefab's and ScriptableObject's) loaded using Asset Database mode modifies the source Asset in the Project. + #### Simulate Groups (advanced) Simulate Groups mode ([`BuildScriptVirtualMode`](xref:UnityEditor.AddressableAssets.Build.DataBuilders.BuildScriptVirtualMode)) analyzes content for layout and dependencies without creating AssetBundles. Assets load from the Asset database though the [`ResourceManager`](xref:UnityEngine.ResourceManagement.ResourceManager), as if they were loaded through bundles. To see when bundles load or unload during game play, view the Asset usage in the [**Addressables Event Viewer** window](MemoryManagement.md#the-addressables-event-viewer) (**Window** > **Asset Management** > **Addressables** > **Event Viewer**). Simulate Groups mode helps you simulate load strategies and tweak your content groups to find the right balance for a production release. +****Please Note****: Modification's to Serialised Assets (Such as Prefab's and ScriptableObject's) loaded using Asset Database mode modifies the source Asset in the Project. + #### Use Existing Build (requires built groups) Use Existing Build mode most closely matches a deployed application build, but it requires you to build the data as a separate step. If you aren't modifying Assets, this mode is the fastest since it does not process any data when entering Play mode. You must either build the content for this mode in the **Addressables Groups** window (**Window** > **Asset Management** > **Addressables** > **Groups**) by selecting **Build** > **New Build** > **Default Build Script**, or using the [`AddressableAssetSettings.BuildPlayerContent()`](xref:UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.BuildPlayerContent) method in your game script. diff --git a/Documentation~/AddressableAssetsGettingStarted.md b/Documentation~/AddressableAssetsGettingStarted.md index 7894e75b..eb3abfc7 100644 --- a/Documentation~/AddressableAssetsGettingStarted.md +++ b/Documentation~/AddressableAssetsGettingStarted.md @@ -21,6 +21,8 @@ In your **Project** window, select the desired asset to view its Inspector. In t ![Marking an asset as Addressable in the Inspector window.](images/inspectorcheckbox.png)
_Marking an asset as Addressable in the **Inspector** window._ +Pressing the `Select` button to the right of the inspector opens the Groups window as sown below. Focusing on the Addressables Entry for the selected Asset. + #### Using the Addressables window Select **Window** > **Asset Management** > **Addressables** > **Groups** to open the **Addressables Groups** window. Next, drag the desired asset from your **Project** window into one of the asset groups in the **Addressables Groups** window. @@ -40,16 +42,39 @@ The Addressables Asset System needs to build your content into files that can be See [Build layout report](DiagnosticTools.md#build-layout-report) for information on how to generate a report about the layout of your built content. -## Assets in Packages +## Addressable Assets in Custom Packages + +**Important**: Marking package assets as Addressable requires Unity version 2019.4.10f1 or later and Asset Import Pipeline v2. For 2019.4 versions the pipeline version being used can be found in **Edit** > **Preferences** > **Cache Server (global)** > **Active Version**. On newer versions (2020.1 or later) Asset Import Pipeline v2 is enabled by default. + +### Exporting Addressable Groups to packages +Create a group in the **Addressables Groups** window. When you are done modifying the group, save the project. Move the group asset and its respective schema assets into your package directory. In preparation for the import process keep the schema assets in one folder, separate from the group asset. + +Any projects that use your package will need to have Addressables Assets package installed. In your `package.json` file, list `com.unity.addressables` as a dependency like so: + +``` +{ + "name": "com.unity.example", + "version": "1.2.3", + "dependencies": { + "com.unity.addressables": "1.17.17" + } +} +``` + +### Importing Addressable Groups from packages + +Importing a group from another project directly, including from a package doesn't set all the internal references correctly. You can either reset these references by creating a custom script or use the Import Groups tool available with the package samples. -**Important**: Marking package assets as Addressable requires Unity version 2020.2.0a9 or later. +#### Import Groups Tool -### Creating Addressable Groups in packages -Create a group in the **Addressables Groups** window. When you are done modifying the group, save the project. Move the group asset and its respective schema assets into your package. +The Addressables package comes with several code samples, including an *Import Groups* tool. The Samples folder can be added to the project through the Addressables page in Package Manager. -Open a new project that uses your package. If your group has a “Content Packing & Unloading” schema, update its build and load paths. Your group can now be included in your next Addressables build. +1. Create the default settings object via the **Addressables Groups** Window. Group assets must always be linked to a settings object in order to build your Addressables content. +2. If Samples is installed correctly there will be new options in the top menu bar. Go to **Window** > **Asset Management** > **Addressables** > **Import Groups** to open the Import Groups Window. +3. Enter the group path (i.e. `Packages/com.unity.example/MyGroup.asset`) and click **Import Groups**. This will copy the group to the default location (`Assets/AddressableAssetsData`) and link the group to the default settings object. Using the copy will prevent any unintentional modifications to the original asset in the package. +4. Enter group name (i.e. `MyGroup`) if it's not already auto-populated and the schema folder path (i.e. `Packages/com.unity.example/Schemas`). Then click **Import Schemas**. This will copy the schemas to the default location (`Assets/AddressableAssetsData`) and link them to the specified group. It will also reset the build and load paths of a “Content Packing & Unloading” schema to the LocalBuildPath and LocalLoadPath. The references we just modified are stored internally as GUIDs, which are project-specific. Relinking the schemas and resetting the paths allow them to be properly evaluated in the current project. -If you want to modify the group again, make sure to close all projects that use the package and reopen them once you save all modifications. This will reload the group asset. If your group has a “Content Packing & Unloading” schema, update its build and load paths again. +At this point your group can now be included in the next Addressables build. If you want to modify the package group asset again, make sure to close all projects that use the package and reopen them once you save all modifications. This will reload the group asset. You will need to do the import process again for any projects that use the package. ## Using Addressable Assets ### Loading or instantiating by address @@ -113,7 +138,7 @@ In addition, you can use an [`AssetReference`](xref:UnityEngine.AddressableAsset For [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) objects specifically, note that the atlas inspector has a checkbox for **Include In Build**. This option does not determine whether the atlas itself is built into AssetBundles, rather it determines how items reference it. When you enable **Include in Build**, all dependency linkages remain intact. Thus, if you have an Addressable prefab sprite that is dependent on an atlas, the atlas will be pulled into an AssetBundle. Similarly, if you explicilty mark the atlas as Addressable, the prefab will list the atlas bundle as a dependency, and things are hooked up properly at load time. If you disable **Include In Build**, the linkage is not preserved. When you load an Addressable prefab that is dependent on the atlas, the atlas will be requested, and you must load and manage the connection manually using [SpriteAtlasManager.atlasRequested](https://docs.unity3d.com/ScriptReference/U2D.SpriteAtlasManager-atlasRequested.html). In this scenario, you can still mark the atlas as Addressable to access each sprite manually. -When viewing a [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) in the **Addressables Groups** window, ensure the [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) has been packed if you intend to view the sub-objects in the Window. This can be done by clicking **Pack Preview** in the inspector of the [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) object. If the [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) is packed and you still cannot see the sub-objects, check the **Show Sprite and Subobject Addresses** option is enabled; it is located in the **Tools** menu of the **Addressables Groups** window. +When viewing a [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) in the **Addressables Groups** window, ensure the [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) has been packed if you intend to view the sub-objects in the Window. This can be done by clicking **Pack Preview** in the inspector of the [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) object. If the [SpriteAtlas](https://docs.unity3d.com/Manual/class-SpriteAtlas.html) is packed and you still cannot see the sub-objects, check the **Show Sprite and Subobject Addresses** option is enabled; it is located in the **Tools** > **Groups View** menu of the **Addressables Groups** window. ### Using the AssetReference class The [`AssetReference`](xref:UnityEngine.AddressableAssets.AssetReference) class provides a way to access Addressable Assets without needing to know their addresses. To access an Addressable Asset using the `AssetReference` class: diff --git a/Documentation~/AddressableAssetsHostingServices.md b/Documentation~/AddressableAssetsHostingServices.md index e22256bc..3b5783ec 100644 --- a/Documentation~/AddressableAssetsHostingServices.md +++ b/Documentation~/AddressableAssetsHostingServices.md @@ -12,7 +12,7 @@ Moving from Editor Play mode testing to platform application build testing intro This article details the initial setup of Asset Hosting Services for your project. While the setup guide focuses on Editor workflows, you can use the API to configure Hosting Services by setting the [`HostingServicesManager`](xref:UnityEditor.AddressableAssets.HostingServices.HostingServicesManager) property of the [`AddressableAssetSettings`](xref:UnityEditor.AddressableAssets.Settings.AddressableAssetSettings) class. ### Configuring a new Hosting Service -Use the **Hosting window** to add, configure, and enable new Hosting Services. In the Editor, select **Window** > **Asset Management** > **Addressables** > **Hosting**, or click the **Tools** > **Hosting Services** button from the **Addressables Groups** window menu to access the **Addressables Hosting** window. +Use the **Hosting window** to add, configure, and enable new Hosting Services. In the Editor, select **Window** > **Asset Management** > **Addressables** > **Hosting**, or click the **Tools** > **Window** > **Hosting Services** button from the **Addressables Groups** window menu to access the **Addressables Hosting** window. ![The Addressables Hosting window.](images/HostingServicesWindow_1.png)
_The **Addressables Hosting** window._ diff --git a/Documentation~/AddressableAssetsProfiles.md b/Documentation~/AddressableAssetsProfiles.md index f5bc670a..f1035d8a 100644 --- a/Documentation~/AddressableAssetsProfiles.md +++ b/Documentation~/AddressableAssetsProfiles.md @@ -10,7 +10,7 @@ For each profile, you specify values for each variable. This way, instead of man There are multiple ways to access the Profile setup window in the Editor. * Select **Window** > **Asset Management** > **Addressables** > **Profiles**. -* Select **Window** > **Asset Management** > **Addressables** > **Groups**, then select **Tools** > **Profiles**. +* Select **Window** > **Asset Management** > **Addressables** > **Groups**, then select **Tools** > **Window** > **Profiles**. * Select **Window** > **Asset Management** > **Addressables** > **Groups**, then select **Profile** > **Manage Profiles**.. * You can also access these settings via the `AddressableAssetSettings` Inspector. @@ -26,6 +26,18 @@ There are five variables we provide by default: You can add or remove variables at any time. +### Path Pairs +Profile variables are automatically divided into a prefix and postfix by the `.` character. Profile variables sharing a prefix and a set of specific postfixes, `BuildPath` and `LoadPath`, are grouped together into a _path pair_. + +A path pair is defined by the following criteria: +1. two variables with a common prefix separated by a `.` character +2. one variable postfix following the `.` must be `BuildPath` +3. one variable postfix following the `.` must be `LoadPath` + +![Path pairs grouped by a common prefix and separated by a period.](images/ProfilesWithPathPairsExample1.png)
+_The **Addressables Profiles** window showing two profiles with two path pairs._ + + ### Adding a new profile You can create new profile by selecting **Create** > **Profile**. A new profile row appears in the table. @@ -38,10 +50,12 @@ You can create new variables to add to the default set by selecting **Create** > Right-click the variable name to rename or delete the variable. You can likewise rename or delete the provided variables. +If a pair of variables meets the path pair criteria, they are automatically grouped into a path pair. + ### Syntax All variables are of type "string". In general, you can type in exactly the needed path or value, but there are two additional syntax designators allowed: * Square brackets [ ]. Items surrounded by square brackets are evaluated at build time. The values inside can be other profile variables (such as [BuildTarget]) or code variables (such as [UnityEditor.EditorUserBuildSettings.activeBuildTarget]). During build time, as the groups are being processed, the items inside square brackets are evaluated and the resulting string is written into the catalog. -* Curly brackets { }. Items surrounded by curly brackets are evaluated at runtime. Generally values here will be code variables (such as {UnityEngine.AddressableAssets.Addressables.RuntimePath}). +* Curly brackets { }. Items surrounded by curly brackets are evaluated at runtime. Generally values here are code variables (such as {UnityEngine.AddressableAssets.Addressables.RuntimePath}).
For example, you have a load path of: `{MyNamespace.MyClass.MyURL}/content/[BuildTarget]}` set on a group that is creating an AssetBundle called "trees.bundle". During the build, the catalog would register the load path for that bundle as `{MyNamespace.MyClass.MyURL}/content/Android/trees.bundle}`. Then, at startup, as the catalog is being processed, the profile system would evaluate MyNamespace.MyClass.MyURL to end up with the final load path of `http://myinternet.com/content/Android/trees.bundle`. @@ -64,6 +78,9 @@ While in development, you would have both your local and remote bundles using lo ![Creating a service profile.](images/ProfilesExample3.png)
_Paths set for local development._ +![Creating a service profile with path pairs.](images/ProfilesWithPathPairsExample2.png)
+_Path pairs set for local development._ + In this instance, you can see that the local and remote paths are in fact local, which makes more sense for development, as setting up a remote server would be a pain. However, once the content is ready for production, you would move the remote bundles to a server, as the diagram below shows. ![Creating a service profile.](images/ProfilesExample2.png)
@@ -71,4 +88,4 @@ _Content with remote bundle moved to a server for production._ In this case, using profiles, you could change the remote load path for "Production" to that server. Without having to change your asset groups, you can change all of your remote bundles to actually become remote. -It should be noted, variables are arbitrary key-value pairs to aid development. Setting variables such as "LocalBuildPath" does not guarantee that data built to that location is copied to the StreamingAssets path of a Player build. Data is only copied to the Player's StreamingAssets folder from `Addressables.BuildPath`. \ No newline at end of file +It should be noted, variables are arbitrary key-value pairs to aid development. Setting variables such as "LocalBuildPath" does not guarantee that data built to that location is copied to the StreamingAssets path of a Player build. Data is only copied to the Player's StreamingAssets folder from `Addressables.BuildPath`. diff --git a/Documentation~/AddressablesAPI.md b/Documentation~/AddressablesAPI.md index daab4602..c943b15a 100644 --- a/Documentation~/AddressablesAPI.md +++ b/Documentation~/AddressablesAPI.md @@ -5,6 +5,7 @@ uid: addressables-api This page exists in addition to the standard Addressable Assets [Scripting API documentation](xref:addressables-script-ref). The purpose of this expanded API documentation is to provide quick access and a deeper understanding of often-used or important API items, including code samples where relevant. +* [AddressableAssetSettings](AddressableAssetSettings.md) * [BuildPlayerContent](BuildPlayerContent.md) * [DownloadDependenciesAsync](DownloadDependenciesAsync.md) * [ExceptionHandler](ExceptionHandler.md) diff --git a/Documentation~/AddressablesFAQ.md b/Documentation~/AddressablesFAQ.md index 21170868..e1dde86e 100644 --- a/Documentation~/AddressablesFAQ.md +++ b/Documentation~/AddressablesFAQ.md @@ -44,10 +44,32 @@ If you are planning to do content updates, you will need the version of this fil ### What are possible scale implications? As your project grows larger, keep an eye on the following aspects of your assets and bundles: * Total bundle size - Historically Unity has not supported files larger than 4GB. This has been fixed in some recent editor versions, but there can still be issues. It is recommended to keep the content of a given bundle under this limit for best compatibility across all platforms. -* Sub assets affecting UI performance - There is no hard limit here, but if you have many assets, and those assets have many sub-assets, it may be best to turn off sub-asset display. This option only affects how the data is displayed in the Groups window, and does not affect what you can and cannot load at runtime. The option is available in the groups window under **Tools** > **Show Sprite and Subobject Addresses**. Disabling this will make the UI more responsive. -* Group hierarchy display - Another UI-only option to help with scale is **Group Hierarchy with Dashes**. This is available within the inspector of the top level settings. With this enabled, groups that contain dashes '-' in their names will display as if the dashes represented folder hierarchy. This does not affect the actual group name, or the way things are built. For example, two groups called "x-y-z" and "x-y-w" would display as if inside a folder called "x", there was a folder called "y". Inside that folder were two groups, called "x-y-z" and "x-y-w". This will not really affect UI responsiveness, but simply makes it easier to browse a large collection of groups. +* Sub assets affecting UI performance - There is no hard limit here, but if you have many assets, and those assets have many sub-assets, it may be best to turn off sub-asset display. This option only affects how the data is displayed in the Groups window, and does not affect what you can and cannot load at runtime. The option is available in the groups window under **Tools** > **Groups View** > **Show Sprite and Subobject Addresses**. Disabling this will make the UI more responsive. +* Group hierarchy display - Another UI-only option to help with scale is **Group Hierarchy with Dashes**. The option is available in the groups window under **Tools** > **Groups View** > **Group Hierarchy with Dashes**. With this enabled, groups that contain dashes '-' in their names will display as if the dashes represented folder hierarchy. This does not affect the actual group name, or the way things are built. For example, two groups called "x-y-z" and "x-y-w" would display as if inside a folder called "x", there was a folder called "y". Inside that folder were two groups, called "x-y-z" and "x-y-w". This will not really affect UI responsiveness, but simply makes it easier to browse a large collection of groups. * Bundle layout at scale - For more information about how best to set up your layout, see the earlier question: [_Is it better to have many small bundles or a few bigger ones_](AddressablesFAQ.md#Is-it-better-t-have-many-small-bundles-or-a-few-bigger-ones) +### Is it safe to edit loaded Assets? +When editing Assets loaded from Bundles, in a Player or when using "Use Existing Build (requires built groups)" playmode setting. The Assets are loaded from the Bundle and only exist in memory. Changes cannot be written back to the Bundle on disk, and any modifications to the Object in memory do not persist between sessions. + +This is different when using "Use Asset Database (fastest)" or "Simulate Groups (advanced)" playmode settings, in these modes the Assets are loaded from the Project files. Any modifications that are made to loaded Asset modifies the Project Asset, and are saved to file. + +In order to prevent this, when making runtime changes, create a new instance of the Object to modify and use as the Object to create an instance of with the Instantiate method. As shown in the example code below. + +``` +var op = Addressables.LoadAssetAsync("myKey"); +yield return op; +if (op.Result != null) +{ + GameObject inst = UnityEngine.Object.Instantiate(op.Result); + // can now use and safely make edits to inst, without the source Project Asset being changed. +} +``` + +****Please Note****, When instancing an Object: +* The AsyncOperationHandle or original Asset must be used when releasing the Asset, not the instance. +* Instantiating an Asset that has references to other Assets does not create new instances other those referenced Assets. The references remain targeting the Project Asset. +* Unity Methods are invoked on the new instance, such as Start, OnEnable, and OnDisable. + ### Is it possible to retrieve the address of an asset or reference at runtime? In the most general case, loaded assets no longer have a tie to their address or `IResourceLocation`. There are ways, however, to get the properly associated `IResourceLocation` and use that to read the field PrimaryKey. The PrimaryKey field will be set to the assets's address unless "Include Address In Catalog" is disabled for the group this object came from. In that case, the PrimaryKey will be the next item in the list of keys (probably a GUID, but possibly a Label or empty string). diff --git a/Documentation~/ContinuousIntegration.md b/Documentation~/ContinuousIntegration.md new file mode 100644 index 00000000..8ed1923a --- /dev/null +++ b/Documentation~/ContinuousIntegration.md @@ -0,0 +1,37 @@ +## Continuous Integration +A common use case for Addressables is having a Continuous Integration (CI) system perform the player content build and player builds. While each team and project setup is unique, here are some general guidelines for building Addressables with CI systems. + +### Selecting a Content Builder +One of the main choices when building Addressables content is selecting a Content Builder. By default, if you call `AddressableAssetSettings.BuildPlayerContent()` the `BuildScriptPackedMode` is the Data Builder that gets used. This is because `BuildPlayerContent()` checks the `ActivePlayerDataBuilder` and calls into that script's `BuildDataImplementation(..)` + +If you've implemented your own custom `IDataBuilder` and want to use it for your CI builds, you'll need to set the `ActivePlayerDataBuilderIndex`. This is not a static property so you'll need to go through an instance of the `AddressableAssetSettings`. By default you can access this through `AddressableAssetSettingsDefaultObject.Settings`. This index refers to the position of the `IDataBuilder` in the list of `AddressableAssetSettings.DataBuilders` Here's a code sample to set a custom `IDataBuilder`: + +``` +public static void SetCustomDataBuilder(IDataBuilder builder) +{ + AddressableAssetSettings settings + = AddressableAssetSettingsDefaultObject.Settings; + + int index = settings.DataBuilders.IndexOf((ScriptableObject) builder); + if (index > 0) + settings.ActivePlayerDataBuilderIndex = index; + else if (AddressableAssetSettingsDefaultObject.Settings.AddDataBuilder(builder)) + settings.ActivePlayerDataBuilderIndex = AddressableAssetSettingsDefaultObject.Settings.DataBuilders.Count - 1; + else + Debug.LogWarning($"{builder} could not be found or added to the list of DataBuilders"); +} +``` +If your custom `IDataBuilder` has already been added to the list of `DataBuilders` in the project prior, all that needs to happen is for you to be sure the `ActivePlayerDataBuilderIndex` is set to the correct index. + +### Cleaning the Addressables Content Builder Cache +`IDataBuilder` implementations define a `ClearCachedData()` which is responsible for cleaning up the files created by that data builder. For example, the built in `BuildScriptPackedMode` deletes the following: +- The content catalog +- The serialized settings file +- The built AssetBundles +- Any link.xml files created + +calling `IDataBuilder.ClearCachedData()` as part of your CI process makes sure no pre-generated content is used, if that is desired. +### Cleaning the Scriptable Build Pipeline Cache +Cleaning the Scriptable Build Pipeline (SBP) cache cleans the `BuildCache` folder from the `Library` directory along with all the hash maps generated by the build and the Type Database. The `Library/BuildCache` folder contains `.info` files created by SBP during the build which speeds up subsequent builds by reading data from these `.info` files instead of re-generating data that hasn't changed. + +How to clear the SBP cache in a script without prompting: `BuildCache.PurgeCache(false);` \ No newline at end of file diff --git a/Documentation~/DiagnosticTools.md b/Documentation~/DiagnosticTools.md index 386f5103..bef4acba 100644 --- a/Documentation~/DiagnosticTools.md +++ b/Documentation~/DiagnosticTools.md @@ -31,7 +31,7 @@ See [Unity Scriptable Build Pipeline](https://docs.unity3d.com/Packages/com.unit Analyze is a tool that gathers information on your Projects' Addressables layout. In some cases, Analyze may take appropriate actions to clean up the state of your Project. In others, Analyze is purely an informational tool that allows you to make more informed decisions about your Addressables layout. ### Using Analyze -In the Editor, open the **Addressables Analyze** window (**Window** > **Asset Management** > **Addressables** > **Analyze**), or open it via the **Addressables Groups** window by clicking the **Tools** > **Analyze** button. +In the Editor, open the **Addressables Analyze** window (**Window** > **Asset Management** > **Addressables** > **Analyze**), or open it via the **Addressables Groups** window by clicking the **Tools** > **Window** > **Analyze** button. The Analyze window displays a list of Analyze rules, along with the following operations: diff --git a/Documentation~/SynchronousAddressables.md b/Documentation~/SynchronousAddressables.md index 32206b31..8bdad8dd 100644 --- a/Documentation~/SynchronousAddressables.md +++ b/Documentation~/SynchronousAddressables.md @@ -30,4 +30,8 @@ void Start() Addressables.Release(op); } -``` \ No newline at end of file +``` +### Synchronous Addressables with Custom Operations +Addressables supports custom `AsyncOperations` which support unique implementations of `InvokeWaitForCompletion`. This overridable method is what you'll use to implement custom synchronous operations. + +Custom operations work with `ChainOperations` and `GroupsOperations`. If you require chained operations to be completed synchronously, ensure that your custom operations implement `InvokeWaitForCompletion` and create a `ChainOperation` using your custom operations. Similarly, `GroupOperations` are well suited to ensure a collection of `AsyncOperations`, including custom operations, complete together. Both `ChainOperation` and `GroupOperation` have their own implementations of `InvokeWaitForCompletion` that relies on the `InvokeWaitForCompletion` implementations of the operations they depend on. \ No newline at end of file diff --git a/Documentation~/TableOfContents.md b/Documentation~/TableOfContents.md index 152d0793..dcbd0ebe 100644 --- a/Documentation~/TableOfContents.md +++ b/Documentation~/TableOfContents.md @@ -59,6 +59,8 @@ * [Addressables content state](AddressablesFAQ.md#What-is-addressables_content_state) * [Scale implications](AddressablesFAQ.md#What-are-possible-scale-implications) * [Address Lookup](AddressablesFAQ.md#is-it-possible-to-retrieve-the-address-of-an-asset-or-reference-at-runtime) + * [Building and Recompiling Scripts](AddressablesFAQ.md#can-i-build-addressables-when-recompiling-scripts) + * [Using Addressables and CI Pipelines](ContinuousIntegration.md) * [Expanded API documentation](AddressablesAPI.md) * [AddressableAssetSettings](AddressableAssetSettings.md) * [BuildPlayerContent](BuildPlayerContent.md) diff --git a/Documentation~/images/AddressableAssetSettings.png b/Documentation~/images/AddressableAssetSettings.png index ec4ebb04680eec15ee98db5bfc5111c3909b3b93..a40ea97912a9a09d6b55e2aec51855959f9fa286 100644 GIT binary patch literal 126682 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 literal 119756 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 diff --git a/Documentation~/images/ProfilesWithPathPairsExample1.png b/Documentation~/images/ProfilesWithPathPairsExample1.png new file mode 100644 index 0000000000000000000000000000000000000000..f3945e250de27474ed70c318a75e90744b93969a GIT binary patch literal 38707 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 T3@Bk^00000NkvXXu0mjf)LP}! literal 7282 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 q3Z!g~KH>jq;y;8F501Mf2)> m_keyToEntries; + public Dictionary> m_keyToEntries; public Dictionary> m_Cache; public AddressableAssetTree m_AddressableAssetTree; HashSet m_Keys = null; @@ -97,7 +97,7 @@ public AddressableAssetSettingsLocator(AddressableAssetSettings settings) LocatorId = m_Settings.name; m_AddressableAssetTree = BuildAddressableTree(m_Settings); m_Cache = new Dictionary>(); - m_keyToEntries = new Dictionary>(m_Settings.labelTable.labelNames.Count); + m_keyToEntries = new Dictionary>(m_Settings.labelTable.labelNames.Count); using (new AddressablesFileEnumerationScope(m_AddressableAssetTree)) { foreach (AddressableAssetGroup g in m_Settings.groups) @@ -131,14 +131,14 @@ public AddressableAssetSettingsLocator(AddressableAssetSettings settings) } } - static void AddEntry(AddressableAssetEntry e, object k, Dictionary> keyToEntries) + static void AddEntry(AddressableAssetEntry e, object k, Dictionary> keyToEntries) { - if (!keyToEntries.TryGetValue(k, out List entries)) - keyToEntries.Add(k, entries = new List()); + if (!keyToEntries.TryGetValue(k, out HashSet entries)) + keyToEntries.Add(k, entries = new HashSet()); entries.Add(e); } - static void AddEntriesToTables(Dictionary> keyToEntries, AddressableAssetEntry e) + static void AddEntriesToTables(Dictionary> keyToEntries, AddressableAssetEntry e) { AddEntry(e, e.address, keyToEntries); AddEntry(e, e.guid, keyToEntries); @@ -204,7 +204,7 @@ public bool Locate(object key, Type type, out IList locations return locations != null; locations = new List(); - if (m_keyToEntries.TryGetValue(key, out List entries)) + if (m_keyToEntries.TryGetValue(key, out HashSet entries)) { foreach (AddressableAssetEntry e in entries) { diff --git a/Editor/Build/AddressablesPlayerBuildProcessor.cs b/Editor/Build/AddressablesPlayerBuildProcessor.cs index 7de62cb1..c3312e33 100644 --- a/Editor/Build/AddressablesPlayerBuildProcessor.cs +++ b/Editor/Build/AddressablesPlayerBuildProcessor.cs @@ -19,7 +19,7 @@ public int callbackOrder } /// - /// Removes temporary data created as part of a build. + /// Restores temporary data created as part of a build. /// public void OnPostprocessBuild(BuildReport report) { @@ -31,9 +31,8 @@ internal static void CleanTemporaryPlayerBuildData() { if (Directory.Exists(Addressables.PlayerBuildDataPath)) { - DirectoryUtility.DeleteDirectory(Addressables.PlayerBuildDataPath, false); - //Will delete the directory only if it's empty - DirectoryUtility.DeleteDirectory(Application.streamingAssetsPath); + DirectoryUtility.DirectoryMove(Addressables.PlayerBuildDataPath, Addressables.BuildPath); + DirectoryUtility.DeleteDirectory(Application.streamingAssetsPath, onlyIfEmpty: true); } } @@ -48,6 +47,17 @@ public void OnPreprocessBuild(BuildReport report) internal static void CopyTemporaryPlayerBuildData() { if (Directory.Exists(Addressables.BuildPath)) - DirectoryUtility.DirectoryCopy(Addressables.BuildPath, Addressables.PlayerBuildDataPath, true); + { + if (Directory.Exists(Addressables.PlayerBuildDataPath)) + { + Debug.LogWarning($"Found and deleting directory \"{Addressables.PlayerBuildDataPath}\", directory is managed through Addressables."); + DirectoryUtility.DeleteDirectory(Addressables.PlayerBuildDataPath, false); + } + + string parentDir = Path.GetDirectoryName(Addressables.PlayerBuildDataPath); + if (!string.IsNullOrEmpty(parentDir) && !Directory.Exists(parentDir)) + Directory.CreateDirectory(parentDir); + Directory.Move(Addressables.BuildPath, Addressables.PlayerBuildDataPath ); + } } } diff --git a/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs b/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs index 338474ac..11c5ec1f 100644 --- a/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs +++ b/Editor/Build/BuildPipelineTasks/BuildLayoutGenerationTask.cs @@ -90,9 +90,7 @@ private BuildLayout CreateBuildLayout() { BuildLayout.Bundle bundle = new BuildLayout.Bundle(); bundle.Name = bundleName; - string path = m_Parameters.GetOutputFilePathForIdentifier(bundle.Name); UnityEngine.BuildCompression compression = m_Parameters.GetCompressionForIdentifier(bundle.Name); - bundle.FileSize = (ulong)new FileInfo(path).Length; bundle.Compression = compression.compression.ToString(); lookup.Bundles.Add(bundle.Name, bundle); } @@ -249,7 +247,8 @@ private BuildLayout CreateBuildLayout() AddressableAssetsBuildContext aaContext = (AddressableAssetsBuildContext)m_AaBuildContext; // Map from GUID to AddrssableAssetEntry Dictionary guidToEntry = aaContext.assetEntries.ToDictionary(x => x.guid, x => x); - + Dictionary groupNameToBuildPath = new Dictionary(); + // create groups foreach (AddressableAssetGroup group in aaContext.Settings.groups) { @@ -268,6 +267,7 @@ private BuildLayout CreateBuildLayout() { sd.KvpDetails.Add(new Tuple("PackingMode", bSchema.BundleMode.ToString())); sd.KvpDetails.Add(new Tuple("Compression", bSchema.Compression.ToString())); + groupNameToBuildPath[group.name] = bSchema.BuildPath.GetValue(aaContext.Settings); } grp.Schemas.Add(sd); } @@ -285,20 +285,37 @@ private BuildLayout CreateBuildLayout() b.ExpandedDependencies = deps2.Select(x => lookup.Bundles[x]).Where(x => b != x).ToList(); if (aaContext.bundleToAssetGroup.TryGetValue(b.Name, out string grpName)) - lookup.GroupLookup[grpName].Bundles.Add(b); + { + var assetGroup = lookup.GroupLookup[grpName]; + b.Name = m_BundleNameRemap[b.Name]; + b.FileSize = (ulong)new FileInfo(Path.Combine(groupNameToBuildPath[assetGroup.Name], b.Name)).Length; + assetGroup.Bundles.Add(b); + } else + { + // will still be associated with a group and can be found in assetGroupToBundles + foreach (KeyValuePair> pair in aaContext.assetGroupToBundles) + { + foreach (string s in pair.Value) + { + if (s == b.Name) + { + b.Name = m_BundleNameRemap[b.Name]; + b.FileSize = (ulong)new FileInfo(Path.Combine(groupNameToBuildPath[pair.Key.Name], b.Name)).Length; + break; + } + } + if (b.FileSize > 0) + break; + } layout.BuiltInBundles.Add(b); + } } // Apply the addressable name to the asset foreach (BuildLayout.ExplicitAsset a in BuildLayoutHelpers.EnumerateAssets(layout)) if (guidToEntry.TryGetValue(a.Guid, out AddressableAssetEntry entry)) a.AddressableName = entry.address; - - // The addressables build script can rename the bundles - foreach (BuildLayout.Bundle b in BuildLayoutHelpers.EnumerateBundles(layout)) - if (m_BundleNameRemap.TryGetValue(b.Name, out string newName)) - b.Name = newName; } return layout; diff --git a/Editor/Build/BuildPipelineTasks/GenerateLocationListsTask.cs b/Editor/Build/BuildPipelineTasks/GenerateLocationListsTask.cs index 22b45076..e2983677 100644 --- a/Editor/Build/BuildPipelineTasks/GenerateLocationListsTask.cs +++ b/Editor/Build/BuildPipelineTasks/GenerateLocationListsTask.cs @@ -39,6 +39,10 @@ public class GenerateLocationListsTask : IBuildTask [InjectContext(ContextUsage.In, true)] IBuildLogger m_Log; + + [InjectContext(ContextUsage.In)] + IBuildParameters m_Parameters; + #pragma warning restore 649 /// @@ -56,6 +60,7 @@ public ReturnCode Run() input.Settings = aaContext.Settings; input.BundleToAssetGroup = aaContext.bundleToAssetGroup; input.AddressableAssetEntries = aaContext.assetEntries; + input.Target = m_Parameters.Target; Output output = RunInternal(input); @@ -85,6 +90,7 @@ internal struct Input public AddressableAssetSettings Settings; public Dictionary BundleToAssetGroup; public List AddressableAssetEntries; + public BuildTarget Target; } internal struct Output @@ -187,7 +193,7 @@ internal static Output RunInternal(Input input) foreach (BundleEntry bEntry in bundleToEntry.Values) { string bundleProvider = GetBundleProviderName(bEntry.Group); - string bundleInternalId = GetLoadPath(bEntry.Group, bEntry.BundleName); + string bundleInternalId = GetLoadPath(bEntry.Group, bEntry.BundleName, input.Target); locations.Add(new ContentCatalogDataEntry(typeof(IAssetBundleResource), bundleInternalId, bundleProvider, new object[] { bEntry.BundleName })); } @@ -257,7 +263,7 @@ internal static string GetAssetProviderName(AddressableAssetGroup group) return group.GetSchema().GetAssetCachedProviderId(); } - static string GetLoadPath(AddressableAssetGroup group, string name) + internal static string GetLoadPath(AddressableAssetGroup group, string name, BuildTarget target) { var bagSchema = group.GetSchema(); if (bagSchema == null || bagSchema.LoadPath == null) @@ -265,10 +271,24 @@ static string GetLoadPath(AddressableAssetGroup group, string name) Debug.LogError("Unable to determine load path for " + name + ". Check that your default group is not '" + AddressableAssetSettings.PlayerDataGroupName + "'"); return string.Empty; } - var loadPath = bagSchema.LoadPath.GetValue(group.Settings) + "/" + name; + + var loadPath = bagSchema.LoadPath.GetValue(group.Settings) + PathSeparatorForPlatform(target) + name; if (!string.IsNullOrEmpty(bagSchema.UrlSuffix)) loadPath += bagSchema.UrlSuffix; return loadPath; } + + static char PathSeparatorForPlatform(BuildTarget target) + { + switch (target) + { + case BuildTarget.StandaloneWindows64: + case BuildTarget.StandaloneWindows: + case BuildTarget.XboxOne: + return '\\'; + default: + return '/'; + } + } } } diff --git a/Editor/Build/DataBuilders/AddressableAssetsBundleBuildParameters.cs b/Editor/Build/DataBuilders/AddressableAssetsBundleBuildParameters.cs index 8d3dd5b1..3a2901e0 100644 --- a/Editor/Build/DataBuilders/AddressableAssetsBundleBuildParameters.cs +++ b/Editor/Build/DataBuilders/AddressableAssetsBundleBuildParameters.cs @@ -3,6 +3,8 @@ using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEditor.Build.Pipeline; using UnityEngine; +using UnityEditor.Build.Content; +using BuildCompression = UnityEngine.BuildCompression; namespace UnityEditor.AddressableAssets.Build.DataBuilders { @@ -28,6 +30,8 @@ public AddressableAssetsBundleBuildParameters(AddressableAssetSettings aaSetting #if NONRECURSIVE_DEPENDENCY_DATA NonRecursiveDependencies = aaSettings.NonRecursiveBuilding; #endif + DisableVisibleSubAssetRepresentations = aaSettings.DisableVisibleSubAssetRepresentations; + m_settings = aaSettings; m_bundleToAssetGroup = bundleToAssetGroup; @@ -36,6 +40,11 @@ public AddressableAssetsBundleBuildParameters(AddressableAssetSettings aaSetting BundleCompression = ConverBundleCompressiontToBuildCompression(m_settings.DefaultGroup.GetSchema().Compression); else BundleCompression = target == BuildTarget.WebGL ? BuildCompression.LZ4Runtime : BuildCompression.LZMA; + +#if UNITY_2019_4_OR_NEWER + if (aaSettings.StripUnityVersionFromBundleBuild) + ContentBuildFlags |= ContentBuildFlags.StripUnityVersion; +#endif } private BuildCompression ConverBundleCompressiontToBuildCompression( diff --git a/Editor/Build/DataBuilders/BuildScriptPackedMode.cs b/Editor/Build/DataBuilders/BuildScriptPackedMode.cs index e570179d..2cc3ca31 100644 --- a/Editor/Build/DataBuilders/BuildScriptPackedMode.cs +++ b/Editor/Build/DataBuilders/BuildScriptPackedMode.cs @@ -519,7 +519,10 @@ internal static void SetAssetEntriesBundleFileIdToCatalogEntryBundleFileId(IColl { foreach (var loc in assetEntries) { - GUID guid = new GUID(loc.guid); + 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)) { @@ -531,10 +534,10 @@ internal static void SetAssetEntriesBundleFileIdToCatalogEntryBundleFileId(IColl 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) + if (loc.parentGroup?.GetSchema()?.BundleNaming == + BundledAssetGroupSchema.BundleNamingStyle.NoHash) { loc.BundleFileId = StripHashFromBundleLocation(loc.BundleFileId); } @@ -917,7 +920,7 @@ static IList RuntimeDataBuildTasks(string builtinShaderBundleName, s return buildTasks; } - static void CopyFileWithTimestampIfDifferent(string srcPath, string destPath, IBuildLogger log) + static void MoveFileToDestinationWithTimestampIfDifferent(string srcPath, string destPath, IBuildLogger log) { if (srcPath == destPath) return; @@ -928,12 +931,14 @@ static void CopyFileWithTimestampIfDifferent(string srcPath, string destPath, IB if (destTime == time) return; - using (log.ScopedStep(LogLevel.Verbose, "Copy File", $"{srcPath} -> {destPath}")) + using (log.ScopedStep(LogLevel.Verbose, "Move File", $"{srcPath} -> {destPath}")) { var directory = Path.GetDirectoryName(destPath); - if (!string.IsNullOrEmpty(directory)) + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) Directory.CreateDirectory(directory); - File.Copy(srcPath, destPath, true); + else if (File.Exists(destPath)) + File.Delete(destPath); + File.Move(srcPath, destPath); } } @@ -993,7 +998,7 @@ void PostProcessBundles(AddressableAssetGroup assetGroup, List buildBund var targetPath = Path.Combine(path, outputBundles[i]); var srcPath = Path.Combine(assetGroup.Settings.buildSettings.bundleBuildPath, buildBundles[i]); bundleRenameMap.Add(buildBundles[i], outputBundles[i]); - CopyFileWithTimestampIfDifferent(srcPath, targetPath, m_Log); + MoveFileToDestinationWithTimestampIfDifferent(srcPath, targetPath, m_Log); AddPostCatalogUpdatesInternal(assetGroup, postCatalogUpdateCallbacks, dataEntry, targetPath, registry); diff --git a/Editor/Build/DataBuilders/BuildScriptVirtualMode.cs b/Editor/Build/DataBuilders/BuildScriptVirtualMode.cs index cb676463..503cad7c 100644 --- a/Editor/Build/DataBuilders/BuildScriptVirtualMode.cs +++ b/Editor/Build/DataBuilders/BuildScriptVirtualMode.cs @@ -189,7 +189,7 @@ TResult DoBuild(AddressablesDataBuilderInput builderInput, AddressableA string originalBundleName = bd.Key as string; string newBundleName = BuildUtility.GetNameWithHashNaming(schema.BundleNaming, hash, originalBundleName); bundleLocData.InternalId = bundleLocData.InternalId.Remove(bundleLocData.InternalId.Length - originalBundleName.Length) + newBundleName; - + var abb = m_AllBundleInputDefinitions.FirstOrDefault(a => a.assetBundleName == originalBundleName); var virtualBundleName = AddressablesRuntimeProperties.EvaluateString(bundleLocData.InternalId); var bundleData = new VirtualAssetBundle(virtualBundleName, isLocalBundle, crc, hash); @@ -197,8 +197,12 @@ TResult DoBuild(AddressablesDataBuilderInput builderInput, AddressableA long headerSize = 0; foreach (var a in bd.Value) { - var size = ComputeSize(a); - bundleData.Assets.Add(new VirtualAssetBundleEntry(a, size)); + var i = Array.IndexOf(abb.addressableNames, a); + var assetPath = abb.assetNames[i]; + var size = ComputeSize(assetPath); + var vab = new VirtualAssetBundleEntry(a, size); + vab.m_AssetPath = assetPath; + bundleData.Assets.Add(vab); dataSize += size; headerSize += a.Length * 5; //assume 5x path length overhead size per item, probably much less } diff --git a/Editor/Build/DirectoryUtility.cs b/Editor/Build/DirectoryUtility.cs index c46b96d6..8a46cf2d 100644 --- a/Editor/Build/DirectoryUtility.cs +++ b/Editor/Build/DirectoryUtility.cs @@ -23,6 +23,32 @@ internal static void DeleteDirectory(string directoryPath, bool onlyIfEmpty = tr } } + internal static void DirectoryMove(string sourceDirName, string destDirName) + { + if (!Directory.Exists(sourceDirName)) + { + Debug.LogError($"Could not Move directory {sourceDirName}, directory not found."); + return; + } + if (Directory.Exists(destDirName)) + { + Debug.LogError($"Could not Move to directory {destDirName}, directory arlready exists."); + return; + } + + Directory.Move(sourceDirName, destDirName); + // check if the folder is valid in the AssetDatabase before deleting through standard file system + string relativePath = sourceDirName.Replace("\\", "/").Replace(Application.dataPath, "Assets"); + if (AssetDatabase.IsValidFolder(relativePath)) + { + // recreate the root folder so that it can be removed via adb + Directory.CreateDirectory(sourceDirName); + AssetDatabase.DeleteAsset(relativePath); + } + else if (File.Exists(sourceDirName + ".meta")) + File.Delete(sourceDirName + ".meta"); + } + internal static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) { // Get the subdirectories for the specified directory. diff --git a/Editor/GUI/AddressableAssetSettingsInspector.cs b/Editor/GUI/AddressableAssetSettingsInspector.cs index f9cbb4b5..1a90d4b0 100644 --- a/Editor/GUI/AddressableAssetSettingsInspector.cs +++ b/Editor/GUI/AddressableAssetSettingsInspector.cs @@ -15,14 +15,19 @@ class AddressableAssetSettingsInspector : Editor { AddressableAssetSettings m_AasTarget; - [FormerlySerializedAs("m_generalFoldout")] - [SerializeField] - bool m_GeneralFoldout = true; [FormerlySerializedAs("m_profilesFoldout")] [SerializeField] + bool m_ProfilesFoldout = true; + [SerializeField] + bool m_DiagnosticsFoldout = true; + [SerializeField] bool m_CatalogFoldout = true; [SerializeField] - bool m_ProfilesFoldout = true; + bool m_ContentUpdateFoldout = true; + [SerializeField] + bool m_DownloadsFoldout = true; + [SerializeField] + bool m_BuildFoldout = true; [FormerlySerializedAs("m_dataBuildersFoldout")] [SerializeField] bool m_DataBuildersFoldout = true; @@ -98,7 +103,7 @@ void OnEnable() GUIContent m_NonRecursiveBundleBuilding = new GUIContent("Non-Recursive Dependency Calculation", "If set, Calculates and build asset bundles using Non-Recursive Dependency calculation methods. This approach helps reduce asset bundle rebuilds and runtime memory consumption."); #else - GUIContent m_NonRecursiveBundleBuilding = + GUIContent m_NonRecursiveBundleBuilding = new GUIContent("Non-Recursive Dependency Calculation", "If set, Calculates and build asset bundles using Non-Recursive Dependency calculation methods. This approach helps reduce asset bundle rebuilds and runtime memory consumption.\n*Requires Unity 2020.2.1 or above"); #endif GUIContent m_BuildRemoteCatalog = @@ -121,12 +126,10 @@ void OnEnable() new GUIContent("Profile In Use", "This is the active profile that will be used to evaluate all profile variables during a build and when entering play mode."); GUIContent m_MaxConcurrentWebRequests = new GUIContent("Max Concurrent Web Requests", "Limits the number of concurrent web requests. If more requests are made, they will be queued until some requests complete."); - GUIContent m_groupHierarchyView = - new GUIContent("Group Hierarchy with Dashes", "If enabled, group names are parsed as if a '-' represented a child in hierarchy. So a group called 'a-b-c' would be displayed as if it were in a folder called 'b' that lived in a folder called 'a'. In this mode, only groups without '-' can be rearranged within the groups window."); GUIContent m_IgnoreUnsupportedFilesInBuild = new GUIContent("Ignore Invalid/Unsupported Files in Build", "If enabled, files that cannot be built will be ignored."); GUIContent m_ContentStateFileBuildPath = - new GUIContent("Content State Build Path", "The path used for saving the addressables_content_state.bin file. If empty, this will be the addressable settings config folder in your project."); + new GUIContent("Content State Build Path", "The path used for saving the addressables_content_state.bin file. If empty, this will be the addressable settings config folder in your project."); GUIContent m_ShaderBundleNaming = new GUIContent("Shader Bundle Naming Prefix", "This setting determines how the Unity built in shader bundle will be named during the build. The recommended setting is Project Name."); GUIContent m_ShaderBundleCustomNaming = @@ -135,6 +138,12 @@ void OnEnable() new GUIContent("MonoScript Bundle Naming Prefix", "This setting determines how and if the MonoScript bundle will be named during the build. The recommended setting is Project Name."); GUIContent m_MonoBundleCustomNaming = new GUIContent("MonoScript Bundle Custom Prefix", "Custom prefix for MonoScript bundle."); +#if UNITY_2019_4_OR_NEWER + GUIContent m_StripUnityVersionFromBundleBuild = + new GUIContent("Strip Unity Version from AssetBundles", "If enabled, the Unity Editor version is stripped from the AssetBundle header."); +#endif + GUIContent m_DisableVisibleSubAssetRepresentations = + new GUIContent("Disable Visible Sub Asset Representations", "If enabled, the build will assume that all sub Assets have no visible asset representations."); public override void OnInspectorGUI() { @@ -186,22 +195,45 @@ public override void OnInspectorGUI() } } + GUILayout.Space(6); + m_DiagnosticsFoldout = EditorGUILayout.Foldout(m_DiagnosticsFoldout, "Diagnostics"); + if (m_DiagnosticsFoldout) + { + ProjectConfigData.PostProfilerEvents = EditorGUILayout.Toggle(m_SendProfilerEvents, ProjectConfigData.PostProfilerEvents); + bool logResourceManagerExceptions = EditorGUILayout.Toggle(m_LogRuntimeExceptions, m_AasTarget.buildSettings.LogResourceManagerExceptions); + + if (logResourceManagerExceptions != m_AasTarget.buildSettings.LogResourceManagerExceptions) + m_QueuedChanges.Add(() => m_AasTarget.buildSettings.LogResourceManagerExceptions = logResourceManagerExceptions); + } + GUILayout.Space(6); m_CatalogFoldout = EditorGUILayout.Foldout(m_CatalogFoldout, "Catalog"); if (m_CatalogFoldout) { - bool disableCatalogOnStartup = EditorGUILayout.Toggle(m_CheckForCatalogUpdateOnInit, m_AasTarget.DisableCatalogUpdateOnStartup); - if (disableCatalogOnStartup != m_AasTarget.DisableCatalogUpdateOnStartup) - m_QueuedChanges.Add(() => m_AasTarget.DisableCatalogUpdateOnStartup = disableCatalogOnStartup); string overridePlayerVersion = EditorGUILayout.TextField(m_OverridePlayerVersion, m_AasTarget.OverridePlayerVersion); if (overridePlayerVersion != m_AasTarget.OverridePlayerVersion) m_QueuedChanges.Add(() => m_AasTarget.OverridePlayerVersion = overridePlayerVersion); + bool bundleLocalCatalog = EditorGUILayout.Toggle(m_BundleLocalCatalog, m_AasTarget.BundleLocalCatalog); if (bundleLocalCatalog != m_AasTarget.BundleLocalCatalog) m_QueuedChanges.Add(() => m_AasTarget.BundleLocalCatalog = bundleLocalCatalog); + bool optimizeCatalogSize = EditorGUILayout.Toggle(m_OptimizeCatalogSize, m_AasTarget.OptimizeCatalogSize); if (optimizeCatalogSize != m_AasTarget.OptimizeCatalogSize) m_QueuedChanges.Add(() => m_AasTarget.OptimizeCatalogSize = optimizeCatalogSize); + } + + GUILayout.Space(6); + m_ContentUpdateFoldout = EditorGUILayout.Foldout(m_ContentUpdateFoldout, "Content Update"); + if (m_ContentUpdateFoldout) + { + bool disableCatalogOnStartup = EditorGUILayout.Toggle(m_CheckForCatalogUpdateOnInit, m_AasTarget.DisableCatalogUpdateOnStartup); + if (disableCatalogOnStartup != m_AasTarget.DisableCatalogUpdateOnStartup) + m_QueuedChanges.Add(() => m_AasTarget.DisableCatalogUpdateOnStartup = disableCatalogOnStartup); + + string contentStateBuildPath = EditorGUILayout.TextField(m_ContentStateFileBuildPath, m_AasTarget.ContentStateBuildPath); + if (contentStateBuildPath != m_AasTarget.ContentStateBuildPath) + m_QueuedChanges.Add(() => m_AasTarget.ContentStateBuildPath = contentStateBuildPath); bool buildRemoteCatalog = EditorGUILayout.Toggle(m_BuildRemoteCatalog, m_AasTarget.BuildRemoteCatalog); if (buildRemoteCatalog != m_AasTarget.BuildRemoteCatalog) @@ -212,26 +244,39 @@ public override void OnInspectorGUI() EditorGUILayout.PropertyField(serializedObject.FindProperty("m_RemoteCatalogBuildPath"), m_RemoteCatBuildPath); EditorGUILayout.PropertyField(serializedObject.FindProperty("m_RemoteCatalogLoadPath"), m_RemoteCatLoadPath); } + } + + GUILayout.Space(6); + m_DownloadsFoldout = EditorGUILayout.Foldout(m_DownloadsFoldout, "Downloads"); + if (m_DownloadsFoldout) + { + EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CertificateHandlerType"), m_CertificateHandlerType); + + var maxWebReqs = EditorGUILayout.IntSlider(m_MaxConcurrentWebRequests, m_AasTarget.MaxConcurrentWebRequests, 1, 1024); + if (maxWebReqs != m_AasTarget.MaxConcurrentWebRequests) + m_QueuedChanges.Add(() => m_AasTarget.MaxConcurrentWebRequests = maxWebReqs); + var catalogTimeouts = EditorGUILayout.IntField(m_CatalogTimeout, m_AasTarget.CatalogRequestsTimeout); if (catalogTimeouts != m_AasTarget.CatalogRequestsTimeout) m_QueuedChanges.Add(() => m_AasTarget.CatalogRequestsTimeout = catalogTimeouts); } GUILayout.Space(6); - m_GeneralFoldout = EditorGUILayout.Foldout(m_GeneralFoldout, "General"); - if (m_GeneralFoldout) + m_BuildFoldout = EditorGUILayout.Foldout(m_BuildFoldout, "Build"); + if (m_BuildFoldout) { - ProjectConfigData.PostProfilerEvents = EditorGUILayout.Toggle(m_SendProfilerEvents, ProjectConfigData.PostProfilerEvents); - bool logResourceManagerExceptions = EditorGUILayout.Toggle(m_LogRuntimeExceptions, m_AasTarget.buildSettings.LogResourceManagerExceptions); - if (logResourceManagerExceptions != m_AasTarget.buildSettings.LogResourceManagerExceptions) - m_QueuedChanges.Add(() => m_AasTarget.buildSettings.LogResourceManagerExceptions = logResourceManagerExceptions); - EditorGUILayout.PropertyField(serializedObject.FindProperty("m_CertificateHandlerType"), m_CertificateHandlerType); + bool ignoreUnsupportedFilesInBuild = EditorGUILayout.Toggle(m_IgnoreUnsupportedFilesInBuild, m_AasTarget.IgnoreUnsupportedFilesInBuild); + if (ignoreUnsupportedFilesInBuild != m_AasTarget.IgnoreUnsupportedFilesInBuild) + m_QueuedChanges.Add(() => m_AasTarget.IgnoreUnsupportedFilesInBuild = ignoreUnsupportedFilesInBuild); + bool uniqueBundleIds = EditorGUILayout.Toggle(m_UniqueBundles, m_AasTarget.UniqueBundleIds); if (uniqueBundleIds != m_AasTarget.UniqueBundleIds) m_QueuedChanges.Add(() => m_AasTarget.UniqueBundleIds = uniqueBundleIds); + bool contiguousBundles = EditorGUILayout.Toggle(m_ContiguousBundles, m_AasTarget.ContiguousBundles); if (contiguousBundles != m_AasTarget.ContiguousBundles) m_QueuedChanges.Add(() => m_AasTarget.ContiguousBundles = contiguousBundles); + #if !NONRECURSIVE_DEPENDENCY_DATA EditorGUI.BeginDisabledGroup(true); #endif @@ -241,42 +286,37 @@ public override void OnInspectorGUI() #if !NONRECURSIVE_DEPENDENCY_DATA EditorGUI.EndDisabledGroup(); #endif - var maxWebReqs = EditorGUILayout.IntSlider(m_MaxConcurrentWebRequests, m_AasTarget.MaxConcurrentWebRequests, 1, 1024); - if (maxWebReqs != m_AasTarget.MaxConcurrentWebRequests) - m_QueuedChanges.Add(() => m_AasTarget.MaxConcurrentWebRequests = maxWebReqs); - ProjectConfigData.ShowGroupsAsHierarchy = EditorGUILayout.Toggle(m_groupHierarchyView, ProjectConfigData.ShowGroupsAsHierarchy); - - bool ignoreUnsupportedFilesInBuild = EditorGUILayout.Toggle(m_IgnoreUnsupportedFilesInBuild, m_AasTarget.IgnoreUnsupportedFilesInBuild); - if (ignoreUnsupportedFilesInBuild != m_AasTarget.IgnoreUnsupportedFilesInBuild) - m_QueuedChanges.Add(() => m_AasTarget.IgnoreUnsupportedFilesInBuild = ignoreUnsupportedFilesInBuild); - string contentStateBuildPath = EditorGUILayout.TextField(m_ContentStateFileBuildPath, m_AasTarget.ContentStateBuildPath); - if (contentStateBuildPath != m_AasTarget.ContentStateBuildPath) - m_QueuedChanges.Add(() => m_AasTarget.ContentStateBuildPath = contentStateBuildPath); - - ShaderBundleNaming shaderBundleNaming = (ShaderBundleNaming) EditorGUILayout.Popup(m_ShaderBundleNaming, - (int) m_AasTarget.ShaderBundleNaming, new[] { "Project Name Hash (Legacy)", "Default Group GUID", "Custom" }); - if(shaderBundleNaming != m_AasTarget.ShaderBundleNaming) + ShaderBundleNaming shaderBundleNaming = (ShaderBundleNaming)EditorGUILayout.Popup(m_ShaderBundleNaming, + (int)m_AasTarget.ShaderBundleNaming, new[] { "Project Name Hash (Legacy)", "Default Group GUID", "Custom" }); + if (shaderBundleNaming != m_AasTarget.ShaderBundleNaming) m_QueuedChanges.Add(() => m_AasTarget.ShaderBundleNaming = shaderBundleNaming); - if (shaderBundleNaming == ShaderBundleNaming.Custom) { string customShaderBundleName = EditorGUILayout.TextField(m_ShaderBundleCustomNaming, m_AasTarget.ShaderBundleCustomNaming); if (customShaderBundleName != m_AasTarget.ShaderBundleCustomNaming) m_QueuedChanges.Add(() => m_AasTarget.ShaderBundleCustomNaming = customShaderBundleName); } - - MonoScriptBundleNaming monoBundleNaming = (MonoScriptBundleNaming) EditorGUILayout.Popup(m_MonoBundleNaming, - (int) m_AasTarget.MonoScriptBundleNaming, new[] { "Disabled", "Project Name", "Default Group GUID", "Custom" }); - if(monoBundleNaming != m_AasTarget.MonoScriptBundleNaming) - m_QueuedChanges.Add(() => m_AasTarget.MonoScriptBundleNaming = monoBundleNaming); + MonoScriptBundleNaming monoBundleNaming = (MonoScriptBundleNaming)EditorGUILayout.Popup(m_MonoBundleNaming, + (int)m_AasTarget.MonoScriptBundleNaming, new[] { "Disabled", "Project Name", "Default Group GUID", "Custom" }); + if (monoBundleNaming != m_AasTarget.MonoScriptBundleNaming) + m_QueuedChanges.Add(() => m_AasTarget.MonoScriptBundleNaming = monoBundleNaming); if (monoBundleNaming == MonoScriptBundleNaming.Custom) { string customMonoScriptBundleName = EditorGUILayout.TextField(m_MonoBundleCustomNaming, m_AasTarget.MonoScriptBundleCustomNaming); if (customMonoScriptBundleName != m_AasTarget.MonoScriptBundleCustomNaming) m_QueuedChanges.Add(() => m_AasTarget.MonoScriptBundleCustomNaming = customMonoScriptBundleName); } + +#if UNITY_2019_4_OR_NEWER + bool stripUnityVersion = EditorGUILayout.Toggle(m_StripUnityVersionFromBundleBuild, m_AasTarget.StripUnityVersionFromBundleBuild); + if (stripUnityVersion != m_AasTarget.StripUnityVersionFromBundleBuild) + m_QueuedChanges.Add(() => m_AasTarget.StripUnityVersionFromBundleBuild = stripUnityVersion); +#endif + bool disableVisibleSubAssetRepresentations = EditorGUILayout.Toggle(m_DisableVisibleSubAssetRepresentations, m_AasTarget.DisableVisibleSubAssetRepresentations); + if (disableVisibleSubAssetRepresentations != m_AasTarget.DisableVisibleSubAssetRepresentations) + m_QueuedChanges.Add(() => m_AasTarget.DisableVisibleSubAssetRepresentations = disableVisibleSubAssetRepresentations); } GUILayout.Space(6); diff --git a/Editor/GUI/AddressableAssetsSettingsGroupEditor.cs b/Editor/GUI/AddressableAssetsSettingsGroupEditor.cs index 34be962d..ea7271a7 100644 --- a/Editor/GUI/AddressableAssetsSettingsGroupEditor.cs +++ b/Editor/GUI/AddressableAssetsSettingsGroupEditor.cs @@ -23,7 +23,7 @@ class AddressableAssetsSettingsGroupEditor [FormerlySerializedAs("mchs")] [SerializeField] MultiColumnHeaderState m_Mchs; - AddressableAssetEntryTreeView m_EntryTree; + internal AddressableAssetEntryTreeView m_EntryTree; public AddressableAssetsWindow window; @@ -58,6 +58,41 @@ public AddressableAssetsSettingsGroupEditor(AddressableAssetsWindow w) OnEnable(); } + public void SelectEntries(IList entries) + { + List selectedIDs = new List(entries.Count); + Stack items = new Stack(); + + if (m_EntryTree == null || m_EntryTree.Root == null) + InitialiseEntryTree(); + + foreach (TreeViewItem item in m_EntryTree.Root.children) + { + if(item is AssetEntryTreeViewItem i) + items.Push(i); + } + while (items.Count > 0) + { + var i = items.Pop(); + if (!i.IsGroup && entries.Contains(i.entry)) + { + selectedIDs.Add(i.id); + } + else if (i.hasChildren) + { + foreach (TreeViewItem child in i.children) + { + if(child is AssetEntryTreeViewItem c) + items.Push(c); + } + } + } + + foreach (int i in selectedIDs) + m_EntryTree.FrameItem(i); + m_EntryTree.SetSelection(selectedIDs); + } + void OnSettingsModification(AddressableAssetSettings s, AddressableAssetSettings.ModificationEvent e, object o) { if (m_EntryTree == null) @@ -153,13 +188,17 @@ void TopToolbar(Rect toolbarPos) EditorGUIUtility.PingObject(AddressableAssetSettingsDefaultObject.Settings); Selection.activeObject = AddressableAssetSettingsDefaultObject.Settings; }); - menu.AddItem(new GUIContent("Profiles"), false, () => EditorWindow.GetWindow().Show(true)); - menu.AddItem(new GUIContent("Labels"), false, () => EditorWindow.GetWindow(true).Intialize(settings)); - menu.AddItem(new GUIContent("Analyze"), false, AnalyzeWindow.ShowWindow); - menu.AddItem(new GUIContent("Hosting Services"), false, () => EditorWindow.GetWindow().Show(settings)); - menu.AddItem(new GUIContent("Event Viewer"), false, ResourceProfilerWindow.ShowWindow); menu.AddItem(new GUIContent("Check for Content Update Restrictions"), false, OnPrepareUpdate); - menu.AddItem(new GUIContent("Show Sprite and Subobject Addresses"), ProjectConfigData.ShowSubObjectsInGroupView, () => { ProjectConfigData.ShowSubObjectsInGroupView = !ProjectConfigData.ShowSubObjectsInGroupView; m_EntryTree.Reload(); }); + + menu.AddItem(new GUIContent("Window/Profiles"), false, () => EditorWindow.GetWindow().Show(true)); + menu.AddItem(new GUIContent("Window/Labels"), false, () => EditorWindow.GetWindow(true).Intialize(settings)); + menu.AddItem(new GUIContent("Window/Analyze"), false, AnalyzeWindow.ShowWindow); + menu.AddItem(new GUIContent("Window/Hosting Services"), false, () => EditorWindow.GetWindow().Show(settings)); + menu.AddItem(new GUIContent("Window/Event Viewer"), false, ResourceProfilerWindow.ShowWindow); + + menu.AddItem(new GUIContent("Groups View/Show Sprite and Subobject Addresses"), ProjectConfigData.ShowSubObjectsInGroupView, () => { ProjectConfigData.ShowSubObjectsInGroupView = !ProjectConfigData.ShowSubObjectsInGroupView; m_EntryTree.Reload(); }); + menu.AddItem(new GUIContent("Groups View/Group Hierarchy with Dashes", "If enabled, group names are parsed as if a '-' represented a child in hierarchy. So a group called 'a-b-c' would be displayed as if it were in a folder called 'b' that lived in a folder called 'a'. In this mode, only groups without '-' can be rearranged within the groups window."), + ProjectConfigData.ShowGroupsAsHierarchy, () => { ProjectConfigData.ShowGroupsAsHierarchy = !ProjectConfigData.ShowGroupsAsHierarchy; m_EntryTree.Reload(); }); var bundleList = AssetDatabase.GetAllAssetBundleNames(); if (bundleList != null && bundleList.Length > 0) @@ -382,19 +421,7 @@ public bool OnGUI(Rect pos) } if (m_EntryTree == null) - { - if (m_TreeState == null) - m_TreeState = new TreeViewState(); - - var headerState = AddressableAssetEntryTreeView.CreateDefaultMultiColumnHeaderState(); - if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_Mchs, headerState)) - MultiColumnHeaderState.OverwriteSerializedFields(m_Mchs, headerState); - m_Mchs = headerState; - - m_SearchField = new SearchField(); - m_EntryTree = new AddressableAssetEntryTreeView(m_TreeState, m_Mchs, this); - m_EntryTree.Reload(); - } + InitialiseEntryTree(); HandleVerticalResize(pos); var inRectY = pos.height; @@ -406,6 +433,22 @@ public bool OnGUI(Rect pos) return m_ResizingVerticalSplitter; } + internal AddressableAssetEntryTreeView InitialiseEntryTree() + { + if (m_TreeState == null) + m_TreeState = new TreeViewState(); + + var headerState = AddressableAssetEntryTreeView.CreateDefaultMultiColumnHeaderState(); + if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_Mchs, headerState)) + MultiColumnHeaderState.OverwriteSerializedFields(m_Mchs, headerState); + m_Mchs = headerState; + + m_SearchField = new SearchField(); + m_EntryTree = new AddressableAssetEntryTreeView(m_TreeState, m_Mchs, this); + m_EntryTree.Reload(); + return m_EntryTree; + } + public void Reload() { if (m_EntryTree != null) diff --git a/Editor/GUI/AddressableAssetsSettingsGroupTreeView.cs b/Editor/GUI/AddressableAssetsSettingsGroupTreeView.cs index 99f54dcd..5d7b2440 100644 --- a/Editor/GUI/AddressableAssetsSettingsGroupTreeView.cs +++ b/Editor/GUI/AddressableAssetsSettingsGroupTreeView.cs @@ -21,6 +21,7 @@ internal class AddressableAssetEntryTreeView : TreeView internal string customSearchString = string.Empty; string m_FirstSelectedGroup; private readonly Dictionary m_SearchedEntries = new Dictionary(); + private bool m_ForceSelectionClear = false; enum ColumnId { @@ -51,6 +52,7 @@ internal AddressableAssetEntryTreeView(AddressableAssetSettings settings) multiColumnHeader.sortingChanged += OnSortingChanged; BuiltinSceneCache.sceneListChanged += OnScenesChanged; + AddressablesAssetPostProcessor.OnPostProcess += OnPostProcessAllAssets; } internal TreeViewItem Root => rootItem; @@ -68,6 +70,20 @@ void OnSortingChanged(MultiColumnHeader mch) Reload(); } + void OnPostProcessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + foreach (Object obj in Selection.objects) + { + if (obj == null) + continue; + if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj.GetInstanceID(), out string guid, out long localId)) + { + m_ForceSelectionClear = true; + return; + } + } + } + protected override void SelectionChanged(IList selectedIds) { if (selectedIds.Count == 1) @@ -453,11 +469,14 @@ public override void OnGUI(Rect rect) base.OnGUI(rect); //TODO - this occasionally causes a "hot control" issue. - if (Event.current.type == EventType.MouseDown && - Event.current.button == 0 && - rect.Contains(Event.current.mousePosition)) + if (m_ForceSelectionClear || + (Event.current.type == EventType.MouseDown && + Event.current.button == 0 && + rect.Contains(Event.current.mousePosition))) { SetSelection(new int[0], TreeViewSelectionOptions.FireSelectionChanged); + if (m_ForceSelectionClear) + m_ForceSelectionClear = false; } } @@ -1268,8 +1287,10 @@ DragAndDropVisualMode HandleDragAndDropItems(AssetEntryTreeViewItem target, Drag { if (args.parentItem == null || args.parentItem == rootItem && visualMode != DragAndDropVisualMode.Rejected) { - foreach (var node in draggedNodes) + // Need to insert groups in reverse order because all groups will be inserted at the same index + for (int i = draggedNodes.Count - 1; i >= 0; i--) { + AssetEntryTreeViewItem node = draggedNodes[i]; AddressableAssetGroup group = node.@group; int index = m_Editor.settings.groups.FindIndex(g => g == group); if (index < args.insertAtIndex) @@ -1308,12 +1329,12 @@ DragAndDropVisualMode HandleDragAndDropItems(AssetEntryTreeViewItem target, Drag foreach (var node in draggedNodes) { modifiedGroups.Add(node.entry.parentGroup); - m_Editor.settings.MoveEntry(node.entry, parent, false, false); - entries.Add(node.entry); + m_Editor.settings.MoveEntry(node.entry, parent, false, false); + entries.Add(node.entry); } foreach (AddressableAssetGroup modifiedGroup in modifiedGroups) AddressableAssetUtility.OpenAssetIfUsingVCIntegration(modifiedGroup); - m_Editor.settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entries, true, false); + m_Editor.settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entries, true, false); } } } @@ -1380,24 +1401,24 @@ DragAndDropVisualMode HandleDragAndDropPaths(AssetEntryTreeViewItem target, Drag if (canMarkNonResources) { - if (nonResourceGuids.Count > 0) - { - var entriesMoved = new List(); - var entriesCreated = new List(); - m_Editor.settings.CreateOrMoveEntries(nonResourceGuids, parent, entriesCreated, entriesMoved, false, false); - - if (entriesMoved.Count > 0) - m_Editor.settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entriesMoved, true); - if (entriesCreated.Count > 0) - m_Editor.settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryAdded, entriesCreated, true); - - AddressableAssetUtility.OpenAssetIfUsingVCIntegration(parent); - } - - if (targetIsGroup) - { - SetExpanded(target.id, true); - } + if (nonResourceGuids.Count > 0) + { + var entriesMoved = new List(); + var entriesCreated = new List(); + m_Editor.settings.CreateOrMoveEntries(nonResourceGuids, parent, entriesCreated, entriesMoved, false, false); + + if (entriesMoved.Count > 0) + m_Editor.settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, entriesMoved, true); + if (entriesCreated.Count > 0) + m_Editor.settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryAdded, entriesCreated, true); + + AddressableAssetUtility.OpenAssetIfUsingVCIntegration(parent); + } + + if (targetIsGroup) + { + SetExpanded(target.id, true); + } } } } diff --git a/Editor/GUI/AddressableAssetsWindow.cs b/Editor/GUI/AddressableAssetsWindow.cs index 3fd7eaef..443424ed 100644 --- a/Editor/GUI/AddressableAssetsWindow.cs +++ b/Editor/GUI/AddressableAssetsWindow.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using UnityEditor.AddressableAssets.Settings; using UnityEditor.PackageManager; using UnityEditor.PackageManager.Requests; @@ -13,7 +14,7 @@ class AddressableAssetsWindow : EditorWindow, IHasCustomMenu [FormerlySerializedAs("m_groupEditor")] [SerializeField] - AddressableAssetsSettingsGroupEditor m_GroupEditor; + internal AddressableAssetsSettingsGroupEditor m_GroupEditor; [MenuItem("Window/Asset Management/Addressables/Settings", priority = 2051)] internal static void ShowSettingsInspector() @@ -45,6 +46,13 @@ public static Vector2 GetWindowPosition() return new Vector2(window.position.x, window.position.y); } + internal void SelectAssetsInGroupEditor(IList entries) + { + if (m_GroupEditor == null) + m_GroupEditor = new AddressableAssetsSettingsGroupEditor(this); + m_GroupEditor.SelectEntries(entries); + } + public void OnEnable() { m_GroupEditor?.OnEnable(); diff --git a/Editor/GUI/AssetInspectorGUI.cs b/Editor/GUI/AssetInspectorGUI.cs index 43036e62..1e5f5e66 100644 --- a/Editor/GUI/AssetInspectorGUI.cs +++ b/Editor/GUI/AssetInspectorGUI.cs @@ -23,7 +23,7 @@ static AddressableAssetInspectorGUI() Editor.finishedDefaultHeaderGUI += OnPostHeaderGUI; } - static void SetAaEntry(AddressableAssetSettings aaSettings, Object[] targets, bool create) + static void SetAaEntry(AddressableAssetSettings aaSettings, List targetInfos, bool create) { if (create && aaSettings.DefaultGroup.ReadOnly) { @@ -33,15 +33,6 @@ static void SetAaEntry(AddressableAssetSettings aaSettings, Object[] targets, bo Undo.RecordObject(aaSettings, "AddressableAssetSettings"); - var targetInfos = new List(); - foreach (var t in targets) - { - if (AddressableAssetUtility.GetPathAndGUIDFromTarget(t, out var path, out var guid, out var mainAssetType)) - { - targetInfos.Add(new TargetInfo(){Guid = guid, Path = path, MainAssetType = mainAssetType}); - } - } - if (!create) { List removedEntries = new List(targetInfos.Count); @@ -102,6 +93,7 @@ static void OnPostHeaderGUI(Editor editor) int addressableCount = 0; bool foundValidAsset = false; bool foundAssetGroup = false; + var targetInfos = new List(); foreach (var t in editor.targets) { foundAssetGroup |= t is AddressableAssetGroup; @@ -112,6 +104,7 @@ static void OnPostHeaderGUI(Editor editor) if (!BuildUtility.IsEditorAssembly(mainAssetType.Assembly)) { foundValidAsset = true; + var info = new TargetInfo(){Guid = guid, Path = path, MainAssetType = mainAssetType}; if (aaSettings != null) { @@ -119,8 +112,10 @@ static void OnPostHeaderGUI(Editor editor) if (entry != null && !entry.IsSubAsset) { addressableCount++; + info.Entry = entry; } } + targetInfos.Add(info); } } } @@ -150,14 +145,14 @@ static void OnPostHeaderGUI(Editor editor) if (addressableCount == 0) { if (GUILayout.Toggle(false, s_AddressableAssetToggleText, GUILayout.ExpandWidth(false))) - SetAaEntry(AddressableAssetSettingsDefaultObject.GetSettings(true), editor.targets, true); + SetAaEntry(AddressableAssetSettingsDefaultObject.GetSettings(true), targetInfos, true); } else if (addressableCount == editor.targets.Length) { GUILayout.BeginHorizontal(); if (!GUILayout.Toggle(true, s_AddressableAssetToggleText, GUILayout.ExpandWidth(false))) { - SetAaEntry(aaSettings, editor.targets, false); + SetAaEntry(aaSettings, targetInfos, false); UnityEngine.GUI.enabled = prevEnabledState; GUIUtility.ExitGUI(); } @@ -176,6 +171,13 @@ static void OnPostHeaderGUI(Editor editor) } } } + else + { + EditorGUILayout.LabelField(addressableCount + " out of " + editor.targets.Length + " assets are addressable."); + } + + if (GUILayout.Button("Select")) + SelectEntriesInGroupsWindow(targetInfos); GUILayout.EndHorizontal(); } else @@ -184,19 +186,35 @@ static void OnPostHeaderGUI(Editor editor) if (s_ToggleMixed == null) s_ToggleMixed = new GUIStyle("ToggleMixed"); if (GUILayout.Toggle(false, s_AddressableAssetToggleText, s_ToggleMixed, GUILayout.ExpandWidth(false))) - SetAaEntry(AddressableAssetSettingsDefaultObject.GetSettings(true), editor.targets, true); + SetAaEntry(AddressableAssetSettingsDefaultObject.GetSettings(true), targetInfos, true); EditorGUILayout.LabelField(addressableCount + " out of " + editor.targets.Length + " assets are addressable."); + if (GUILayout.Button("Select")) + SelectEntriesInGroupsWindow(targetInfos); GUILayout.EndHorizontal(); } UnityEngine.GUI.enabled = prevEnabledState; } } + static void SelectEntriesInGroupsWindow(List targets) + { + AddressableAssetsWindow.Init(); + var window = EditorWindow.GetWindow(); + List entries = new List(targets.Count); + foreach (TargetInfo info in targets) + { + if (info.Entry != null) + entries.Add(info.Entry); + } + window.SelectAssetsInGroupEditor(entries); + } + class TargetInfo { public string Guid; public string Path; public Type MainAssetType; + public AddressableAssetEntry Entry; } } } diff --git a/Editor/GUI/LabelWindow.cs b/Editor/GUI/LabelWindow.cs index 82d07f98..62e94d2f 100644 --- a/Editor/GUI/LabelWindow.cs +++ b/Editor/GUI/LabelWindow.cs @@ -14,6 +14,12 @@ public class LabelWindow : EditorWindow private Vector2 m_ScrollPosition; private int m_BorderSpacing = 7; + //Edit menu + int m_ActiveIndex = -1; + bool m_IsEditing = false; + string m_CurrentEdit; + string m_OldName; + /// /// Creates a new LabelWindow instance and retrieves label names from the given settings object. /// @@ -28,9 +34,17 @@ public void Intialize(AddressableAssetSettings settings) m_LabelNamesRl.drawElementCallback = DrawLabelNamesCallback; m_LabelNamesRl.onAddDropdownCallback = OnAddLabel; m_LabelNamesRl.onRemoveCallback = OnRemoveLabel; + m_LabelNamesRl.onSelectCallback = list => + { + m_ActiveIndex = list.index; + EndEditMenu(); + }; m_LabelNamesRl.headerHeight = 0; // hide header completely - } + m_ActiveIndex = -1; + m_IsEditing = false; + } + void OnGUI() { GUILayout.BeginVertical(EditorStyles.label); @@ -39,19 +53,64 @@ void OnGUI() m_LabelNamesRl.DoLayoutList(); GUILayout.EndScrollView(); GUILayout.EndVertical(); + + HandleEvent(Event.current); + } + + void HandleEvent(Event current) + { + if (m_ActiveIndex < 0 || m_Settings.labelTable.labelNames.Count == 0) + return; + + if (current.type == EventType.ContextClick) + { + GenericMenu contextMenu = new GenericMenu(); + contextMenu.AddItem(new GUIContent("Edit"), false, () => + { + m_IsEditing = true; + m_CurrentEdit = m_Settings.labelTable.labelNames[m_ActiveIndex]; + Repaint(); + }); + contextMenu.ShowAsContext(); + Repaint(); + } + else if (m_IsEditing && (current.keyCode == KeyCode.Return || current.keyCode == KeyCode.KeypadEnter)) + { + m_Settings.RenameLabel(m_OldName, m_CurrentEdit); + EndEditMenu(); + } + else if(current.type == EventType.MouseDown && m_IsEditing) + EndEditMenu(); + } + + void EndEditMenu() + { + m_IsEditing = false; + m_CurrentEdit = string.Empty; + m_OldName = string.Empty; + Repaint(); } void DrawLabelNamesCallback(Rect rect, int index, bool isActive, bool isFocused) { var oldName = m_Settings.labelTable.labelNames[index]; - EditorGUI.LabelField(rect, oldName); + + if (m_IsEditing && index == m_ActiveIndex) + { + m_OldName = oldName; + UnityEngine.GUI.SetNextControlName(m_OldName); + m_CurrentEdit = EditorGUI.TextField(rect, m_CurrentEdit); + UnityEngine.GUI.FocusControl(m_OldName); + } + else + EditorGUI.LabelField(rect, oldName); } void OnRemoveLabel(ReorderableList list) { m_Settings.RemoveLabel(m_Settings.labelTable.labelNames[list.index]); AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings); - } + } void OnAddLabel(Rect buttonRect, ReorderableList list) { diff --git a/Editor/GUI/ProfileWindow.cs b/Editor/GUI/ProfileWindow.cs index 1a845d6e..60c9ad9f 100644 --- a/Editor/GUI/ProfileWindow.cs +++ b/Editor/GUI/ProfileWindow.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using UnityEditor.AddressableAssets.HostingServices; using UnityEditor.AddressableAssets.Settings; using UnityEditor.IMGUI.Controls; -using UnityEditorInternal; using UnityEngine; using UnityEngine.AddressableAssets; -using UnityEngine.Assertions.Comparers; -using UnityEngine.Serialization; +using System.Linq; namespace UnityEditor.AddressableAssets.GUI { @@ -32,6 +29,9 @@ internal class ProfileWindow : EditorWindow private float m_LabelWidth = 155f; private float m_FieldBufferWidth = 0f; + //Separator + private const char k_PrefixSeparator = '.'; + GUIStyle m_ItemRectPadding; float m_HorizontalSplitterRatio = k_DefaultHorizontalSplitterRatio; @@ -59,6 +59,9 @@ public int ProfileIndex private GUIStyle m_ButtonStyle; + + private Dictionary m_foldouts = new Dictionary(); + [MenuItem("Window/Asset Management/Addressables/Profiles", priority = 2051)] internal static void ShowWindow() { @@ -162,7 +165,7 @@ void NewVariable() { try { - PopupWindow.Show(new Rect(0, k_ToolbarHeight, position.width, k_ToolbarHeight), + PopupWindow.Show(new Rect(position.x, position.y + k_ToolbarHeight, position.width, k_ToolbarHeight), new ProfileNewVariablePopup(position.width, position.height, 0, m_ProfileTreeView, settings)); } catch (ExitGUIException) @@ -210,14 +213,6 @@ void VariablesPane(Rect variablesPaneRect) //ensures amount of visible text is not affected by label width float fieldWidth = variablesPaneRect.width - (2 * k_ItemRectPadding) + m_LabelWidth + m_FieldBufferWidth; - float fieldX = variablesPaneRect.x + k_ItemRectPadding; - float fieldHeight = k_ToolbarHeight; - - //Amount of text visible not affected by amount of text either, large enough for arbitrary # of variables - float viewRectHeight = (fieldHeight + k_VariableItemPadding) * settings.profileSettings.profileEntryNames.Count + fieldHeight; - float viewRectWidth = fieldWidth + (2 * k_ItemRectPadding); - - Rect viewRect = new Rect(variablesPaneRect.x, variablesPaneRect.y, viewRectWidth, viewRectHeight); if (!EditorGUIUtility.labelWidth.Equals(m_LabelWidth)) EditorGUIUtility.labelWidth = m_LabelWidth; @@ -225,32 +220,77 @@ void VariablesPane(Rect variablesPaneRect) int maxLabelLen = 0; int maxFieldLen = 0; - m_VariablesPaneScrollPosition = UnityEngine.GUI.BeginScrollView(variablesPaneRect, m_VariablesPaneScrollPosition, viewRect); - for (int i = 0; i < settings.profileSettings.profileEntryNames.Count; i++) + GUILayout.BeginArea(variablesPaneRect); + EditorGUI.indentLevel++; + List groupTypes = CreateGroupTypes(selectedProfile); + HashSet drawnGroupTypes = new HashSet(); + + //Displaying Path Groups + foreach (ProfileGroupType groupType in groupTypes) { - //Keep track of the maximum length label, field so we can ensure that variable names, values are always completely visible - maxLabelLen = Math.Max(maxLabelLen, settings.profileSettings.profileEntryNames[i].ProfileName.Length); - maxFieldLen = Math.Max(maxFieldLen, selectedProfile.values[i].value.Length); + bool? foldout; + m_foldouts.TryGetValue(groupType.GroupTypePrefix, out foldout); + GUILayout.Space(5); + m_foldouts[groupType.GroupTypePrefix] = EditorGUILayout.Foldout(foldout != null ? foldout.Value : true, groupType.GroupTypePrefix, true); + //Specific Grouped variables + List pathVariables = new List(); + pathVariables.Add(groupType.GetVariableBySuffix("BuildPath")); + drawnGroupTypes.Add(groupType.GetName(groupType.GetVariableBySuffix("BuildPath"))); + pathVariables.Add(groupType.GetVariableBySuffix("LoadPath")); + drawnGroupTypes.Add(groupType.GetName(groupType.GetVariableBySuffix("LoadPath"))); + + if (m_foldouts[groupType.GroupTypePrefix].Value) + { + EditorGUI.indentLevel++; - float fieldY = (variablesPaneRect.y + k_VariableItemPadding) * i + k_ItemRectPadding + k_ToolbarHeight; - AddressableAssetProfileSettings.ProfileIdData curVariable = settings.profileSettings.profileEntryNames[i]; + //Displaying Path Groups + foreach(var variable in pathVariables) + { + Rect newPathRect = EditorGUILayout.BeginVertical(); + string newPath = EditorGUILayout.TextField(groupType.GetName(variable), variable.Value); + EditorGUILayout.EndVertical(); + if (evt.type == EventType.ContextClick) + { + CreateVariableContextMenu(variablesPaneRect, newPathRect, settings.profileSettings.GetProfileDataByName(groupType.GetName(variable)), evt); + } + if (newPath != variable.Value && ProfileIndex == m_ProfileTreeView.lastClickedProfile) + { + Undo.RecordObject(settings, "Variable value changed"); + settings.profileSettings.SetValue(selectedProfile.id, groupType.GetName(variable), newPath); + AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings); + } + } + EditorGUI.indentLevel--; + } + } - Rect fieldRect = new Rect(fieldX, fieldY, fieldWidth, fieldHeight); - Rect labelRect = new Rect(fieldX, fieldY, m_LabelWidth, fieldHeight); + //Display all other variables + for (var i = 0; i < settings.profileSettings.profileEntryNames.Count; i++) + { + AddressableAssetProfileSettings.ProfileIdData curVariable = settings.profileSettings.profileEntryNames[i]; + if (!drawnGroupTypes.Contains(curVariable.ProfileName)) + { + GUILayout.Space(5); + Rect newValueRect = EditorGUILayout.BeginVertical(); + string newValue = EditorGUILayout.TextField(curVariable.ProfileName, selectedProfile.values[i].value); + EditorGUILayout.EndVertical(); + if (newValue != selectedProfile.values[i].value && ProfileIndex == m_ProfileTreeView.lastClickedProfile) + { + Undo.RecordObject(settings, "Variable value changed"); + settings.profileSettings.SetValue(selectedProfile.id, settings.profileSettings.profileEntryNames[i].ProfileName, newValue); + AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings); + } - string newName = EditorGUI.TextField(fieldRect, curVariable.ProfileName, selectedProfile.values[i].value); - //Ensure changes get serialized - if (selectedProfile.values[i].value != newName && ProfileIndex == m_ProfileTreeView.lastClickedProfile) - { - Undo.RecordObject(settings, "Variable value changed"); - settings.profileSettings.SetValue(selectedProfile.id, settings.profileSettings.profileEntryNames[i].ProfileName, newName); - AddressableAssetUtility.OpenAssetIfUsingVCIntegration(settings); + if (evt.type == EventType.ContextClick) + { + CreateVariableContextMenu(variablesPaneRect, newValueRect, curVariable, evt); + } } - - if (evt.type == EventType.ContextClick) - CreateVariableContextMenu(labelRect, curVariable, i, evt); + maxLabelLen = Math.Max(maxLabelLen, curVariable.ProfileName.Length); } - UnityEngine.GUI.EndScrollView(); + + EditorGUI.indentLevel--; + GUILayout.EndArea(); //Update the label width to the maximum of the minimum acceptable label width and the amount of //space required to contain the longest variable name @@ -258,9 +298,37 @@ void VariablesPane(Rect variablesPaneRect) m_FieldBufferWidth = Mathf.Clamp((maxFieldLen * k_ApproxCharWidth) - fieldWidth, 0f, float.MaxValue); } + //UI magic to group the path pairs from profile variables + List CreateGroupTypes(AddressableAssetProfileSettings.BuildProfile buildProfile) + { + Dictionary groups = new Dictionary(); + foreach(var profileEntry in settings.profileSettings.profileEntryNames) + { + string[] parts = profileEntry.ProfileName.Split(k_PrefixSeparator); + if (parts.Length > 1) + { + string prefix = String.Join(k_PrefixSeparator.ToString(), parts, 0, parts.Length - 1); + string suffix = parts[parts.Length - 1]; + string profileEntryValue = buildProfile.GetValueById(profileEntry.Id); + ProfileGroupType group; + groups.TryGetValue(prefix, out group); + if (group == null) + { + group = new ProfileGroupType(prefix); + } + ProfileGroupType.GroupTypeVariable variable = new ProfileGroupType.GroupTypeVariable(suffix, profileEntryValue); + group.AddVariable(variable); + groups[prefix] = group; + } + } + + List groupList = new List(); + groupList.AddRange(groups.Values.Where(group => group.IsValidGroupType())); + return groupList; + } + //Creates the context menu for the selected variable - void CreateVariableContextMenu(Rect menuRect, AddressableAssetProfileSettings.ProfileIdData variable, - int index, Event evt) + void CreateVariableContextMenu(Rect parentWindow, Rect menuRect, AddressableAssetProfileSettings.ProfileIdData variable, Event evt) { if (menuRect.Contains(evt.mousePosition)) { @@ -268,19 +336,21 @@ void CreateVariableContextMenu(Rect menuRect, AddressableAssetProfileSettings.Pr //Displays name of selected variable so user can be confident they're deleting/renaming the right one menu.AddDisabledItem(new GUIContent(variable.ProfileName)); menu.AddSeparator(""); - menu.AddItem(new GUIContent("Rename Variable (All Profiles)"), false, () => { RenameVariable(variable, menuRect); }); - menu.AddItem(new GUIContent("Delete Variable (All Profiles)"), false, () => { DeleteVariable(settings.profileSettings.profileEntryNames[index]); }); + menu.AddItem(new GUIContent("Rename Variable (All Profiles)"), false, () => { RenameVariable(variable, parentWindow, menuRect); }); + menu.AddItem(new GUIContent("Delete Variable (All Profiles)"), false, () => { DeleteVariable(variable); }); menu.ShowAsContext(); evt.Use(); } } //Opens ProfileRenameVariablePopup - void RenameVariable(AddressableAssetProfileSettings.ProfileIdData profileVariable, Rect displayRect) + void RenameVariable(AddressableAssetProfileSettings.ProfileIdData profileVariable, Rect parentWindow, Rect displayRect) { try { - PopupWindow.Show(displayRect, new ProfileRenameVariablePopup(displayRect, profileVariable, settings)); + //Determines the current variable rect location + Rect variableRect = new Rect(position.x + parentWindow.x + displayRect.x, position.y + parentWindow.y + displayRect.y, position.width, k_ToolbarHeight); + PopupWindow.Show(variableRect, new ProfileRenameVariablePopup(displayRect, profileVariable, settings)); } catch (ExitGUIException) { diff --git a/Editor/Settings/AddressableAssetEntry.cs b/Editor/Settings/AddressableAssetEntry.cs index 0a641f09..83de421a 100644 --- a/Editor/Settings/AddressableAssetEntry.cs +++ b/Editor/Settings/AddressableAssetEntry.cs @@ -62,6 +62,10 @@ public class AddressableAssetEntry : ISerializationCallbackReceiver, IReferenceE internal virtual bool HasSettings() { return false; } + internal bool IsFolder { get; set; } + [NonSerialized] + internal List SubAssets = new List(); + [NonSerialized] AddressableAssetGroup m_ParentGroup; /// @@ -345,7 +349,7 @@ public UnityEngine.Object MainAsset { get { - if (m_MainAsset == null) + if (m_MainAsset == null || !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_MainAsset, out string guid, out long localId)) { AddressableAssetEntry e = this; while (string.IsNullOrEmpty(e.AssetPath)) @@ -370,7 +374,7 @@ public UnityEngine.Object TargetAsset { get { - if (m_TargetAsset == null) + if (m_TargetAsset == null || !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_TargetAsset, out string guid, out long localId)) { if (!string.IsNullOrEmpty(AssetPath) || !IsSubAsset) { @@ -490,7 +494,9 @@ public void GatherAllAssets(List assets, bool includeSelf if (AssetDatabase.IsValidFolder(AssetPath)) { + IsFolder = true; GatherFolderEntries(assets, recurseAll, entryFilter); + SubAssets = assets; } else { diff --git a/Editor/Settings/AddressableAssetSettings.cs b/Editor/Settings/AddressableAssetSettings.cs index dde6f3b4..4d8b33e6 100644 --- a/Editor/Settings/AddressableAssetSettings.cs +++ b/Editor/Settings/AddressableAssetSettings.cs @@ -307,7 +307,7 @@ public string GroupTemplateFolder [SerializeField] bool m_OptimizeCatalogSize = false; - + [SerializeField] bool m_BuildRemoteCatalog = false; @@ -325,7 +325,7 @@ public string GroupTemplateFolder [SerializeField] bool m_UniqueBundleIds = false; - + [SerializeField] #if UNITY_2021_1_OR_NEWER bool m_NonRecursiveBuilding = true; @@ -387,7 +387,7 @@ public bool NonRecursiveBuilding get { return m_NonRecursiveBuilding; } set { m_NonRecursiveBuilding = value; } } - + /// /// Enables size optimization of content catalogs. This may increase the cpu usage of loading the catalog. /// @@ -424,6 +424,28 @@ public bool DisableCatalogUpdateOnStartup set { m_DisableCatalogUpdateOnStart = value; } } +#if UNITY_2019_4_OR_NEWER + bool m_StripUnityVersionFromBundleBuild = false; + /// + /// If true, this option will strip the Unity Editor Version from the header of the AssetBundle during a build. + /// + internal bool StripUnityVersionFromBundleBuild + { + get { return m_StripUnityVersionFromBundleBuild; } + set { m_StripUnityVersionFromBundleBuild = value; } + } +#endif + bool m_DisableVisibleSubAssetRepresentations = false; + /// + /// If true, the build will assume that sub Assets have no visible asset representations (are not visible in the Project view) which results in improved build times. + /// However sub assets in the built bundles cannot be accessed by AssetBundle.LoadAsset<T> or AssetBundle.LoadAllAssets<T>. + /// + internal bool DisableVisibleSubAssetRepresentations + { + get { return m_DisableVisibleSubAssetRepresentations; } + set { m_DisableVisibleSubAssetRepresentations = value; } + } + /// /// Whether unsupported files during build should be ignored or treated as an error. /// @@ -455,7 +477,7 @@ public string ShaderBundleCustomNaming get { return m_ShaderBundleCustomNaming; } set { m_ShaderBundleCustomNaming = value; } } - + [SerializeField] MonoScriptBundleNaming m_MonoScriptBundleNaming = MonoScriptBundleNaming.Disabled; /// @@ -1039,6 +1061,30 @@ public void AddLabel(string label, bool postEvent = true) SetDirty(ModificationEvent.LabelAdded, label, postEvent, true); } + internal void RenameLabel(string oldLabelName, string newLabelName) + { + int index = m_LabelTable.GetIndexOfLabel(oldLabelName); + if (index < 0) + return; + + if (!m_LabelTable.AddLabelName(newLabelName, index)) + return; + + foreach (var group in groups) + { + foreach (var entry in group.entries) + { + if (entry.labels.Contains(oldLabelName)) + { + entry.labels.Remove(oldLabelName); + entry.SetLabel(newLabelName, true); + } + } + } + + m_LabelTable.RemoveLabelName(oldLabelName); + } + /// /// Remove a label by name. /// @@ -1737,6 +1783,7 @@ internal AddressableAssetEntry CreateSubEntryIfUnique(string guid, string addres entry = new AddressableAssetEntry(guid, address, parentEntry.parentGroup, true); entry.IsSubAsset = true; entry.ParentEntry = parentEntry; + entry.BundleFileId = parentEntry.BundleFileId; //parentEntry.parentGroup.AddAssetEntry(entry); return entry; } @@ -1748,6 +1795,7 @@ internal AddressableAssetEntry CreateSubEntryIfUnique(string guid, string addres entry.IsInResources = parentEntry.IsInResources; entry.address = address; entry.ReadOnly = true; + entry.BundleFileId = parentEntry.BundleFileId; return entry; } return null; diff --git a/Editor/Settings/AssetReferenceDrawerUtilities.cs b/Editor/Settings/AssetReferenceDrawerUtilities.cs index 15a3c8e3..d191838f 100644 --- a/Editor/Settings/AssetReferenceDrawerUtilities.cs +++ b/Editor/Settings/AssetReferenceDrawerUtilities.cs @@ -245,24 +245,24 @@ static internal List GatherFilters(Seriali if (o != null) { var t = o.GetType(); - + FieldInfo info = null; + // We need to look into sub types, if any. string[] pathParts = property.propertyPath.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries); - for (int i = 0; i < pathParts.Length - 1; i++) + for (int i = 0; i < pathParts.Length; i++) { - FieldInfo info = t.GetField(pathParts[i], + FieldInfo f = t.GetField(pathParts[i], BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (info != null) + if (f != null) { - t = info.FieldType; + t = f.FieldType; + info = f; } } - - string propertyName = pathParts.LastOrDefault(); - var f = t.GetField(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (f != null) + + if (info != null) { - var a = f.GetCustomAttributes(false); + var a = info.GetCustomAttributes(false); foreach (var attr in a) { var uiRestriction = attr as AssetReferenceUIRestriction; diff --git a/Editor/Settings/LabelTable.cs b/Editor/Settings/LabelTable.cs index 25042673..59075ec4 100644 --- a/Editor/Settings/LabelTable.cs +++ b/Editor/Settings/LabelTable.cs @@ -29,6 +29,19 @@ internal bool AddLabelName(string name) return true; } + internal bool AddLabelName(string name, int index) + { + if (m_LabelNames.Contains(name)) + return false; + if (name.Contains("[") && name.Contains("]")) + { + Debug.LogErrorFormat("Label name '{0}' cannot contain '[ ]'.", name); + return false; + } + m_LabelNames.Insert(index, name); + return true; + } + internal string GetUniqueLabelName(string name) { var newName = name; @@ -71,6 +84,11 @@ internal string GetString(HashSet val, float width) //TODO - use width t return sb.ToString(); } + internal int GetIndexOfLabel(string label) + { + return m_LabelNames.IndexOf(label); + } + internal long GetMask(HashSet maskSet) { if (maskSet.Count == 0) diff --git a/Editor/Settings/ProfileGroupType.cs b/Editor/Settings/ProfileGroupType.cs new file mode 100644 index 00000000..7549b866 --- /dev/null +++ b/Editor/Settings/ProfileGroupType.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace UnityEditor.AddressableAssets.Settings +{ + /// + /// Used to store path pairs and act as an abstraction between path pairs and profile variables. + /// + internal class ProfileGroupType + { + /// + /// Used to store path values, identified by postfix + /// + internal class GroupTypeVariable + { + /// + /// Constructor for variables + /// + /// + /// + internal GroupTypeVariable(string suffix, string value) + { + m_Suffix = suffix; + m_Value = value; + } + + /// + /// Postfix of a GroupTypeVariable + /// + internal string m_Suffix; + internal string Suffix + { + get { return m_Suffix; } + set { m_Suffix = value; } + } + + /// + /// Specified Value + /// + internal string m_Value; + internal string Value + { + get { return m_Value; } + set { m_Value = value; } + } + } + + const char k_PrefixSeparator = '.'; + + string m_GroupTypePrefix; + + /// + /// Common prefix used in determining a path pair + /// + internal string GroupTypePrefix + { + get { return m_GroupTypePrefix; } + set { m_GroupTypePrefix = value; } + } + + /// + /// Group of variables that share a common prefix + /// + internal List m_Variables; + internal List Variables + { + get { return m_Variables; } + } + + /// + /// ctors for profile group type + /// + internal ProfileGroupType() { } + + internal ProfileGroupType(string prefix) + { + m_GroupTypePrefix = prefix; + m_Variables = new List(); + } + + /// + /// Returns the full variable name + /// + /// + /// the full name of a variable + internal string GetName(GroupTypeVariable variable) + { + return m_GroupTypePrefix + k_PrefixSeparator + variable.Suffix; + } + + /// + /// Adds a variable in the group + /// + /// + /// the added variable + internal GroupTypeVariable AddVariable(GroupTypeVariable variable) + { + GroupTypeVariable exists = m_Variables.Where(ps => ps.Suffix == variable.Suffix).FirstOrDefault(); + if (exists != null) + { + Addressables.LogErrorFormat("{0} already exists.", GetName(variable)); + return null; + } + else + { + m_Variables.Add(variable); + } + return variable; + } + + /// + /// Removes a variable from the group + /// + /// + internal void RemoveVariable(GroupTypeVariable variable) + { + GroupTypeVariable exists = m_Variables.Where(ps => ps.Suffix == variable.Suffix).FirstOrDefault(); + if (exists == null) + { + Addressables.LogErrorFormat("{0} does not exist.", GetName(variable)); + return; + } + else + { + m_Variables.Remove(variable); + } + } + + /// + /// Gets a variable by its suffix name + /// + /// + /// the variable if exists, null otherwise + internal GroupTypeVariable GetVariableBySuffix(string suffix) + { + return m_Variables.Where(var => var.m_Suffix == suffix).FirstOrDefault(); + } + + /// + /// Determines if the group type is a valid + /// + /// True, if the group type has a prefix, a build path, and a load path, false otherwise + internal bool IsValidGroupType() + { + return m_GroupTypePrefix != null && GetVariableBySuffix("BuildPath") != null && GetVariableBySuffix("LoadPath") != null; + } + } + +} \ No newline at end of file diff --git a/Tests/Editor/AssetReferenceDrawerTests.cs.meta b/Editor/Settings/ProfileGroupType.cs.meta similarity index 80% rename from Tests/Editor/AssetReferenceDrawerTests.cs.meta rename to Editor/Settings/ProfileGroupType.cs.meta index fa3d0223..9a24e264 100644 --- a/Tests/Editor/AssetReferenceDrawerTests.cs.meta +++ b/Editor/Settings/ProfileGroupType.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8f1af3c9e697f3c439c2716bb5704abb +guid: c965ab7077c7542bd9130d8c2a8c3c95 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Addressables.cs b/Runtime/Addressables.cs index 402fa84c..9ae22e5a 100644 --- a/Runtime/Addressables.cs +++ b/Runtime/Addressables.cs @@ -11,6 +11,7 @@ using UnityEngine.ResourceManagement.Util; using UnityEngine.SceneManagement; using System.Collections; +using UnityEngine.Networking; #if UNITY_EDITOR using UnityEditor; #endif @@ -146,6 +147,18 @@ static public Func InternalIdTransformFunc set { m_Addressables.InternalIdTransformFunc = value; } } + /// + /// Delegate that can be used to override the web request options before being sent. + /// + /// + /// The web request passed to this delegate has already been preconfigured internally. Override at your own risk. + /// + internal static Action WebRequestOverride + { + get { return m_Addressables.WebRequestOverride; } + set { m_Addressables.WebRequestOverride = value; } + } + /// /// Options for merging the results of requests. /// If keys (A, B) mapped to results ([1,2,4],[3,4,5])... @@ -195,8 +208,8 @@ public enum MergeMode /// /// The path to the Addressables Library subfolder /// - public static string LibraryPath = "Library/com.unity.addressables/"; - + public static string LibraryPath = "Library/com.unity.addressables/"; + /// /// The path used by the Addressables system for its initialization data. /// @@ -796,6 +809,7 @@ public static AsyncOperationHandle GetDownloadSizeAsync(object key) { return m_Addressables.GetDownloadSizeAsync(key); } + /// /// Determines the required download size, dependencies included, for the specified . /// Cached assets require no download and thus their download size will be 0. The Result of the operation @@ -882,6 +896,7 @@ public static AsyncOperationHandle DownloadDependenciesAsync(IList keys, { return m_Addressables.DownloadDependenciesAsync(keys, mode, autoReleaseHandle); } + /// /// Downloads dependencies of assets marked with the specified labels or addresses. /// See the [DownloadDependenciesAsync](xref:addressables-api-download-dependencies-async) documentation for more details. @@ -925,7 +940,7 @@ public static void ClearDependencyCacheAsync(IList keys) { m_Addressables.ClearDependencyCacheAsync(keys, true); } - + /// /// Clear the cached AssetBundles for a list of Addressable keys. Operation may be performed async if Addressables /// is initializing or updating. @@ -935,7 +950,7 @@ public static void ClearDependencyCacheAsync(IEnumerable keys) { m_Addressables.ClearDependencyCacheAsync(keys, true); } - + /// /// Clear the cached AssetBundles for a list of Addressable keys. Operation may be performed async if Addressables /// is initializing or updating. diff --git a/Runtime/AddressablesImpl.cs b/Runtime/AddressablesImpl.cs index 08f2ab21..7d19208a 100644 --- a/Runtime/AddressablesImpl.cs +++ b/Runtime/AddressablesImpl.cs @@ -8,6 +8,7 @@ using UnityEngine.AddressableAssets.Initialization; using UnityEngine.AddressableAssets.ResourceLocators; using UnityEngine.AddressableAssets.ResourceProviders; +using UnityEngine.Networking; using UnityEngine.ResourceManagement; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; @@ -135,6 +136,11 @@ public Func InternalIdTransformFunc set { ResourceManager.InternalIdTransformFunc = value; } } + internal Action WebRequestOverride + { + get { return ResourceManager.WebRequestOverride; } + set { ResourceManager.WebRequestOverride = value; } + } public AsyncOperationHandle ChainOperation { diff --git a/Runtime/Initialization/InitializationOperation.cs b/Runtime/Initialization/InitializationOperation.cs index 4df2f28f..14475389 100644 --- a/Runtime/Initialization/InitializationOperation.cs +++ b/Runtime/Initialization/InitializationOperation.cs @@ -102,7 +102,7 @@ protected override void Execute() Addressables.LogFormat("Initializing Addressables version {0}.", m_rtdOp.Result.AddressablesVersion); #endif var rtd = m_rtdOp.Result; - + m_Addressables.ResourceManager.postProfilerEvents = rtd.ProfileEvents; WebRequestQueue.SetMaxConcurrentRequests(rtd.MaxConcurrentWebRequests); m_Addressables.CatalogRequestsTimeout = rtd.CatalogRequestsTimeout; @@ -131,7 +131,7 @@ protected override void Execute() m_Diagnostics = null; m_Addressables.ResourceManager.ClearDiagnosticCallbacks(); } - + Addressables.Log("Addressables - loading initialization objects."); ContentCatalogProvider ccp = m_Addressables.ResourceManager.ResourceProviders @@ -242,23 +242,23 @@ static AsyncOperationHandle OnCatalogDataLoaded(AddressablesIm public static AsyncOperationHandle LoadContentCatalog(AddressablesImpl addressables, IResourceLocation loc, string providerSuffix, IResourceLocation remoteHashLocation = null) { Type provType = typeof(ProviderOperation); - var catalogOp = addressables.ResourceManager.CreateOperation>( provType, provType.GetHashCode(), 0, null ); + var catalogOp = addressables.ResourceManager.CreateOperation>(provType, provType.GetHashCode(), null, null); IResourceProvider catalogProvider = null; - foreach( IResourceProvider provider in addressables.ResourceManager.ResourceProviders ) + foreach (IResourceProvider provider in addressables.ResourceManager.ResourceProviders) { - if( provider is ContentCatalogProvider ) + if (provider is ContentCatalogProvider) { catalogProvider = provider; break; } } - var dependencies = addressables.ResourceManager.CreateGroupOperation( loc.Dependencies, true ); - catalogOp.Init( addressables.ResourceManager, catalogProvider, loc, dependencies, true ); - var catalogHandle = addressables.ResourceManager.StartOperation( catalogOp, dependencies ); + var dependencies = addressables.ResourceManager.CreateGroupOperation(loc.Dependencies, true); + catalogOp.Init(addressables.ResourceManager, catalogProvider, loc, dependencies, true); + var catalogHandle = addressables.ResourceManager.StartOperation(catalogOp, dependencies); dependencies.Release(); - + var chainOp = addressables.ResourceManager.CreateChainOperation(catalogHandle, res => OnCatalogDataLoaded(addressables, res, providerSuffix, remoteHashLocation)); return chainOp; } diff --git a/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs b/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs index 26224df1..16ea98fe 100644 --- a/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs +++ b/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs @@ -15,7 +15,7 @@ namespace UnityEngine.ResourceManagement.AsyncOperations { internal interface ICachable { - int Hash { get; set; } + IOperationCacheKey Key { get; set; } } internal interface IAsyncOperation @@ -106,7 +106,7 @@ protected virtual void GetDependencies(List dependencies) internal Action OnDestroy { set { m_OnDestroyAction = value; } } internal int ReferenceCount { get { return m_referenceCount; } } Action m_dependencyCompleteAction; - protected bool HasExecuted = false; + protected internal bool HasExecuted = false; /// /// True if the current op has begun but hasn't yet reached completion. False otherwise. @@ -158,7 +158,7 @@ public void WaitForCompletion() /// Used for the implementation of WaitForCompletion in an IAsyncOperation. /// /// True if the operation has completed, otherwise false. - protected virtual bool InvokeWaitForCompletion(){ return IsDone; } + protected virtual bool InvokeWaitForCompletion() { return IsDone; } internal void DecrementReferenceCount() { @@ -338,7 +338,7 @@ internal void InvokeCompletionEvent() } if (m_taskCompletionSource != null) m_taskCompletionSource.TrySetResult(Result); - + if (m_taskCompletionSourceTypeless != null) m_taskCompletionSourceTypeless.TrySetResult(Result); @@ -434,8 +434,8 @@ public void Complete(TObject result, bool success, Exception exception, bool rel m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationFail, 0, exception?.ToString())); ICachable cachedOperation = this as ICachable; - if (cachedOperation != null) - m_RM?.RemoveOperationFromCache(cachedOperation.Hash); + if (cachedOperation?.Key != null) + m_RM?.RemoveOperationFromCache(cachedOperation.Key); RegisterForDeferredCallbackEvent(false); } @@ -451,6 +451,7 @@ internal void Start(ResourceManager rm, AsyncOperationHandle dependency, Delegat { m_RM = rm; IsRunning = true; + HasExecuted = false; if (m_RM != null && m_RM.postProfilerEvents) { m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationCreate)); diff --git a/Runtime/ResourceManager/AsyncOperations/GroupOperation.cs b/Runtime/ResourceManager/AsyncOperations/GroupOperation.cs index 2cb4fdae..a1ca63be 100644 --- a/Runtime/ResourceManager/AsyncOperations/GroupOperation.cs +++ b/Runtime/ResourceManager/AsyncOperations/GroupOperation.cs @@ -51,7 +51,7 @@ protected override bool InvokeWaitForCompletion() return IsDone; } - int ICachable.Hash { get; set; } + IOperationCacheKey ICachable.Key { get; set; } internal IList GetDependentOps() { diff --git a/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs b/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs index 592f7694..198af150 100644 --- a/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs +++ b/Runtime/ResourceManager/AsyncOperations/ProviderOperation.cs @@ -36,7 +36,7 @@ internal class ProviderOperation : AsyncOperationBase, IGeneri private IResourceLocation m_Location; private int m_ProvideHandleVersion; private bool m_NeedsRelease; - int ICachable.Hash { get; set; } + IOperationCacheKey ICachable.Key { get; set; } private ResourceManager m_ResourceManager; private const float k_OperationWaitingToCompletePercentComplete = 0.99f; public int ProvideHandleVersion { get { return m_ProvideHandleVersion; } } diff --git a/Runtime/ResourceManager/ResourceManager.cs b/Runtime/ResourceManager/ResourceManager.cs index 3c1d6da3..e67746d1 100644 --- a/Runtime/ResourceManager/ResourceManager.cs +++ b/Runtime/ResourceManager/ResourceManager.cs @@ -136,6 +136,14 @@ public string TransformInternalId(IResourceLocation location) return InternalIdTransformFunc == null ? location.InternalId : InternalIdTransformFunc(location); } + /// + /// Delegate that can be used to override the web request options before being sent. + /// + /// + /// The web request passed to this delegate has already been preconfigured internally. Override at your own risk. + /// + internal Action WebRequestOverride { get; set; } + internal bool CallbackHooksEnabled = true; // tests might need to disable the callback hooks to manually pump updating ListWithEvents m_ResourceProviders = new ListWithEvents(); @@ -144,12 +152,15 @@ public string TransformInternalId(IResourceLocation location) // list of all the providers in s_ResourceProviders that implement IUpdateReceiver ListWithEvents m_UpdateReceivers = new ListWithEvents(); List m_UpdateReceiversToRemove = null; + //this prevents removing receivers during iteration bool m_UpdatingReceivers = false; + //this prevents re-entrance into the Update method, which can cause stack overflow and infinite loops + bool m_InsideUpdateMethod = false; internal int OperationCacheCount { get { return m_AssetOperationCache.Count; } } internal int InstanceOperationCount { get { return m_TrackedInstanceOperations.Count; } } //cache of type + providerId to IResourceProviders for faster lookup internal Dictionary m_providerMap = new Dictionary(); - Dictionary m_AssetOperationCache = new Dictionary(); + Dictionary m_AssetOperationCache = new Dictionary(); HashSet m_TrackedInstanceOperations = new HashSet(); DelegateList m_UpdateCallbacks = DelegateList.CreateWithGlobalCache(); List m_DeferredCompleteCallbacks = new List(); @@ -382,8 +393,8 @@ private AsyncOperationHandle ProvideResource(IResourceLocation location, Type de } IAsyncOperation op; - int hash = location.Hash(desiredType); - if (m_AssetOperationCache.TryGetValue(hash, out op)) + var key = new LocationCacheKey(location, desiredType); + if (m_AssetOperationCache.TryGetValue(key, out op)) { op.IncrementReferenceCount(); return new AsyncOperationHandle(op, location.ToString());; @@ -392,7 +403,7 @@ private AsyncOperationHandle ProvideResource(IResourceLocation location, Type de Type provType; if (!m_ProviderOperationTypeCache.TryGetValue(desiredType, out provType)) m_ProviderOperationTypeCache.Add(desiredType, provType = typeof(ProviderOperation<>).MakeGenericType(new Type[] { desiredType })); - op = CreateOperation(provType, provType.GetHashCode(), hash, m_ReleaseOpCached); + op = CreateOperation(provType, provType.GetHashCode(), key, m_ReleaseOpCached); // Calculate the hash of the dependencies int depHash = location.DependencyHashCode; @@ -501,13 +512,13 @@ void OnOperationDestroyCached(IAsyncOperation o) { Allocator.Release(o.GetType().GetHashCode(), o); var cachable = o as ICachable; - if (cachable != null) - RemoveOperationFromCache(cachable.Hash); + if (cachable?.Key != null) + RemoveOperationFromCache(cachable.Key); } - internal T CreateOperation(Type actualType, int typeHash, int operationHash, Action onDestroyAction) where T : IAsyncOperation + internal T CreateOperation(Type actualType, int typeHash, IOperationCacheKey cacheKey, Action onDestroyAction) where T : IAsyncOperation { - if (operationHash == 0) + if (cacheKey == null) { var op = (T)Allocator.New(actualType, typeHash); op.OnDestroy = onDestroyAction; @@ -517,31 +528,32 @@ internal T CreateOperation(Type actualType, int typeHash, int operationHash, { var op = (T)Allocator.New(actualType, typeHash); op.OnDestroy = onDestroyAction; - var cachable = op as ICachable; - if (cachable != null) - cachable.Hash = operationHash; - AddOperationToCache(operationHash, op); + if (op is ICachable cachable) + { + cachable.Key = cacheKey; + AddOperationToCache(cacheKey, op); + } return op; } } - internal void AddOperationToCache(int hash, IAsyncOperation operation) + internal void AddOperationToCache(IOperationCacheKey key, IAsyncOperation operation) { - if (!IsOperationCached(hash)) - m_AssetOperationCache.Add(hash, operation); + if (!IsOperationCached(key)) + m_AssetOperationCache.Add(key, operation); } - internal bool RemoveOperationFromCache(int hash) + internal bool RemoveOperationFromCache(IOperationCacheKey key) { - if (!IsOperationCached(hash)) + if (!IsOperationCached(key)) return true; - return m_AssetOperationCache.Remove(hash); + return m_AssetOperationCache.Remove(key); } - internal bool IsOperationCached(int hash) + internal bool IsOperationCached(IOperationCacheKey key) { - return m_AssetOperationCache.ContainsKey(hash); + return m_AssetOperationCache.ContainsKey(key); } internal int CachedOperationCount() @@ -576,7 +588,7 @@ public AsyncOperationHandle CreateCompletedOperationWithException CreateCompletedOperationInternal(TObject result, bool success, Exception exception, bool releaseDependenciesOnFailure = true) { - var cop = CreateOperation>(typeof(CompletedOperation), typeof(CompletedOperation).GetHashCode(), 0, m_ReleaseOpNonCached); + var cop = CreateOperation>(typeof(CompletedOperation), typeof(CompletedOperation).GetHashCode(), null, m_ReleaseOpNonCached); cop.Init(result, success, exception, releaseDependenciesOnFailure); return StartOperation(cop, default(AsyncOperationHandle)); } @@ -599,10 +611,10 @@ public void Acquire(AsyncOperationHandle handle) handle.Acquire(); } - private GroupOperation AcquireGroupOpFromCache(int hash) + private GroupOperation AcquireGroupOpFromCache(IOperationCacheKey key) { IAsyncOperation opGeneric; - if (m_AssetOperationCache.TryGetValue(hash, out opGeneric)) + if (m_AssetOperationCache.TryGetValue(key, out opGeneric)) { opGeneric.IncrementReferenceCount(); return (GroupOperation)opGeneric; @@ -618,7 +630,7 @@ private GroupOperation AcquireGroupOpFromCache(int hash) /// The operation for the entire group. public AsyncOperationHandle> CreateGroupOperation(IList locations) { - var op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, 0, m_ReleaseOpNonCached); + var op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, null, m_ReleaseOpNonCached); var ops = new List(locations.Count); foreach (var loc in locations) ops.Add(ProvideResource(loc)); @@ -636,7 +648,7 @@ public AsyncOperationHandle> CreateGroupOperation /// The operation for the entire group. internal AsyncOperationHandle> CreateGroupOperation(IList locations, bool allowFailedDependencies) { - var op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, 0, m_ReleaseOpNonCached); + var op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, null, m_ReleaseOpNonCached); var ops = new List(locations.Count); foreach (var loc in locations) ops.Add(ProvideResource(loc)); @@ -656,7 +668,7 @@ internal AsyncOperationHandle> CreateGroupOperation< /// The operation for the entire group public AsyncOperationHandle> CreateGenericGroupOperation(List operations, bool releasedCachedOpOnComplete = false) { - var op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, operations.GetHashCode(), releasedCachedOpOnComplete ? m_ReleaseOpCached : m_ReleaseOpNonCached); + var op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, new AsyncOpHandlesCacheKey(operations), releasedCachedOpOnComplete ? m_ReleaseOpCached : m_ReleaseOpNonCached); op.Init(operations); return StartOperation(op, default); } @@ -664,11 +676,12 @@ public AsyncOperationHandle> CreateGenericGroupOpera internal AsyncOperationHandle> ProvideResourceGroupCached( IList locations, int groupHash, Type desiredType, Action callback, bool releaseDependenciesOnFailure = true) { - GroupOperation op = AcquireGroupOpFromCache(groupHash); + var depsKey = new DependenciesCacheKey(locations, groupHash); + GroupOperation op = AcquireGroupOpFromCache(depsKey); AsyncOperationHandle> handle; if (op == null) { - op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, groupHash, m_ReleaseOpCached); + op = CreateOperation(typeof(GroupOperation), s_GroupOperationTypeHash, depsKey, m_ReleaseOpCached); var ops = new List(locations.Count); foreach (var loc in locations) ops.Add(ProvideResource(loc, desiredType, releaseDependenciesOnFailure)); @@ -789,7 +802,7 @@ public AsyncOperationHandle> ProvideResources(IListThe operation handle. public AsyncOperationHandle CreateChainOperation(AsyncOperationHandle dependentOp, Func, AsyncOperationHandle> callback) { - var op = CreateOperation>(typeof(ChainOperation), typeof(ChainOperation).GetHashCode(), 0, null); + var op = CreateOperation>(typeof(ChainOperation), typeof(ChainOperation).GetHashCode(), null, null); op.Init(dependentOp, callback, true); return StartOperation(op, dependentOp); } @@ -819,7 +832,7 @@ public AsyncOperationHandle CreateChainOperation(AsyncOperatio /// The operation handle. public AsyncOperationHandle CreateChainOperation(AsyncOperationHandle dependentOp, Func, AsyncOperationHandle> callback, bool releaseDependenciesOnFailure = true) { - var op = CreateOperation>(typeof(ChainOperation), typeof(ChainOperation).GetHashCode(), 0, null); + var op = CreateOperation>(typeof(ChainOperation), typeof(ChainOperation).GetHashCode(), null, null); op.Init(dependentOp, callback, releaseDependenciesOnFailure); return StartOperation(op, dependentOp); } @@ -901,7 +914,7 @@ protected override bool InvokeWaitForCompletion() if (m_instance == null && !HasExecuted) InvokeExecute(); - return m_instance != null; + return IsDone; } protected override void Execute() @@ -970,7 +983,7 @@ public AsyncOperationHandle ProvideInstance(IInstanceProvider provid throw new ArgumentNullException("location"); var depOp = ProvideResource(location); - var baseOp = CreateOperation(typeof(InstanceOperation), s_InstanceOperationTypeHash, 0, m_ReleaseInstanceOp); + var baseOp = CreateOperation(typeof(InstanceOperation), s_InstanceOperationTypeHash, null, m_ReleaseInstanceOp); baseOp.Init(this, provider, instantiateParameters, depOp); m_TrackedInstanceOperations.Add(baseOp); return StartOperation(baseOp, depOp); @@ -1022,6 +1035,9 @@ internal void RegisterForDeferredCallback(IAsyncOperation op, bool incrementRefC internal void Update(float unscaledDeltaTime) { + if (m_InsideUpdateMethod) + throw new Exception("Reentering the Update method is not allowed. This can happen when calling WaitForCompletion on an operation while inside of a callback."); + m_InsideUpdateMethod = true; m_UpdateCallbacks.Invoke(unscaledDeltaTime); m_UpdatingReceivers = true; for (int i = 0; i < m_UpdateReceivers.Count; i++) @@ -1034,6 +1050,7 @@ internal void Update(float unscaledDeltaTime) m_UpdateReceiversToRemove = null; } ExecuteDeferredCallbacks(); + m_InsideUpdateMethod = false; } /// diff --git a/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs b/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs index ccefd1c3..b6e7ff81 100644 --- a/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs @@ -132,26 +132,36 @@ public virtual long ComputeSize(IResourceLocation location, ResourceManager reso } } - class AssetBundleResource : IAssetBundleResource + internal class AssetBundleResource : IAssetBundleResource { + internal enum LoadType + { + None, + Local, + Web + } + AssetBundle m_AssetBundle; DownloadHandlerAssetBundle m_downloadHandler; AsyncOperation m_RequestOperation; WebRequestQueueOperation m_WebRequestQueueOperation; internal ProvideHandle m_ProvideHandle; internal AssetBundleRequestOptions m_Options; + [NonSerialized] + bool m_WebRequestCompletedCallbackCalled = false; int m_Retries; long m_BytesToDownload; long m_DownloadedBytes; bool m_Completed = false; - + const int k_WaitForWebRequestMainThreadSleep = 1; + string m_TransformedInternalId; internal UnityWebRequest CreateWebRequest(IResourceLocation loc) { var url = m_ProvideHandle.ResourceManager.TransformInternalId(loc); return CreateWebRequest(url); } - + internal UnityWebRequest CreateWebRequest(string url) { if (m_Options == null) @@ -184,6 +194,8 @@ internal UnityWebRequest CreateWebRequest(string url) webRequest.certificateHandler = m_ProvideHandle.ResourceManager.CertificateHandlerInstance; webRequest.disposeCertificateHandlerOnDispose = false; } + + m_ProvideHandle.ResourceManager.WebRequestOverride?.Invoke(webRequest); return webRequest; } @@ -234,6 +246,7 @@ internal void Start(ProvideHandle provideHandle) m_AssetBundle = null; m_downloadHandler = null; m_RequestOperation = null; + m_WebRequestCompletedCallbackCalled = false; m_ProvideHandle = provideHandle; m_Options = m_ProvideHandle.Location.Data as AssetBundleRequestOptions; if (m_Options != null) @@ -251,10 +264,16 @@ private bool WaitForCompletionHandler() //We don't want to wait for request op to complete if it's a LoadFromFileAsync. Only UWR will complete in a tight loop like this. if (!(m_RequestOperation is AssetBundleCreateRequest)) - while (!m_RequestOperation.isDone) {} + while (!m_RequestOperation.isDone) { System.Threading.Thread.Sleep(k_WaitForWebRequestMainThreadSleep); } + + if (m_RequestOperation is UnityWebRequestAsyncOperation && !m_WebRequestCompletedCallbackCalled) + { + WebRequestOperationCompleted(m_RequestOperation); + m_RequestOperation.completed -= WebRequestOperationCompleted; + } var assetBundle = GetAssetBundle(); - if (!m_Completed && assetBundle != null) + if (!m_Completed && m_RequestOperation.isDone) { m_ProvideHandle.Complete(this, m_AssetBundle != null, null); m_Completed = true; @@ -270,38 +289,53 @@ void AddCallbackInvokeIfDone(AsyncOperation operation, Action ca else operation.completed += callback; } + + internal static void GetLoadInfo(ProvideHandle handle, out LoadType loadType, out string path) + { + var options = handle.Location?.Data as AssetBundleRequestOptions; + if (options == null) + { + loadType = LoadType.None; + path = null; + return; + } + + path = handle.ResourceManager.TransformInternalId(handle.Location); + if (Application.platform == RuntimePlatform.Android && path.StartsWith("jar:")) + loadType = options.UseUnityWebRequestForLocalBundles ? LoadType.Web : LoadType.Local; + else if (ResourceManagerConfig.ShouldPathUseWebRequest(path)) + loadType = LoadType.Web; + else if (options.UseUnityWebRequestForLocalBundles) + { + path = "file:///" + Path.GetFullPath(path); + loadType = LoadType.Web; + } + else + loadType = LoadType.Local; + } private void BeginOperation() { m_DownloadedBytes = 0; - string path = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location); - bool fileExists = File.Exists(path); - bool shouldLoadFromFile = fileExists; - if ((Application.platform == RuntimePlatform.Android && path.StartsWith("jar:"))) - shouldLoadFromFile = !m_Options.UseUnityWebRequestForLocalBundles; - else if (shouldLoadFromFile && m_Options.UseUnityWebRequestForLocalBundles) - { - shouldLoadFromFile = false; - if (fileExists && !ResourceManagerConfig.ShouldPathUseWebRequest(path)) - path = "file:///" + Path.GetFullPath(path); - } - if (shouldLoadFromFile) + GetLoadInfo(m_ProvideHandle, out LoadType loadType, out m_TransformedInternalId); + + if (loadType == LoadType.Local) { #if !UNITY_2021_1_OR_NEWER if (AsyncOperationHandle.IsWaitingForCompletion) - CompleteBundleLoad(AssetBundle.LoadFromFile(path, m_Options == null ? 0 : m_Options.Crc)); + CompleteBundleLoad(AssetBundle.LoadFromFile(m_TransformedInternalId, m_Options == null ? 0 : m_Options.Crc)); else #endif { - m_RequestOperation = AssetBundle.LoadFromFileAsync(path, m_Options == null ? 0 : m_Options.Crc); + m_RequestOperation = AssetBundle.LoadFromFileAsync(m_TransformedInternalId, m_Options == null ? 0 : m_Options.Crc); AddCallbackInvokeIfDone(m_RequestOperation, LocalRequestOperationCompleted); } } - else if (ResourceManagerConfig.ShouldPathUseWebRequest(path)) + else if (loadType == LoadType.Web) { - var req = CreateWebRequest(path); + var req = CreateWebRequest(m_TransformedInternalId); req.disposeDownloadHandlerOnDispose = false; - + m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req); if (m_WebRequestQueueOperation.IsDone) { @@ -323,7 +357,7 @@ private void BeginOperation() else { m_RequestOperation = null; - m_ProvideHandle.Complete(null, false, new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", path), m_ProvideHandle.Location)); + m_ProvideHandle.Complete(null, false, new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", m_TransformedInternalId), m_ProvideHandle.Location)); m_Completed = true; } } @@ -336,12 +370,19 @@ private void LocalRequestOperationCompleted(AsyncOperation op) private void CompleteBundleLoad(AssetBundle bundle) { m_AssetBundle = bundle; - m_ProvideHandle.Complete(this, m_AssetBundle != null, null); + if (m_AssetBundle != null) + m_ProvideHandle.Complete(this, true, null); + else + m_ProvideHandle.Complete(null, false, new RemoteProviderException(string.Format("Invalid path in AssetBundleProvider: '{0}'.", m_TransformedInternalId), m_ProvideHandle.Location)); m_Completed = true; } private void WebRequestOperationCompleted(AsyncOperation op) { + if (m_WebRequestCompletedCallbackCalled) + return; + + m_WebRequestCompletedCallbackCalled = true; UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation; var webReq = remoteReq.webRequest; if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult)) diff --git a/Runtime/ResourceManager/ResourceProviders/AssetDatabaseProvider.cs b/Runtime/ResourceManager/ResourceProviders/AssetDatabaseProvider.cs index 1e9fcec7..577dae28 100644 --- a/Runtime/ResourceManager/ResourceProviders/AssetDatabaseProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/AssetDatabaseProvider.cs @@ -1,13 +1,10 @@ #if UNITY_EDITOR using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; using UnityEditor; -using UnityEditor.SceneManagement; using UnityEngine.ResourceManagement.ResourceLocations; using UnityEngine.ResourceManagement.Util; -using UnityEngine.SceneManagement; namespace UnityEngine.ResourceManagement.ResourceProviders { @@ -17,72 +14,11 @@ namespace UnityEngine.ResourceManagement.ResourceProviders [DisplayName("Assets from AssetDatabase Provider")] public class AssetDatabaseProvider : ResourceProviderBase { - private class AssetInstance - { - public Object Instance; - public int LoadCount; - } - float m_LoadDelay = .1f; - private static Dictionary InstanceCache = new Dictionary(); - private static Dictionary InstanceIDToAsset = new Dictionary(); - private static Type GameObjectType = typeof(GameObject); - private static Type ScriptableObjectType = typeof(ScriptableObject); - private static Scene _PreviewScene; - private static Scene PreviewScene - { - get - { - if (!_PreviewScene.IsValid()) - { - _PreviewScene = EditorSceneManager.NewPreviewScene(); - _PreviewScene.name = "InstancePreviewScene"; - - EditorApplication.playModeStateChanged += PlayModeState; - if (EditorPrefs.GetInt("ScriptCompilationDuringPlay") == 0) // Recompile and continue playing - EditorApplication.update += EditorUpdate; - } - - return _PreviewScene; - } - } - - private static void PlayModeState(PlayModeStateChange state) - { - if (state == PlayModeStateChange.ExitingPlayMode) - ClosePreviewScene(); - } - - private static void EditorUpdate() - { - if (EditorApplication.isPlaying && EditorApplication.isCompiling && _PreviewScene.IsValid()) - ClosePreviewScene(); - } - - private static void ClosePreviewScene() - { - if (_PreviewScene.IsValid()) - { - EditorSceneManager.ClosePreviewScene(_PreviewScene); - InstanceCache.Clear(); - InstanceIDToAsset.Clear(); - } - else - { - Debug.LogError("Unable to close AssetDatabaseMode instance preview scene"); - } - - EditorApplication.update -= EditorUpdate; - EditorApplication.playModeStateChanged -= PlayModeState; - } - private static Object[] LoadAllAssetRepresentationsAtPath(string assetPath) { - var allObjects = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath); - for (int i = 0; i < allObjects.Length; ++i) - allObjects[i] = GetInstanceObject(allObjects[i]); - return allObjects; + return AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath); } internal static Object LoadAssetSubObject(string assetPath, string subObjectName, Type type) @@ -93,9 +29,7 @@ internal static Object LoadAssetSubObject(string assetPath, string subObjectName if (o.name == subObjectName) { if (type.IsAssignableFrom(o.GetType())) - { - return GetInstanceObject(o); - } + return o; } } return null; @@ -103,7 +37,7 @@ internal static Object LoadAssetSubObject(string assetPath, string subObjectName private static Object LoadMainAssetAtPath(string assetPath) { - return GetInstanceObject(AssetDatabase.LoadMainAssetAtPath(assetPath)); + return AssetDatabase.LoadMainAssetAtPath(assetPath); } internal static object LoadAssetAtPath(string assetPath, ProvideHandle provideHandle) @@ -111,85 +45,7 @@ internal static object LoadAssetAtPath(string assetPath, ProvideHandle provideHa Object obj = AssetDatabase.LoadAssetAtPath(assetPath, provideHandle.Location.ResourceType); Type objType = obj.GetType(); obj = obj != null && provideHandle.Type.IsAssignableFrom(objType) ? obj : null; - return GetInstanceObject(obj, objType); - } - - private static Object GetInstanceObject(Object original, Type resultType = null) - { - if (original != null) - { - if (resultType == null) - resultType = original.GetType(); - if (GameObjectType.IsAssignableFrom(resultType)) - { - Object o = AddOrGetInstanceFromCache(original); - SceneManager.MoveGameObjectToScene((GameObject)o, PreviewScene); - return o; - } - if (ScriptableObjectType.IsAssignableFrom(resultType)) - { - return AddOrGetInstanceFromCache(original); - } - } - - return original; - } - - private static Object AddOrGetInstanceFromCache(Object original) - { - if (InstanceCache.TryGetValue(original, out AssetInstance inst)) - { - inst.LoadCount++; - return inst.Instance; - } - Object o = Object.Instantiate(original); - o.name = o.name.Substring(0, o.name.Length - 7); - InstanceCache.Add(original, new AssetInstance() {Instance = o, LoadCount = 1}); - InstanceIDToAsset.Add(o.GetInstanceID(), original); - return o; - } - - internal static void ReleaseAssetDatabaseLoadedObject(object obj) - { - switch (obj) - { - case Array objArray: - { - foreach (object o in objArray) - Release(o as Object); - break; - } - case IList objList: - { - foreach (object o in objList) - Release(o as Object); - break; - } - default: - { - Release(obj as Object); - break; - } - } - } - - private static void Release(Object o) - { - if (o == null) - return; - - int instanceID = o.GetInstanceID(); - if (InstanceIDToAsset.TryGetValue(instanceID, out Object original)) - { - AssetInstance usage = InstanceCache[original]; - usage.LoadCount--; - if (usage.LoadCount == 0) - { - InstanceIDToAsset.Remove(instanceID); - Object.Destroy(usage.Instance); - InstanceCache.Remove(original); - } - } + return obj; } /// @@ -269,11 +125,6 @@ public override void Provide(ProvideHandle provideHandle) { new InternalOp().Start(provideHandle, m_LoadDelay); } - - public override void Release(IResourceLocation location, object obj) - { - ReleaseAssetDatabaseLoadedObject(obj); - } } } #endif diff --git a/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs b/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs index c985bc36..d45b5085 100644 --- a/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs +++ b/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualAssetBundle.cs @@ -128,6 +128,8 @@ public class VirtualAssetBundleEntry /// public long Size { get { return m_Size; } } + [SerializeField] + internal string m_AssetPath; /// /// Construct a new VirtualAssetBundleEntry /// @@ -489,7 +491,7 @@ public bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaT if (!(Context is IResourceLocation)) return false; var location = Context as IResourceLocation; - var assetPath = m_provideHandle.ResourceManager.TransformInternalId(location); + var assetPath = m_AssetInfo.m_AssetPath; object result = null; var pt = m_provideHandle.Type; @@ -499,8 +501,8 @@ public bool Load(long localBandwidth, long remoteBandwidth, float unscaledDeltaT result = ResourceManagerConfig.CreateListResult(pt, AssetDatabaseProvider.LoadAssetsWithSubAssets(assetPath)); else { - if (ResourceManagerConfig.ExtractKeyAndSubKey(assetPath, out string mainPath, out string subKey)) - result = AssetDatabaseProvider.LoadAssetSubObject(mainPath, subKey, pt); + if (ResourceManagerConfig.ExtractKeyAndSubKey(location.InternalId, out string mainPath, out string subKey)) + result = AssetDatabaseProvider.LoadAssetSubObject(assetPath, subKey, pt); else result = AssetDatabaseProvider.LoadAssetAtPath(assetPath, m_provideHandle); } diff --git a/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualBundledAssetProvider.cs b/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualBundledAssetProvider.cs index ee865cb4..9bff4f3f 100644 --- a/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualBundledAssetProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/Simulation/VirtualBundledAssetProvider.cs @@ -100,11 +100,6 @@ public override void Provide(ProvideHandle provideHandle) new InternalOp().Start(provideHandle, bundle); } } - - public override void Release(IResourceLocation location, object obj) - { - AssetDatabaseProvider.ReleaseAssetDatabaseLoadedObject(obj); - } } } #endif diff --git a/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs b/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs index 3d425b39..77e37d8d 100644 --- a/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs +++ b/Runtime/ResourceManager/ResourceProviders/TextDataProvider.cs @@ -156,6 +156,7 @@ protected virtual void SendWebRequest(string path) if (m_Timeout > 0) request.timeout = m_Timeout; + m_PI.ResourceManager.WebRequestOverride?.Invoke(request); m_RequestQueueOperation = WebRequestQueue.QueueRequest(request); if (m_RequestQueueOperation.IsDone) { diff --git a/Runtime/ResourceManager/Util/OperationCacheKeys.cs b/Runtime/ResourceManager/Util/OperationCacheKeys.cs new file mode 100644 index 00000000..47808e53 --- /dev/null +++ b/Runtime/ResourceManager/Util/OperationCacheKeys.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceLocations; + +namespace UnityEngine.ResourceManagement.Util +{ + internal interface IOperationCacheKey : IEquatable {} + + internal sealed class LocationCacheKey : IOperationCacheKey + { + readonly IResourceLocation m_Location; + readonly Type m_DesiredType; + + public LocationCacheKey(IResourceLocation location, Type desiredType) + { + if (location == null) + throw new NullReferenceException($"Resource location cannot be null."); + if (desiredType == null) + throw new NullReferenceException($"Desired type cannot be null."); + + m_Location = location; + m_DesiredType = desiredType; + } + + public override int GetHashCode() + { + return m_Location.Hash(m_DesiredType); + } + + public override bool Equals(object obj) => Equals(obj as LocationCacheKey); + public bool Equals(IOperationCacheKey other) => Equals(other as LocationCacheKey); + bool Equals(LocationCacheKey other) + { + if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(other, null)) return false; + return LocationUtils.LocationEquals(m_Location, other.m_Location) && Equals(m_DesiredType, other.m_DesiredType); + } + } + + internal sealed class DependenciesCacheKey : IOperationCacheKey + { + readonly IList m_Dependencies; + readonly int m_DependenciesHash; + public DependenciesCacheKey(IList dependencies, int dependenciesHash) + { + m_Dependencies = dependencies; + m_DependenciesHash = dependenciesHash; + } + + public override int GetHashCode() + { + return m_DependenciesHash; + } + + public override bool Equals(object obj) => Equals(obj as DependenciesCacheKey); + public bool Equals(IOperationCacheKey other) => Equals(other as DependenciesCacheKey); + bool Equals(DependenciesCacheKey other) + { + if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(other, null)) return false; + return LocationUtils.DependenciesEqual(m_Dependencies, other.m_Dependencies); + } + } + + internal sealed class AsyncOpHandlesCacheKey : IOperationCacheKey + { + readonly HashSet m_Handles; + public AsyncOpHandlesCacheKey(IList handles) + { + m_Handles = new HashSet(handles); + } + + public override int GetHashCode() + { + return m_Handles.GetHashCode(); + } + + public override bool Equals(object obj) => Equals(obj as AsyncOpHandlesCacheKey); + public bool Equals(IOperationCacheKey other) => Equals(other as AsyncOpHandlesCacheKey); + bool Equals(AsyncOpHandlesCacheKey other) + { + if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(other, null)) return false; + return m_Handles.SetEquals(other.m_Handles); + } + } + + internal static class LocationUtils + { + // TODO : Added equality methods here to prevent a minor version bump since we intend to stay on v1.18.x for a while. + // Ideally this should have been the Equals() implementation of IResourceLocation + public static bool LocationEquals(IResourceLocation loc1, IResourceLocation loc2) + { + if (ReferenceEquals(loc1, loc2)) return true; + if (ReferenceEquals(loc1, null)) return false; + if (ReferenceEquals(loc2, null)) return false; + if (loc1.GetType() != loc2.GetType()) return false; + + bool membersEqual = loc1.PrimaryKey == loc2.PrimaryKey + && loc1.InternalId == loc2.InternalId + && loc1.ProviderId == loc2.ProviderId + && loc1.ResourceType == loc2.ResourceType; + + if (!membersEqual) + return false; + + if (loc1.HasDependencies != loc2.HasDependencies) + return false; + + return DependenciesEqual(loc1.Dependencies, loc2.Dependencies); + } + + public static bool DependenciesEqual(IList deps1, IList deps2) + { + if (ReferenceEquals(deps1, deps2)) return true; + if (ReferenceEquals(deps1, null)) return false; + if (ReferenceEquals(deps2, null)) return false; + if (deps1.Count != deps2.Count) + return false; + + for (int i = 0; i < deps1.Count; i++) + { + if (!LocationEquals(deps1[i], deps2[i])) + return false; + } + + return true; + } + } +} diff --git a/Runtime/ResourceManager/Util/OperationCacheKeys.cs.meta b/Runtime/ResourceManager/Util/OperationCacheKeys.cs.meta new file mode 100644 index 00000000..d08f804b --- /dev/null +++ b/Runtime/ResourceManager/Util/OperationCacheKeys.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d597abbd594bb545be7bc273326f95c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/ImportExistingGroup.meta b/Samples~/ImportExistingGroup.meta new file mode 100644 index 00000000..2dd447bc --- /dev/null +++ b/Samples~/ImportExistingGroup.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5a7501c50d3f9304c960e1d1be564ebd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/ImportExistingGroup/.sample.json b/Samples~/ImportExistingGroup/.sample.json new file mode 100644 index 00000000..46d3757f --- /dev/null +++ b/Samples~/ImportExistingGroup/.sample.json @@ -0,0 +1,4 @@ +{ + "displayName": "Import Groups Tool", + "description": "A tool that imports group assets (for example from a custom package) to the current project." +} \ No newline at end of file diff --git a/Samples~/ImportExistingGroup/Editor.meta b/Samples~/ImportExistingGroup/Editor.meta new file mode 100644 index 00000000..1fc14234 --- /dev/null +++ b/Samples~/ImportExistingGroup/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 84f6a4f5c4b77e44aa3e2decf12f1bfc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs b/Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs new file mode 100644 index 00000000..7b22daac --- /dev/null +++ b/Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs @@ -0,0 +1,155 @@ +#if UNITY_EDITOR +using System; +using System.Collections; +using System.IO; +using UnityEditor; +using UnityEditor.AddressableAssets; +using UnityEditor.AddressableAssets.Settings; +using UnityEditor.AddressableAssets.Settings.GroupSchemas; +using UnityEngine; + +/// +/// Imports an existing AddressableAssetGroup and existing AddressableAssetGroupSchemas. +/// +public class ImportExistingGroup : EditorWindow +{ + string groupPath; + string groupName; + string schemaFolder; + + [MenuItem("Window/Asset Management/Addressables/Import Groups", priority = 2063)] + public static void ShowWindow() + { + AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings; + if (settings == null) + { + EditorUtility.DisplayDialog("Error", "Attempting to open Import Groups window, but no Addressables Settings file exists. \n\nOpen 'Window/Asset Management/Addressables/Groups' for more info.", "Ok"); + return; + } + GetWindow(typeof(ImportExistingGroup), false, "Import Groups"); + } + + void OnGUI() + { + groupPath = EditorGUILayout.TextField(new GUIContent("Group Path", "The path of the group asset to import, for example 'Packages/com.unity.example/MyGroup.asset'."), groupPath); + using (new GUILayout.HorizontalScope()) + { + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Import Groups")) + { + ImportGroup(groupPath, schemaFolder); + groupName = Path.GetFileNameWithoutExtension(groupPath); + } + } + + GUILayout.Space(10f); + groupName = EditorGUILayout.TextField(new GUIContent("Group Name", "The name of the group that the schemas will be added to. This should be the filename of the imported group, for example 'MyGroup'."), groupName); + schemaFolder = EditorGUILayout.TextField(new GUIContent("Schema Folder", "The folder containing the schema assets of the group to import, for example 'Packages/com.unity.example/Schemas'."), schemaFolder); + using (new GUILayout.HorizontalScope()) + { + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Import Schemas")) + ImportSchemas(groupName, schemaFolder); + } + } + + /// + /// Adds an existing group to the default Settings. This will copy the group to the default location, typically in the Assets/AddressableAssetsData folder. + /// + /// The path of the group. + /// The folder containing only the group's schema assets. + public void ImportGroup(string groupPath, string schemaFolder) + { + AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings; + if (settings == null) + { + EditorUtility.DisplayDialog("Error", "Cannot import group. No Addressables Settings file exists. \n\nOpen 'Window/Asset Management/Addressables/Groups' for more info.", "Ok"); + return; + } + ImportGroupInternal(settings, groupPath); + } + + /// + /// Adds existing schemas to a group. This will copy the schemas to the default location, typically in the Assets/AddressableAssetsData folder. + /// + /// The name of the group. + /// The folder containing the schema assets. + public void ImportSchemas(string groupName, string schemaFolder) + { + AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings; + if (settings == null) + { + EditorUtility.DisplayDialog("Error", "Cannot import schemas. No Addressables Settings file exists. \n\nOpen 'Window/Asset Management/Addressables/Groups' for more info.", "Ok"); + return; + } + ImportSchemasInternal(settings, groupName, schemaFolder); + } + + void ImportGroupInternal(AddressableAssetSettings settings, string groupPath) + { + if (string.IsNullOrEmpty(groupPath) || Path.GetExtension(groupPath).ToLower() != ".asset" || !File.Exists(groupPath)) + { + Debug.LogError($"Group at '{groupPath}' not a valid group asset. Group will not be imported."); + return; + } + AddressableAssetGroup oldGroup = AssetDatabase.LoadAssetAtPath(groupPath); + if (oldGroup == null) + { + Debug.LogError($"Cannot load group asset at '{groupPath}'. Group will not be imported."); + return; + } + if (settings.FindGroup(oldGroup.Name) != null) + { + Debug.LogError($"Settings already contains group '{oldGroup.Name}'. Group will not be imported."); + return; + } + + string groupFileName = Path.GetFileName(groupPath); + string newGroupPath = $"{settings.GroupFolder}/{groupFileName}"; + newGroupPath = newGroupPath.Replace("\\", "/"); + if (File.Exists(newGroupPath)) + { + Debug.LogError($"File already exists at '{newGroupPath}'. Group will not be imported."); + return; + } + if (!AssetDatabase.CopyAsset(groupPath, newGroupPath)) + Debug.LogError("Failed to copy group asset. Importing group failed."); + } + + void ImportSchemasInternal(AddressableAssetSettings settings, string groupName, string schemaFolder) + { + if (string.IsNullOrEmpty(schemaFolder) || !Directory.Exists(schemaFolder)) + { + Debug.LogError($"Schema folder path is not a valid folder '{schemaFolder}'. Schemas will not be imported."); + return; + } + AddressableAssetGroup group = settings.FindGroup(groupName); + if (group == null) + { + Debug.LogError($"Settings does not contain group '{groupName}'. Schemas will not be imported."); + return; + } + + string[] schemaPaths = Directory.GetFiles(schemaFolder); + foreach (string unparsedPath in schemaPaths) + { + if (Path.GetExtension(unparsedPath).ToLower() != ".asset") + continue; + + string path = unparsedPath.Replace("\\", "/"); + AddressableAssetGroupSchema schema = AssetDatabase.LoadAssetAtPath(path); + if (schema == null) + { + Debug.LogError($"Cannot load schema asset at '{path}'. Schema will not be imported."); + continue; + } + if (schema is BundledAssetGroupSchema bundledSchema) + { + bundledSchema.BuildPath.SetVariableByName(settings, AddressableAssetSettings.kLocalBuildPath); + bundledSchema.LoadPath.SetVariableByName(settings, AddressableAssetSettings.kLocalLoadPath); + } + group.AddSchema(schema); + } + } +} +#endif diff --git a/Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs.meta b/Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs.meta new file mode 100644 index 00000000..d4a94910 --- /dev/null +++ b/Samples~/ImportExistingGroup/Editor/ImportExistingGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94d2183767d45b94e8fc22db85592b2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples~/Unity.Addressables.SamplesFolder.asmdef b/Samples~/Unity.Addressables.SamplesFolder.asmdef index c1e4adbb..72dcb0c6 100644 --- a/Samples~/Unity.Addressables.SamplesFolder.asmdef +++ b/Samples~/Unity.Addressables.SamplesFolder.asmdef @@ -1,15 +1,18 @@ { "name": "Unity.Addressables.SamplesFolder", + "rootNamespace": "", "references": [ "Unity.Addressables", - "Unity.ResourceManager" + "Unity.ResourceManager", + "Unity.Addressables.Editor" ], - "optionalUnityReferences": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [] + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/Tests/Editor/AddressableAssetEntryTests.cs b/Tests/Editor/AddressableAssetEntryTests.cs index 9bd07d89..68039544 100644 --- a/Tests/Editor/AddressableAssetEntryTests.cs +++ b/Tests/Editor/AddressableAssetEntryTests.cs @@ -783,6 +783,28 @@ public void WhenAddressHasSquareBracketsAndGuidIsEmptyString_SettingTheAddressOn Assert.DoesNotThrow(() => entry.SetAddress("[Entry]")); } + [Test] + public void WhenTargetAssetNotFoundInAssetDatabase_ReloadObject() + { + var obj = UnityEngine.AddressableAssets.Tests.TestObject.Create("test_targetAsset"); + var obj2 = UnityEngine.AddressableAssets.Tests.TestObject.Create("test_targetAsset2"); + string path = GetAssetPath("test_targetAsset.asset"); + + AssetDatabase.CreateAsset(obj, path); + AssetDatabase.SaveAssets(); + var entry = new AddressableAssetEntry(AssetDatabase.AssetPathToGUID(path), "Entry", null, false); + UnityEngine.Object targetAsset = entry.TargetAsset; + + AssetDatabase.CreateAsset(obj2, path); + AssetDatabase.SaveAssets(); + + Assert.IsFalse(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(targetAsset, out string guid, out long localId)); + UnityEngine.Object targetAsset2 = entry.TargetAsset; + Assert.IsTrue(AssetDatabase.TryGetGUIDAndLocalFileIdentifier(targetAsset2, out string guid2, out long localId2)); + + AssetDatabase.DeleteAsset(path); + } + string CreateSpriteAtlasWithSprite() { // create a Sprite atlas, + sprite diff --git a/Tests/Editor/AddressableAssetSettingsLocatorTests.cs b/Tests/Editor/AddressableAssetSettingsLocatorTests.cs index 71d9f665..806d7fd9 100644 --- a/Tests/Editor/AddressableAssetSettingsLocatorTests.cs +++ b/Tests/Editor/AddressableAssetSettingsLocatorTests.cs @@ -100,13 +100,23 @@ void AssertLocateResult(IResourceLocator locator, object key, Type type, params } [Test] - public void WhenLocatorWithSingleAsset_LocateWithAddressReturnsSingleLocation() + public void WhenLocatorWithSingleAsset_LocateWithAddress_ReturnsSingleLocation() { var path = GetPath("asset1.asset"); m_Settings.CreateOrMoveEntry(CreateAsset("asset1", path), m_Settings.DefaultGroup).address = "address1"; AssertLocateResult(new AddressableAssetSettingsLocator(m_Settings), "address1", null, path); } + [Test] + public void WhenLocatorWithSingleAsset_LocateWithSameAddressAsLabel_ReturnsSingleLocation() + { + var path = GetPath("asset1.asset"); + var e = m_Settings.CreateOrMoveEntry(CreateAsset("asset1", path), m_Settings.DefaultGroup); + e.address = "address1"; + e.SetLabel("address1", true, true); + AssertLocateResult(new AddressableAssetSettingsLocator(m_Settings), "address1", null, path); + } + [Test] public void WhenLocatorWithSingleAsset_LocateWithInvalidKeyReturnsFalse() { diff --git a/Tests/Editor/AddressableAssetSettingsTests.cs b/Tests/Editor/AddressableAssetSettingsTests.cs index 0485218e..3c703e23 100644 --- a/Tests/Editor/AddressableAssetSettingsTests.cs +++ b/Tests/Editor/AddressableAssetSettingsTests.cs @@ -106,6 +106,65 @@ public void AddRemovelabel() Assert.False(Settings.labelTable.labelNames.Contains(labelName)); } + [Test] + public void RenameLabel_KeepsIndexTheSame_ForNewTableEntry() + { + string dummyLabel1, dummyLabel2, dummyLabel3; + dummyLabel3 = dummyLabel2 = dummyLabel1 = "dummylabel"; + string replaceMe = "replaceme"; + string useMeToReplace = "usemetoreplace"; + Settings.AddLabel(dummyLabel1); + Settings.AddLabel(dummyLabel2); + Settings.AddLabel(replaceMe); + Settings.AddLabel(dummyLabel3); + + int startIndex = Settings.labelTable.GetIndexOfLabel(replaceMe); + + Settings.RenameLabel(replaceMe, useMeToReplace); + + int endIndex = Settings.labelTable.GetIndexOfLabel(useMeToReplace); + + Assert.AreEqual(startIndex, endIndex); + + Settings.RemoveLabel(dummyLabel1); + Settings.RemoveLabel(dummyLabel2); + Settings.RemoveLabel(dummyLabel3); + Settings.RemoveLabel(useMeToReplace); + } + + [Test] + public void RenameLabel_UpdatesLabelList_WithCorrectLabels() + { + string replaceMe = "replaceme"; + string useMeToReplace = "usemetoreplace"; + Settings.AddLabel(replaceMe); + + Settings.RenameLabel(replaceMe, useMeToReplace); + + Assert.IsFalse(Settings.GetLabels().Contains(replaceMe)); + Assert.IsTrue(Settings.GetLabels().Contains(useMeToReplace)); + + Settings.RemoveLabel(useMeToReplace); + } + + [Test] + public void RenameLabel_UpdatesAssetEntries_ThatContainUsesOfTheOldLabels() + { + string replaceMe = "replaceme"; + string useMeToReplace = "usemetoreplace"; + Settings.AddLabel(replaceMe); + var assetEntry = Settings.CreateOrMoveEntry(m_AssetGUID, Settings.DefaultGroup); + assetEntry.SetLabel(replaceMe, true); + + Settings.RenameLabel(replaceMe, useMeToReplace); + + Assert.IsTrue(assetEntry.labels.Contains(useMeToReplace)); + Assert.IsFalse(assetEntry.labels.Contains(replaceMe)); + + Settings.RemoveAssetEntry(assetEntry); + Settings.RemoveLabel(useMeToReplace); + } + [Test] public void GetLabels_ShouldReturnCopy() { diff --git a/Tests/Editor/AddressableAssetUtilityTests.cs b/Tests/Editor/AddressableAssetUtilityTests.cs index 220b7666..513b31f0 100644 --- a/Tests/Editor/AddressableAssetUtilityTests.cs +++ b/Tests/Editor/AddressableAssetUtilityTests.cs @@ -220,46 +220,6 @@ public void AreConvertableEditorAssemblyTypesConverted() } } - [TestCase(1, TestName = "OneBundle")] - [TestCase(5, TestName = "MultipleBundles")] - [Test] - public void AddressableAssetUtility_ConvertAssetBundlesToAddressables_CanConvertBundles(int numBundles) - { - // Setup - var prevGroupCount = Settings.groups.Count; - var testAssetGUIDs = new List(); - for (int i = 0; i < numBundles; i++) - { - var testObject = new GameObject("TestObjectForBundles" + i); -#if UNITY_2018_3_OR_NEWER - PrefabUtility.SaveAsPrefabAsset(testObject, ConfigFolder + "/testasset" + i + ".prefab"); -#else - PrefabUtility.CreatePrefab(k_TestConfigFolder + "/testasset" + i + ".prefab", testObject); -#endif - testAssetGUIDs.Add(AssetDatabase.AssetPathToGUID(ConfigFolder + "/testasset" + i + ".prefab")); - var importer = AssetImporter.GetAtPath(AssetDatabase.GUIDToAssetPath(testAssetGUIDs[i])); - importer.assetBundleName = "testAssetBundleName" + i; - AssetDatabase.SaveAssets(); - } - AddressableAssetSettingsDefaultObject.Settings = Settings; - - // Test - AddressableAssetUtility.ConvertAssetBundlesToAddressables(); - Assert.AreEqual(prevGroupCount + numBundles, Settings.groups.Count); - Assert.AreEqual(0, AssetDatabase.GetAllAssetBundleNames().Length); - for (int i = 0; i < numBundles; i++) - { - Assert.NotNull(Settings.FindAssetEntry(testAssetGUIDs[i])); - } - - // Cleanup - for (int i = 0; i < numBundles; i++) - { - var lastGroupIndex = AddressableAssetSettingsDefaultObject.Settings.groups.Count - 1; - AddressableAssetSettingsDefaultObject.Settings.RemoveGroup(AddressableAssetSettingsDefaultObject.Settings.groups[lastGroupIndex]); - } - } - [Test] public void SafeMoveResourcesToGroup_ResourcesMovedToNewFolderAndGroup() { diff --git a/Tests/Editor/AddressableAssetsBundleBuildParametersTests.cs b/Tests/Editor/AddressableAssetsBundleBuildParametersTests.cs index 267ab295..a22012a8 100644 --- a/Tests/Editor/AddressableAssetsBundleBuildParametersTests.cs +++ b/Tests/Editor/AddressableAssetsBundleBuildParametersTests.cs @@ -4,10 +4,10 @@ using NUnit.Framework; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; -using UnityEngine; using UnityEngine.TestTools; using UnityEditor.AddressableAssets.Build.DataBuilders; - +using UnityEditor.Build.Content; +using BuildCompression = UnityEngine.BuildCompression; namespace UnityEditor.AddressableAssets.Tests { @@ -22,7 +22,7 @@ static IEnumerable GetValues(Type t) enumerations.Add((Enum)fieldInfo.GetValue(null)); return enumerations; } - + [Test] public void WhenNonRecursiveBuildingSet_BuildParametersHaveCorrectValue() { @@ -30,11 +30,11 @@ public void WhenNonRecursiveBuildingSet_BuildParametersHaveCorrectValue() Assert.Ignore($"Skipping test {nameof(WhenNonRecursiveBuildingSet_BuildParametersHaveCorrectValue)}."); #else var bundleToAssetGroup = new Dictionary(); - + Settings.NonRecursiveBuilding = true; var testParams = new AddressableAssetsBundleBuildParameters(Settings, bundleToAssetGroup, BuildTarget.StandaloneWindows64, BuildTargetGroup.Standalone, "Unused"); Assert.AreEqual(testParams.NonRecursiveDependencies, Settings.NonRecursiveBuilding); - + Settings.NonRecursiveBuilding = false; testParams = new AddressableAssetsBundleBuildParameters(Settings, bundleToAssetGroup, BuildTarget.StandaloneWindows64, BuildTargetGroup.Standalone, "Unused"); Assert.AreEqual(testParams.NonRecursiveDependencies, Settings.NonRecursiveBuilding); @@ -66,5 +66,41 @@ public void WhenCompressionSetForGroups_GetCompressionForIdentifier_ReturnsExpec Assert.AreEqual(expectedValues[i].level, comp.level); } } + +#if UNITY_2019_4_OR_NEWER + [Test] + [TestCase(true)] + [TestCase(false)] + public void StripUnityVersion_SetsBuildFlagCorrectly(bool stripUnityVersion) + { + bool oldValue = Settings.StripUnityVersionFromBundleBuild; + Settings.StripUnityVersionFromBundleBuild = stripUnityVersion; + + var testParams = new AddressableAssetsBundleBuildParameters(Settings, new Dictionary(), + BuildTarget.StandaloneWindows64, BuildTargetGroup.Standalone, "Unused"); + + var buildSettings = testParams.GetContentBuildSettings(); + + Assert.AreEqual(stripUnityVersion, (buildSettings.buildFlags & ContentBuildFlags.StripUnityVersion) != 0); + + Settings.StripUnityVersionFromBundleBuild = oldValue; + } +#endif + + [Test] + [TestCase(true)] + [TestCase(false)] + public void WhenDisableVisibleSubAssetRepresentationsSet_BuildParametersHaveCorrectValue(bool disableVisibleSubAssetRepresentations) + { + bool oldValue = Settings.DisableVisibleSubAssetRepresentations; + Settings.DisableVisibleSubAssetRepresentations = disableVisibleSubAssetRepresentations; + + var testParams = new AddressableAssetsBundleBuildParameters(Settings, new Dictionary(), + BuildTarget.StandaloneWindows64, BuildTargetGroup.Standalone, "Unused"); + + Assert.AreEqual(disableVisibleSubAssetRepresentations, testParams.DisableVisibleSubAssetRepresentations); + + Settings.DisableVisibleSubAssetRepresentations = oldValue; + } } } diff --git a/Tests/Editor/AddressableAssetsWindowTests.cs b/Tests/Editor/AddressableAssetsWindowTests.cs index c9c77c42..e0d4927c 100644 --- a/Tests/Editor/AddressableAssetsWindowTests.cs +++ b/Tests/Editor/AddressableAssetsWindowTests.cs @@ -1,20 +1,55 @@ using NUnit.Framework; +using System.Collections.Generic; using UnityEditor.AddressableAssets.GUI; - +using UnityEditor.AddressableAssets.Settings; +using UnityEngine; namespace UnityEditor.AddressableAssets.Tests { public class AddressableAssetWindowTests : AddressableAssetTestBase { - private AddressableAssetsWindow m_AddressableAssetsWindow; - [Test] public void AddressableAssetWindow_OfferToConvert_CantConvertWithNoBundles() { - m_AddressableAssetsWindow = new AddressableAssetsWindow(); + AddressableAssetsWindow aaWindow = ScriptableObject.CreateInstance(); var prevGroupCount = Settings.groups.Count; - m_AddressableAssetsWindow.OfferToConvert(Settings); + aaWindow.OfferToConvert(Settings); Assert.AreEqual(prevGroupCount, Settings.groups.Count); + Object.DestroyImmediate(aaWindow); + } + + [Test] + public void AddressableAssetWindow_CanSelectGroupTreeViewByAddressableAssetEntries() + { + //Setup + var defaultGroup = Settings.DefaultGroup; + Assert.IsNotNull(defaultGroup, "Default Group is not found"); + string p1 = AssetDatabase.AssetPathToGUID(GetAssetPath("test 1.prefab")); + Assert.IsFalse(string.IsNullOrEmpty(p1), "Could not setup for Asset \"test 1.prefab\""); + string p2 = AssetDatabase.AssetPathToGUID(GetAssetPath("test 2.prefab")); + Assert.IsFalse(string.IsNullOrEmpty(p2), "Could not setup for Asset \"test 2.prefab\""); + var e1 = Settings.CreateOrMoveEntry(p1, defaultGroup); + var e2 = Settings.CreateOrMoveEntry(p2, defaultGroup); + + AddressableAssetsWindow aaWindow = ScriptableObject.CreateInstance(); + aaWindow.m_GroupEditor = new AddressableAssetsSettingsGroupEditor(aaWindow); + aaWindow.m_GroupEditor.OnDisable(); + aaWindow.m_GroupEditor.settings = Settings; + var entryTree = aaWindow.m_GroupEditor.InitialiseEntryTree(); + + //Test + Assert.AreEqual(0, entryTree.GetSelection().Count, "entryTree is not expected to have anything select at creation"); + aaWindow.SelectAssetsInGroupEditor(new List(){e1}); + Assert.AreEqual(1, entryTree.GetSelection().Count, "Expecting to have \"test 1.prefab\" selected."); + aaWindow.SelectAssetsInGroupEditor(new List(){e2}); + Assert.AreEqual(1, entryTree.GetSelection().Count, "Expecting to have \"test 2.prefab\" selected."); + aaWindow.SelectAssetsInGroupEditor(new List(){e1, e2}); + Assert.AreEqual(2, entryTree.GetSelection().Count, "Expecting to have \"test 1.prefab\" and \"test 2.prefab\" selected."); + + //Cleanup + Assert.IsTrue(Settings.RemoveAssetEntry(e1, false), "Failed to cleanup AssetEntry \"test 1.prefab\" from test settings."); + Assert.IsTrue(Settings.RemoveAssetEntry(e2, false), "Failed to cleanup AssetEntry \"test 2.prefab\" from test settings."); + Object.DestroyImmediate(aaWindow); } } } diff --git a/Tests/Editor/AssetReferenceDrawerTests.cs b/Tests/Editor/AssetReferenceDrawerTests.cs deleted file mode 100644 index f42b3d3d..00000000 --- a/Tests/Editor/AssetReferenceDrawerTests.cs +++ /dev/null @@ -1,1189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using NUnit.Framework; -using UnityEngine; -using UnityEditor.AddressableAssets.GUI; -using UnityEditor.AddressableAssets.Settings; -using UnityEditor.IMGUI.Controls; -using UnityEditor.U2D; -using UnityEditor.VersionControl; -using UnityEngine.AddressableAssets; -using UnityEngine.U2D; -using Object = UnityEngine.Object; - -namespace UnityEditor.AddressableAssets.Tests -{ - [TestFixture] - public class AssetReferenceDrawerTestsFixture : AddressableAssetTestBase - { - protected string m_fbxAssetPath; - - [OneTimeTearDown] - public void OneTimeTearDown() - { - if (File.Exists(m_fbxAssetPath)) - { - AssetDatabase.DeleteAsset(m_fbxAssetPath); - AssetDatabase.Refresh(); - } - } - - internal AssetReferenceDrawer m_AssetReferenceDrawer { get; set; } - internal List _subAssets; - internal SerializedProperty _property; - internal SpriteAtlas _atlas; - - internal class TestObjectWithRef : TestObject - { - [SerializeField] - public AssetReference Reference = new AssetReference(); - } - internal class TestObjectWithRestrictedRef : TestObject - { - [SerializeField] - [AssetReferenceUILabelRestriction(new[] { "HD" })] - private AssetReference Reference = new AssetReference(); - } - - internal class TestObjectWithRestrictedRefByMultipleLabels : TestObject - { - [SerializeField] - [AssetReferenceUILabelRestriction(new[] { "HDR", "test", "default" })] - private AssetReference ReferenceMultiple = new AssetReference(); - } - - internal class TestObjectWithRestrictedRefInNestedClass : TestObject - { - [SerializeField] - NestedClass OneLevelNested = new NestedClass(); - - [SerializeField] - TwoLevelNestedClass TwoLevelNested = new TwoLevelNestedClass(); - - [Serializable] - class TwoLevelNestedClass - { - [SerializeField] - NestedClass Nested = new NestedClass(); - } - - [Serializable] - class NestedClass - { - [SerializeField] - [AssetReferenceUILabelRestriction(new[] { "HD" })] - AssetReference ReferenceInNestedClass = new AssetReference(); - } - } - - internal class TestSubObjectsSpriteAtlas : TestObject - { - [SerializeField] - public AssetReferenceSprite testSpriteReference; - } - - internal class TestSubObjectsSpriteAtlasList : TestObject - { - [SerializeField] - public AssetReferenceSprite[] testSpriteReference; - } - - internal class TestAssetReferenceDrawer : AssetReferenceDrawer - { - TestAssetReferencePopup _popup; - internal void SetAssetReference(AssetReference ar) - { - m_AssetRefObject = ar; - } - - internal void TreeSetup(TreeViewState treeState) - { - _popup = new TestAssetReferencePopup(this, "testpopup", ""); - _popup.TreeSetup(treeState, this, _popup); - } - - internal void TreeSelectionChangedHelper(IList selectedIds) - { - _popup.TreeSelectionChangedHelper(selectedIds); - } - - class TestAssetReferencePopup : AssetReferencePopup - { - TestSelectionTree _testTree; - internal TestAssetReferencePopup(AssetReferenceDrawer drawer, string guid, string nonAddressedAsset) - : base(drawer, guid, nonAddressedAsset) {} - - internal void TreeSetup(TreeViewState treeState, AssetReferenceDrawer testARD, AssetReferencePopup popup) - { - _testTree = new TestSelectionTree(treeState, testARD, popup, "testtree", ""); - _testTree.Reload(); - } - - internal void TreeSelectionChangedHelper(IList selectedIds) - { - _testTree.SelectionChangedHelper(selectedIds); - } - - class TestSelectionTree : AssetReferencePopup.AssetReferenceTreeView - { - internal TestSelectionTree(TreeViewState state, AssetReferenceDrawer drawer, - AssetReferencePopup popup, string guid, string nonAddressedAsset) - : base(state, drawer, popup, guid, nonAddressedAsset) {} - - internal void SelectionChangedHelper(IList selectedIds) - { - SelectionChanged(selectedIds); - } - } - } - } - - internal class TestAssetReference : AssetReference - { - internal TestAssetReference(string guid) : base(guid) {} - - internal Object CachedAssetProperty - { - get - { - return CachedAsset; - } - set - { - CachedAsset = value; - } - } - } - - internal void SetupDefaultSettings() - { - if (!Directory.Exists("Assets/AddressableAssetsData")) - Directory.CreateDirectory("Assets/AddressableAssetsData"); - AddressableAssetSettingsDefaultObject.Settings = Settings; - } - - internal void TearDownTestDir() - { - if (Directory.Exists(ConfigFolder + "/test")) - AssetDatabase.DeleteAsset(ConfigFolder + "/test"); - } - - internal Object SetupAssetReference(out string assetPath) - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - AssetReference ar = new AssetReference(); - assetPath = AssetDatabase.GUIDToAssetPath(m_AssetGUID); - m_AssetReferenceDrawer.m_AssetRefObject = ar; - m_AssetReferenceDrawer.m_AssetRefObject.SubObjectName = "test"; - var testObject = AssetDatabase.LoadMainAssetAtPath(assetPath); - return testObject; - } - - internal SerializedProperty SetupForSetObjectNullTests(out string newEntryGuid) - { - // Setup Original AssetReference to not be null - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - var assetPath = ConfigFolder + "/test" + "/test.prefab"; - CreateTestPrefabAddressable(assetPath); - newEntryGuid = AssetDatabase.AssetPathToGUID(assetPath); - AssetReference ar = new AssetReference(newEntryGuid); - SetupDefaultSettings(); - - // Setup property - TestObjectWithRef obj = ScriptableObject.CreateInstance(); - Settings.CreateOrMoveEntry(newEntryGuid, Settings.groups[0]); - obj.Reference = ar; - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - m_AssetReferenceDrawer.m_AssetRefObject = ar; - AssetReferenceDrawerUtilities.GatherFilters(property); - string sprGuid; - FieldInfo propertyFieldInfo = typeof(TestObjectWithRef).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, property, obj, propertyFieldInfo, "", out sprGuid); - - return property; - } - - internal SerializedProperty SetupForSetSubAssets(SpriteAtlas spriteAtlas, int numReferences, bool setReferences = true, int numToSet = -1) - { - // Setup AssetReference selected - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - var assetPath = AssetDatabase.GetAssetOrScenePath(spriteAtlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - AssetReferenceSprite ar = new AssetReferenceSprite(atlasGuid); - - // Setup property - if (numToSet == -1) - numToSet = numReferences - 1; - var targetObjects = new Object[numReferences]; - for (int i = 0; i < numReferences; i++) - { - var testScriptable = TestSubObjectsSpriteAtlas.CreateInstance(); - - // Preset references for certain tests - if (setReferences && i <= numToSet) - testScriptable.testSpriteReference = ar; - targetObjects[i] = testScriptable; - } - var so = new SerializedObject(targetObjects); - var property = so.FindProperty("testSpriteReference"); - m_AssetReferenceDrawer.m_AssetRefObject = ar; - AssetReferenceDrawerUtilities.GatherFilters(property); - SetupDefaultSettings(); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - return property; - } - - internal SerializedProperty SetupForSetSubAssetsList(SpriteAtlas spriteAtlas, - int numReferences, - int selectedElement, - int numElements = 1, - bool setReferences = true, - int numToSet = -1) - { - // Setup AssetReference selected - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - var assetPath = AssetDatabase.GetAssetOrScenePath(spriteAtlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - AssetReferenceSprite sar = new AssetReferenceSprite(atlasGuid); - - // Default to preset all references - if (setReferences && numToSet == -1) - numToSet = numReferences - 1; - - // Select one element per reference array as a targetObject - var targetObjects = new Object[numElements]; - for (int refIdx = 0; refIdx < numReferences; refIdx++) - { - var testScriptable = TestSubObjectsSpriteAtlasList.CreateInstance(); - testScriptable.testSpriteReference = new AssetReferenceSprite[numElements]; - - // Preset reference array elements for certain tests - if (setReferences && refIdx <= numToSet) - { - for (int i = 0; i < numElements; i++) - { - AssetReferenceSprite ar = new AssetReferenceSprite(atlasGuid); - testScriptable.testSpriteReference[i] = ar; - testScriptable.testSpriteReference[i].SubObjectName = "test"; - } - } - - var serialObj = new SerializedObject(testScriptable); - var serProp = serialObj.FindProperty("testSpriteReference.Array.data[" + selectedElement + "]"); - targetObjects[refIdx] = serProp.serializedObject.targetObject; - } - - // Get main property to pass to AssetReferenceDrawer - var property = SetupPropertyDrawerSetListTests(targetObjects, sar, selectedElement); - - return property; - } - - internal SerializedProperty SetupPropertyDrawerSetListTests(Object[] targetObjects, AssetReference ar, int selectedElement) - { - var so = new SerializedObject(targetObjects); - var property = so.FindProperty("testSpriteReference.Array.data[" + selectedElement + "]"); - m_AssetReferenceDrawer.m_AssetRefObject = ar; - AssetReferenceDrawerUtilities.GatherFilters(property); - SetupDefaultSettings(); - return property; - } - - internal string CreateTestPrefabAddressable(string newEntryPath, bool createEntry = true) - { - GameObject testObject = new GameObject("TestObject"); - Directory.CreateDirectory(ConfigFolder + "/test"); - PrefabUtility.SaveAsPrefabAsset(testObject, newEntryPath); - var folderGuid = AssetDatabase.AssetPathToGUID(ConfigFolder + "/test"); - if (createEntry) - { - Settings.CreateOrMoveEntry(folderGuid, Settings.groups[0]); - } - - return folderGuid; - } - - internal Object SetUpSingleSprite(out string atlasGuid) - { - // Setup Sprite data - var texture = new Texture2D(32, 32); - var data = ImageConversion.EncodeToPNG(texture); - UnityEngine.Object.DestroyImmediate(texture); - - // Setup Sprite - Directory.CreateDirectory(ConfigFolder + "/test"); - var spritePath = ConfigFolder + "/test" + "/testSprite.png"; - var atlasPath = ConfigFolder + "/test" + "/testAtlas.spriteatlas"; - var newAtlas = new SpriteAtlas(); - AssetDatabase.GenerateUniqueAssetPath(ConfigFolder); - File.WriteAllBytes(spritePath, data); - - AssetDatabase.ImportAsset(spritePath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate); - var importer = (TextureImporter)AssetImporter.GetAtPath(spritePath); - importer.textureType = TextureImporterType.Sprite; - importer.spriteImportMode = SpriteImportMode.Multiple; - importer.spritesheet = new SpriteMetaData[] { new SpriteMetaData() { name = "topleft", pivot = Vector2.zero, rect = new Rect(0, 0, 16, 16) }, - new SpriteMetaData() { name = "testSprite", pivot = Vector2.zero, rect = new Rect(16, 16, 16, 16) }}; - importer.SaveAndReimport(); - - // Add sprite to subassets - Object spr = AssetDatabase.LoadAssetAtPath(spritePath, typeof(Sprite)); - spr.name = "testSprite"; - - // Setup Atlas - var spriteList = new[] {spr}; - newAtlas.Add(spriteList); - AssetDatabase.CreateAsset(newAtlas, atlasPath); - AssetDatabase.Refresh(); - SpriteAtlasExtensions.Add(newAtlas, spriteList); - SpriteAtlasUtility.PackAtlases(new SpriteAtlas[] { newAtlas }, EditorUserBuildSettings.activeBuildTarget, false); - - var spriteGuid = AssetDatabase.AssetPathToGUID(spritePath); - Settings.CreateOrMoveEntry(spriteGuid, Settings.groups[0]); - atlasGuid = AssetDatabase.AssetPathToGUID(atlasPath); - Settings.CreateOrMoveEntry(atlasGuid, Settings.groups[0]); - - return spr; - } - - - internal SpriteAtlas SetUpSpriteAtlas(int numAtlasObjects, out List subAssets) - { - // Setup Sprite data - var texture = new Texture2D(32, 32); - var data = ImageConversion.EncodeToPNG(texture); - UnityEngine.Object.DestroyImmediate(texture); - - // Setup Sprites - subAssets = new List(); - Directory.CreateDirectory(ConfigFolder + "/test"); - var atlasPath = ConfigFolder + "/test" + "/testAtlas.spriteatlas"; - var newAtlas = new SpriteAtlas(); - var sprites = new Object[numAtlasObjects]; - for (int i = 0; i < numAtlasObjects; i++) - { - // Create Sprite asset - AssetDatabase.GenerateUniqueAssetPath(ConfigFolder); - var newSpritePath = ConfigFolder + "/test" + "/testSprite" + i + ".png"; - File.WriteAllBytes(newSpritePath, data); - - AssetDatabase.ImportAsset(newSpritePath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate); - var importer = (TextureImporter)AssetImporter.GetAtPath(newSpritePath); - importer.textureType = TextureImporterType.Sprite; - importer.spriteImportMode = SpriteImportMode.Multiple; - importer.spritesheet = new SpriteMetaData[] { new SpriteMetaData() { name = "topleft", pivot = Vector2.zero, rect = new Rect(0, 0, 16, 16) }, - new SpriteMetaData() { name = "testSprite" + i, pivot = Vector2.zero, rect = new Rect(16, 16, 16, 16) }}; - importer.SaveAndReimport(); - - // Add sprite to subassets - Object spr = AssetDatabase.LoadAssetAtPath(newSpritePath, typeof(Sprite)); - spr.name = "testSprite" + i; - sprites[i] = spr; - subAssets.Add(spr); - } - // Setup Atlas - newAtlas.Add(sprites); - AssetDatabase.CreateAsset(newAtlas, atlasPath); - AssetDatabase.Refresh(); - SpriteAtlasExtensions.Add(newAtlas, sprites); - SpriteAtlasUtility.PackAtlases(new SpriteAtlas[] { newAtlas }, EditorUserBuildSettings.activeBuildTarget, false); - - var atlasGuid = AssetDatabase.AssetPathToGUID(atlasPath); - Settings.CreateOrMoveEntry(atlasGuid, Settings.groups[0]); - - return newAtlas; - } - - public void SetUpForSubassetPerformanceTests(int numAtlasObjects, int numReferences, int selectedId) - { - // Drawer Setup - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - _subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out _subAssets); - _property = SetupForSetSubAssets(atlas, numReferences, true); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.SetSubAssets(_property, _subAssets[selectedId], propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - AssetReferenceDrawerUtilities.GatherFilters(_property); - } - - public void SetupForSetAssetsPerformanceTests(int numReferences) - { - _subAssets = new List(); - _atlas = SetUpSpriteAtlas(numReferences, out _subAssets); - _property = SetupForSetSubAssets(_atlas, numReferences); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - } - - public void SetObjectForPerformanceTests() - { - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, _property, _atlas, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text, out string guid); - } - - public void SetMainAssetsForPerformanceTests() - { - _subAssets = new List(); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.SetMainAssets(ref m_AssetReferenceDrawer.m_ReferencesSame, _property, _atlas, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - } - - public void GetNameForAssetForPerformanceTests() - { - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.GetNameForAsset(ref m_AssetReferenceDrawer.m_ReferencesSame, _property, false, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - } - - public void GetSubAssetsListForPerformanceTests() - { - AssetReferenceDrawerUtilities.GetSubAssetsList(m_AssetReferenceDrawer.m_AssetRefObject); - } - - public void GetSelectedSubassetIndexForPerformanceTests() - { - m_AssetReferenceDrawer.GetSelectedSubassetIndex(_subAssets, out var selIndex, out var objNames); - } - - public void CheckTargetObjectsSubassetsAreDifferentForPerformanceTests() - { - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.CheckTargetObjectsSubassetsAreDifferent(_property, m_AssetReferenceDrawer.m_AssetRefObject.SubObjectName, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - } - } - - public class AssetReferenceDrawerTests : AssetReferenceDrawerTestsFixture - { - [Test] - public void CanRestrictLabel() - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - TestObjectWithRestrictedRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - List restrictions = AssetReferenceDrawerUtilities.GatherFilters(property); - Assert.AreEqual(restrictions.Count, 1); - Assert.True(restrictions.First().ToString().Contains("HD")); - } - - [Test] - public void CanRestrictMultipleLabels() - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - TestObjectWithRestrictedRefByMultipleLabels obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("ReferenceMultiple"); - AssetReferenceDrawerUtilities.GatherFilters(property); - List restrictions = AssetReferenceDrawerUtilities.GatherFilters(property); - string restriction = restrictions.First().ToString(); - Assert.True(restriction.Contains("HDR")); - Assert.True(restriction.Contains("test")); - Assert.True(restriction.Contains("default")); - } - - [Test] - public void AssetReferenceDrawer_GatherFilters_CanRestrictInSingleNestedClass() - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - TestObjectWithRestrictedRefInNestedClass obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var oneLevelProp = so.FindProperty("OneLevelNested.ReferenceInNestedClass"); - var restrictions = AssetReferenceDrawerUtilities.GatherFilters(oneLevelProp); - Assert.True(restrictions.First().ToString().Contains("HD")); - } - - [Test] - public void AssetReferenceDrawer_GatherFilters_CanRestrictInDoubleNestedClass() - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - TestObjectWithRestrictedRefInNestedClass obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var twoLevelProp = so.FindProperty("TwoLevelNested.Nested.ReferenceInNestedClass"); - var restrictions = AssetReferenceDrawerUtilities.GatherFilters(twoLevelProp); - Assert.True(restrictions.First().ToString().Contains("HD")); - } - - [Test] - public void AssetReferenceDrawer_ValidateAsset_CanValidateAssetWithRestrictionsFromPath() - { - // Setup AssetReference - string assetPath = ""; - var testObject = SetupAssetReference(out assetPath); - - // Setup property - TestObjectWithRestrictedRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - var restrictions = AssetReferenceDrawerUtilities.GatherFilters(property); - - // Test - string guid; - - FieldInfo propertyFieldInfo = typeof(TestObjectWithRestrictedRef).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - Assert.IsTrue(AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, property, testObject, propertyFieldInfo, "", out guid)); - Assert.False(AssetReferenceDrawerUtilities.ValidateAsset(m_AssetReferenceDrawer.m_AssetRefObject, restrictions, assetPath)); - } - - [Test] - public void AssetReferenceDrawer_ValidateAsset_CanValidateAssetWithoutRestrictionsFromPath() - { - // Setup AssetReference - string assetPath = ""; - var testObject = SetupAssetReference(out assetPath); - - // Setup property - TestObjectWithRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - var restrictions = AssetReferenceDrawerUtilities.GatherFilters(property); - - // Test - string guid; - - FieldInfo propertyFieldInfo = typeof(TestObjectWithRestrictedRef).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - Assert.IsTrue(AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, property, testObject, propertyFieldInfo, "", out guid)); - Assert.IsTrue(AssetReferenceDrawerUtilities.ValidateAsset(m_AssetReferenceDrawer.m_AssetRefObject, restrictions, assetPath)); - } - - [Test] - public void AssetReferenceDrawer_IsAssetPathInAddressableDirectory_PathInAddressableFolder() - { - // Asset setup - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - string assetName = ""; - var newEntryPath = ConfigFolder + "/test" + "/test.prefab"; - var folderGuid = CreateTestPrefabAddressable(newEntryPath); - - // Test - Assert.IsTrue(Settings.IsAssetPathInAddressableDirectory(newEntryPath, out assetName)); - Assert.AreEqual(assetName, newEntryPath); - - // Cleanup - Settings.RemoveAssetEntry(AssetDatabase.AssetPathToGUID(newEntryPath)); - Settings.RemoveAssetEntry(folderGuid); - TearDownTestDir(); - m_AssetReferenceDrawer = null; - } - - [Test] - public void AssetReferenceDrawer_IsAssetPathInAddressableDirectory_PathNotInAddressableFolder() - { - // Asset setup - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - string assetName = ""; - var newEntryPath = ConfigFolder + "/test" + "/test.prefab"; - var folderGuid = CreateTestPrefabAddressable(newEntryPath, false); - - // Test - Assert.IsFalse(Settings.IsAssetPathInAddressableDirectory(newEntryPath, out assetName)); - Assert.AreEqual(assetName, ""); - - // Cleanup - Settings.RemoveAssetEntry(AssetDatabase.AssetPathToGUID(newEntryPath)); - Settings.RemoveAssetEntry(folderGuid); - TearDownTestDir(); - m_AssetReferenceDrawer = null; - } - - [Test] - public void AssetReferenceDrawer_IsAssetPathInAddressableDirectory_PathEmptyString() - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - string assetName = ""; - - Assert.IsFalse(Settings.IsAssetPathInAddressableDirectory("", out assetName)); - Assert.AreEqual(assetName, ""); - } - - [Test] - public void AssetReferenceDrawer_IsAssetPathInAddressableDirectory_PathPointToNonexistentAsset() - { - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - string assetName = ""; - - Assert.IsFalse(Settings.IsAssetPathInAddressableDirectory(ConfigFolder + "/test.prefab", out assetName)); - Assert.AreEqual(assetName, ""); - } - - [Test] - public void AssetReferenceDrawer_SelectionChanged_CanSelectSameNameAssetsInDifferentGroups() - { - // Drawer Setup - var testAssetReferenceDrawer = new TestAssetReferenceDrawer(); - testAssetReferenceDrawer.SetAssetReference(new AssetReference()); - TestObjectWithRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - testAssetReferenceDrawer.m_Restrictions = AssetReferenceDrawerUtilities.GatherFilters(property); - - // Entries setup - var newEntryPath = ConfigFolder + "/test" + "/test.prefab"; - var testEntry = Settings.CreateOrMoveEntry(m_AssetGUID, Settings.groups[0]); - GameObject testObject = new GameObject("TestObject"); - Directory.CreateDirectory(ConfigFolder + "/test"); - PrefabUtility.SaveAsPrefabAsset(testObject, newEntryPath); - var newEntryGuid = AssetDatabase.AssetPathToGUID(newEntryPath); - var secondTestEntry = Settings.CreateOrMoveEntry(newEntryGuid, Settings.groups[1]); - - // Tree setup - var testId = testEntry.AssetPath.GetHashCode(); - List selectedIds = new List() { testId }; - var treeState = new TreeViewState(); - treeState.selectedIDs = selectedIds; - Directory.CreateDirectory("Assets/AddressableAssetsData"); - AddressableAssetSettingsDefaultObject.Settings = Settings; - testAssetReferenceDrawer.TreeSetup(treeState); - - - // Test - testAssetReferenceDrawer.TreeSelectionChangedHelper(selectedIds); - Assert.AreEqual(m_AssetGUID, testAssetReferenceDrawer.newGuid); - selectedIds[0] = secondTestEntry.AssetPath.GetHashCode(); - testAssetReferenceDrawer.TreeSelectionChangedHelper(selectedIds); - Assert.AreEqual(AssetDatabase.AssetPathToGUID(newEntryPath), testAssetReferenceDrawer.newGuid); - - // Cleanup - if (Directory.Exists("Assets/AddressableAssetsData")) - AssetDatabase.DeleteAsset("Assets/AddressableAssetsData"); - EditorBuildSettings.RemoveConfigObject("Assets/AddressableAssetsData"); - Settings.RemoveAssetEntry(AssetDatabase.AssetPathToGUID(newEntryPath)); - Settings.RemoveAssetEntry(m_AssetGUID); - } - - [Test] - public void AssetReferenceDrawer_HandleDragAndDrop_CanRecognizeNonAddressableInAddressableFolder() - { - // ScriptableObject property and Drawer setup - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - m_AssetReferenceDrawer.m_AssetRefObject = new AssetReference(); - TestObjectWithRestrictedRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var propertyName = "Reference"; - var property = so.FindProperty(propertyName); - m_AssetReferenceDrawer.m_label = new GUIContent(propertyName); - - // Group setup - string groupName = "TestGroup"; - var newGroup = Settings.CreateGroup(groupName, false, false, false, null); - - // Asset setup - var newEntryPath = ConfigFolder + "/test" + "/test.prefab"; - var folderGuid = CreateTestPrefabAddressable(newEntryPath); - var newAssetGuid = AssetDatabase.AssetPathToGUID(newEntryPath); - Settings.CreateOrMoveEntry(folderGuid, Settings.groups[2]); - SetupDefaultSettings(); - - // Test - m_AssetReferenceDrawer.DragAndDropNotFromAddressableGroupWindow(newEntryPath, newAssetGuid, property, Settings); - var newentry = Settings.FindAssetEntry(newAssetGuid); - Assert.IsNull(newentry); - Assert.AreEqual(m_AssetReferenceDrawer.m_AssetRefObject.AssetGUID, newAssetGuid); - - // Cleanup - Settings.RemoveAssetEntry(AssetDatabase.AssetPathToGUID(newEntryPath)); - Settings.RemoveAssetEntry(folderGuid); - Settings.RemoveGroup(newGroup); - TearDownTestDir(); - } - - [Test] - public void AssetReferenceDrawer_SetObject_CanSetObject() - { - // Setup AssetReference - string assetPath = ""; - var testObject = SetupAssetReference(out assetPath); - - // Setup property - TestObjectWithRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - AssetReferenceDrawerUtilities.GatherFilters(property); - - // Test - string guid; - - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - Assert.IsTrue(AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame,property, testObject, propertyFieldInfo, "", out guid)); - Assert.AreEqual(m_AssetGUID, m_AssetReferenceDrawer.m_AssetRefObject.AssetGUID); - Assert.AreEqual(m_AssetGUID, guid); - Assert.AreEqual(testObject.name, m_AssetReferenceDrawer.m_AssetRefObject.editorAsset.name); - } - - [Test] - public void AssetReferenceDrawer_SetObject_CanSetSpriteObject() - { - // Setup - string assetPath = ""; - SetupAssetReference( out assetPath); - var assetGuid = ""; - var testObject = SetUpSingleSprite(out assetGuid); - - // Setup property - TestObjectWithRef obj = ScriptableObject.CreateInstance(); - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - AssetReferenceDrawerUtilities.GatherFilters(property); - - // Test - string guid; - - FieldInfo propertyFieldInfo = typeof(TestObjectWithRef).GetField("Reference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - SetupDefaultSettings(); - Assert.IsTrue(AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame,property, testObject, propertyFieldInfo, "", out guid)); - Assert.AreEqual(assetGuid, m_AssetReferenceDrawer.m_AssetRefObject.AssetGUID); - Assert.AreEqual("testAtlas", m_AssetReferenceDrawer.m_AssetRefObject.editorAsset.name); - Assert.AreEqual("testSprite", m_AssetReferenceDrawer.m_AssetRefObject.SubObjectName); - - // Cleanup - TearDownTestDir(); - } - - [Test] - public void SetObject_WhenTargetIsSubAsset_IsSetAsSubObject() - { - // Prepare test fbx - m_fbxAssetPath = GetAssetPath("testFBX.fbx"); - if (!File.Exists(m_fbxAssetPath)) - { - string fbxResourcePath = null; - var repoRoot = Directory.GetParent(Application.dataPath).Parent?.FullName; - if (!string.IsNullOrEmpty(repoRoot)) - fbxResourcePath = Path.Combine(repoRoot, "Projects", "TestsResources", "testFBX.fbx"); - - if (string.IsNullOrEmpty(fbxResourcePath) || !File.Exists(fbxResourcePath)) - Assert.Ignore($"Unable to find required FBX file to run this test. Ignoring."); - - File.Copy(fbxResourcePath, m_fbxAssetPath, true); - AssetDatabase.Refresh(); - } - - Assert.IsTrue(File.Exists(m_fbxAssetPath)); - var fbxAsset = AssetDatabase.LoadAssetAtPath(m_fbxAssetPath); - var meshSubAsset = AssetDatabase.LoadAllAssetRepresentationsAtPath(m_fbxAssetPath).First(o => o is Mesh); - AssetDatabase.TryGetGUIDAndLocalFileIdentifier(fbxAsset, out string fbxAssetGuid, out long _); - Assert.IsFalse(string.IsNullOrEmpty(fbxAssetGuid)); - - // Setup property - var ar = new AssetReferenceT(""); - var obj = ScriptableObject.CreateInstance(); - obj.Reference = ar; - var so = new SerializedObject(obj); - var property = so.FindProperty("Reference"); - - // Test - string guid; - m_AssetReferenceDrawer = new AssetReferenceDrawer(); - m_AssetReferenceDrawer.m_AssetRefObject = ar; - AssetReferenceDrawerUtilities.GatherFilters(property); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var success = AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, property, meshSubAsset, propertyFieldInfo, "", out guid); - - // Assert - Assert.IsTrue(success); - Assert.AreEqual(fbxAssetGuid, guid); - Assert.AreEqual(fbxAsset.name, m_AssetReferenceDrawer.m_AssetRefObject.editorAsset.name); - Assert.AreEqual(meshSubAsset.name, m_AssetReferenceDrawer.m_AssetRefObject.SubObjectName); - Assert.AreEqual(meshSubAsset.GetType(), m_AssetReferenceDrawer.m_AssetRefObject.SubOjbectType); - } - - [Test] - public void AssetReferenceDrawer_SetObject_CanSetToNull() - { - // Setup Original AssetReference to not be null and property - var newAssetGuid = ""; - var property = SetupForSetObjectNullTests(out newAssetGuid); - - // Test - string guid; - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, property, null, propertyFieldInfo, "", out guid); - Assert.AreEqual(null, m_AssetReferenceDrawer.m_AssetRefObject.SubObjectName); - Assert.AreEqual(string.Empty, m_AssetReferenceDrawer.m_AssetRefObject.AssetGUID); - - // Cleanup - Settings.RemoveAssetEntry(newAssetGuid); - TearDownTestDir(); - } - - [Test] - public void AssetReference_WhenCachedGUIDIsNotEqualToAssetGUID_CachedAssetIsNull() - { - // Setup - string guid = "8888888888888888888"; - var assetRef = new TestAssetReference(guid); - assetRef.CachedAssetProperty = new Object(); - - // Test - Assert.IsTrue(assetRef.CachedAssetProperty == null); - } - -#if UNITY_2019_2_OR_NEWER - - [Test] - public void AssetReferenceDrawer_SetObject_SetToNullDirtiesObject() - { - // Setup Original AssetReference to not be null and property - var newAssetGuid = ""; - var property = SetupForSetObjectNullTests(out newAssetGuid); - - // Test - string guid; - EditorUtility.ClearDirty(property.serializedObject.targetObject); - var prevDirty = EditorUtility.IsDirty(property.serializedObject.targetObject); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - AssetReferenceDrawerUtilities.SetObject(ref m_AssetReferenceDrawer.m_AssetRefObject, ref m_AssetReferenceDrawer.m_ReferencesSame, property, null, propertyFieldInfo, "", out guid); - Assert.IsFalse(prevDirty); - Assert.IsTrue(EditorUtility.IsDirty(property.serializedObject.targetObject)); - - // Cleanup - Settings.RemoveAssetEntry(newAssetGuid); - TearDownTestDir(); - } - - [TestCase(1, 0, 1)] - [TestCase(20, 10, 8)] - public void AssetReferenceDrawer_SetSubAssets_CanSetSubAssets(int numAtlasObjects, int selectedId, int numReferences) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssets(atlas, numReferences, true); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - - // Test - AssetReferenceDrawerUtilities.SetSubAssets(property, subAssets[selectedId], propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - foreach (var obj in property.serializedObject.targetObjects) - { - Assert.AreEqual(((TestSubObjectsSpriteAtlas)obj).testSpriteReference.SubObjectName, subAssets[selectedId].name); - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 1, 1, 0, 0)] - [TestCase(4, 3, 3, 1, 1)] - public void AssetReferenceDrawer_SetSubAssets_CanSetInList(int numAtlasObjects, int numReferences, int numElements, int selectedElement, int selectedSubAsset) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssetsList(atlas, numReferences, selectedElement, numElements, true, numReferences); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlasList).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - m_AssetReferenceDrawer.m_label = new GUIContent("Element " + selectedElement); - - // Test - AssetReferenceDrawerUtilities.SetSubAssets(property, subAssets[selectedSubAsset], propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - - // Check that only the selected element of each object's reference list was set to null - foreach (var obj in property.serializedObject.targetObjects) - { - var checkList = ((TestSubObjectsSpriteAtlasList)obj).testSpriteReference; - for (int currElement = 0; currElement < checkList.Length; currElement++) - { - if (currElement == selectedElement) - Assert.AreEqual(subAssets[selectedSubAsset].name, checkList[selectedElement].SubObjectName); - else - Assert.AreEqual("test", checkList[currElement].SubObjectName); - } - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 0, 1)] - [TestCase(20, 10, 8)] - public void AssetReferenceDrawer_SetSubAssets_CanSetSubAssetsToNull(int numAtlasObjects, int selectedId, int numReferences) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssets(atlas, numReferences, true); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - - // Test - AssetReferenceDrawerUtilities.SetSubAssets(property, subAssets[selectedId], propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - AssetReferenceDrawerUtilities.SetSubAssets(property, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - foreach (var obj in property.serializedObject.targetObjects) - { - Assert.AreEqual( null,((TestSubObjectsSpriteAtlas)obj).testSpriteReference.SubObjectName); - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 1, 1, 0)] - [TestCase(2, 3, 3, 1)] - public void AssetReferenceDrawer_SetSubAssets_CanSetToNullInList(int numAtlasObjects, int numReferences, int numElements, int selectedElement) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssetsList(atlas, numReferences, selectedElement, numElements, true, numReferences); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlasList).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - m_AssetReferenceDrawer.m_label = new GUIContent("Element " + selectedElement); - - // Test - AssetReferenceDrawerUtilities.SetSubAssets(property, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - - // Check that only the selected element of each object's reference list was set to null - foreach (var obj in property.serializedObject.targetObjects) - { - var checkList = ((TestSubObjectsSpriteAtlasList)obj).testSpriteReference; - for (int currElement = 0; currElement < checkList.Length; currElement++) - { - if (currElement == selectedElement) - Assert.AreEqual(null, checkList[selectedElement].SubObjectName); - else - Assert.AreEqual("test", checkList[currElement].SubObjectName); - } - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 0, 1)] - [TestCase(20, 10, 8)] - public void AssetReferenceDrawer_GetSubAssetsList_CanGetSubAssetsList(int numAtlasObjects, int selectedId, int numReferences) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssets(atlas, numReferences, true); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - - // Test - AssetReferenceDrawerUtilities.SetSubAssets(property, subAssets[selectedId], propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - var subassetsList = AssetReferenceDrawerUtilities.GetSubAssetsList(m_AssetReferenceDrawer.m_AssetRefObject); - Assert.AreEqual(subassetsList.Count,subAssets.Count + 1); - foreach (var obj in subassetsList) - { - if (obj != null) - { - Assert.IsTrue(FindSubassetNameInList(obj.name, subAssets)); - } - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - bool FindSubassetNameInList(string objName, List objects) - { - foreach (var obj in objects) - { - if (objName.Contains(obj.name)) - return true; - } - return false; - } - - [TestCase(1, 1)] - [TestCase(20, 8)] - public void AssetReferenceDrawer_SetMainAssets_CanSetMultipleAssets(int numAtlasObjects, int numReferences) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssets(atlas, numReferences); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - - // Test - AssetReferenceDrawerUtilities.SetMainAssets(ref m_AssetReferenceDrawer.m_ReferencesSame, property, atlas, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - foreach (var obj in property.serializedObject.targetObjects) - { - Assert.AreEqual(((TestSubObjectsSpriteAtlas)obj).testSpriteReference.AssetGUID, atlasGuid); - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 1, 1, 0)] - [TestCase(2, 3, 3, 1)] - public void AssetReferenceDrawer_SetMainAssets_CanSetMultipleAssetReferencesInList(int numAtlasObjects, int numReferences, int numElements, int selectedElement) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssetsList(atlas, numReferences, selectedElement, numElements, false); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlasList).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("Element " + selectedElement); - - // Test - AssetReferenceDrawerUtilities.SetMainAssets(ref m_AssetReferenceDrawer.m_ReferencesSame, property, atlas, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - - // Check that only the selected element of each object's reference list was set - foreach (var obj in property.serializedObject.targetObjects) - { - var checkList = ((TestSubObjectsSpriteAtlasList)obj).testSpriteReference; - for (int currElement = 0; currElement < checkList.Length; currElement++) - { - if (currElement == selectedElement) - Assert.AreEqual(atlasGuid, checkList[selectedElement].AssetGUID); - else - Assert.AreEqual(null, checkList[currElement].AssetGUID); - } - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [Test] - public void AssetReferenceDrawer_SetMainAssets_CanSetToNull() - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(1, out subAssets); - var property = SetupForSetSubAssets(atlas, 1); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - - // Test - AssetReferenceDrawerUtilities.SetMainAssets(ref m_AssetReferenceDrawer.m_ReferencesSame, property, null, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - Assert.AreEqual(((TestSubObjectsSpriteAtlas)property.serializedObject.targetObject).testSpriteReference.Asset, null); - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 1, 1, 0)] - [TestCase(2, 3, 3, 1)] - public void AssetReferenceDrawer_SetMainAssets_CanSetToNullInList(int numAtlasObjects, int numReferences, int numElements, int selectedElement) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssetsList(atlas, numReferences, selectedElement, numElements, true, numReferences); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlasList).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("Element " + selectedElement); - - // Test - AssetReferenceDrawerUtilities.SetMainAssets(ref m_AssetReferenceDrawer.m_ReferencesSame, property, null, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - - // Check that only the selected element of each object's reference list was set to null - foreach (var obj in property.serializedObject.targetObjects) - { - var checkList = ((TestSubObjectsSpriteAtlasList)obj).testSpriteReference; - for (int currElement = 0; currElement < checkList.Length; currElement++) - { - if (currElement == selectedElement) - Assert.AreEqual(String.Empty, checkList[selectedElement].AssetGUID); - else - Assert.AreEqual(atlasGuid, checkList[currElement].AssetGUID); - } - } - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [Test] - public void AssetReferenceDrawer_SetMainAssets_SetToNullDirtiesObject() - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(1, out subAssets); - var property = SetupForSetSubAssets(atlas, 1); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - - // Test - EditorUtility.ClearDirty(property.serializedObject.targetObject); - var prevDirty = EditorUtility.IsDirty(property.serializedObject.targetObject); - AssetReferenceDrawerUtilities.SetMainAssets(ref m_AssetReferenceDrawer.m_ReferencesSame, property, null, null, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - Assert.IsFalse(prevDirty); - Assert.IsTrue(EditorUtility.IsDirty(property.serializedObject.targetObject)); - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(1, 1)] - [TestCase(20, 8)] - public void AssetReferenceDrawer_GetNameForAsset_CanGetAssetNameWhenAllSame(int numAtlasObjects, int numReferences) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssets(atlas, numReferences); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - m_AssetReferenceDrawer.m_AssetName = atlas.name; - - // Test - var nameforAsset = AssetReferenceDrawerUtilities.GetNameForAsset(ref m_AssetReferenceDrawer.m_ReferencesSame, property, false, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - Assert.AreEqual(atlas.name, nameforAsset); - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - - [TestCase(10, 4, 8)] - public void AssetReferenceDrawer_GetNameForAsset_CanGetAssetNameWhenDifferent(int numAtlasObjects, int numToSet, int numReferences) - { - // Setup - var subAssets = new List(); - var atlas = SetUpSpriteAtlas(numAtlasObjects, out subAssets); - var property = SetupForSetSubAssets(atlas, numReferences, true, numToSet); - var assetPath = AssetDatabase.GetAssetOrScenePath(atlas); - var atlasGuid = AssetDatabase.AssetPathToGUID(assetPath); - m_AssetReferenceDrawer.m_label = new GUIContent("testSpriteReference"); - FieldInfo propertyFieldInfo = typeof(TestSubObjectsSpriteAtlas).GetField("testSpriteReference", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - m_AssetReferenceDrawer.m_AssetName = atlas.name; - - // Test - var nameforAsset = AssetReferenceDrawerUtilities.GetNameForAsset(ref m_AssetReferenceDrawer.m_ReferencesSame, property, false, propertyFieldInfo, m_AssetReferenceDrawer.m_label.text); - Assert.AreEqual("--", nameforAsset); - - // Cleanup - TearDownTestDir(); - Settings.RemoveAssetEntry(atlasGuid); - } - -#endif - } -} diff --git a/Tests/Editor/Build/BuildScriptTests.cs b/Tests/Editor/Build/BuildScriptTests.cs index b74ec32b..50f87f4d 100644 --- a/Tests/Editor/Build/BuildScriptTests.cs +++ b/Tests/Editor/Build/BuildScriptTests.cs @@ -64,6 +64,68 @@ public void ClearCachedData_CleansStreamingAssetFolder() Assert.IsTrue(builderCount > 0); } + [Test] + public void Folder_WithSubAssets_GetsBundleFileIdAssigned_DuringBuild() + { + var context = new AddressablesDataBuilderInput(Settings); + string folderGuid = AssetDatabase.CreateFolder(ConfigFolder, "FolderAsset"); + string folderPath = $"{ConfigFolder}/FolderAsset"; + PrefabUtility.SaveAsPrefabAsset(new GameObject(), $"{folderPath}/subfolderprefab.prefab"); + + AddressableAssetEntry folderEntry = Settings.CreateOrMoveEntry(folderGuid, Settings.DefaultGroup); + + Assert.IsTrue(string.IsNullOrEmpty(folderEntry.BundleFileId)); + + Settings.ActivePlayerDataBuilder.BuildData(context); + + Assert.IsTrue(folderEntry.IsFolder); + Assert.IsFalse(string.IsNullOrEmpty(folderEntry.BundleFileId)); + + //Cleanup + AssetDatabase.DeleteAsset(folderPath); + Settings.RemoveAssetEntry(folderEntry); + } + + [Test] + public void Folder_WithNoSubAssets_DoesNotThrowErrorDuringBuild() + { + var context = new AddressablesDataBuilderInput(Settings); + string folderGuid = AssetDatabase.CreateFolder(ConfigFolder, "FolderAsset"); + string folderPath = $"{ConfigFolder}/FolderAsset"; + + AddressableAssetEntry folderEntry = Settings.CreateOrMoveEntry(folderGuid, Settings.DefaultGroup); + + Assert.IsTrue(string.IsNullOrEmpty(folderEntry.BundleFileId)); + + Settings.ActivePlayerDataBuilder.BuildData(context); + + Assert.IsTrue(folderEntry.IsFolder); + Assert.IsTrue(string.IsNullOrEmpty(folderEntry.BundleFileId)); + + //Cleanup + AssetDatabase.DeleteAsset(folderPath); + Settings.RemoveAssetEntry(folderEntry); + } + + [Test] + public void Folder_DoesNotAssignBundleFileId_ForDynamicallyCreatedSubEntries() + { + var context = new AddressablesDataBuilderInput(Settings); + string folderGuid = AssetDatabase.CreateFolder(ConfigFolder, "FolderAsset"); + string folderPath = $"{ConfigFolder}/FolderAsset"; + PrefabUtility.SaveAsPrefabAsset(new GameObject(), $"{folderPath}/subfolderprefab.prefab"); + + AddressableAssetEntry folderEntry = Settings.CreateOrMoveEntry(folderGuid, Settings.DefaultGroup); + + Settings.ActivePlayerDataBuilder.BuildData(context); + + Assert.True(string.IsNullOrEmpty(folderEntry.SubAssets[0].BundleFileId)); + + //Cleanup + AssetDatabase.DeleteAsset(folderPath); + Settings.RemoveAssetEntry(folderEntry); + } + [Test] public void CopiedStreamingAssetAreCorrectlyDeleted_DirectoriesWithoutImport() { @@ -174,57 +236,6 @@ public void CopiedStreamingAssetAreCorrectlyDeleted_WithExistingFiles() } } - [Test] - public void BuildCompleteCallbackGetsCalled() - { - LogAssert.ignoreFailingMessages = true; - AddressableAssetSettings oldSettings = AddressableAssetSettingsDefaultObject.Settings; - AddressableAssetSettingsDefaultObject.Settings = Settings; - - try - { - bool callbackCalled = false; - BuildScript.buildCompleted += (result) => - { - callbackCalled = true; - }; - AddressableAssetSettings.BuildPlayerContent(); - Assert.IsTrue(callbackCalled); - } - finally - { - if (oldSettings != null) - { - AddressableAssetSettingsDefaultObject.Settings = oldSettings; - AddressableAssetSettings.BuildPlayerContent(); - } - LogAssert.ignoreFailingMessages = false; - } - } - - [Test] - public void BuildCompleteWithResult() - { - LogAssert.ignoreFailingMessages = true; - AddressableAssetSettings oldSettings = AddressableAssetSettingsDefaultObject.Settings; - AddressableAssetSettingsDefaultObject.Settings = Settings; - - try - { - AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult result); - Assert.IsTrue(string.IsNullOrEmpty(result.Error)); - } - finally - { - if (oldSettings != null) - { - AddressableAssetSettingsDefaultObject.Settings = oldSettings; - AddressableAssetSettings.BuildPlayerContent(); - } - LogAssert.ignoreFailingMessages = false; - } - } - [Test] public void BuildScriptBase_FailsCanBuildData() { diff --git a/Tests/Editor/Build/GenerateLocationListsTaskTests.cs b/Tests/Editor/Build/GenerateLocationListsTaskTests.cs index 5ddcf5ad..b0b909c4 100644 --- a/Tests/Editor/Build/GenerateLocationListsTaskTests.cs +++ b/Tests/Editor/Build/GenerateLocationListsTaskTests.cs @@ -21,6 +21,7 @@ GenerateLocationListsTask.Input GenerateDefaultInput() input.FileToBundle = new Dictionary(); input.Settings = m_Settings; input.BundleToAssetGroup = new Dictionary(); + input.Target = EditorUserBuildSettings.activeBuildTarget; return input; } @@ -194,6 +195,25 @@ public void WhenAssetHasDependencyOnBundle_AssetLocationIncludesRecursiveBundleD AssertLocationDependencies(output, "p6", "bundleW"); } + //static + [Test] + [TestCase("abc", BuildTarget.XboxOne, @"\abc")] + [TestCase("abc", BuildTarget.StandaloneWindows64, @"\abc")] + [TestCase("abc", BuildTarget.iOS, @"/abc")] + [TestCase("abc", BuildTarget.Android, @"/abc")] + [TestCase("abc", BuildTarget.StandaloneLinux64, @"/abc")] + [TestCase("abc", BuildTarget.Switch, @"/abc")] + [TestCase("abc", BuildTarget.StandaloneOSX, @"/abc")] + public void WhenBuildTargetIsWindowsOrXBox_BackSlashUsedInLoadPath(string id, BuildTarget target, string expected) + { + AddressableAssetGroup group = m_Settings.CreateGroup($"xyz", false, false, false, null, typeof(BundledAssetGroupSchema)); + var bag = group.GetSchema(); + var expectedPath = $"{bag.LoadPath.GetValue(m_Settings)}{expected}"; + var path = GenerateLocationListsTask.GetLoadPath(group, id, target); + Assert.AreEqual(expectedPath, path); + m_Settings.RemoveGroup(group); + } + //[Test] //public void WhenEntryAddressContainsBrackets_ExceptionIsThrown() //{ diff --git a/Tests/Editor/HostingServices/HttpHostingServiceTests.cs b/Tests/Editor/HostingServices/HttpHostingServiceTests.cs index 85556644..9612825e 100644 --- a/Tests/Editor/HostingServices/HttpHostingServiceTests.cs +++ b/Tests/Editor/HostingServices/HttpHostingServiceTests.cs @@ -61,15 +61,15 @@ public void TearDown() Directory.Delete(m_ContentRoot, true); } - [TestCase("subdir", "subdir1", "subdir3")] - [TestCase("subdír☠", "subdirãúñ", "subdirü", TestName = "ShouldServeFilesWSpecialCharacters")] - public void ShouldServeRequestedFiles(string subdir1, string subdir2, string subdir3) + [TestCase("subdir", "subdir1")] //"subdir3")] + [TestCase("subdír☠", "subdirãúñ", TestName = "ShouldServeFilesWSpecialCharacters")] //"subdirü", + public void ShouldServeRequestedFiles(string subdir1, string subdir2) // string subdir3) { var fileNames = new[] { Path.GetRandomFileName(), Path.Combine(subdir1, Path.GetRandomFileName()), - Path.Combine(subdir2, Path.Combine(subdir3, Path.GetRandomFileName())) + Path.Combine(subdir2, Path.GetRandomFileName()) }; foreach (var fileName in fileNames) @@ -100,14 +100,15 @@ public void ShouldServeRequestedFiles(string subdir1, string subdir2, string sub public void HttpServiceCompletesWithUploadSpeedWhenExpected() { string subdir1 = "subdir"; - string subdir2 = "subdir1"; - string subdir3 = "subdir3"; + //string subdir2 = "subdir1"; // Remove comment when Mono limit Fixed + //string subdir3 = "subdir3"; var fileNames = new[] { Path.GetRandomFileName(), Path.Combine(subdir1, Path.GetRandomFileName()), - Path.Combine(subdir2, Path.Combine(subdir3, Path.GetRandomFileName())) + //Path.Combine(subdir2, Path.Combine(subdir3, Path.GetRandomFileName())) // Remove comment when Mono limit Fixed. + //Path.Combine(subdir3, Path.GetRandomFileName()) // Remove when Mono Fix }; foreach (var fileName in fileNames) diff --git a/Tests/Editor/ProfileGroupTypeTests.cs b/Tests/Editor/ProfileGroupTypeTests.cs new file mode 100644 index 00000000..fedbfd81 --- /dev/null +++ b/Tests/Editor/ProfileGroupTypeTests.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using UnityEditor.AddressableAssets.GUI; +using UnityEditor.AddressableAssets.Settings; +using UnityEngine; +using UnityEngine.TestTools; + +namespace UnityEditor.AddressableAssets.Tests +{ + public class ProfileGroupTypeTests + { + [Test] + public void CreateEmptyProfileGroupType_Returns_InvalidProfileGroupType() + { + ProfileGroupType profileGroupType = new ProfileGroupType(); + Assert.False(profileGroupType.IsValidGroupType()); + } + + [Test] + public void CreatePrefixedProfileGroupType_Returns_InvalidProfileGroupType() + { + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + Assert.False(profileGroupType.IsValidGroupType()); + } + + [Test] + public void CreateValidProfileGroupType_Returns_ValidProfileGroupType() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType.GroupTypeVariable loadPath = new ProfileGroupType.GroupTypeVariable("LoadPath", "Test Load Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + profileGroupType.AddVariable(buildPath); + profileGroupType.AddVariable(loadPath); + Assert.True(profileGroupType.IsValidGroupType()); + } + + [Test] + public void GetPathValuesBySuffix_Returns_ExpectedPathValues() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + profileGroupType.AddVariable(buildPath); + Assert.AreEqual("Test Build Path", profileGroupType.GetVariableBySuffix(buildPath.Suffix).Value); + } + + [Test] + public void GetName_Returns_ExpectedVariableName() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + profileGroupType.AddVariable(buildPath); + Assert.AreEqual("prefix.BuildPath", profileGroupType.GetName(buildPath)); + } + + [Test] + public void AddVariableToGroupType_Returns_ExpectedNotNullVariable() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType.GroupTypeVariable loadPath = new ProfileGroupType.GroupTypeVariable("LoadPath", "Test Load Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + + Assert.NotNull(profileGroupType.AddVariable(buildPath)); + Assert.NotNull(profileGroupType.AddVariable(loadPath)); + Assert.True(profileGroupType.Variables.Count == 2); + } + + [Test] + public void AddDuplicateVariableToGroupType_Returns_NullVariable() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + + Assert.NotNull(profileGroupType.AddVariable(buildPath)); + LogAssert.Expect(LogType.Error, "prefix.BuildPath already exists."); + Assert.Null(profileGroupType.AddVariable(buildPath)); + } + + [Test] + public void RemoveVariable_Returns_ExpectedAction() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + profileGroupType.AddVariable(buildPath); + profileGroupType.RemoveVariable(buildPath); + Assert.True(profileGroupType.Variables.Count == 0); + } + + [Test] + public void RemoveNonExistentVariable_Returns_ExpectedAction() + { + ProfileGroupType.GroupTypeVariable buildPath = new ProfileGroupType.GroupTypeVariable("BuildPath", "Test Build Path"); + ProfileGroupType profileGroupType = new ProfileGroupType("prefix"); + profileGroupType.RemoveVariable(buildPath); + LogAssert.Expect(LogType.Error, "prefix.BuildPath does not exist."); + } + } +} diff --git a/Tests/Editor/ProfileGroupTypeTests.cs.meta b/Tests/Editor/ProfileGroupTypeTests.cs.meta new file mode 100644 index 00000000..3d539611 --- /dev/null +++ b/Tests/Editor/ProfileGroupTypeTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5bda7698faa434f0f954bdae3c7b06b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/AddressablesImplTests.cs b/Tests/Runtime/AddressablesImplTests.cs index 3cd94328..8a673305 100644 --- a/Tests/Runtime/AddressablesImplTests.cs +++ b/Tests/Runtime/AddressablesImplTests.cs @@ -5,7 +5,6 @@ using NUnit.Framework; using UnityEngine; using UnityEngine.AddressableAssets; -using UnityEngine.AddressableAssets.Tests; using UnityEngine.ResourceManagement; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceLocations; @@ -354,199 +353,6 @@ public IEnumerator AddressablesImpl_DownloadDependenciesAsync_CanDownloadDepende // Cleanup op.Release(); } - - [UnityTest] - public IEnumerator AddressablesImpl_LoadAsset_AssetDatabasePlaymode_PrefabIsInstanced() - { - //Setup - yield return Init(); - - if (string.IsNullOrEmpty(TypeName) || - (TypeName != "BuildScriptFastMode" && TypeName != "BuildScriptVirtualMode")) - { - Assert.Ignore($"Skipping test {nameof(AddressablesImpl_LoadAsset_AssetDatabasePlaymode_PrefabIsInstanced)} for {TypeName}, AssetDatabase based Playmode test."); - } - -#if UNITY_EDITOR - //Test - string key = "test0BASE"; - var locHandle = m_Addressables.LoadResourceLocationsAsync(key); - yield return locHandle; - Assert.IsNotNull(locHandle.Result); - Assert.IsTrue(locHandle.Result.Count > 0, $"Could not LocadResourceLocation with key {key}"); - - IResourceLocation location = locHandle.Result[0]; - var prefabHandle = m_Addressables.LoadAssetAsync(location); - yield return prefabHandle; - Assert.IsNotNull(prefabHandle.Result, $"Failed to load location {location.PrimaryKey}"); - - GameObject projectAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(location.InternalId); - Assert.AreNotEqual(projectAsset.GetInstanceID(), prefabHandle.Result.GetInstanceID(), "project Prefab and Addressables loaded Prefab are the same."); - - //Cleanup - m_Addressables.Release(prefabHandle); - m_Addressables.Release(locHandle); -#endif - } - - [UnityTest] - public IEnumerator AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectIsInstanced() - { - //Setup - yield return Init(); - - if (string.IsNullOrEmpty(TypeName) || - (TypeName != "BuildScriptFastMode" && TypeName != "BuildScriptVirtualMode")) - { - Assert.Ignore($"Skipping test {nameof(AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectIsInstanced)} for {TypeName}, AssetDatabase based Playmode test."); - } - -#if UNITY_EDITOR - //Test - string key = "assetWithSubObjects"; - var locHandle = m_Addressables.LoadResourceLocationsAsync(key); - yield return locHandle; - Assert.IsNotNull(locHandle.Result); - Assert.IsTrue(locHandle.Result.Count > 0, $"Could not LocadResourceLocation with key {key}"); - - IResourceLocation location = locHandle.Result[0]; - var objectHandle = m_Addressables.LoadAssetAsync(location); - yield return objectHandle; - Assert.IsNotNull(objectHandle.Result, $"Failed to load location {location.PrimaryKey}"); - - TestObject projectAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(location.InternalId); - Assert.AreNotEqual(projectAsset.GetInstanceID(), objectHandle.Result.GetInstanceID(), "project ScriptableObject and Addressables loaded ScriptableObject are the same."); - - //Cleanup - m_Addressables.Release(objectHandle); - m_Addressables.Release(locHandle); -#endif - } - - [UnityTest] - public IEnumerator AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectListIsInstanced() - { - //Setup - yield return Init(); - - if (string.IsNullOrEmpty(TypeName) || - (TypeName != "BuildScriptFastMode" && TypeName != "BuildScriptVirtualMode")) - { - Assert.Ignore($"Skipping test {nameof(AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectListIsInstanced)} for {TypeName}, AssetDatabase based Playmode test."); - } - -#if UNITY_EDITOR - //Test - string key = "assetWithSubObjects"; - var locHandle = m_Addressables.LoadResourceLocationsAsync(key); - yield return locHandle; - Assert.IsNotNull(locHandle.Result); - Assert.IsTrue(locHandle.Result.Count > 0, $"Could not LocadResourceLocation with key {key}"); - - IResourceLocation location = locHandle.Result[0]; - var objectHandle = m_Addressables.LoadAssetAsync>(location); - yield return objectHandle; - Assert.IsNotNull(objectHandle.Result, $"Failed to load location {location.PrimaryKey}"); - - string assetPath = location.InternalId; - var subObjects = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath); - var mainObject = UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath); - Assert.AreNotEqual(mainObject.GetInstanceID(), objectHandle.Result[0].GetInstanceID(), "project ScriptableObject and Addressables loaded ScriptableObject are the same. index 0"); - for (int i = 0; i < subObjects.Length; ++i) - Assert.AreNotEqual(subObjects[i].GetInstanceID(), objectHandle.Result[i+1].GetInstanceID(), $"project ScriptableObject and Addressables loaded ScriptableObject are the same. index {i+1}"); - - //Cleanup - m_Addressables.Release(objectHandle); - m_Addressables.Release(locHandle); -#endif - } - - [UnityTest] - public IEnumerator AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectArrayIsInstanced() - { - //Setup - yield return Init(); - - if (string.IsNullOrEmpty(TypeName) || - (TypeName != "BuildScriptFastMode" && TypeName != "BuildScriptVirtualMode")) - { - Assert.Ignore($"Skipping test {nameof(AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectArrayIsInstanced)} for {TypeName}, AssetDatabase based Playmode test."); - } - -#if UNITY_EDITOR - //Test - string key = "assetWithSubObjects"; - var locHandle = m_Addressables.LoadResourceLocationsAsync(key); - yield return locHandle; - Assert.IsNotNull(locHandle.Result); - Assert.IsTrue(locHandle.Result.Count > 0, $"Could not LocadResourceLocation with key {key}"); - - IResourceLocation location = locHandle.Result[0]; - var objectHandle = m_Addressables.LoadAssetAsync(location); - yield return objectHandle; - Assert.IsNotNull(objectHandle.Result, $"Failed to load location {location.PrimaryKey}"); - - string assetPath = location.InternalId; - var subObjects = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath); - var mainObject = UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath); - Assert.AreNotEqual(mainObject.GetInstanceID(), objectHandle.Result[0].GetInstanceID(), "project ScriptableObject and Addressables loaded ScriptableObject are the same. index 0"); - for (int i = 0; i < subObjects.Length; ++i) - Assert.AreNotEqual(subObjects[i].GetInstanceID(), objectHandle.Result[i+1].GetInstanceID(), $"project ScriptableObject and Addressables loaded ScriptableObject are the same. index {i+1}"); - - //Cleanup - m_Addressables.Release(objectHandle); - m_Addressables.Release(locHandle); -#endif - } - - [UnityTest] - public IEnumerator AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectSubObjectIsInstanced() - { - //Setup - yield return Init(); - - if (string.IsNullOrEmpty(TypeName) || - (TypeName != "BuildScriptFastMode" && TypeName != "BuildScriptVirtualMode")) - { - Assert.Ignore($"Skipping test {nameof(AddressablesImpl_LoadAsset_AssetDatabasePlaymode_ScriptableObjectSubObjectIsInstanced)} for {TypeName}, AssetDatabase based Playmode test."); - } - -#if UNITY_EDITOR - //Test - string key = "assetWithSubObjects[sub-shown]"; - var locHandle = m_Addressables.LoadResourceLocationsAsync(key); - yield return locHandle; - Assert.IsNotNull(locHandle.Result); - Assert.IsTrue(locHandle.Result.Count > 0, $"Could not LocadResourceLocation with key {key}"); - - IResourceLocation location = locHandle.Result[0]; - var objectHandle = m_Addressables.LoadAssetAsync(location); - yield return objectHandle; - Assert.IsNotNull(objectHandle.Result, $"Failed to load location {location.PrimaryKey}"); - - string assetPath = location.InternalId.Substring(0,location.InternalId.IndexOf('[')); - var objs = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath); - Type type = typeof(TestObject); - UnityEngine.Object projectObject = null; - foreach (var o in objs) - { - if (o.name == "sub-shown") - { - if (type.IsAssignableFrom(o.GetType())) - { - projectObject = o; - break; - } - } - } - Assert.IsNotNull(projectObject); - Assert.AreNotEqual(projectObject.GetInstanceID(), objectHandle.Result.GetInstanceID(), "project sub ScriptableObject and Addressables loaded ScriptableObject are the same."); - - //Cleanup - m_Addressables.Release(objectHandle); - m_Addressables.Release(locHandle); -#endif - } [UnityTest] public IEnumerator AddressablesImpl_DownloadDependenciesAsync_CantDownloadWhenGetResourceLocFailsObjectList() diff --git a/Tests/Runtime/AddressablesIntegrationTestsImpl.cs b/Tests/Runtime/AddressablesIntegrationTestsImpl.cs index fb0a52b0..467c6f6d 100644 --- a/Tests/Runtime/AddressablesIntegrationTestsImpl.cs +++ b/Tests/Runtime/AddressablesIntegrationTestsImpl.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Text; using UnityEngine.AddressableAssets.ResourceProviders.Tests; +using UnityEngine.Networking; using UnityEngine.U2D; using Object = UnityEngine.Object; @@ -240,6 +241,22 @@ public IEnumerator InternalIdTranslationTest() Assert.AreEqual("original", originalId); } + [UnityTest] + public IEnumerator WebRequestOverrideTest() + { + yield return Init(); + var originalUrl = "http://127.0.0.1/original.asset"; + var replacedUrl = "http://127.0.0.1/replaced.asset"; + var uwr = new UnityWebRequest(originalUrl); + + m_Addressables.WebRequestOverride = request => request.url = replacedUrl; + m_Addressables.ResourceManager.WebRequestOverride(uwr); + var currentUrl = uwr.url; + uwr.Dispose(); + m_Addressables.WebRequestOverride = null; + Assert.AreEqual(replacedUrl, currentUrl); + } + [UnityTest] public IEnumerator CanLoadTextureAsTexture() { @@ -1673,7 +1690,6 @@ public IEnumerator DownloadDependnecies_DoesNotRetainLoadedBundles_WithAutoRelea Assert.AreEqual(bundleCountBefore, AssetBundle.GetAllLoadedAssetBundles().Count()); } - [UnityTest] [Ignore("Test is unstable until task refactor is finished.")] public IEnumerator DownloadDependencies_ReturnsValidTask() @@ -1685,7 +1701,7 @@ public IEnumerator DownloadDependencies_ReturnsValidTask() Assert.IsNotNull(op.Task); yield return op; Assert.IsNotNull(op.Task); - + op.Release(); } diff --git a/Tests/Runtime/AssetBundleProviderTests.cs b/Tests/Runtime/AssetBundleProviderTests.cs index 733c2d3b..66c2c7c6 100644 --- a/Tests/Runtime/AssetBundleProviderTests.cs +++ b/Tests/Runtime/AssetBundleProviderTests.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using NUnit.Framework; @@ -11,6 +12,8 @@ using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement; using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceLocations; +using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; using UnityEngine.TestTools; @@ -55,8 +58,16 @@ internal override void Setup(AddressableAssetSettings settings, string tempAsset entry.address = "testprefab"; } } + #endif + [SetUp] + public void Setup() + { + if (m_Addressables != null) + m_Addressables.WebRequestOverride = null; + } + [UnityTest] public IEnumerator WhenUWRExceedsMaxLimit_UWRAreQueued() { @@ -88,6 +99,54 @@ public IEnumerator WhenAssetBundleIsLocal_AndForceUWRIsDisabled_UWRIsNotUsed() yield return h; h.Release(); } + + [UnityTest] + public IEnumerator WhenWebRequestOverrideIsSet_CallbackIsCalled_AssetBundleProvider() + { + bool webRequestOverrideCalled = false; + m_Addressables.WebRequestOverride = request => webRequestOverrideCalled = true; + + var prev = LogAssert.ignoreFailingMessages; + LogAssert.ignoreFailingMessages = true; + + var nonExistingPath = "http://127.0.0.1/non-existing-bundle"; + var loc = new ResourceLocationBase(nonExistingPath, nonExistingPath, typeof(AssetBundleProvider).FullName, typeof(AssetBundleResource)); + loc.Data = new AssetBundleRequestOptions(); + var h = m_Addressables.ResourceManager.ProvideResource(loc); + yield return h; + + if (h.IsValid()) h.Release(); + LogAssert.ignoreFailingMessages = prev; + Assert.IsTrue(webRequestOverrideCalled); + } + + [Test] + [TestCase("Relative/Local/Path", true, false)] + [TestCase("Relative/Local/Path", true, true)] + [TestCase("http://127.0.0.1/Web/Path", false, true)] + [TestCase("jar:file://Local/Path", true, false)] + [TestCase("jar:file://Local/Path", true, true)] + public void AssetBundleLoadPathsCorrectForGetLoadInfo(string internalId, bool isLocal, bool useUnityWebRequestForLocalBundles) + { + if (internalId.StartsWith("jar") && Application.platform != RuntimePlatform.Android) + Assert.Ignore($"Skipping test {TestContext.CurrentContext.Test.Name} due jar based tests are only for running on Android Platform."); + + var loc = new ResourceLocationBase("dummy", internalId, "dummy", typeof(Object)); + loc.Data = new AssetBundleRequestOptions { UseUnityWebRequestForLocalBundles = useUnityWebRequestForLocalBundles }; + ProviderOperation op = new ProviderOperation(); + op.Init(m_Addressables.ResourceManager, null, loc, new AsyncOperationHandle>()); + ProvideHandle h = new ProvideHandle(m_Addressables.ResourceManager, op); + + AssetBundleResource.GetLoadInfo(h, out AssetBundleResource.LoadType loadType, out string path); + var expectedLoadType = isLocal ? useUnityWebRequestForLocalBundles ? AssetBundleResource.LoadType.Web : AssetBundleResource.LoadType.Local : AssetBundleResource.LoadType.Web; + Assert.AreEqual(expectedLoadType, loadType, "Incorrect load type found for internalId " + internalId); + var expectedPath = internalId; + if (isLocal && useUnityWebRequestForLocalBundles) + { + expectedPath = internalId.StartsWith("jar") ? internalId : "file:///" + Path.GetFullPath(internalId); + } + Assert.AreEqual(expectedPath, path); + } } #if UNITY_EDITOR diff --git a/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs b/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs index 8287c6ad..65846022 100644 --- a/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs +++ b/Tests/Runtime/ResourceManager/Operations/BaseOperationBehaviorTests.cs @@ -244,7 +244,7 @@ public void GroupOperation_WithOpsThatImplementGetDownloadStatus_ComputesExpecte var mdpco = new List(); for (int i = 0; i < 4; i++) { - var o = m_RM.CreateOperation(typeof(ManualDownloadPercentCompleteOperation), 1, 0, null); + var o = m_RM.CreateOperation(typeof(ManualDownloadPercentCompleteOperation), 1, null, null); o.Start(m_RM, default, null); mdpco.Add(o); ops.Add(new AsyncOperationHandle(o)); @@ -263,7 +263,7 @@ public void GroupOperation_WithOpsThatImplementGetDownloadStatus_ComputesExpecte [Test] public void ChainOperation_WithOpThatImplementGetDownloadStatus_ComputesExpectedDownloadPercentComplete() { - var depOp = m_RM.CreateOperation(typeof(ManualDownloadPercentCompleteOperation), 1, 0, null); + var depOp = m_RM.CreateOperation(typeof(ManualDownloadPercentCompleteOperation), 1, null, null); depOp.Start(m_RM, default, null); var chainOp = m_RM.CreateChainOperation(new AsyncOperationHandle(depOp), s => m_RM.CreateCompletedOperationInternal(null, true, null)); @@ -294,7 +294,7 @@ public void PercentComplete_ReturnsZero_WhenChainOperationHasNotBegun() public void GroupOperation_WithDuplicateOpThatImplementGetDownloadStatus_DoesNotOverCountValues() { var ops = new List(); - var o = m_RM.CreateOperation(typeof(ManualDownloadPercentCompleteOperation), 1, 0, null); + var o = m_RM.CreateOperation(typeof(ManualDownloadPercentCompleteOperation), 1, null, null); o.Start(m_RM, default, null); for (int i = 0; i < 4; i++) ops.Add(new AsyncOperationHandle(o)); @@ -328,5 +328,18 @@ public void CompletionEvents_AreInvoked_InOrderAdded() op.Start(null, default, null); op.Complete(1, true, null); } + + [Test] + public void WhenOperationIsReused_HasExecutedIsReset() + { + var op = new TestOp(); + op.Start(null, default, null); + op.Complete(1, true, null); + + Assert.IsTrue(op.HasExecuted); + var dep = new AsyncOperationHandle(new TestOp()); + op.Start(null, dep, null); + Assert.IsFalse(op.HasExecuted); + } } } diff --git a/Tests/Runtime/ResourceManager/OperationsCacheTests.cs b/Tests/Runtime/ResourceManager/OperationsCacheTests.cs new file mode 100644 index 00000000..74b75ec2 --- /dev/null +++ b/Tests/Runtime/ResourceManager/OperationsCacheTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceLocations; +using UnityEngine.ResourceManagement.ResourceProviders; +using UnityEngine.ResourceManagement.Util; + +namespace UnityEngine.ResourceManagement.Tests +{ + public class OperationsCacheTests + { + ResourceManager m_ResourceManager; + [SetUp] + public void Setup() + { + m_ResourceManager = new ResourceManager(); + m_ResourceManager.CallbackHooksEnabled = false; // default for tests. disabled callback hooks. we will call update manually + } + + [TearDown] + public void TearDown() + { + Assert.Zero(m_ResourceManager.OperationCacheCount); + m_ResourceManager.Dispose(); + } + + [Test] + public void OperationCache_CollisionsAreProperlyHandled() + { + Assert.Zero(m_ResourceManager.OperationCacheCount); + var provType = typeof(InstanceProvider); + var loc1 = new TestResourceLocation("asset1", "asset1", provType.FullName, typeof(GameObject)); + var key1 = new LocationCacheKey(loc1, typeof(GameObject)); + var opType = typeof(TestOperation); + m_ResourceManager.CreateOperation(opType, opType.GetHashCode(), key1, null); + + Assert.AreEqual(1, m_ResourceManager.CachedOperationCount()); + Assert.IsTrue(m_ResourceManager.IsOperationCached(key1)); + + var loc2 = new TestResourceLocation("asset2", "asset2", provType.FullName, typeof(GameObject)); + var key2 = new LocationCacheKey(loc2, typeof(GameObject)); + Assert.IsFalse(m_ResourceManager.IsOperationCached(key2)); + + Assert.IsTrue(m_ResourceManager.RemoveOperationFromCache(key1)); + } + + class TestOperation : AsyncOperationBase, ICachable + { + protected override void Execute() + { + } + + public IOperationCacheKey Key { get; set; } + } + + class TestResourceLocation : IResourceLocation + { + public TestResourceLocation(string name, string id, string providerId, Type t, params IResourceLocation[] dependencies) + { + PrimaryKey = name; + InternalId = id; + ProviderId = providerId; + Dependencies = new List(dependencies); + ResourceType = t == null ? typeof(object) : t; + } + + public string InternalId { get; } + public string ProviderId { get; } + public IList Dependencies { get; } + + public int Hash(Type type) + { + // Returning a constant hashcode to force collisions. + return 1337; + } + + public int DependencyHashCode => 0; + public bool HasDependencies => Dependencies.Count > 0; + public object Data { get; } + public string PrimaryKey { get; } + public Type ResourceType { get; } + } + } +} diff --git a/Tests/Runtime/ResourceManager/OperationsCacheTests.cs.meta b/Tests/Runtime/ResourceManager/OperationsCacheTests.cs.meta new file mode 100644 index 00000000..daa6b5b4 --- /dev/null +++ b/Tests/Runtime/ResourceManager/OperationsCacheTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1fd8a0466f984a08874b05c3243189f6 +timeCreated: 1620745004 \ No newline at end of file diff --git a/Tests/Runtime/ResourceManager/ResourceManagerBaseTests.cs b/Tests/Runtime/ResourceManager/ResourceManagerBaseTests.cs index d770a70e..b0528990 100644 --- a/Tests/Runtime/ResourceManager/ResourceManagerBaseTests.cs +++ b/Tests/Runtime/ResourceManager/ResourceManagerBaseTests.cs @@ -171,7 +171,7 @@ async void Start() } } - [UnityTest, Timeout(10000)] + [UnityTest] public IEnumerator WhenAsyncOperationIsDone_TaskIsCompleted() { // Setup @@ -192,7 +192,7 @@ public IEnumerator WhenAsyncOperationIsDone_TaskIsCompleted() GameObject.Destroy(go); } - [UnityTest, Timeout(10000)] + [UnityTest] public IEnumerator WhenAsyncOperationIsDone_TasksAndCallbackIsCompleted() { // Setup diff --git a/Tests/Runtime/ResourceManager/ResourceManagerTests.cs b/Tests/Runtime/ResourceManager/ResourceManagerTests.cs index 6720e65b..69584a7d 100644 --- a/Tests/Runtime/ResourceManager/ResourceManagerTests.cs +++ b/Tests/Runtime/ResourceManager/ResourceManagerTests.cs @@ -68,6 +68,47 @@ public void WhenIUpdateReceiverAdded_CallbackIsInvoked() Assert.IsFalse(ur.invoked); } + class RMTestOp : AsyncOperationBase + { + public int CompletedEventTriggeredCount = 0; + + protected override void Execute() + { + m_RM.RegisterForDeferredCallback(this); + } + + protected override bool InvokeWaitForCompletion() + { + m_RM.Update(1); + return true; + } + } + class RMTestUpdateReceiver : IUpdateReceiver + { + public int UpdateCount = 0; + public void Update(float unscaledDeltaTime) + { + UpdateCount++; + } + } + [Test] + public void Reentering_UpdateMethod_ThrowsException() + { + var op = new RMTestOp(); + op.Completed += o => + { + (o.m_InternalOp as RMTestOp).CompletedEventTriggeredCount++; + Assert.Throws(() => o.WaitForCompletion()); + }; + var rec = new RMTestUpdateReceiver(); + m_ResourceManager.AddUpdateReceiver(rec); + m_ResourceManager.StartOperation(op, default); + op.WaitForCompletion(); + m_ResourceManager.RemoveUpdateReciever(rec); + Assert.AreEqual(1, op.CompletedEventTriggeredCount); + Assert.AreEqual(1, rec.UpdateCount); + } + class TestUpdateReceiverThatRemovesSelfDuringUpdate : IUpdateReceiver { public ResourceManager rm; diff --git a/Tests/Runtime/ResourceManager/ResourceManagerVirtualModeTests.cs b/Tests/Runtime/ResourceManager/ResourceManagerVirtualModeTests.cs index d5d28a60..3cb4c3a3 100644 --- a/Tests/Runtime/ResourceManager/ResourceManagerVirtualModeTests.cs +++ b/Tests/Runtime/ResourceManager/ResourceManagerVirtualModeTests.cs @@ -54,10 +54,12 @@ protected IResourceLocation CreateLocationForAsset(string name, string path) Random.Range(0, sharedBundleLocations.Count - 3); IResourceLocation bundle = sharedBundleLocations[sharedBundleIndex]; VirtualAssetBundle vBundle = bundleMap[bundle.InternalId]; - vBundle.Assets.Add(new VirtualAssetBundleEntry(path, Random.Range(1024, 1024 * 1024))); + var vab = new VirtualAssetBundleEntry(path, Random.Range(1024, 1024 * 1024)); + vab.m_AssetPath = path; + vBundle.Assets.Add(vab); IResourceLocation dep1Location = sharedBundleLocations[sharedBundleIndex + 1]; IResourceLocation dep2Location = sharedBundleLocations[sharedBundleIndex + 2]; - return new ResourceLocationBase(name, path, typeof(BundledAssetProvider).FullName, typeof(object), bundle, dep1Location, dep2Location); + return new ResourceLocationBase(path, path, typeof(BundledAssetProvider).FullName, typeof(object), bundle, dep1Location, dep2Location); } } } diff --git a/Tests/Runtime/SyncAddressableTests.cs b/Tests/Runtime/SyncAddressableTests.cs index 541443e7..d12f4668 100644 --- a/Tests/Runtime/SyncAddressableTests.cs +++ b/Tests/Runtime/SyncAddressableTests.cs @@ -7,7 +7,9 @@ #endif using UnityEngine; using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement; using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.SceneManagement; using UnityEngine.TestTools; @@ -297,6 +299,47 @@ public void LoadContentCatalogSynchronously_SuccessfullyCompletes_WithInvalidPat //Cleanup ReleaseOp(loadCatalogOp); } + + [Test] + public void InstanceOperation_WithFailedBundleLoad_CompletesSync() + { + var depOp = m_Addressables.LoadAssetAsync(m_InvalidKey); + LogAssert.Expect(LogType.Error, new Regex("InvalidKeyException*")); + + var instanceOperation = new ResourceManager.InstanceOperation(); + instanceOperation.Init(m_Addressables.ResourceManager, new InstanceProvider(), new InstantiationParameters(), depOp); + //Since we're calling the operation directly we need to simulate the full workflow of the additional ref count during load + instanceOperation.IncrementReferenceCount(); + + instanceOperation.WaitForCompletion(); + LogAssert.Expect(LogType.Error, new Regex("InvalidKeyException*")); + + Assert.IsTrue(instanceOperation.IsDone); + m_Addressables.Release(depOp); + } + + [Test] + public void InstanceOperation_WithSuccessfulBundleLoad_CompletesSync() + { + var depOp = m_Addressables.LoadAssetAsync(m_PrefabKey); + var instanceOperation = new ResourceManager.InstanceOperation(); + instanceOperation.Init(m_Addressables.ResourceManager, new InstanceProvider(), new InstantiationParameters(), depOp); + //Since we're calling the operation directly we need to simulate the full workflow of the additional ref count during load + instanceOperation.IncrementReferenceCount(); + + instanceOperation.WaitForCompletion(); + + Assert.IsTrue(instanceOperation.IsDone); + m_Addressables.Release(depOp); + } + + class FailedAssetBundleResource : IAssetBundleResource + { + public AssetBundle GetAssetBundle() + { + return null; + } + } } //This class is made because of, and should be refactored away when resolved, bug: https://jira.unity3d.com/browse/ADDR-1215 diff --git a/Tests/Runtime/TextDataProviderTests.cs b/Tests/Runtime/TextDataProviderTests.cs new file mode 100644 index 00000000..ca9549fc --- /dev/null +++ b/Tests/Runtime/TextDataProviderTests.cs @@ -0,0 +1,65 @@ +using System.Collections; +using System.Text.RegularExpressions; +using NUnit.Framework; +#if UNITY_EDITOR +using UnityEditor.AddressableAssets.Settings; +#endif +using UnityEngine; +using UnityEngine.ResourceManagement.ResourceLocations; +using UnityEngine.ResourceManagement.ResourceProviders; +using UnityEngine.TestTools; + +public abstract class TextDataProviderTests : AddressablesTestFixture +{ + [SetUp] + public void Setup() + { + if (m_Addressables != null) + m_Addressables.WebRequestOverride = null; + } + + [UnityTest] + public IEnumerator WhenWebRequestOverrideIsSet_CallbackIsCalled_TextDataProvider() + { + bool webRequestOverrideCalled = false; + m_Addressables.WebRequestOverride = request => webRequestOverrideCalled = true; + + var prev = LogAssert.ignoreFailingMessages; + LogAssert.ignoreFailingMessages = true; + + var nonExistingPath = "http://127.0.0.1/non-existing-catalog"; + var loc = new ResourceLocationBase(nonExistingPath, nonExistingPath, typeof(TextDataProvider).FullName, typeof(string)); + var h = m_Addressables.ResourceManager.ProvideResource(loc); + yield return h; + + if (h.IsValid()) h.Release(); + LogAssert.ignoreFailingMessages = prev; + Assert.IsTrue(webRequestOverrideCalled); + } + + [UnityTest] + public IEnumerator WhenWebRequestOverrideIsSet_CallbackIsCalled_JsonAssetProvider() + { + bool webRequestOverrideCalled = false; + m_Addressables.WebRequestOverride = request => webRequestOverrideCalled = true; + + var prev = LogAssert.ignoreFailingMessages; + LogAssert.ignoreFailingMessages = true; + + var nonExistingPath = "http://127.0.0.1/non-existing-catalog"; + var loc = new ResourceLocationBase(nonExistingPath, nonExistingPath, typeof(JsonAssetProvider).FullName, typeof(string)); + var h = m_Addressables.ResourceManager.ProvideResource(loc); + yield return h; + + if (h.IsValid()) h.Release(); + LogAssert.ignoreFailingMessages = prev; + Assert.IsTrue(webRequestOverrideCalled); + } +} + +#if UNITY_EDITOR +class TextDataProviderTests_PackedPlaymodeMode : TextDataProviderTests { protected override TestBuildScriptMode BuildScriptMode { get { return TestBuildScriptMode.PackedPlaymode; } } } +#endif + +[UnityPlatform(exclude = new[] { RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor })] +class TextDataProviderTests_PackedMode : TextDataProviderTests { protected override TestBuildScriptMode BuildScriptMode { get { return TestBuildScriptMode.Packed; } } } diff --git a/Tests/Runtime/TextDataProviderTests.cs.meta b/Tests/Runtime/TextDataProviderTests.cs.meta new file mode 100644 index 00000000..da53bfbd --- /dev/null +++ b/Tests/Runtime/TextDataProviderTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1c2d1f26ef3845fab1313226df7724c3 +timeCreated: 1620326770 \ No newline at end of file diff --git a/ValidationExceptions.json b/ValidationExceptions.json index 2fcc54cc..db3ab994 100644 --- a/ValidationExceptions.json +++ b/ValidationExceptions.json @@ -4,7 +4,7 @@ { "ValidationTest": "API Validation", "ExceptionError": "", - "PackageVersion": "1.18.4" + "PackageVersion": "1.18.9" } ] } diff --git a/package.json b/package.json index dccdd252..79bd33e0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.addressables", "displayName": "Addressables", - "version": "1.18.4", + "version": "1.18.9", "unity": "2018.4", "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": "1.18.0", + "com.unity.scriptablebuildpipeline": "1.19.0", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", @@ -22,10 +22,10 @@ "repository": { "url": "https://github.cds.internal.unity3d.com/unity/Addressables.git", "type": "git", - "revision": "cb4e6f60b4355966681a164019a82d71abbd0524" + "revision": "0fc62dc1024e533883dfa7a8485a6a1e6ae99e0a" }, "upmCi": { - "footprint": "8d9efce387cd5072bfe96fe8672c09cb679438c7" + "footprint": "baab8e83bf1f1b32dd3cd9db6da06f4982e19cda" }, "samples": [ { @@ -38,6 +38,11 @@ "description": "A script that disables asset importing during a player build. This improves build performance since AssetBundles are copied into StreamingAssets at build time.", "path": "Samples~/DisableAssetImportOnBuild" }, + { + "displayName": "Import Groups Tool", + "description": "A tool that imports group assets (for example from a custom package) to the current project.", + "path": "Samples~/ImportExistingGroup" + }, { "displayName": "Prefab Spawner", "description": "A basic script that instantiates and destroys a prefab AssetReference.",