using System; using System.Collections.Generic; using System.IO; using System.Reflection; using UnityEditor; using UnityEngine; namespace jeanf.scenemanagement.Editor { /// /// Project-wide version of the SubScene inspector's "Reimport" button: marks every entity /// scene in the import cache dirty so the asset pipeline re-runs baking for all of them, /// instead of only the SubScenes open in the current scene. /// public static class SubSceneReimportTool { private const string LogPrefix = "[SceneManagement]"; // Entities writes one .sceneWithBuildSettings per (scene, build config, editor/player) // combination it has imported; rewriting an entry's DirtyValue is the official reimport trigger. private const string CachePath = "Assets/SceneDependencyCache"; [MenuItem("Tools/Jeanf/SceneManagement/Re-import All SubScenes")] public static void ReimportAllSubScenes() { var files = Directory.Exists(CachePath) ? Directory.GetFiles(CachePath, "*.sceneWithBuildSettings") : Array.Empty(); if (files.Length == 0) { Debug.Log($"{LogPrefix} {CachePath} holds no entity scene cache entries — no subscene has been imported yet, nothing to re-import."); return; } // SceneWithBuildConfigurationGUIDs is internal to Unity.Scenes; ReadFromFile + Dirty are // the same entry points SubSceneInspectorUtility.ForceReimport goes through. var type = typeof(Unity.Scenes.SubScene).Assembly.GetType("Unity.Scenes.SceneWithBuildConfigurationGUIDs"); var readFromFile = type?.GetMethod("ReadFromFile", BindingFlags.Public | BindingFlags.Static); var dirty = type?.GetMethod("Dirty", BindingFlags.Public | BindingFlags.Static); var sceneGuidField = type?.GetField("SceneGUID"); var buildConfigField = type?.GetField("BuildConfiguration"); if (readFromFile == null || dirty == null || sceneGuidField == null || buildConfigField == null) { Debug.LogError($"{LogPrefix} com.unity.entities internals changed (SceneWithBuildConfigurationGUIDs) — cannot force a project-wide reimport. Re-import subscenes one by one via the SubScene inspector's Reimport button instead."); return; } // Dirty() rewrites both the editor and player variant of a (scene, config) pair, so // dedupe to avoid touching each pair twice. var seen = new HashSet<(string scene, string config)>(); var dirtied = 0; try { for (var i = 0; i < files.Length; i++) { EditorUtility.DisplayProgressBar("Re-import All SubScenes", files[i], i / (float)files.Length); object entry; try { entry = readFromFile.Invoke(null, new object[] { files[i] }); } catch (Exception e) { Debug.LogWarning($"{LogPrefix} Skipping unreadable cache entry '{files[i]}': {e.Message}"); continue; } var sceneGuid = sceneGuidField.GetValue(entry); var buildConfig = buildConfigField.GetValue(entry); if (!seen.Add((sceneGuid.ToString(), buildConfig.ToString()))) continue; if ((bool)dirty.Invoke(null, new[] { sceneGuid, buildConfig })) dirtied++; } } finally { EditorUtility.ClearProgressBar(); } if (dirtied > 0) AssetDatabase.Refresh(); Debug.Log($"{LogPrefix} Marked {dirtied} subscene(s) for re-import. Open subscenes rebake now; closed ones rebake the next time they are loaded."); } } }