Loading issue after patch update

bodkin1289

New member
Hello,

I've recently patched our game and are now getting reports of scrambled player inventories.

This manifests by installing the patch then loading a save file, the inventory system then reports the items incorrectly (name, icon, description) however, the quantity is correct.

I have tested a save file with the patch on a target system and can see the bug occur, rolling back the patch restores the inventory correctly without loss. Using the same save file in the Editor on the full patch build displays the correct inventory items and quantities. The scrambling also appears to happen with mutable or immutable and unique items.

Build Details

--------------

Unity 2020.3.26f1

Dialogue System Save System 2.2.28

UIS 1.2.10
---------------

I'm confused as to why a patch would be reporting incorrect items, is it possible that an item's UID could have been altered at runtime? The items in shops etc are all listed correctly so this appears limited to an issue during load. Other items being loaded external to the UIS, during the same load cycle are being restored correctly, so this indicates something is amiss/different with the UIS restoration/update.

I've exported two version of the inventory database, before and after patch, but there are no differences between the values etc.

Any pointers as to what may be causing or areas I could look deeper into would be gratefully received.

Thanks in advance for any help
 
This problem rings a bell. I'm sure someone mentioned it a long time ago but I can't find the thread anymore.
I was never able to reproduce it and if I remember correctly it seemed to fix itself on its own for the person who posted the problem.

The only way I can think of that this would happen is if the Item IDs changed during the update.

The way the InventorySaver savers items is by having a struct (amount, itemID).
If the ItemID is Mutable and Unique it is saved in the InventorySystemItemSaver which saves the items that are created at runtime with their ID, including their attribute values. To do that it saves the ItemDefinition ID so that it can recreate to the item when loading the save.
So unless the ItemDefinition ID has changed for some reason (Or that when building your game the ItemDefinition is given a new ID)
I can't think of any reason this would happen
 
I did see mention of this in the forum, and as you said there was no follow up.

I've compared the Item and Item category ID's of the two release versions and they aren't changing for any of the items, what is more confusing is that the save file works correctly when extracted and used within the editor without warnings or other issue.

Running it on the target I can see the queries going to the inventory manager and it reporting either with the incorrect item or non-found response. the ID's appear to be correct, but I will test this again to ensure I am validating the correct reference number.

Could this be an initialization issue? am I attempting to load the items before the Inventory Manager has correctly initialized its own internal database?

Do you have a state diagram/activity diagram or similar that would explain its loading/initialization process so that I can try and intercept the load at specific points to check that
  1. the values are actually being extracted correctly,
  2. that the Inventory Manager is correctly initializing
  3. that the ID's are not being incorrectly processed.
During a build is the database rebuild or does it make reference to the asset files? As this is patch, there may be an issue with how the patch has been generated so if I know what files etc can be changed I can try and isolate this issue from there.

Does the manager use internally generated ID's or does it use another UID generator?

As I'm trying to reverse engineer where this error could be occurring so are there any additional areas I could check?

Thanks
 
Could this be an initialization issue? am I attempting to load the items before the Inventory Manager has correctly initialized its own internal database?
I'm not 100% sure, but I don't think that's the case.

Do you have a state diagram/activity diagram or similar that would explain its loading/initialization process so that I can try and intercept the load at specific points to check that
  1. the values are actually being extracted correctly,
  2. that the Inventory Manager is correctly initializing
  3. that the ID's are not being incorrectly processed.
Sorry I don't have a diagram.

The InventorySystemManager is always the first thing that gets initialized. Because everything else needs to know about the Item Categories and Definition

You can load the save data after the InventorySystemManager is initialized. When loading the save data you simply load it in cache. The InventorySaver will either load the data at the same time if it was initialized, or it will fetch that cached save data when it does get intialized.

So I do not think the order in which you load the items the problem. But just to be thourough lets not ignore the idea completely

As I mentioned previously the InventorySystemItemSaver is the one in charged of saving the "dynamic" items. So this is where you should start looking into. Maybe it is trying to load an item with an ID but that ID is already taken by another Item?

The InventorySystemItemSaver is set to always load first before any other saver. There is a protected priority property that can be set to change the order in which savers are loaded, similar to the Unity Script Execution order.

