Skip to content

Commit 56e1e14

Browse files
authored
Merge pull request #954 from jiajunfeng/fix/editor-window-initial-open-lag
perf: avoid blocking update checks in editor window
2 parents 0763bf5 + 2ab5f79 commit 56e1e14

3 files changed

Lines changed: 188 additions & 13 deletions

File tree

MCPForUnity/Editor/Services/IPackageUpdateService.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,23 @@ 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+
/// Caches a successful fetch result in EditorPrefs. Must be called from the main thread.
29+
/// </summary>
30+
void CacheFetchResult(string currentVersion, string fetchedVersion);
31+
1532
/// <summary>
1633
/// Compares two version strings to determine if the first is newer than the second
1734
/// </summary>

MCPForUnity/Editor/Services/PackageUpdateService.cs

Lines changed: 121 additions & 2 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,88 @@ 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+
122+
string latestVersion = isGitInstallation
123+
? FetchLatestVersionFromGitHub(gitBranch)
124+
: FetchLatestVersionFromAssetStoreJson();
125+
126+
if (!string.IsNullOrEmpty(latestVersion))
127+
{
128+
return new UpdateCheckResult
129+
{
130+
CheckSucceeded = true,
131+
LatestVersion = latestVersion,
132+
UpdateAvailable = IsNewerVersion(latestVersion, currentVersion),
133+
Message = "Successfully checked for updates"
134+
};
135+
}
136+
137+
return new UpdateCheckResult
138+
{
139+
CheckSucceeded = false,
140+
UpdateAvailable = false,
141+
Message = isGitInstallation
142+
? "Failed to check for updates (network issue or offline)"
143+
: "Failed to check for Asset Store updates (network issue or offline)"
144+
};
145+
}
146+
147+
/// <inheritdoc/>
148+
public void CacheFetchResult(string currentVersion, string fetchedVersion)
149+
{
150+
if (string.IsNullOrEmpty(fetchedVersion)) return;
151+
152+
bool isGitInstallation = IsGitInstallation();
153+
string gitBranch = isGitInstallation ? GetGitUpdateBranch(currentVersion) : "main";
154+
bool useBetaChannel = isGitInstallation && string.Equals(gitBranch, "beta", StringComparison.OrdinalIgnoreCase);
155+
156+
string lastCheckKey = isGitInstallation
157+
? (useBetaChannel ? LastBetaCheckDateKey : LastCheckDateKey)
158+
: LastAssetStoreCheckDateKey;
159+
string cachedVersionKey = isGitInstallation
160+
? (useBetaChannel ? CachedBetaVersionKey : CachedVersionKey)
161+
: CachedAssetStoreVersionKey;
162+
163+
EditorPrefs.SetString(lastCheckKey, DateTime.Now.ToString("yyyy-MM-dd"));
164+
EditorPrefs.SetString(cachedVersionKey, fetchedVersion);
165+
}
166+
84167
/// <inheritdoc/>
85168
public bool IsNewerVersion(string version1, string version2)
86169
{
@@ -265,7 +348,7 @@ protected virtual string FetchLatestVersionFromGitHub(string branch)
265348
// - More reliable - doesn't require releases to be published
266349
// - Direct source of truth from the main branch
267350

268-
using (var client = new WebClient())
351+
using (var client = CreateWebClient())
269352
{
270353
client.Headers.Add("User-Agent", "Unity-MCPForUnity-UpdateChecker");
271354
string packageJsonUrl = string.Equals(branch, "beta", StringComparison.OrdinalIgnoreCase)
@@ -304,7 +387,7 @@ protected virtual string FetchLatestVersionFromAssetStoreJson()
304387
{
305388
try
306389
{
307-
using (var client = new WebClient())
390+
using (var client = CreateWebClient())
308391
{
309392
client.Headers.Add("User-Agent", "Unity-MCPForUnity-AssetStoreUpdateChecker");
310393
string jsonContent = client.DownloadString(AssetStoreVersionUrl);
@@ -322,5 +405,41 @@ protected virtual string FetchLatestVersionFromAssetStoreJson()
322405
return null;
323406
}
324407
}
408+
409+
protected virtual WebClient CreateWebClient()
410+
{
411+
return new TimeoutWebClient(GetRequestTimeoutMs());
412+
}
413+
414+
protected virtual int GetRequestTimeoutMs()
415+
{
416+
return DefaultRequestTimeoutMs;
417+
}
418+
419+
private sealed class TimeoutWebClient : WebClient
420+
{
421+
private readonly int _timeoutMs;
422+
423+
public TimeoutWebClient(int timeoutMs)
424+
{
425+
_timeoutMs = timeoutMs;
426+
}
427+
428+
protected override WebRequest GetWebRequest(Uri address)
429+
{
430+
var request = base.GetWebRequest(address);
431+
if (request != null)
432+
{
433+
request.Timeout = _timeoutMs;
434+
435+
if (request is HttpWebRequest httpRequest)
436+
{
437+
httpRequest.ReadWriteTimeout = _timeoutMs;
438+
}
439+
}
440+
441+
return request;
442+
}
443+
}
325444
}
326445
}

MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class MCPForUnityEditorWindow : EditorWindow
5252
private double lastRefreshTime = 0;
5353
private const double RefreshDebounceSeconds = 0.5;
5454
private bool updateCheckQueued = false;
55+
private bool updateCheckInFlight = false;
5556

5657
private enum ActivePanel
5758
{
@@ -352,7 +353,7 @@ private void UpdateVersionLabel()
352353

353354
private void QueueUpdateCheck()
354355
{
355-
if (updateCheckQueued)
356+
if (updateCheckQueued || updateCheckInFlight)
356357
{
357358
return;
358359
}
@@ -377,23 +378,61 @@ private void CheckForPackageUpdates()
377378
return;
378379
}
379380

380-
try
381+
// Main thread: resolve service + read EditorPrefs cache (both require main thread)
382+
var updateService = MCPServiceLocator.Updates;
383+
var cachedResult = updateService.TryGetCachedResult(currentVersion);
384+
if (cachedResult != null)
381385
{
382-
var result = MCPServiceLocator.Updates.CheckForUpdate(currentVersion);
383-
if (result.CheckSucceeded && result.UpdateAvailable && !string.IsNullOrEmpty(result.LatestVersion))
386+
ApplyUpdateCheckResult(cachedResult, currentVersion);
387+
return;
388+
}
389+
390+
// Background thread: network I/O only (no EditorPrefs access)
391+
updateCheckInFlight = true;
392+
Task.Run(() =>
393+
{
394+
try
384395
{
385-
updateNotificationText.text = $"Update available: v{result.LatestVersion} (current: v{currentVersion})";
386-
updateNotificationText.tooltip = $"Latest version: v{result.LatestVersion}\nCurrent version: v{currentVersion}";
387-
updateNotification.AddToClassList("visible");
396+
return updateService.FetchAndCompare(currentVersion);
388397
}
389-
else
398+
catch (Exception ex)
390399
{
391-
updateNotification.RemoveFromClassList("visible");
400+
McpLog.Info($"Package update check skipped: {ex.Message}");
401+
return null;
392402
}
403+
}).ContinueWith(t =>
404+
{
405+
EditorApplication.delayCall += () =>
406+
{
407+
updateCheckInFlight = false;
408+
409+
// Main thread: cache the result in EditorPrefs
410+
var result = t.Status == TaskStatus.RanToCompletion ? t.Result : null;
411+
if (result != null && result.CheckSucceeded && !string.IsNullOrEmpty(result.LatestVersion))
412+
{
413+
updateService.CacheFetchResult(currentVersion, result.LatestVersion);
414+
}
415+
416+
if (this == null || updateNotification == null || updateNotificationText == null)
417+
{
418+
return;
419+
}
420+
421+
ApplyUpdateCheckResult(result, currentVersion);
422+
};
423+
}, TaskScheduler.Default);
424+
}
425+
426+
private void ApplyUpdateCheckResult(UpdateCheckResult result, string currentVersion)
427+
{
428+
if (result != null && result.CheckSucceeded && result.UpdateAvailable && !string.IsNullOrEmpty(result.LatestVersion))
429+
{
430+
updateNotificationText.text = $"Update available: v{result.LatestVersion} (current: v{currentVersion})";
431+
updateNotificationText.tooltip = $"Latest version: v{result.LatestVersion}\nCurrent version: v{currentVersion}";
432+
updateNotification.AddToClassList("visible");
393433
}
394-
catch (Exception ex)
434+
else
395435
{
396-
McpLog.Info($"Package update check skipped: {ex.Message}");
397436
updateNotification.RemoveFromClassList("visible");
398437
}
399438
}

0 commit comments

Comments
 (0)