Before 优化 机场
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Kamgam.UIToolkitBlurredBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows you to register a callback before compilation
|
||||
/// which is then executed automatically after compilation.<br />
|
||||
/// <br />
|
||||
/// Methods registered are usually executed in FIFO order. Though there
|
||||
/// is no guarantee that this will always be the case.
|
||||
/// <example>
|
||||
/// CrossCompileCallbacks.RegisterCallback(testCallbackA); // It can find the type automatically.
|
||||
/// CrossCompileCallbacks.RegisterCallback(typeof(YourClass), "testCallbackA");
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public static class CrossCompileCallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// If set to true then the callbacks will not be called immediately but
|
||||
/// within the next editor update cycle.<br />
|
||||
/// Use this to avoid "Calling ... from assembly reloading callbacks are not supported." errors.
|
||||
/// </summary>
|
||||
public static bool DelayExecutionAfterCompilation
|
||||
{
|
||||
get => SessionState.GetBool(typeName() + ".DelayExecution", false);
|
||||
set => SessionState.SetBool(typeName() + ".DelayExecution", value);
|
||||
}
|
||||
|
||||
static string typeName() => typeof(CrossCompileCallbacks).FullName;
|
||||
|
||||
const string _maxIndexKey = ".MaxIndex";
|
||||
static string maxIndexKey() => typeName() + _maxIndexKey;
|
||||
|
||||
const string _lastReleasedIndexKey = ".LastReleasedIndex";
|
||||
static string lastReleasedIndexKey() => typeName() + _lastReleasedIndexKey;
|
||||
|
||||
const string _indexTypeKey = ".Index[{0}].Type";
|
||||
static string indexTypeKey(int index) => string.Format(typeName() + _indexTypeKey, index);
|
||||
|
||||
const string _indexMethodKey = ".Index[{0}].Method";
|
||||
static string indexMethodKey(int index) => string.Format(typeName() + _indexMethodKey, index);
|
||||
|
||||
|
||||
static int getMaxIndex()
|
||||
{
|
||||
return SessionState.GetInt(maxIndexKey(), -1);
|
||||
}
|
||||
|
||||
static int getNextIndex()
|
||||
{
|
||||
int maxIndex;
|
||||
|
||||
// Try to reuse an old index (update max index if necessary)
|
||||
int reusableIndex = SessionState.GetInt(lastReleasedIndexKey(), -1);
|
||||
if (reusableIndex >= 0)
|
||||
{
|
||||
SessionState.SetInt(lastReleasedIndexKey(), -1);
|
||||
|
||||
maxIndex = getMaxIndex();
|
||||
if(maxIndex < reusableIndex)
|
||||
SessionState.SetInt(maxIndexKey(), reusableIndex);
|
||||
|
||||
return reusableIndex;
|
||||
}
|
||||
|
||||
// New index needed (increase max index).
|
||||
maxIndex = SessionState.GetInt(maxIndexKey(), -1);
|
||||
maxIndex++;
|
||||
SessionState.SetInt(maxIndexKey(), maxIndex);
|
||||
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
public static void ReleaseIndex(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
SessionState.SetInt(lastReleasedIndexKey(), index);
|
||||
SessionState.EraseString(indexTypeKey(index));
|
||||
SessionState.EraseString(indexMethodKey(index));
|
||||
|
||||
// Decrease or erase max index if needed.
|
||||
int maxIndex = getMaxIndex();
|
||||
if(index == maxIndex)
|
||||
{
|
||||
maxIndex--;
|
||||
if(maxIndex < 0)
|
||||
SessionState.EraseInt(maxIndexKey());
|
||||
else
|
||||
SessionState.SetInt(maxIndexKey(), maxIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReleaseAllOnType(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
return;
|
||||
|
||||
int maxIndex = getMaxIndex();
|
||||
for (int i = maxIndex; i >= 0; i--)
|
||||
{
|
||||
string typeName;
|
||||
GetCallbackInfo(i, out typeName, out _);
|
||||
|
||||
if(typeName == type.FullName)
|
||||
{
|
||||
ReleaseIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback and returns an index >= 0 on success and -1 on failure.
|
||||
/// </summary>
|
||||
/// <param name="callback">A static method without any parameters.</param>
|
||||
/// <returns></returns>
|
||||
public static int RegisterCallback(System.Action callback)
|
||||
{
|
||||
if (callback == null)
|
||||
return -1;
|
||||
|
||||
var methodInfo = callback.GetMethodInfo();
|
||||
if (methodInfo == null)
|
||||
return -1;
|
||||
|
||||
if (!methodInfo.IsStatic)
|
||||
{
|
||||
Debug.Log("Method needs to be static.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return RegisterCallback(methodInfo.DeclaringType, methodInfo.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a callback and returns an index >= 0 on success and -1 on failure.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="staticMethodName">A static method without any parameters.</param>
|
||||
/// <returns></returns>
|
||||
public static int RegisterCallback(Type type, string staticMethodName)
|
||||
{
|
||||
if (type == null || string.IsNullOrEmpty(staticMethodName))
|
||||
{
|
||||
Debug.Assert(type != null);
|
||||
Debug.Assert(staticMethodName != null);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if methods has any parameters (that's not supported)
|
||||
try
|
||||
{
|
||||
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
|
||||
var methodInfo = type.GetMethod(staticMethodName, flags);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
Debug.LogError("No static method '" + staticMethodName + "' found in '" + type.FullName + "'.");
|
||||
return -1;
|
||||
}
|
||||
if (methodInfo.GetParameters().Length > 0)
|
||||
{
|
||||
Debug.Assert(methodInfo.GetParameters().Length == 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"CrossCompileCallbacks: Error while checking '{staticMethodName}' method parameters. Error:\n" + e.Message);
|
||||
}
|
||||
|
||||
int index = getNextIndex();
|
||||
SessionState.SetString(indexTypeKey(index), type.FullName);
|
||||
SessionState.SetString(indexMethodKey(index), staticMethodName);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public static void GetCallbackInfo(int index, out string typeName, out string methodName)
|
||||
{
|
||||
typeName = SessionState.GetString(indexTypeKey(index), null);
|
||||
methodName = SessionState.GetString(indexMethodKey(index), null);
|
||||
}
|
||||
|
||||
[DidReloadScripts(-1)]
|
||||
static void onAfterCompilation()
|
||||
{
|
||||
if (DelayExecutionAfterCompilation)
|
||||
{
|
||||
EditorApplication.delayCall -= delayedExecuteRegisteredCallbacks;
|
||||
EditorApplication.delayCall += delayedExecuteRegisteredCallbacks;
|
||||
}
|
||||
else
|
||||
{
|
||||
executeRegisteredCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
static void delayedExecuteRegisteredCallbacks()
|
||||
{
|
||||
EditorApplication.delayCall -= delayedExecuteRegisteredCallbacks;
|
||||
executeRegisteredCallbacks();
|
||||
}
|
||||
|
||||
static void executeRegisteredCallbacks()
|
||||
{
|
||||
int maxIndex = getMaxIndex();
|
||||
for (int i = maxIndex; i >= 0; i--)
|
||||
{
|
||||
string typeName;
|
||||
string methodName;
|
||||
GetCallbackInfo(i, out typeName, out methodName);
|
||||
|
||||
try
|
||||
{
|
||||
ReleaseIndex(i);
|
||||
|
||||
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(methodName))
|
||||
continue;
|
||||
|
||||
var methodInfo = findStaticMethod(typeName, methodName);
|
||||
methodInfo.Invoke(null, null);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
string errorMsg = e.Message;
|
||||
if(errorMsg.Contains("invocation") && e.InnerException != null)
|
||||
{
|
||||
errorMsg += "\n" + e.InnerException.Message;
|
||||
}
|
||||
Debug.LogError($"CrossCompileCallbacks: Calling '{typeName}.{methodName}' failed. Error:\n" + errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static MethodInfo findStaticMethod(string fullTypeName, string methodName)
|
||||
{
|
||||
var type = findType(fullTypeName);
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
|
||||
var methodInfo = type.GetMethod(methodName, flags);
|
||||
|
||||
return methodInfo;
|
||||
}
|
||||
|
||||
static Type findType(string fullTypeName)
|
||||
{
|
||||
Debug.Assert(fullTypeName != null);
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
Type t = assembly.GetType(fullTypeName, throwOnError: false);
|
||||
if (t != null)
|
||||
return t;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Type " + fullTypeName + " doesn't exist in the current app domain.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to store a static parameterless Action
|
||||
/// in the SessionState for retrieval at a later time.
|
||||
/// </summary>
|
||||
/// <param name="sessionStorageKey"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static bool StoreAction(string sessionStorageKey, System.Action action)
|
||||
{
|
||||
if (action == null)
|
||||
return false;
|
||||
|
||||
var methodInfo = action.GetMethodInfo();
|
||||
if (methodInfo == null)
|
||||
return false;
|
||||
|
||||
if (!methodInfo.IsStatic)
|
||||
{
|
||||
Debug.Log("Method '"+ methodInfo.Name + "'needs to be static.");
|
||||
return false;
|
||||
}
|
||||
|
||||
SessionState.SetString(sessionStorageKey + ".Type", methodInfo.DeclaringType.FullName);
|
||||
SessionState.SetString(sessionStorageKey + ".Method", methodInfo.Name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Action from the SessionState.
|
||||
/// </summary>
|
||||
/// <param name="sessionStorageKey"></param>
|
||||
/// <returns></returns>
|
||||
public static System.Action GetStoredAction(string sessionStorageKey)
|
||||
{
|
||||
var typeName = SessionState.GetString(sessionStorageKey + ".Type", null);
|
||||
var methodName = SessionState.GetString(sessionStorageKey + ".Method", null);
|
||||
|
||||
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(methodName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = findType(typeName);
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var methodInfo = findStaticMethod(typeName, methodName);
|
||||
if (methodInfo == null)
|
||||
return null;
|
||||
|
||||
return (Action) Delegate.CreateDelegate(typeof(Action), methodInfo);
|
||||
}
|
||||
|
||||
public static void ClearStoredAction(string sessionStorageKey)
|
||||
{
|
||||
SessionState.EraseString(sessionStorageKey + ".Type");
|
||||
SessionState.EraseString(sessionStorageKey + ".Method");
|
||||
}
|
||||
|
||||
// Testing
|
||||
/*
|
||||
[DidReloadScripts]
|
||||
static void StartTest()
|
||||
{
|
||||
Debug.Log("CrossCompileCallbacks: Starting test.");
|
||||
RegisterCallback(testCallbackA);
|
||||
RegisterCallback(typeof(CrossCompileCallbacks), "testCallbackB");
|
||||
|
||||
var action = GetStoredAction("storedActionA");
|
||||
ClearStoredAction("storedActionA");
|
||||
if (action != null)
|
||||
action.Invoke();
|
||||
StoreAction("storedActionA", storedActionA);
|
||||
}
|
||||
|
||||
static void testCallbackA()
|
||||
{
|
||||
Debug.Log("Test callback A executed.");
|
||||
}
|
||||
|
||||
static void testCallbackB()
|
||||
{
|
||||
Debug.Log("Test callback B executed.");
|
||||
}
|
||||
|
||||
static void storedActionA()
|
||||
{
|
||||
Debug.Log("Stored action A executed.");
|
||||
}
|
||||
//*/
|
||||
}
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user