Skip to content

Commit 9827775

Browse files
authored
Merge branch 'CoplayDev:main' into main
2 parents e63096f + 14b11ba commit 9827775

File tree

338 files changed

+7720
-28901
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

338 files changed

+7720
-28901
lines changed

.github/workflows/python-tests.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Python Tests
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
paths:
7+
- MCPForUnity/UnityMcpServer~/src/**
8+
- .github/workflows/python-tests.yml
9+
workflow_dispatch: {}
10+
11+
jobs:
12+
test:
13+
name: Run Python Tests
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v4
21+
with:
22+
version: "latest"
23+
24+
- name: Set up Python
25+
run: uv python install 3.10
26+
27+
- name: Install dependencies
28+
run: |
29+
cd MCPForUnity/UnityMcpServer~/src
30+
uv sync
31+
uv pip install -e ".[dev]"
32+
33+
- name: Run tests
34+
run: |
35+
cd MCPForUnity/UnityMcpServer~/src
36+
uv run pytest tests/ -v --tb=short
37+
38+
- name: Upload test results
39+
uses: actions/upload-artifact@v4
40+
if: always()
41+
with:
42+
name: pytest-results
43+
path: |
44+
MCPForUnity/UnityMcpServer~/src/.pytest_cache/
45+
MCPForUnity/UnityMcpServer~/src/tests/

.github/workflows/unity-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Unity Tests
33
on:
44
workflow_dispatch: {}
55
push:
6-
branches: [main]
6+
branches: ["**"]
77
paths:
88
- TestProjects/UnityMCPTests/**
99
- MCPForUnity/Editor/**

MCPForUnity/Editor/Dependencies/DependencyManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private static void GenerateRecommendations(DependencyCheckResult result, IPlatf
126126
{
127127
if (dep.Name == "Python")
128128
{
129-
result.RecommendedActions.Add($"Install Python 3.11+ from: {detector.GetPythonInstallUrl()}");
129+
result.RecommendedActions.Add($"Install Python 3.10+ from: {detector.GetPythonInstallUrl()}");
130130
}
131131
else if (dep.Name == "UV Package Manager")
132132
{

MCPForUnity/Editor/Dependencies/PlatformDetectors/LinuxPlatformDetector.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public override DependencyStatus DetectPython()
6262
}
6363
}
6464

65-
status.ErrorMessage = "Python not found. Please install Python 3.11 or later.";
65+
status.ErrorMessage = "Python not found. Please install Python 3.10 or later.";
6666
status.Details = "Checked common installation paths including system, snap, and user-local locations.";
6767
}
6868
catch (Exception ex)
@@ -144,10 +144,10 @@ private bool TryValidatePython(string pythonPath, out string version, out string
144144
version = output.Substring(7); // Remove "Python " prefix
145145
fullPath = pythonPath;
146146

147-
// Validate minimum version (Python 4+ or Python 3.11+)
147+
// Validate minimum version (Python 4+ or Python 3.10+)
148148
if (TryParseVersion(version, out var major, out var minor))
149149
{
150-
return major > 3 || (major >= 3 && minor >= 11);
150+
return major > 3 || (major >= 3 && minor >= 10);
151151
}
152152
}
153153
}

MCPForUnity/Editor/Dependencies/PlatformDetectors/MacOSPlatformDetector.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ public override DependencyStatus DetectPython()
3333
"/usr/bin/python3",
3434
"/usr/local/bin/python3",
3535
"/opt/homebrew/bin/python3",
36+
"/Library/Frameworks/Python.framework/Versions/3.14/bin/python3",
3637
"/Library/Frameworks/Python.framework/Versions/3.13/bin/python3",
3738
"/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
38-
"/Library/Frameworks/Python.framework/Versions/3.11/bin/python3"
39+
"/Library/Frameworks/Python.framework/Versions/3.11/bin/python3",
40+
"/Library/Frameworks/Python.framework/Versions/3.10/bin/python3"
3941
};
4042

4143
foreach (var candidate in candidates)
@@ -64,7 +66,7 @@ public override DependencyStatus DetectPython()
6466
}
6567
}
6668

67-
status.ErrorMessage = "Python not found. Please install Python 3.11 or later.";
69+
status.ErrorMessage = "Python not found. Please install Python 3.10 or later.";
6870
status.Details = "Checked common installation paths including Homebrew, Framework, and system locations.";
6971
}
7072
catch (Exception ex)
@@ -143,10 +145,10 @@ private bool TryValidatePython(string pythonPath, out string version, out string
143145
version = output.Substring(7); // Remove "Python " prefix
144146
fullPath = pythonPath;
145147

146-
// Validate minimum version (Python 4+ or Python 3.11+)
148+
// Validate minimum version (Python 4+ or Python 3.10+)
147149
if (TryParseVersion(version, out var major, out var minor))
148150
{
149-
return major > 3 || (major >= 3 && minor >= 11);
151+
return major > 3 || (major >= 3 && minor >= 10);
150152
}
151153
}
152154
}

MCPForUnity/Editor/Dependencies/PlatformDetectors/WindowsPlatformDetector.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,26 @@ public override DependencyStatus DetectPython()
3030
{
3131
"python.exe",
3232
"python3.exe",
33+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
34+
"Programs", "Python", "Python314", "python.exe"),
3335
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
3436
"Programs", "Python", "Python313", "python.exe"),
3537
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
3638
"Programs", "Python", "Python312", "python.exe"),
3739
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
3840
"Programs", "Python", "Python311", "python.exe"),
41+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
42+
"Programs", "Python", "Python310", "python.exe"),
43+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
44+
"Python314", "python.exe"),
3945
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
4046
"Python313", "python.exe"),
4147
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
42-
"Python312", "python.exe")
48+
"Python312", "python.exe"),
49+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
50+
"Python311", "python.exe"),
51+
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
52+
"Python310", "python.exe")
4353
};
4454

4555
foreach (var candidate in candidates)
@@ -68,7 +78,7 @@ public override DependencyStatus DetectPython()
6878
}
6979
}
7080

71-
status.ErrorMessage = "Python not found. Please install Python 3.11 or later.";
81+
status.ErrorMessage = "Python not found. Please install Python 3.10 or later.";
7282
status.Details = "Checked common installation paths and PATH environment variable.";
7383
}
7484
catch (Exception ex)
@@ -94,7 +104,7 @@ public override string GetInstallationRecommendations()
94104
return @"Windows Installation Recommendations:
95105
96106
1. Python: Install from Microsoft Store or python.org
97-
- Microsoft Store: Search for 'Python 3.12' or 'Python 3.13'
107+
- Microsoft Store: Search for 'Python 3.10' or higher
98108
- Direct download: https://python.org/downloads/windows/
99109
100110
2. UV Package Manager: Install via PowerShell
@@ -132,10 +142,10 @@ private bool TryValidatePython(string pythonPath, out string version, out string
132142
version = output.Substring(7); // Remove "Python " prefix
133143
fullPath = pythonPath;
134144

135-
// Validate minimum version (Python 4+ or Python 3.11+)
145+
// Validate minimum version (Python 4+ or Python 3.10+)
136146
if (TryParseVersion(version, out var major, out var minor))
137147
{
138-
return major > 3 || (major >= 3 && minor >= 11);
148+
return major > 3 || (major >= 3 && minor >= 10);
139149
}
140150
}
141151
}

MCPForUnity/Editor/Helpers/PortManager.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,17 @@ public static int GetPortWithFallback()
6060
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Stored port {storedConfig.unity_port} became available after short wait");
6161
return storedConfig.unity_port;
6262
}
63-
// Prefer sticking to the same port; let the caller handle bind retries/fallbacks
64-
return storedConfig.unity_port;
63+
// Port is still busy after waiting - find a new available port instead
64+
if (IsDebugEnabled()) Debug.Log($"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: Stored port {storedConfig.unity_port} is occupied by another instance, finding alternative...");
65+
int newPort = FindAvailablePort();
66+
SavePort(newPort);
67+
return newPort;
6568
}
6669

6770
// If no valid stored port, find a new one and save it
68-
int newPort = FindAvailablePort();
69-
SavePort(newPort);
70-
return newPort;
71+
int foundPort = FindAvailablePort();
72+
SavePort(foundPort);
73+
return foundPort;
7174
}
7275

7376
/// <summary>

MCPForUnity/Editor/Helpers/ServerInstaller.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,10 +733,12 @@ internal static string FindUvPath()
733733
Path.Combine(programFiles, "WinGet", "Links", "uv.exe"),
734734

735735
// Common per-user installs
736+
Path.Combine(localAppData, @"Programs\Python\Python314\Scripts\uv.exe"),
736737
Path.Combine(localAppData, @"Programs\Python\Python313\Scripts\uv.exe"),
737738
Path.Combine(localAppData, @"Programs\Python\Python312\Scripts\uv.exe"),
738739
Path.Combine(localAppData, @"Programs\Python\Python311\Scripts\uv.exe"),
739740
Path.Combine(localAppData, @"Programs\Python\Python310\Scripts\uv.exe"),
741+
Path.Combine(appData, @"Python\Python314\Scripts\uv.exe"),
740742
Path.Combine(appData, @"Python\Python313\Scripts\uv.exe"),
741743
Path.Combine(appData, @"Python\Python312\Scripts\uv.exe"),
742744
Path.Combine(appData, @"Python\Python311\Scripts\uv.exe"),
@@ -761,8 +763,11 @@ internal static string FindUvPath()
761763
Path.Combine(home, ".local", "bin", "uv"),
762764
"/opt/homebrew/opt/uv/bin/uv",
763765
// Framework Python installs
766+
"/Library/Frameworks/Python.framework/Versions/3.14/bin/uv",
764767
"/Library/Frameworks/Python.framework/Versions/3.13/bin/uv",
765768
"/Library/Frameworks/Python.framework/Versions/3.12/bin/uv",
769+
"/Library/Frameworks/Python.framework/Versions/3.11/bin/uv",
770+
"/Library/Frameworks/Python.framework/Versions/3.10/bin/uv",
766771
// Fallback to PATH resolution by name
767772
"uv"
768773
};

MCPForUnity/Editor/MCPForUnityBridge.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,24 @@ public static void Start()
362362
}
363363
catch (SocketException se) when (se.SocketErrorCode == SocketError.AddressAlreadyInUse && attempt >= maxImmediateRetries)
364364
{
365+
// Port is occupied by another instance, get a new available port
366+
int oldPort = currentUnityPort;
365367
currentUnityPort = PortManager.GetPortWithFallback();
368+
369+
// GetPortWithFallback() may return the same port if it became available during wait
370+
// or a different port if switching to an alternative
371+
if (IsDebugEnabled())
372+
{
373+
if (currentUnityPort == oldPort)
374+
{
375+
McpLog.Info($"Port {oldPort} became available, proceeding");
376+
}
377+
else
378+
{
379+
McpLog.Info($"Port {oldPort} occupied, switching to port {currentUnityPort}");
380+
}
381+
}
382+
366383
listener = new TcpListener(IPAddress.Loopback, currentUnityPort);
367384
listener.Server.SetSocketOption(
368385
SocketOptionLevel.Socket,
@@ -474,6 +491,22 @@ public static void Stop()
474491
try { AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload; } catch { }
475492
try { EditorApplication.quitting -= Stop; } catch { }
476493

494+
// Clean up status file when Unity stops
495+
try
496+
{
497+
string statusDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".unity-mcp");
498+
string statusFile = Path.Combine(statusDir, $"unity-mcp-status-{ComputeProjectHash(Application.dataPath)}.json");
499+
if (File.Exists(statusFile))
500+
{
501+
File.Delete(statusFile);
502+
if (IsDebugEnabled()) McpLog.Info($"Deleted status file: {statusFile}");
503+
}
504+
}
505+
catch (Exception ex)
506+
{
507+
if (IsDebugEnabled()) McpLog.Warn($"Failed to delete status file: {ex.Message}");
508+
}
509+
477510
if (IsDebugEnabled()) McpLog.Info("MCPForUnityBridge stopped.");
478511
}
479512

@@ -1184,13 +1217,38 @@ private static void WriteHeartbeat(bool reloading, string reason = null)
11841217
}
11851218
Directory.CreateDirectory(dir);
11861219
string filePath = Path.Combine(dir, $"unity-mcp-status-{ComputeProjectHash(Application.dataPath)}.json");
1220+
1221+
// Extract project name from path
1222+
string projectName = "Unknown";
1223+
try
1224+
{
1225+
string projectPath = Application.dataPath;
1226+
if (!string.IsNullOrEmpty(projectPath))
1227+
{
1228+
// Remove trailing /Assets or \Assets
1229+
projectPath = projectPath.TrimEnd('/', '\\');
1230+
if (projectPath.EndsWith("Assets", StringComparison.OrdinalIgnoreCase))
1231+
{
1232+
projectPath = projectPath.Substring(0, projectPath.Length - 6).TrimEnd('/', '\\');
1233+
}
1234+
projectName = Path.GetFileName(projectPath);
1235+
if (string.IsNullOrEmpty(projectName))
1236+
{
1237+
projectName = "Unknown";
1238+
}
1239+
}
1240+
}
1241+
catch { }
1242+
11871243
var payload = new
11881244
{
11891245
unity_port = currentUnityPort,
11901246
reloading,
11911247
reason = reason ?? (reloading ? "reloading" : "ready"),
11921248
seq = heartbeatSeq,
11931249
project_path = Application.dataPath,
1250+
project_name = projectName,
1251+
unity_version = Application.unityVersion,
11941252
last_heartbeat = DateTime.UtcNow.ToString("O")
11951253
};
11961254
File.WriteAllText(filePath, JsonConvert.SerializeObject(payload), new System.Text.UTF8Encoding(false));

UnityMcpBridge/Editor.meta renamed to MCPForUnity/Editor/Resources/Editor.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)