Fix issue with two TimelineAsset referencing same clips

* Resolves #1843
* Resolves #1599
This commit is contained in:
ds5678 2025-07-14 21:57:14 -07:00
parent c434e1fc67
commit f26c4b99a9
4 changed files with 59 additions and 4 deletions

View File

@ -1,5 +1,6 @@
using AssetRipper.Assets;
using AssetRipper.Assets.Metadata;
using System.Diagnostics;
namespace AssetRipper.Processing;
@ -16,6 +17,7 @@ public abstract class AssetGroup : UnityObjectBase
MainAsset = this;
foreach (IUnityObjectBase asset in Assets)
{
Debug.Assert(asset.MainAsset is null || asset.MainAsset == this, "Asset already has a main asset assigned.");
asset.MainAsset = this;
}
}

View File

@ -12,7 +12,7 @@ public sealed class ScriptableObjectGroup : AssetGroup, INamed
Root = root;
}
public IMonoBehaviour Root { get; set; }
public IMonoBehaviour Root { get; }
public List<IMonoBehaviour> Children { get; } = [];
public override IEnumerable<IMonoBehaviour> Assets => Children.Prepend(Root);
@ -36,7 +36,7 @@ public sealed class ScriptableObjectGroup : AssetGroup, INamed
{
this.WalkPrimitiveField(walker, Name);
walker.DivideAsset(this);
this.WalkPrimitiveField(walker, FileExtension ?? "");
this.WalkPrimitiveField(walker, FileExtension ?? "", nameof(FileExtension));
walker.DivideAsset(this);
this.WalkPPtrField(walker, Root);
walker.DivideAsset(this);

View File

@ -55,14 +55,24 @@ public class ScriptableObjectProcessor : IAssetProcessor
continue;
}
children.Add(child);
SerializableStructure? childStructure = child.LoadStructure();
if (childStructure is null)
{
continue;
}
if (!childStructure.TryGetField("m_Parent", out SerializableValue parent))
{
continue;
}
if (root.Collection.TryGetAsset(parent.AsPPtr.FileID, parent.AsPPtr.PathID) != root)
{
continue;
}
children.Add(child);
if (childStructure.TryGetField("m_Clips", out SerializableValue clips))
{
foreach (SerializableStructure clip in clips.AsAssetArray.Cast<SerializableStructure>())

View File

@ -6,6 +6,7 @@ using AssetRipper.Import.Structure.Assembly.Managers;
using AssetRipper.IO.Files;
using AssetRipper.Primitives;
using AssetRipper.Processing;
using AssetRipper.Processing.ScriptableObject;
using AssetRipper.SourceGenerated.Classes.ClassID_114;
using AssetRipper.SourceGenerated.Classes.ClassID_115;
using AssetRipper.SourceGenerated.Classes.ClassID_43;
@ -41,6 +42,48 @@ internal class ExportTests
Assert.That(fileSystem.File.Exists("/output/ExportedProject/Assets/MonoBehaviour/MonoBehaviour.asset"));
}
[Test]
public void ScriptableObjectGroupIsExported_1()
{
ProcessedAssetCollection collection = AssetCreator.CreateCollection(UnityVersion.V_2022);
IMonoBehaviour behaviour1 = collection.CreateMonoBehaviour();
IMonoBehaviour behaviour2 = collection.CreateMonoBehaviour();
ScriptableObjectGroup group = collection.CreateAsset(default, (assetInfo) => new ScriptableObjectGroup(assetInfo, behaviour1));
group.Children.Add(behaviour2);
group.SetMainAsset();
Assert.DoesNotThrow(() => Export(collection));
}
[Test]
public void ScriptableObjectGroupIsExported_2()
{
ProcessedAssetCollection collection = AssetCreator.CreateCollection(UnityVersion.V_2022);
IMonoBehaviour behaviour1 = collection.CreateMonoBehaviour();
IMonoBehaviour behaviour2 = collection.CreateMonoBehaviour();
ScriptableObjectGroup group = collection.CreateAsset(default, (assetInfo) => new ScriptableObjectGroup(assetInfo, behaviour2));
group.Children.Add(behaviour1);
group.SetMainAsset();
Assert.DoesNotThrow(() => Export(collection));
}
[Test]
public void ScriptableObjectGroupIsExported_3()
{
ProcessedAssetCollection collection = AssetCreator.CreateCollection(UnityVersion.V_2022);
IMonoBehaviour behaviour1 = collection.CreateMonoBehaviour();
ScriptableObjectGroup group = collection.CreateAsset(default, (assetInfo) => new ScriptableObjectGroup(assetInfo, behaviour1));
IMonoBehaviour behaviour2 = collection.CreateMonoBehaviour();
group.Children.Add(behaviour2);
group.SetMainAsset();
Assert.DoesNotThrow(() => Export(collection));
}
[Test]
public void EmptyMeshIsExported()
{