Search Unity

  1. The 2022.1 beta is now available for testing. To find out what's new, have a look at our 2022.1 beta blog post.
    Dismiss Notice

Change Remote Catalog Load Path at Runtime

Discussion in 'Addressables' started by LarsGameloft, Jul 17, 2019.

  1. LarsGameloft

    LarsGameloft

    Joined:
    Jun 4, 2019
    Posts:
    17
    Hi, I am trying to add a cheat to a client app that lets you change the url of the catalog at runtime. For example:

    I "build Player Content" and upload it to bucket 1, the client is set to look for the catalog in bucket 1.
    then I make some changes to the content and do a "build for content update" and upload the new files and catalog to bucket 2.

    Now with the cheat set to bucket 2 url I want the app to get the bundles from bucket 2.

    I tried with LoadContentCatalogAsync using the url of the json catalog in bucket 2, nothing happens, if I remove my assets from bucket 1 the app complains that it cannot find them in bucket 1. Do I have to clean up my catalog data then call LoadContentCatalogAsync? or refresh it after?

    I do this so we can test a client version (possibly live) against a possible content update without touching the original set of data or catalog.

    Thank you.
     
    M_R likes this.
  2. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    There are a couple ways to do this. Most involve setting your RemoteLoadPath to include a variable you define yourself. Note that we control local builds by setting the load path to
    {UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget]
    The [] brackets are evaluated at build time. the {} at runtime. So if you declared some string MyApp.Something.URL, you could set your load path to:
    {MyApp.Something.URL}/whatever (maybe [BuildTarget] or not).

    Addressables does a lazy-init. It won't try to load a catalog until you've asked Addressables to do something (Addressables.Load*, or even Addressables.Initialize). So as long as you set your variable before you initialize, you're fine.


    From here you have the choice of how you set MyApp.Something.URL. At the fancy end of the spectrum, you ping some server with info about your device, and the server tells you where the content is. At the less fancy end, you have the code default to "bucketProduction", but check for some on-device file that tells it an alternate "bucketQA" or "bucketStaging".
     
  3. LarsGameloft

    LarsGameloft

    Joined:
    Jun 4, 2019
    Posts:
    17
    Thank you , I think I can use this to get what I need. This includes my other post which I cannot link because I get a warning that I may be spamming.

    Update: Yes it works perfectly, thank you for the help.
     
    Last edited: Jul 22, 2019
    unity_bill likes this.
  4. deepakguptaPR

    deepakguptaPR

    Joined:
    Aug 20, 2019
    Posts:
    12
    @LarsGameloft : Could you please provide snippet of code how you are able to load content from the url? I am trying to do it by using Addressables.LoadContentCatalogAsync([my cdn url]/catalog.json), I am getting the following error:
    Code (CSharp):
    1. Exception encountered in operation Resource<ContentCatalogData>(catalog_2019.09.24.13.06.53.json): Unknown error in AsyncOperation
    2. UnityEngine.AsyncOperation:InvokeCompletionEvent()
     
  5. better_walk_away

    better_walk_away

    Joined:
    Jul 12, 2016
    Posts:
    190
    Hi, I was able to change the remote load path to a different folder, but Addressables was still trying to load the original bundle files. Each version of bundles has different hash code on its file name, because Addressable was still trying to load the old file name in a different folder, Addressables cannot find it. I noticed that when Addressables is initialized, it loads the catalog file that is inside the apk, and it was trying to use the hash codes of that catalog to find the bundles with those exact hash codes.
    For example,
    I have a dog image in my scene, I build the dog image as an Addressable bundle and its file name is "image_xxx.bundle", this bundle is in folder "0.1" on my http server, "xxx" represents the random hash code that Unity generated. Then I change the image to a cat image, I build the image as an Addressable bundle again, this time, because I changed the image, Unity generated a new hash code for it and its file name becomes "image_yyy.bundle", it is in folder "0.2".
    After I build the apk, I run the game, Addressables will load the catalog in the apk, and try to find a bundle with the hash code "yyy". But now I want Addressables to load "image_xxx.bundle" which is in folder "0.1", I was able to change the remote load path using my variable but Addressables was still trying to load "image_yyy.bundle" in folder "0.1", so it throws an error saying that it cannot find the bundle.

    How can I address this issue? Thanks in advance.
     
    mandisaw likes this.
  6. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    1,053
  7. Atlantiss

    Atlantiss

    Joined:
    Aug 5, 2018
    Posts:
    14
    Is it possible to change the remote load path or catalog file even after the lazy init of addressables?
     
  8. errormee

    errormee

    Joined:
    Dec 26, 2014
    Posts:
    4
    How to change my custom “Variable” at runtime?
    just like "DevAssetCDN"
    企业微信截图_15794350038763.png
     
  9. Pdpalma

    Pdpalma

    Joined:
    Aug 14, 2014
    Posts:
    11
    Hey everyone,
    I'm trying load a Catalog Paht Remote using this line:

    Code (CSharp):
    1.     AsyncOperationHandle<IResourceLocator> handle = Addressables.LoadContentCatalogAsync(catalogPath);
    The url i'm using it's: gs://addressablesbucket

    But i'm having the following error:

    Curl error 1: Protocol "gs" not supported or disabled in libcurl


    I'm not sure if LoadContentCatalogAsync loads the ServerData content (hash and json files) or it's other files that i need load.

    Full code:

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UnityEngine.AddressableAssets;
    4. using UnityEngine.AddressableAssets.ResourceLocators;
    5. using UnityEngine.ResourceManagement.AsyncOperations;
    6. using UnityEngine.ResourceManagement.ResourceLocations;
    7.  
    8. public class AssetLoader : MonoBehaviour
    9. {
    10.     public string catalogPath;
    11.     public List<IResourceLocation> locations;
    12.  
    13.     void Start()
    14.     {
    15.         initAddressables();
    16.     }
    17.  
    18.  
    19.     void initAddressables()
    20.     {
    21.         Debug.Log("initAddressables");
    22.         AsyncOperationHandle<IResourceLocator> handle = Addressables.InitializeAsync();
    23.         handle.Completed += initDone;
    24.     }
    25.     private void initDone(AsyncOperationHandle<IResourceLocator> obj)
    26.     {
    27.         Debug.Log("Initialization Complete ==> " + obj.Status);
    28.         if (obj.Status == AsyncOperationStatus.Succeeded)
    29.         {
    30.             loadCatalog();
    31.         }
    32.     }
    33.  
    34.     void loadCatalog()
    35.     {
    36.         Debug.Log("loadCatalog");
    37.         AsyncOperationHandle<IResourceLocator> handle = Addressables.LoadContentCatalogAsync(catalogPath);
    38.         handle.Completed += loadCatalogsCompleted;
    39.     }
    40.     void loadCatalogsCompleted(AsyncOperationHandle<IResourceLocator> obj)
    41.     {
    42.         Debug.Log("loadCatalogsCompleted ==> " + obj.Status);
    43.         if (obj.Status == AsyncOperationStatus.Succeeded)
    44.         {
    45.             loadResourceLocation();
    46.         }
    47.         else
    48.         {
    49.             Debug.LogError("LoadCatalogsCompleted is failed");
    50.         }
    51.     }
    52.  
    53.     void loadResourceLocation()
    54.     {
    55.         Debug.Log("loadResourceLocation");
    56.         AsyncOperationHandle<IList<IResourceLocation>> handle = Addressables.LoadResourceLocationsAsync("remote");
    57.         handle.Completed += locationsLoaded;
    58.     }
    59.     void locationsLoaded(AsyncOperationHandle<IList<IResourceLocation>> obj)
    60.     {
    61.         Debug.Log("locationsLoaded ==> " + obj.Status);
    62.         if (obj.Status == AsyncOperationStatus.Succeeded)
    63.         {
    64.             locations = new List<IResourceLocation>(obj.Result);
    65.             loadDependency();
    66.         }
    67.         else
    68.         {
    69.             Debug.LogError("locationsLoaded is failed");
    70.         }
    71.     }
    72.  
    73.     void loadDependency()
    74.     {
    75.         Debug.Log("loadDependency");
    76.         AsyncOperationHandle handle = Addressables.DownloadDependenciesAsync("decor");
    77.         handle.Completed += dependencyLoaded;
    78.     }
    79.     void dependencyLoaded(AsyncOperationHandle obj)
    80.     {
    81.         Debug.Log("dependencyLoaded ==> " + obj.Status);
    82.         if (obj.Status == AsyncOperationStatus.Succeeded)
    83.         {
    84.             loadAssets();
    85.         }
    86.         else
    87.         {
    88.             Debug.LogError("dependencyLoaded is Failed");
    89.         }
    90.     }
    91.  
    92.     private void loadAssets()
    93.     {
    94.         AsyncOperationHandle<IList<GameObject>> handle = Addressables.LoadAssetsAsync<GameObject>(locations, onAssetsCategoryLoaded);
    95.         handle.Completed += onAssetsLoaded;
    96.     }
    97.     private void onAssetsCategoryLoaded(GameObject obj)
    98.     {
    99.         SpawnItem(obj.name);
    100.     }
    101.     private void onAssetsLoaded(AsyncOperationHandle<IList<GameObject>> obj)
    102.     {
    103.     }
    104.  
    105.     void SpawnItem(string addressableKey)
    106.     {
    107.         Debug.Log("SpawnItem ==> " + addressableKey);
    108.         AsyncOperationHandle<GameObject> asyncLoad = Addressables.InstantiateAsync(addressableKey, Vector3.zero, Quaternion.identity);
    109.         StartCoroutine(progressAsync(asyncLoad));
    110.         asyncLoad.Completed += assetSpawned;
    111.     }
    112.     void SpawnItem(GameObject addressableObj)
    113.     {
    114.         Debug.Log("SpawnItem ==> " + addressableObj);
    115.         AsyncOperationHandle<GameObject> asyncLoad = Addressables.InstantiateAsync(addressableObj);
    116.         StartCoroutine(progressAsync(asyncLoad));
    117.         asyncLoad.Completed += assetSpawned;
    118.     }
    119.     private System.Collections.IEnumerator progressAsync(AsyncOperationHandle<GameObject> asyncOperation)
    120.     {
    121.         float percentLoaded = asyncOperation.PercentComplete;
    122.         while (!asyncOperation.IsDone)
    123.         {
    124.             Debug.Log("Progress = " + percentLoaded + "%");
    125.             yield return 0;
    126.         }
    127.         Debug.Log("Progress Done= " + percentLoaded + "%");
    128.     }
    129.     void assetSpawned(AsyncOperationHandle<GameObject> obj)
    130.     {
    131.         Debug.Log("Instantiate completed ==> " + obj.Status);
    132.     }
    133. }
    Thanks.
     
    mango_khan likes this.
  10. mango_khan

    mango_khan

    Joined:
    Oct 10, 2016
    Posts:
    5
    tonialatalo likes this.
  11. Pdpalma

    Pdpalma

    Joined:
    Aug 14, 2014
    Posts:
    11

    Attached Files:

    gale_murakami likes this.
  12. KBaxtrom-LevelEx

    KBaxtrom-LevelEx

    Joined:
    Feb 5, 2018
    Posts:
    28
    Overall relying on the RemoteLoadPath to do the right thing is pretty frustrating and seems brittle. It's hard to debug what's going wrong and the system seems to keep falling back to something it's loaded before. Can I choose and load the catalog myself and then initialize the Addressable with the loaded catalog?

    @unity_bill
     
    mandisaw, ImpossibleRobert and pahe like this.
  13. pahe

    pahe

    Joined:
    May 10, 2011
    Posts:
    506
    @KBaxtrom-LevelEx exactly my obversations currently :)

    You could download the catalog with Addressables.LoadContentCatalogAsync(url); and use the new links provided in there. There are a couple of drawbacks though. But besides that, I would like to be able, like you, to have a clean addressables system and then feed my catalog(s) into it, so I know which assets would be available from the catalog. Currently the addressables system seems to me like a ball of mud which gets bigger and bigger with each update of the catalogs.

    @unity_bill It would be nice if we could access the download of the async loading operation to figure some things out, like which url is used, get a real progress of the download,...
     
  14. Gamrek

    Gamrek

    Joined:
    Sep 28, 2010
    Posts:
    154

    Is there any way to make the [BuildTarget] into lower case? It is because I am using Azure and path need to be lower case.
     
    sujanmishra likes this.
  15. KBaxtrom-LevelEx

    KBaxtrom-LevelEx

    Joined:
    Feb 5, 2018
    Posts:
    28
    Following up on this thread. We ended up using this as our method. We just fully set the remotepath from code and don't rely on any parts of the auto population bits.

    {MyNameSpace.AddressablesWrapper.BASE_URL}/[BuildTarget]


    namespace MyNameSpace{
    class AddressablesWrapper {
    public static string BASE_URL = AddressablesConfig.DEFAULT_URL;
    }
    }
     
  16. DennisWardAltair

    DennisWardAltair

    Joined:
    Apr 4, 2019
    Posts:
    27
    I also found that the platform name such as "StandaloneWindows" is incompatible with Azure. I pasted the path from azure as lower case for the catalog.json file and was able to see the text correcty ("standalonewindows"), but when I used the mixed case version "StandaloneWindows", I get the 400 error. I tried another CDN that allowed folders to have mixed case and had no problem. Am I missing something?
     
  17. DennisWardAltair

    DennisWardAltair

    Joined:
    Apr 4, 2019
    Posts:
    27
    Ok... I found that the container name has a lower-case requirement in Azure, but there can be subfolders below that which have properly cased names for the platform such as "StandaloneWindows". It worked for me after I placed the platform directories as subfolders of a lower-case container name. It also makes more sense to me to do it this way as the same container can contain sub-folders for all platforms.
     
    Last edited: Aug 6, 2020
  18. LessThanEpic

    LessThanEpic

    Joined:
    Aug 22, 2014
    Posts:
    13
    Did anybody ever figure out if you can change this after the lazy init? It would be really nice to have an admin setting for this in our app that users can change but it's very likely that we'd already be initialized by the time they change the setting. It would also be very useful for the users/testers to be able to toggle the setting back and forth and see the different behavior without needing to restart the app.
     
    Malicjusz and MurphyMurph_21 like this.
  19. MurphyMurph_21

    MurphyMurph_21

    Joined:
    Jul 3, 2020
    Posts:
    10
    this post might help you
    How-to Host Addressable Assets with an Azure Blob | by Badger Dox | Medium
     
  20. Lalitsharma81

    Lalitsharma81

    Joined:
    Jun 19, 2020
    Posts:
    13
    Hey did you find any solution for changing the runtime path variable after lazy init?
     
    mrsorry78 likes this.
  21. spreddy_unity

    spreddy_unity

    Joined:
    Nov 13, 2020
    Posts:
    4
    @unity_bill Is there a way to load asset of local group to load from remote after app release using Curly brackets in load path or loading remote catalog.
     
  22. kactus223

    kactus223

    Joined:
    May 20, 2014
    Posts:
    35
    ^ I'm looking for the same thing.
     
  23. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    74
    You cannot change the runtime path after init using the {}s since that is computed during init. There is a callback that allows for custom processing of the internal ids that you could use. This gets called on the url right before web requests are sent. this is set via Addressables.ResourceManager.InternalIdTransformFunc.
     
    levykin likes this.
  24. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    74
    I don't quite understand your question, can you give some context of what you are trying to achieve?
     
  25. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    74
    One trick that can be used is to define public static variables and embed them in your profile variables. these will be evaluated via reflection during the build and runtime init. Beware of code stripping for runtime variables though. Make sure you have some executed code reference your variable or create a link.xml file in your project that references it so that it does not get stripped.
     
    KwahuNashoba likes this.
  26. Malicjusz

    Malicjusz

    Joined:
    Jan 26, 2015
    Posts:
    26
    Does this mean that If there is any addressables init (for example when loading local addressable assets for a loading screen or something like that) and then I load a remote catalog via
    Addressables.LoadContentCatalogAsync, then that catalogs remote load path cannot use {} ? Or is the initialization separate for each catalog file, and you can't THEN change the runtime path, after the catalog was used?
     
  27. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    74
    Yes, it would be per catalog load - all of the string stuff is done then to reduce GC allocations later when loading
     
    Malicjusz likes this.
  28. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    458
    @unity_bill and @PaulBurslem

    I did set RemoteLoadPath in Default profile as
    {PhoenixHttpClient.Instance.BaseUrl}/api/v2/gemini_assets/[BuildTarget]

    In playmode (with "use existing build" option) I get this error:

    Exception encountered in operation Resource<IAssetBundleResource>(packedassets_scenes_all_00984e604235a51eafb54ed27a29bb56.bundle): Invalid path in AssetBundleProvider: 'PhoenixHttpClient.Instance.BaseUrl/api/v2/gemini_assets/WebGL/packedassets_scenes_all_00984e604235a51eafb54ed27a29bb56.bundle'.

    It looks like {} part of RemoteLoadPath variable isn't populated correctly using the value of existing PhoenixHttpClient.Instance.BaseUrl class property.

    Can you help with this please?
     
  29. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    458
    Any help please?
     
  30. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    458
    Someone suggested a possible solution on Unity Slack. I'll try that.

    romaha
    Just a guess - you are referencing to Instance of PhoenixHttpClient class, maybe that’s the problem. Try to make BaseUrl static variable

    xtro
    Yea. Maybe they didn't implement it to handle object instances. I'll try that.
     
  31. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    458
    Yes. That fixed my problem.

    Adressable profile variables only support static fields; not instance fields.
     
  32. DrStoned

    DrStoned

    Joined:
    Sep 28, 2020
    Posts:
    2
    I am getting same error, can you send your code snippet please?
     
  33. Xtro

    Xtro

    Joined:
    Apr 17, 2013
    Posts:
    458
    There isn't much code for this.

    As I mentioned before, Adressables profile variables only support static fields.

    This is how my profile variable looks like:
    upload_2021-8-6_11-12-18.png

    And this is how I defined BaseUrl static field in the relevant class:
    upload_2021-8-6_11-16-5.png

    I set the value of _baseUrl to the address of our server before loading the scene via the addressables system.

    I hope this is helpful to you.
     
    ggzerosum likes this.
  34. DrStoned

    DrStoned

    Joined:
    Sep 28, 2020
    Posts:
    2
    Thank you for your quick response, I've managed to work it out it helped a lot
     
unityunity