chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖

This commit is contained in:
Liubing\LB
2026-09-28 18:14:59 +08:00
parent 0eb43b2b40
commit c4ae02def9
952 changed files with 70137 additions and 11 deletions

View File

@@ -0,0 +1,202 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace io.github.hatayama.uLoopMCP
{
/// <summary>
/// Standalone window for server status monitoring and lifecycle control.
/// Accessible from Window > Unity CLI Loop > Server.
/// </summary>
public class ServerEditorWindow : EditorWindow
{
private const string UXML_RELATIVE_PATH = "Editor/UI/Server/ServerEditorWindow.uxml";
private const string USS_RELATIVE_PATH = "Editor/UI/Server/ServerEditorWindow.uss";
private ServerStatusSection _serverStatusSection;
private ServerControlsSection _serverControlsSection;
private volatile bool _needsRepaint;
[MenuItem("Window/Unity CLI Loop/Server", priority = 2)]
public static void ShowWindow()
{
ServerEditorWindow window = GetWindow<ServerEditorWindow>("Server");
window.Show();
}
private void OnEnable()
{
EditorApplication.update += OnEditorUpdate;
McpBridgeServer.OnServerStarted += OnServerStateChanged;
McpBridgeServer.OnServerStopping += OnServerStateChanged;
}
private void OnDisable()
{
EditorApplication.update -= OnEditorUpdate;
McpBridgeServer.OnServerStarted -= OnServerStateChanged;
McpBridgeServer.OnServerStopping -= OnServerStateChanged;
}
private void CreateGUI()
{
LoadLayout();
InitializeSections();
RefreshUI();
}
private void OnFocus()
{
RefreshUI();
}
private void LoadLayout()
{
string uxmlPath = $"{McpConstants.PackageAssetPath}/{UXML_RELATIVE_PATH}";
VisualTreeAsset visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxmlPath);
Debug.Assert(visualTree != null, $"UXML not found at {uxmlPath}");
visualTree.CloneTree(rootVisualElement);
string ussPath = $"{McpConstants.PackageAssetPath}/{USS_RELATIVE_PATH}";
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(ussPath);
Debug.Assert(styleSheet != null, $"USS not found at {ussPath}");
rootVisualElement.styleSheets.Add(styleSheet);
}
private void InitializeSections()
{
_serverStatusSection = new ServerStatusSection(rootVisualElement);
_serverControlsSection = new ServerControlsSection(rootVisualElement);
_serverControlsSection.OnToggleServer += ToggleServer;
_serverControlsSection.OnPortChanged += OnPortChanged;
}
private void RefreshUI()
{
if (_serverStatusSection == null)
{
return;
}
SyncPortIfRunning();
ServerStatusData statusData = CreateServerStatusData();
_serverStatusSection.Update(statusData);
ServerControlsData controlsData = CreateServerControlsData();
_serverControlsSection.Update(controlsData);
}
private void SyncPortIfRunning()
{
if (!McpServerController.IsServerRunning)
{
return;
}
int actualPort = McpServerController.ServerPort;
int savedPort = McpEditorSettings.GetCustomPort();
if (savedPort != actualPort)
{
McpEditorSettings.SetCustomPort(actualPort);
}
}
private ServerStatusData CreateServerStatusData()
{
(bool isRunning, int port, bool _) = McpServerController.GetServerStatus();
string status = isRunning ? "Running" : "Stopped";
Color statusColor = isRunning ? Color.green : Color.red;
return new ServerStatusData(isRunning, port, status, statusColor);
}
private ServerControlsData CreateServerControlsData()
{
bool isRunning = McpServerController.IsServerRunning;
int customPort = McpEditorSettings.GetCustomPort();
bool hasPortWarning = false;
string portWarningMessage = null;
if (!isRunning)
{
if (!McpPortValidator.ValidatePort(customPort))
{
hasPortWarning = true;
portWarningMessage = $"Port {customPort} is invalid. Port must be 1024 or higher and not a reserved system port.";
}
else if (NetworkUtility.IsPortInUse(customPort))
{
hasPortWarning = true;
portWarningMessage = $"Port {customPort} is already in use. Please choose a different port or stop the other process using this port.";
}
}
return new ServerControlsData(customPort, isRunning, !isRunning, hasPortWarning, portWarningMessage);
}
private void ToggleServer()
{
if (McpServerController.IsServerRunning)
{
McpServerController.StopServer();
}
else
{
StartServer();
}
RefreshUI();
}
private void StartServer()
{
int port = McpEditorSettings.GetCustomPort();
if (!McpPortValidator.ValidatePort(port))
{
EditorUtility.DisplayDialog("Port Error",
$"Port must be between {McpServerConfig.MIN_PORT_NUMBER} and {McpServerConfig.MAX_PORT_NUMBER}",
"OK");
return;
}
// Config auto-update is best-effort; do not block server startup on unrelated config-file problems
try
{
McpConfigAutoUpdater.UpdateAllConfiguredEditors(port);
}
catch (System.Exception ex)
{
Debug.LogWarning($"[uLoopMCP] Failed to auto-update configurations: {ex.Message}");
}
McpServerController.StartServer(port);
}
private void OnPortChanged(int port)
{
McpEditorSettings.SetCustomPort(port);
RefreshUI();
}
private void OnServerStateChanged()
{
_needsRepaint = true;
}
private void OnEditorUpdate()
{
if (!_needsRepaint)
{
return;
}
_needsRepaint = false;
RefreshUI();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 48d305aead26c4120aca9cef9d4fcce7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,157 @@
/* ===========================================
ServerEditorWindow Stylesheet
Extracted from McpEditorWindow.uss for the
standalone Server window.
=========================================== */
:root {
--mcp-color-success: #4caf50;
--mcp-color-danger: #ef5350;
}
/* ===========================================
Block: Section
=========================================== */
.mcp-section {
margin-bottom: 8px;
}
.mcp-section--box {
background-color: var(--unity-colors-helpbox-background);
border-color: var(--unity-colors-helpbox-border);
border-width: 1px;
border-radius: 3px;
padding: 8px;
overflow: hidden;
}
/* ===========================================
Block: Server Status
=========================================== */
.mcp-server-status {
flex-direction: row;
align-items: center;
margin-bottom: 8px;
margin-left: 7px;
}
.mcp-server-status__indicator {
width: 8px;
height: 8px;
border-radius: 4px;
margin-right: 6px;
flex-shrink: 0;
}
.mcp-server-status__indicator--running {
background-color: var(--mcp-color-success);
}
.mcp-server-status__indicator--stopped {
background-color: var(--mcp-color-danger);
}
.mcp-server-status__text {
-unity-font-style: bold;
}
.mcp-server-status__text--running {
color: var(--mcp-color-success);
border-color: var(--mcp-color-success);
border-width: 1px;
border-radius: 3px;
padding: 2px 6px;
}
.mcp-server-status__text--stopped {
color: var(--mcp-color-danger);
border-color: var(--mcp-color-danger);
border-width: 1px;
border-radius: 3px;
padding: 2px 6px;
}
.mcp-server-status__spacer {
flex-grow: 1;
}
.mcp-server-status__port-label {
margin-right: 4px;
}
.mcp-server-status__port-field {
width: 60px;
-unity-text-align: middle-right;
}
/* ===========================================
Block: Server Controls
=========================================== */
.mcp-server-controls {
margin-top: 4px;
padding-left: 8px;
}
.mcp-server-controls .mcp-button {
flex-grow: 1;
}
/* ===========================================
Block: Warning Container
=========================================== */
.mcp-warning-container {
display: none;
margin-bottom: 8px;
}
.mcp-warning-container--visible {
display: flex;
}
.mcp-warning-label {
background-color: #5c4a00;
border-color: #7a6400;
color: #ffcc00;
border-width: 1px;
border-radius: 3px;
padding: 8px;
white-space: normal;
}
/* ===========================================
Block: Button
=========================================== */
.mcp-button {
margin-top: 4px;
margin-bottom: 4px;
}
.mcp-button--compact {
height: 22px;
padding-left: 12px;
padding-right: 12px;
font-size: 11px;
margin-top: 4px;
margin-bottom: 4px;
margin-left: 0;
}
.mcp-button--start {
background-color: #2e7d32;
border-color: var(--mcp-color-success);
color: #ffffff;
}
.mcp-button--start:hover {
background-color: #388e3c;
}
.mcp-button--stop {
background-color: #4a4a4a;
border-color: #666666;
color: #ff6b6b;
}
.mcp-button--stop:hover {
background-color: #555555;
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eda86d2b6ce0b4f4298f640ca17c9539
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements">
<ui:VisualElement name="server-section" class="mcp-section mcp-section--box">
<ui:VisualElement class="mcp-server-status">
<ui:VisualElement name="server-status-indicator" class="mcp-server-status__indicator" />
<ui:Label name="status-value" text="Stopped" class="mcp-server-status__text mcp-server-status__text--stopped" />
<ui:VisualElement class="mcp-server-status__spacer" />
<ui:Label text="Port:" class="mcp-server-status__port-label" />
<uie:IntegerField name="port-field" value="8080" class="mcp-server-status__port-field" />
</ui:VisualElement>
<ui:VisualElement name="port-warning-container" class="mcp-warning-container">
<ui:Label name="port-warning-label" class="mcp-warning-label" />
</ui:VisualElement>
<ui:VisualElement class="mcp-server-controls">
<ui:Button name="toggle-server-button" text="Start Server" class="mcp-button mcp-button--compact mcp-button--start" />
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 92fdf4f625a914ca186abb4fb130a4c7
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}