Skip to content

Commit fe7e4cb

Browse files
committed
refactor: consolidate package installation and detection into unified lifecycle manager
Duplicate code for pretty much no reason, as they both initialized there was a small chance of a race condition as well. Consolidating made sense here
1 parent 1a6fded commit fe7e4cb

File tree

6 files changed

+373
-154
lines changed

6 files changed

+373
-154
lines changed

MCPForUnity/Editor/Helpers/PackageDetector.cs

Lines changed: 0 additions & 106 deletions
This file was deleted.

MCPForUnity/Editor/Helpers/PackageInstaller.cs

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using System.IO;
2+
using UnityEditor;
3+
using UnityEngine;
4+
5+
namespace MCPForUnity.Editor.Helpers
6+
{
7+
/// <summary>
8+
/// Manages package lifecycle events including first-time installation,
9+
/// version updates, and legacy installation detection.
10+
/// Consolidates the functionality of PackageInstaller and PackageDetector.
11+
/// </summary>
12+
[InitializeOnLoad]
13+
public static class PackageLifecycleManager
14+
{
15+
private const string VersionKeyPrefix = "MCPForUnity.InstalledVersion:";
16+
private const string LegacyInstallFlagKey = "MCPForUnity.ServerInstalled"; // For migration
17+
18+
static PackageLifecycleManager()
19+
{
20+
// Schedule the check for after Unity is fully loaded
21+
EditorApplication.delayCall += CheckAndInstallServer;
22+
}
23+
24+
private static void CheckAndInstallServer()
25+
{
26+
try
27+
{
28+
string currentVersion = GetPackageVersion();
29+
string versionKey = VersionKeyPrefix + currentVersion;
30+
bool hasRunForThisVersion = EditorPrefs.GetBool(versionKey, false);
31+
32+
// Check for conditions that require installation/verification
33+
bool isFirstTimeInstall = !EditorPrefs.HasKey(LegacyInstallFlagKey) && !hasRunForThisVersion;
34+
bool legacyPresent = LegacyRootsExist();
35+
bool canonicalMissing = File.Exists(
36+
Path.Combine(ServerInstaller.GetServerPath(), "server.py")
37+
);
38+
39+
// Run if: first install, version update, legacy detected, or canonical missing
40+
if (isFirstTimeInstall || !hasRunForThisVersion || legacyPresent || canonicalMissing)
41+
{
42+
PerformInstallation(currentVersion, versionKey, isFirstTimeInstall);
43+
}
44+
}
45+
catch (System.Exception ex)
46+
{
47+
McpLog.Info($"Package lifecycle check failed: {ex.Message}. Open Window > MCP For Unity if needed.", always: false);
48+
}
49+
}
50+
51+
private static void PerformInstallation(string version, string versionKey, bool isFirstTimeInstall)
52+
{
53+
string error = null;
54+
System.Exception capturedEx = null;
55+
56+
try
57+
{
58+
ServerInstaller.EnsureServerInstalled();
59+
60+
// Mark as installed for this version
61+
EditorPrefs.SetBool(versionKey, true);
62+
63+
// Migrate legacy flag if this is first time
64+
if (isFirstTimeInstall)
65+
{
66+
EditorPrefs.SetBool(LegacyInstallFlagKey, true);
67+
}
68+
69+
// Clean up old version keys (keep only current version)
70+
CleanupOldVersionKeys(version);
71+
72+
// Clean up legacy preference keys
73+
CleanupLegacyPrefs();
74+
75+
// Only log success if server was actually embedded and copied
76+
if (ServerInstaller.HasEmbeddedServer() && isFirstTimeInstall)
77+
{
78+
McpLog.Info("MCP server installation completed successfully.");
79+
}
80+
}
81+
catch (System.Exception ex)
82+
{
83+
error = ex.Message;
84+
capturedEx = ex;
85+
86+
// Mark as handled to avoid repeated failures
87+
EditorPrefs.SetBool(versionKey, true);
88+
if (isFirstTimeInstall)
89+
{
90+
EditorPrefs.SetBool(LegacyInstallFlagKey, true);
91+
}
92+
}
93+
94+
if (!string.IsNullOrEmpty(error))
95+
{
96+
McpLog.Info($"Server check: {error}. Download via Window > MCP For Unity if needed.", always: false);
97+
}
98+
}
99+
100+
private static string GetPackageVersion()
101+
{
102+
try
103+
{
104+
var info = UnityEditor.PackageManager.PackageInfo.FindForAssembly(
105+
typeof(PackageLifecycleManager).Assembly
106+
);
107+
if (info != null && !string.IsNullOrEmpty(info.version))
108+
{
109+
return info.version;
110+
}
111+
}
112+
catch { }
113+
114+
// Fallback to embedded server version
115+
return GetEmbeddedServerVersion();
116+
}
117+
118+
private static string GetEmbeddedServerVersion()
119+
{
120+
try
121+
{
122+
if (ServerPathResolver.TryFindEmbeddedServerSource(out var embeddedSrc))
123+
{
124+
var versionPath = Path.Combine(embeddedSrc, "server_version.txt");
125+
if (File.Exists(versionPath))
126+
{
127+
return File.ReadAllText(versionPath)?.Trim() ?? "unknown";
128+
}
129+
}
130+
}
131+
catch { }
132+
return "unknown";
133+
}
134+
135+
private static bool LegacyRootsExist()
136+
{
137+
try
138+
{
139+
string home = System.Environment.GetFolderPath(
140+
System.Environment.SpecialFolder.UserProfile
141+
) ?? string.Empty;
142+
143+
string[] legacyRoots =
144+
{
145+
Path.Combine(home, ".config", "UnityMCP", "UnityMcpServer", "src"),
146+
Path.Combine(home, ".local", "share", "UnityMCP", "UnityMcpServer", "src")
147+
};
148+
149+
foreach (var root in legacyRoots)
150+
{
151+
try
152+
{
153+
if (File.Exists(Path.Combine(root, "server.py")))
154+
{
155+
return true;
156+
}
157+
}
158+
catch { }
159+
}
160+
}
161+
catch { }
162+
return false;
163+
}
164+
165+
private static void CleanupOldVersionKeys(string currentVersion)
166+
{
167+
try
168+
{
169+
// Get all EditorPrefs keys that start with our version prefix
170+
// Note: Unity doesn't provide a way to enumerate all keys, so we can only
171+
// clean up known legacy keys. Future versions will be cleaned up when
172+
// a newer version runs.
173+
// This is a best-effort cleanup.
174+
}
175+
catch { }
176+
}
177+
178+
private static void CleanupLegacyPrefs()
179+
{
180+
try
181+
{
182+
// Clean up old preference keys that are no longer used
183+
string[] legacyKeys =
184+
{
185+
"MCPForUnity.ServerSrc",
186+
"MCPForUnity.PythonDirOverride",
187+
"MCPForUnity.LegacyDetectLogged" // Old prefix without version
188+
};
189+
190+
foreach (var key in legacyKeys)
191+
{
192+
try
193+
{
194+
if (EditorPrefs.HasKey(key))
195+
{
196+
EditorPrefs.DeleteKey(key);
197+
}
198+
}
199+
catch { }
200+
}
201+
}
202+
catch { }
203+
}
204+
}
205+
}

MCPForUnity/Editor/Helpers/PackageDetector.cs.meta renamed to MCPForUnity/Editor/Helpers/PackageLifecycleManager.cs.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)