Search Unity

How to remove legacy (orphaned) scene data

Discussion in 'Scripting' started by Michael-Ryan, Mar 30, 2020.

  1. Michael-Ryan

    Michael-Ryan

    Joined:
    Apr 10, 2009
    Posts:
    184
    I'm working with a project this has been around for a few years now, and many of our scenes are loaded with legacy serialized data that I'd like to remove.

    For example, the serialized data for one scene includes the following:
    Code (csharp):
    1.   m_Modification:
    2.     m_TransformParent: {fileID: 388556337}
    3.     m_Modifications:
    4.     - target: {fileID: 443484, guid: bf5541658cbba4240a25d4af4ea55998, type: 3}
    5.       propertyPath: m_RootOrder
    6.       value: 20
    7.       objectReference: {fileID: 0}
    8.     - target: {fileID: 177540, guid: bf5541658cbba4240a25d4af4ea55998, type: 3}
    9.       propertyPath: m_Name
    10.       value: HelpSign [Moon Shot]
    11.       objectReference: {fileID: 0}
    12.     - target: {fileID: 11443242, guid: bf5541658cbba4240a25d4af4ea55998, type: 3}
    13.       propertyPath: HelpText
    14.       value: That shiny blue thing over there is a MOON CRYSTAL. Collect all of them
    15.         on the island to activate the MOON PORTAL.
    16.       objectReference: {fileID: 0}
    17.     - target: {fileID: 11443242, guid: bf5541658cbba4240a25d4af4ea55998, type: 3}
    18.       propertyPath: FullText
    19.       value: Quest/MoonIsles/Help/SIGNS_A1-002_MOONSHOT
    20.       objectReference: {fileID: 0}
    21.     - target: {fileID: 11443242, guid: bf5541658cbba4240a25d4af4ea55998, type: 3}
    22.       propertyPath: message
    23.       value: Quest/MoonIsles/Help/SIGNS_A1-002_MOONSHOT
    24.       objectReference: {fileID: 0}
    25.     - target: {fileID: 11400872, guid: bf5541658cbba4240a25d4af4ea55998, type: 3}
    26.       propertyPath: mTerm
    27.       value: HelpSigns/SIGNS_A1-002_ MOONSHOT
    28.       objectReference: {fileID: 0}
    The serialized field "message" contains current data (object on line 21). The field was renamed a few times, and data associated with the old field names is still present in the serialized scene data (objects on lines 12 and 17).

    I would love to strip this old data from our scenes. Is this possible? I've tried using AssetDatabase.ForceReserializeAssets(), but the legacy data is still present. I've removed the [FormerlySerializedAs] attribute from the fields reloaded and resaved, but the data is still present.

    Where is this data stored? I'm assuming it's loaded from disk and deserialized into the UnityEngine.Object, but what happens with data that could not be deserialized, such as the "HelpText" and "FullText" objects listed above? Since the data is being retained when the scene is saved, I'm guessing it has to be storing it in memory (or a hidden scene object) and rewritten to disk when the scene is saved.

    If there's no way to automatically prune a scene of this legacy (orphaned/unused) data, is there at least an way to identify when it exists short of viewing the serialized file directly in a text editor? If there were a hidden scene object that stored the generic extra data, for example, I could then look at that to see what legacy data exists and possibly write some editor tools to remove it myself.

    The above example is just a small snippet from one file. There are LOTS of these in the project.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,741
    I think a lot of this gets stripped out on build. It's only to enable relinking of properties in editor.

    One thought is to create a new scene while keeping the above scene open, then drag all the objects from one scene into the next. It might work, but it also might drag the crud with it. :)

    Another help is to delete prefabs that are only used in that scene. Any prefab that is only dragged into a single scene should simply be broken and deleted: there is no point for that prefab to exist. It only serves as a possible source of bugs and inconsistencies, as well as merge errors when working on a team using source control. Source: I fight this constantly.
     
    Joe-Censored likes this.
  3. Michael-Ryan

    Michael-Ryan

    Joined:
    Apr 10, 2009
    Posts:
    184
    Yeah. It wouldn't surprise me if the orphan data is excluded from the build. I'm assuming it's being retained so that FormallySerializedAs attributes can function or when scripts go missing and Unity wants to be able to link things back up.

    In this case, we haven't changed field names in a while, and everything has been reserialized, so that orphan data is really just taking up space in the project but causing no real harm.

    I did try saving the scene to a new file as a test, but that brought everything along with it. I haven't tried copying the prefabs to a new scene or even replacing the prefab instances, because at the end of the day, I'd like to avoid recreating scene setups or adding new scenes. We're using Addressables, and everything has been labeled and grouped.

    Earlier today, I discovered that you can use
    PrefabUtility.GetPrefabInstanceHandle(object)
    , which gives you access to the PrefabInstance object. From there, you can serialize the object and access all modifications on the prefab instance. It seems like if a given modification has a null target, that's certainly orphaned data, and if the target is not null (meaning it points to a valid component on the object), I'm thinking I could check the modification propertyPath (the field name) against the prefab/MonoBehaviour script to see if the field actual exists. If there's no serialized field with a matching name, that should also be orphan data.

    Anyhow, I may end up writing an EditorWindow that will list all the orphan data in the open scene(s) and provide a means of removing modifications after reviewing them. It would have to be tested to make sure nothing important is being stripped out.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,741
    My spidey sense says manipulating this stuff without fully understanding it does not have adequate benefit compared to the possible downsides of subtle serialization errors, missing stuff, mis-serialized stuff, etc.

    But,YMMV.
     
  5. Michael-Ryan

    Michael-Ryan

    Joined:
    Apr 10, 2009
    Posts:
    184
    After a bit of research, I found that Unity provides direct access to all property modifications using PrefabUtility.GetPropertyModifications(targetPrefab) on a given prefab instance. You can then modify the list and then pass it back to the object using SetPropertyModifications(targetPrefab, modifications). There's no need to serialize anything unless you want to analyze the modifications to see how they compare against the prefab or to see if the property path actually exists.

    In the case the PropertyModification target is null, the data appears to be entirely dead, as it's not associated with anything. When the propertyPath isn't found when you serialize the source prefab, that's usually an indicator that the field was renamed or removed. If you're using [FormerlySerializedAs] attributes, the "invalid" propertyPath might still be associated with a renamed field, otherwise it's also a dead modifications.

    And then finally, when comparing the prefab instance against the source prefab asset, some modifications will include the default value (that matches the prefab). Such modifications might be intentional, if the designer wants to flag a prefab instance property as changed so when the prefab is later updated, the value on the instance won't be updated, but more likely it's the result of a value having been changed away from the default prefab value and then changed back without actually reverting the change. For example, if you drop a prefab instance in the scene, temporarily disable the prefab instance and then re-enable it again. That instance will now be saved to the scene with a PropertyModification on its GameObject for "m_IsActive" that matches the prefab. This extra data will forever live with the scene unless that property is reverted on the prefab instance.

    My understanding of the above three data conditions is that the "null target" is just dead data that isn't associated with any target, can't be utilized, and can be removed. The "missing propertyPath" might be associated with FormerlySerializedAs attributes, but if the new field name is also being serialized, the old propertyPath modification is effectively dead data. And the "unnecessary override" modification is only useful if the designer intentionally modified a property (to the same value as the prefab) so that when the prefab is later changed the change will not propagate to the instance.

    I wrote a tool that can list all PropertyModifications on a selected object or in one or more loaded scenes. You might be surprised at how much extra data is saved with the scene that isn't being used, but I suppose that all depends on the age of the project and scenes, and how frequently serialized fields were removed or renamed in components.

    Unity_2020-04-18_15-05-03.png

    The "HelpText" property shown in the image was the original name for a serialized field established years ago. The field was later renamed to "FullText", which also appears in the image as an invalid property path, because it's now called "message" and is being serialized with "message" as the propertyPath (it's not shown in the image, because all "Default Override" and "Valid Overrides" were hidden when the screenshot was taken).
     
    Nelvin123, kalineh and cooloon like this.
  6. Michael-Ryan

    Michael-Ryan

    Joined:
    Apr 10, 2009
    Posts:
    184
    For anyone curious, here's a screenshot of the "HelpSign [Moon Portal]" object referred to in the previous post, but with all property modifications visible.

    The GameObject name was changed and its Transform was updated to position the object in the scene. Those changes are considered "Default Overrides" by Unity and the PrefabUtility.IsDefaultOverride(modification) method.

    The "message" propertyPath is the only real change from the prefab, and that's a "Valid Override". Everything else shown is dead or old data. That's just one object in the scene.

    Unity_2020-04-18_15-41-58.png

    It should be noted that while the GameObject "IsActive" and HelpSign "message" properties can be reverted through the inspector, all of those "Null Target" and "Invalid Property Path" modifications are entirely hidden. Without a tool like this, I don't believe there's a way to identify or remove them without directly editing the serialized scene text file like I was doing in the original post that started this thread.
     
    Last edited: Apr 19, 2020
    Nelvin123, kalineh, funkyCoty and 3 others like this.
  7. icoum

    icoum

    Joined:
    Nov 2, 2019
    Posts:
    3
    Hi all,

    Good job Michael-Ryan for your research, explanations, and for making your tool !
    Are you sharing it here ?
    I have to admit I feel lazy to remake it :D

    Have a nice day !
     
    xavier-ateo likes this.
  8. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    727
    Please share this tool! Make a github gist!
     
  9. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,066
    Yeah, prefabs only exist in the editor. During the build, scenes get flattened and all prefabs are just regular scene game objects in player. All the prefab metadata is discarded, which is also why PrefabUtility is editor-only.

    But as you said, the dead properties still take up space and probably some processing time in the editor. Also would be interested to try out your tool.