Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [1.22.3] - 2024-10-18
- Fixed issue where the content of the list with Addressable Assets Groups is improperly indented when displayed in Group Hierarchy with Dashes Group View.
- Fixed issue where “Profile rename failed because default profile cannot be renamed“ error is thrown when renaming a new profile in Addressables Profiles.
- Fixed loading error when the loading from an assetbundle that is currently being preloaded.
  • Loading branch information
Unity Technologies committed Oct 18, 2024
1 parent 481c813 commit e9c165d
Show file tree
Hide file tree
Showing 14 changed files with 254 additions and 59 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ 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.22.3] - 2024-10-18
- Fixed issue where the content of the list with Addressable Assets Groups is improperly indented when displayed in Group Hierarchy with Dashes Group View.
- Fixed issue where “Profile rename failed because default profile cannot be renamed“ error is thrown when renaming a new profile in Addressables Profiles.
- Fixed loading error when the loading from an assetbundle that is currently being preloaded.

## [1.22.2] - 2024-05-13
- Fixed memory leak when loading Sprite objects from a SpriteAtlas asset.
- Added support for directly calling Release() on AsyncOperationHandles that didn't have the option to.
Expand Down
7 changes: 6 additions & 1 deletion Editor/GUI/AddressableAssetsSettingsGroupTreeView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,12 @@ protected override void RowGUI(RowGUIArgs args)
if (item == null || item.group == null && item.entry == null)
{
using (new EditorGUI.DisabledScope(true))
base.RowGUI(args);
{
if (!string.IsNullOrEmpty(item.folderPath))
CellGUI(args.GetCellRect(1), null, (int)ColumnId.Id, ref args);
else
base.RowGUI(args);
}
}
else
{
Expand Down
48 changes: 44 additions & 4 deletions Editor/Settings/AddressableAssetProfileSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -499,12 +499,36 @@ string GetDefaultProfileId()
return null;
}

BuildProfile GetDefaultProfile()
internal BuildProfile GetDefaultProfile()
{
BuildProfile profile = null;
BuildProfile defaultProfile = null;
if (m_Profiles.Count > 0)
profile = m_Profiles[0];
return profile;
{
if (m_Profiles[0].profileName != k_RootProfileName)
{
int rootProfileIndex = -1;
for (int i = 0; i < m_Profiles.Count; i++)
{
if (m_Profiles[i].profileName == k_RootProfileName)
{
rootProfileIndex = i;
break;
}
}

if (rootProfileIndex != -1)
{
BuildProfile rootProfile = m_Profiles[rootProfileIndex];
m_Profiles[rootProfileIndex] = m_Profiles[0];
m_Profiles[0] = rootProfile;

defaultProfile = rootProfile;
}
}
else
defaultProfile = m_Profiles[0];
}
return defaultProfile;
}

bool ValidateProfiles()
Expand Down Expand Up @@ -914,5 +938,21 @@ public void OnBeforeSerialize()
public void OnAfterDeserialize()
{
}

