#if UNITY_EDITOR using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; namespace Unity.Netcode { /// /// This is a helper tool to update all in-scene placed instances of a prefab that /// originally did not have a NetworkObject component but one was added to the prefab /// later. /// internal class NetworkObjectRefreshTool { private static List s_ScenesToUpdate = new List(); private static bool s_ProcessScenes; private static bool s_CloseScenes; internal static Action AllScenesProcessed; internal static NetworkObject PrefabNetworkObject; internal static void LogInfo(string msg, bool append = false) { if (!append) { s_Log.AppendLine(msg); } else { s_Log.Append(msg); } } internal static void FlushLog() { Debug.Log(s_Log.ToString()); s_Log.Clear(); } private static StringBuilder s_Log = new StringBuilder(); internal static void ProcessScene(string scenePath, bool processScenes = true) { if (!s_ScenesToUpdate.Contains(scenePath)) { if (s_ScenesToUpdate.Count == 0) { EditorSceneManager.sceneOpened += EditorSceneManager_sceneOpened; EditorSceneManager.sceneSaved += EditorSceneManager_sceneSaved; s_Log.Clear(); LogInfo("NetworkObject Refresh Scenes to Process:"); } LogInfo($"[{scenePath}]", true); s_ScenesToUpdate.Add(scenePath); } s_ProcessScenes = processScenes; } internal static void ProcessActiveScene() { FlushLog(); var activeScene = SceneManager.GetActiveScene(); if (s_ScenesToUpdate.Contains(activeScene.path) && s_ProcessScenes) { SceneOpened(activeScene); } } internal static void ProcessScenes() { if (s_ScenesToUpdate.Count != 0) { s_CloseScenes = true; var scenePath = s_ScenesToUpdate.First(); EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive); } else { s_ProcessScenes = false; s_CloseScenes = false; EditorSceneManager.sceneSaved -= EditorSceneManager_sceneSaved; EditorSceneManager.sceneOpened -= EditorSceneManager_sceneOpened; AllScenesProcessed?.Invoke(); FlushLog(); } } private static void FinishedProcessingScene(Scene scene, bool refreshed = false) { if (s_ScenesToUpdate.Contains(scene.path)) { // Provide a log of all scenes that were modified to the user if (refreshed) { LogInfo($"Refreshed and saved updates to scene: {scene.name}"); } s_ScenesToUpdate.Remove(scene.path); if (scene != SceneManager.GetActiveScene()) { EditorSceneManager.CloseScene(scene, s_CloseScenes); } ProcessScenes(); } } private static void EditorSceneManager_sceneSaved(Scene scene) { FinishedProcessingScene(scene, true); } private static void SceneOpened(Scene scene) { LogInfo($"Processing scene {scene.name}:"); if (s_ScenesToUpdate.Contains(scene.path)) { if (s_ProcessScenes) { var prefabInstances = PrefabUtility.FindAllInstancesOfPrefab(PrefabNetworkObject.gameObject); if (prefabInstances.Length > 0) { var instancesSceneLoadedSpecific = prefabInstances.Where((c) => c.scene == scene).ToList(); if (instancesSceneLoadedSpecific.Count > 0) { foreach (var prefabInstance in instancesSceneLoadedSpecific) { prefabInstance.GetComponent().OnValidate(); } if (!EditorSceneManager.MarkSceneDirty(scene)) { LogInfo($"Scene {scene.name} did not get marked as dirty!"); FinishedProcessingScene(scene); } else { LogInfo($"Changes detected and applied!"); EditorSceneManager.SaveScene(scene); } return; } } } LogInfo($"No changes required."); FinishedProcessingScene(scene); } } private static void EditorSceneManager_sceneOpened(Scene scene, OpenSceneMode mode) { SceneOpened(scene); } } } #endif // UNITY_EDITOR