Skip to content

Commit da0bd24

Browse files
committed
fix: save prefab stages via prefab asset workflow
1 parent 6948193 commit da0bd24

2 files changed

Lines changed: 110 additions & 9 deletions

File tree

MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,15 +1347,12 @@ private static object SavePrefabStage()
13471347
return new ErrorResponse("Not currently in prefab editing mode. Open a prefab stage first with open_prefab_stage.");
13481348
}
13491349

1350-
string prefabPath = prefabStage.assetPath;
1351-
EditorSceneManager.MarkSceneDirty(prefabStage.scene);
1352-
bool saved = EditorSceneManager.SaveScene(prefabStage.scene);
1353-
if (!saved)
1350+
if (!TrySavePrefabStage(prefabStage, out string prefabPath, out string errorMessage))
13541351
{
1355-
return new ErrorResponse($"Failed to save prefab stage for '{prefabPath}'. The file may be read-only or the disk may be full.");
1352+
return new ErrorResponse(errorMessage);
13561353
}
13571354

1358-
return new SuccessResponse($"Saved prefab stage changes for '{prefabPath}'.", new { prefabPath, saved });
1355+
return new SuccessResponse($"Saved prefab stage changes for '{prefabPath}'.", new { prefabPath, saved = true });
13591356
}
13601357
catch (Exception e)
13611358
{
@@ -1375,10 +1372,9 @@ private static object ClosePrefabStage(bool saveBeforeClose = false)
13751372

13761373
if (saveBeforeClose)
13771374
{
1378-
var saveResult = SavePrefabStage();
1379-
if (saveResult is ErrorResponse)
1375+
if (!TrySavePrefabStage(prefabStage, out _, out string errorMessage))
13801376
{
1381-
return saveResult;
1377+
return new ErrorResponse(errorMessage);
13821378
}
13831379
}
13841380

@@ -1392,6 +1388,31 @@ private static object ClosePrefabStage(bool saveBeforeClose = false)
13921388
}
13931389
}
13941390

1391+
private static bool TrySavePrefabStage(PrefabStage prefabStage, out string prefabPath, out string errorMessage)
1392+
{
1393+
prefabPath = prefabStage.assetPath;
1394+
errorMessage = null;
1395+
1396+
if (prefabStage.prefabContentsRoot == null)
1397+
{
1398+
errorMessage = $"Failed to save prefab stage for '{prefabPath}'. The prefab contents root is missing.";
1399+
return false;
1400+
}
1401+
1402+
bool saved;
1403+
PrefabUtility.SaveAsPrefabAsset(prefabStage.prefabContentsRoot, prefabPath, out saved);
1404+
if (!saved)
1405+
{
1406+
errorMessage = $"Failed to save prefab stage for '{prefabPath}'. The file may be read-only or the disk may be full.";
1407+
return false;
1408+
}
1409+
1410+
prefabStage.ClearDirtiness();
1411+
AssetDatabase.SaveAssets();
1412+
AssetDatabase.Refresh();
1413+
return true;
1414+
}
1415+
13951416
#endregion
13961417
}
13971418
}

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsStageTests.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,86 @@ public void OpenPrefabStage_PrefabPathTakesPrecedenceOverPath()
151151
}
152152
}
153153

154+
[Test]
155+
public void SavePrefabStage_PersistsPrefabContentChanges()
156+
{
157+
string prefabPath = CreateTestPrefab("SaveStageRoot");
158+
159+
try
160+
{
161+
AssertOpenPrefabStage(prefabPath);
162+
163+
var stage = PrefabStageUtility.GetCurrentPrefabStage();
164+
Assert.IsNotNull(stage, "Expected prefab stage to be open.");
165+
166+
var child = new GameObject("SavedChild");
167+
child.transform.SetParent(stage.prefabContentsRoot.transform, false);
168+
169+
var saveResult = ToJObject(ManagePrefabs.HandleCommand(new JObject
170+
{
171+
["action"] = "save_prefab_stage"
172+
}));
173+
174+
Assert.IsTrue(saveResult.Value<bool>("success"), $"Expected save to succeed but got: {saveResult}");
175+
Assert.AreEqual(prefabPath, saveResult["data"].Value<string>("prefabPath"));
176+
177+
StageUtility.GoToMainStage();
178+
179+
GameObject reloaded = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
180+
Assert.IsNotNull(reloaded.transform.Find("SavedChild"), "Saved prefab should contain the new child after save_prefab_stage.");
181+
}
182+
finally
183+
{
184+
StageUtility.GoToMainStage();
185+
SafeDeleteAsset(prefabPath);
186+
}
187+
}
188+
189+
[Test]
190+
public void ClosePrefabStage_SaveBeforeClose_PersistsChanges()
191+
{
192+
string prefabPath = CreateTestPrefab("CloseSaveRoot");
193+
194+
try
195+
{
196+
AssertOpenPrefabStage(prefabPath);
197+
198+
var stage = PrefabStageUtility.GetCurrentPrefabStage();
199+
Assert.IsNotNull(stage, "Expected prefab stage to be open.");
200+
201+
var child = new GameObject("CloseSavedChild");
202+
child.transform.SetParent(stage.prefabContentsRoot.transform, false);
203+
204+
var closeResult = ToJObject(ManagePrefabs.HandleCommand(new JObject
205+
{
206+
["action"] = "close_prefab_stage",
207+
["saveBeforeClose"] = true
208+
}));
209+
210+
Assert.IsTrue(closeResult.Value<bool>("success"), $"Expected close with save to succeed but got: {closeResult}");
211+
Assert.IsNull(PrefabStageUtility.GetCurrentPrefabStage(), "Prefab stage should be closed after close_prefab_stage.");
212+
213+
GameObject reloaded = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
214+
Assert.IsNotNull(reloaded.transform.Find("CloseSavedChild"), "Saved prefab should contain the new child after close_prefab_stage(saveBeforeClose: true).");
215+
}
216+
finally
217+
{
218+
StageUtility.GoToMainStage();
219+
SafeDeleteAsset(prefabPath);
220+
}
221+
}
222+
223+
private static void AssertOpenPrefabStage(string prefabPath)
224+
{
225+
var openResult = ToJObject(ManagePrefabs.HandleCommand(new JObject
226+
{
227+
["action"] = "open_prefab_stage",
228+
["prefabPath"] = prefabPath
229+
}));
230+
231+
Assert.IsTrue(openResult.Value<bool>("success"), $"Expected open to succeed but got: {openResult}");
232+
}
233+
154234
private static string CreateTestPrefab(string rootName)
155235
{
156236
string prefabPath = Path.Combine(TempDirectory, $"{rootName}.prefab").Replace('\\', '/');

0 commit comments

Comments
 (0)