internal void SortProfiles()
{
BuildProfile defaultProfile = GetDefaultProfile();

if (m_Profiles.Count <= 1)
return;

List<BuildProfile> otherProfiles = m_Profiles.GetRange(1, m_Profiles.Count - 1);
otherProfiles.Sort((a, b) => string.CompareOrdinal(a.id, b.id));

for (int i = 1; i < m_Profiles.Count; i++)
{
m_Profiles[i] = otherProfiles[i - 1];
}
}
}
}
2 changes: 1 addition & 1 deletion Editor/Settings/AddressableAssetSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3105,7 +3105,7 @@ public static bool InvokeAssetGroupCommand(string cmdId, IEnumerable<Addressable
public void OnBeforeSerialize()
{
// m_InitializationObjects, m_DataBuilders, and m_GroupTemplateObjects are serialized by array index
profileSettings.profiles.Sort((a, b) => string.CompareOrdinal(a.id, b.id));
profileSettings.SortProfiles();
m_GroupAssets.Sort((a, b) => string.CompareOrdinal(a?.Guid, b?.Guid));
}

Expand Down
22 changes: 3 additions & 19 deletions Runtime/ResourceManager/ResourceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ internal int InstanceOperationCount
HashSet<InstanceOperation> m_TrackedInstanceOperations = new HashSet<InstanceOperation>();
internal DelegateList<float> m_UpdateCallbacks = DelegateList<float>.CreateWithGlobalCache();
List<IAsyncOperation> m_DeferredCompleteCallbacks = new List<IAsyncOperation>();
HashSet<IResourceProvider> m_AssetBundleProviders = new HashSet<IResourceProvider>();

bool m_InsideExecuteDeferredCallbacksMethod = false;
List<DeferredCallbackRegisterRequest> m_DeferredCallbacksToRegister = null;
Expand Down Expand Up @@ -507,29 +506,14 @@ internal IAsyncOperation GetOperationFromCache(IResourceLocation location, Type
/// <returns>An IOperationCacheKey for use with the async operation cache.</returns>
internal IOperationCacheKey CreateCacheKeyForLocation(IResourceProvider provider, IResourceLocation location, Type desiredType = null)
{
IOperationCacheKey key = null;
bool isAssetBundleProvider = false;
if (provider != null)
{
if (m_AssetBundleProviders.Contains(provider))
isAssetBundleProvider = true;
else if (typeof(AssetBundleProvider).IsAssignableFrom(provider.GetType()))
{
isAssetBundleProvider = true;
m_AssetBundleProviders.Add(provider);
}
}

//Actual key generation has been moved to AssetBundleProvider virtual method to allow provider derived from AssetBundleProvider
//skip calling TransformInternalId method, as it may return different values before and after asset bundle is loaded,
//for example when using Play Asset Delivery for Android.
//For major package release consider this method to be made a part of IResourceProvider to simplify this whole logic.
if (isAssetBundleProvider)
key = (provider as AssetBundleProvider).CreateCacheKeyForLocation(this, location, desiredType);
if(provider is AssetBundleProvider abProvider)
return abProvider.CreateCacheKeyForLocation(this, location, desiredType);
else
key = new LocationCacheKey(location, desiredType);

return key;
return new LocationCacheKey(location, desiredType);
}

Dictionary<Type, Type> m_ProviderOperationTypeCache = new Dictionary<Type, Type>();
Expand Down
20 changes: 16 additions & 4 deletions Runtime/ResourceManager/ResourceProviders/AssetBundleProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,21 @@ private void BeginOperation()
m_DownloadedBytes = 0;
m_RequestCompletedCallbackCalled = false;
GetLoadInfo(m_ProvideHandle, out LoadType loadType, out m_TransformedInternalId);

if (loadType == LoadType.Local)
{
LoadLocalBundle();
//download only bundles loads should not load local bundles
if (m_ProvideHandle.Location is DownloadOnlyLocation)
{
m_Source = BundleSource.Local;
m_RequestOperation = null;
m_ProvideHandle.Complete<AssetBundleResource>(null, true, null);
m_Completed = true;
}
else
{
LoadLocalBundle();
}
return;
}

Expand Down Expand Up @@ -960,7 +971,8 @@ public override void Release(IResourceLocation location, object asset)
throw new ArgumentNullException("location");
if (asset == null)
{
Debug.LogWarningFormat("Releasing null asset bundle from location {0}. This is an indication that the bundle failed to load.", location);
if(!(location is DownloadOnlyLocation))
Debug.LogWarningFormat("Releasing null asset bundle from location {0}. This is an indication that the bundle failed to load.", location);
return;
}

Expand All @@ -984,7 +996,7 @@ internal virtual IOperationCacheKey CreateCacheKeyForLocation(ResourceManager rm
{
//We need to transform the ID first
//so we don't try and load the same bundle twice if the user is manipulating the path at runtime.
return new IdCacheKey(rm.TransformInternalId(location));
return new IdCacheKey(location.GetType(), rm.TransformInternalId(location));
}
}
}
9 changes: 2 additions & 7 deletions Runtime/ResourceManager/ResourceProviders/SceneProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,9 @@ protected override void Execute()
#endif
var unloadOp = SceneManager.UnloadSceneAsync(m_Instance.Scene, m_UnloadOptions);
if (unloadOp == null)
UnloadSceneCompletedNoRelease(null);
UnloadSceneCompleted(null);
else
unloadOp.completed += UnloadSceneCompletedNoRelease;
unloadOp.completed += UnloadSceneCompleted;
}
else
UnloadSceneCompleted(null);
Expand Down Expand Up @@ -266,11 +266,6 @@ private void UnloadSceneCompleted(AsyncOperation obj)
}
}

