LogEntry.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using Object = UnityEngine.Object;
  6. namespace EnhancedHierarchy {
  7. /// <summary>
  8. /// Log Entries from the console, to check if a game object has any errors or warnings.
  9. /// </summary>
  10. public sealed class LogEntry {
  11. private const double UPDATE_FREQUENCY = 0.75; // Every 750ms
  12. private static readonly Type logEntriesType;
  13. private static readonly Type logEntryType;
  14. public int RowIndex { get; private set; }
  15. public string Condition { get; private set; }
  16. public int ErrorNum { get; private set; }
  17. public string File { get; private set; }
  18. public int Line { get; private set; }
  19. public int Column { get; private set; }
  20. public EntryMode Mode { get; private set; }
  21. public int InstanceID { get; private set; }
  22. public int Identifier { get; private set; }
  23. public Object ObjectReference { get; private set; }
  24. public MonoScript Script { get; private set; }
  25. public Type ClassType { get; private set; }
  26. public static Dictionary<GameObject, List<LogEntry>> gameObjectEntries = new Dictionary<GameObject, List<LogEntry>>(100);
  27. public static List<LogEntry> compileEntries = new List<LogEntry>(100);
  28. private static int lastCount;
  29. private static bool entriesDirty;
  30. private static bool lastCompileFailedState;
  31. private static double lastUpdatedTime;
  32. private static readonly Icons.Warnings warnings = new Icons.Warnings();
  33. static LogEntry() {
  34. try {
  35. logEntriesType = ReflectionHelper.FindType("UnityEditorInternal.LogEntries");
  36. logEntryType = ReflectionHelper.FindType("UnityEditorInternal.LogEntry");
  37. if (logEntriesType == null)
  38. logEntriesType = ReflectionHelper.FindType("UnityEditor.LogEntries");
  39. if (logEntryType == null)
  40. logEntryType = ReflectionHelper.FindType("UnityEditor.LogEntry");
  41. ReloadReferences();
  42. } catch (Exception e) {
  43. Debug.LogException(e);
  44. Preferences.ForceDisableButton(new Icons.Warnings());
  45. }
  46. Application.logMessageReceived += (logString, stackTrace, type) => MarkEntriesDirty();
  47. EditorApplication.update += () => {
  48. try {
  49. #if UNITY_2017_1_OR_NEWER
  50. if (!entriesDirty && EditorUtility.scriptCompilationFailed != lastCompileFailedState) {
  51. lastCompileFailedState = EditorUtility.scriptCompilationFailed;
  52. MarkEntriesDirty();
  53. }
  54. #endif
  55. if (EditorApplication.timeSinceStartup - lastUpdatedTime > UPDATE_FREQUENCY) {
  56. if (!entriesDirty) {
  57. var currentCount = GetLogCount();
  58. if (lastCount > currentCount) { // Console possibly cleared
  59. if (Preferences.DebugEnabled)
  60. Debug.Log("Detected console clear");
  61. MarkEntriesDirty();
  62. }
  63. lastCount = currentCount;
  64. }
  65. if (entriesDirty)
  66. ReloadReferences();
  67. }
  68. } catch (Exception e) {
  69. Debug.LogException(e);
  70. Preferences.ForceDisableButton(new Icons.Warnings());
  71. }
  72. };
  73. }
  74. private LogEntry(object nativeEntry, int rowIndex) {
  75. RowIndex = rowIndex;
  76. if (nativeEntry.HasField("condition"))
  77. Condition = nativeEntry.GetInstanceField<string>("condition");
  78. else if (nativeEntry.HasField("message"))
  79. Condition = nativeEntry.GetInstanceField<string>("message");
  80. else
  81. throw new MissingFieldException("LogEntry doesn't have a message field");
  82. if (nativeEntry.HasField("errorNum"))
  83. ErrorNum = nativeEntry.GetInstanceField<int>("errorNum");
  84. File = nativeEntry.GetInstanceField<string>("file");
  85. Line = nativeEntry.GetInstanceField<int>("line");
  86. if (nativeEntry.HasField("column"))
  87. Column = nativeEntry.GetInstanceField<int>("column");
  88. Mode = nativeEntry.GetInstanceField<EntryMode>("mode");
  89. InstanceID = nativeEntry.GetInstanceField<int>("instanceID");
  90. Identifier = nativeEntry.GetInstanceField<int>("identifier");
  91. if (InstanceID != 0)
  92. ObjectReference = EditorUtility.InstanceIDToObject(InstanceID);
  93. if (ObjectReference)
  94. Script = ObjectReference as MonoScript;
  95. if (Script)
  96. ClassType = Script.GetClass();
  97. }
  98. public static void MarkEntriesDirty() {
  99. if (!entriesDirty && Preferences.Enabled && Preferences.IsButtonEnabled(warnings))
  100. entriesDirty = true;
  101. }
  102. private static void ReloadReferences() {
  103. if (Preferences.DebugEnabled)
  104. Debug.Log("Reloading Logs References");
  105. gameObjectEntries.Clear();
  106. compileEntries.Clear();
  107. try {
  108. var count = logEntriesType.InvokeStaticMethod<int>("StartGettingEntries");
  109. var nativeEntry = Activator.CreateInstance(logEntryType);
  110. for (var i = 0; i < count; i++) {
  111. logEntriesType.InvokeStaticMethod("GetEntryInternal", i, nativeEntry);
  112. var proxyEntry = new LogEntry(nativeEntry, i);
  113. var go = proxyEntry.ObjectReference as GameObject;
  114. if (proxyEntry.ObjectReference && !go) {
  115. var component = proxyEntry.ObjectReference as Component;
  116. if (component)
  117. go = component.gameObject;
  118. }
  119. // if(entry.HasMode(EntryMode.ScriptCompileError | EntryMode.ScriptCompileWarning | EntryMode.AssetImportWarning) && entry.ClassType != null)
  120. // if(!referencedComponents.Any(e => e.ClassType == entry.ClassType))
  121. if (proxyEntry.ClassType != null)
  122. compileEntries.Add(proxyEntry);
  123. if (go)
  124. if (gameObjectEntries.ContainsKey(go))
  125. gameObjectEntries[go].Add(proxyEntry);
  126. else
  127. gameObjectEntries.Add(go, new List<LogEntry>() { proxyEntry });
  128. }
  129. EditorApplication.RepaintHierarchyWindow();
  130. } catch (Exception e) {
  131. Debug.LogException(e);
  132. Preferences.ForceDisableButton(new Icons.Warnings());
  133. } finally {
  134. entriesDirty = false;
  135. lastUpdatedTime = EditorApplication.timeSinceStartup;
  136. logEntriesType.InvokeStaticMethod("EndGettingEntries");
  137. }
  138. }
  139. public bool HasMode(EntryMode toCheck) {
  140. return (Mode & toCheck) != 0;
  141. }
  142. public void OpenToEdit() {
  143. logEntriesType.InvokeStaticMethod("RowGotDoubleClicked", RowIndex);
  144. }
  145. private static int GetLogCount() {
  146. return logEntriesType.InvokeStaticMethod<int>("GetCount");
  147. }
  148. public override string ToString() {
  149. return Condition;
  150. }
  151. }
  152. }