Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#nullable enable

using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
Expand Down Expand Up @@ -53,41 +54,83 @@ public AddressableObjectReference(string address)
_address = address ?? throw new ArgumentNullException(nameof(address), "Address cannot be null.");
}

public async ValueTask<T> LoadAsync(CancellationToken cancellationToken)
public ValueTask<T> LoadAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!_handle.IsValid())
if (_handle.IsValid() && _handle.Status == AsyncOperationStatus.Succeeded)
{
_handle = Addressables.LoadAssetAsync<T>(_address);
return new ValueTask<T>(_handle.Result);
}

return await _handle.ToUniTask(cancellationToken: cancellationToken, autoReleaseWhenCanceled: true);
return LoadAsyncCore(this, cancellationToken);

static async ValueTask<T> LoadAsyncCore(AddressableObjectReference<T> reference, CancellationToken cancellationToken)
{
if (!reference._handle.IsValid())
{
reference._handle = Addressables.LoadAssetAsync<T>(reference._address);
}

try
{
return await reference._handle.ToUniTask(cancellationToken: cancellationToken);
}
catch
{
reference.Release();
throw;
}
}
}

/// <inheritdoc />
public async ValueTask<T> LoadAsync(IProgress<float> progress, CancellationToken cancellationToken)
public ValueTask<T> LoadAsync(IProgress<float> progress, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!_handle.IsValid())
if (_handle.IsValid() && _handle.Status == AsyncOperationStatus.Succeeded)
{
_handle = Addressables.LoadAssetAsync<T>(_address);
progress.Report(1.0f);
return new ValueTask<T>(_handle.Result);
}

var result = await _handle.ToUniTask(progress: progress, cancellationToken: cancellationToken, autoReleaseWhenCanceled: true);
progress.Report(1.0f);
return result;
return LoadAsyncCore(this, progress, cancellationToken);

static async ValueTask<T> LoadAsyncCore(AddressableObjectReference<T> reference, IProgress<float> progress, CancellationToken cancellationToken)
{
if (!reference._handle.IsValid())
{
reference._handle = Addressables.LoadAssetAsync<T>(reference._address);
}

T result;
try
{
result = await reference._handle.ToUniTask(progress: progress, cancellationToken: cancellationToken);
}
catch
{
reference.Release();
throw;
}

progress.Report(1.0f);
return result;
}
}

/// <inheritdoc />
public void Dispose()
public void Dispose() => Release();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Release()
{
if (_handle.IsValid())
{
Addressables.Release(_handle);
_handle.Release();
_handle = default;
}
}
}
}

#endif
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#nullable enable

using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
Expand All @@ -18,38 +19,74 @@ internal sealed class SerializableAddressableObjectReference<T> : IObjectReferen
private T? _cached;

/// <inheritdoc />
public async ValueTask<T> LoadAsync(CancellationToken cancellationToken)
public ValueTask<T> LoadAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (_cached == null)
if (_cached != null)
{
_cached = await _value.LoadAssetAsync<T>().ToUniTask(cancellationToken: cancellationToken, autoReleaseWhenCanceled: true);
return new ValueTask<T>(_cached);
}

return LoadAsyncCore(this, cancellationToken);

static async ValueTask<T> LoadAsyncCore(SerializableAddressableObjectReference<T> reference, CancellationToken cancellationToken)
{
try
{
reference._cached = await reference._value.LoadAssetAsync<T>().ToUniTask(cancellationToken: cancellationToken);
return reference._cached;
}
catch
{
reference.Release();
throw;
}
}
return _cached;
}

/// <inheritdoc />
public async ValueTask<T> LoadAsync(IProgress<float> progress, CancellationToken cancellationToken)
public ValueTask<T> LoadAsync(IProgress<float> progress, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (_cached == null)
if (_cached != null)
{
progress.Report(1.0f);
return new ValueTask<T>(_cached);
}

return LoadAsyncCore(this, progress, cancellationToken);

static async ValueTask<T> LoadAsyncCore(SerializableAddressableObjectReference<T> reference, IProgress<float> progress, CancellationToken cancellationToken)
{
_cached ??= await _value.LoadAssetAsync<T>().ToUniTask(progress: progress, cancellationToken: cancellationToken, autoReleaseWhenCanceled: true);
try
{
reference._cached = await reference._value.LoadAssetAsync<T>().ToUniTask(progress: progress, cancellationToken: cancellationToken);
}
catch
{
reference.Release();
throw;
}

progress.Report(1.0f);
return reference._cached;
}
progress.Report(1.0f);
return _cached;
}

/// <inheritdoc />
public void Dispose()
public void Dispose() => Release();

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Release()
{
if (_cached != null)
if (_value.OperationHandle.IsValid())
{
_value.ReleaseAsset();
_cached = null;
}

_cached = null;
}
}
}

#endif
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ internal sealed class ObjectReferenceTestEnvironment : IDisposable
{
public const string CubeAddress = "objectreference-tests-cube";
public const string MaterialAddress = "objectreference-tests-material";
public const string MissingAddress = "objectreference-tests-missing";
public const string CubeGuid = "11111111111111111111111111111111";
public const string MaterialGuid = "22222222222222222222222222222222";
public const string MissingGuid = "33333333333333333333333333333333";

private const BindingFlags InstanceFieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

Expand Down Expand Up @@ -70,11 +72,13 @@ public ObjectReferenceTestEnvironment()
});
Addressables.ResourceManager.ResourceProviders.Add(_provider);

_locator = new ResourceLocationMap("ObjectReferenceTests", 4);
_locator = new ResourceLocationMap("ObjectReferenceTests", 6);
AddLocation(CubeAddress, typeof(GameObject));
AddLocation(MaterialAddress, typeof(Material));
AddLocation(MissingAddress, typeof(GameObject));
AddLocation(CubeGuid, typeof(GameObject));
AddLocation(MaterialGuid, typeof(Material));
AddLocation(MissingGuid, typeof(GameObject));
Addressables.AddResourceLocator(_locator);
}

Expand All @@ -92,6 +96,24 @@ public IObjectReference<T> CreateSerializableAddressableReference<T>(string guid
"SerializableAddressableObjectReference`1",
new AssetReferenceT<T>(guid));

public bool HasValidOperationHandle<T>(IObjectReference<T> reference)
where T : UnityEngine.Object
{
var valueField = reference.GetType().GetField("_value", InstanceFieldFlags)
?? throw new MissingFieldException(reference.GetType().FullName, "_value");
var assetReference = (AssetReference)valueField.GetValue(reference)!;
return assetReference.OperationHandle.IsValid();
}

public bool HasValidOperationHandle<T>(AddressableObjectReference<T> reference)
where T : UnityEngine.Object
{
var handleField = typeof(AddressableObjectReference<T>).GetField("_handle", InstanceFieldFlags)
?? throw new MissingFieldException(typeof(AddressableObjectReference<T>).FullName, "_handle");
var handle = (AsyncOperationHandle<T>)handleField.GetValue(reference)!;
return handle.IsValid();
}

public void Dispose()
{
Addressables.RemoveResourceLocator(_locator);
Expand Down Expand Up @@ -171,4 +193,4 @@ private async UniTaskVoid CompleteAsync(ProvideHandle provideHandle)
provideHandle.Complete(value, status: true, exception: null);
}
}
}
}
Loading
Loading