Search Unity

  1. Get the latest news, tutorials and offers directly to your inbox with our newsletters. Sign up now.
    Dismiss Notice

m_Modification doesn't clear old references when the prefab is modified

Discussion in 'Prefabs' started by Baste, Oct 15, 2019.

  1. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    I've got a bunch of prefabs with a script on them that used to have a field that's been removed. Some of the prefab instances have modifications to that field. I thought that modifications to removed fields would automatically be removed when the prefab instance got reserialized due to something else changing, but it's not happening.

    The reason this is especially bad is that the field in question is a ScriptableObject, and that SO was stored as a part of the scene. Unity cleans out such SO's when they're not referenced anymore. SInce the modification to that field stays there, the SO won't get cleared out.
    In addition to all of this, we have deleted that SO's type. This means that each of these SO's are causing these warnings to appear in the console on startup:
    Code (csharp):
    1.  
    2. The referenced script (Unknown) on this Behaviour is missing!
    3. The referenced script on this Behaviour (Game Object '<null>') is missing!
    In the scene file, this looks like this. I've got a prefab instance with this in it:
    Code (csharp):
    1.  
    2. --- !u!1001 &1435652222
    3. PrefabInstance:
    4.   m_ObjectHideFlags: 0
    5.   serializedVersion: 2
    6.   m_Modification:
    7.     ...
    8.     - target: {fileID: 114238998614804306, guid: ff4165164015226499e68b7cb5fdc74e,
    9.         type: 3}
    10.       propertyPath: navLocation
    11.       value:
    12.       objectReference: {fileID: 218451484}
    The "navLocation" field on the script has been deleted. This is one of several fields who's overrides are not getting removed, but this is the one causing the most problems.

    Further down the file is this line:

    Code (csharp):
    1.  
    2. --- !u!114 &218451484
    3. MonoBehaviour:
    4.   m_ObjectHideFlags: 0
    5.   m_CorrespondingSourceObject: {fileID: 0}
    6.   m_PrefabInstance: {fileID: 0}
    7.   m_PrefabAsset: {fileID: 0}
    8.   m_GameObject: {fileID: 0}
    9.   m_Enabled: 1
    10.   m_EditorHideFlags: 0
    11.   m_Script: {fileID: 11500000, guid: 35134fd8a34d47be92f33e58371449fc, type: 3}
    12.   m_Name: Krathaven 9_Location
    13.   m_EditorClassIdentifier:
    35134fd8a34d47be92f33e58371449fc references a deleted script.

    Things I have tried to clean this up:
    - Make changes to the prefab instance. In my experience, that causes the prefab instance to reserialize, Which usually fixes things like this, but no dice
    - Reserialize the entire scene asset, using AssetDatabase.ForceReserializeAssets. That doesn't work either.

    - Delete the prefab instance, enter play mode, exit play mode, Undo.
    This half-solves the issue, as the SO is removed from the scene file. The prefab modification is still there, but now with
    objectReference: {fileID: 0}


    Any advice? Is this a bug? What's supposed to make prefab modifications that are not relevant anymore be cleared?
     
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,852
    It's not a bug, but we're aware the design has some issues.

    In general, Unity is very conservative with regards to deleting user data. For example, if you setup properties on a Material and later change the shader to a different one that doesn't have those properties, we don't delete the properties, and if you switch the shader back, your saved values are still there. This principle is used throughout lots of data in Unity.

    For this Prefab case, say you remove that script property, but later revert that via version control or other means. Your old data is now back in the state you left it in. This is frequently convenient. The unused overrides data is still stripped from builds, since builds don't has any concept of overrides at all, so it doesn't cause overhead in that sense. But it does cause issues sometimes, and I think the fact that dependencies are still tracked for unused overrides is one such issue.

    There are a few cases where we do remove unused overrides. For examples, if you have overrides on a component, but then remove that component (and applies it), then the overrides for that component are removed as well. But in more indirect cases, we don't remove them.

    Having tooling to allow cleaning up unused overrides is one of the things that are up for consideration for future improvements we'll work on.
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    In our case, this is really bad, because the ScriptableObject type that the removed field is referencing has been removed from the project. Since Unity only removes ScriptableObjects that are a part of the scene when there's no scripts removing them, those are permanently stuck there, and are puking warning messages into the console on startup.

    The warning messages, together with the bloat of thousands of unused fields and ScriptableObjects cause our project size to bloat, and the warning messages also causes entering play mode to take a lot longer, since warning messages are slow. They also make it super hard to find real warning messages that we care about.


    Unity also has a long-standing issue where you cannot delete nested assets who's type is not known. If these were ScriptableObjects who's type were known, I could probably fix this by doing LoadAllAssetsAtPath for the scene file, and deleting the instances I wanted gone. But in this instance that becomes impossible.


    The warning message is also really, really hard to track down, as they look like this:
    Code (csharp):
    1. The referenced script (Unknown) on this Behaviour is missing!
    and:
    Code (csharp):
    1. The referenced script on this Behaviour (Game Object '<null>') is missing!
    There's no context for any of these messages, and they're wrong, as the referenced script is not a Behaviour, and not attached to any GameObject. I had to spend hours binary search deleting the objects in the scene in order to even understand what's going on.


    I think I can fix this, by creating a new, empty ScriptableObject, assigning it the GUID of the deleted ScriptableObject, and then search for instances of that to delete. That's really, really hacky, and requires quite a lot of in-depth understanding of how Unity works - I think a lot of users would just be stuck with not being able to use warning messages for the rest of their project.
     
  4. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,852
    Yeah it sounds frustrating.

    I forgot to be more clear that I meant that the fact that overrides are not removed is not a bug, but the fact that it causes referenced objects to be kept in the scene might still be. We'd appreciate a bug report on this.

    It sounds like there's also an unrelated issue with some of our warning messages that are not really related to Prefabs, but feel free to file bug reports about that too of course.
     
    Baste likes this.
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    I've reported both; 1191638 and 1191643.
     
    runevision likes this.
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    Hi!

    I eventually got both bugs back as "by design", with mentions that a tool to clear the outdated references might be in the pipeline.

    We just ran into this issue again in a different manner. We have some assets that used to be directly linked, but are now loaded from an asset bundle. So we went from:

    Code (csharp):
    1. public class Foo : MonoBehaviour {
    2.     public Bar bar; // Bar is an asset file
    3. }
    To this:

    Code (csharp):
    1. public class Foo : MonoBehaviour {
    2.     public string barID;
    3.     private Bar bar;
    4.  
    5.     void Start() {
    6.         var = GetFromAssetBundle(barID);
    7.     }
    8. }
    The problem here is that Foo is on prefab instances, and prefab instances has old m_Modification's that reference bar. This causes the Bar assets to be included both in the asset bundle, and in the game build itself, due to there being a reference from the scene the Foo instance is in, to the Bar asset.

    That has to be an actual bug, right? We're getting an asset included in a build, without having any editable references to that asset - all of our references are outdated m_Modification's that we can't really touch.

    If I create a bug report showing this, any chance it'll be handled? The m_Modification hanging around forever is sketchy, but fine. That that causes files that shouldn't be included in builds to be included is pretty bad.
     
  7. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    600
    Hi,

    I just wrote a long reply about how keeping property modification is by design and bla bla bla, but in the end you are right that should not cause assets to be included in the build. I'll see if I can make a fix
     
    Baste likes this.
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    Hey, thanks. If you want to, I can sit down and create a repro project for exactly that issue and create a bug report.
     
  9. steinbitglis

    steinbitglis

    Joined:
    Sep 22, 2011
    Posts:
    178
  10. sschoellhammer

    sschoellhammer

    Joined:
    Feb 15, 2013
    Posts:
    33
    Hey @steinbitglis
    This looks great. We had - I believe - the same issue (https://forum.unity.com/threads/removing-dangling-dependencies.892948/#post-5865292)

    So I tried your tool :). Maybe I'm missing something, but it doesn not show me anything to clean up in my scene.

    In the test project attached is a "test" script which exposed a
    "PrefabDependency" field, which then got removed.
    The assignment however persists on the Cylinder prefab (causing a "hidden dependency"):

    PrefabDependency: {fileID: 611505733756841739, guid: 9d8d10dd541e6d94799fc2a7344510b4,
    type: 3}

    I was hoping this is something your tool would find and clean out, or am I mistaken?

    Thanks in advance!

    seb
     

    Attached Files:

  11. steinbitglis

    steinbitglis

    Joined:
    Sep 22, 2011
    Posts:
    178
    In the project you've posted here, there is no prefab instance override data for the field that you have removed.
    If you assign some data to the field (and save)., then remove it ... then the window will pick it up and suggest removal.
    PrefabCleaner_help.png
     
    KAJed likes this.
  12. steinbitglis

    steinbitglis

    Joined:
    Sep 22, 2011
    Posts:
    178
    KAJed likes this.
  13. KAJed

    KAJed

    Joined:
    Mar 15, 2013
    Posts:
    88
    The cleaner script is awesome - but it would be good if it supported more than just prefabs in scenes!

    But also... please fix this Unity!
     
  14. steinbitglis

    steinbitglis

    Joined:
    Sep 22, 2011
    Posts:
    178
    Prefab instances in the project (nested prefab children) are also supported. Are there other things that need this?
     
  15. Ziflin

    Ziflin

    Joined:
    Mar 12, 2013
    Posts:
    94
    @runevision What are the plans to fix this issue as I now have several prefab modifications in a scene that no longer need to be there? Is there an API call that can be made to remove currently invalid modifications?
     
  16. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,852
    You would have to ask Steen who last commented on it here, since I'm not aware of the status:
    https://forum.unity.com/threads/m_m...n-the-prefab-is-modified.761219/#post-5397249
    He's currently on vacation though.

    In the mean time it looks like several people have found this workaround tool by steinbitglis useful:
    https://forum.unity.com/threads/m_m...n-the-prefab-is-modified.761219/#post-5436957
     
  17. Ziflin

    Ziflin

    Joined:
    Mar 12, 2013
    Posts:
    94
    Thanks for the reply @runevision. I took a look at the tool above, but it was easier to just manually edit the file and remove what was needed for now.

    @SteenLund It would still be nice if there was a least an API call to clean a scene / prefab of invalid modifications. In our case we were left with a large orphaned array of assets still referenced from m_Modifications that I assume would still be loaded while in the editor.
     
  18. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    600
    Hi, I have spent some time on this now.

    As previously mentioned it is by design that overrides are not removed from the scene files. We will be building some tooling to help you clean up overrides but for now we have other higher priority issues. As Rune mentioned above @steinbitglis created a script that is very useful for this.

    @Baste
    We also discussed that these redundant overrides should not cause assets to be included in builds. I have tried to reproduce this in various setups and the only way I was able to reproduce this was when including scenes in asset bundles. In this case asset would be included due to the redundant overrides, but this is only a problem in the older asset bundle system. The dependencies are calculated correctly in the Scriptable Build Pipeline and redundant overrides are ignored.
     
  19. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    Thanks!

    I'll look into if we're using the old API or the new one - I think it's a bit of both? We're manually getting dependencies recursively in order to make some decisions, which is where I think this kicks in.
     
  20. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    600
    @Baste

    If you are using
    AssetDatabase.GetDependencies(asset, false)
    to get direct dependencies then I just fixed a bug where prefabs would wrongly collect recursive dependencies. I will get this backported to 2020.2
     
  21. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,418
    That's exactly what we do!

    Do you know how old the bug is? For the project where we use it, it's considerably more work for us to update to 2020.2 (from 2018.4) than to work around the bug (since it's already worked around :p )
     
  22. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    600
    @Baste

    This bug has probably been around since nested prefabs were introduced. I will request it gets back ported to 2018 as well, but I can't promise it will be done.
     
    Baste likes this.
  23. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    600
    I should mention we still don't have a fix for variants/nested prefabs that have stale references, those references still needs to be cleaned up before getting dependencies. We will see if we can find a solution for this.
     
    Rafarel likes this.
unityunity