chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the caching of compilation results.
|
||||
/// Avoids recompiling the same code by hashing requests with SHA256.
|
||||
/// </summary>
|
||||
public class CompilationCacheManager
|
||||
{
|
||||
private const int MaxCacheEntries = 32;
|
||||
private readonly Dictionary<string, CachedCompilationResult> _compilationCache = new();
|
||||
private readonly Queue<string> _cacheOrder = new();
|
||||
|
||||
public CompilationResult CheckCache(CompilationRequest request)
|
||||
{
|
||||
string cacheKey = GenerateCacheKey(request);
|
||||
if (_compilationCache.TryGetValue(cacheKey, out CachedCompilationResult cachedResult))
|
||||
{
|
||||
return CloneCompilationResult(cachedResult);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CacheResultIfSuccessful(CompilationResult result, CompilationRequest request)
|
||||
{
|
||||
if (result.Success && result.CompiledAssembly != null)
|
||||
{
|
||||
string cacheKey = GenerateCacheKey(request);
|
||||
CachedCompilationResult cachedResult = CreateCachedCompilationResult(result);
|
||||
if (_compilationCache.ContainsKey(cacheKey))
|
||||
{
|
||||
_compilationCache[cacheKey] = cachedResult;
|
||||
return;
|
||||
}
|
||||
|
||||
// Dynamic assemblies cannot be unloaded from the default AppDomain,
|
||||
// so the cache keeps only a small hot set instead of retaining every snippet forever.
|
||||
if (_compilationCache.Count >= MaxCacheEntries)
|
||||
{
|
||||
string oldestKey = _cacheOrder.Dequeue();
|
||||
_compilationCache.Remove(oldestKey);
|
||||
}
|
||||
|
||||
_compilationCache[cacheKey] = cachedResult;
|
||||
_cacheOrder.Enqueue(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
_compilationCache.Clear();
|
||||
_cacheOrder.Clear();
|
||||
}
|
||||
|
||||
public string GenerateCacheKey(CompilationRequest request)
|
||||
{
|
||||
StringBuilder keyBuilder = new StringBuilder();
|
||||
keyBuilder.Append(request.Code);
|
||||
keyBuilder.Append("|");
|
||||
keyBuilder.Append(request.ClassName ?? "");
|
||||
keyBuilder.Append("|");
|
||||
keyBuilder.Append(request.Namespace ?? "");
|
||||
|
||||
if (request.AdditionalReferences != null && request.AdditionalReferences.Any())
|
||||
{
|
||||
keyBuilder.Append("|");
|
||||
keyBuilder.Append(string.Join(",", request.AdditionalReferences.OrderBy(r => r)));
|
||||
}
|
||||
|
||||
using (SHA256 sha256 = SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(keyBuilder.ToString()));
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static CachedCompilationResult CreateCachedCompilationResult(CompilationResult result)
|
||||
{
|
||||
return new CachedCompilationResult(
|
||||
result.CompiledAssembly,
|
||||
CloneCompilationErrors(result.Errors),
|
||||
CloneStrings(result.Warnings),
|
||||
result.UpdatedCode,
|
||||
result.HasSecurityViolations,
|
||||
CloneSecurityViolations(result.SecurityViolations),
|
||||
result.FailureReason,
|
||||
CloneAmbiguousTypeCandidates(result.AmbiguousTypeCandidates),
|
||||
CloneStrings(result.AutoInjectedNamespaces),
|
||||
CloneStrings(result.Timings),
|
||||
CloneStrings(result.AdvisoryLogs),
|
||||
result.CompilationBackendKind);
|
||||
}
|
||||
|
||||
private static CompilationResult CloneCompilationResult(CachedCompilationResult cachedResult)
|
||||
{
|
||||
return new CompilationResult
|
||||
{
|
||||
Success = true,
|
||||
CompiledAssembly = cachedResult.CompiledAssembly,
|
||||
Errors = CloneCompilationErrors(cachedResult.Errors),
|
||||
Warnings = CloneStrings(cachedResult.Warnings),
|
||||
UpdatedCode = cachedResult.UpdatedCode,
|
||||
HasSecurityViolations = cachedResult.HasSecurityViolations,
|
||||
SecurityViolations = CloneSecurityViolations(cachedResult.SecurityViolations),
|
||||
FailureReason = cachedResult.FailureReason,
|
||||
AmbiguousTypeCandidates = CloneAmbiguousTypeCandidates(cachedResult.AmbiguousTypeCandidates),
|
||||
AutoInjectedNamespaces = CloneStrings(cachedResult.AutoInjectedNamespaces),
|
||||
Timings = BuildCachedCompilationTimings(cachedResult.CompilationBackendKind),
|
||||
AdvisoryLogs = CloneStrings(cachedResult.AdvisoryLogs),
|
||||
CompilationBackendKind = cachedResult.CompilationBackendKind
|
||||
};
|
||||
}
|
||||
|
||||
private static List<string> BuildCachedCompilationTimings(
|
||||
DynamicCompilationBackendKind compilationBackendKind)
|
||||
{
|
||||
List<string> timings = new List<string>
|
||||
{
|
||||
"[Perf] ReferenceResolution: 0.0ms",
|
||||
"[Perf] Build: 0.0ms",
|
||||
"[Perf] AssemblyLoad: 0.0ms"
|
||||
};
|
||||
|
||||
switch (compilationBackendKind)
|
||||
{
|
||||
case DynamicCompilationBackendKind.SharedRoslynWorker:
|
||||
timings.Add("[Perf] Backend: SharedRoslynWorker");
|
||||
break;
|
||||
case DynamicCompilationBackendKind.OneShotRoslyn:
|
||||
timings.Add("[Perf] Backend: OneShotRoslyn");
|
||||
break;
|
||||
case DynamicCompilationBackendKind.AssemblyBuilderFallback:
|
||||
timings.Add("[Perf] Backend: AssemblyBuilderFallback");
|
||||
break;
|
||||
}
|
||||
|
||||
timings.Add("[Perf] CacheHit: true");
|
||||
return timings;
|
||||
}
|
||||
|
||||
private static List<CompilationError> CloneCompilationErrors(List<CompilationError> errors)
|
||||
{
|
||||
List<CompilationError> clonedErrors = new List<CompilationError>();
|
||||
if (errors == null)
|
||||
{
|
||||
return clonedErrors;
|
||||
}
|
||||
|
||||
foreach (CompilationError error in errors)
|
||||
{
|
||||
clonedErrors.Add(new CompilationError
|
||||
{
|
||||
Message = error.Message,
|
||||
Line = error.Line,
|
||||
Column = error.Column,
|
||||
ErrorCode = error.ErrorCode
|
||||
});
|
||||
}
|
||||
|
||||
return clonedErrors;
|
||||
}
|
||||
|
||||
private static List<SecurityViolation> CloneSecurityViolations(List<SecurityViolation> securityViolations)
|
||||
{
|
||||
List<SecurityViolation> clonedViolations = new List<SecurityViolation>();
|
||||
if (securityViolations == null)
|
||||
{
|
||||
return clonedViolations;
|
||||
}
|
||||
|
||||
foreach (SecurityViolation violation in securityViolations)
|
||||
{
|
||||
clonedViolations.Add(new SecurityViolation
|
||||
{
|
||||
Type = violation.Type,
|
||||
Description = violation.Description,
|
||||
LineNumber = violation.LineNumber,
|
||||
CodeSnippet = violation.CodeSnippet,
|
||||
Message = violation.Message,
|
||||
ApiName = violation.ApiName,
|
||||
Location = violation.Location
|
||||
});
|
||||
}
|
||||
|
||||
return clonedViolations;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> CloneAmbiguousTypeCandidates(
|
||||
Dictionary<string, List<string>> ambiguousTypeCandidates)
|
||||
{
|
||||
Dictionary<string, List<string>> clonedCandidates = new Dictionary<string, List<string>>();
|
||||
if (ambiguousTypeCandidates == null)
|
||||
{
|
||||
return clonedCandidates;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<string>> entry in ambiguousTypeCandidates)
|
||||
{
|
||||
clonedCandidates[entry.Key] = CloneStrings(entry.Value);
|
||||
}
|
||||
|
||||
return clonedCandidates;
|
||||
}
|
||||
|
||||
private static List<string> CloneStrings(List<string> values)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
return new List<string>(values);
|
||||
}
|
||||
|
||||
private sealed class CachedCompilationResult
|
||||
{
|
||||
public Assembly CompiledAssembly { get; }
|
||||
|
||||
public List<CompilationError> Errors { get; }
|
||||
|
||||
public List<string> Warnings { get; }
|
||||
|
||||
public string UpdatedCode { get; }
|
||||
|
||||
public bool HasSecurityViolations { get; }
|
||||
|
||||
public List<SecurityViolation> SecurityViolations { get; }
|
||||
|
||||
public CompilationFailureReason FailureReason { get; }
|
||||
|
||||
public Dictionary<string, List<string>> AmbiguousTypeCandidates { get; }
|
||||
|
||||
public List<string> AutoInjectedNamespaces { get; }
|
||||
|
||||
public List<string> Timings { get; }
|
||||
|
||||
public List<string> AdvisoryLogs { get; }
|
||||
|
||||
public DynamicCompilationBackendKind CompilationBackendKind { get; }
|
||||
|
||||
public CachedCompilationResult(
|
||||
Assembly compiledAssembly,
|
||||
List<CompilationError> errors,
|
||||
List<string> warnings,
|
||||
string updatedCode,
|
||||
bool hasSecurityViolations,
|
||||
List<SecurityViolation> securityViolations,
|
||||
CompilationFailureReason failureReason,
|
||||
Dictionary<string, List<string>> ambiguousTypeCandidates,
|
||||
List<string> autoInjectedNamespaces,
|
||||
List<string> timings,
|
||||
List<string> advisoryLogs,
|
||||
DynamicCompilationBackendKind compilationBackendKind)
|
||||
{
|
||||
CompiledAssembly = compiledAssembly;
|
||||
Errors = errors;
|
||||
Warnings = warnings;
|
||||
UpdatedCode = updatedCode;
|
||||
HasSecurityViolations = hasSecurityViolations;
|
||||
SecurityViolations = securityViolations;
|
||||
FailureReason = failureReason;
|
||||
AmbiguousTypeCandidates = ambiguousTypeCandidates;
|
||||
AutoInjectedNamespaces = autoInjectedNamespaces;
|
||||
Timings = timings;
|
||||
AdvisoryLogs = advisoryLogs;
|
||||
CompilationBackendKind = compilationBackendKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8f1ec6f980b54adda3f6317615191b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class DomainReloadStateRegistry
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static IDomainReloadStateProvider _provider;
|
||||
|
||||
public static void RegisterProvider(IDomainReloadStateProvider provider)
|
||||
{
|
||||
Debug.Assert(provider != null, "provider must not be null");
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDomainReloadInProgress()
|
||||
{
|
||||
IDomainReloadStateProvider provider;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
provider = _provider;
|
||||
}
|
||||
|
||||
if (provider == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return provider.IsDomainReloadInProgress();
|
||||
}
|
||||
|
||||
public static IDomainReloadStateProvider SwapProviderForTests(IDomainReloadStateProvider provider)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
IDomainReloadStateProvider previousProvider = _provider;
|
||||
_provider = provider;
|
||||
return previousProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16f03c71027d5433cb920e1830289b86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class DynamicCompilationServiceRegistry
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static IDynamicCompilationServiceFactory _factory;
|
||||
|
||||
public static bool HasRegisteredFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return _factory != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterFactory(IDynamicCompilationServiceFactory factory)
|
||||
{
|
||||
Debug.Assert(factory != null, "factory must not be null");
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryCreate(DynamicCodeSecurityLevel securityLevel, out IDynamicCompilationService service)
|
||||
{
|
||||
IDynamicCompilationServiceFactory factory;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
factory = _factory;
|
||||
}
|
||||
|
||||
if (factory == null)
|
||||
{
|
||||
service = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
service = factory.Create(securityLevel);
|
||||
return service != null;
|
||||
}
|
||||
|
||||
public static IDynamicCompilationServiceFactory SwapFactoryForTests(IDynamicCompilationServiceFactory factory)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
IDynamicCompilationServiceFactory previousFactory = _factory;
|
||||
_factory = factory;
|
||||
return previousFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed1454bd9d3934dea8704fd28061c595
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class PreloadAssemblySecurityValidatorRegistry
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static IPreloadAssemblySecurityValidator _validator;
|
||||
|
||||
public static void RegisterValidator(IPreloadAssemblySecurityValidator validator)
|
||||
{
|
||||
Debug.Assert(validator != null, "validator must not be null");
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
_validator = validator;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetValidator(out IPreloadAssemblySecurityValidator validator)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
validator = _validator;
|
||||
}
|
||||
|
||||
return validator != null;
|
||||
}
|
||||
|
||||
public static IPreloadAssemblySecurityValidator SwapValidatorForTests(IPreloadAssemblySecurityValidator validator)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
IPreloadAssemblySecurityValidator previousValidator = _validator;
|
||||
_validator = validator;
|
||||
return previousValidator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb0c09b05bdd64ef3b534c2a17507638
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user