Fix crash when removing Anim Event visual script that is used in opened Animation timeline

#2471
This commit is contained in:
Wojtek Figat
2024-04-22 18:11:25 +02:00
parent 32b15f90ab
commit b7dc0dd004
6 changed files with 78 additions and 11 deletions

View File

@@ -249,6 +249,7 @@ namespace FlaxEditor.Content
private ScriptMemberInfo[] _parameters; private ScriptMemberInfo[] _parameters;
private ScriptMemberInfo[] _methods; private ScriptMemberInfo[] _methods;
private object[] _attributes; private object[] _attributes;
private List<Action<ScriptType>> _disposing;
/// <summary> /// <summary>
/// Gets the Visual Script asset that contains this type. /// Gets the Visual Script asset that contains this type.
@@ -310,6 +311,13 @@ namespace FlaxEditor.Content
internal void Dispose() internal void Dispose()
{ {
if (_disposing != null)
{
foreach (var e in _disposing)
e(new ScriptType(this));
_disposing.Clear();
_disposing = null;
}
if (_parameters != null) if (_parameters != null)
{ {
OnAssetReloading(_asset); OnAssetReloading(_asset);
@@ -510,6 +518,14 @@ namespace FlaxEditor.Content
} }
return _methods; return _methods;
} }
/// <inheritdoc />
public void TrackLifetime(Action<ScriptType> disposing)
{
if (_disposing == null)
_disposing = new List<Action<ScriptType>>();
_disposing.Add(disposing);
}
} }
/// <summary> /// <summary>

View File

@@ -99,6 +99,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
if (Type == ScriptType.Null) if (Type == ScriptType.Null)
{ {
Editor.LogError("Missing anim event type " + _instanceTypeName); Editor.LogError("Missing anim event type " + _instanceTypeName);
InitMissing();
return; return;
} }
Instance = (AnimEvent)Type.CreateInstance(); Instance = (AnimEvent)Type.CreateInstance();
@@ -125,20 +126,37 @@ namespace FlaxEditor.GUI.Timeline.Tracks
_isRegisteredForScriptsReload = true; _isRegisteredForScriptsReload = true;
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
} }
Type.TrackLifetime(OnTypeDisposing);
}
private void OnTypeDisposing(ScriptType type)
{
if (Type == type && !IsDisposing)
{
// Turn into missing script
OnScriptsReloadBegin();
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
InitMissing();
}
}
private void InitMissing()
{
CanDelete = true;
CanSplit = false;
CanResize = false;
TooltipText = $"Missing Anim Event Type '{_instanceTypeName}'";
BackgroundColor = Color.Red;
Type = ScriptType.Null;
Instance = null;
} }
internal void InitMissing(string typeName, byte[] data) internal void InitMissing(string typeName, byte[] data)
{ {
Type = ScriptType.Null;
IsContinuous = false; IsContinuous = false;
CanDelete = true;
CanSplit = false;
CanResize = false;
TooltipText = $"Missing Anim Event Type '{typeName}'";
Instance = null;
BackgroundColor = Color.Red;
_instanceTypeName = typeName; _instanceTypeName = typeName;
_instanceData = data; _instanceData = data;
InitMissing();
} }
internal void Load(BinaryReader stream) internal void Load(BinaryReader stream)

View File

@@ -144,5 +144,11 @@ namespace FlaxEditor.Scripting
{ {
return Utils.GetEmptyArray<ScriptMemberInfo>(); return Utils.GetEmptyArray<ScriptMemberInfo>();
} }
/// <inheritdoc />
public void TrackLifetime(Action<ScriptType> disposing)
{
ElementType.TrackLifetime(disposing);
}
} }
} }

View File

@@ -167,6 +167,12 @@ namespace FlaxEditor.Scripting
/// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param> /// <param name="bindingAttr">A bitmask comprised of one or more <see cref="T:System.Reflection.BindingFlags" /> that specify how the search is conducted.-or- Zero (<see cref="F:System.Reflection.BindingFlags.Default" />), to return an empty array.</param>
/// <returns>An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints.</returns> /// <returns>An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints.</returns>
ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr); ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr);
/// <summary>
/// Registers delegate to be invoked upon script type disposal (except hot-reload in Editor via <see cref="ScriptsBuilder.ScriptsReload"/>). For example, can happen when user deleted Visual Script asset.
/// </summary>
/// <param name="disposing">Event to call when script type gets disposed (eg. removed asset).</param>
void TrackLifetime(Action<ScriptType> disposing);
} }
/// <summary> /// <summary>

View File

@@ -1395,11 +1395,21 @@ namespace FlaxEditor.Scripting
} }
/// <summary> /// <summary>
/// Basic check to see if a type could be casted to another type /// Registers delegate to be invoked upon script type disposal (except hot-reload in Editor via <see cref="ScriptsBuilder.ScriptsReload"/>). For example, can happen when user deleted Visual Script asset.
/// </summary>
/// <param name="disposing">Event to call when script type gets disposed (eg. removed asset).</param>
public void TrackLifetime(Action<ScriptType> disposing)
{
if (_custom != null)
_custom.TrackLifetime(disposing);
}
/// <summary>
/// Basic check to see if a type could be cast to another type
/// </summary> /// </summary>
/// <param name="from">Source type</param> /// <param name="from">Source type</param>
/// <param name="to">Target type</param> /// <param name="to">Target type</param>
/// <returns>True if the type can be casted</returns> /// <returns>True if the type can be cast.</returns>
public static bool CanCast(ScriptType from, ScriptType to) public static bool CanCast(ScriptType from, ScriptType to)
{ {
if (from == to) if (from == to)
@@ -1412,10 +1422,10 @@ namespace FlaxEditor.Scripting
} }
/// <summary> /// <summary>
/// Basic check to see if this type could be casted to another type /// Basic check to see if this type could be cast to another type
/// </summary> /// </summary>
/// <param name="to">Target type</param> /// <param name="to">Target type</param>
/// <returns>True if the type can be casted</returns> /// <returns>True if the type can be cast.</returns>
public bool CanCastTo(ScriptType to) public bool CanCastTo(ScriptType to)
{ {
return CanCast(this, to); return CanCast(this, to);

View File

@@ -104,6 +104,16 @@ namespace FlaxEditor.Surface.Archetypes
} }
} }
private void OnTypeDisposing(ScriptType type)
{
if (_type == type && !IsDisposing)
{
// Turn into missing script
_type = ScriptType.Null;
Instance = null;
}
}
public override void OnLoaded(SurfaceNodeActions action) public override void OnLoaded(SurfaceNodeActions action)
{ {
base.OnLoaded(action); base.OnLoaded(action);
@@ -113,6 +123,7 @@ namespace FlaxEditor.Surface.Archetypes
_type = TypeUtils.GetType(typeName); _type = TypeUtils.GetType(typeName);
if (_type != null) if (_type != null)
{ {
_type.TrackLifetime(OnTypeDisposing);
TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type); TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type);
try try
{ {