Addressables in Unity: bundles and catalogs issues
Today we will share our experience of the Addressables usage and tell about the most significant problems we encountered while Unity development using remote bundles and builds. Therefore, there will be only non-obvious and more complex problems that took us a lot of time to find and solve. We assume that you’re familiar with concept Addressables at least superficially.
First things first
During the development of a mobile AR application, we needed the ability to update content without rebuilding the entire application, this approach is usually included in the Continuous Delivery pipeline. We decided to use the Addressables system, which is essentially a wrapper for Asset Bundles in Unity. This was our first experience with this system, none of the team had ever used Addressables in projects before.
Looking ahead, I would like to say that all the problems mentioned here were described in the official documentation. Although we tried to take into account all the nuances, carefully read the documentation and examples, some things became clear only with their practical use. In this regard, we do not want to show the correct way of working with Addressables, but rather share our experience in solving non-obvious and complex problems.
Issue 1. Duplicating updated bundles or how to take all the space on a CDN
One of the first problems we encountered was the mechanism for updating bundles in Unity. More precisely, the fact that Unity does not remove old bundles when new versions are created.
When you click “Update a Previous build”, Unity creates new bundles and puts them in the specified folder, while the old bundles are not deleted. In this case, by updating the bundle 2 times, you will have 3 files in the folder, each of which is a specific bundle version.
Keep this in mind — if you don’t delete old bundles, your disk space can run out very quickly.
Although Unity does not remove old bundles by default, you can specify a maximum cache size. When the weight of all bundles exceeds the maximum cache size, Unity deletes the previous bundles.
Issue 2. Duplicating updated local bundles or a build that is always growing
Another problem comes from the previous one: as we understand, updating bundles creates many versions of the same bundle, taking up more and more space. But what if we are talking about local bundles? After all, local bundles are saved to the StreamingAssets
As we remember, everything that is in StreamingAssets is embedded into the build. This means that after making several updates, we will get many versions of the same bundle in the StreamingAssets folder. Once we get a new build, we will pack all these versions into it. Despite the fact that only the last one will be used, all the previous ones will be embedded into the build. Thus, with each update and rebuild, the build weight will constantly increase until you remove the old versions of the bundles from StreamingAssets.
Issue 3. “NullReferenceException” error trying to load a scene or an asset from the local bundle after updating
Prior to that, the problems were rather production-related and not so critical. Now we will talk about the package creators’ presumption, due to which (if you don’t know it) bundles in builds will be breaking before the update.
Initially, we had remote bundles with the main scene in our project. However, due to the weak mobile Internet, this scene could take a very long time to load, and the user would have to wait for several minutes on the loading screen. Therefore, we decided to change the bundle type to local — now the bundle is embedded into the build, which fixed the problem with long loading times.
Development went on, we changed other bundles, changed the code, which is why we almost always rebuilt all Addressables.
Approaching the release, we had to test the entire system: we built the application, built Addressables — everything worked well. Then we changed content in remote bundles, enabled “bundle update”, launched the build (without rebuilding), and got this:
NullReferenceException: Object reference not set to an instance of an object., stackTrace = UnityEngine.ResourceManagement.ResourceProviders.SceneProvider+SceneOp.InternalLoadScene…
The loading screen was stuck on this error at the stage of loading the main scene (the main scene is in the local bundle). Moreover, if you try creating a new build, this error doesn’t appear, and the download proceeds without any problems.
After various tests and looking for information on the forums, we realized that this problem is also encountered by some developers while loading scenes from local assets. The result of the research was the following conclusion:
If you change something in the local group and update Addressables (without rebuilding), then the previous build will “lose” the updated local bundles, throwing a NullReferenceException error when trying to access them.
Why is this happening?
Fortunately, we are using our own CDN server, so we could run any tests to find the problem. We tried to remove all catalog files from the CDN. And surprisingly, this solved the problem: of course, the remote bundles were no longer updated either, but the old build no longer produced a NullReferenceException error and successfully loaded the local scene.
As it turned out, the reason for this problem is that Unity does not separate the logic for loading local and remote bundles in any way (in the subtext of this problem). All bundles have their own address in the catalog file. Not only remote ones, but also local ones. So, what happens when the user launches the application? Unity downloads catalog from CDN server. If there is no catalog available, Unity uses the local catalog that is built into the build.
In the local catalog, all bundles have links that were relevant at the time the build was created. In the remote catalog, all links change when updated.
After downloading the remote catalog file, Unity replaces the local one with it. And now all paths to bundles are taken from the new catalog. But since we updated the local bundle, its path is different from the original one.
To make it easier to understand, let’s look at this in a diagram:
We built Addressables and created build #1. This build has our local bundle with the main scene (MS_), and the local catalog has a link to it.
The CDN contains remote content bundles and a catalog identical to the one included in the build.
This is what happens if Unity is unable to download a new catalog file from the server: Unity will look at the local catalog for the bundle address and simply load it from disk, since it is a local bundle.
Now let’s run the Addressables update and rebuild the build for clarity (build #2)
Several problems can be seen here at once. We have build #2, which contains 2 local bundles, which are actually two versions of the bundle with the main scene. So now, there are 2 versions of the main scene in the local build. And both versions take up space. At the same time, the local catalog contains a link only to the new version (222), while the old version (111) is not used anywhere and hangs like a dead weight. This is a visual illustration of the first problem described in this article.
Now let’s look at the CDN: here we see the same thing — we happened to have duplicated deleted bundles. Now we have several versions of our content at once, which just takes up space on the CDN, since the catalog file (identical to the local one from build #2) refers only to new versions of the bundles. This means that we could delete all the first versions (… 1) without consequences and free up storage space.
It is also important to understand that at this stage build #1 refers to the remote catalog, which now refers to the second version of the bundles.
Now if we try to run the first build, it will successfully update its catalog and download new versions of the remote bundles (Content_, Icons_). But what should he do with the local bundle? And so, we come to the reason for this error.
In brief, this is what happens with two builds:
Build #2: it needs to load the scene => needs to download the new version of the catalog from the server => reads the path to the bundle with the desired scene => loads the bundle using the resulting path. Since the path is local, it boots directly from disk. Scene is loaded successfully.
Build #1: it needs to load the scene => needs to download the new version of the catalog from the server => reads the path to the bundle with the desired scene => tries to load the bundle using the resulting path. Since the path is local, it boots directly from disk. But this build does not have the MS_222 bundle, since at the time of this build #1 there was no such bundle yet. This is where the NullReferenceException error occurs.
And so, we found a problem: when updating bundles, the link to any (including local) bundle changes for the new and old build, but the bundle itself exists only in the new build. How do I fix this now?
Unfortunately, Unity offers a rather inconvenient way, but it still includes “accidental error protection”.
You can read more details HERE, but in short: you must mark ALL your LOCAL groups as “Cannot Change Post Release”.
What exactly does this option do? It tells Unity that this bundle should not change after a rebuild. Actually, this means that if you change something in this bundle and execute Update Addressables, the update WILL NOT BE PERFORMED for this bundle. That is, even after building a new assembly, the local bundle will not be updated. To update it, you will have to rebuild Addressables entirely without using Update Addressables. There is also another way, which, however, seems very impractical, as it creates useless copies of bundles, taking up more and more space in new assemblies (until you do a complete rebuild of Addressables). You can read more about it HERE and HERE.
Finally
Surprisingly, all of these problems are documented in the official Addressables documentation. However, some features of the system “may seem counter intuitive” ©.
The Addressables system itself seems to be relatively stable. The main problem is that some things are done in a very non-obvious, illogical and inconvenient way.
Hopefully this article will save you time and a few annoying problems when working with Addressables.