Search Unity

Versioning of features dynamic content

Discussion in 'Addressables' started by AndersonDev, Jul 18, 2019.

  1. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Hi, Addressables System was designed in thinking of one block of resources which can be delivered for user later.

    But let's think about a lot of different blocks. My game has ~ 50 different features. It can be new scene with some new dynamic content, or just a new popup (one prefab + graphic).

    Previously I have builded one asset bundle per feature with all dependent content. Each bundle was placed in some folder with version name: feature/1.0/feature.bundle or feature_v1.0.bundle. That gives us possibility for user on version of game build 1.0 to load old bundle, and users on 2.0 to load a new bundle. Users on v1.0 of game should get an older one, because in new feature resources there are a new scripts which are available only on v.2.0 of game. And resources for feature located in separate git repo. All resources can't be available in build time. Only feature's ones and it dependencies.

    How to achieve this with addressables? I thinking about marking all assets of one feature with feature tag, and also add tag with current version. Is it the best one?
     
  2. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    The v1 to v2 issue should be resolved by our catalog system. A new build will use a newer catalog, which only knows about v2 bundles.

    As to your desire to have each feature in it's own bundle, that can most easily be done via putting each feature in it's own folder in your project and marking each folder as addressable. The "Pack Separately" mode on the group bundle schema will create one bundle per top-level entry in the group.
     
  3. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Yes but we have two main issues:

    - When the game releases with new version 2.0, we build new resources for Feature1 with some changes and new scripts from game version 2.0, which was shipped to some AppStore. But we have users on game version 1.0 and they should get Feature1 version 1.0 resources rather than new 2.0 resources. And if we deliver 2-3 releases per month it should be clear. We can have 4 different users on different game versions in same time with code changes for Feature1 and they should get resources from corresponding bundles. You get the point?

    - We need possibility to switch user to older resources version if in current one we have some issues. Without any rebuild, just on flight. Previously we need just to switch url from feature1/v1.5 to feature1/v1.2 in db.

    Will be thankful for your advice.
     
    LarsGameloft likes this.
  4. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Moreover we have around 300+ features with hundreds gigabytes of graphics, sounds, prefabs. When we build some feature, we remove everything except that one feature (I mean only resources not code). Than build bundles ~ 3 minutes and push them to server.

    When we build game build, we leave only build in resources.

    That is only way to work with tons of stuff.

    We just can't keep everything in one settings file. I see some workaround to place something like .asmdef to parent folder. It will mark parent folder as addressable and change the name on import.
     
    Last edited: Jul 22, 2019
  5. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    This is the part that I was saying should "just work". You build catalog_v1.json. then later build catalog_v2.json. Some time after that, if you need to update content for the older build, when you do a "build for content update", it'll make a new version of catalog_v1.json. This is what the app will look for, so you can keep updating that catalog with content fit for that player.

    This one we do not support out of the gate. We recommend having a server that the app pings to ask where it's content is if you need this sort of functionality. One of the other teams at Unity is working on an offering that could meet this need. For now you unfortunately have to roll your own.
     
  6. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    The main issue is that we don't keep all resources in one moment. Only resources for Feature1. And different clients can have different versions of feature. Moreover we want to control which version user uses.

    Here are some more deep info about catalog? As I understand we have one catalog for all resources in a game. But we want add new resources without roll out a new version of game in app store. I want to open core project, import all resources for Feature1 in one folder, mark as addressables, build bundle(s) for it and push it on cdn. Now old user get a new version of Feature1.

    Thanks!
     
  7. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    This is the intent of the update system and should work. The one caveat is that you cannot push updates to code.
    1. assets that are tied to new code, can't be built (prefabs, scriptable objects, etc.)
    2. assets loadable only in new ways won't work. Meaning, if your game did LoadAssetsAsync("enemies"), and you added more things with that label, you're fine. If you wanted to make a new label "big_enemies", you can't ship new game code in an addressables update that knows about that key.
     
  8. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Ok, I discovered question more deeply. Here we have several problems:
    1) When I have 5 versions of game supporting on one platform, I need keep .bin file for each shipped build. If I want to add a new feature (some level pack), I need to make 5 builds for each version with it's .bin file to modify catalog. Right?
    2) I haven't any tools to see, which content available for which app version. If it is some issues it will be so hard to locate them;
    3)I can't add a new content separately. That is my blocker now.
    As I wrote we have ~300GB of dynamic content. If I want to build a new level pack for testing (without building a player) I need to restore all my settings for ~500 level packs I had before, add my new feature and build. That is boring. I can't add new content to catalog file.
    It will override and delete all of my 500 level packs if I will build only one feature.

    In AssetBundles system we had one manifest per feature. It had all dependencies needed to be downloaded all feature correct. It shouldn't know about Feature 2. But now you need to put all features inside one place...

    Thanks for your help Bill!
     
    chanon81 likes this.
  9. chanon81

    chanon81

    Joined:
    Oct 6, 2015
    Posts:
    88
    These are the things that we need to have a complete understanding of, and it should be all laid out clearly in the documentation. Otherwise how would one have the confidence to go live with a game using it?
     
  10. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Yes. Like for small project with one resources pack this feet well. But for delivering separate features that is really hard. @unity_bill what do you recommend? There are some tricks here already? If not, do you planing to add something?

    That is very necessary for us, because we starting to make a new game and have three options:
    1) Use custom asset system with own build pipeline and asset bundles. That costs more time, and Asset Bundles, as I heard may be removed and moved to a new system.
    2) Take Addressables, but make a custom build scripts and providers to take bundles by our old scheme with manifests and separate folder for each version. Later we can move to fully Addressables.
    3) Use fully Addressables and play around. That is depends on your advices.

    Thanks again!
     
  11. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    In addition to my original post, I need to have some possibility to:
    - Build new player without any assets in project. It will generate new .bin file with with url to new catalog and new player version. After that I need some script to inject catalog path from another player build state. Will be that a crunch?)
    - Add or change catalog when build dynamic content update for new feature or when you update some one feature. Maybe take some .bin and it's catalog and add to existing features new one, or change path to only this feature's new bundles. Like as static content update but for dynamic one.

    P.S. As I see catalog is also an entry with own provider. Maybe it is possible to make a root container catalog with per feature children catalogs (and own .bins AddressablesContentStates)?

    P.P.S. Or maybe: if no asset is found in project, but we have an entry in settings, then mark it as "moved" and include it in catalog without any changes and build triggers.
     
    Last edited: Jul 30, 2019
  12. danilonishimura

    danilonishimura

    Joined:
    Jul 13, 2010
    Posts:
    49
    "I need some script to inject catalog path from another player build state"

    Maybe something in the lines of (code not tested):
    Code (CSharp):
    1. var asyncInitialization = Addressables.InitializeAsync();
    2. while (!asyncInitialization.IsDone)
    3. {
    4.     yield return new WaitForEndOfFrame();
    5. }
    6. Addressables.Release(asyncInitialization);
    7.  
    8. var asyncCatalogLoad = Addressables.LoadContentCatalogAsync(catalogPath);
    9.  
    10. while (!asyncCatalogLoad.IsDone)
    11. {
    12.     yield return new WaitForEndOfFrame();
    13. }
    14.  
    15. var asyncInstantiation = Addressables.InstantiateAsync(assetKey);
     
  13. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    You don't get it. Inject one catalog intro some master catalog. That is more deep logic.
     
  14. danilonishimura

    danilonishimura

    Joined:
    Jul 13, 2010
    Posts:
    49
    Oh sorry, I see it now. Sorry to ask but, why doesn't loading additional catalogs fix your problem?
     
  15. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    a couple notes.

    If you chose (1), we aren't getting rid of AssetBundles, so you can continue to use them.
    Overall, my recommendation is #2. That is the intent of addressables. To be a framework upon which you can write small build and loading code that is custom to your needs. It is not a magic bullet that you can just put in your game and it works for all usecases with no coding. The more complex the game, the more likely you are to need to extend Addressables. Our primary focus was not making sure we solved all possible problems, but trying to make sure we did not block anyone from solving their problems in a simple way.

    Shipping several different versions of a game, and having large amounts of content that works with various different versions is a very difficult problem. We hope to make it easier, but it'll not be "easy". The main complication is code. Unity today does not support a way to ship new code (and even if it did, stores like Apple's wouldn't allow it). So if you have 5 versions of your game out there, you have to be careful with your code-dependent content.

    I may not have my head totally wrapped around your situation, but here is one idea that may work (or may help you think of a better idea for your situation)....

    - Make some groups only have things with scripts attached (prefabs, scriptable objects), and the rest of the groups only have things without scripts.
    - create a modified build script that splits the results into two catalogs. The normal catalog knows about the items connected to code. The second catalog knows about things not connected to code.
    - on your sever, you can have a unique copy of each "code-dependent" catalog tied to each build of the game you've shipped. You can also have one copy of the "code-independent" catalog that all builds point to. This would allow you to update "safe" content to all builds.

    The challenge is truly ensuring what content is "safe". That's a tough problem if you are using addressables, or if not.



    I do want to add one more note. There are two reasons we look in the content_state.bin. One is to know the name of the catalog so that when we build an updated catalog, the old player can find it. Depending on how you look up catalogs, this may not be needed (such as pinging a server to ask for the catalog name/location). The second purpose is to detect changes to local content. Again, this might not be needed if you plan your work carefully. So keep in mind that there are ways to do "updates" beyond the .bin file update path we've provided. Addressables is open source, and should be extensible, so feel free to mold it to your needs.
     
    AndersonDev likes this.
  16. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Thanks a lot for those huge response. I have the nerve to ask little more:
    - May I modify current build script to add add feature to catalog, not having all other not dependent assets? Now it gets main settings file and go throw all asset and try to build all asset bundles. But in our case assets is stored in different git repos (due to huge size). And builder should import resources in project only for 1 feature, build it and change catalog. By current scenario, catalog will have only 1 feature (because main settings file has only 1 feature's assets now in project). Maybe use not only old .bin but also old catalog to get info from?
    - May I store in main catalog as dependencies ~ 100 catalogs for other features?
    - It is possible to load several catalogs?

    Thanks!)
     
  17. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    It should be possible to do what you want by modifying the catalog. Of note, each group also has a "include in build" checkbox. Toggling that off will make our current build script ignore that group. If you only need to build one feature, that is a way to do it. If you need to build 100's of features, one at a time, then you probably need some kind of custom build script.
    The easiest way is to actually do one build, but break up the catalog. It sounds like for your situation, you can't do that, and you need many builds (so you can sync git & import separately). If that's the case, you probably want a higher level script that syncs git, triggers the import, then runs an addressables build script.

    We haven't thought of that, but I think it'd work. Catalogs use a provider, so it should be possible to set things up this way.

    yes, use https://docs.unity3d.com/Packages/c...tentCatalogAsync_System_String_System_String_
     
  18. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Wow, that is closer!

    - In variant if I want add something to catalog checking off "include in build" for group will remove it from new override catalog. Right? That doesn't feet in this model.

    - I can try to compile new container catalog by providing location of other catalogs. Like: build one group with one feature and add it's catalog as CatalogEntry to some root one. Maybe need some utility to do that.

    - Another variant: just to store some array of features names and make LoadContentCatalogAsync for each feature. It will add locations to root addressable object.

    P.S. Tried first variant - it rewrite catalog with new one without feature. Which is logical. It doesn't feet well.
     
    Last edited: Aug 2, 2019
  19. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Okey. I managed to make loading of additional catalogs:
    - You build your game with only build in resources;
    - Save catalog and .bin;
    - Exclude from build all except target group with only 1 feature in;
    - Build (default or for content, depends on versioning);
    - Add feature name to catalog as prefix;
    - Add all features names to some json serialized file and put it on server;
    - Then on main loader:
    Code (CSharp):
    1.         foreach (string featureName in FeaturesList)
    2.         {
    3.             yield return Addressables.LoadContentCatalogAsync("http://192.168.31.158:52488" + "/" + featureName + "_catalog_1.0.json").Task;
    4.         }
    That additively will load all locations to runtime controller. As I think it will provide caching and versioning of each catalog.
    - It is also possible to add .json feature list as location during player build (like main catalog).

    The second variant currently in research:
    - Little bit difficult to get one catalog, add some location and save back because of lacking of api.

    P.S. @unity_bill I can't simply load existing catalog from json, add new entry (another catalog) and save back?
     
    Last edited: Aug 6, 2019
  20. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    this sounds like a fairly viable option.

    if you want to try this, one option could be to specify the "player version override" in the main AddressableAssetSettings inspector. This will override the catalog name... "catalog_version.json".

    yay!

    for your later questions, I'm not exactly sure what you're running into. please clarify.
     
    AndersonDev likes this.
  21. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    For now ContentCatalogData has only option to be refilled fully by SetData() method. If I want load ContentCatalogData from disk and add to it new ContentCatalogDataEntry I will not have this possibility. Moreover I can't get ContentCatalogDataEntry's from it, to add something and refill again.

    I need this features, because I building ~ 10 features (only 1 folder marked as addressables and included in build in one group) with base Build For Content Update. Now I have 10 different catalogs for each feature (and bundles). My next step is to take a generate a Main Catalog file and add to this catalog early builded catalogs names.

    After this, on init of my game, I can just load them by feature's name from addressable system (exactly as you are doing for main catalog, but for all keys entries in Main Catalog). That will do all the staff for me.
     
  22. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Okey, variant with compiling a new catalog with 10 more catalog inside works!

    Code is bad for now, just for test purposes:
    Code (CSharp):
    1.  AsyncOperationHandle<IResourceLocator> asyncInitialization = Addressables.InitializeAsync();
    2.         while (!asyncInitialization.IsDone)
    3.         {
    4.             yield return new WaitForEndOfFrame();
    5.         }
    6.  
    7.         foreach (object key in asyncInitialization.Result.Keys)
    8.         {
    9.             if (!(key is string) || !(key as string).EndsWith("_catalog"))
    10.                 continue;
    11.  
    12.             AsyncOperationHandle<ContentCatalogData> task = Addressables.LoadAssetAsync<ContentCatalogData>(key as string);
    13.             while (!task.IsDone)
    14.             {
    15.                 yield return new WaitForEndOfFrame();
    16.             }
    17.            
    18.             if (task.Result != null)
    19.                 Addressables.ResourceLocators.Add(task.Result.CreateLocator());
    20.            
    21.             Addressables.Release(task);
    22.         }
    23.        
    24.         Addressables.Release(asyncInitialization);
    And making a container:
    Code (CSharp):
    1. private void MakeCatalog()
    2.     {
    3.         AddressableAssetSettings aaSettings = AddressableAssetSettingsDefaultObject.Settings;
    4.        
    5.         List<ContentCatalogDataEntry> catalogLocations = new List<ContentCatalogDataEntry>();
    6.  
    7.         foreach (Feature featureName in Features)
    8.         {
    9.             string catalogName = featureName.Name + "_catalog";
    10.  
    11.             string featureVersionedFileName = aaSettings.profileSettings.EvaluateString(aaSettings.activeProfileId, "/" + catalogName + "_" + aaSettings.PlayerBuildVersion);
    12.            
    13.             string remoteLoadFolder = aaSettings.RemoteCatalogLoadPath.GetValue(aaSettings);
    14.  
    15.             string remoteCatalogLoadPath = remoteLoadFolder + featureVersionedFileName + ".hash";
    16.             string remoteHashLoadPath = remoteLoadFolder + featureVersionedFileName + ".hash";
    17.  
    18.             string cacheLoadPath = "{UnityEngine.Application.persistentDataPath}/com.unity.addressables" + featureVersionedFileName + ".hash";
    19.  
    20.             string[] dependencyHashes = new string[(int)ContentCatalogProvider.DependencyHashIndex.Count];
    21.             dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Remote] = catalogName + "RemoteHash";
    22.             dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Cache] = catalogName + "CacheHash";
    23.  
    24.             catalogLocations.Add(new ContentCatalogDataEntry(
    25.                 typeof(string),
    26.                 remoteHashLoadPath,
    27.                 typeof(TextDataProvider).FullName,
    28.                 new []{ dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Remote] }));
    29.  
    30.             catalogLocations.Add(new ContentCatalogDataEntry(
    31.                 typeof(string),
    32.                 cacheLoadPath,
    33.                 typeof(TextDataProvider).FullName,
    34.                 new []{ dependencyHashes[(int)ContentCatalogProvider.DependencyHashIndex.Cache] }));
    35.  
    36.             catalogLocations.Add(new ContentCatalogDataEntry(
    37.                 typeof(ContentCatalogData),
    38.                 remoteCatalogLoadPath,
    39.                 typeof(ContentCatalogProvider).FullName,
    40.                 new []{ catalogName },
    41.                 dependencyHashes));
    42.         }
    43.        
    44.         string versionedFileName = aaSettings.profileSettings.EvaluateString(aaSettings.activeProfileId,"/catalog_" + aaSettings.PlayerBuildVersion);
    45.         string remoteBuildFolder = aaSettings.RemoteCatalogBuildPath.GetValue(aaSettings);
    46.         string remoteJsonBuildPath = remoteBuildFolder + versionedFileName + ".json";
    47.         string remoteHashBuildPath = remoteBuildFolder + versionedFileName + ".hash";
    48.        
    49.         ContentCatalogData newCatalog = CurrentContentCatalogData;
    50.         newCatalog.SetData(catalogLocations);
    51.        
    52.         string jsonText = JsonUtility.ToJson(newCatalog);
    53.         string contentHash = HashingMethods.Calculate(jsonText).ToString();
    54.        
    55.         WriteFile(remoteJsonBuildPath, jsonText);
    56.         WriteFile(remoteHashBuildPath, contentHash);
    57.     }
     
    unity_bill likes this.
  23. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    @unity_bill Hi, may we add some feature to call InitializationObjects after LoadContentCatalogInternal. Like after Init fully completed but before anyone will do LoadAsset(). I need this one to load additional catalogs throw main one. Now I doing this on Preloader Scene, but that is not flexible and I need to switch scene every time.

    For now I have something like:
    Code (CSharp):
    1. public class PreloaderController : MonoBehaviour
    2. {
    3.     [SerializeField] private Slider LoadingSlider;
    4.     [SerializeField] private TextMeshProUGUI LoadingPercent;
    5.  
    6.     private void Start()
    7.     {
    8.         StartCoroutine(PreloadProcess());
    9.     }
    10.  
    11.     private IEnumerator PreloadProcess()
    12.     {
    13.         AsyncOperationHandle<IResourceLocator> asyncInitialization = Addressables.InitializeAsync();
    14.  
    15.         while (!asyncInitialization.IsDone)
    16.             yield return new WaitForEndOfFrame();
    17.  
    18.         foreach (object keyObj in asyncInitialization.Result.Keys)
    19.         {
    20.             if (!(keyObj is string key) || !key.EndsWith("_catalog"))
    21.                 continue;
    22.  
    23.             Addressables.ResourceLocators.First().Locate(keyObj, typeof(ContentCatalogData), out IList<IResourceLocation> locations);
    24.             yield return LoadContentCatalog(locations.First(), "").Task;
    25.         }
    26.        
    27.         Addressables.Release(asyncInitialization);
    28.  
    29.         yield return Addressables.LoadSceneAsync("Lobby/Scenes/Lobby.unity").Task;
    30.     }
    31.  
    32.     public AsyncOperationHandle<IResourceLocator> LoadContentCatalog(IResourceLocation loc, string providerSuffix)
    33.     {
    34.         AsyncOperationHandle<ContentCatalogData> loadOp = Addressables.LoadAssetAsync<ContentCatalogData>(loc);
    35.         AsyncOperationHandle<IResourceLocator> chainOp = Addressables.ResourceManager.CreateChainOperation(loadOp, res => OnCatalogDataLoaded(res, providerSuffix));
    36.         Addressables.Release(loadOp);
    37.         return chainOp;
    38.     }
    39.  
    40.     private static AsyncOperationHandle<IResourceLocator> OnCatalogDataLoaded(AsyncOperationHandle<ContentCatalogData> op, string providerSuffix)
    41.     {
    42.         ContentCatalogData data = op.Result;
    43.         if (data == null)
    44.         {
    45.             return Addressables.ResourceManager.CreateCompletedOperation<IResourceLocator>(null, new Exception("Failed to load content catalog.").Message);
    46.         }
    47.  
    48.         if (data.ResourceProviderData != null)
    49.             foreach (ObjectInitializationData providerData in data.ResourceProviderData)
    50.                 LoadProvider(providerData, providerSuffix);
    51.  
    52.         ResourceLocationMap locMap = data.CreateLocator(providerSuffix);
    53.  
    54.         Addressables.ResourceLocators.Add(locMap);
    55.         return Addressables.ResourceManager.CreateCompletedOperation<IResourceLocator>(locMap, string.Empty);
    56.     }
    57.  
    58.     private static void LoadProvider(ObjectInitializationData providerData, string providerSuffix)
    59.     {
    60.         int indexOfExistingProvider = -1;
    61.         string newProviderId = string.IsNullOrEmpty(providerSuffix) ? providerData.Id : (providerData.Id + providerSuffix);
    62.         for (int i = 0; i < Addressables.ResourceManager.ResourceProviders.Count; i++)
    63.         {
    64.             IResourceProvider rp = Addressables.ResourceManager.ResourceProviders[i];
    65.             if (rp.ProviderId == newProviderId)
    66.             {
    67.                 indexOfExistingProvider = i;
    68.                 break;
    69.             }
    70.         }
    71.        
    72.         if (indexOfExistingProvider >= 0 && string.IsNullOrEmpty(providerSuffix))
    73.             return;
    74.  
    75.         IResourceProvider provider = providerData.CreateInstance<IResourceProvider>(newProviderId);
    76.         if (provider != null)
    77.         {
    78.             if (indexOfExistingProvider < 0 || !string.IsNullOrEmpty(providerSuffix))
    79.             {
    80.                 Addressables.ResourceManager.ResourceProviders.Add(provider);
    81.             }
    82.             else
    83.             {
    84.                 Addressables.ResourceManager.ResourceProviders[indexOfExistingProvider] = provider;
    85.             }
    86.         }
    87.         else
    88.         {
    89.             Addressables.LogWarningFormat("Addressables - Unable to load resource provider from {0}.", providerData);
    90.         }
    91.  
    92.     }
    93.  
    94.     public void UpdateLoadingBar(float percent)
    95.     {
    96.         LoadingSlider.value = percent;
    97.         LoadingPercent.text = $"{percent * 100:0.##}%";
    98.     }
    99. }
    And may I ask you to make some API more public? For example:
    - Initialization.InitializationOperation.LoadContentCatalog()
    I need some possibility to load catalog additively not by path but by address or by
    IResourceLocation.I can’t move that logic because of lack of Addressables.SceneProvider and Addressables.InstanceProvider setter. Moreover code duplication isn’t great.
     
    Last edited: Aug 14, 2019
  24. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    And why AsyncOperationBase is so coupled to Resource Manager? It will be nice to have it as separate util.
     
  25. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    Could one option be to have your code not call any Loads until your own init is done? And in your init call
    Addressables.InitializeAsync().Completed += DoPostInit;

    Once your PostInit is done, then let your game proceed.

    Does that align with what you're trying to do?
     
  26. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    I have custom game init pipeline with Addressables.InitializeAsync() for now. However it requires switching to some "Preloader" scene or some Init script on each scene (which will switch to preloader). But I'm working on some concrete scene and want to run it, change and run again. I want to make guaranty that when I use LoadAsync() my custom Addressables init logic will pass.

    You are using nice pattern with InitOperation to guaranty that when you LoadAsync() in any place of app initialization process will be completed and then asset will be loaded. I just want to append some async logic before LoadAsync().
     
  27. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    849
    Thanks for your input. Odds are if we fixed this we'd look at a way to make the current InitializationObject system. It seems like that system doesn't do it's init-ing late enough for your needs
     
  28. AndersonDev

    AndersonDev

    Joined:
    Dec 13, 2015
    Posts:
    70
    Thanks! Yes, not late enough:
    Code (CSharp):
    1.    Addressables.Log("Addressables - loading initialization objects.");
    2.             foreach (var i in rtd.InitializationObjects)
    3.             {
    4.                 if (i.ObjectType.Value == null)
    5.                 {
    6.                     Addressables.LogFormat("Invalid initialization object type {0}.", i.ObjectType);
    7.                     continue;
    8.                 }
    9.                 try
    10.                 {
    11.                     var o = i.CreateInstance<object>();
    12.                     Addressables.LogFormat("Initialization object {0} created instance {1}.", i, o);
    13.                 }
    14.                 catch (Exception ex)
    15.                 {
    16.                     Addressables.LogErrorFormat("Exception thrown during initialization of object {0}: {1}", i, ex.ToString());
    17.                 }
    18.             }
    19.  
    20.             //It does init before loading main catalog. But I need catalog to be loaded to locate other catalogs
    21.             var locMap = new ResourceLocationMap(rtd.CatalogLocations);
    22.             m_Addressables.ResourceLocators.Add(locMap);
    23.             IList<IResourceLocation> catalogs;
    24.             if (!locMap.Locate(ResourceManagerRuntimeData.kCatalogAddress, typeof(ContentCatalogData), out catalogs))
    25.             {
    26.                 Addressables.LogWarningFormat("Addressables - Unable to find any catalog locations in the runtime data.");
    27.                 m_Addressables.ResourceLocators.Remove(locMap);
    28.                 Complete(m_Result, false, "Addressables - Unable to find any catalog locations in the runtime data.");
    29.             }
    30.             else
    31.             {
    32.                 Addressables.LogFormat("Addressables - loading content catalogs, {0} found.", catalogs.Count);
    33.                 LoadContentCatalogInternal(catalogs, 0, locMap);
    34.             }