Skip to content

Commit 26e0946

Browse files
fix
This fixes the issue with the GlobalObjectId value being incorrect when entering into play mode while still in prefab edit mode (InContext or InIsolation).
1 parent f170341 commit 26e0946

File tree

1 file changed

+131
-7
lines changed

1 file changed

+131
-7
lines changed

com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)