Skip to content

Commit c418fe4

Browse files
committed
feat(prefabs): add refresh flag to save_prefab_stage to skip AssetDatabase.Refresh()
Allows callers to opt out of the trailing AssetDatabase.Refresh() (often ~1s on large projects) and trigger refresh_unity themselves when needed. SaveAssets() still runs unconditionally so any dirty assets are flushed before returning.
1 parent a2a5edf commit c418fe4

2 files changed

Lines changed: 23 additions & 10 deletions

File tree

MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,15 @@ public static object HandleCommand(JObject @params)
6060
return OpenPrefabStage(prefabPath);
6161
}
6262
case ACTION_SAVE_PREFAB_STAGE:
63-
return SavePrefabStage();
63+
{
64+
bool refresh = @params["refresh"]?.ToObject<bool>() ?? true;
65+
return SavePrefabStage(refresh);
66+
}
6467
case ACTION_CLOSE_PREFAB_STAGE:
6568
{
6669
bool saveBeforeClose = @params["saveBeforeClose"]?.ToObject<bool>() ?? false;
67-
return ClosePrefabStage(saveBeforeClose);
70+
bool refresh = @params["refresh"]?.ToObject<bool>() ?? true;
71+
return ClosePrefabStage(saveBeforeClose, refresh);
6872
}
6973
default:
7074
return new ErrorResponse($"Unknown action: '{action}'. Valid actions are: {SupportedActions}.");
@@ -1338,7 +1342,7 @@ private static object OpenPrefabStage(string requestedPath)
13381342
}
13391343
}
13401344

1341-
private static object SavePrefabStage()
1345+
private static object SavePrefabStage(bool refreshAfterSave = true)
13421346
{
13431347
try
13441348
{
@@ -1348,20 +1352,20 @@ private static object SavePrefabStage()
13481352
return new ErrorResponse("Not currently in prefab editing mode. Open a prefab stage first with open_prefab_stage.");
13491353
}
13501354

1351-
if (!TrySavePrefabStage(prefabStage, out string prefabPath, out string errorMessage))
1355+
if (!TrySavePrefabStage(prefabStage, refreshAfterSave, out string prefabPath, out string errorMessage))
13521356
{
13531357
return new ErrorResponse(errorMessage);
13541358
}
13551359

1356-
return new SuccessResponse($"Saved prefab stage changes for '{prefabPath}'.", new { prefabPath, saved = true });
1360+
return new SuccessResponse($"Saved prefab stage changes for '{prefabPath}'.", new { prefabPath, saved = true, refreshed = refreshAfterSave });
13571361
}
13581362
catch (Exception e)
13591363
{
13601364
return new ErrorResponse($"Error saving prefab stage: {e.Message}");
13611365
}
13621366
}
13631367