During a build is the database rebuild or does it make reference to the asset files? As this is patch, there may be an issue with how the patch has been generated so if I know what files etc can be changed I can try and isolate this issue from there.
The build is not rebuilt. It is used as is as a scriptable object referencing other scriptable objects

Does the manager use internally generated ID's or does it use another UID generator?
Yes we generate IDs internally. Its a simple uint random generator. When we generate new IDs we always check that it doesn't match an existing ID, if it does we change its ID with a new random ID until it is a safe ID.

As I'm trying to reverse engineer where this error could be occurring so are there any additional areas I could check?
I think a good way to debug this would be to print any Item that is being registered by the InventorySystemManager. As well as what items are loaded by the InventorySystemItemSaver and when.


If you are able to find the root of this issue it would put my mind at ease, I really don't like having bugs that I can't reproduce myself. I'll make the apropriate changes to the code or documentation once we figure this out.
 
OK, thanks for the information.

I'm using the Dialogue System as the main save system, but looking at the loading sequence this simply calls into the Inventory Manager save/load routine.

I will see what I can dig up and hopefully report back with an explanation.
 
Update so far
---------------
I've identified that the issue lies inside the InventorySystemManagerItemSaver and Shared.dll, as it does not correctly interpret the item save values, on either the target or in the editor,

I've thought maybe it was an endian issue, but have ruled this out through manually manipulating the values. What I have noticed is that the more builds and patches I do the further away from the original values the recovered values appear to be. This is demonstrated by an increasing number of "item not found" messages. I've also conducted numerous tests to investigate the process but to no avail.

There does seem like there may be an issue with how the values are being deserialized and interpreted on load, possibly stale statics/invalid unity object references or something similar. but I'm now looking at creating a bespoke saver to ensure I can recover future saves, as at present updating our game, trashes the players inventory and forces the player to start the game again, not an optimal solution especially as we use the inventory system heavily.
 
I'm very sorry to hear that.
If you find any clues do let us know.

@Justin wrote the serializer we use for saving and loading. Justin can you think of any reason this could happen?
 
The serializer/deserializer is consistent so I don't think that's the cause. It has been used in Behavior Designer and the character controller for many years.

For this type of situation it's best if we can somehow reproduce it on our end so we can track down the issue. It could be a number of things and it's really hard to debug unless you can step through the code/data and see what is different.
 
As you quite rightly point out, without seeing it and being able to intercept it yourselves its very hard to diagnose.

To that end I've been able to make the issue repeatable using the inventory system demo scene, this is in isolation without any other 3rd party assets being included. so I'm hoping you will be able to see what I'm missing.

To reproduce:
  • create a new unity project (2021.3.4f1 with android support)
  • install UIS from package manager (1.2.10)
  • change build target to Android (Android is used here as an example as its the easiest to set up)
  • connect android device and test connection
  • set demo scene as main scene
  • "build and run" on connected android device.
  • On device
    • check scene runs and can be interacted with
    • using main menu save to File One slot
    • exit main menu
    • exit game on device
  • In unity editor
    • remove all inventory items from player inventory collections
    • add new item definition to inventory e.g. "Super Sword" as item category Sword
    • on new item change icon and change usable item prefab - firewand
    • add 4 UI buttons to add items (Super Sword, Ice Wand, Wood, Surprise Box) I've used a simple custom script with a reference to player inventory that uses the example code here https://opsive.com/support/documentation/ultimate-inventory-system/inventory/item-collections/ using the string name of the item definition provided by the buttons
    • In build Settings select Patch and Run
  • On device
    • use buttons to add items to the player inventory and verify added correctly
    • then select save -> file two
    • then load file one
    • Review the inventory items previously saved
When I have followed these steps I've been able to observe that the original save items have been mangled with descriptions/icons being misrepresented.

If you need it or just want to confirm what I've done, I can send the example project,

Thanks
 
Thank you very much for your detailed reproduction steps.
I was able to reproduce the issue not only in the demo scene but also in the "Save and Load" feature scene. And I used a windows build instead of android, so it's definetly device agnostic.

The ItemDefinition ID saved does not match the item definition in the the database for mutable items.

