Search Unity

Bug [Case 1425309] Changes to SerializeReference class after empty causes data corruption

Discussion in 'Scripting' started by CDF, May 4, 2022.

  1. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Using Unity 2021.3.0

    So after assigning an instance of a empty class to a [SerializeReference] field, then making changes to that class, Unity will be unable to serialize those changes and the asset object containing the reference becomes corrupted.

    An error spits out on inspecting the asset:
    Code (CSharp):
    1. Cannot access reference object for property path 'metadata'. The reference managed script might have changed.
    Can see the issue here:
    empty_metadata.gif

    A Scriptable object contains a [SerializeReference] field set to default:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. #if UNITY_EDITOR
    4. using UnityEditor;
    5. #endif
    6.  
    7. namespace My.Namespace {
    8.  
    9.     public class MyAsset : ScriptableObject {
    10.  
    11.         [SerializeReference]
    12.         private IMetadata metadata = default;
    13.  
    14. #if UNITY_EDITOR
    15.         [MenuItem("Test/Create Empty Meta Asset Object")]
    16.         private static void CreateAssetWithEmptyMetadata() {
    17.  
    18.             MyAsset asset = CreateInstance<MyAsset>();
    19.             asset.metadata = new EmptyMetadata();
    20.  
    21.             AssetDatabase.CreateAsset(asset, "Assets/Empty Metadata Object.asset");
    22.             AssetDatabase.SaveAssets();
    23.         }
    24. #endif
    25.     }
    26. }
    And the default "EmptyMetadata" class:


    Code (CSharp):
    1. namespace My.Namespace {
    2.  
    3.     public class EmptyMetadata : IMetadata {
    4.  
    5.         //Uncomment the following code lines after asset creation:
    6.  
    7.         /*
    8.         public enum Mode {
    9.             None,
    10.             Value1,
    11.             Value2
    12.         }
    13.  
    14.         public Mode mode = Mode.None;
    15.         */
    16.     }
    17. }
    After asset creation and uncommenting those lines. The error occurs.
     
    Last edited: May 4, 2022
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Pretty sure there are at least two reasons this doesn't work the way you expect:

    - AFAIK the editor doesn't support drag-and-dropping interfaces (perhaps this has changed?)

    - your EmptyMetadata class does not derive from UnityEngine.Object, and also is not marked System.Serializable.
     
  3. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    It does work, as it's using [SerializeReference] not [SerializeField]

    New serialization feature since 2019.3: https://docs.unity3d.com/ScriptReference/SerializeReference.html

    The issue does not occur if the class had members before being referenced on the asset.
    Only when the class is empty then members are introduced later, the issue occurs.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,992
    I can't reproduce your issue. When I use your menu item to create an instance of your scriptable object while the content of your metadata object is commented out, the resulting asset looks like that:
    Code (CSharp):
    1.  
    2. %YAML 1.1
    3. %TAG !u! tag:unity3d.com,2011:
    4. --- !u!114 &11400000
    5. MonoBehaviour:
    6.   m_ObjectHideFlags: 0
    7.   m_CorrespondingSourceObject: {fileID: 0}
    8.   m_PrefabInstance: {fileID: 0}
    9.   m_PrefabAsset: {fileID: 0}
    10.   m_GameObject: {fileID: 0}
    11.   m_Enabled: 1
    12.   m_EditorHideFlags: 0
    13.   m_Script: {fileID: 11500000, guid: 9bccfc8109832714389aab016f8d5746, type: 3}
    14.   m_Name: Empty Metadata Object
    15.   m_EditorClassIdentifier:
    16.   metadata:
    17.     id: 0
    18.   references:
    19.     version: 1
    20.     00000000:
    21.       type: {class: EmptyMetadata, ns: My.Namespace, asm: Assembly-CSharp}
    22.  

    This is how it looks like when I uncomment the content, let Unity recompile the classes and hit CTRL+S to save the changes:

    Code (CSharp):
    1. %YAML 1.1
    2. %TAG !u! tag:unity3d.com,2011:
    3. --- !u!114 &11400000
    4. MonoBehaviour:
    5.   m_ObjectHideFlags: 0
    6.   m_CorrespondingSourceObject: {fileID: 0}
    7.   m_PrefabInstance: {fileID: 0}
    8.   m_PrefabAsset: {fileID: 0}
    9.   m_GameObject: {fileID: 0}
    10.   m_Enabled: 1
    11.   m_EditorHideFlags: 0
    12.   m_Script: {fileID: 11500000, guid: 9bccfc8109832714389aab016f8d5746, type: 3}
    13.   m_Name: Empty Metadata Object
    14.   m_EditorClassIdentifier:
    15.   metadata:
    16.     id: 0
    17.   references:
    18.     version: 1
    19.     00000000:
    20.       type: {class: EmptyMetadata, ns: My.Namespace, asm: Assembly-CSharp}
    21.       data:
    22.         mode: 0
    23.  

    As you can see in the first case the internal reference just contains an EmptyMetadata class instance with no data. After the change and resave the instance now contains data with the only field "mode". If I comment out the content again, the data persists as usual with Unity's serialization system. So if I again adding the content, the mode field reflects the stored value as expected.

    Tested in Unity 2019.4.19f1 as this is the version I currently had open ^^.
     
  5. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Interesting, I'm using 2021.3.0

    I'll see if I can reproduce in other versions
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,992
    You are right. The newer Unity versions have actually changed the serialization format for referenced instances. If I try the same thing in Unity 2022.1.0a13 (another test project) I get the same errors. So Unity does not fix corrupted serialized data on the fly as it used to. This may just be an issue in the inspector UI. Probably because the "data" sub field is missing. Like you said, if the class contains any other fields when it's first created (so it has its data field), adding new fields does not cause any errors.

    As you can see, the new format uses version "2" while the one I posted above used version "1"
    Code (CSharp):
    1. %YAML 1.1
    2. %TAG !u! tag:unity3d.com,2011:
    3. --- !u!114 &11400000
    4. MonoBehaviour:
    5.   m_ObjectHideFlags: 0
    6.   m_CorrespondingSourceObject: {fileID: 0}
    7.   m_PrefabInstance: {fileID: 0}
    8.   m_PrefabAsset: {fileID: 0}
    9.   m_GameObject: {fileID: 0}
    10.   m_Enabled: 1
    11.   m_EditorHideFlags: 0
    12.   m_Script: {fileID: 11500000, guid: 62096f4c20156ed4b9f30fa00a44ba94, type: 3}
    13.   m_Name: Empty Metadata Object
    14.   m_EditorClassIdentifier:
    15.   metadata:
    16.     rid: 6456410453277147136
    17.   references:
    18.     version: 2
    19.     RefIds:
    20.     - rid: 6456410453277147136
    21.       type: {class: EmptyMetadata, ns: My.Namespace, asm: Assembly-CSharp}
    22.  
     
  7. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Still happening for me using Unity 2019, 2020, 2021.
    Here's a video of the issue using Unity 2019.4.19:



    Curious to know how this is working for you and not me?
    Maybe we have different editor settings that are affecting this?
     
  8. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,859
    Just chiming in to say I've used SerializeReference heavily in both 2020 and 2021 and never encountered this issue.

    Only changing the name of the class, the namespace it's in, or the assembly it's in, breaks it. When that happens there's the UnityEngine.Scripting.APIUpdating.MovedFrom attribute.

    Main difference in my case from OP is that I'm using Odin Inspector to draw the SerializeReference fields (Unity is still doing the serialisation).
     
  9. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    It definitely happens. It's a real bug. It only happens when adding serialized fields to a class that didn't have any serialized fields before, so maybe most people haven't seen that because it's a rare case.

    @CDF Have you reported this bug to Unity? I'm worried this might be an issue waiting to happen in some projects, months later when I won't even remember what could trigger it, so I'd gladly make a report if you haven't already :). Thanks for this info. I made some tests and I'm pretty sure that it happens because Unity trips up when it expects the serialized representation to contain a "data" member and it doesn't have it. That is to say that the bug disappears if I open the asset in a text editor and add an empty
    data:
    row at the end so it looks like this:

    Code (CSharp):
    1. %YAML 1.1
    2. %TAG !u! tag:unity3d.com,2011:
    3. --- !u!114 &11400000
    4. MonoBehaviour:
    5.   m_ObjectHideFlags: 0
    6.   m_CorrespondingSourceObject: {fileID: 0}
    7.   m_PrefabInstance: {fileID: 0}
    8.   m_PrefabAsset: {fileID: 0}
    9.   m_GameObject: {fileID: 0}
    10.   m_Enabled: 1
    11.   m_EditorHideFlags: 0
    12.   m_Script: {fileID: 11500000, guid: 62096f4c20156ed4b9f30fa00a44ba94, type: 3}
    13.   m_Name: Empty Metadata Object
    14.   m_EditorClassIdentifier:
    15.   metadata:
    16.     rid: 6456410453277147136
    17.   references:
    18.     version: 2
    19.     RefIds:
    20.     - rid: 6456410453277147136
    21.       type: {class: EmptyMetadata, ns: My.Namespace, asm: Assembly-CSharp}
    22.       data:
    EDIT
    Also, if I delete the
    data:
    lines from a managed reference in a text editor, the same error appears.
     
    Last edited: May 9, 2022
  10. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    I have reported it yes. Case # is in the title
     
    oscarAbraham likes this.
  11. Oneiros90

    Oneiros90

    Joined:
    Apr 29, 2014
    Posts:
    78
    We just faced this problem. Any update? Is there a link to the Issue Tracker?
     
  12. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,992
    This is such a rare case that you really have to have a strange workflow to actually account this issue. Though even if you managed to do that, just fix the serialized instance and the issue is gone. Just don't create empty serialized classes (which just makes no sense at all).
     
  13. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
  14. Oneiros90

    Oneiros90

    Joined:
    Apr 29, 2014
    Posts:
    78
    It isn't that weird at all. We have empty classes we want to serialize because they contain a specific implementation of an abstract method. Other inheritors of that abstract class have serialized fields, but some don't. And when we added a new parameter we lost references in all assets that had a reference to that kind of classes.
     
    zandalus and oscarAbraham like this.