private void FindCrossPrefabReferences() { _crossPrefabReferences = new Dictionary <GameObject, List <GameObject> >(); _nestedPrefabs = new Dictionary <GameObject, List <GameObject> >(); foreach (var rootPrefab in _loadedPrefabs) { List <GameObject> foundNestedPrefabs = rootPrefab.CheckHierarchyForNestedPrefabs(); if (foundNestedPrefabs.Count() > 0) { _nestedPrefabs.Add(rootPrefab, foundNestedPrefabs); bool foundErrors = false; MultilineStringBuilder msb = new MultilineStringBuilder($"Cross Prefab Reference serach, load failures f or <<{rootPrefab.name}>>"); foreach (var nestedPrefab in foundNestedPrefabs) { var overrides = PrefabUtility.GetObjectOverrides(nestedPrefab); //TODO This has to be done with msb logger instead //TODO Note there are obviously many overrides so this may cause problewms if (overrides.Count > 0) { //Debug.Log($"{rootPrefab} has overrides at {nestedPrefab}"); } //! This has to be done since nested prefabs are separate instances // Than those we loaded with LoadAllPrefabs var loadedInstance = _loadedPrefabs.FindInstanceOfTheSamePrefab(nestedPrefab); if (loadedInstance == null) { foundErrors = true; msb.AddLine($"Failed to find {nestedPrefab.name} in loadedPrefabs"); continue; } if (!_crossPrefabReferences.ContainsKey(loadedInstance)) { _crossPrefabReferences[loadedInstance] = new List <GameObject>(); } _crossPrefabReferences[loadedInstance].Add(rootPrefab); } if (foundErrors) { Debug.LogError(msb.ToString()); } } } }
// // ─── ANALYSIS ──────────────────────────────────────────────────── // #region ANALYSIS private void AnalyzePrefab(GameObject originalPrefab, MultilineStringBuilder msb, ref int currentDepth) { // Skips execition if there were no text components found in the hierarchy if (!originalPrefab.TryGetComponentsInChildren(out List <Text> localTextComponents, skipNestedPrefabs: true)) { return; } else { currentDepth++; } bool logthisone = true; foreach (Text text in localTextComponents) { string originalPrefabPath = AssetDatabase.GetAssetPath(originalPrefab); //* Internal text component references SaveTextReferences(originalPrefabPath, originalPrefab, text); // 2. Text components referenced by nested prefabs // 3. Text components of prefabs that are reference by the other nested prefabs // X???. Text components of prefabs that are referenced by parent of a parent OR by a nested prefab of a parent of a parent OOF //* Foreign text component references // Get all instances of a Text component along with the parentPrefab that is holding them // It is a list of Text components because parentPrefab may hold multiple references to that Text Component foreach (var kvp in GetAllTextInstances(originalPrefab, text)) { // * Clean this up, extract the recurring method GameObject instanceParentPreafab = kvp.Key; List <Text> textComponentInstances = kvp.Value; foreach (Text textInstance in textComponentInstances) { SaveTextReferences(originalPrefabPath, instanceParentPreafab, textInstance); // ? This is because the counter will _replaceCounter.updatedTextComponentCount++; } } } if (logthisone) { PrintPrefabAnalysis(originalPrefab, msb, localTextComponents); } }
private void LogScriptReferences(MultilineStringBuilder msb, int logDepth = 30) { int depth = 0; foreach (var c in _scriptReferences) { msb.AddLine(new[] { "mono: ", c.Key.Name, " has reference count: ", c.Value.Count.ToString() }); foreach (var reference in c.Value) { msb.AddLine(new[] { "---> ", reference.name }); } if (depth > logDepth) { return; } depth++; } }
private void PrintPrefabAnalysis(GameObject prefab, MultilineStringBuilder msb, List <Text> localTextComponents) { msb.AddLine($"Analysis of {prefab.name}"); if (_nestedPrefabs.ContainsKey(prefab) && _nestedPrefabs[prefab].Count > 0) { msb.AddLine($"Has nested prefabs:"); foreach (var n in _nestedPrefabs[prefab]) { msb.AddLine($"---> {n}"); } } if (localTextComponents.Count > 0) { msb.AddLine($"Has text components:"); foreach (var n in localTextComponents) { msb.AddLine($"---> {n.gameObject}"); } } if (_scriptsByPrefab[prefab].Count > 0) { msb.AddLine($"Has custom monobehaviours:"); foreach (Type monoType in _scriptsByPrefab[prefab]) { msb.AddLine($"---> {monoType.Name}"); } } if (_crossPrefabReferences.ContainsKey(prefab)) { msb.AddLine($"Is referenced by other prefabs:"); foreach (GameObject referencer in _crossPrefabReferences[prefab]) { msb.AddLine($"---> by: {referencer}"); } } msb.AddSeparator(); }
// // ─── LOGGING ──────────────────────────────────────────────────────────────────── // #region LOGGING private void DrawLoggingButtons(VisualElement root) { var label = new Label() { text = "Logging" }; var box = new Box(); box.style.height = StyleKeyword.Auto; box.style.overflow = Overflow.Visible; root.Add(label); root.Add(box); IntegerField loggingDepthField = new IntegerField("Logging Depth"); loggingDepthField.value = 30; box.Add(loggingDepthField); var logOverridesButton = new Button(() => { var msb = new MultilineStringBuilder("Log nested prefabs with overrides"); foreach (var item in _nestedPrefabs) { bool loggedOverride = false; foreach (var nested in item.Value) { bool hasOverrides = PrefabUtility.HasPrefabInstanceAnyOverrides(nested, false); if (hasOverrides && loggedOverride == false) { loggedOverride = true; msb.AddLine($"{item.Key.gameObject.name} has overrides at:"); } if (hasOverrides) { msb.AddLine($"---> {nested.name}"); } } loggedOverride = false; } DisplayInBox(GetTextElement(msb.ToString())); }) { text = "Log overrides" }; box.Add(logOverridesButton); var logCrossReferences = new Button(() => { var msb = new MultilineStringBuilder("Log Cross References"); LogCrossReferences(msb, loggingDepthField.value); DisplayInBox(GetTextElement(msb.ToString())); }) { text = "Log Cross References" }; box.Add(logCrossReferences); var logScriptReferencesButton = new Button(() => { var msb = new MultilineStringBuilder("Log Script References"); LogScriptReferences(msb, loggingDepthField.value); DisplayInBox(GetTextElement(msb.ToString())); }) { text = "Log Script References" }; box.Add(logScriptReferencesButton); // TODO this is dead var logUnhandledReferences = new Button(() => { var msb = new MultilineStringBuilder("Unhandled script references"); foreach (var kvp in _textFieldsByMonobehaviour) { msb.AddLine(new string[] { kvp.Key.GetType().ToString(), " from ", _customMonobehavioursByPrefab.First(slot => slot.Value.Contains(kvp.Key)).Key.ToString() }); foreach (var unhandledField in kvp.Value) { msb.AddLine($"---> {unhandledField.Name}"); } } DisplayInBox(GetTextElement(msb.ToString())); }) { text = "Unhandled script references" }; box.Add(logUnhandledReferences); }
// // ─── EDITOR DRAWING ────────────────────────────────────────────── // #region Editor Drawing private void DrawReplacerMenu(VisualElement root) { var container = new Box(); root.Add(container); var label = new Label() { text = "Replacer" }; container.Add(label); _selectedPrefabsField = new ObjectField("Selected prefabs") { objectType = typeof(SelectedPrefabsBook) }; container.Add(_selectedPrefabsField); _excludedPrefabsField = new ObjectField("Excluded prefabs") { objectType = typeof(SelectedPrefabsBook) }; _excludedPrefabsField.value = AssetDatabase.LoadAssetAtPath(EXCLUDED_PREFABS_ASSET_PATH, typeof(SelectedPrefabsBook)); container.Add(_excludedPrefabsField); Button initializeButton = new Button(() => { //* Where to search for prefabs (depending on whether we make a backup or not) // ! Prefab backup abandoned UpdatedReferenceAddressBook.ClearAddressBook(); LoadPrefabs(); FindCrossPrefabReferences(); FindScriptReferences(); UpgradeProgressBar.Invoke(); }) { text = "Initialize" }; container.Add(initializeButton); IntegerField analysisDepth = new IntegerField("Prefab Analysis Depth"); analysisDepth.value = FabulousTextComponentReplacer.WORK_DEPTH; container.Add(analysisDepth); var analysePrefabsButton = new Button() { text = "Analyse prefabs" }; container.Add(analysePrefabsButton); var updateScriptsButton = new Button() { text = "Update scripts" }; _scriptUpdater = new ScriptUpdater( UpdatedReferenceAddressBook, updateScriptsButton); container.Add(updateScriptsButton); var updateComponentsButton = new Button() { text = "Update components" }; _componentReplacer = new ComponentReplacer( UpdatedReferenceAddressBook, updateComponentsButton); container.Add(updateComponentsButton); analysePrefabsButton.clicked += () => { List <string> analysisResultsParts = new List <string>(); int currentDepth = 0; var msb = new MultilineStringBuilder("1 - Prefab analysis"); foreach (var prefab in _loadedPrefabs) { if (msb.Length > 5000) { analysisResultsParts.Add(msb.ToString()); msb = new MultilineStringBuilder($"{analysisResultsParts.Count + 1} - Prefab analysis"); } if (analysisDepth.value != -1 && currentDepth >= analysisDepth.value) { break; } AnalyzePrefab(prefab, msb, ref currentDepth); } analysisResultsParts.Add(msb.ToString()); UpgradeProgressBar.Invoke(); DisplayInBox(GetTextBlock(analysisResultsParts)); }; }