LoadContentCatalogAsync always returns downlaod size of 0

Version: 1.18.11

I am trying to determine if the catalog I am additively adding to my project is a newer version than the one already in your cache.

It seems to me that when you do the LoadContentCatalogAsync call it auto checks for updates on the catalog and downloads it if its needed, but I am trying to interrupt that and check and see if you already have that catalog in your cache and give the user the option to use the cached one or the new one on the server, but every time I check the GetDownloadStatus().TotalBytes it returns 0.

The 2 percent checks also return 0 the whole time
handle.PercentComplete and handle.GetDownloadStatus().Percent

My code is as followed;

        public static IEnumerator Load(string url, string suffix = null)
        {
            var handle = Addressables.LoadContentCatalogAsync(url, true, suffix);
            if (handle.GetDownloadStatus().TotalBytes > 0)
            {
                Debug.Log("Catalog found");
            }
            else
            {
                Debug.Log("Catalog not found");
            }

            while (handle.IsDone == false)
            {
                Debug.Log($"Downloading catalog: {handle.PercentComplete}/{handle.GetDownloadStatus().Percent}");
                yield return null;
            }
            yield return handle;
        }

The debugs that are printed are as followed;

Catalog not found
Downloading catalog: 0/0
Downloading catalog: 0/0

It only prints the Downloading catalog: 0/0 twice, to be clear.

I believe the version checking is not working at all and it is always just forcing a new download regardless. Then for some reason, it does not display the status of that download that will always be done.

side note, even if you use CheckForCatalogUpdates after running load then making a change on s3 it will return no update found as well

This is the method I am going to use

Going to upload the hash and json to s3.
I am going to download the hash file and json manually from s3, storing where I want on the user’s device.
I will then use the LoadContentCatalogAsync method to load my newly downloaded json from its download location.
This should work for just getting the catalog to download from s3 and be usable.

On the next iterations of a user using the system, I will check the location of the downloaded json/hash and manually compare the local hash with the hash of the s3 file and will manually download and replace the file if different and the user wants updates this session.
Once that’s done I’ll run the LoadCatalog function.

Shame we have to basically reimplement versioning, but this should work. I Will report back with my findings.

Have it working.

Here is how to check if the catalog needs updating.

public static AsyncOperationHandle<bool> IsCatalogUpdated(MonoBehaviour owner, string uri)
{
    var resourceManager = new ResourceManager();
    var op = new ContentUpdateCheckOperation(owner, uri);
    return resourceManager.StartOperation(op, default);
}

public class ContentUpdateCheckOperation : AsyncOperationBase<bool>
{
    private readonly MonoBehaviour _owner;
    private readonly string _uri;

    private UnityWebRequest uwr;


    public ContentUpdateCheckOperation(MonoBehaviour owner, string uri)
    {
        _owner = owner;
        _uri = uri;
    }


    protected override float Progress => uwr.downloadProgress;

    protected override string DebugName => "Download Operation";


    protected override void Execute()
    {
        _owner.StartCoroutine(DownloadData(_uri));
    }


    private IEnumerator DownloadData(string uri)
    {
        using (uwr = new UnityWebRequest(uri))
        {
            uwr.downloadHandler = new DownloadHandlerBuffer();
            yield return uwr.SendWebRequest();

            if (uwr.result != UnityWebRequest.Result.Success)
            {
                Failed(uwr);
            }
            else
            {
                Succeeded(uwr);
            }
        }
    }


    private void Succeeded(UnityWebRequest www)
    {
        var available = IsUpdateAvailable(www.downloadHandler.text);
        Complete(available, true, www.error);
    }


    private void Failed(UnityWebRequest www)
    {
        Debug.LogWarning($"Content catalog update check failed with error: {www.error}");
        Complete(false, false, www.error);
    }

 
    private bool IsUpdateAvailable(string hash)
    {
        var localHash = GetLocalHash();
        Debug.Log($"Local Hash: {localHash}, S3 hash: {hash}. Same: {localHash.Equals(hash)}");
        return !localHash.Equals(hash);
    }
 

    private string GetLocalHash()
    {
        const string catalog = "catalog_2021.10.08.04.31.12.hash";
        var path = Path.Combine(DownloadLocation, catalog);
        var localHash = LoadFile(path);
        return localHash;
    }


    private static string LoadFile(string path)
    {
        if (File.Exists(path) == false)
            return "";
 
        using var file = new StreamReader(path);
        var fileText = file.ReadToEnd();
        file.Close();
        return fileText;
    }

 
    protected override void Destroy()
    {
        uwr.Dispose();
        uwr = null;
    }
}

then you just need to do something like

        private IEnumerator UpdateCatalog()
        {
            var remoteHashPath = REMOTE_CATALOG.Replace(".json", ".hash");
            var updateOperation = IsCatalogUpdated(this, remoteHashPath);
            yield return updateOperation;
            if(updateOperation.result)
            {
               const string catalog = "catalog_2021.10.08.04.31.12";
               var hashPath = Path.Combine(DownloadLocation, $"{catalog}.hash");
               var jsonPath = Path.Combine(DownloadLocation, $"{catalog}.json");
               yield return Download(remoteHashPath, hashPath);
               yield return Download(REMOTE_CATALOG, jsonPath);
               yield return Addressables.LoadContentCatalogAsync(jsonPath, true, suffix);
               Debug.Log("Catalog loaded successfully");
            }
        }

