Search Unity

Modding with Addressables?

Discussion in 'Addressables' started by laurentlavigne, Jul 11, 2018.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,446
    Is there a plan for an inbuilt modding utility in Addressables or you think modding is still too specific to the games to be part of the engine?
     
    andrew-lukasik likes this.
  2. PaulBurslem

    PaulBurslem

    Unity Technologies

    Joined:
    Oct 7, 2016
    Posts:
    49
    We don't have plans to specifically support mods, but you can dynamically add IResourceLocators (which are essentially the ContentCatlogs) at runtime. When you load your collection of mods, each one could have the path to load its catalog. Predefined/known labels can be used to retrieve all assets that are the entry points to the mods (prefabs or other data).
     
    andrew-lukasik likes this.
  3. Vallar

    Vallar

    Joined:
    Oct 18, 2012
    Posts:
    173
    Excellent question @laurentlavigne I was about to ask the same question.

    Could you please elaborate on this further? We are currently planning to use AssetBundles; we group all our meshes in StreamingAssets in folders based on their type (buildings, characters, items, etc...) and then we load these at run time and when we Instantiate an object at run time, we assign their mesh, sprite, data, etc... using the AssetBundles loaded data.

    How does one do the same with Addressable Assets?

    I kind of understand in a vague way what you've mentioned but not sure I follow how to implement that; like for example what is a ContentCatalog?

    Would what you said mean that instead of AssetBundles I'd use Addressable Assets, load based on group (buildings, characters, items, etc...) and then use the name perhaps of the "Addressable Asset" to assign to the object same way we're doing with Asset Bundles?

    EDIT: Crap, I just realized I necroed the post... I didn't see this is from July. Should I make a new post?
     
  4. nTronz

    nTronz

    Joined:
    Nov 16, 2015
    Posts:
    3
    @Vallar, I came to ask the same questions.

    What functions need to be called to Load said catalog from a predefined user folder? Do users need to have a pre-built Addressable AssetGroup?

    I think we just need some specific functions to understand. I'll make a new thread if we don't get a reply soon.
     
    JotaRata likes this.
  5. pdinklag

    pdinklag

    Joined:
    Jan 24, 2017
    Posts:
    56
    Necro or not, I'd like to second the questions since they aren't answered yet @PaulBurslem . Nothing seems to be really documented and I cannot put the pieces together here. I see there's a method Addressables.LoadContentCatalog, but here are some specific questions:
    • What exactly is a content catalog (as opposed to an asset bundle)?
    • How do we use that method? What path do we pass to it, relative to what? What does it point to / what is expected to be found at that path - an asset bundle file? Something like that catalog.json?
    • How will modders prepare a content catalog for their mods?
    • How can the game get a list of available content catalogs ("installed mods") to load? Do we put them somewhere under StreamingAssets and scan for certain JSON files using System.IO?
     
    bamial_grehk and Sylmerria like this.
  6. Sylmerria

    Sylmerria

    Joined:
    Jul 2, 2012
    Posts:
    260
  7. Korindian

    Korindian

    Joined:
    Jun 25, 2013
    Posts:
    464
    I'd also like to get some clarification about how to actually implement modding with Addressables.
     
  8. Loofou

    Loofou

    Joined:
    Aug 22, 2012
    Posts:
    16
    A second this. A bit of clarification or at least pointers to documentation or even just code that can help with this would be very helpful indeed.
     
  9. pdinklag

    pdinklag

    Joined:
    Jan 24, 2017
    Posts:
    56
    I see so much activity in this forum, yet the developers (atm it's @unity_bill ?) seem to actively avoid threads with these kinds of questions.

    Addressables are worthless to me with all these open questions. I have long built my own system since I posted my very concrete and presumably easy to answer (for the devs) questions 4 months ago. Probably a lot of other people have as well while Addressables are slumbering in this undocumented "no idea how to use it" state. And the more time progresses with this left unanswered the less likely it becomes that I'm ever interested in using them, because I will have to migrate.

    If you want people to use this feature, please answer their questions (and within less than a year...) or provide a solid documentation.
     
    Last edited: Aug 7, 2019
    bamial_grehk and Sylmerria like this.
  10. danilonishimura

    danilonishimura

    Joined:
    Jul 13, 2010
    Posts:
    65
    @pdinklag I'll try my best to help you. If you guys think I'm wrong, please point it out because I'm also learning about it.

    AFAIK, ContentCatalog is a file that describes the relationship between the key and the asset. At runtime, the catalog is loaded, parsed and becomes a Resource Locator, where it maps the addressable name to the actual asset. In the AssetBundle you reference the asset by its path, where in the addressable, you access it by a name. The advantage is that whenever a catalog is loaded (you can load multiple catalogs), you get the asset by a single call Addressables.LoadAssetAsync(string key) or Addressables.InstantiateAsync(string key), and it will figure out where is that asset from the many bundles registred in the ResourceLocators.

    Addressables.InstantiateAsync(string key), where key is the AddressableName you put in the AddressableWindow:
    upload_2019-8-7_18-15-18.png
    In this case, I'd use:
    var instantiateOp = Addressables.InstantiateAsync<GameObject>("VIEW_Test_Capsule")// it returns a AsyncOperation.
    while (!instantiateOp.IsDone)
    {
    yield return new WaitForEndOfFrame();
    }

    var myCapsule = instantiateOp.Result; // The instantiated capsule object.

    If you use the Addressables.LoadAssetAsync, it will return a non instantiated instance of the asset. You can instantiate it using Instantiate(myCapsule);

    There are more overloads and methods in the Addressables class. Please refer to https://docs.unity3d.com/Packages/c...1/manual/AddressableAssetsGettingStarted.html and https://docs.unity3d.com/Packages/c...ityEngine.AddressableAssets.Addressables.html

    Honestly, I don't know if there is a "right" way to do it. Updating content using addressables can be easy. Setting up a webservice that returns a list of catalogs containing the updated assets, load them, and you should be good to go. Updating game logic is bit trickier. Some people use reflection, some people use runtime scripting (lua, python, etc). There are many approaches, you just have to be sure they work on your target environment.


    The way I see, the easiest way is to put the catalogs in a webserver and download them from there, where you can write server scripts to filter what catalogs you want to be available. You can also put the catalogs in your project, but it makes more complicated to update because you'll have to publish a update of your game to update the assets. Loading them from another source definitively makes it more scalable.
     
    Nickromancer and pdinklag like this.
  11. senfield

    senfield

    Joined:
    Apr 1, 2019
    Posts:
    28
    You can load a catalog manually using Addressables.LoadContentCatalogAsync(path);
    The path has to be the absolute path to the catalog.json.

    Modding implies that you might want to have a separate Unity project from the one used to create the original game. To do that, you have to pay very close attention to load path and build path settings you use in the Addressable Asset settings for the mod project. They must match where it will be loading from once it is installed in the game.

    Also, if you want to use a script on a modded object it has to be present in both the game and the mod project.
     
    laurentlavigne likes this.
  12. deepakguptaPR

    deepakguptaPR

    Joined:
    Aug 20, 2019
    Posts:
    10
    @danilonishimura : Could you please provide a code snippet for loading catalog from CDN?
    I am using the following code to load catalog json
    Code (CSharp):
    1. Addressables.LoadContentCatalog("[My CDN server]/Android/catalog_2019.09.24.13.06.53.json").Completed += LoadCatalogsCompleted;
    And I am getting 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()
    3.  
    Thanks in advance
     
  13. JotaRata

    JotaRata

    Joined:
    Dec 8, 2014
    Posts:
    37
    It is possible to download and store the asset bundles built with addressables in a custom folder (i.e. Documents folder) then load any bundle stored in that folder. I want to make a modding system for my game which users are allowed to create custom objects and implement them in the scene of my game.
    Is addressables suited for that or should I revert to the older AB system?
     
  14. Ramobo

    Ramobo

    Joined:
    Dec 26, 2018
    Posts:
    82
    That's pretty much what I asked about here. As far as I know:
    • Loading a content catalog inevitably loads all bundles it references.
    • There's no way to separate bundles into multiple catalogs. The only way would be multiple projects.
    • Good luck properly changing build location.
    The last two are great if want to build your game as mods, like me. Maybe that functionality is in a dark corner of the API I'm not aware of. Maybe.
     
    JotaRata likes this.
  15. JotaRata

    JotaRata

    Joined:
    Dec 8, 2014
    Posts:
    37
    Well I'm using asset bundes in the meanwhile, both of them have their pro and cons
     
  16. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,446
    what are the pros + cons of each?
     
  17. Mayhem-McAwesome

    Mayhem-McAwesome

    Joined:
    Oct 31, 2012
    Posts:
    1
    Just wanted to add another voice to the clamoring for more detailed documentation on how to do exactly this.

    There is scant little intel to be dug up about what precisely most of the addressable options shown on the inspector panel are for. Let alone clearly laid out instructions for coding some use out of this new and seemingly quite capable system, without resorting to CSI-style trial and error methods.

    One might suppose there ought to be a manual. Yet in a whole day's worth of searching I've encountered but a most superficial introduction to the subject. Even the few sample projects I managed to find in some unmarked basement of GitHub had little to offer in clearing up the Hows and Whys to the concepts being demonstrated.

    Am I missing something totally obvious that somehow I managed to neglect?

    Is there any detailed and comprehensive documentation, such as about how to achieve anything more than implementing example features without fully understanding them?


    Thanks in advance
     
    pdinklag likes this.
  18. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    134
    Something very helpful would be build script to build single Addressable Group (mod package) together with catalog info.

    I'll make custom script for it but another very helpful addition to write custom build scripts would be to make all private fields in
    BuildScriptPackedMode.cs
    protected and all static/private functions protected virtual.

    If that's the case I could simply inherit from BuildScriptPackedMode override ErrorCheckBundleSettings (or ProcessBundledAssetSchema) and CreateCatalog, copy/paste functions and replace few lines. I can't do it as fields are private and functions are static. BuildScriptPackedMode.cs has 9! private static fields/functions - very bad extensibility practice.

    This means I have to copy/paste whole BuildScriptPackedMode and check changes every single Addressables update :/
     
    Last edited: Jan 8, 2020
    senfield and Favo-Yang like this.
  19. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    134
    Here's the code that will build single group with catalog info: https://github.com/kamyker/BeatAim-...ster/Editor/Scripts/BuildScriptSingleGroup.cs


    I've managed to get everything working. It requires some painful setup as docs are lacking. Custom scripts and shaders obviously won't work but proxies like this are enough for me:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class VisualizeToMusic_MaterialFloat  : MonoBehaviour
    7. {
    8.     public static event Action<VisualizeToMusic_MaterialFloat> Enabled;
    9.     public static event Action<VisualizeToMusic_MaterialFloat> Disabled;
    10.  
    11.     [SerializeField] public string ShaderFieldName = "_Exposure";
    12.     [SerializeField] public MinMaxSmoothFloat ReactionToMusicLoudness = new MinMaxSmoothFloat(0.05f,0.35f,3, false);
    13.  
    14.     private void OnEnable()
    15.     {
    16.         Enabled?.Invoke( this );
    17.     }
    18.     private void OnDisable()
    19.     {
    20.         Disabled?.Invoke( this );
    21.     }
    22. }
    Reading Unity's code I have to say number of parameters in functions are crazy. Ex:
    Code (CSharp):
    1. PostProcessBundles( assetGroup, buildBundles, outputBundles, results, extractData.WriteData, aaContext.runtimeData, aaContext.locations, builderInput.Registry );
    2. PostProcessCatalogEnteries( assetGroup, extractData.WriteData, aaContext.locations, builderInput.Registry );
     
    Last edited: Jan 10, 2020
  20. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    134
    Trying to make generic class out of the one above. Any idea how to write it better?

    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4.  
    5. public abstract class MonoBehaviourEventful<T> : MonoBehaviour
    6.     where T : MonoBehaviourEventful<T>
    7. {
    8.     public static event Action<T> Enabled;
    9.     public static event Action<T> Disabled;
    10.  
    11.     protected abstract T _this { get; }
    12.  
    13.     private void OnEnable()
    14.         => Enabled?.Invoke( _this );
    15.  
    16.     private void OnDisable()
    17.         => Disabled?.Invoke( _this );
    18. }
    19.  
    Code (CSharp):
    1. public class VisualizeToMusic_MaterialFloat : MonoBehaviourEventful<VisualizeToMusic_MaterialFloat>
    2. {
    3.     [SerializeField] public string ShaderFieldName = "_Exposure";
    4.     [SerializeField] public MinMaxSmoothFloat ReactionToMusicLoudness = new MinMaxSmoothFloat(0.05f,0.35f,3, false);
    5.  
    6.     protected override VisualizeToMusic_MaterialFloat _this => this;
    7. }
     
  21. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    134
    I was wrong, shaders work fine. (oh and animators/animations also!)

    Whole scene imported in my game:


    Tbh it is very powerful function as Unity as level editor is easy to learn for modders.
     
    laurentlavigne likes this.
  22. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    134
    Updated my game to 2019.3 and I have bad news. Maps made in 2019.2 crash the game. They load fine but one class fails:
    Code (csharp):
    1. Could not produce class with ID 92.
    2. 0x00007FFEE131A54C (UnityPlayer) StackWalker::GetCurrentCallstack
    3. 0x00007FFEE131D1E9 (UnityPlayer) StackWalker::ShowCallstack
    4. 0x00007FFEE130E048 (UnityPlayer) GetStacktrace
    5. 0x00007FFEE2586ABD (UnityPlayer) DebugStringToFile
    6. 0x00007FFEE1DD2F72 (UnityPlayer) PersistentManager::produceObject
    7. 0x00007FFEE1DCE536 (UnityPlayer) PersistentManager::CreateThreadActivationQueueEntry
    8. 0x00007FFEE1DD2CD5 (UnityPlayer) PersistentManager::preallocateObjectThreaded
    9. 0x00007FFEE1402693 (UnityPlayer) ImmediatePtr<Unity::Component>::Transfer<StreamedBinaryRead>
    10. 0x00007FFEE140294D (UnityPlayer) GameObject::Transfer<StreamedBinaryRead>
    11. 0x00007FFEE140D521 (UnityPlayer) GameObject::VirtualRedirectTransfer
    12. 0x00007FFEE1DE40F8 (UnityPlayer) SerializedFile::ReadObject
    13. 0x00007FFEE1DD33DE (UnityPlayer) PersistentManager::ReadAndActivateObjectThreaded
    14. 0x00007FFEE1DD175D (UnityPlayer) PersistentManager::LoadFileCompletelyThreaded
    15. 0x00007FFEE1A5D826 (UnityPlayer) LoadSceneOperation::perform
    16. 0x00007FFEE1A5E3C0 (UnityPlayer) PreloadManager::processSingleOperation
    17. 0x00007FFEE1A5E779 (UnityPlayer) PreloadManager::Run
    18. 0x00007FFEE1C9A1CD (UnityPlayer) Thread::RunThreadWrapper
    19. 0x00007FFF79437BD4 (KERNEL32) BaseThreadInitThunk
    20. 0x00007FFF7A08CED1 (ntdll) RtlUserThreadStart
    It's wouldn't be that bad but game crashes when engine tries to unload unused assets and I guess the class that failed to load. Maps that don't use this script work fine.
    Then:
    Code (csharp):
    1. 0x00007FFEE140D764 (UnityPlayer) GameObject::WillDestroyGameObject
    2. 0x00007FFEE1A286F2 (UnityPlayer) PreDestroyRecursive
    3. 0x00007FFEE1A28747 (UnityPlayer) PreDestroyRecursive
    4. 0x00007FFEE1A26E1A (UnityPlayer) DestroyObjectHighLevel_Internal
    5. 0x00007FFEE1A29BDD (UnityPlayer) UnloadGameScene
    6. 0x00007FFEE1A5DFE1 (UnityPlayer) LoadSceneOperation::playerLoadSceneFromThread
    7. 0x00007FFEE1A5C969 (UnityPlayer) LoadSceneOperation::IntegrateMainThread
    8. 0x00007FFEE1A5F2FC (UnityPlayer) PreloadManager::UpdatePreloadingSingleStep
    9. 0x00007FFEE1A5F61D (UnityPlayer) PreloadManager::WaitForAllAsyncOperationsToComplete
    10. 0x00007FFEE1A5EF19 (UnityPlayer) PreloadManager::UpdatePreloading
    11. 0x00007FFEE1A49844 (UnityPlayer) `InitPlayerLoopCallbacks'::`2'::EarlyUpdateUpdatePreloadingRegistrator::Forward
    12. 0x00007FFEE1A38D47 (UnityPlayer) ExecutePlayerLoop
    13. 0x00007FFEE1A3C9C8 (UnityPlayer) PlayerLoop
    14. 0x00007FFEE134B6CB (UnityPlayer) PerformMainLoop
    15. 0x00007FFEE134A01A (UnityPlayer) MainMessageLoop
    16. 0x00007FFEE134E646 (UnityPlayer) UnityMainImpl
    17. 0x00007FFEE135224B (UnityPlayer) UnityMain
    18. 0x00007FF732A511F2 (BeatAim) __scrt_common_main_seh
    19. 0x00007FFF79437BD4 (KERNEL32) BaseThreadInitThunk
    20. 0x00007FFF7A08CED1 (ntdll) RtlUserThreadStart
    Crash dmp assembly:
    Code (csharp):
    1. GameObject::WillDestroyGameObject:
    2. 00007FFF3635C6C0  mov         qword ptr [rsp+8],rbx
    3. 00007FFF3635C6C5  push        rdi
    4. 00007FFF3635C6C6  sub         rsp,20h
    5. 00007FFF3635C6CA  mov         dword ptr [rcx+78h],10h
    6. 00007FFF3635C6D1  mov         rbx,qword ptr [rcx+48h]
    7. 00007FFF3635C6D5  mov         rdi,qword ptr [rcx+60h]
    8. 00007FFF3635C6D9  shl         rdi,4
    9. 00007FFF3635C6DD  add         rdi,rbx
    10. 00007FFF3635C6E0  cmp         rbx,rdi
    11. 00007FFF3635C6E3  je          GameObject::WillDestroyGameObject+46h (07FFF3635C706h)
    12. 00007FFF3635C6E5  nop         word ptr [rax+rax]
    13. 00007FFF3635C6F0  mov         rcx,qword ptr [rbx+8]
    14. 00007FFF3635C6F4  mov         rax,qword ptr [rcx] //crash here
    15. 00007FFF3635C6F7  call        qword ptr [rax+0B8h]
    16. 00007FFF3635C6FD  add         rbx,10h
    17. 00007FFF3635C701  cmp         rbx,rdi
    18. 00007FFF3635C704  jne         GameObject::WillDestroyGameObject+30h (07FFF3635C6F0h)
    19. 00007FFF3635C706  mov         rbx,qword ptr [rsp+30h]
    20. 00007FFF3635C70B  add         rsp,20h
    21. 00007FFF3635C70F  pop         rdi
    22. 00007FFF3635C710  ret
    23. 00007FFF3635C711  int         3
    24.  
    Code (CSharp):
    1. Unhandled exception at 0x00007FFF3635C6F4 (UnityPlayer.dll) in crash.dmp: 0xC0000005: Access violation reading location 0x0000000000000000.
    Next I've tried:
    Rebuild maps with 2019.3 and Addressables 1.5 (game updated to 1.6)
    Game crashes on map load
    Rebuild maps with 2019.3 and Addressables 1.6 (same version in game)
    Everything works fine again

    With this being said I'm not sure what to do. I have 2 options:
    - maps can't be managed by users as they won't keep them updated - kind of kills modding
    - never ever update unity and addressables

    @karl_jones @DavidUnity3d are there any plans of how backward compatible Addressables will be? Packages made in 2019.3 work in 2019.2 but not the opposite way.

    Edit:
    Forgot to mention that it works fine in editor and is backward compatible. Could it be an issue with il2cpp? - Tried Mono also bugged in build.

    Edit2
    Updated stack traces with pdb files
     
    Last edited: Feb 12, 2020
    laurentlavigne likes this.
  23. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    134
    It seems that
    Code (csharp):
    1. Could not produce class with ID 92.
    is the GUILayer class that got removed in 2019.3. I can update my maps and delete it from cameras but would be good in the future to make GameObject::WillDestroyGameObject less likely to crash.
     
  24. senfield

    senfield

    Joined:
    Apr 1, 2019
    Posts:
    28
    OMG yes. Every time I need to make a build script that fixes just some small thing (such as this bug: https://forum.unity.com/threads/bug...ted-and-breaks-the-build.782540/#post-5207681) I have to duplicate nearly the entire BuildScriptPackedMode script and then use some reflection to hook up the parts they made internal. I have griped about this in the past to insufficient avail and am frustrated. Don't lock down code that people are going to want to override!

    I haven't wanted to mess with the build scripts very much as a result. Instead, I ended up just using a separate project to build the mod catalog. If you configure the paths just right in the addressables settings it can work when installed to a directory in Application.persistentDataPath for example.
    Although you need to do this too to make it available: UnityEngine.AddressableAssets.Initialization.AddressablesRuntimeProperties.SetPropertyValue("Application.persistentDataPath", Application.persistentDataPath);
     
unityunity