private void UnloadSceneCompletedNoRelease(AsyncOperation obj)
{
Complete(m_Instance, true, "");
}

protected override float Progress
{
get { return m_sceneLoadHandle.PercentComplete; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public override void Release(IResourceLocation location, object asset)
throw new ArgumentNullException("location");
if (asset == null)
{
Debug.LogWarningFormat("Releasing null asset bundle from location {0}. This is an indication that the bundle failed to load.", location);
if (!(location is DownloadOnlyLocation))
Debug.LogWarningFormat("Releasing null asset bundle from location {0}. This is an indication that the bundle failed to load.", location);
return;
}

Expand Down
8 changes: 5 additions & 3 deletions Runtime/ResourceManager/Util/OperationCacheKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,25 @@ internal interface IOperationCacheKey : IEquatable<IOperationCacheKey>
internal sealed class IdCacheKey : IOperationCacheKey
{
public string ID;
public Type locationType;

public IdCacheKey(string id)
public IdCacheKey(Type locType, string id)
{
ID = id;
locationType = locType;
}

bool Equals(IdCacheKey other)
{
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(other, null)) return false;

return other.ID == ID;
return other.ID == ID && locationType == other.locationType;
}

public override int GetHashCode()
{
return ID.GetHashCode();
return (17 * 31 + ID.GetHashCode()) * 31 + locationType.GetHashCode();
}

public override bool Equals(object obj) => Equals(obj as IdCacheKey);
Expand Down
91 changes: 91 additions & 0 deletions Tests/Editor/ProfileSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,5 +381,96 @@ public void GenerateUniqueName_AlwaysReturnUniqueNames()

Assert.AreEqual(count, list.Distinct().Count());
}

[Test]
public void WhenInvalidDefaultProfileState_GetDefaultProfileDoesNotLogError()
{
Assert.IsNotNull(Settings.profileSettings);

try
{
var idDefault = Settings.profileSettings.Reset();
Settings.profileSettings.AddProfile("A Test Profile", idDefault);
Settings.profileSettings.AddProfile("B Test Profile", idDefault);

List<AddressableAssetProfileSettings.BuildProfile> profiles = Settings.profileSettings.profiles;

var temp = profiles[0];
profiles[0] = profiles[2];
profiles[2] = temp;

var defaultProfile = Settings.profileSettings.GetDefaultProfile();
Assert.IsTrue(defaultProfile != null);
Assert.AreEqual(idDefault, defaultProfile.id, "GetDefaultProfile did not return the default profile.");
}
finally
{
Settings.profileSettings.Reset();
}
}

[Test]
public void WhenNoProfiles_SortDoesNotLogError()
{
Assert.IsNotNull(Settings.profileSettings);

try
{
Settings.profileSettings.profiles.Clear();
Assert.DoesNotThrow(() => Settings.profileSettings.SortProfiles(), "SortProfiles should not log errors when there are no profiles.");
}
finally
{
Settings.profileSettings.Reset();
}
}

[Test]
public void CanSortAndRenameProfiles()
{
Assert.IsNotNull(Settings.profileSettings);
var defaultProfileInitId = Settings.profileSettings.Reset();

string idSuffix = defaultProfileInitId.Substring(1);
string idDefault = '4' + idSuffix;
string idA = '1' + idSuffix;
string idB = '2' + idSuffix;
string idC = '3' + idSuffix;

var defaultProfile = Settings.profileSettings.GetProfile(defaultProfileInitId);
defaultProfile.id = idDefault;

string profileIdB = Settings.profileSettings.AddProfile("B Test Profile", idDefault);
var profileB = Settings.profileSettings.GetProfile(profileIdB);
profileB.id = idB;

string profileIdA = Settings.profileSettings.AddProfile("A Test Profile", idDefault);
var profileA = Settings.profileSettings.GetProfile(profileIdA);
profileA.id = idA;

string profileIdC = Settings.profileSettings.AddProfile("C Test Profile", idDefault);
var profileC = Settings.profileSettings.GetProfile(profileIdC);
profileC.id = idC;

try
{
List<string> profileIds = new List<string> { profileIdC, profileA.id, profileIdB };
profileIds.Sort((a, b) => string.CompareOrdinal(a, b));
Settings.profileSettings.SortProfiles();

var currentProfiles = Settings.profileSettings.profiles;
Assert.AreEqual(idDefault, currentProfiles[0].id, "After all profiles were sorted, the default profile is no longer the first profile.");

Assert.AreEqual(idA, currentProfiles[1].id);
Assert.AreEqual(idB, currentProfiles[2].id);
Assert.AreEqual(idC, currentProfiles[3].id);

Assert.IsTrue(Settings.profileSettings.RenameProfile(profileA, "a test profile"), "After all profiles were sorted, renaming a non-default profile failed.");
}
finally
{
Settings.profileSettings.Reset();
}
}
}
}
36 changes: 36 additions & 0 deletions Tests/Runtime/AddressablesImplTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,42 @@ public IEnumerator AddressablesImpl_DownloadDependenciesAsync_CanDownloadDepende
op.Release();
}

