Commit 7127fcb
fix(resolver): unstick legacy-folder + stale-flat duplicate-assembly state (#741)
* fix(resolver): make legacy-folder migration best-effort + auto-disable locked DLLs
NuGetLegacyMigration.Run was atomic per folder via Directory.Delete(recursive: true)
and aborted the entire restore on any IOException. When a legacy {Id}.{Version}/
folder contained a McpPlugin.dll that Unity had loaded into the editor AppDomain,
the file was locked, the migration aborted, and downstream cleanup
(NuGetPackageInstaller.RemoveStaleVersionDllsByStem) never ran — leaving stale
flat-format DLLs (e.g. McpPlugin.6.2.0.dll) on disk to conflict with the canonical
filename Unity wanted to load. Result: CS1704 duplicate-assembly errors with no
path forward for the user.
Switch the migration to best-effort:
- TryRemoveDirectoryRecursive deletes per-file with try/catch; locked files no
longer throw out of the loop, so unlocked siblings still get removed.
- For each locked DLL, disable its PluginImporter (no platforms compatible) so
the next domain reload unloads it and unlocks the OS handle. The next
migration pass then completes the deletion.
- Restore() no longer returns false on AbortedFileLock — proceeds to Install,
which runs RemoveStaleVersionDllsByStem and extraction even when one folder
survived this pass.
Outcome.AbortedFileLock now means "at least one folder is still pending"
rather than "abort everything"; the downstream cleanup is what unbricks the
duplicate-assembly state in the user's report. Updated NuGetLegacyMigrationTests
for the new log shape (warning, not error) and added a sibling-folder
partial-success test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(resolver): also disable PluginImporter when stale-flat sweep hits a file lock
The user's CS1704 reproduction: stale McpPlugin.6.2.0.dll on disk alongside
the canonical 6.2.1, both with assembly name "McpPlugin" — Unity loads one,
locks it, and RemoveStaleVersionDllsByStem's TryDeleteFile silently logged
and gave up. The sweep then reported success, the file survived, and the
duplicate-assembly error never cleared.
Pull the locked-file recovery path out of NuGetLegacyMigration (where it
already existed as a private helper) into a shared
NuGetPluginConfigurator.DisableImporter, and call it from the stale-flat
sweep on IOException / UnauthorizedAccessException. The next domain reload
unloads the disabled DLL, the file unlocks, and the next sweep pass deletes
it. Two-cycle recovery instead of "stuck forever".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(resolver): drop version from DLL filenames; track version in manifest only
Previously the install layout embedded the package version in the on-disk
filename (e.g. McpPlugin.6.2.1.dll). Every package version bump required
asmdef precompiledReferences edits across 4 asmdefs in lockstep, and a
stale {stem}.<oldVersion>.dll co-existing with a fresh {stem}.<newVersion>.dll
produced CS1704 duplicate-assembly errors that the user repeatedly hit.
Move the version out of the filename and into the manifest:
- Canonical install filename is now just {stem}.dll (the .nupkg's lib-folder
filename, written verbatim).
- NuGetInstallManifest's {packageId → version, dlls} entry is now the only
source of truth for which version is installed; the resolver compares that
against NuGetConfig.Packages to decide when a DLL needs to be replaced.
- Asmdef precompiledReferences reference the unversioned filename and never
need to be edited when a configured package version bumps — extraction
overwrites the same file with the new version's bytes in place.
Migration:
- NuGetLegacyMigration.Run now sweeps two kinds of legacy state: the original
{Id}.{Version}/ per-package directories AND every {stem}.{numericVersion}.dll
at the install root. Both use the same best-effort + PluginImporter-disable
recovery for locked files.
- NuGetPackageRestorer.AllPackagesInstalled forces a full Restore() when it
finds versioned-filename DLLs on disk OR versioned-filename entries in the
manifest, so users transitioning from the previous layout get migrated on
the first reload.
- 41 committed DLLs + their .meta sidecars renamed via git mv. 4 asmdefs
updated to reference unversioned filenames. .nuget-installed.json updated
in lockstep (version-field stays as-is; dlls list now carries unversioned
filenames).
Tests: NuGetExtractorFlatLayoutTests, NuGetPackageInstallerStaleVersionFilesystemSweepTests
fully rewritten for the new naming. NuGetLegacyMigrationTests gets two new
cases for the versioned-filename sweep (root-level cleanup, mixed-state
cleanup with both legacy folders + versioned files in one pass).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ui): wipe Library/ScriptAssemblies before package update install
When the user clicks "Install Update" in UpdatePopupWindow, delete
Library/ScriptAssemblies BEFORE handing off to Client.Add. Without this,
if the post-install recompile hits any user-asmdef CS0246 (e.g. while
the resolver is mid-migration off versioned filenames), Unity may skip
the post-compile domain reload, leaving the OLD plugin AppDomain in
memory and the new resolver code idle on disk — exactly the stale-state
bug we just diagnosed in the Demo-MCP project.
Atomic Directory.Delete first; fall back to per-file best-effort on
IOException / UnauthorizedAccessException so a single locked file (the
currently-loaded plugin DLLs are mmapped) doesn't abort the wipe.
Surviving locked files get overwritten by Unity's post-install recompile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(resolver): wipe ScriptAssemblies in Force Resolve menu + extract shared util
The same stale-AppDomain problem the package update popup guards against
also bites the manual "Force Resolve NuGet DLLs" menu: when user-asmdef
compile errors prevent Unity from completing a domain reload, force-resolve
runs against the OLD plugin assemblies in memory and any new resolver code
on disk stays idle.
Pull the wipe helper out of UpdatePopupWindow (where I had it as a private
method) into a shared NuGetExtractor.DependencyResolver.ScriptAssembliesCache
utility, and call it at the top of NuGetResolverMenu.ForceResolve. The
update popup now delegates to the same helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(resolver): isolate DependencyResolver asmdef from Assets/Plugins so it compiles past duplicate-assembly conflicts
The user reported a consumer project where Unity refused to start the
resolver: legacy {Id}.{Version}/ folders + stale flat versioned DLLs in
Assets/Plugins/NuGet caused Unity to compile every asmdef with -r: for
both copies of McpPlugin / McpPlugin.Common (assembly name McpPlugin /
McpPlugin.Common, two versions on disk). csc.exe fired CS1704 for *every*
assembly in the project — including our DependencyResolver, even though
its asmdef references zero McpPlugin DLLs in its own
precompiledReferences. The DependencyResolver assembly never compiled,
so its [InitializeOnLoad] never ran, so the migration sweep that *would*
have removed the duplicates never executed. Chicken-and-egg.
Set "overrideReferences": true with an empty precompiledReferences list
so csc.exe only sees the BCL references for this assembly. Whatever
duplicate-assembly chaos exists in Assets/Plugins/NuGet stays invisible
to the resolver's own compile, the resolver loads cleanly, and its
migration sweep can clean things up on the first reload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(resolver): apply simplify-pass cleanups across migration code
- Remove NuGetPackageInstaller.RemoveStaleVersionDllsByStem and its test
fixture: the broad versioned-filename sweep in NuGetLegacyMigration.Run
now covers the same scenarios authoritatively, so the per-package sweep
was a 35x-per-restore Directory.GetFiles call doing duplicate work for
no incremental coverage.
- Consolidate three identical TryDeleteFile copies into a single
NuGetPluginConfigurator.TryDeleteFile (already the home of the related
DisableImporter helper); both NuGetLegacyMigration and
NuGetPackageInstaller now call it.
- Rename NuGetLegacyMigration.Result fields to match the post-best-effort
semantics: RemovedDirectories -> RemovedItems (phase 2 adds files too),
FailedDirectory -> FirstFailedItem, FailureMessage -> FirstFailureMessage.
Update tests in lockstep.
- Trim verbose XML/inline comments that restated WHAT the code does:
Run() doc shrunk from ~38 lines to 6, ScriptAssembliesCache class+method
docs collapsed, NuGetPackageRestorer canonical-name comments reduced to
one-line WHY each. BuildLockMessage reason is now non-nullable since
every callsite has one (with a "(unknown)" fallback at the call site for
nullable-flow safety).
- Simplify the migration outcome-check in Restore() to
`outcome != NoLegacyState`, replacing a two-arm OR.
Net change: -378 / +69 lines (mostly comment trim + dead-code removal).
No behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(asmdef): add DependencyResolver to Editor asmdef references
UpdatePopupWindow.cs and the menu now call ScriptAssembliesCache.Wipe()
which lives in the DependencyResolver assembly. The Editor asmdef did
not list it under references, so the compile broke with CS0234. Add
"com.IvanMurzak.Unity.MCP.DependencyResolver" to references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update Unity NuGet packages: Add System.Numerics.Vectors, System.Reflection.Metadata, System.Runtime.CompilerServices.Unsafe, System.Text.Encoding.CodePages, System.Text.Encodings.Web, System.Text.Json, System.Threading.Channels, and System.Threading.Tasks.Extensions; remove obsolete versions and metadata files.
* fix(resolver): strip UNITY_MCP_READY before package update / force-resolve
Belt-and-braces guard against the wedge state where the resolver itself
can't compile through some unrelated user-asmdef error: lift the
ReadyDefine constant from NuGetDependencyResolver to NuGetConfig (so it
can be referenced from outside the [InitializeOnLoad] class), add a public
ScriptAssembliesCache.RemoveReadyDefine() helper that strips the define
from every build target group, and call it alongside the existing
ScriptAssembliesCache.Wipe() in both the package update popup and the
Force Resolve menu.
Effect: after the user clicks Update or Force Resolve, the next compile
pass skips the main plugin asmdefs entirely (they're gated on
UNITY_MCP_READY). Only the DependencyResolver assembly compiles, so even
if every other asmdef in the project has CS0246 / CS1704 / etc., the
resolver still gets a chance to run, finish migration, and re-add the
define via NuGetDependencyResolver.EnsureScriptingDefine() — at which
point the main plugin asmdefs compile against the now-healthy DLL set.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(resolver): rename ScriptAssembliesCache to RecompileGate, add Reset() facade, consolidate define-iteration
The class no longer matches its name: ScriptAssembliesCache implies it
only touches Library/ScriptAssemblies, but it also manipulates
PlayerSettings via RemoveReadyDefine. Both call sites
(UpdatePopupWindow.StartPackageInstall, NuGetResolverMenu.ForceResolve)
also always invoked Wipe() and RemoveReadyDefine() in lockstep —
Reviewer flagged the duplication and the misnomer.
- Rename class+file to RecompileGate (git mv preserves history+meta GUID)
to match the actual responsibility: gating Unity's next compile to
include only the resolver itself.
- Add public Reset() facade that does Wipe() + RemoveReadyDefine() so
callers can't accidentally do half the operation.
- Move EnsureScriptingDefine + TryAddDefine off NuGetDependencyResolver
onto RecompileGate.EnsureReadyDefine (paired with RemoveReadyDefine).
Both now share a single ForEachTarget(Func<NamedBuildTarget, bool>)
helper that iterates BuildTargetGroup values + the distinct
NamedBuildTarget.Server — the iteration boilerplate that was mirrored
across both methods is now declared once.
- Trim verbose call-site comment blocks to a single WHY sentence each.
Net: -190 / +178 lines, mostly comment trim + dedupe. NuGetDependencyResolver
loses ~65 lines of editor-build-target plumbing and is now focused
purely on the [InitializeOnLoad] flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Refactor NuGet packages: Replace outdated assemblies with updated versions
- Removed obsolete System.Collections.Immutable.7.0.0.dll and its metadata.
- Added new System.Collections.Immutable.dll and its metadata.
- Removed obsolete System.ComponentModel.Annotations.5.0.0.dll and its metadata.
- Added new System.ComponentModel.Annotations.dll and its metadata.
- Removed obsolete System.Diagnostics.DiagnosticSource.8.0.1.dll and its metadata.
- Added new System.Diagnostics.DiagnosticSource.dll and its metadata.
- Removed obsolete System.IO.Pipelines.8.0.0.dll and its metadata.
- Added new System.IO.Pipelines.dll and its metadata.
- Added new System.Memory.dll and its metadata.
- Added new System.Numerics.Vectors.dll and its metadata.
- Removed obsolete System.Reflection.Metadata.7.0.0.dll and its metadata.
- Added new System.Reflection.Metadata.dll and its metadata.
- Removed obsolete System.Runtime.CompilerServices.Unsafe.6.1.2.dll and its metadata.
- Added new System.Runtime.CompilerServices.Unsafe.dll and its metadata.
- Removed obsolete System.Text.Encoding.CodePages.7.0.0.dll and its metadata.
- Added new System.Text.Encoding.CodePages.dll and its metadata.
- Removed obsolete System.Text.Encodings.Web.8.0.0.dll and its metadata.
- Added new System.Text.Encodings.Web.dll and its metadata.
- Removed obsolete System.Text.Json.8.0.5.dll and its metadata.
- Added new System.Text.Json.dll and its metadata.
- Removed obsolete System.Threading.Channels.8.0.0.dll and its metadata.
- Added new System.Threading.Channels.dll and its metadata.
- Added new System.Threading.Tasks.Extensions.dll and its metadata.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 0165384 commit 7127fcb
727 files changed
Lines changed: 6858 additions & 7467 deletions
File tree
- Unity-MCP-Plugin
- Assets/Plugins/NuGet
- Packages/com.ivanmurzak.unity.mcp
- Editor
- DependencyResolver
- Scripts
- UI/Window
- Runtime
- Tests
- Editor
- DependencyResolver
- Runtime
- Unity-Tests
- 2022.3.62f3/Assets/Plugins/NuGet
- 2023.2.22f1/Assets/Plugins/NuGet
- 6000.3.1f1/Assets/Plugins/NuGet
- 6000.5.0b3
- Assets/Plugins/NuGet
- com.IvanMurzak.McpPlugin.Common.6.2.1
- ProjectSettings
- 6000.6.0a2/Assets/Plugins/NuGet
- com.IvanMurzak.McpPlugin.6.2.1
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 42 additions & 42 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
| 42 | + | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
| 54 | + | |
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
| 72 | + | |
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
78 | | - | |
| 78 | + | |
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| |||
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
88 | | - | |
| 88 | + | |
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
94 | | - | |
| 94 | + | |
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
| 112 | + | |
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
118 | | - | |
| 118 | + | |
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
124 | | - | |
| 124 | + | |
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
130 | | - | |
| 130 | + | |
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
135 | 135 | | |
136 | | - | |
| 136 | + | |
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
142 | | - | |
| 142 | + | |
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
148 | | - | |
| 148 | + | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
153 | 153 | | |
154 | | - | |
| 154 | + | |
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
160 | | - | |
| 160 | + | |
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
166 | | - | |
| 166 | + | |
167 | 167 | | |
168 | 168 | | |
169 | 169 | | |
170 | 170 | | |
171 | 171 | | |
172 | | - | |
| 172 | + | |
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
177 | 177 | | |
178 | | - | |
| 178 | + | |
179 | 179 | | |
180 | 180 | | |
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
184 | | - | |
| 184 | + | |
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
190 | | - | |
| 190 | + | |
191 | 191 | | |
192 | 192 | | |
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
196 | | - | |
| 196 | + | |
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
200 | 200 | | |
201 | 201 | | |
202 | | - | |
| 202 | + | |
203 | 203 | | |
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
207 | 207 | | |
208 | | - | |
| 208 | + | |
209 | 209 | | |
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
213 | 213 | | |
214 | | - | |
| 214 | + | |
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
218 | 218 | | |
219 | 219 | | |
220 | | - | |
| 220 | + | |
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
224 | 224 | | |
225 | 225 | | |
226 | | - | |
| 226 | + | |
227 | 227 | | |
228 | 228 | | |
229 | 229 | | |
230 | 230 | | |
231 | 231 | | |
232 | | - | |
| 232 | + | |
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | 236 | | |
237 | 237 | | |
238 | | - | |
| 238 | + | |
239 | 239 | | |
240 | 240 | | |
241 | 241 | | |
242 | 242 | | |
243 | 243 | | |
244 | | - | |
| 244 | + | |
245 | 245 | | |
246 | 246 | | |
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
250 | | - | |
| 250 | + | |
251 | 251 | | |
252 | 252 | | |
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
256 | | - | |
| 256 | + | |
257 | 257 | | |
258 | 258 | | |
259 | 259 | | |
| |||
0 commit comments