I'm still investigating why but I haven't found the cause yet. I'm wondering if just the saved item definition ID is incorrect. Or it is that the ItemDefinition ID changes in the build... but that doesn't really makes sense as it seems to only affect mutable items.

I also notice that two mutable items do keep the reference to the same ItemDefinition even if it is the one with the wrong ID.
So it is more probable that the item definition ID changes for mutable items for whatever reason, rather than a new duplicate item definition was created.

I'm going to need more time to diagnose this but thanks to your help I think we should be able to figure it out.
Thank you for your patience
 
Hi Bodkin,

Just an update. I was able to figure out what caused the problem... But I'm not sure how to fix it reliably.

The problem has to do with updating builds and unity objects serialized references. My assumption is that updating an existing build shuffles the references in an unreliable way. So the save data is correct but loading it can cause issues in the updated build.

The loaded item will sometimes use the wrong item definition because the deserializer will find the wrong object or no object at all.

The problem is that once you save -> load -> save after an update, the new save data will most likely be corrupted with thos wrong references.

So at least for now my short term solution:

On line 220 of the InventorySystemManagerItemSaver script:
Code:
//TODO the item definition reference will not match the item definition ID anymore after a build patch.
item.Initialize(false, true, true);
This requires a change to the Item script:
Code:
/// <summary>
/// Initializes and updates the attributes.
/// </summary>
/// <param name="force">Force initialize the object.</param>
/// <param name="updateAttributes">Update the attributes to make sure they are correct?</param>
/// <param name="useDefinitionID">Use the item definition ID instead of the item definition reference? (useful when using the save data).</param>
public virtual void Initialize(bool force, bool updateAttributes = true, bool useDefinitionID = false)
{
    if (m_Initialized && !force) { return; }
    if (useDefinitionID == false && m_ItemDefinition == null) {
        if (m_ItemDefinitionID != 0) {
            m_ItemDefinition = InventorySystemManager.GetItemDefinition(m_ItemDefinitionID);
        }
        if (m_ItemDefinition == null) {
            Debug.LogError($"Item Definition is null: {name} ID:{m_ID} ItemDefinitionID:{m_ItemDefinitionID}");
            return;
        }
    }else if (useDefinitionID) {
        if (m_ItemDefinitionID != 0) {
            m_ItemDefinition = InventorySystemManager.GetItemDefinition(m_ItemDefinitionID);
        }
        if (m_ItemDefinition == null) {
            Debug.LogError($"Item Definition is null: {name} ID:{m_ID} ItemDefinitionID:{m_ItemDefinitionID}");
            return;
        }
    }
    m_ItemDefinitionID = m_ItemDefinition.ID;
    m_Name = m_ItemDefinition.name;
    Deserialize();
    if (m_ItemObjects.Array == null) { m_ItemObjects.Initialize(0); }
    // The item needs to be set as initialized before the attributes are updated or a Infinite loop will occur.
    m_Initialized = true;
    if (updateAttributes) {
        UpdateAttributes();
    }
}

After making this change the problem stopped happening.
While debugging this issue I added quite a few useful functions for debugging the current manager state and save path. I also fixed a bug with the json save file that was being written at the wrong index.

We will continue investigating this to find a more robust solution. But I hope my findings can at least help you salvage some of the save files.
 
Thanks for this, really appreciate the help!

I'll see from my end whether there is a deterministic way to identify the objects that this fix is unable to accurately locate/reproduce, my hack achieved similar results to the code above with the same item exceptions, where an incorrect item was found and added.

Thanks again.
 
So after doing more research it seems that UnityObject references cannot be saved reliably between builds.
That means a custom ID system must be setup for all objects that need to be saved. In our case ItemDefinition already has an ID so we can take advantage of that.

So with the fix above it should fix all saving from now on.

The issue is with Item Attributes that reference UnityObjects. These cannot be saved reliably unfortunatly. And there isn't anything we can really do about it. So I'll probably add a disclaimer in the documentation saying that this is a Unity limitation and explain how a workd around could be made.

Also it seems that this won't be a problem for too long as there should be a fix in 2022.1
https://issuetracker.unity3d.com/is...test-on-standalone-build-and-using-instanceid
 
Top