241 lines
8.0 KiB
C#
241 lines
8.0 KiB
C#
using Unity.Profiling;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace io.github.hatayama.uLoopMCP
|
|
{
|
|
/// <summary>
|
|
/// Event handler for McpEditorWindow - Manages Unity and server events
|
|
/// Helper class for Presenter layer in MVP architecture
|
|
/// Related classes:
|
|
/// - McpEditorWindow: Main presenter that owns this handler
|
|
/// - McpEditorModel: Model layer for state management
|
|
/// - McpBridgeServer: Server that provides events
|
|
/// - McpServerController: Server lifecycle management
|
|
/// </summary>
|
|
internal class McpEditorWindowEventHandler
|
|
{
|
|
private static readonly ProfilerMarker s_onEditorUpdateMarker =
|
|
new ProfilerMarker("McpEditorWindow.OnEditorUpdate");
|
|
private static readonly ProfilerMarker s_refreshUiMarker =
|
|
new ProfilerMarker("McpEditorWindow.RefreshUI");
|
|
|
|
private readonly McpEditorModel _model;
|
|
private readonly McpEditorWindow _window;
|
|
|
|
public McpEditorWindowEventHandler(McpEditorModel model, McpEditorWindow window)
|
|
{
|
|
_model = model;
|
|
_window = window;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize all event subscriptions
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
SubscribeToUnityEvents();
|
|
SubscribeToServerEvents();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cleanup all event subscriptions
|
|
/// </summary>
|
|
public void Cleanup()
|
|
{
|
|
UnsubscribeFromUnityEvents();
|
|
UnsubscribeFromServerEvents();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Subscribe to Unity Editor events
|
|
/// </summary>
|
|
private void SubscribeToUnityEvents()
|
|
{
|
|
EditorApplication.update += OnEditorUpdate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsubscribe from Unity Editor events
|
|
/// </summary>
|
|
private void UnsubscribeFromUnityEvents()
|
|
{
|
|
EditorApplication.update -= OnEditorUpdate;
|
|
}
|
|
|
|
private void SubscribeToServerEvents()
|
|
{
|
|
UnsubscribeFromServerEvents();
|
|
|
|
McpBridgeServer.OnServerStarted += OnServerStateChanged;
|
|
McpBridgeServer.OnServerStopping += OnServerStateChanged;
|
|
ConnectedToolsMonitoringService.OnConnectedToolsChanged += OnConnectedToolsChanged;
|
|
CustomToolManager.OnToolsChanged += OnToolsChanged;
|
|
|
|
McpBridgeServer currentServer = McpServerController.CurrentServer;
|
|
if (currentServer != null)
|
|
{
|
|
currentServer.OnClientConnected += OnClientConnected;
|
|
currentServer.OnClientDisconnected += OnClientDisconnected;
|
|
}
|
|
}
|
|
|
|
private void UnsubscribeFromServerEvents()
|
|
{
|
|
McpBridgeServer.OnServerStarted -= OnServerStateChanged;
|
|
McpBridgeServer.OnServerStopping -= OnServerStateChanged;
|
|
ConnectedToolsMonitoringService.OnConnectedToolsChanged -= OnConnectedToolsChanged;
|
|
CustomToolManager.OnToolsChanged -= OnToolsChanged;
|
|
|
|
McpBridgeServer currentServer = McpServerController.CurrentServer;
|
|
if (currentServer != null)
|
|
{
|
|
currentServer.OnClientConnected -= OnClientConnected;
|
|
currentServer.OnClientDisconnected -= OnClientDisconnected;
|
|
}
|
|
}
|
|
|
|
private void OnServerStateChanged()
|
|
{
|
|
_window.InvalidateToolSettingsCatalog();
|
|
_model.RequestRepaint();
|
|
}
|
|
|
|
private void OnConnectedToolsChanged()
|
|
{
|
|
_model.RequestRepaint();
|
|
}
|
|
|
|
private void OnToolsChanged()
|
|
{
|
|
_window.InvalidateToolSettingsCatalog();
|
|
_model.RequestRepaint();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client connection event - force UI repaint for immediate update
|
|
/// </summary>
|
|
private void OnClientConnected(string clientEndpoint)
|
|
{
|
|
// Enhanced logging for debugging client connection
|
|
// Count check for debugging purposes only
|
|
|
|
// Clear reconnecting flags when client connects
|
|
McpServerController.ClearReconnectingFlag();
|
|
|
|
// Mark that repaint is needed since events are called from background thread
|
|
_model.RequestRepaint();
|
|
|
|
// Exit post-compile mode when client connects
|
|
if (_model.Runtime.IsPostCompileMode)
|
|
{
|
|
_model.DisablePostCompileMode();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle client disconnection event - force UI repaint for immediate update
|
|
/// </summary>
|
|
private void OnClientDisconnected(string clientEndpoint)
|
|
{
|
|
// Enhanced logging for debugging client disconnection issues
|
|
// Count check for debugging purposes only
|
|
|
|
|
|
// Mark that repaint is needed since events are called from background thread
|
|
_model.RequestRepaint();
|
|
}
|
|
|
|
private void OnEditorUpdate()
|
|
{
|
|
using (s_onEditorUpdateMarker.Auto())
|
|
{
|
|
if (!McpEditorWindowRefreshPolicy.ShouldRefreshOnEditorUpdate(_model.Runtime))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_model.ClearRepaintRequest();
|
|
using (s_refreshUiMarker.Auto())
|
|
{
|
|
_window.RefreshAllSections();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Re-subscribe to server events (called after server start)
|
|
/// </summary>
|
|
public void RefreshServerEventSubscriptions()
|
|
{
|
|
SubscribeToServerEvents();
|
|
}
|
|
}
|
|
|
|
// Post-compile recovery can stay active while UI data is unchanged, so explicit repaint
|
|
// requests gate expensive full-section refreshes.
|
|
internal static class McpEditorWindowRefreshPolicy
|
|
{
|
|
public static bool ShouldRefreshOnEditorUpdate(RuntimeState runtimeState)
|
|
{
|
|
Debug.Assert(runtimeState != null, "runtimeState must not be null");
|
|
|
|
return runtimeState.NeedsRepaint;
|
|
}
|
|
|
|
public static bool ShouldRunExpensiveChecks(McpEditorWindowRefreshMode refreshMode)
|
|
{
|
|
return refreshMode == McpEditorWindowRefreshMode.Full;
|
|
}
|
|
|
|
public static bool ShouldRefreshSkillInstallState(
|
|
McpEditorWindowRefreshMode refreshMode,
|
|
bool refreshRequested)
|
|
{
|
|
return refreshRequested && ShouldRunExpensiveChecks(refreshMode);
|
|
}
|
|
|
|
public static bool ShouldKeepToolSettingsCatalogDirty(ToolSettingsSectionData toolSettingsData)
|
|
{
|
|
Debug.Assert(toolSettingsData != null, "toolSettingsData must not be null");
|
|
|
|
return toolSettingsData.ShowToolSettings && !toolSettingsData.IsRegistryAvailable;
|
|
}
|
|
|
|
public static bool ShouldStartToolSettingsRegistryWarmup(
|
|
bool isAlreadyScheduled,
|
|
int attemptCount,
|
|
int maxAttempts)
|
|
{
|
|
Debug.Assert(attemptCount >= 0, "attemptCount must not be negative");
|
|
Debug.Assert(maxAttempts > 0, "maxAttempts must be positive");
|
|
|
|
return !isAlreadyScheduled && attemptCount < maxAttempts;
|
|
}
|
|
|
|
public static double CalculateToolSettingsRegistryWarmupDelaySeconds(
|
|
double initialDelaySeconds,
|
|
double maxDelaySeconds,
|
|
int attemptCount)
|
|
{
|
|
Debug.Assert(initialDelaySeconds > 0.0, "initialDelaySeconds must be positive");
|
|
Debug.Assert(maxDelaySeconds >= initialDelaySeconds, "maxDelaySeconds must not be smaller than initialDelaySeconds");
|
|
Debug.Assert(attemptCount >= 0, "attemptCount must not be negative");
|
|
|
|
double delaySeconds = initialDelaySeconds;
|
|
for (int i = 0; i < attemptCount; i++)
|
|
{
|
|
delaySeconds *= 2.0;
|
|
}
|
|
|
|
return System.Math.Min(delaySeconds, maxDelaySeconds);
|
|
}
|
|
}
|
|
|
|
internal enum McpEditorWindowRefreshMode
|
|
{
|
|
InitialPaint,
|
|
Full
|
|
}
|
|
}
|