@@ -102,6 +102,14 @@ public uint PrefabIdHash
102102 private const int k_SceneObjectType = 2 ;
103103 private const int k_SourceAssetObjectType = 3 ;
104104
105+ // Used to track any InContext or InIsolation prefab being edited
106+ private static PrefabStage s_PrefabStage ;
107+ // The network prefab asset that the edit mode scene has created an instance of (s_PrefabInstance)
108+ private static NetworkObject s_PrefabAsset ;
109+ // The InContext or InIsolation edit mode network prefab scene instance of the prefab asset (s_PrefabAsset)
110+ private static NetworkObject s_PrefabInstance ;
111+
112+
105113 [ ContextMenu ( "Refresh In-Scene Prefab Instances" ) ]
106114 internal void RefreshAllPrefabInstances ( )
107115 {
@@ -134,8 +142,124 @@ internal void RefreshAllPrefabInstances()
134142 NetworkObjectRefreshTool . ProcessScenes ( ) ;
135143 }
136144
145+ /// <summary>
146+ /// Register for <see cref="PrefabStage"/> opened and closing event notifications
147+ /// </summary>
148+ [ InitializeOnLoadMethod ]
149+ private static void OnApplicationStart ( )
150+ {
151+ PrefabStage . prefabStageOpened -= PrefabStageOpened ;
152+ PrefabStage . prefabStageOpened += PrefabStageOpened ;
153+ PrefabStage . prefabStageClosing -= PrefabStageClosing ;
154+ PrefabStage . prefabStageClosing += PrefabStageClosing ;
155+ }
156+
157+ private static void PrefabStageClosing ( PrefabStage prefabStage )
158+ {
159+ // If domain reloading is enabled, then this will be null when we return from playmode.
160+ if ( s_PrefabStage == null )
161+ {
162+ // Determine if we have a network prefab opened in edit mode or not
163+ CheckPrefabStage ( prefabStage ) ;
164+ }
165+
166+ // TODO: Remove before merging PR
167+ // If we had a prefab opened in isolation mode, then notify we are closing the prefab
168+ if ( s_PrefabStage && s_PrefabAsset )
169+ {
170+ Debug . Log ( $ "Prefab { s_PrefabAsset . name } is exiting prefab { s_PrefabStage . mode } edit mode.") ;
171+ }
172+
173+ s_PrefabStage = null ;
174+ s_PrefabInstance = null ;
175+ s_PrefabAsset = null ;
176+ }
177+
178+ private static void PrefabStageOpened ( PrefabStage prefabStage )
179+ {
180+ // Determine if we have a network prefab opened in edit mode or not
181+ CheckPrefabStage ( prefabStage ) ;
182+ }
183+
184+ /// <summary>
185+ /// Determines if we have opened a network prefab in edit mode (InContext or InIsolation)
186+ /// </summary>
187+ /// <remarks>
188+ /// InContext: Typically means a are in prefab edit mode for an in-scene placed network prefab instance.
189+ /// (currently no such thing as a network prefab with nested network prefab instances)
190+ ///
191+ /// InIsolation: Typically means we are in prefb edit mode for a prefab asset.
192+ /// </remarks>
193+ /// <param name="prefabStage"></param>
194+ private static void CheckPrefabStage ( PrefabStage prefabStage )
195+ {
196+ s_PrefabStage = prefabStage ;
197+ s_PrefabInstance = prefabStage . prefabContentsRoot ? . GetComponent < NetworkObject > ( ) ;
198+ if ( s_PrefabInstance )
199+ {
200+ Debug . Log ( $ "[{ s_PrefabStage . mode } Mode] NetworkPrefab { s_PrefabInstance . name } ({ s_PrefabInstance . GlobalObjectIdHash } ) was opened in edit mode.") ;
201+
202+ if ( s_PrefabStage . mode == PrefabStage . Mode . InContext && s_PrefabStage . openedFromInstanceRoot != null )
203+ {
204+ // This is needed to handle the scenario where a user completely loads a new scene while in an InContext prefab edit mode.
205+ try
206+ {
207+ s_PrefabAsset = s_PrefabStage . openedFromInstanceRoot ? . GetComponent < NetworkObject > ( ) ;
208+ }
209+ catch
210+ {
211+ s_PrefabAsset = null ;
212+ }
213+ }
214+ else
215+ {
216+ // When editing in InIsolation mode, load the original prefab asset
217+ s_PrefabAsset = AssetDatabase . LoadAssetAtPath < NetworkObject > ( s_PrefabStage . assetPath ) ;
218+ }
219+
220+ if ( s_PrefabAsset )
221+ {
222+ Debug . Log ( $ "NetworkPrefab { s_PrefabAsset . name } ({ s_PrefabAsset . GlobalObjectIdHash } ) is the { nameof ( SceneAsset ) } opened in { s_PrefabStage . mode } edit mode.") ;
223+ }
224+ if ( s_PrefabInstance . GlobalObjectIdHash != s_PrefabAsset . GlobalObjectIdHash )
225+ {
226+ s_PrefabInstance . GlobalObjectIdHash = s_PrefabAsset . GlobalObjectIdHash ;
227+ // For InContext mode, we don't want to record these modifications (the in-scene GlobalObjectIdHash is serialized with the scene)
228+ if ( s_PrefabStage . mode == PrefabStage . Mode . InIsolation )
229+ {
230+ PrefabUtility . RecordPrefabInstancePropertyModifications ( s_PrefabAsset ) ;
231+ }
232+ }
233+ }
234+ else
235+ {
236+ s_PrefabStage = null ;
237+ s_PrefabInstance = null ;
238+ s_PrefabAsset = null ;
239+ }
240+ }
241+
242+ /// <summary>
243+ /// Returns if the current NetworkObject being validated is a component of an edit mode instance or not
244+ /// </summary>
245+ /// <returns></returns>
246+ private bool IsPrefabOpenedForEditing ( )
247+ {
248+ if ( ! s_PrefabStage )
249+ {
250+ return false ;
251+ }
252+ return s_PrefabInstance == this ;
253+ }
254+
137255 internal void OnValidate ( )
138256 {
257+ // Always exit early if we are in prefab edit mode.
258+ if ( IsPrefabOpenedForEditing ( ) )
259+ {
260+ return ;
261+ }
262+
139263 // do NOT regenerate GlobalObjectIdHash for NetworkPrefabs while Editor is in PlayMode
140264 if ( EditorApplication . isPlaying && ! string . IsNullOrEmpty ( gameObject . scene . name ) )
141265 {
@@ -149,8 +273,7 @@ internal void OnValidate()
149273 }
150274
151275 // Get a global object identifier for this network prefab
152- var globalId = GetGlobalId ( ) ;
153-
276+ var globalId = GlobalObjectId . GetGlobalObjectIdSlow ( this ) ;
154277
155278 // if the identifier type is 0, then don't update the GlobalObjectIdHash
156279 if ( globalId . identifierType == k_NullObjectType )
@@ -161,11 +284,14 @@ internal void OnValidate()
161284 var oldValue = GlobalObjectIdHash ;
162285 GlobalObjectIdHash = globalId . ToString ( ) . Hash32 ( ) ;
163286
287+ // Always check for in-scene placed to assure any previous version scene assets with in-scene place NetworkObjects gets updated
288+ CheckForInScenePlaced ( ) ;
289+
164290 // If the GlobalObjectIdHash value changed, then mark the asset dirty
165291 if ( GlobalObjectIdHash != oldValue )
166292 {
167293 // Check if this is an in-scnee placed NetworkObject (Special Case for In-Scene Placed)
168- if ( ! IsEditingPrefab ( ) && gameObject . scene . name != null && gameObject . scene . name != gameObject . name )
294+ if ( IsSceneObject . Value )
169295 {
170296 // Sanity check to make sure this is a scene placed object
171297 if ( globalId . identifierType != k_SceneObjectType )
@@ -186,9 +312,6 @@ internal void OnValidate()
186312 EditorUtility . SetDirty ( this ) ;
187313 }
188314 }
189-
190- // Always check for in-scene placed to assure any previous version scene assets with in-scene place NetworkObjects gets updated
191- CheckForInScenePlaced ( ) ;
192315 }
193316
194317 private bool IsEditingPrefab ( )
@@ -201,6 +324,7 @@ private bool IsEditingPrefab()
201324 {
202325 return false ;
203326 }
327+ Debug . LogWarning ( $ "{ name } is returning true for the legacy IsEditingPrefab method!") ;
204328 return true ;
205329 }
206330
@@ -218,7 +342,7 @@ private bool IsEditingPrefab()
218342 /// </remarks>
219343 private void CheckForInScenePlaced ( )
220344 {
221- if ( PrefabUtility . IsPartOfAnyPrefab ( this ) && ! IsEditingPrefab ( ) && gameObject . scene . IsValid ( ) && gameObject . scene . isLoaded && gameObject . scene . buildIndex >= 0 )
345+ if ( PrefabUtility . IsPartOfAnyPrefab ( this ) && gameObject . scene . IsValid ( ) && gameObject . scene . isLoaded && gameObject . scene . buildIndex >= 0 )
222346 {
223347 var prefab = PrefabUtility . GetCorrespondingObjectFromSource ( gameObject ) ;
224348 var assetPath = AssetDatabase . GetAssetPath ( prefab ) ;
0 commit comments