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]);
}
}
}