NetworkBehaviourInspector.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Reflection;
  5. using UnityEditor;
  6. using UnityEngine;
  7. namespace Mirror
  8. {
  9. [CustomEditor(typeof(NetworkBehaviour), true)]
  10. [CanEditMultipleObjects]
  11. public class NetworkBehaviourInspector : Editor
  12. {
  13. /// <summary>
  14. /// List of all visible syncVars in target class
  15. /// </summary>
  16. protected List<string> syncVarNames = new List<string>();
  17. bool syncsAnything;
  18. SyncListDrawer syncListDrawer;
  19. // does this type sync anything? otherwise we don't need to show syncInterval
  20. bool SyncsAnything(Type scriptClass)
  21. {
  22. // check for all SyncVar fields, they don't have to be visible
  23. foreach (FieldInfo field in InspectorHelper.GetAllFields(scriptClass, typeof(NetworkBehaviour)))
  24. {
  25. if (field.IsSyncVar())
  26. {
  27. return true;
  28. }
  29. }
  30. // has OnSerialize that is not in NetworkBehaviour?
  31. // then it either has a syncvar or custom OnSerialize. either way
  32. // this means we have something to sync.
  33. MethodInfo method = scriptClass.GetMethod("OnSerialize");
  34. if (method != null && method.DeclaringType != typeof(NetworkBehaviour))
  35. {
  36. return true;
  37. }
  38. // SyncObjects are serialized in NetworkBehaviour.OnSerialize, which
  39. // is always there even if we don't use SyncObjects. so we need to
  40. // search for SyncObjects manually.
  41. // Any SyncObject should be added to syncObjects when unity creates an
  42. // object so we can check length of list so see if sync objects exists
  43. FieldInfo syncObjectsField = scriptClass.GetField("syncObjects", BindingFlags.NonPublic | BindingFlags.Instance);
  44. List<SyncObject> syncObjects = (List<SyncObject>)syncObjectsField.GetValue(serializedObject.targetObject);
  45. return syncObjects.Count > 0;
  46. }
  47. void OnEnable()
  48. {
  49. if (target == null) { Debug.LogWarning("NetworkBehaviourInspector had no target object"); return; }
  50. // If target's base class is changed from NetworkBehaviour to MonoBehaviour
  51. // then Unity temporarily keep using this Inspector causing things to break
  52. if (!(target is NetworkBehaviour)) { return; }
  53. Type scriptClass = target.GetType();
  54. syncVarNames = new List<string>();
  55. foreach (FieldInfo field in InspectorHelper.GetAllFields(scriptClass, typeof(NetworkBehaviour)))
  56. {
  57. if (field.IsSyncVar() && field.IsVisibleField())
  58. {
  59. syncVarNames.Add(field.Name);
  60. }
  61. }
  62. syncListDrawer = new SyncListDrawer(serializedObject.targetObject);
  63. syncsAnything = SyncsAnything(scriptClass);
  64. }
  65. public override void OnInspectorGUI()
  66. {
  67. DrawDefaultInspector();
  68. DrawDefaultSyncLists();
  69. DrawDefaultSyncSettings();
  70. }
  71. /// <summary>
  72. /// Draws Sync Objects that are IEnumerable
  73. /// </summary>
  74. protected void DrawDefaultSyncLists()
  75. {
  76. // Need this check in case OnEnable returns early
  77. if (syncListDrawer == null) { return; }
  78. syncListDrawer.Draw();
  79. }
  80. /// <summary>
  81. /// Draws SyncSettings if the NetworkBehaviour has anything to sync
  82. /// </summary>
  83. protected void DrawDefaultSyncSettings()
  84. {
  85. // does it sync anything? then show extra properties
  86. // (no need to show it if the class only has Cmds/Rpcs and no sync)
  87. if (!syncsAnything)
  88. {
  89. return;
  90. }
  91. EditorGUILayout.Space();
  92. EditorGUILayout.LabelField("Sync Settings", EditorStyles.boldLabel);
  93. EditorGUILayout.PropertyField(serializedObject.FindProperty("syncMode"));
  94. EditorGUILayout.PropertyField(serializedObject.FindProperty("syncInterval"));
  95. // apply
  96. serializedObject.ApplyModifiedProperties();
  97. }
  98. }
  99. public class SyncListDrawer
  100. {
  101. readonly UnityEngine.Object targetObject;
  102. readonly List<SyncListField> syncListFields;
  103. public SyncListDrawer(UnityEngine.Object targetObject)
  104. {
  105. this.targetObject = targetObject;
  106. syncListFields = new List<SyncListField>();
  107. foreach (FieldInfo field in InspectorHelper.GetAllFields(targetObject.GetType(), typeof(NetworkBehaviour)))
  108. {
  109. if (field.IsSyncObject() && field.IsVisibleSyncObject())
  110. {
  111. syncListFields.Add(new SyncListField(field));
  112. }
  113. }
  114. }
  115. public void Draw()
  116. {
  117. if (syncListFields.Count == 0) { return; }
  118. EditorGUILayout.Space();
  119. EditorGUILayout.LabelField("Sync Lists", EditorStyles.boldLabel);
  120. for (int i = 0; i < syncListFields.Count; i++)
  121. {
  122. DrawSyncList(syncListFields[i]);
  123. }
  124. }
  125. void DrawSyncList(SyncListField syncListField)
  126. {
  127. syncListField.visible = EditorGUILayout.Foldout(syncListField.visible, syncListField.label);
  128. if (syncListField.visible)
  129. {
  130. using (new EditorGUI.IndentLevelScope())
  131. {
  132. object fieldValue = syncListField.field.GetValue(targetObject);
  133. if (fieldValue is IEnumerable synclist)
  134. {
  135. int index = 0;
  136. foreach (object item in synclist)
  137. {
  138. string itemValue = item != null ? item.ToString() : "NULL";
  139. string itemLabel = "Element " + index;
  140. EditorGUILayout.LabelField(itemLabel, itemValue);
  141. index++;
  142. }
  143. }
  144. }
  145. }
  146. }
  147. class SyncListField
  148. {
  149. public bool visible;
  150. public readonly FieldInfo field;
  151. public readonly string label;
  152. public SyncListField(FieldInfo field)
  153. {
  154. this.field = field;
  155. visible = false;
  156. label = field.Name + " [" + field.FieldType.Name + "]";
  157. }
  158. }
  159. }
  160. }