We’re having similar issues. Thanks for posting the alternative solution @sniffle63 ! We’re trying it out.

I hope Unity sees this!

No worries, I left a few things out, and is not the final version so feel free to ask if you need any help/explanations on it

It’d be great to check out your final version.

using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;

namespace Content_Update
{
    public class CatalogUpdateCheckOperation : AsyncOperationBase<bool>
    {
        private readonly MonoBehaviour _owner;
        private readonly string _host;
        private readonly string _catalog;
        private UnityWebRequest uwr;


        public CatalogUpdateCheckOperation(MonoBehaviour owner, string host, string catalog)
        {
            _owner = owner;
            _host = host;
            _catalog = $"{catalog}.hash";
        }


        protected override float Progress => uwr.downloadProgress;

        protected override string DebugName => "Content Catalog Update Check";


        protected override void Execute()
        {
            _owner.StartCoroutine(DownloadData());
        }


        private IEnumerator DownloadData()
        {
            var uri = Path.Combine(_host, _catalog);
            using (uwr = new UnityWebRequest(uri))
            {
                uwr.downloadHandler = new DownloadHandlerBuffer();
                yield return uwr.SendWebRequest();

                if (uwr.result != UnityWebRequest.Result.Success)
                    Failed(uwr);
                else
                    Succeeded(uwr);
            }
        }


        private void Succeeded(UnityWebRequest www)
        {
            var available = IsUpdateAvailable(www.downloadHandler.text);
            Complete(available, true, www.error);
        }


        private void Failed(UnityWebRequest www)
        {
            Debug.LogWarning($"Content catalog update check failed with error: {www.error}");
            Complete(false, false, www.error);
        }

 
        private bool IsUpdateAvailable(string hash)
        {
            var localHash = GetLocalHash();
            Debug.Log($"Local Hash: {localHash}, S3 hash: {hash}. Same: {localHash.Equals(hash)}");
            return !localHash.Equals(hash);
        }
 

        private string GetLocalHash()
        {
            var path = Path.Combine(CacheLocation, _catalog);
            Debug.Log($"getting local hash from path: {path}");
            var localHash = LoadFile(path);
            return localHash;
        }


        private static string LoadFile(string path)
        {
            if (File.Exists(path) == false)
                return "";
   
            using var file = new StreamReader(path);
            var fileText = file.ReadToEnd();
            file.Close();
            file.Dispose();
            return fileText;
        }

 
        protected override void Destroy()
        {
            uwr.Dispose();
            uwr = null;
        }
    }
}
        public static AsyncOperationHandle<bool> IsUpdateAvailable(MonoBehaviour owner, string host, string catalog)
        {
            var op = new CatalogUpdateCheckOperation(owner, host, catalog);
            return ResourceManager.StartOperation(op, default);
        }


        public static void Release<T>(AsyncOperationHandle<T> op)
        {
            ResourceManager.Release(op);
        }
        private IEnumerator CatalogUpdateCheck()
        {
            var updateOperation = IsUpdateAvailable(this, HOST, CATALOG);
            yield return updateOperation;
            var updateAvailable = updateOperation.Result;
            ContentCatalog.Release(updateOperation);
            if (updateAvailable)
            {
                UpdateFound();
            }
            else
            {
                NoUpdateFound();
            }
        }
        private IEnumerator UpdateCatalog()
        {
            var remotePath = Path.Combine(HOST, CATALOG);
            var localHashPath = Path.Combine(CacheLocation, $"{CATALOG}.hash");
            var localJsonPath = Path.Combine(CacheLocation, $"{CATALOG}.json");
            yield return Download($"{remotePath}.json", localJsonPath);
            yield return Download($"{remotePath}.hash", localHashPath);
            yield return Addressables.LoadContentCatalogAsync(localJsonPath, true, suffix);
            Debug.Log("Catalog loaded successfully");
        }
        public static IEnumerator Download(string uri, string storagePath)
        {
            using var uwr = new UnityWebRequest(uri, UnityWebRequest.kHttpVerbGET);
            uwr.downloadHandler = new DownloadHandlerFile(storagePath);
            yield return uwr.SendWebRequest();
            if (uwr.result != UnityWebRequest.Result.Success)
                Debug.LogError(uwr.error);
            else
                Debug.Log("File successfully downloaded and saved to " + storagePath);
        }
        private static ResourceManager ResourceManager =>
            _resourceManager ??= new ResourceManager(new DefaultAllocationStrategy());

This is still a more simplified version of it because it would be too complex to show the exact way im using it. But I believe this is enough information for someone to implement it into their project. (for example, these calls are in a few different files and not just 1 and I combined a few functions together for ease of reading)

might change the enumerator to use a Task also so I don’t have to pass the MonoBehaviour into the operation

Probably would want better failure checks also so you dont end up with a corrupt file if their internet goes down or something.

Technically you don’t have to use the AsyncOperations either, just seemed more correct to use them. could really just take the coroutine out of it and run it by itself

Also technically don’t have to download the hash file again because we already did in the catalog check and could just create a file with that returned hash.

Thank you!

1 Like

Flagging for the team to review!

2 Likes

Hey all, looking through this now. I’ll make a ticket for us to fix this. I’m glad that a workaround was found in the meantime. Thanks for bringing this to our attention!

2 Likes

Can also confirm that myDownloadHandler.percentComplete still returns 0 every time until the final image loads, then it returns 1, in Unity 2020.3.24

1 Like

Is there any update?