Resources.LoadAll<Quest>("") throws weird errors in a built game

As near as I can tell only Resources.LoadAll("") causes this and not
Resources.LoadAll("");

The asymmetry of error is weird - kinda like this old issue with stuttering on loading Quests but not InventoryItem. I had the same stuttering issue and implemented the lookup which addresses the stuttering but not this problem with throwing errors. It doesn’t seem to affect gameplay beyond the occasional but often extreme freezes of the game while it loads. The caching solution in the linked thread makes sure the issue happens only the first time the game loads.

Still I am perplexed why there is this weird asymmetry between LoadAll on Quests but not InventoryItem.

One difference might be the use of [System.Serializable] in Quest but not InventoryItem. Unity seems to not like something about the way Quest is set up.

I will attempt to solve this by making a separate Objective class (I was going to do that anyway) and experimenting on Quests with no reward.

Snippet in text format

Builds.framework.js.gz:10 The referenced script (UnityEngine.Rendering.UI.DebugUIHandlerValueTuple) on this Behaviour is missing!

A scripted object (probably UnityEngine.Rendering.UI.DebugUIHandlerHBox?) has a different serialization layout when loading. (Read 32 bytes but expected 64 bytes)
Did you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts?

Are you using my old InventoryItem Editor script by chance?
I know this comes up in newer versions of Unity with the old InventoryItemEditorWindow I wrote, because I serialized some properties related to the foldouts within the ScriptableObjects themselves, which causes this mismatch.
It should have no affect on the game itself, however, other than getting spammed by these messages in a Development version of the game.

Now the DebugUIHBox, DebugUIToggle, DebugUIHandlerToggleHistory, those I’m not seeing… it seems to be saying that there are misssing scripts on one or more GameObjects within the scene (likely ones that are in an Editor folder). In this case, the serialization of the GameObject itself will be off. I suspect that the bug is due to Unity serializing the size of the serialization in the .meta files, and using that when packaging, but any serialized information that was expressly declared and serialized exclusively within editor code is stripped out, and the header is not being updated (more of a Unity error).

Which version of Unity are you using? I only started running into this in my built players in Unity 2022.

Are you referring to this? No. Not yet. I want to - it’s in the backlog.

It comes up only when Resources.LoadAll<Quest>("") is called. InventoryItem loading doesn’t seem to affect it.

If you can load up the console, you’ll also see these in a non-development build of the game. It might be a red herring too but I know spamming the log with errors tends to result in performance drops which could be an explanation why we saw performance gaps loading Quest but not InventoryItems. I haven’t done detailed profiling to be sure.

I started looking deeply at logs in built games after I made a build and wanted to troubleshoot various build-related issues - stutters, performance drops, unexpected null values, etc.

Yeah it does seem like a Unity error, but it is weird that it is triggered by loading Quest and not InventoryItem. I am getting pretty close to refactoring Quest to not have those System.Serializeable objects. That’s my best guess since I remember seeing something Unity and nested/custom Serialized objects. I am “out of time” for this afternoon but will come back to this in the evening.

2022.03.10 - the latest LTS

That is especially wierd… my game that I’m getting the messages (Spellbound Hunter) isn’t currrently using any Quests… but I still get spam, spam, spam, spam, baked beans and spam with these in a Development build. That being said, the Player Log gets filled regardless of development or not. Gold mine of information if somebody says your game crashed. Unfortunately, that gold mine is hidden deep inside a giant load of other messages, LOL.

