using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Mirror { [CustomPreview(typeof(GameObject))] class NetworkInformationPreview : ObjectPreview { struct NetworkIdentityInfo { public GUIContent name; public GUIContent value; } struct NetworkBehaviourInfo { // This is here just so we can check if it's enabled/disabled public NetworkBehaviour behaviour; public GUIContent name; } class Styles { public GUIStyle labelStyle = new GUIStyle(EditorStyles.label); public GUIStyle componentName = new GUIStyle(EditorStyles.boldLabel); public GUIStyle disabledName = new GUIStyle(EditorStyles.miniLabel); public Styles() { Color fontColor = new Color(0.7f, 0.7f, 0.7f); labelStyle.padding.right += 20; labelStyle.normal.textColor = fontColor; labelStyle.active.textColor = fontColor; labelStyle.focused.textColor = fontColor; labelStyle.hover.textColor = fontColor; labelStyle.onNormal.textColor = fontColor; labelStyle.onActive.textColor = fontColor; labelStyle.onFocused.textColor = fontColor; labelStyle.onHover.textColor = fontColor; componentName.normal.textColor = fontColor; componentName.active.textColor = fontColor; componentName.focused.textColor = fontColor; componentName.hover.textColor = fontColor; componentName.onNormal.textColor = fontColor; componentName.onActive.textColor = fontColor; componentName.onFocused.textColor = fontColor; componentName.onHover.textColor = fontColor; disabledName.normal.textColor = fontColor; disabledName.active.textColor = fontColor; disabledName.focused.textColor = fontColor; disabledName.hover.textColor = fontColor; disabledName.onNormal.textColor = fontColor; disabledName.onActive.textColor = fontColor; disabledName.onFocused.textColor = fontColor; disabledName.onHover.textColor = fontColor; } } GUIContent title; Styles styles = new Styles(); public override GUIContent GetPreviewTitle() { if (title == null) { title = new GUIContent("Network Information"); } return title; } public override bool HasPreviewGUI() { // need to check if target is null to stop MissingReferenceException return target != null && target is GameObject gameObject && gameObject.GetComponent() != null; } public override void OnPreviewGUI(Rect r, GUIStyle background) { if (Event.current.type != EventType.Repaint) return; if (target == null) return; GameObject targetGameObject = target as GameObject; if (targetGameObject == null) return; NetworkIdentity identity = targetGameObject.GetComponent(); if (identity == null) return; if (styles == null) styles = new Styles(); // padding RectOffset previewPadding = new RectOffset(-5, -5, -5, -5); Rect paddedr = previewPadding.Add(r); //Centering float initialX = paddedr.x + 10; float Y = paddedr.y + 10; Y = DrawNetworkIdentityInfo(identity, initialX, Y); Y = DrawNetworkBehaviors(identity, initialX, Y); Y = DrawObservers(identity, initialX, Y); _ = DrawOwner(identity, initialX, Y); } float DrawNetworkIdentityInfo(NetworkIdentity identity, float initialX, float Y) { IEnumerable infos = GetNetworkIdentityInfo(identity); // Get required label size for the names of the information values we're going to show // There are two columns, one with label for the name of the info and the next for the value Vector2 maxNameLabelSize = new Vector2(140, 16); Vector2 maxValueLabelSize = GetMaxNameLabelSize(infos); Rect labelRect = new Rect(initialX, Y, maxNameLabelSize.x, maxNameLabelSize.y); Rect idLabelRect = new Rect(maxNameLabelSize.x, Y, maxValueLabelSize.x, maxValueLabelSize.y); foreach (NetworkIdentityInfo info in infos) { GUI.Label(labelRect, info.name, styles.labelStyle); GUI.Label(idLabelRect, info.value, styles.componentName); labelRect.y += labelRect.height; labelRect.x = initialX; idLabelRect.y += idLabelRect.height; } return labelRect.y; } float DrawNetworkBehaviors(NetworkIdentity identity, float initialX, float Y) { IEnumerable behavioursInfo = GetNetworkBehaviorInfo(identity); // Show behaviours list in a different way than the name/value pairs above Vector2 maxBehaviourLabelSize = GetMaxBehaviourLabelSize(behavioursInfo); Rect behaviourRect = new Rect(initialX, Y + 10, maxBehaviourLabelSize.x, maxBehaviourLabelSize.y); GUI.Label(behaviourRect, new GUIContent("Network Behaviours"), styles.labelStyle); // indent names behaviourRect.x += 20; behaviourRect.y += behaviourRect.height; foreach (NetworkBehaviourInfo info in behavioursInfo) { if (info.behaviour == null) { // could be the case in the editor after existing play mode. continue; } GUI.Label(behaviourRect, info.name, info.behaviour.enabled ? styles.componentName : styles.disabledName); behaviourRect.y += behaviourRect.height; Y = behaviourRect.y; } return Y; } float DrawObservers(NetworkIdentity identity, float initialX, float Y) { if (identity.observers != null && identity.observers.Count > 0) { Rect observerRect = new Rect(initialX, Y + 10, 200, 20); GUI.Label(observerRect, new GUIContent("Network observers"), styles.labelStyle); // indent names observerRect.x += 20; observerRect.y += observerRect.height; foreach (KeyValuePair kvp in identity.observers) { GUI.Label(observerRect, $"{kvp.Value.address}:{kvp.Value}", styles.componentName); observerRect.y += observerRect.height; Y = observerRect.y; } } return Y; } float DrawOwner(NetworkIdentity identity, float initialX, float Y) { if (identity.connectionToClient != null) { Rect ownerRect = new Rect(initialX, Y + 10, 400, 20); GUI.Label(ownerRect, new GUIContent($"Client Authority: {identity.connectionToClient}"), styles.labelStyle); Y += ownerRect.height; } return Y; } // Get the maximum size used by the value of information items Vector2 GetMaxNameLabelSize(IEnumerable infos) { Vector2 maxLabelSize = Vector2.zero; foreach (NetworkIdentityInfo info in infos) { Vector2 labelSize = styles.labelStyle.CalcSize(info.value); if (maxLabelSize.x < labelSize.x) { maxLabelSize.x = labelSize.x; } if (maxLabelSize.y < labelSize.y) { maxLabelSize.y = labelSize.y; } } return maxLabelSize; } Vector2 GetMaxBehaviourLabelSize(IEnumerable behavioursInfo) { Vector2 maxLabelSize = Vector2.zero; foreach (NetworkBehaviourInfo behaviour in behavioursInfo) { Vector2 labelSize = styles.labelStyle.CalcSize(behaviour.name); if (maxLabelSize.x < labelSize.x) { maxLabelSize.x = labelSize.x; } if (maxLabelSize.y < labelSize.y) { maxLabelSize.y = labelSize.y; } } return maxLabelSize; } IEnumerable GetNetworkIdentityInfo(NetworkIdentity identity) { List infos = new List { GetAssetId(identity), GetString("Scene ID", identity.sceneId.ToString("X")) }; if (Application.isPlaying) { infos.Add(GetString("Network ID", identity.netId.ToString())); infos.Add(GetBoolean("Is Client", identity.isClient)); infos.Add(GetBoolean("Is Server", identity.isServer)); infos.Add(GetBoolean("Has Authority", identity.hasAuthority)); infos.Add(GetBoolean("Is Local Player", identity.isLocalPlayer)); } return infos; } IEnumerable GetNetworkBehaviorInfo(NetworkIdentity identity) { List behaviourInfos = new List(); NetworkBehaviour[] behaviours = identity.GetComponents(); foreach (NetworkBehaviour behaviour in behaviours) { behaviourInfos.Add(new NetworkBehaviourInfo { name = new GUIContent(behaviour.GetType().FullName), behaviour = behaviour }); } return behaviourInfos; } NetworkIdentityInfo GetAssetId(NetworkIdentity identity) { string assetId = identity.assetId.ToString(); if (string.IsNullOrWhiteSpace(assetId)) { assetId = ""; } return GetString("Asset ID", assetId); } static NetworkIdentityInfo GetString(string name, string value) { return new NetworkIdentityInfo { name = new GUIContent(name), value = new GUIContent(value) }; } static NetworkIdentityInfo GetBoolean(string name, bool value) { return new NetworkIdentityInfo { name = new GUIContent(name), value = new GUIContent((value ? "Yes" : "No")) }; } } }