1364-
private static object ClosePrefabStage(bool saveBeforeClose = false)
1368+
private static object ClosePrefabStage(bool saveBeforeClose = false, bool refreshAfterSave = true)
13651369
{
13661370
try
13671371
{
@@ -1373,7 +1377,7 @@ private static object ClosePrefabStage(bool saveBeforeClose = false)
13731377

13741378
if (saveBeforeClose)
13751379
{
1376-
if (!TrySavePrefabStage(prefabStage, out _, out string errorMessage))
1380+
if (!TrySavePrefabStage(prefabStage, refreshAfterSave, out _, out string errorMessage))
13771381
{
13781382
return new ErrorResponse(errorMessage);
13791383
}
@@ -1389,7 +1393,7 @@ private static object ClosePrefabStage(bool saveBeforeClose = false)
13891393
}
13901394
}
13911395

1392-
private static bool TrySavePrefabStage(PrefabStage prefabStage, out string prefabPath, out string errorMessage)
1396+
private static bool TrySavePrefabStage(PrefabStage prefabStage, bool refreshAfterSave, out string prefabPath, out string errorMessage)
13931397
{
13941398
prefabPath = prefabStage.assetPath;
13951399
errorMessage = null;
@@ -1410,7 +1414,10 @@ private static bool TrySavePrefabStage(PrefabStage prefabStage, out string prefa
14101414

14111415
prefabStage.ClearDirtiness();
14121416
AssetDatabase.SaveAssets();
1413-
AssetDatabase.Refresh();
1417+
if (refreshAfterSave)
1418+
{
1419+
AssetDatabase.Refresh();
1420+
}
14141421
return true;
14151422
}
14161423

Server/src/services/tools/manage_prefabs.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"Use component_properties with modify_contents to set serialized fields on existing components "
3939
"(e.g. component_properties={\"Rigidbody\": {\"mass\": 5.0}, \"MyScript\": {\"health\": 100}}). "
4040
"Supports object references via {\"guid\": \"...\"}, {\"path\": \"Assets/...\"}, or {\"instanceID\": 123}. "
41-
"Use manage_asset action=search filterType=Prefab to list prefabs."
41+
"Use manage_asset action=search filterType=Prefab to list prefabs. "
42+
"Performance tip: for save_prefab_stage, pass refresh=false to skip the trailing AssetDatabase.Refresh() (often ~1s on large projects); SaveAssets() still runs so dirty assets are flushed. Call refresh_unity yourself when you actually need the asset database to reimport."
4243
),
4344
annotations=ToolAnnotations(
4445
title="Manage Prefabs",
@@ -78,6 +79,7 @@ async def manage_prefabs(
7879
create_child: Annotated[dict[str, Any] | list[dict[str, Any]], "Create child GameObject(s) in the prefab. Single object or array of objects, each with: name (required), parent (optional, defaults to target), source_prefab_path (optional: asset path to instantiate as nested prefab, e.g. 'Assets/Prefabs/Bullet.prefab'), primitive_type (optional: Cube, Sphere, Capsule, Cylinder, Plane, Quad), position, rotation, scale, components_to_add, tag, layer, set_active. source_prefab_path and primitive_type are mutually exclusive."] | None = None,
7980
delete_child: Annotated[str | list[str], "Child name(s) or path(s) to remove from the prefab. Supports single string or array for batch deletion (e.g. 'Child1' or ['Child1', 'Child1/Grandchild'])."] | None = None,
8081
component_properties: Annotated[dict[str, dict[str, Any]], "Set properties on existing components in modify_contents. Keys are component type names, values are dicts of property name to value. Example: {\"Rigidbody\": {\"mass\": 5.0}, \"MyScript\": {\"health\": 100}}. Supports object references via {\"guid\": \"...\"}, {\"path\": \"Assets/...\"}, or {\"instanceID\": 123}. For Sprite sub-assets: {\"guid\": \"...\", \"spriteName\": \"<name>\"}. Single-sprite textures auto-resolve."] | None = None,
82+
refresh: Annotated[bool, "For save_prefab_stage: run AssetDatabase.Refresh() after writing the prefab (default true). Set false to skip the ~1s trailing import; SaveAssets() still runs so dirty assets are flushed. Call refresh_unity yourself when needed."] | None = None,
8183
) -> dict[str, Any]:
8284
# Back-compat: map 'name' → 'target' for create_from_gameobject (Unity accepts both)
8385
if action == "create_from_gameobject" and target is None and name is not None:
@@ -198,6 +200,10 @@ def normalize_child_params(child: Any, index: int | None = None) -> tuple[dict |
198200
if delete_child is not None:
199201
params["deleteChild"] = delete_child
200202

203+
refresh_val = coerce_bool(refresh)
204+
if refresh_val is not None:
205+
params["refresh"] = refresh_val
206+
201207
# Send command to Unity
202208
response = await send_with_unity_instance(
203209
async_send_command_with_retry, unity_instance, "manage_prefabs", params

0 commit comments

Comments
 (0)