Loading AssetBundle's with the same name

We have a growing number of AssetBundle’s located in the cloud. We download these bundles at runtime and instantiate the prefabs inside them. Our content team seems to have bundled quite a few of these AssetBundle’s with the same bundle name even though the files inside them are entirely different. When we try to download and load more than one of these AssetBundle’s on the client side, Unity throws an error on the second and later bundles: “Error while getting Asset Bundle: The AssetBundle ‘https://myserver.com/files/id/bundlename’ can’t be loaded because another AssetBundle with the same files is already loaded.”

Our loading code is as follows:

DownloadOperation = UnityWebRequestAssetBundle.GetAssetBundle( _model.ModelFileUrl, Convert.ToUInt32(_model.UpdatedAt), 0).SendWebRequest();

DownloadOperation.completed += operation =>
{
    var webRequest = DownloadOperation.webRequest;
    if (webRequest.isNetworkError || webRequest.isHttpError)
    {
        throw new Exception($"Error while downloading asset: {webRequest.error}");
    }

    _assetBundle = DownloadHandlerAssetBundle.GetContent(webRequest); // this is the line that causes the error
    if (!_assetBundle)
    {
        throw new Exception($"Could not get content from AssetBundle with ID: {_model.Id}");
    }
    // use _assetBundle to instantiate prefab
}

Right now, we can handle this error so no crashes happen, but we would like to know if there is a way to solve this without re-bundling the AssetBundle’s with the same bundle name. Any insight on this would be much appreciated.

I have the same problem and I don’ know how to handle this error and avoid crashes. Could you tel me how you did it ?
Thanks

Unity’s API doesn’t seem to support this in any way, so we had to write a custom class AssetBundleManager which handles loading and caching AssetBundle’s, so that we wouldn’t attempt to load two bundles with the same name. As for the pre-existing bundles, unfortunately we had to re-bundle them all.

You need to switch to using this version of the GetAssetBundle API:

public static Networking.UnityWebRequest GetAssetBundle(string uri, CachedAssetBundle cachedAssetBundle, uint crc);

Reason:
Asset bundle names can contain path notations My/AssetBundle1/prefabs and My/AssetBundle2/prefabs which is not something that can be determined from a URL automatically easily: https://mygames.com/mygame/bundles/My/AssetBundle1/prefabs vs https://mygames.com/mygame/bundles/My/AssetBundle2/prefabs. In both of those cases the cached asset bundle name becomes prefabs which is incorrect. The CachedAssetBundle struct resolves this issue by requiring you to provide the cached asset bundle name and hash instead of us pulling it from the URL. So you construct a CachedAssetBundle with the name My/AssetBundle1/prefabs and correct hash, and the cache will then store it using that name and hash and be able to distinguish it correctly from My/AssetBundle2/prefabs.

3 Likes

Hi Ryanc_unity,
Your answer got me excited because it’s exactly what I need. However, it does not appear to work the way you describe - the downloaded bundle still uses a name derived from the url instead of the name provided with CachedAssetBundle, so I am still getting “Error while getting Asset Bundle: The AssetBundle ‘https://myserver.com/files/id/bundlename’ can’t be loaded because another AssetBundle with the same files is already loaded.”.
The docs say that the name in CachedAssetBundle is “AssetBundle name which is used as the customized cache path.”. I wonder if that means that it can only be used to cache more than one version of the bundle, but one still has to manually make sure only one of them is loaded at any given time…

I’ve been trying to figure out the caching system but Im just a little confused on how to get the hash and the name of the asset bundle before it even gets downloaded with the .GetAssetBundle()? Like when you are constructing the CachedAssetBundle struct do you have to download the manifest for that asset bundle first then pull its hash and name THEN pass the cachedAssetBundle into the .GetAssetBundle(uri, cachedAssetBundle, 0)?