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

[Question] Change Remote Load Path at runtime?

Discussion in 'Addressables' started by Vladnee, Mar 11, 2020.

  1. Vladnee

    Vladnee

    Joined:
    Apr 28, 2017
    Posts:
    26
    Is it possible to change the remote load path after the lazy init of addressables?
     
  2. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    I don't think you can change the remote load path after it's initialized, but you can download a new catalog from a different path directly. The caveat is that you can never unload catalogs (currently).

    I haven't tested it myself, but I think the catalog contains relative paths to all of its bundles, so when you download a new catalog, addressables caches the path you used to download it and then use a relative path to download any bundles that are requested from it.
     
  3. araki_yuta

    araki_yuta

    Joined:
    Jan 21, 2020
    Posts:
    14
    AFIK, you can't do that without forking and modifying Addressables Package.

    If you really want to, you can add reset function and destroy all values thats set by initialize process in AddressableImpl and call Addressables.InitializeAsync again to redo the whole initialization process. (You need to take care of whole async process that might be running in the background as well) Hopefully Addressables team can implement this kind of feature sometime soon so we can reset the game safely.
     
  4. kactus223

    kactus223

    Joined:
    May 20, 2014
    Posts:
    35
    Yeah, this is extremely frustrating. Our game needs a response from the server to know the latest URL of the remote load path (this URL is configurable), but before sending the request we would like to load a prefab from addressable (from a bundle in StreamingAssets) to display a TOS/PP popup to the players. If I want to load that prefab, I have to call Addressables.InitAsync, but if I call that, I can't update the URL later because Addressables already get the value of my variables for the URL in InitAsync.
     
    Procyonx_ and Prodigga like this.
  5. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    @kactus223 The solution to that is to use 2 separate catalogs. Just put that base prefab in the built-in catalog (you can even make the group built-in to the engine if you want, depending on your needs), then use the LoadContentCatalogAsync API to load the second catalog with all the other assets.
     
    kactus223 likes this.
  6. kactus223

    kactus223

    Joined:
    May 20, 2014
    Posts:
    35
    Thanks for the suggestion. The actual problem is: I configure the RemoteLoadPath is {MyNamespace.MyAddressablesManagerClass.URL}. I used the curly bracket to dynamically set the URL of the remote bundle. At first, before I set the value to MyNamespace.MyAddressablesManagerClass.URL, if I use Addressables.LoadAssetAsync, Addressables will automatically initialize and set the URL of the remote bundle to the value of MyNamespace.MyAddressablesManagerClass.URL, which is empty at that moment. Even if I call LoadContentCatalogAsync later, it still use the empty URL to load the remote bundle.
     
  7. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    Right, so until the addressables team adds the capability to change it, you'll have to not use that for your main assets and instead pass that url to LoadContentCatalogAsync.
     
  8. kactus223

    kactus223

    Joined:
    May 20, 2014
    Posts:
    35
    After 2 days reading the source code of Addressables, I finally found a way to reset the remote load path.

    Code (CSharp):
    1. AddressablesRuntimeProperties.ClearCachedPropertyValues();
    2. Addressables.UpdateCatalogs(new List<string> {ResourceManagerRuntimeData.kCatalogAddress});
     
    Bauschen likes this.
  9. delexaet

    delexaet

    Joined:
    Apr 14, 2016
    Posts:
    39
  10. EndorphinGames

    EndorphinGames

    Joined:
    Apr 14, 2017
    Posts:
    4
    I get this error
    NullReferenceException: Object reference not set to an instance of an object
    UnityEngine.AddressableAssets.UpdateCatalogsOperation.Start (System.Collections.Generic.IEnumerable`1[T] catalogIds) (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/Initialization/UpdateCatalogsOperation.cs:27)
    UnityEngine.AddressableAssets.AddressablesImpl.UpdateCatalogs (System.Collections.Generic.IEnumerable`1[T] catalogIds, System.Boolean autoReleaseHandle) (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/AddressablesImpl.cs:1219)
    UnityEngine.AddressableAssets.Addressables.UpdateCatalogs (System.Collections.Generic.IEnumerable`1[T] catalogs, System.Boolean autoReleaseHandle) (at Library/PackageCache/com.unity.addressables@1.18.2/Runtime/Addressables.cs:1333)
    AddressableManagerEditor+<Start>d__25.MoveNext () (at Assets/AddressableManagerEditor.cs:75)
    UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
     
  11. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,121
    Hi Proto, is this the best way in 2023?

    We are encountering a problem here ourselves. Our game contacts remote server to determine Catalog url. We bind that as the remote load path before addressables init + Catalog update... All good.

    Addressables initialise. Catalog update detects any new data and downloads and updates correctly. Assets are loadable.

    We push an update out and all the players currently online are sent back to the launch screen and told their game needs to be updated.

    All good so far...

    We bind the new remote Catalog URL to our load path, we reset the addressable variable evaluation cache.. and still it doesn't work.

    RemoteHash is same as LocalHash for the Catalog, so it doesn't even try to check.

    The client is unable to update the data. Even CheckForCatalogUpdate fails to detect any changes.

    Restarting the application fixes the problem, as the hash cache is reset and all checks are performed as expected.
     
  12. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,121
    It seems we are one single "reset local hash caches" function away from this working flawlessly...

    I tried to write some code to reset and invalidate some fields via reflection but I couldn't figure it out. The ResourceManager later is a labyrinth to navigate..!
     
  13. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    566
    I'm not sure, I haven't touched Addressables in over 2 years.
     
    Prodigga likes this.
  14. kenorbik

    kenorbik

    Joined:
    Jan 9, 2023
    Posts:
    7
    Catalog path can be changed using the sample code at https://docs.unity3d.com/Packages/com.unity.addressables@1.20/manual/LoadContentCatalogAsync.html
    Not sure why the sample code was removed from 1.21, but it still works.

    Code (CSharp):
    1. public IEnumerator Start()
    2. {
    3.     //Load a catalog and automatically release the operation handle.
    4.     AsyncOperationHandle<IResourceLocator> handle
    5.         = Addressables.LoadContentCatalogAsync("path_to_secondary_catalog", true);
    6.     yield return handle;
    7.  
    8.     //...
    9. }
     
  15. CameronND

    CameronND

    Joined:
    Oct 2, 2018
    Posts:
    81

    Hey, we are wanting to do something very similar to you. In our case the remote URL is evaluated by a static string property which we will change at runtime on a per device basis.
    I was hoping that all I would need to do would be to call AddressablesRuntimeProperties.ClearCachedPropertyValues and then CheckForCatalogUpdate and have addressables automatically detect and load the new content.

    Did you end up figuring out what else was needed?
     
  16. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,121
    Yes there was one other devious little detail that may be causing you issues. Or maybe it can help someone else out in the future..!

    I think this is a bug, but who knows. Maybe an addressable person can tell us if it's intended or not... Anyway:

    When you call CheckForCatalogUpdate it..
    1. Resolved the load path using your vars
    2. Downloads the remote hash
    3. Compares it to local hash
    4. If different, it downloads Catalog
    5. ...and so on
    This happens automatically when U call CheckForCatalogUpdate.

    However, here is where the quirk/bug messes things up: step 2 only runs once and caches the remote cache.. it doesn't care to check if the load URL changed since it last downloaded it.

    So, next time you update your runtime properties, clear the property cache, and call CheckForCatalogUpdate.. it thinks....yep..! Remote hash matches local one! No updates! Making no attempt to actually check.

    This hash caching behaviour seems to 'live and die" with the IResourceLocator and the caching behaviour is only 'in memory' - that is to say, when you restart your app then the next (first) call to CheckForCatalogUpdate will succeed, as expected.

    "Solution":

    This works for me, I don't know if it's intended. It impossible to tell what's truly intended.

    The idea is:
    1. Don't use CheckForCatalogUpdate, as it makes it more complicated to access the IResourceLocator associated with the Catalog
    2. Disable Catalog update at startup for the same reason (I think internally this just does a CheckForCatalogUpdate for you on init)
    3. Use LoadContentCatalogAsync to manually load the Catalog. Store a reference to the IResourceLocator it provides.
    4. Have some way to know if the remote Catalog has changed and needs to be reloaded. *
    5. When you want to update.. Unload the IResourceLocator first (this kills the remote hash caching behaviour) and rerun these steps.
    6. Thus this will force a download of your remote hash each time you run it.
    * Application specific. Personally just keep track of the last RemoteLoad path and diff against that to know if I new content is available. In our pipeline we made sure that new content updates are served from a new URL. We don't 'override' content in a single folder on our content server.). So if there is a remote update available, then the url must be different. We have other systems in place to determine if there are new content updates available and what their URLs are but this is all very app specific and beyond the scope of this..​

    Example code

    Here is our init addressables function..

    Code (CSharp):
    1.         private static string _activeCatalogPath;
    2.          private static IResourceLocator _activeCatalogResourceLocator;
    3.  
    4.          static async UniTask<IResourceLocator> SetupAndInitAddressables(string loadPath, CancellationToken ct)
    5.          {
    6.              using var exeTimer = new ExecutionTimer($"{nameof(PackDownloader)}.{nameof(SetupAndInitAddressables)}");
    7.              
    8.              D.Log("Setting up Addressable Properties");
    9.              AddressableVariables.LoadPath = loadPath;
    10.              UnityEngine.AddressableAssets.Initialization.AddressablesRuntimeProperties.ClearCachedPropertyValues();
    11.              
    12.              D.Log("Initializing Addressables");
    13.              await Addressables.InitializeAsync().WithCancellation(ct);
    14.              
    15.  
    16.              var catalogPath = $"{loadPath}/catalog_root.json";
    17.              if (_activeCatalogPath != catalogPath)
    18.              {
    19.                  using var exeTimer2 = new ExecutionTimer($"{nameof(PackDownloader)}.{nameof(Addressables.LoadContentCatalogAsync)}");
    20.                  
    21.                  Addressables.ClearResourceLocators();
    22.                  _activeCatalogPath = catalogPath;
    23.                  
    24.                  if(_activeCatalogResourceLocator != null) Addressables.RemoveResourceLocator(_activeCatalogResourceLocator);
    25.                  _activeCatalogResourceLocator = await Addressables.LoadContentCatalogAsync(catalogPath, true).WithCancellation(ct);
    26.              }
    27.              
    28.              return _activeCatalogResourceLocator;
    29.          }
    AddressableVariables.LoadPath is a static string, and our active profile sets the remote load path to be {AddressableVariables.LoadPath}
     
    CameronND likes this.
  17. CameronND

    CameronND

    Joined:
    Oct 2, 2018
    Posts:
    81
    Thanks for the detailed reply! I think we managed to get it working.