// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Reflection;
using FlaxEditor.Scripting;
using FlaxEditor.Surface;
using FlaxEngine;
using FlaxEngine.Utilities;
using MethodInfo = System.Reflection.MethodInfo;
namespace FlaxEditor.Modules.SourceCodeEditing
{
///
/// Cached collection of custom nodes types including cached node archetypes for each one of them.
///
///
[HideInEditor]
public class CachedCustomAnimGraphNodesCollection : CachedTypesCollection
{
private List _archetypes;
///
/// Gets a value indicating whether this instance has any type from game scripts (or editor scripts) - those can be reloaded at runtime so prevent crashes.
///
public bool HasTypeFromGameScripts { get; protected set; }
///
public CachedCustomAnimGraphNodesCollection(int capacity, ScriptType type, Func checkFunc, Func checkAssembly)
: base(capacity, type, checkFunc, checkAssembly)
{
}
///
/// Gets the cached archetypes.
///
/// The archetypes collection.
public List GetArchetypes()
{
Get();
return _archetypes;
}
///
protected override void Search()
{
base.Search();
HasTypeFromGameScripts = false;
if (_list.Count == 0)
{
_archetypes?.Clear();
return;
}
if (_archetypes == null)
_archetypes = new List(_list.Capacity);
_archetypes.Clear();
for (int i = _list.Count - 1; i >= 0 && _list.Count > 0; i--)
{
var nodeType = _list[i].Type;
var methods = nodeType.GetMethods(BindingFlags.Static | BindingFlags.Public);
for (int j = 0; j < methods.Length; j++)
{
var arch = GetArchetype(methods[j]);
if (arch != null)
{
// Cache type
_archetypes.Add(arch);
}
}
}
}
private NodeArchetype GetArchetype(MethodBase method)
{
// Validate method signature
if (!method.IsStatic ||
!method.IsPublic ||
method as MethodInfo == null ||
((MethodInfo)method).ReturnType != typeof(NodeArchetype) ||
method.GetParameters().Length != 0 ||
method.IsGenericMethod)
return null;
// Invoke method
try
{
var arch = (NodeArchetype)method.Invoke(null, null);
// Validate archetype
if (arch.Tag != null || string.IsNullOrEmpty(arch.Title))
{
Debug.LogWarning(string.Format("Method {0} from {1} returned invalid node archetype. Tag must be null and title must be specified.", method, method.DeclaringType));
return null;
}
if (arch.DefaultValues == null || arch.DefaultValues.Length < 2 || string.IsNullOrEmpty(arch.DefaultValues[0] as string) || string.IsNullOrEmpty(arch.DefaultValues[1] as string))
{
Debug.LogWarning(string.Format("Method {0} from {1} returned invalid node archetype. Default values are invalid. DefaultValues[0] must specify the C# runtime controller typename. DefaultValues[1] must specify the node group name.", method, method.DeclaringType));
return null;
}
// Validate node type
var typeName = Surface.Archetypes.Custom.GetNodeTypeName(arch);
var type = TypeUtils.GetType(typeName).Type;
if (type == null)
{
Debug.LogWarning(string.Format("Method {0} from {1} returned invalid node archetype. Failed to find node logic defined in type {2}.", method, method.DeclaringType, typeName));
return null;
}
// Check if type comes from scripts that can be reloaded at runtime
HasTypeFromGameScripts |= FlaxEngine.Scripting.IsTypeFromGameScripts(method.DeclaringType) || FlaxEngine.Scripting.IsTypeFromGameScripts(type);
return arch;
}
catch (Exception ex)
{
Debug.LogWarning("Failed to get the custom node archetype.");
Debug.LogWarning(ex);
}
return null;
}
///
public override void ClearTypes()
{
base.ClearTypes();
_archetypes?.Clear();
}
}
}