Check if address (or key combination) exists?

I am struggling for a few days now to find a way to synchronously find out if my address or key pairs (address, labels) are valid.

I always come back to this post: Can I ask Addressables if a specified key (addressable name) is valid?
Where the solution by unity is stated to use Addressables.LoadAsset<IResourceLocation>("key");

Problem is that this method is:

  • obsolete
  • only takes one key

I have to check this because we are using multiple skins and have a fallback skin. If the requested asset doesn’t exist we want to load the fallback skin for this element, which is always guaranteed to be there.

In a ideal world I would be calling:
Addressables.DoesAssetExist<Sprite>(new List<object> { "Background" , "Skin2" });
Where “Background” would be the address and “Skin2” the label

What about using Addressables.LoadAssetAsync, wait until it’s done using AsyncOperationHandle<T>.isDone and then check whether it was possible to load the Asset using AsyncOperationHandle<T>.status? If it failed, you can try to load the fallback skin and if you suceeded you’re already done.

1 Like

That is kind of the approach I’m going for right now. Thank you for the suggestion, makes me more confident in doing so ^^ I’ll try out the status approach, I just checked if the handle.Result was null.
But I’m getting spammed with ugly exceptions like ```
Exception encountered in operation UnityEngine.ResourceManagement.ResourceManager+CompletedOperation1[System.Collections.Generic.IList1[UnityEngine.Sprite]], result=‘’, status=‘Failed’: Exception of type ‘UnityEngine.AddressableAssets.InvalidKeyException’ was thrown., Key=System.Collections.Generic.List1[System.Object], Type=UnityEngine.Sprite UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase1:<.ctor>b__33_0(AsyncOperationHandle)


Those will still appear when I try to load an address that doesn't exist right? Any ideas to work around that?

My post in that thread you linked shows how I cache the keys from initialization to check if an address exists. Since then, I have implemented a build script that rolls through all asset entries and builds a dictionary of keys to labels so I can lookup the label from the key. I save that dictionary to json in a remote location (next to the catalog), then while I’m initializing addressables, I also download and deserialize that json to dictionary so I can do a synchronous lookup. That json can be updated at the same time as the content catalog if necessary.

Of course, Addressables already has that information internally, so I’m doing a lot of workaround for something they could easily just expose. IReadOnlyCollection<string> Addressables.GetLabels(object key) would be nice. Or even IResourceLocation Addressables.GetResourceLocation(object key) then add IReadOnlyCollection<string> IResourceLocation.Labels. I’m not even sure how we’re supposed to access or use IResourceLocations with the API as it is…

As for your issue about getting spammed errors, you can disable that in the top level addressable settings then go about your business.

Yes they still appear because the exceptions are not thrown but just logged instead. Luckily there is indeed a very simple workaround: Turn off “Log runtime exceptions” in AddressablesAssetSettings:

5971049--640742--Anmerkung 2020-06-12 103746.png

You can also find more information on this probleme in this thread: https://discussions.unity.com/t/756913/9

If you really want to load the asset, LoadAssetAssync is not a bad choice. But if you only want to check for the existence of the key, a better approach would be to use the method in AddressablesImpl.cs.

        internal bool GetResourceLocations(IEnumerable<object> keys, Type type, Addressables.MergeMode merge, out IList<IResourceLocation> locations)
        {
            locations = null;
            HashSet<IResourceLocation> current = null;
            foreach (var key in keys)
            {
                IList<IResourceLocation> locs;
                if (GetResourceLocations(key, type, out locs))
                {
                    if (locations == null)
                    {
                        locations = locs;
                        if (merge == Addressables.MergeMode.UseFirst)
                            return true;
                    }
                    else
                    {
                        if (current == null)
                        {
                            current = new HashSet<IResourceLocation>();
                            foreach (var loc in locations)
                                current.Add(loc);
                        }

                        if (merge == Addressables.MergeMode.Intersection)
                            current.IntersectWith(locs);
                        else if (merge == Addressables.MergeMode.Union)
                            current.UnionWith(locs);
                    }
                }
                else
                {
                    //if entries for a key are not found, the intersection is empty
                    if (merge == Addressables.MergeMode.Intersection)
                    {
                        locations = null;
                        return false;
                    }
                }
            }

            if (current == null)
                return locations != null;
            if (current.Count == 0)
            {
                locations = null;
                return false;
            }
            locations = new List<IResourceLocation>(current);
            return true;
        }

Of course this assumes that the system is already initialized.

Oh yeah, so all they need to do is change that internal to public!

I still, to that day, haven’t found a way to overcome this, oh my god!

We went for “Load, see if something comes back, if not, load something else” way.
First of all, the “Log Runtime Exceptions” flag in the Addressables settings activates itself randomly (of course we had to uncheck it to don’t get spammed in the console).
But the team now decided to keep that checked, because it mutes potential problems elsewhere (which I understand).
I tried the whole bank.

I tried to create my own helper class that wraps these functions, but as you try you see that it depends on all kinds of other stuff that is only internal or private. And I can’t change the Addressables classes because we would never be able to update the package.

Unity, there MUST be a way to ask the system if a combination of keys exist. Make it async, I don’t care anymore, but we can’t work with it how it is.

I tried…

                    var validateAddress = Addressables.LoadResourceLocationsAsync(new List<object> {address, skin.skinLabel.labelString});
                    await validateAddress.Task;
                    if (validateAddress.Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded)
                    {
                        if (validateAddress.Result.Count > 0)
                        {
                            foreach (var loc in validateAddress.Result)
                                Log.Print(loc.PrimaryKey, "nob");
                        }
                        else
                        {
                            Log.Print("No: " + address + " " + skin.skinLabel.labelString, "nob");
                        }
                    }
                    else
                    {
                        Log.Print("Nope", "nob");
                    }

This always gives me “No” this can’t be, I’m raging!

1 Like

I guess I’m onto something, but it seems like a workaround, still:

var validateAddress = Addressables.LoadResourceLocationsAsync(new List<object> {address, label}, Addressables.MergeMode.Intersection);
await validateAddress.Task;
if (validateAddress.Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded)
{
    if (validateAddress.Result.Count > 0)
    {
        foreach (var loc in validateAddress.Result)
            Debug.Log("Found: " + loc.PrimaryKey);
    }
    else
    {
        Debug.Log("Didn't find: " + address + " " + label)
    }
}