// UltEvents // Copyright 2020 Kybernetik //
using System;
using System.Reflection;
using System.Text;
using UnityEngine;
using Object = UnityEngine.Object;
namespace UltEvents
{
///
/// Encapsulates a delegate so it can be serialized for .
///
[Serializable]
public sealed class PersistentCall
#if UNITY_EDITOR
: ISerializationCallbackReceiver
#endif
{
/************************************************************************************************************************/
#region Fields and Properties
/************************************************************************************************************************/
[SerializeField]
private Object _Target;
/// The object on which the persistent method is called.
public Object Target
{
get { return _Target; }
}
/************************************************************************************************************************/
[SerializeField]
private string _MethodName;
/// The name of the persistent method.
public string MethodName
{
get { return _MethodName; }
}
/************************************************************************************************************************/
[SerializeField]
internal PersistentArgument[] _PersistentArguments = NoArguments;
/// The arguments which are passed to the method when it is invoked.
public PersistentArgument[] PersistentArguments
{
get { return _PersistentArguments; }
}
/************************************************************************************************************************/
[NonSerialized]
internal MethodBase _Method;
/// The method which this call encapsulates.
public MethodBase Method
{
get
{
if (_Method == null)
{
Type declaringType;
string methodName;
GetMethodDetails(out declaringType, out methodName);
if (declaringType == null || string.IsNullOrEmpty(methodName))
return null;
var argumentCount = _PersistentArguments.Length;
var parameters = ArrayCache.GetTempArray(argumentCount);
for (int i = 0; i < argumentCount; i++)
{
parameters[i] = _PersistentArguments[i].SystemType;
}
if (methodName == "ctor")
_Method = declaringType.GetConstructor(UltEventUtils.AnyAccessBindings, null, parameters, null);
else
_Method = declaringType.GetMethod(methodName, UltEventUtils.AnyAccessBindings, null, parameters, null);
}
return _Method;
}
}
internal MethodBase GetMethodSafe()
{
try { return Method; }
catch { return null; }
}
/************************************************************************************************************************/
#if UNITY_EDITOR
/************************************************************************************************************************/
// Always clear the cached method in the editor in case the fields have been directly modified by Inspector or Undo operations.
void ISerializationCallbackReceiver.OnBeforeSerialize() { ClearCache(); }
void ISerializationCallbackReceiver.OnAfterDeserialize() { ClearCache(); }
private void ClearCache()
{
_Method = null;
for (int i = 0; i < _PersistentArguments.Length; i++)
{
_PersistentArguments[i].ClearCache();
}
}
/************************************************************************************************************************/
#endif
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
/// Constructs a new with default values.
public PersistentCall() { }
/// Constructs a new to serialize the specified `method`.
public PersistentCall(MethodInfo method, Object target)
{
SetMethod(method, target);
}
/// Constructs a new to serialize the specified `method`.
public PersistentCall(Delegate method)
{
SetMethod(method);
}
/// Constructs a new to serialize the specified `method`.
public PersistentCall(Action method)
{
SetMethod(method);
}
/************************************************************************************************************************/
/// Sets the method which this call encapsulates.
public void SetMethod(MethodBase method, Object target)
{
_Method = method;
_Target = target;
if (method != null)
{
if (method.IsStatic || method.IsConstructor)
{
_MethodName = UltEventUtils.GetFullyQualifiedName(method);
_Target = null;
}
else _MethodName = method.Name;
var parameters = method.GetParameters();
if (_PersistentArguments == null || _PersistentArguments.Length != parameters.Length)
{
_PersistentArguments = NewArgumentArray(parameters.Length);
}
for (int i = 0; i < _PersistentArguments.Length; i++)
{
var parameter = parameters[i];
var persistentArgument = _PersistentArguments[i];
persistentArgument.SystemType = parameter.ParameterType;
switch (persistentArgument.Type)
{
case PersistentArgumentType.Parameter:
case PersistentArgumentType.ReturnValue:
break;
default:
if ((parameter.Attributes & ParameterAttributes.HasDefault) == ParameterAttributes.HasDefault)
{
persistentArgument.Value = parameter.DefaultValue;
}
break;
}
}
}
else
{
_MethodName = null;
_PersistentArguments = NoArguments;
}
}
/// Sets the delegate which this call encapsulates.
public void SetMethod(Delegate method)
{
if (method.Target == null)
{
SetMethod(method.Method, null);
}
else
{
var target = method.Target as Object;
if (target != null)
SetMethod(method.Method, target);
else
throw new InvalidOperationException("SetMethod failed because action.Target is not a UnityEngine.Object.");
}
}
/// Sets the delegate which this call encapsulates.
public void SetMethod(Action method)
{
SetMethod((Delegate)method);
}
/************************************************************************************************************************/
private static readonly PersistentArgument[] NoArguments = new PersistentArgument[0];
private static PersistentArgument[] NewArgumentArray(int length)
{
if (length == 0)
{
return NoArguments;
}
else
{
var array = new PersistentArgument[length];
for (int i = 0; i < length; i++)
array[i] = new PersistentArgument();
return array;
}
}
/************************************************************************************************************************/
///
/// Acquire a delegate based on the and and invoke it.
///
public object Invoke()
{
if (Method == null)
{
Debug.LogWarning("Attempted to Invoke a PersistentCall which couldn't find it's method: " + MethodName);
return null;
}
object[] parameters;
if (_PersistentArguments != null && _PersistentArguments.Length > 0)
{
parameters = ArrayCache