Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved Check if address (or key combination) exists?

Discussion in 'Addressables' started by Noblauch, Jun 11, 2020.

  1. Noblauch

    Noblauch

    Joined:
    May 23, 2017
    Posts:
    256
    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: https://forum.unity.com/threads/can...ecified-key-addressable-name-is-valid.574033/
    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
     
  2. Tayfe

    Tayfe

    Joined:
    Nov 21, 2011
    Posts:
    103
    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.
     
    Noblauch likes this.
  3. Noblauch

    Noblauch

    Joined:
    May 23, 2017
    Posts:
    256
    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+CompletedOperation`1[System.Collections.Generic.IList`1[UnityEngine.Sprite]], result='', status='Failed': Exception of type 'UnityEngine.AddressableAssets.InvalidKeyException' was thrown., Key=System.Collections.Generic.List`1[System.Object], Type=UnityEngine.Sprite
    UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationBase`1:<.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?
     
  4. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    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.
     
  5. Tayfe

    Tayfe

    Joined:
    Nov 21, 2011
    Posts:
    103
    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:

    Anmerkung 2020-06-12 103746.png

    You can also find more information on this probleme in this thread: https://forum.unity.com/threads/how-to-handle-invalidkeyexception.738848/#post-5968007
     
  6. HugoClip

    HugoClip

    Joined:
    Feb 28, 2018
    Posts:
    52
    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.

    Code (CSharp):
    1.         internal bool GetResourceLocations(IEnumerable<object> keys, Type type, Addressables.MergeMode merge, out IList<IResourceLocation> locations)
    2.         {
    3.             locations = null;
    4.             HashSet<IResourceLocation> current = null;
    5.             foreach (var key in keys)
    6.             {
    7.                 IList<IResourceLocation> locs;
    8.                 if (GetResourceLocations(key, type, out locs))
    9.                 {
    10.                     if (locations == null)
    11.                     {
    12.                         locations = locs;
    13.                         if (merge == Addressables.MergeMode.UseFirst)
    14.                             return true;
    15.                     }
    16.                     else
    17.                     {
    18.                         if (current == null)
    19.                         {
    20.                             current = new HashSet<IResourceLocation>();
    21.                             foreach (var loc in locations)
    22.                                 current.Add(loc);
    23.                         }
    24.  
    25.                         if (merge == Addressables.MergeMode.Intersection)
    26.                             current.IntersectWith(locs);
    27.                         else if (merge == Addressables.MergeMode.Union)
    28.                             current.UnionWith(locs);
    29.                     }
    30.                 }
    31.                 else
    32.                 {
    33.                     //if entries for a key are not found, the intersection is empty
    34.                     if (merge == Addressables.MergeMode.Intersection)
    35.                     {
    36.                         locations = null;
    37.                         return false;
    38.                     }
    39.                 }
    40.             }
    41.  
    42.             if (current == null)
    43.                 return locations != null;
    44.             if (current.Count == 0)
    45.             {
    46.                 locations = null;
    47.                 return false;
    48.             }
    49.             locations = new List<IResourceLocation>(current);
    50.             return true;
    51.         }
    52.  
    53.  
    Of course this assumes that the system is already initialized.
     
  7. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    Oh yeah, so all they need to do is change that
    internal
    to
    public
    !
     
  8. Noblauch

    Noblauch

    Joined:
    May 23, 2017
    Posts:
    256
    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..
    Code (CSharp):
    1.                     var validateAddress = Addressables.LoadResourceLocationsAsync(new List<object> {address, skin.skinLabel.labelString});
    2.                     await validateAddress.Task;
    3.                     if (validateAddress.Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded)
    4.                     {
    5.                         if (validateAddress.Result.Count > 0)
    6.                         {
    7.                             foreach (var loc in validateAddress.Result)
    8.                                 Log.Print(loc.PrimaryKey, "nob");
    9.                         }
    10.                         else
    11.                         {
    12.                             Log.Print("No: " + address + " " + skin.skinLabel.labelString, "nob");
    13.                         }
    14.                     }
    15.                     else
    16.                     {
    17.                         Log.Print("Nope", "nob");
    18.                     }
    This always gives me "No" this can't be, I'm raging!
     
    OneManArmy3D likes this.
  9. Noblauch

    Noblauch

    Joined:
    May 23, 2017
    Posts:
    256
    I guess I'm onto something, but it seems like a workaround, still:
    Code (CSharp):
    1. var validateAddress = Addressables.LoadResourceLocationsAsync(new List<object> {address, label}, Addressables.MergeMode.Intersection);
    2. await validateAddress.Task;
    3. if (validateAddress.Status == UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationStatus.Succeeded)
    4. {
    5.     if (validateAddress.Result.Count > 0)
    6.     {
    7.         foreach (var loc in validateAddress.Result)
    8.             Debug.Log("Found: " + loc.PrimaryKey);
    9.     }
    10.     else
    11.     {
    12.         Debug.Log("Didn't find: " + address + " " + label)
    13.     }
    14. }