chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// UseCase responsible for temporal cohesion of Domain Reload recovery processing
|
||||
/// Processing sequence: 1. Pre-stop processing, 2. Recovery processing, 3. Notification processing
|
||||
/// Related classes: DomainReloadDetectionService, SessionRecoveryService, ClientNotificationService
|
||||
/// </summary>
|
||||
public class DomainReloadRecoveryUseCase
|
||||
{
|
||||
/// <summary>
|
||||
/// Execute processing before Domain Reload starts
|
||||
/// </summary>
|
||||
/// <param name="currentServer">Current server instance</param>
|
||||
/// <returns>Processing result</returns>
|
||||
public ServiceResult<string> ExecuteBeforeDomainReload(McpBridgeServer currentServer)
|
||||
{
|
||||
// 1. Generate tracking ID for related operations
|
||||
string correlationId = VibeLogger.GenerateCorrelationId();
|
||||
|
||||
// 2. Check server state from instance
|
||||
bool serverRunning = currentServer?.IsRunning ?? false;
|
||||
int? serverPort = currentServer?.Port;
|
||||
|
||||
// 3. Fallback to session state if instance is null but session says server was running
|
||||
// Handles case where mcpServer instance became null unexpectedly
|
||||
if (currentServer == null && McpEditorSettings.GetIsServerRunning())
|
||||
{
|
||||
int sessionPort = McpEditorSettings.GetCustomPort();
|
||||
if (NetworkUtility.IsValidPort(sessionPort))
|
||||
{
|
||||
serverRunning = true;
|
||||
serverPort = sessionPort;
|
||||
VibeLogger.LogWarning(
|
||||
"domain_reload_session_fallback",
|
||||
"Server instance is null but session state indicates running. Using session state for recovery.",
|
||||
new { session_port = sessionPort },
|
||||
correlationId
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
VibeLogger.LogWarning(
|
||||
"domain_reload_session_fallback_invalid_port",
|
||||
"Session indicates running but port is invalid. Ignoring session state fallback.",
|
||||
new { session_port = sessionPort },
|
||||
correlationId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Detect and record Domain Reload start
|
||||
DomainReloadDetectionService.StartDomainReload(correlationId, serverRunning, serverPort);
|
||||
|
||||
// 4. If server is running, execute stop processing
|
||||
if (currentServer?.IsRunning == true)
|
||||
{
|
||||
int portToSave = currentServer.Port;
|
||||
|
||||
try
|
||||
{
|
||||
// 4.1. Notify client of server stop
|
||||
ClientNotificationService.LogServerStoppingBeforeDomainReload(correlationId, portToSave);
|
||||
|
||||
// 4.2. Stop server
|
||||
currentServer.StopServerBeforeDomainReload();
|
||||
|
||||
// 4.3. Notify client of stop completion
|
||||
ClientNotificationService.LogServerStoppedAfterDomainReload(correlationId);
|
||||
|
||||
return ServiceResult<string>.SuccessResult(correlationId);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// 4.4. Error notification
|
||||
ClientNotificationService.LogServerShutdownError(correlationId, ex, portToSave);
|
||||
DomainReloadDetectionService.RollbackDomainReloadStart(correlationId);
|
||||
|
||||
// Server stop failure is a critical error (causes port conflicts)
|
||||
throw new System.InvalidOperationException(
|
||||
$"Failed to properly shutdown MCP server before assembly reload. This may cause port conflicts on restart.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceResult<string>.SuccessResult(correlationId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute recovery processing after Domain Reload completion
|
||||
/// </summary>
|
||||
/// <returns>Processing result</returns>
|
||||
public async Task<ServiceResult<string>> ExecuteAfterDomainReloadAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. Generate tracking ID for related operations
|
||||
string correlationId = VibeLogger.GenerateCorrelationId();
|
||||
|
||||
// 2. Record Domain Reload completion
|
||||
DomainReloadDetectionService.CompleteDomainReload(correlationId);
|
||||
|
||||
// 3. Start timeout for reconnection UI display if needed
|
||||
if (DomainReloadDetectionService.ShouldShowReconnectingUI())
|
||||
{
|
||||
SessionRecoveryService.StartReconnectionUITimeoutAsync().Forget();
|
||||
}
|
||||
|
||||
// 4. Restore server state
|
||||
ValidationResult restoreResult = SessionRecoveryService.RestoreServerStateIfNeeded();
|
||||
if (!restoreResult.IsValid)
|
||||
{
|
||||
return ServiceResult<string>.FailureResult($"Server restoration failed: {restoreResult.ErrorMessage}");
|
||||
}
|
||||
|
||||
// 5. Process pending compile requests (currently disabled)
|
||||
ProcessPendingCompileRequests(correlationId);
|
||||
|
||||
// 6. Send tool change notification if server is running
|
||||
if (McpServerController.IsServerRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ClientNotificationService.SendToolNotificationAfterCompilationAsync();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
VibeLogger.LogWarning("tool_notification_failed", $"Failed to send tool notification: {ex.Message}", correlationId: correlationId);
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceResult<string>.SuccessResult(correlationId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process pending compile requests
|
||||
/// Note: Currently disabled by feature flag (to avoid main thread errors)
|
||||
/// </summary>
|
||||
/// <param name="correlationId">Correlation ID for tracking related operations</param>
|
||||
private void ProcessPendingCompileRequests(string correlationId)
|
||||
{
|
||||
// Feature flag control - currently disabled, can be enabled via editor settings in the future
|
||||
// TODO: Add McpEditorSettings.GetEnablePendingCompileProcessing() when needed
|
||||
bool enablePendingCompileProcessing = false;
|
||||
|
||||
if (enablePendingCompileProcessing)
|
||||
{
|
||||
// Planned to be enabled after main thread issue resolution
|
||||
// CompileSessionState.StartForcedRecompile();
|
||||
VibeLogger.LogInfo(
|
||||
"pending_compile_processing",
|
||||
"Processing pending compile requests",
|
||||
correlationId: correlationId
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
VibeLogger.LogInfo(
|
||||
"pending_compile_processing_disabled",
|
||||
"Pending compile request processing is disabled via feature flag",
|
||||
correlationId: correlationId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1be0ac44f3434a16a6bd693cf4eaf41
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,155 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// UseCase responsible for temporal cohesion of server initialization processing
|
||||
/// Processing sequence: 1. Configuration validation, 2. Port allocation, 3. Server startup, 4. State update
|
||||
/// Related classes: McpServerConfigurationService, PortAllocationService, McpServerStartupService, SecurityValidationService
|
||||
/// </summary>
|
||||
public class McpServerInitializationUseCase : AbstractUseCase<ServerInitializationSchema, ServerInitializationResponse>
|
||||
{
|
||||
private readonly McpServerConfigurationService _configService;
|
||||
private readonly SecurityValidationService _securityService;
|
||||
private readonly PortAllocationService _portService;
|
||||
private readonly McpServerStartupService _startupService;
|
||||
private readonly InitializationNotificationService _notificationService;
|
||||
|
||||
public McpServerInitializationUseCase()
|
||||
{
|
||||
_configService = new McpServerConfigurationService();
|
||||
_securityService = new SecurityValidationService();
|
||||
_portService = new PortAllocationService();
|
||||
_startupService = new McpServerStartupService();
|
||||
_notificationService = new InitializationNotificationService();
|
||||
}
|
||||
|
||||
public McpServerInitializationUseCase(
|
||||
McpServerConfigurationService configService,
|
||||
SecurityValidationService securityService,
|
||||
PortAllocationService portService,
|
||||
McpServerStartupService startupService,
|
||||
InitializationNotificationService notificationService)
|
||||
{
|
||||
_configService = configService ?? throw new System.ArgumentNullException(nameof(configService));
|
||||
_securityService = securityService ?? throw new System.ArgumentNullException(nameof(securityService));
|
||||
_portService = portService ?? throw new System.ArgumentNullException(nameof(portService));
|
||||
_startupService = startupService ?? throw new System.ArgumentNullException(nameof(startupService));
|
||||
_notificationService = notificationService ?? throw new System.ArgumentNullException(nameof(notificationService));
|
||||
}
|
||||
/// <summary>
|
||||
/// Execute server initialization processing
|
||||
/// </summary>
|
||||
/// <param name="parameters">Initialization parameters</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Initialization result</returns>
|
||||
public override Task<ServerInitializationResponse> ExecuteAsync(ServerInitializationSchema parameters, CancellationToken cancellationToken)
|
||||
{
|
||||
ServerInitializationResponse response = new ServerInitializationResponse();
|
||||
|
||||
try
|
||||
{
|
||||
// 1. Configuration validation - McpServerConfigurationService
|
||||
var portResult = _configService.ResolvePort(parameters.Port);
|
||||
if (!portResult.Success)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = portResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
int actualPort = portResult.Data;
|
||||
|
||||
var validationResult = _configService.ValidateConfiguration(actualPort);
|
||||
if (!validationResult.Success)
|
||||
{
|
||||
_notificationService.ShowInvalidPortDialog(actualPort);
|
||||
|
||||
response.Success = false;
|
||||
response.Message = validationResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
// 2. Security validation - SecurityValidationService
|
||||
var editorStateValidation = _securityService.ValidateEditorState();
|
||||
if (!editorStateValidation.IsValid)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = editorStateValidation.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
var portSecurityValidation = _securityService.ValidatePortSecurity(actualPort);
|
||||
if (!portSecurityValidation.IsValid)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = portSecurityValidation.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
// 3. Port allocation - PortAllocationService
|
||||
var availablePortResult = _portService.FindAvailablePort(actualPort);
|
||||
if (!availablePortResult.Success)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = availablePortResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
int availablePort = availablePortResult.Data;
|
||||
|
||||
// Handle port conflict
|
||||
if (availablePort != actualPort)
|
||||
{
|
||||
var conflictResult = _portService.HandlePortConflict(actualPort, availablePort);
|
||||
if (!conflictResult.Success || !conflictResult.Data)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = "Port conflict resolution cancelled by user";
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Server startup - McpServerStartupService
|
||||
var serverResult = _startupService.StartServer(
|
||||
availablePort,
|
||||
!parameters.PreserveStartupLockUntilExplicitRelease);
|
||||
if (!serverResult.Success)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = serverResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
McpBridgeServer serverInstance = serverResult.Data;
|
||||
|
||||
// 5. Session state update
|
||||
string projectRootPath = UnityMcpPathResolver.GetProjectRoot();
|
||||
var sessionUpdateResult =
|
||||
_startupService.UpdateSessionState(true, availablePort, projectRootPath);
|
||||
if (!sessionUpdateResult.Success)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = sessionUpdateResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
// Success response
|
||||
response.Success = true;
|
||||
response.ServerPort = availablePort;
|
||||
response.IsRunning = true;
|
||||
response.ServerInstance = serverInstance;
|
||||
response.Message = "Server initialization completed successfully";
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// Log the full exception for debugging
|
||||
UnityEngine.Debug.LogError($"Server initialization failed: {ex}");
|
||||
|
||||
response.Success = false;
|
||||
response.Message = "Server initialization failed. Please check the logs for details.";
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a7ed1e9e34fd422e840c465c156ca08
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,99 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// UseCase responsible for temporal cohesion of server shutdown processing
|
||||
/// Processing sequence: 1. Server stop, 2. Session state clear, 3. Resource disposal
|
||||
/// Related classes: McpServerStartupService, McpSessionManager
|
||||
/// </summary>
|
||||
public class McpServerShutdownUseCase : AbstractUseCase<ServerShutdownSchema, ServerShutdownResponse>
|
||||
{
|
||||
private readonly McpServerStartupService _startupService;
|
||||
|
||||
public McpServerShutdownUseCase(McpServerStartupService startupService)
|
||||
{
|
||||
_startupService = startupService ?? throw new System.ArgumentNullException(nameof(startupService));
|
||||
}
|
||||
/// <summary>
|
||||
/// Execute server shutdown processing
|
||||
/// </summary>
|
||||
/// <param name="parameters">Shutdown parameters</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Shutdown result</returns>
|
||||
public override Task<ServerShutdownResponse> ExecuteAsync(ServerShutdownSchema parameters, CancellationToken cancellationToken)
|
||||
{
|
||||
ServerShutdownResponse response = new ServerShutdownResponse();
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 1. Get current server instance
|
||||
McpBridgeServer currentServer = McpServerController.CurrentServer;
|
||||
if (currentServer == null)
|
||||
{
|
||||
response.Success = true;
|
||||
response.Message = "Server was not running";
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 2. Server stop processing - McpServerStartupService
|
||||
var stopResult = _startupService.StopServer(currentServer);
|
||||
if (!stopResult.Success)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = stopResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 3. Session state clear
|
||||
var sessionUpdateResult = _startupService.UpdateSessionState(false);
|
||||
if (!sessionUpdateResult.Success)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = sessionUpdateResult.ErrorMessage;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 4. Session clear with SessionManager
|
||||
try
|
||||
{
|
||||
McpEditorSettings.ClearServerSession();
|
||||
}
|
||||
catch (System.Exception sessionEx)
|
||||
{
|
||||
response.Success = false;
|
||||
response.Message = $"Failed to clear session: {sessionEx.Message}";
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
// Success response
|
||||
response.Success = true;
|
||||
response.Message = "Server shutdown completed successfully";
|
||||
}
|
||||
catch (System.OperationCanceledException)
|
||||
{
|
||||
// Propagate cancellation exceptions
|
||||
throw;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// Log the full exception for debugging
|
||||
UnityEngine.Debug.LogError($"Server shutdown failed: {ex}");
|
||||
|
||||
response.Success = false;
|
||||
response.Message = "Server shutdown failed. Please check the logs for details.";
|
||||
}
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 409f66a22ed544772b7811a24e53ff26
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user