using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace Mirror
{
[CustomEditor(typeof(NetworkBehaviour), true)]
[CanEditMultipleObjects]
public class NetworkBehaviourInspector : Editor
{
///
/// List of all visible syncVars in target class
///
protected List syncVarNames = new List();
bool syncsAnything;
SyncListDrawer syncListDrawer;
// does this type sync anything? otherwise we don't need to show syncInterval
bool SyncsAnything(Type scriptClass)
{
// check for all SyncVar fields, they don't have to be visible
foreach (FieldInfo field in InspectorHelper.GetAllFields(scriptClass, typeof(NetworkBehaviour)))
{
if (field.IsSyncVar())
{
return true;
}
}
// has OnSerialize that is not in NetworkBehaviour?
// then it either has a syncvar or custom OnSerialize. either way
// this means we have something to sync.
MethodInfo method = scriptClass.GetMethod("OnSerialize");
if (method != null && method.DeclaringType != typeof(NetworkBehaviour))
{
return true;
}
// SyncObjects are serialized in NetworkBehaviour.OnSerialize, which
// is always there even if we don't use SyncObjects. so we need to
// search for SyncObjects manually.
// Any SyncObject should be added to syncObjects when unity creates an
// object so we can check length of list so see if sync objects exists
FieldInfo syncObjectsField = scriptClass.GetField("syncObjects", BindingFlags.NonPublic | BindingFlags.Instance);
List syncObjects = (List)syncObjectsField.GetValue(serializedObject.targetObject);
return syncObjects.Count > 0;
}
void OnEnable()
{
if (target == null) { Debug.LogWarning("NetworkBehaviourInspector had no target object"); return; }
// If target's base class is changed from NetworkBehaviour to MonoBehaviour
// then Unity temporarily keep using this Inspector causing things to break
if (!(target is NetworkBehaviour)) { return; }
Type scriptClass = target.GetType();
syncVarNames = new List();
foreach (FieldInfo field in InspectorHelper.GetAllFields(scriptClass, typeof(NetworkBehaviour)))
{
if (field.IsSyncVar() && field.IsVisibleField())
{
syncVarNames.Add(field.Name);
}
}
syncListDrawer = new SyncListDrawer(serializedObject.targetObject);
syncsAnything = SyncsAnything(scriptClass);
}
public override void OnInspectorGUI()
{
DrawDefaultInspector();
DrawDefaultSyncLists();
DrawDefaultSyncSettings();
}
///
/// Draws Sync Objects that are IEnumerable
///
protected void DrawDefaultSyncLists()
{
// Need this check in case OnEnable returns early
if (syncListDrawer == null) { return; }
syncListDrawer.Draw();
}
///
/// Draws SyncSettings if the NetworkBehaviour has anything to sync
///
protected void DrawDefaultSyncSettings()
{
// does it sync anything? then show extra properties
// (no need to show it if the class only has Cmds/Rpcs and no sync)
if (!syncsAnything)
{
return;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Sync Settings", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(serializedObject.FindProperty("syncMode"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("syncInterval"));
// apply
serializedObject.ApplyModifiedProperties();
}
}
public class SyncListDrawer
{
readonly UnityEngine.Object targetObject;
readonly List syncListFields;
public SyncListDrawer(UnityEngine.Object targetObject)
{
this.targetObject = targetObject;
syncListFields = new List();
foreach (FieldInfo field in InspectorHelper.GetAllFields(targetObject.GetType(), typeof(NetworkBehaviour)))
{
if (field.IsSyncObject() && field.IsVisibleSyncObject())
{
syncListFields.Add(new SyncListField(field));
}
}
}
public void Draw()
{
if (syncListFields.Count == 0) { return; }
EditorGUILayout.Space();
EditorGUILayout.LabelField("Sync Lists", EditorStyles.boldLabel);
for (int i = 0; i < syncListFields.Count; i++)
{
DrawSyncList(syncListFields[i]);
}
}
void DrawSyncList(SyncListField syncListField)
{
syncListField.visible = EditorGUILayout.Foldout(syncListField.visible, syncListField.label);
if (syncListField.visible)
{
using (new EditorGUI.IndentLevelScope())
{
object fieldValue = syncListField.field.GetValue(targetObject);
if (fieldValue is IEnumerable synclist)
{
int index = 0;
foreach (object item in synclist)
{
string itemValue = item != null ? item.ToString() : "NULL";
string itemLabel = "Element " + index;
EditorGUILayout.LabelField(itemLabel, itemValue);
index++;
}
}
}
}
}
class SyncListField
{
public bool visible;
public readonly FieldInfo field;
public readonly string label;
public SyncListField(FieldInfo field)
{
this.field = field;
visible = false;
label = field.Name + " [" + field.FieldType.Name + "]";
}
}
}
}