using System; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEngine.UI; using UnityEngine.Events; using UnityEngine.EventSystems; using qtools.qhierarchy.pcomponent.pbase; using qtools.qhierarchy.phierarchy; using qtools.qhierarchy.phelper; using qtools.qhierarchy.pdata; using System.Reflection; using System.Collections; using UnityEditorInternal; using System.Text; namespace qtools.qhierarchy.pcomponent { public class QErrorComponent: QBaseComponent { // PRIVATE private Texture2D errorIconTexture; private Texture2D errorChildIconTexture; private Color backgroundColor; private bool showErrorOfChildren; private bool showErrorTypeReferenceIsNull; private bool showErrorTypeStringIsEmpty; private bool showErrorIconScriptIsMissing; private bool showErrorIconWhenTagIsUndefined; private bool showErrorForDisabledComponents; private bool showErrorIconMissingEventMethod; private List ignoreErrorOfMonoBehaviours; private StringBuilder errorStringBuilder; private int errorCount; // CONSTRUCTOR public QErrorComponent () { errorIconTexture = QResources.getInstance().getTexture(QTexture.QErrorIcon); errorChildIconTexture = QResources.getInstance().getTexture(QTexture.QErrorChildIcon); backgroundColor = QResources.getInstance().getColor(QColor.Background); QSettings.getInstance().addEventListener(QSetting.ShowErrorIconParent , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorIconReferenceIsNull , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorIconStringIsEmpty , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorIconScriptIsMissing , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorForDisabledComponents , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorIconMissingEventMethod , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorIconWhenTagOrLayerIsUndefined , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorComponent , settingsChanged); QSettings.getInstance().addEventListener(QSetting.ShowErrorComponentDuringPlayMode, settingsChanged); QSettings.getInstance().addEventListener(QSetting.IgnoreErrorOfMonoBehaviours , settingsChanged); settingsChanged(); } // PRIVATE private void settingsChanged() { showErrorOfChildren = QSettings.getInstance().get(QSetting.ShowErrorIconParent); showErrorTypeReferenceIsNull = QSettings.getInstance().get(QSetting.ShowErrorIconReferenceIsNull); showErrorTypeStringIsEmpty = QSettings.getInstance().get(QSetting.ShowErrorIconStringIsEmpty); showErrorIconScriptIsMissing = QSettings.getInstance().get(QSetting.ShowErrorIconScriptIsMissing); showErrorForDisabledComponents = QSettings.getInstance().get(QSetting.ShowErrorForDisabledComponents); showErrorIconMissingEventMethod = QSettings.getInstance().get(QSetting.ShowErrorIconMissingEventMethod); showErrorIconWhenTagIsUndefined = QSettings.getInstance().get(QSetting.ShowErrorIconWhenTagOrLayerIsUndefined); enabled = QSettings.getInstance().get(QSetting.ShowErrorComponent); showComponentDuringPlayMode = QSettings.getInstance().get(QSetting.ShowErrorComponentDuringPlayMode); string ignoreErrorOfMonoBehavioursString = QSettings.getInstance().get(QSetting.IgnoreErrorOfMonoBehaviours); if (ignoreErrorOfMonoBehavioursString != "") { ignoreErrorOfMonoBehaviours = new List(ignoreErrorOfMonoBehavioursString.Split(new char[] { ',', ';', '.', ' ' })); ignoreErrorOfMonoBehaviours.RemoveAll(item => item == ""); } else ignoreErrorOfMonoBehaviours = null; } // DRAW public override void layout(GameObject gameObject, QObjectList objectList, ref Rect rect) { rect.x -= 16; rect.width = 16; } public override void draw(GameObject gameObject, QObjectList objectList, Rect selectionRect, Rect curRect) { bool errorFound = findError(gameObject, gameObject.GetComponents()); EditorGUI.DrawRect(curRect, backgroundColor); if (errorFound) { GUI.DrawTexture(curRect, errorIconTexture); } else if (showErrorOfChildren) { errorFound = findError(gameObject, gameObject.GetComponentsInChildren(true)); if (errorFound) GUI.DrawTexture(curRect, errorChildIconTexture); } } public override void eventHandler(GameObject gameObject, QObjectList objectList, Event currentEvent, Rect curRect) { if (currentEvent.isMouse && currentEvent.type == EventType.MouseDown && currentEvent.button == 0 && curRect.Contains(currentEvent.mousePosition)) { currentEvent.Use(); errorCount = 0; errorStringBuilder = new StringBuilder(); findError(gameObject, gameObject.GetComponents(), true); if (errorCount > 0) { EditorUtility.DisplayDialog(errorCount + (errorCount == 1 ? " error was found" : " errors were found"), errorStringBuilder.ToString(), "OK"); } } } // PRIVATE private bool findError(GameObject gameObject, MonoBehaviour[] components, bool printError = false) { if (showErrorIconWhenTagIsUndefined) { try { gameObject.tag.CompareTo(null); } catch { if (printError) { appendErrorLine("Tag is undefined"); } else { return true; } } if (LayerMask.LayerToName(gameObject.layer).Equals("")) { if (printError) { appendErrorLine("Layer is undefined"); } else { return true; } } } for (int i = 0; i < components.Length; i++) { MonoBehaviour monoBehaviour = components[i]; if (monoBehaviour == null) { if (showErrorIconScriptIsMissing) { if (printError) { appendErrorLine("Component #" + i + " is missing"); } else { return true; } } } else { if (ignoreErrorOfMonoBehaviours != null) { for (int j = ignoreErrorOfMonoBehaviours.Count - 1; j >= 0; j--) { if (monoBehaviour.GetType().FullName.Contains(ignoreErrorOfMonoBehaviours[j])) { return false; } } } if (showErrorIconMissingEventMethod) { if (monoBehaviour.gameObject.activeSelf || showErrorForDisabledComponents) { try { if (isUnityEventsNullOrMissing(monoBehaviour, printError)) { return true; } } catch { } } } if (showErrorTypeReferenceIsNull || showErrorTypeStringIsEmpty) { if (!(monoBehaviour.enabled && monoBehaviour.gameObject.activeSelf) && !showErrorForDisabledComponents) continue; FieldInfo[] fieldArray = monoBehaviour.GetType().GetFields(); for (int j = 0; j < fieldArray.Length; j++) { FieldInfo field = fieldArray[j]; try { if (System.Attribute.IsDefined(field, typeof(HideInInspector)) || field.IsStatic) continue; object value = field.GetValue(monoBehaviour); if (showErrorTypeReferenceIsNull && (value == null || value.Equals(null))) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + "." + field.Name + ": Reference is null"); } else { return true; } } else if (field.FieldType == typeof(string)) { if (showErrorTypeStringIsEmpty && value != null && ((string)value).Equals("")) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + "." + field.Name + ": String value is empty"); } else { return true; } } } else { if (showErrorTypeReferenceIsNull && (value is IEnumerable)) { foreach (var item in (IEnumerable)value) { if (item == null) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + "." + field.Name + ": IEnumerable has value with null reference"); } else { return true; } } } } } } catch { } } } } } return false; } private List targetPropertiesNames = new List(10); private bool isUnityEventsNullOrMissing(MonoBehaviour monoBehaviour, bool printError) { targetPropertiesNames.Clear(); FieldInfo[] fieldArray = monoBehaviour.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); for (int i = fieldArray.Length - 1; i >= 0; i--) { FieldInfo field = fieldArray[i]; if (field.FieldType == typeof(UnityEvent) || field.FieldType.IsSubclassOf(typeof(UnityEvent))) { targetPropertiesNames.Add(field.Name); } } if (targetPropertiesNames.Count > 0) { SerializedObject serializedMonoBehaviour = new SerializedObject(monoBehaviour); for (int i = targetPropertiesNames.Count - 1; i >= 0; i--) { string targetProperty = targetPropertiesNames[i]; SerializedProperty property = serializedMonoBehaviour.FindProperty(targetProperty); SerializedProperty propertyRelativeArrray = property.FindPropertyRelative("m_PersistentCalls.m_Calls"); for (int j = propertyRelativeArrray.arraySize - 1; j >= 0; j--) { SerializedProperty arrayElementAtIndex = propertyRelativeArrray.GetArrayElementAtIndex(j); SerializedProperty propertyTarget = arrayElementAtIndex.FindPropertyRelative("m_Target"); if (propertyTarget.objectReferenceValue == null) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + ": Event object reference is null"); } else { return true; } } SerializedProperty propertyMethodName = arrayElementAtIndex.FindPropertyRelative("m_MethodName"); if (string.IsNullOrEmpty(propertyMethodName.stringValue)) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + ": Event handler function is not selected"); continue; } else { return true; } } string argumentAssemblyTypeName = arrayElementAtIndex.FindPropertyRelative("m_Arguments").FindPropertyRelative("m_ObjectArgumentAssemblyTypeName").stringValue; System.Type argumentAssemblyType; if (!string.IsNullOrEmpty(argumentAssemblyTypeName)) argumentAssemblyType = System.Type.GetType(argumentAssemblyTypeName, false) ?? typeof(UnityEngine.Object); else argumentAssemblyType = typeof(UnityEngine.Object); UnityEventBase dummyEvent; System.Type propertyTypeName = System.Type.GetType(property.FindPropertyRelative("m_TypeName").stringValue, false); if (propertyTypeName == null) dummyEvent = (UnityEventBase) new UnityEvent(); else dummyEvent = Activator.CreateInstance(propertyTypeName) as UnityEventBase; if (!UnityEventDrawer.IsPersistantListenerValid(dummyEvent, propertyMethodName.stringValue, propertyTarget.objectReferenceValue, (PersistentListenerMode)arrayElementAtIndex.FindPropertyRelative("m_Mode").enumValueIndex, argumentAssemblyType)) { if (printError) { appendErrorLine(monoBehaviour.GetType().Name + ": Event handler function is missing"); } else { return true; } } } } } return false; } private void appendErrorLine(string error) { errorCount++; errorStringBuilder.Append(errorCount.ToString()); errorStringBuilder.Append(") "); errorStringBuilder.AppendLine(error); } } }