Skip to content

Commit 61521a2

Browse files
Merge pull request #1008 from CoplayDev/release/v9.6.4
chore: bump version to 9.6.4
2 parents 3dd5ac7 + a98485c commit 61521a2

88 files changed

Lines changed: 9453 additions & 142 deletions

File tree

Some content is hidden

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

.claude/skills/unity-mcp-skill/references/tools-reference.md

Lines changed: 204 additions & 39 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ from services.registry import mcp_for_unity_tool
7070

7171
@mcp_for_unity_tool(
7272
description="Does something in Unity.",
73-
group="core", # core (default), vfx, animation, ui, scripting_ext, testing, probuilder
73+
group="core", # core (default), vfx, animation, ui, scripting_ext, testing, probuilder, profiling, docs
7474
)
7575
async def manage_something(
7676
ctx: Context,

MCPForUnity/Editor/Helpers/AssetPathUtility.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public static string GetMcpPackageRootPath()
127127

128128
// Script is at: {packageRoot}/Editor/Helpers/AssetPathUtility.cs
129129
// Extract {packageRoot}
130-
int editorIndex = scriptPath.IndexOf("/Editor/", StringComparison.Ordinal);
130+
int editorIndex = scriptPath.LastIndexOf("/Editor/", StringComparison.Ordinal);
131131

132132
if (editorIndex >= 0)
133133
{

MCPForUnity/Editor/Helpers/McpLogRecord.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ namespace MCPForUnity.Editor.Helpers
1010
{
1111
internal static class McpLogRecord
1212
{
13-
private static readonly string LogPath = Path.Combine(Application.dataPath, "mcp.log");
14-
private static readonly string ErrorLogPath = Path.Combine(Application.dataPath, "mcpError.log");
13+
private static readonly string LogDir = Path.Combine(Application.dataPath, "UnityMCP", "Log");
14+
private static readonly string LogPath = Path.Combine(LogDir, "mcp.log");
15+
private static readonly string ErrorLogPath = Path.Combine(LogDir, "mcpError.log");
1516
private const long MaxLogSizeBytes = 1024 * 1024; // 1 MB
1617
private static bool _sessionStarted;
1718
private static readonly object _logLock = new();
@@ -79,6 +80,7 @@ internal static void Log(string commandType, JObject parameters, string type, st
7980

8081
private static void RotateAndAppend(string path, string line)
8182
{
83+
Directory.CreateDirectory(LogDir);
8284
RotateIfNeeded(path);
8385
File.AppendAllText(path, line + Environment.NewLine);
8486
}

MCPForUnity/Editor/Services/IPackageUpdateService.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@ public interface IPackageUpdateService
1212
/// <returns>Update check result containing availability and latest version info</returns>
1313
UpdateCheckResult CheckForUpdate(string currentVersion);
1414

15+
/// <summary>
16+
/// Returns a cached update result if one exists for today, or null if a network fetch is needed.
17+
/// Main-thread only (reads EditorPrefs).
18+
/// </summary>
19+
UpdateCheckResult TryGetCachedResult(string currentVersion);
20+
21+
/// <summary>
22+
/// Performs only the network fetch and version comparison (no EditorPrefs access).
23+
/// Safe to call from a background thread.
24+
/// </summary>
25+
UpdateCheckResult FetchAndCompare(string currentVersion);
26+
27+
/// <summary>
28+
/// Performs only the network fetch and version comparison using pre-computed installation info.
29+
/// Use this overload when calling from a background thread to avoid main-thread-only API calls.
30+
/// </summary>
31+
UpdateCheckResult FetchAndCompare(string currentVersion, bool isGitInstallation, string gitBranch);
32+
33+
/// <summary>
34+
/// Caches a successful fetch result in EditorPrefs. Must be called from the main thread.
35+
/// </summary>
36+
void CacheFetchResult(string currentVersion, string fetchedVersion);
37+
1538
/// <summary>
1639
/// Compares two version strings to determine if the first is newer than the second
1740
/// </summary>
@@ -26,6 +49,12 @@ public interface IPackageUpdateService
2649
/// <returns>True if installed via Git, false if Asset Store or unknown</returns>
2750
bool IsGitInstallation();
2851

52+
/// <summary>
53+
/// Determines the Git branch to check for updates (e.g. "main" or "beta").
54+
/// Must be called from the main thread (uses Unity PackageManager APIs).
55+
/// </summary>
56+
string GetGitUpdateBranch(string currentVersion);
57+
2958
/// <summary>
3059
/// Clears the cached update check data, forcing a fresh check on next request
3160
/// </summary>

MCPForUnity/Editor/Services/PackageUpdateService.cs

Lines changed: 128 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace MCPForUnity.Editor.Services
1414
/// </summary>
1515
public class PackageUpdateService : IPackageUpdateService
1616
{
17+
private const int DefaultRequestTimeoutMs = 3000;
1718
private const string LastCheckDateKey = EditorPrefKeys.LastUpdateCheck;
1819
private const string CachedVersionKey = EditorPrefKeys.LatestKnownVersion;
1920
private const string LastBetaCheckDateKey = EditorPrefKeys.LastUpdateCheck + ".beta";
@@ -81,6 +82,93 @@ public UpdateCheckResult CheckForUpdate(string currentVersion)
8182
};
8283
}
8384

85+
/// <inheritdoc/>
86+
public UpdateCheckResult TryGetCachedResult(string currentVersion)
87+
{
88+
bool isGitInstallation = IsGitInstallation();
89+
string gitBranch = isGitInstallation ? GetGitUpdateBranch(currentVersion) : "main";
90+
bool useBetaChannel = isGitInstallation && string.Equals(gitBranch, "beta", StringComparison.OrdinalIgnoreCase);
91+
92+
string lastCheckKey = isGitInstallation
93+
? (useBetaChannel ? LastBetaCheckDateKey : LastCheckDateKey)
94+
: LastAssetStoreCheckDateKey;
95+
string cachedVersionKey = isGitInstallation
96+
? (useBetaChannel ? CachedBetaVersionKey : CachedVersionKey)
97+
: CachedAssetStoreVersionKey;
98+
99+
string lastCheckDate = EditorPrefs.GetString(lastCheckKey, "");
100+
string cachedLatestVersion = EditorPrefs.GetString(cachedVersionKey, "");
101+
102+
if (lastCheckDate == DateTime.Now.ToString("yyyy-MM-dd") && !string.IsNullOrEmpty(cachedLatestVersion))
103+
{
104+
return new UpdateCheckResult
105+
{
106+
CheckSucceeded = true,
107+
LatestVersion = cachedLatestVersion,
108+
UpdateAvailable = IsNewerVersion(cachedLatestVersion, currentVersion),
109+
Message = "Using cached version check"
110+
};
111+
}
112+
113+
return null;
114+
}
115+
116+
/// <inheritdoc/>
117+
public UpdateCheckResult FetchAndCompare(string currentVersion)
118+
{
119+
bool isGitInstallation = IsGitInstallation();
120+
string gitBranch = isGitInstallation ? GetGitUpdateBranch(currentVersion) : "main";
121+
return FetchAndCompare(currentVersion, isGitInstallation, gitBranch);
122+
}
123+
124+
/// <inheritdoc/>
125+
public UpdateCheckResult FetchAndCompare(string currentVersion, bool isGitInstallation, string gitBranch)
126+
{
127+
string latestVersion = isGitInstallation
128+
? FetchLatestVersionFromGitHub(gitBranch)
129+
: FetchLatestVersionFromAssetStoreJson();
130+
131+
if (!string.IsNullOrEmpty(latestVersion))
132+
{
133+
return new UpdateCheckResult
134+
{
135+
CheckSucceeded = true,
136+
LatestVersion = latestVersion,
137+
UpdateAvailable = IsNewerVersion(latestVersion, currentVersion),
138+
Message = "Successfully checked for updates"
139+
};
140+
}
141+
142+
return new UpdateCheckResult
143+
{
144+
CheckSucceeded = false,
145+
UpdateAvailable = false,
146+
Message = isGitInstallation
147+
? "Failed to check for updates (network issue or offline)"
148+
: "Failed to check for Asset Store updates (network issue or offline)"
149+
};
150+
}
151+
152+
/// <inheritdoc/>
153+
public void CacheFetchResult(string currentVersion, string fetchedVersion)
154+
{
155+
if (string.IsNullOrEmpty(fetchedVersion)) return;
156+
157+
bool isGitInstallation = IsGitInstallation();
158+
string gitBranch = isGitInstallation ? GetGitUpdateBranch(currentVersion) : "main";
159+
bool useBetaChannel = isGitInstallation && string.Equals(gitBranch, "beta", StringComparison.OrdinalIgnoreCase);
160+
161+
string lastCheckKey = isGitInstallation
162+
? (useBetaChannel ? LastBetaCheckDateKey : LastCheckDateKey)
163+
: LastAssetStoreCheckDateKey;
164+
string cachedVersionKey = isGitInstallation
165+
? (useBetaChannel ? CachedBetaVersionKey : CachedVersionKey)
166+
: CachedAssetStoreVersionKey;
167+
168+
EditorPrefs.SetString(lastCheckKey, DateTime.Now.ToString("yyyy-MM-dd"));
169+
EditorPrefs.SetString(cachedVersionKey, fetchedVersion);
170+
}
171+
84172
/// <inheritdoc/>
85173
public bool IsNewerVersion(string version1, string version2)
86174
{
@@ -196,7 +284,8 @@ private static bool IsPreReleaseVersion(string version)
196284
return version.IndexOf('-', StringComparison.Ordinal) >= 0;
197285
}
198286

199-
private static string GetGitUpdateBranch(string currentVersion)
287+
/// <inheritdoc/>
288+
public string GetGitUpdateBranch(string currentVersion)
200289
{
201290
try
202291
{
@@ -265,7 +354,7 @@ protected virtual string FetchLatestVersionFromGitHub(string branch)
265354
// - More reliable - doesn't require releases to be published
266355
// - Direct source of truth from the main branch
267356

268-
using (var client = new WebClient())
357+
using (var client = CreateWebClient())
269358
{
270359
client.Headers.Add("User-Agent", "Unity-MCPForUnity-UpdateChecker");
271360
string packageJsonUrl = string.Equals(branch, "beta", StringComparison.OrdinalIgnoreCase)
@@ -304,7 +393,7 @@ protected virtual string FetchLatestVersionFromAssetStoreJson()
304393
{
305394
try
306395
{
307-
using (var client = new WebClient())
396+
using (var client = CreateWebClient())
308397
{
309398
client.Headers.Add("User-Agent", "Unity-MCPForUnity-AssetStoreUpdateChecker");
310399
string jsonContent = client.DownloadString(AssetStoreVersionUrl);
@@ -322,5 +411,41 @@ protected virtual string FetchLatestVersionFromAssetStoreJson()
322411
return null;
323412
}
324413
}
414+
415+
protected virtual WebClient CreateWebClient()
416+
{
417+
return new TimeoutWebClient(GetRequestTimeoutMs());
418+
}
419+
420+
protected virtual int GetRequestTimeoutMs()
421+
{
422+
return DefaultRequestTimeoutMs;
423+
}
424+
425+
private sealed class TimeoutWebClient : WebClient
426+
{
427+
private readonly int _timeoutMs;
428+
429+
public TimeoutWebClient(int timeoutMs)
430+
{
431+
_timeoutMs = timeoutMs;
432+
}
433+
434+
protected override WebRequest GetWebRequest(Uri address)
435+
{
436+
var request = base.GetWebRequest(address);
437+
if (request != null)
438+
{
439+
request.Timeout = _timeoutMs;
440+
441+
if (request is HttpWebRequest httpRequest)
442+
{
443+
httpRequest.ReadWriteTimeout = _timeoutMs;
444+
}
445+
}
446+
447+
return request;
448+
}
449+
}
325450
}
326451
}

MCPForUnity/Editor/Services/Server/TerminalLauncher.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,13 @@ public System.Diagnostics.ProcessStartInfo CreateTerminalProcessStartInfo(string
7070
CreateNoWindow = true
7171
};
7272
#else
73-
// Linux: Try common terminal emulators
74-
// We use bash -c to execute the command, so we must properly quote/escape for bash
75-
// Escape single quotes for the inner bash string
76-
string escapedCommandLinux = command.Replace("'", "'\\''");
77-
// Wrap the command in single quotes for bash -c
78-
string script = $"'{escapedCommandLinux}; exec bash'";
79-
// Escape double quotes for the outer Process argument string
80-
string escapedScriptForArg = script.Replace("\"", "\\\"");
73+
// Linux: Try common terminal emulators.
74+
// ProcessStartInfo passes the argument string directly to the terminal, so we only
75+
// need to escape for the double-quoted bash -c payload — no inner single quotes.
76+
string script = $"{command}; exec bash";
77+
string escapedScriptForArg = script
78+
.Replace("\\", "\\\\")
79+
.Replace("\"", "\\\"");
8180
string bashCmdArgs = $"bash -c \"{escapedScriptForArg}\"";
8281

8382
string[] terminals = { "gnome-terminal", "xterm", "konsole", "xfce4-terminal" };

MCPForUnity/Editor/Tools/Build/BuildRunner.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private static void RunBuildCore(BuildJob job, Func<BuildReport> buildFunc)
111111
else
112112
{
113113
job.State = BuildJobState.Failed;
114-
#if UNITY_2022_3_OR_NEWER
114+
#if UNITY_2023_1_OR_NEWER
115115
job.ErrorMessage = report.SummarizeErrors();
116116
#else
117117
job.ErrorMessage = $"Build failed with result: {summary.result}";

MCPForUnity/Editor/Tools/Graphics/RenderingStatsOps.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Unity.Profiling;
99
using Unity.Profiling.LowLevel.Unsafe;
1010
using UnityEngine.Profiling;
11+
using UProfiler = UnityEngine.Profiling.Profiler;
1112

1213
namespace MCPForUnity.Editor.Tools.Graphics
1314
{
@@ -126,12 +127,12 @@ internal static object GetMemory(JObject @params)
126127
{
127128
var data = new Dictionary<string, object>
128129
{
129-
["totalAllocatedMB"] = Math.Round(Profiler.GetTotalAllocatedMemoryLong() / (1024.0 * 1024.0), 2),
130-
["totalReservedMB"] = Math.Round(Profiler.GetTotalReservedMemoryLong() / (1024.0 * 1024.0), 2),
131-
["totalUnusedReservedMB"] = Math.Round(Profiler.GetTotalUnusedReservedMemoryLong() / (1024.0 * 1024.0), 2),
132-
["monoUsedMB"] = Math.Round(Profiler.GetMonoUsedSizeLong() / (1024.0 * 1024.0), 2),
133-
["monoHeapMB"] = Math.Round(Profiler.GetMonoHeapSizeLong() / (1024.0 * 1024.0), 2),
134-
["graphicsDriverMB"] = Math.Round(Profiler.GetAllocatedMemoryForGraphicsDriver() / (1024.0 * 1024.0), 2),
130+
["totalAllocatedMB"] = Math.Round(UProfiler.GetTotalAllocatedMemoryLong() / (1024.0 * 1024.0), 2),
131+
["totalReservedMB"] = Math.Round(UProfiler.GetTotalReservedMemoryLong() / (1024.0 * 1024.0), 2),
132+
["totalUnusedReservedMB"] = Math.Round(UProfiler.GetTotalUnusedReservedMemoryLong() / (1024.0 * 1024.0), 2),
133+
["monoUsedMB"] = Math.Round(UProfiler.GetMonoUsedSizeLong() / (1024.0 * 1024.0), 2),
134+
["monoHeapMB"] = Math.Round(UProfiler.GetMonoHeapSizeLong() / (1024.0 * 1024.0), 2),
135+
["graphicsDriverMB"] = Math.Round(UProfiler.GetAllocatedMemoryForGraphicsDriver() / (1024.0 * 1024.0), 2),
135136
};
136137

137138
return new

0 commit comments

Comments
 (0)