11using System ;
2+ using System . IO ;
23using MCPForUnity . Editor . Helpers ;
34using Newtonsoft . Json . Linq ;
45using UnityEditor ;
56using UnityEditor . SceneManagement ;
67using UnityEngine ;
8+ using UnityEngine . SceneManagement ;
79
810namespace MCPForUnity . Editor . Tools . Prefabs
911{
1012 public static class ManagePrefabs
1113 {
12- private const string SupportedActions = "open_stage, close_stage, save_open_stage, apply_instance_overrides, revert_instance_overrides " ;
14+ private const string SupportedActions = "open_stage, close_stage, save_open_stage, create_from_gameobject " ;
1315
1416 public static object HandleCommand ( JObject @params )
1517 {
@@ -34,10 +36,8 @@ public static object HandleCommand(JObject @params)
3436 return CloseStage ( @params ) ;
3537 case "save_open_stage" :
3638 return SaveOpenStage ( ) ;
37- case "apply_instance_overrides" :
38- return ApplyInstanceOverrides ( @params ) ;
39- case "revert_instance_overrides" :
40- return RevertInstanceOverrides ( @params ) ;
39+ case "create_from_gameobject" :
40+ return CreatePrefabFromGameObject ( @params ) ;
4141 default :
4242 return Response . Error ( $ "Unknown action: '{ action } '. Valid actions are: { SupportedActions } .") ;
4343 }
@@ -51,13 +51,13 @@ public static object HandleCommand(JObject @params)
5151
5252 private static object OpenStage ( JObject @params )
5353 {
54- string path = @params [ "path " ] ? . ToString ( ) ;
55- if ( string . IsNullOrEmpty ( path ) )
54+ string prefabPath = @params [ "prefabPath " ] ? . ToString ( ) ;
55+ if ( string . IsNullOrEmpty ( prefabPath ) )
5656 {
5757 return Response . Error ( "'path' parameter is required for open_stage." ) ;
5858 }
5959
60- string sanitizedPath = AssetPathUtility . SanitizeAssetPath ( path ) ;
60+ string sanitizedPath = AssetPathUtility . SanitizeAssetPath ( prefabPath ) ;
6161 GameObject prefabAsset = AssetDatabase . LoadAssetAtPath < GameObject > ( sanitizedPath ) ;
6262 if ( prefabAsset == null )
6363 {
@@ -125,119 +125,120 @@ private static void SaveStagePrefab(PrefabStage stage)
125125 }
126126 }
127127
128- private static object ApplyInstanceOverrides ( JObject @params )
128+ private static object CreatePrefabFromGameObject ( JObject @params )
129129 {
130- if ( ! TryGetPrefabInstance ( @params , out GameObject instanceRoot , out string error ) )
130+ string targetName = @params [ "target" ] ? . ToString ( ) ?? @params [ "name" ] ? . ToString ( ) ;
131+ if ( string . IsNullOrEmpty ( targetName ) )
131132 {
132- return Response . Error ( error ) ;
133+ return Response . Error ( "'target' parameter is required for create_from_gameobject." ) ;
133134 }
134135
135- PrefabUtility . ApplyPrefabInstance ( instanceRoot , InteractionMode . AutomatedAction ) ;
136- string prefabAssetPath = PrefabUtility . GetPrefabAssetPathOfNearestInstanceRoot ( instanceRoot ) ;
136+ bool includeInactive = @params [ "searchInactive" ] ? . ToObject < bool > ( ) ?? false ;
137+ GameObject sourceObject = FindSceneObjectByName ( targetName , includeInactive ) ;
138+ if ( sourceObject == null )
139+ {
140+ return Response . Error ( $ "GameObject '{ targetName } ' not found in the active scene.") ;
141+ }
137142
138- return Response . Success (
139- $ "Applied overrides on prefab instance '{ instanceRoot . name } '.",
140- new
141- {
142- prefabAssetPath ,
143- instanceId = instanceRoot . GetInstanceID ( )
144- }
145- ) ;
146- }
143+ if ( PrefabUtility . IsPartOfPrefabAsset ( sourceObject ) )
144+ {
145+ return Response . Error (
146+ $ "GameObject '{ sourceObject . name } ' is part of a prefab asset. Open the prefab stage to save changes instead."
147+ ) ;
148+ }
147149
148- private static object RevertInstanceOverrides ( JObject @params )
149- {
150- if ( ! TryGetPrefabInstance ( @params , out GameObject instanceRoot , out string error ) )
150+ PrefabInstanceStatus status = PrefabUtility . GetPrefabInstanceStatus ( sourceObject ) ;
151+ if ( status != PrefabInstanceStatus . NotAPrefab )
151152 {
152- return Response . Error ( error ) ;
153+ return Response . Error (
154+ $ "GameObject '{ sourceObject . name } ' is already linked to an existing prefab instance."
155+ ) ;
153156 }
154157
155- PrefabUtility . RevertPrefabInstance ( instanceRoot , InteractionMode . AutomatedAction ) ;
158+ string requestedPath = @params [ "prefabPath" ] ? . ToString ( ) ?? @params [ "path" ] ? . ToString ( ) ;
159+ if ( string . IsNullOrWhiteSpace ( requestedPath ) )
160+ {
161+ return Response . Error ( "'prefabPath' (or 'path') parameter is required for create_from_gameobject." ) ;
162+ }
156163
157- return Response . Success (
158- $ "Reverted overrides on prefab instance '{ instanceRoot . name } '.",
159- new
160- {
161- instanceId = instanceRoot . GetInstanceID ( )
162- }
163- ) ;
164- }
164+ string sanitizedPath = AssetPathUtility . SanitizeAssetPath ( requestedPath ) ;
165+ if ( ! sanitizedPath . EndsWith ( ".prefab" , StringComparison . OrdinalIgnoreCase ) )
166+ {
167+ sanitizedPath += ".prefab" ;
168+ }
165169
166- private static bool TryGetPrefabInstance ( JObject @params , out GameObject instanceRoot , out string error )
167- {
168- instanceRoot = null ;
169- error = null ;
170+ bool allowOverwrite = @params [ "allowOverwrite" ] ? . ToObject < bool > ( ) ?? false ;
171+ string finalPath = sanitizedPath ;
170172
171- JToken instanceIdToken = @params [ "instanceId" ] ?? @params [ "instanceID" ] ;
172- if ( instanceIdToken != null && instanceIdToken . Type == JTokenType . Integer )
173+ if ( ! allowOverwrite && AssetDatabase . LoadAssetAtPath < UnityEngine . Object > ( finalPath ) != null )
173174 {
174- int instanceId = instanceIdToken . Value < int > ( ) ;
175- if ( ! TryResolveInstance ( instanceId , out instanceRoot , out error ) )
176- {
177- return false ;
178- }
179- return true ;
175+ finalPath = AssetDatabase . GenerateUniqueAssetPath ( finalPath ) ;
180176 }
181177
182- string targetName = @params [ "target" ] ? . ToString ( ) ;
183- if ( ! string . IsNullOrEmpty ( targetName ) )
178+ EnsureAssetDirectoryExists ( finalPath ) ;
179+
180+ try
184181 {
185- GameObject target = GameObject . Find ( targetName ) ;
186- if ( target == null )
182+ GameObject connectedInstance = PrefabUtility . SaveAsPrefabAssetAndConnect (
183+ sourceObject ,
184+ finalPath ,
185+ InteractionMode . AutomatedAction
186+ ) ;
187+
188+ if ( connectedInstance == null )
187189 {
188- error = $ "GameObject '{ targetName } ' not found in the current scene.";
189- return false ;
190+ return Response . Error ( $ "Failed to save prefab asset at '{ finalPath } '.") ;
190191 }
191192
192- instanceRoot = GetPrefabInstanceRoot ( target , out error ) ;
193- return instanceRoot != null ;
194- }
195-
196- error = "Parameter 'instanceId' (or 'target') is required for this action." ;
197- return false ;
198- }
199-
200- private static bool TryResolveInstance ( int instanceId , out GameObject instanceRoot , out string error )
201- {
202- instanceRoot = null ;
203- error = null ;
193+ Selection . activeGameObject = connectedInstance ;
204194
205- GameObject obj = EditorUtility . InstanceIDToObject ( instanceId ) as GameObject ;
206- if ( obj == null )
195+ return Response . Success (
196+ $ "Prefab created at '{ finalPath } ' and instance linked.",
197+ new
198+ {
199+ prefabPath = finalPath ,
200+ instanceId = connectedInstance . GetInstanceID ( )
201+ }
202+ ) ;
203+ }
204+ catch ( Exception e )
207205 {
208- error = $ "No GameObject found for instanceId { instanceId } .";
209- return false ;
206+ return Response . Error ( $ "Error saving prefab asset at '{ finalPath } ': { e . Message } ") ;
210207 }
211-
212- instanceRoot = GetPrefabInstanceRoot ( obj , out error ) ;
213- return instanceRoot != null ;
214208 }
215209
216- private static GameObject GetPrefabInstanceRoot ( GameObject obj , out string error )
210+ private static void EnsureAssetDirectoryExists ( string assetPath )
217211 {
218- error = null ;
219-
220- if ( ! PrefabUtility . IsPartOfPrefabInstance ( obj ) )
212+ string directory = Path . GetDirectoryName ( assetPath ) ;
213+ if ( string . IsNullOrEmpty ( directory ) )
221214 {
222- error = $ "GameObject '{ obj . name } ' is not part of a prefab instance.";
223- return null ;
215+ return ;
224216 }
225217
226- GameObject root = PrefabUtility . GetOutermostPrefabInstanceRoot ( obj ) ;
227- if ( root == null )
218+ string fullDirectory = Path . Combine ( Directory . GetCurrentDirectory ( ) , directory ) ;
219+ if ( ! Directory . Exists ( fullDirectory ) )
228220 {
229- error = $ "Failed to resolve prefab instance root for ' { obj . name } '." ;
230- return null ;
221+ Directory . CreateDirectory ( fullDirectory ) ;
222+ AssetDatabase . Refresh ( ) ;
231223 }
224+ }
232225
233- PrefabInstanceStatus status = PrefabUtility . GetPrefabInstanceStatus ( root ) ;
234- if ( status == PrefabInstanceStatus . NotAPrefab )
226+ private static GameObject FindSceneObjectByName ( string name , bool includeInactive )
227+ {
228+ Scene activeScene = SceneManager . GetActiveScene ( ) ;
229+ foreach ( GameObject root in activeScene . GetRootGameObjects ( ) )
235230 {
236- error = $ "GameObject '{ obj . name } ' is not recognised as a prefab instance.";
237- return null ;
231+ foreach ( Transform transform in root . GetComponentsInChildren < Transform > ( includeInactive ) )
232+ {
233+ GameObject candidate = transform . gameObject ;
234+ if ( candidate . name == name )
235+ {
236+ return candidate ;
237+ }
238+ }
238239 }
239240
240- return root ;
241+ return null ;
241242 }
242243
243244 private static object SerializeStage ( PrefabStage stage )
0 commit comments