Don’t forget to use the ` whenever you use a statement like <Quest> (or escape the < with a \<) or the <Quest> will be replaced with empty space… It originally read Resources.LoadAll("").

Actually the performance gap with Quests was simply that we were calling Resources.LoadAll<Quest>() multiple times. Within a built player, there is absolutely no excuse for calling this method more than once within a game because, like the InventoryItem, we can cache the result in a static Dictionary.

2022, where LTS means “Less Than Satisfactory”.
(I’m actually using 2022.3.5 in Spellbound Hunter, which goes against my own advice, while it can be used, there are just unexplained landmines – have you encountered the “My UI forgot it’s position on the screen and 1/2 of it is outside the viewscreen” bug yet?)

Ok scratch that. I am getting it within the InventoryItem class too. So you’re getting a similar spam of log messages in your built game?

On the performance issue, I was curious why only Quest was impacted on not InventoryItem? Maybe it was because it was called multiple times in a short time span on Quest?


I decided to try something. I did the following to Quest GetByName and where do you think the spam of error message came from? I added a comment to show. Note: I used “LogWarning” instead of normal Debug.Log because I filtered out normal log statements.

So here is what I think is actually happening. The first time you call Resources.LoadAll() for a session you get the spam. Potentially it seems to happen once per class it’s called from but if you call Resources.LoadAll for two different Ts within the same class it only seems to happen only for the first call.

Which I think means there’s an option to preload all the caches and just get all the errors out in the beginning so at least the rest of the logs for the session become more useful.

public static Quest GetByName(string questName)
        {
            if (masterQuestDictionary == null)
            {
                masterQuestDictionary = new Dictionary<string, Quest>();
                Debug.LogWarning("Before loading invItems now");
                // spam of errors came between these two debug lines
                var itemList = Resources.LoadAll<InventoryItem>("");
                Debug.LogWarning("*** Just called LoadAll invItems now");
                foreach (var item in itemList)
                {
                    Debug.LogWarning($"item: {item.GetItemID()}");
                }
                
                Debug.LogWarning("Before loading quests now");
                var questList =      Resources.LoadAll<Quest>("");
                Debug.LogWarning("*** Just called LoadAll quests now");
                foreach (var quest in questList)
                {
                    if(masterQuestDictionary.ContainsKey(quest.name)) Debug.Log($"There are two {quest.name} quests in the system");
                    else masterQuestDictionary.Add(quest.name, quest);
                }
            }
            return masterQuestDictionary.ContainsKey(questName) ? masterQuestDictionary[questName] : null;
        }

That’s actually NOT what I would have expected. It’s possible that Resources.LoadAll<T>() is also caching results, in addition to our own cache (though I still found using our cache more performant, likely because we’re using a Dictionary (glorified hash table) and hash tables are FAST.

This generally should be happening in a saved game, certainly with InventoryItem. In fact, unless you’re playing around with instantiating the inventory item (mandatory for procedural stats), you won’t see the errors outside of RestoreState() when the game should be blacked out.

No, I’m going to correct that… if you have a condition that requires an InventoryItem or a quest, the first time that condition is evaluated, you’ll hit the caching process and the spate of warnings. A Pre-cache may not be a bad thing.

Fortunately, memory management is not a huge issue with our InventoryItems and Quests. They are, relatively speaking, a pretty low memory resource. In fact, at one point, I’d considered a Json based approach to defining both of these, the goal being to having the repository of InventoryItems and Quests be easily defined in either a .config file or stored on a server (UGS Cloud Services in particular). This would allow a developer to add or tweak Inventory Items or Quests without sending out an update to the game. The game would retrieve the database of items and quests at the same time it retrieved the character’s permanent save file.

Yes. Very likely caching. I noticed even on the performance side it’s only bad the first time around. But it seems to expire the cache quickly.

Ok cool. I can take care of that. Yeah I have all those scenarios going on.

I know you’re working on some cloud integration … is it part of the network course or will it be published in tips and tricks? I’m looking forward to that.

For now, it will be part of the tips and tricks. The Network course is more geared towards a client server model where either a clients and servers are connected by a lobby (meaning for an RPG, you would have to keep a computer running as a server) or the server is hosted on a Linux instance(s) in the UGS service (bring money, once that $800.00 six month credit expires!).

The model I’m looking at will keep save files in Unity Cloud Saving. The new storage for cloud save files is startlingly large (1Gig per player for save files, but if you go that high, you’ll start reaching past the free tier in terms of bandwith sooner than later).

Unity also has Cloud Code, which could, for example, be used to procedurally generate equipment drops, etc, at a fairly liberal rate.

More importantly, Unity now has Cloud Content delivery, where the first 50Gib of bandwidth (apparently, the actual storage is unlimited, but we’ll see) is free each month. Now if you’re War of the Visions, who puts out 250Mb to 500Mb of new content every week, with a million + users, you’ll cruise past that limit on the 1st week of the month, but for the rest of us, this is quite manageable. Their goal with this is to push us away from Resources, and into Addressables.

There are some technical challenges with Addressables (and Cloud saving, for that matter!)… Even if the Addressables are packaged up with your eventual installation, they are all accessed (cached) via true async calls. This isn’t multi-threaded stuff, but it does change the way we have to interact with our content. For example, to get the save files from the server, I have to request them from the server and await their arrival. The same is true for content delivery. This means caching that data on our end is a good idea once the game starts.

The cloud saving system is actually done at this point. I had been preparing to go through the process of adding it as a tutorial addition to the Json saving system, and then somebody at Unity decided to try to end the world as we know it. I’ve gotten side tracked into another project, and at this point, to keep from losing where I’m at with that project, I’m putting off the cloud saving system tutorial for a bit longer.

Funny story on that cloud saving… when I adapted it into Spellbound Hunter (where I’ve had a chance to absolutely gorilla test it!), there were much stricter limits on per player saves, so I did a lot of saving measures to reduce the size of the Json files…
Instead of using class names, which include the fully qualified class name (RPG.Movement.Mover, GameDevTV.Inventories.Inventory, etc), I added a method to the interface to allow ISaveables to return their own unique identifier… so “Mover” or “Inv”, or “Eq”, etc, and in InventoryItem. I also forced enums to int instead of string, converted the ItemID from the string returned by GUID to Base64, etc… everything I could to squeeze every last bit I could out of the save file but still be a Json. With the new limits, the tutorial isn’t going to bother with any of that.

Looking forward to all of this!

Privacy & Terms