using System; using System.Runtime.CompilerServices; using UnityEngine; namespace FishNet.Managing.Statistic { [System.Serializable] public class NetworkTraficStatistics { #region Public. /// /// Called when NetworkTraffic is updated for the client. /// public event Action OnClientNetworkTraffic; /// /// Called when NetworkTraffic is updated for the server. /// public event Action OnServerNetworkTraffic; #endregion #region Serialized. /// /// How often to update traffic statistics. /// [Tooltip("How often to update traffic statistics.")] [SerializeField] [Range(0f, 10f)] private float _updateInteval = 1f; /// /// /// [Tooltip("True to update client statistics.")] [SerializeField] private bool _updateClient; /// /// True to update client statistics. /// public bool UpdateClient { get => _updateClient; private set => _updateClient = value; } /// /// Sets UpdateClient value. /// /// public void SetUpdateClient(bool update) { UpdateClient = update; } /// /// /// [Tooltip("True to update server statistics.")] [SerializeField] private bool _updateServer; /// /// True to update client statistics. /// public bool UpdateServer { get => _updateServer; private set => _updateServer = value; } /// /// Sets UpdateServer value. /// /// public void SetUpdateServer(bool update) { UpdateServer = update; } #endregion #region Private. /// /// NetworkManager for this statistics. /// private NetworkManager _networkManager; /// /// Bytes sent to the server from local client. /// private ulong _client_toServerBytes; /// /// Bytes received on the local client from the server. /// private ulong _client_fromServerBytes; /// /// Bytes sent to all clients from the local server. /// private ulong _server_toClientsBytes; /// /// Bytes received on the local server from all clients. /// private ulong _server_fromClientsBytes; /// /// Next time network traffic updates may invoke. /// private float _nextUpdateTime; /// /// Size suffixes as text. /// private static readonly string[] _sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; #endregion internal void InitializeOnce_Internal(NetworkManager manager) { _networkManager = manager; manager.TimeManager.OnPreTick += TimeManager_OnPreTick; } /// /// Called before the TimeManager ticks. /// private void TimeManager_OnPreTick() { if (Time.unscaledTime < _nextUpdateTime) return; _nextUpdateTime = Time.unscaledTime + _updateInteval; if (UpdateClient && _networkManager.IsClient) OnClientNetworkTraffic?.Invoke(new NetworkTrafficArgs(_client_toServerBytes, _client_fromServerBytes)); if (UpdateServer && _networkManager.IsServer) OnServerNetworkTraffic?.Invoke(new NetworkTrafficArgs(_server_fromClientsBytes, _server_toClientsBytes)); _client_toServerBytes = 0; _client_fromServerBytes = 0; _server_toClientsBytes = 0; _server_fromClientsBytes = 0; } /// /// Called when the local client sends data. /// internal void LocalClientSentData(ulong dataLength) { _client_toServerBytes = Math.Min(_client_toServerBytes + dataLength, ulong.MaxValue); } /// /// Called when the local client receives data. /// public void LocalClientReceivedData(ulong dataLength) { _client_fromServerBytes = Math.Min(_client_fromServerBytes + dataLength, ulong.MaxValue); } /// /// Called when the local client sends data. /// internal void LocalServerSentData(ulong dataLength) { _server_toClientsBytes = Math.Min(_server_toClientsBytes + dataLength, ulong.MaxValue); } /// /// Called when the local client receives data. /// public void LocalServerReceivedData(ulong dataLength) { _server_fromClientsBytes = Math.Min(_server_fromClientsBytes + dataLength, ulong.MaxValue); } //Attribution: https://stackoverflow.com/questions/14488796/does-net-provide-an-easy-way-convert-bytes-to-kb-mb-gb-etc /// /// Formats passed in bytes value to the largest possible data type with 2 decimals. /// public static string FormatBytesToLargest(ulong bytes) { int decimalPlaces = 2; if (bytes == 0) { decimalPlaces = 0; return string.Format("{0:n" + decimalPlaces + "} bytes", 0); } // mag is 0 for bytes, 1 for KB, 2, for MB, etc. int mag = (int)Math.Log(bytes, 1024); // 1L << (mag * 10) == 2 ^ (10 * mag) // [i.e. the number of bytes in the unit corresponding to mag] decimal adjustedSize = (decimal)bytes / (1L << (mag * 10)); // make adjustment when the value is large enough that // it would round up to 1000 or more if (Math.Round(adjustedSize, decimalPlaces) >= 1000) { mag += 1; adjustedSize /= 1024; } //Don't show decimals for bytes. if (mag == 0) decimalPlaces = 0; return string.Format("{0:n" + decimalPlaces + "} {1}", adjustedSize, _sizeSuffixes[mag]); } } }