chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilities to await arbitrary awaitable objects using the C# awaitable pattern.
|
||||
/// Supports Task, Task<T>, ValueTask, ValueTask<T>, UniTask, UniTask<T> and any type
|
||||
/// implementing GetAwaiter/OnCompleted/GetResult.
|
||||
/// </summary>
|
||||
internal static class AwaitableHelper
|
||||
{
|
||||
public static async Task<object> AwaitIfNeeded(object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Type valueType = value.GetType();
|
||||
|
||||
// Task and Task<T>
|
||||
if (typeof(Task).IsAssignableFrom(valueType))
|
||||
{
|
||||
Task task = (Task)value;
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
PropertyInfo resultProperty = valueType.GetProperty("Result");
|
||||
if (resultProperty != null)
|
||||
{
|
||||
object taskResult = resultProperty.GetValue(value);
|
||||
return taskResult;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ValueTask and ValueTask<T> via AsTask if available
|
||||
if (IsValueTask(valueType))
|
||||
{
|
||||
Task asTask = ConvertValueTaskToTask(value);
|
||||
await asTask.ConfigureAwait(false);
|
||||
return null;
|
||||
}
|
||||
if (IsGenericValueTask(valueType))
|
||||
{
|
||||
Task asTask = ConvertGenericValueTaskToTask(value);
|
||||
await asTask.ConfigureAwait(false);
|
||||
|
||||
Type[] genericArgs = valueType.GetGenericArguments();
|
||||
if (genericArgs != null && genericArgs.Length == 1)
|
||||
{
|
||||
Type taskType = typeof(Task<>).MakeGenericType(genericArgs[0]);
|
||||
PropertyInfo resultProperty = taskType.GetProperty("Result");
|
||||
if (resultProperty != null)
|
||||
{
|
||||
object resultValue = resultProperty.GetValue(asTask);
|
||||
return resultValue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Awaitable pattern fallback (e.g., UniTask/UniTask<T> or custom awaitables)
|
||||
MethodInfo getAwaiterMethod = valueType.GetMethod("GetAwaiter", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
if (getAwaiterMethod != null)
|
||||
{
|
||||
object awaiter = getAwaiterMethod.Invoke(value, null);
|
||||
if (awaiter == null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
Type awaiterType = awaiter.GetType();
|
||||
PropertyInfo isCompletedProperty = awaiterType.GetProperty("IsCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
MethodInfo getResultMethod = awaiterType.GetMethod("GetResult", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
MethodInfo onCompletedMethod = awaiterType.GetMethod("OnCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Action) }, null);
|
||||
MethodInfo unsafeOnCompletedMethod = awaiterType.GetMethod("UnsafeOnCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Action) }, null);
|
||||
|
||||
if (isCompletedProperty != null)
|
||||
{
|
||||
bool isCompleted = (bool)isCompletedProperty.GetValue(awaiter);
|
||||
if (isCompleted)
|
||||
{
|
||||
object completedResult = InvokeGetResultSafely(getResultMethod, awaiter);
|
||||
return completedResult;
|
||||
}
|
||||
}
|
||||
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
|
||||
|
||||
Action continuation = () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
object continuationResult = InvokeGetResultSafely(getResultMethod, awaiter);
|
||||
tcs.TrySetResult(continuationResult);
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
Exception inner = tie.InnerException ?? tie;
|
||||
tcs.TrySetException(inner);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
};
|
||||
|
||||
if (unsafeOnCompletedMethod != null)
|
||||
{
|
||||
unsafeOnCompletedMethod.Invoke(awaiter, new object[] { continuation });
|
||||
}
|
||||
else if (onCompletedMethod != null)
|
||||
{
|
||||
onCompletedMethod.Invoke(awaiter, new object[] { continuation });
|
||||
}
|
||||
else
|
||||
{
|
||||
// No completion registration; treat as completed
|
||||
object immediateResult = InvokeGetResultSafely(getResultMethod, awaiter);
|
||||
return immediateResult;
|
||||
}
|
||||
|
||||
object awaited = await tcs.Task.ConfigureAwait(false);
|
||||
return awaited;
|
||||
}
|
||||
|
||||
// Not awaitable; return as-is
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool IsValueTask(Type type)
|
||||
{
|
||||
return type.FullName == "System.Threading.Tasks.ValueTask";
|
||||
}
|
||||
|
||||
private static bool IsGenericValueTask(Type type)
|
||||
{
|
||||
if (!type.IsGenericType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Type generic = type.GetGenericTypeDefinition();
|
||||
return generic.FullName == "System.Threading.Tasks.ValueTask`1";
|
||||
}
|
||||
|
||||
private static Task ConvertValueTaskToTask(object valueTask)
|
||||
{
|
||||
Type type = valueTask.GetType();
|
||||
MethodInfo asTaskMethod = type.GetMethod("AsTask", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (asTaskMethod != null)
|
||||
{
|
||||
object taskObj = asTaskMethod.Invoke(valueTask, null);
|
||||
return (Task)taskObj;
|
||||
}
|
||||
|
||||
// Fallback: build a Task from awaiter
|
||||
MethodInfo getAwaiter = type.GetMethod("GetAwaiter", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
if (getAwaiter == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
object awaiter = getAwaiter.Invoke(valueTask, null);
|
||||
Type awaiterType = awaiter.GetType();
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
MethodInfo onCompleted = awaiterType.GetMethod("OnCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Action) }, null);
|
||||
MethodInfo unsafeOnCompleted = awaiterType.GetMethod("UnsafeOnCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Action) }, null);
|
||||
MethodInfo getResult = awaiterType.GetMethod("GetResult", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
PropertyInfo isCompletedProperty = awaiterType.GetProperty("IsCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
bool hasIsCompleted = false;
|
||||
bool isCompleted = false;
|
||||
if (isCompletedProperty != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
object isCompletedValue = isCompletedProperty.GetValue(awaiter);
|
||||
if (isCompletedValue is bool boolValue)
|
||||
{
|
||||
isCompleted = boolValue;
|
||||
hasIsCompleted = true;
|
||||
}
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
Exception inner = tie.InnerException ?? tie;
|
||||
tcs.TrySetException(inner);
|
||||
return tcs.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
|
||||
Action cont = () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (getResult != null)
|
||||
{
|
||||
getResult.Invoke(awaiter, null);
|
||||
}
|
||||
tcs.TrySetResult(null);
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
Exception inner = tie.InnerException ?? tie;
|
||||
tcs.TrySetException(inner);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
};
|
||||
|
||||
if (hasIsCompleted && isCompleted)
|
||||
{
|
||||
cont();
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
if (unsafeOnCompleted != null)
|
||||
{
|
||||
unsafeOnCompleted.Invoke(awaiter, new object[] { cont });
|
||||
}
|
||||
else if (onCompleted != null)
|
||||
{
|
||||
onCompleted.Invoke(awaiter, new object[] { cont });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasIsCompleted && isCompleted)
|
||||
{
|
||||
cont();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Task.Run(cont);
|
||||
}
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private static Task ConvertGenericValueTaskToTask(object valueTaskT)
|
||||
{
|
||||
Type type = valueTaskT.GetType();
|
||||
MethodInfo asTaskMethod = type.GetMethod("AsTask", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (asTaskMethod != null)
|
||||
{
|
||||
object taskObj = asTaskMethod.Invoke(valueTaskT, null);
|
||||
return (Task)taskObj;
|
||||
}
|
||||
|
||||
// Fallback through awaiter
|
||||
MethodInfo getAwaiter = type.GetMethod("GetAwaiter", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
if (getAwaiter == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
object awaiter = getAwaiter.Invoke(valueTaskT, null);
|
||||
Type awaiterType = awaiter.GetType();
|
||||
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
MethodInfo onCompleted = awaiterType.GetMethod("OnCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Action) }, null);
|
||||
MethodInfo unsafeOnCompleted = awaiterType.GetMethod("UnsafeOnCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(Action) }, null);
|
||||
MethodInfo getResult = awaiterType.GetMethod("GetResult", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
|
||||
PropertyInfo isCompletedProperty = awaiterType.GetProperty("IsCompleted", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
bool hasIsCompleted = false;
|
||||
bool isCompleted = false;
|
||||
if (isCompletedProperty != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
object isCompletedValue = isCompletedProperty.GetValue(awaiter);
|
||||
if (isCompletedValue is bool boolValue)
|
||||
{
|
||||
isCompleted = boolValue;
|
||||
hasIsCompleted = true;
|
||||
}
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
Exception inner = tie.InnerException ?? tie;
|
||||
tcs.TrySetException(inner);
|
||||
return tcs.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
|
||||
Action cont = () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (getResult != null)
|
||||
{
|
||||
object _ = getResult.Invoke(awaiter, null);
|
||||
}
|
||||
tcs.TrySetResult(null);
|
||||
}
|
||||
catch (TargetInvocationException tie)
|
||||
{
|
||||
Exception inner = tie.InnerException ?? tie;
|
||||
tcs.TrySetException(inner);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tcs.TrySetException(ex);
|
||||
}
|
||||
};
|
||||
|
||||
if (hasIsCompleted && isCompleted)
|
||||
{
|
||||
cont();
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
if (unsafeOnCompleted != null)
|
||||
{
|
||||
unsafeOnCompleted.Invoke(awaiter, new object[] { cont });
|
||||
}
|
||||
else if (onCompleted != null)
|
||||
{
|
||||
onCompleted.Invoke(awaiter, new object[] { cont });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasIsCompleted && isCompleted)
|
||||
{
|
||||
cont();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = Task.Run(cont);
|
||||
}
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private static object InvokeGetResultSafely(MethodInfo getResultMethod, object awaiter)
|
||||
{
|
||||
if (getResultMethod == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
object result = getResultMethod.Invoke(awaiter, null);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user