using FishNet.Managing.Statistic; using GameKit.Dependencies.Utilities.Types; using UnityEngine; namespace FishNet.Component.Utility { /// /// Add to any object to display current ping(round trip time). /// [AddComponentMenu("FishNet/Component/BandwidthDisplay")] public class BandwidthDisplay : MonoBehaviour { #region Types. private enum Corner { TopLeft, TopRight, BottomLeft, BottomRight } public class InOutAverage { private RingBuffer _in; private RingBuffer _out; public InOutAverage(byte seconds) { _in = new(seconds); _out = new(seconds); } public void AddIn(ulong value) => _in.Add(value); public void AddOut(ulong value) => _out.Add(value); public ulong GetAverage(bool inAverage) { RingBuffer buffer = (inAverage) ? _in : _out; int count = buffer.Count; if (count == 0) return 0; ulong total = 0; foreach (ulong v in buffer) total += v; return (total / (uint)count); } public void ResetState() { _in.Clear(); _out.Clear(); } public void InitializeState(int capacity) { _in.Initialize(capacity); _out.Initialize(capacity); } } #endregion #region Public. #if UNITY_EDITOR || !UNITY_SERVER /// /// Averages for client. /// public InOutAverage ClientAverages { get; private set; } /// /// Averages for server. /// public InOutAverage ServerAverages { get; private set; } #endif #endregion #region Serialized. /// /// Color for text. /// [Tooltip("Color for text.")] [SerializeField] private Color _color = Color.white; /// /// Which corner to display network statistics in. /// [Tooltip("Which corner to display network statistics in.")] [SerializeField] private Corner _placement = Corner.TopRight; /// /// Number of seconds used to gather data per second. Lower values will show more up to date usage per second while higher values provide a better over-all estimate. /// [Tooltip("Number of seconds used to gather data per second. Lower values will show more up to date usage per second while higher values provide a better over-all estimate.")] [SerializeField] [Range(1, byte.MaxValue)] private byte _secondsAveraged = 1; /// /// rue to show outgoing data bytes. /// [Tooltip("True to show outgoing data bytes.")] [SerializeField] private bool _showOutgoing = true; /// /// Sets ShowOutgoing value. /// /// public void SetShowOutgoing(bool value) => _showOutgoing = value; /// /// True to show incoming data bytes. /// [Tooltip("True to show incoming data bytes.")] [SerializeField] private bool _showIncoming = true; /// /// Sets ShowIncoming value. /// /// public void SetShowIncoming(bool value) => _showIncoming = value; #endregion #if UNITY_EDITOR || !UNITY_SERVER #region Private. /// /// Style for drawn ping. /// private readonly GUIStyle _style = new(); /// /// Text to show for client in/out data. /// private string _clientText; /// /// Text to show for server in/out data. /// private string _serverText; /// /// First found NetworkTrafficStatistics. /// private NetworkTraficStatistics _networkTrafficStatistics; #endregion private void Start() { SetSecondsAveraged(_secondsAveraged); _networkTrafficStatistics = InstanceFinder.NetworkManager.StatisticsManager.NetworkTraffic; //Subscribe to both traffic updates. _networkTrafficStatistics.OnClientNetworkTraffic += NetworkTraffic_OnClientNetworkTraffic; _networkTrafficStatistics.OnServerNetworkTraffic += NetworkTraffic_OnServerNetworkTraffic; if (!_networkTrafficStatistics.UpdateClient && !_networkTrafficStatistics.UpdateServer) Debug.LogWarning($"StatisticsManager.NetworkTraffic is not updating for client nor server. To see results ensure your NetworkManager has a StatisticsManager component added with the NetworkTraffic values configured."); } private void OnDestroy() { if (_networkTrafficStatistics != null) { _networkTrafficStatistics.OnClientNetworkTraffic -= NetworkTraffic_OnClientNetworkTraffic; _networkTrafficStatistics.OnServerNetworkTraffic -= NetworkTraffic_OnServerNetworkTraffic; } } /// /// Sets a new number of seconds to average from. /// public void SetSecondsAveraged(byte seconds) { if (seconds <= 0) seconds = 1; ClientAverages = new(seconds); ServerAverages = new(seconds); } /// /// Called when client network traffic is updated. /// private void NetworkTraffic_OnClientNetworkTraffic(NetworkTrafficArgs obj) { string nl = System.Environment.NewLine; string result = string.Empty; ClientAverages.AddIn(obj.FromServerBytes); ClientAverages.AddOut(obj.ToServerBytes); if (_showIncoming) result += $"Client In: {NetworkTraficStatistics.FormatBytesToLargest(ClientAverages.GetAverage(inAverage: true))}/s{nl}"; if (_showOutgoing) result += $"Client Out: {NetworkTraficStatistics.FormatBytesToLargest(ClientAverages.GetAverage(inAverage: false))}/s{nl}"; _clientText = result; } /// /// Called when server network traffic is updated. /// private void NetworkTraffic_OnServerNetworkTraffic(NetworkTrafficArgs obj) { string nl = System.Environment.NewLine; string result = string.Empty; ServerAverages.AddIn(obj.ToServerBytes); ServerAverages.AddOut(obj.FromServerBytes); if (_showIncoming) result += $"Server In: {NetworkTraficStatistics.FormatBytesToLargest(ServerAverages.GetAverage(inAverage: true))}/s{nl}"; if (_showOutgoing) result += $"Server Out: {NetworkTraficStatistics.FormatBytesToLargest(ServerAverages.GetAverage(inAverage: false))}/s{nl}"; _serverText = result; } private void OnGUI() { _style.normal.textColor = _color; _style.fontSize = 15; float width = 100f; float height = 0f; if (_showIncoming) height += 15f; if (_showOutgoing) height += 15f; bool isClient = InstanceFinder.IsClientStarted; bool isServer = InstanceFinder.IsServerStarted; if (!isClient) ResetCalculationsAndDisplay(forServer: false); if (!isServer) ResetCalculationsAndDisplay(forServer: true); if (isServer && isClient) height *= 2f; float edge = 10f; float horizontal; float vertical; if (_placement == Corner.TopLeft) { horizontal = 10f; vertical = 10f; _style.alignment = TextAnchor.UpperLeft; } else if (_placement == Corner.TopRight) { horizontal = Screen.width - width - edge; vertical = 10f; _style.alignment = TextAnchor.UpperRight; } else if (_placement == Corner.BottomLeft) { horizontal = 10f; vertical = Screen.height - height - edge; _style.alignment = TextAnchor.LowerLeft; } else { horizontal = Screen.width - width - edge; vertical = Screen.height - height - edge; _style.alignment = TextAnchor.LowerRight; } GUI.Label(new(horizontal, vertical, width, height), (_clientText + _serverText), _style); } [ContextMenu("Reset Averages")] public void ResetAverages() { ResetCalculationsAndDisplay(forServer: true); ResetCalculationsAndDisplay(forServer: false); } private void ResetCalculationsAndDisplay(bool forServer) { if (forServer) { _serverText = string.Empty; ServerAverages.ResetState(); } else { _clientText = string.Empty; ClientAverages.ResetState(); } } #endif } }