[UnityTest]
public IEnumerator AddressablesImpl_DownloadDependenciesAsync_CanLoadFromCompletionEvent()
{
// Setup
yield return Init();

if (TypeName == "BuildScriptFastMode")
{
Assert.Ignore($"Skipping test {nameof(AddressablesImpl_DownloadDependenciesAsync_CanLoadFromCompletionEvent)} for {TypeName}");
}
#if ENABLE_CACHING
Caching.ClearCache();
#endif
AsyncOperationHandle op = m_Addressables.DownloadDependenciesAsync(m_PrefabKeysList[0]);

AsyncOperationStatus loadStatus = AsyncOperationStatus.None;

op.Completed += (o) =>
{
m_Addressables.LoadAssetAsync<GameObject>(m_PrefabKeysList[0]).Completed += (o2) =>
{
loadStatus = o2.Status;
o2.Release();
};
};
yield return op;
while (loadStatus == AsyncOperationStatus.None)
yield return null;

Assert.IsTrue(loadStatus == AsyncOperationStatus.Succeeded);

// Cleanup
op.Release();
}


[UnityTest]
public IEnumerator AddressablesImpl_DownloadDependenciesAsync_CantDownloadWhenGetResourceLocFailsKey()
{
Expand Down
1 change: 1 addition & 0 deletions Tests/Runtime/AddressablesIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public virtual void DeleteTempFiles()
[TearDown]
public void TearDown()
{
AssetBundleProvider.WaitForAllUnloadingBundlesToComplete();
if (m_Addressables != null)
{
Assert.AreEqual(0, m_Addressables.ResourceManager.DeferredCompleteCallbacksCount);
Expand Down
Loading

0 comments on commit e9c165d

Please sign in to comment.