Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
- Добавлен компонент подмены спрайтов игрока перед началом игры. - Обновлены сцена уровня, префабы, шейдеры, настройки импорта и данные призов. - Зафиксирован embedded-пакет Unity MCP 9.6.8 и обновлены связанные файлы проекта.
164 lines
5.8 KiB
C#
164 lines
5.8 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using MCPForUnity.Editor.Constants;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using MCPForUnity.Editor.Services.Transport;
|
|
using MCPForUnity.Editor.Windows;
|
|
using UnityEditor;
|
|
|
|
namespace MCPForUnity.Editor.Services
|
|
{
|
|
/// <summary>
|
|
/// Ensures HTTP transports resume after domain reloads similar to the legacy stdio bridge.
|
|
/// </summary>
|
|
[InitializeOnLoad]
|
|
internal static class HttpBridgeReloadHandler
|
|
{
|
|
private static readonly TimeSpan[] ResumeRetrySchedule =
|
|
{
|
|
TimeSpan.Zero,
|
|
TimeSpan.FromSeconds(1),
|
|
TimeSpan.FromSeconds(3),
|
|
TimeSpan.FromSeconds(5),
|
|
TimeSpan.FromSeconds(10),
|
|
TimeSpan.FromSeconds(30)
|
|
};
|
|
|
|
static HttpBridgeReloadHandler()
|
|
{
|
|
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
|
|
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
|
|
}
|
|
|
|
private static void OnBeforeAssemblyReload()
|
|
{
|
|
try
|
|
{
|
|
var transport = MCPServiceLocator.TransportManager;
|
|
bool shouldResume = transport.IsRunning(TransportMode.Http);
|
|
|
|
if (shouldResume)
|
|
{
|
|
EditorPrefs.SetBool(EditorPrefKeys.ResumeHttpAfterReload, true);
|
|
}
|
|
else
|
|
{
|
|
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeHttpAfterReload);
|
|
}
|
|
|
|
if (shouldResume)
|
|
{
|
|
// beforeAssemblyReload is synchronous; force a synchronous teardown so we do not
|
|
// leave an orphaned socket due to an unfinished async close handshake.
|
|
transport.ForceStop(TransportMode.Http);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
McpLog.Warn($"Failed to evaluate HTTP bridge reload state: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private static void OnAfterAssemblyReload()
|
|
{
|
|
bool resume = false;
|
|
try
|
|
{
|
|
// Only resume HTTP if it is still the selected transport.
|
|
bool useHttp = EditorConfigurationCache.Instance.UseHttpTransport;
|
|
resume = useHttp && EditorPrefs.GetBool(EditorPrefKeys.ResumeHttpAfterReload, false);
|
|
if (resume)
|
|
{
|
|
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeHttpAfterReload);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
McpLog.Warn($"Failed to read HTTP bridge reload flag: {ex.Message}");
|
|
resume = false;
|
|
}
|
|
|
|
if (!resume)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the editor is not compiling, attempt an immediate restart without relying on editor focus.
|
|
bool isCompiling = EditorApplication.isCompiling;
|
|
try
|
|
{
|
|
var pipeline = Type.GetType("UnityEditor.Compilation.CompilationPipeline, UnityEditor");
|
|
var prop = pipeline?.GetProperty("isCompiling", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
|
if (prop != null) isCompiling |= (bool)prop.GetValue(null);
|
|
}
|
|
catch { }
|
|
|
|
if (!isCompiling)
|
|
{
|
|
_ = ResumeHttpWithRetriesAsync();
|
|
return;
|
|
}
|
|
|
|
// Fallback when compiling: schedule on the editor loop
|
|
EditorApplication.delayCall += () =>
|
|
{
|
|
_ = ResumeHttpWithRetriesAsync();
|
|
};
|
|
}
|
|
|
|
private static async Task ResumeHttpWithRetriesAsync()
|
|
{
|
|
Exception lastException = null;
|
|
|
|
for (int i = 0; i < ResumeRetrySchedule.Length; i++)
|
|
{
|
|
int attempt = i + 1;
|
|
McpLog.Debug($"[HTTP Reload] Resume attempt {attempt}/{ResumeRetrySchedule.Length}");
|
|
|
|
TimeSpan delay = ResumeRetrySchedule[i];
|
|
if (delay > TimeSpan.Zero)
|
|
{
|
|
McpLog.Debug($"[HTTP Reload] Waiting {delay.TotalSeconds:0.#}s before resume attempt {attempt}");
|
|
try { await Task.Delay(delay); }
|
|
catch { return; }
|
|
}
|
|
|
|
// Abort retries if the user switched transports while we were waiting.
|
|
if (!EditorConfigurationCache.Instance.UseHttpTransport)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
bool started = await MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http);
|
|
if (started)
|
|
{
|
|
McpLog.Debug($"[HTTP Reload] Resume succeeded on attempt {attempt}");
|
|
MCPForUnityEditorWindow.RequestHealthVerification();
|
|
return;
|
|
}
|
|
|
|
var state = MCPServiceLocator.TransportManager.GetState(TransportMode.Http);
|
|
string reason = string.IsNullOrWhiteSpace(state?.Error) ? "no error detail" : state.Error;
|
|
McpLog.Debug($"[HTTP Reload] Resume attempt {attempt} failed: {reason}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lastException = ex;
|
|
McpLog.Debug($"[HTTP Reload] Resume attempt {attempt} threw: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
if (lastException != null)
|
|
{
|
|
McpLog.Warn($"Failed to resume HTTP MCP bridge after domain reload: {lastException.Message}");
|
|
}
|
|
else
|
|
{
|
|
McpLog.Warn("Failed to resume HTTP MCP bridge after domain reload");
|
|
}
|
|
}
|
|
}
|
|
}
|