[Add] FishNet
This commit is contained in:
@@ -0,0 +1,421 @@
|
||||
using System;
|
||||
using FishNet.Editing;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Statistic;
|
||||
using FishNet.Managing.Timing;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using GameKit.Dependencies.Utilities.Types;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Component.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Add to any object to display current ping(round trip time).
|
||||
/// </summary>
|
||||
[AddComponentMenu("FishNet/Component/BandwidthDisplay")]
|
||||
public class BandwidthDisplay : MonoBehaviour
|
||||
{
|
||||
#region Types.
|
||||
private enum Corner
|
||||
{
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
|
||||
public class InOutAverage
|
||||
{
|
||||
private RingBuffer<ulong> _in;
|
||||
private RingBuffer<ulong> _out;
|
||||
|
||||
public InOutAverage(int ticks)
|
||||
{
|
||||
_in = new(ticks);
|
||||
_out = new(ticks);
|
||||
}
|
||||
|
||||
public void AddIn(ulong value) => _in.Add(value);
|
||||
public void AddOut(ulong value) => _out.Add(value);
|
||||
|
||||
public float GetAverage(bool inBuffer)
|
||||
{
|
||||
RingBuffer<ulong> buffer = GetBuffer(inBuffer);
|
||||
|
||||
int bufferCount = buffer.Count;
|
||||
if (bufferCount == 0)
|
||||
return 0;
|
||||
|
||||
ulong total = GetTotal(inBuffer);
|
||||
return (float)total / bufferCount;
|
||||
}
|
||||
|
||||
public ulong GetTotal(bool inBuffer)
|
||||
{
|
||||
RingBuffer<ulong> buffer = GetBuffer(inBuffer);
|
||||
|
||||
ulong total = 0;
|
||||
foreach (ulong v in buffer)
|
||||
total += v;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private RingBuffer<ulong> GetBuffer(bool inBuffer) => inBuffer ? _in : _out;
|
||||
|
||||
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
|
||||
/// <summary>
|
||||
/// Averages for client.
|
||||
/// </summary>
|
||||
public InOutAverage ClientAverages { get; private set; }
|
||||
/// <summary>
|
||||
/// Averages for server.
|
||||
/// </summary>
|
||||
public InOutAverage ServerAverages { get; private set; }
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Serialized.
|
||||
[Header("Misc")]
|
||||
/// <summary>
|
||||
/// True to operate while in release. This may cause allocations and impact performance.
|
||||
/// </summary>
|
||||
[Tooltip("True to operate while in release. This may cause allocations and impact performance.")]
|
||||
[SerializeField]
|
||||
private bool _runInRelease;
|
||||
[Header("Timing")]
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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;
|
||||
/// <summary>
|
||||
/// How often to update displayed text.
|
||||
/// </summary>
|
||||
[Tooltip("How often to update displayed text.")]
|
||||
[Range(0f, 10f)]
|
||||
[SerializeField]
|
||||
private float _updateInterval = 1f;
|
||||
[Header("Appearance")]
|
||||
/// <summary>
|
||||
/// Color for text.
|
||||
/// </summary>
|
||||
[Tooltip("Color for text.")]
|
||||
[SerializeField]
|
||||
private Color _color = Color.white;
|
||||
/// <summary>
|
||||
/// Which corner to display network statistics in.
|
||||
/// </summary>
|
||||
[Tooltip("Which corner to display network statistics in.")]
|
||||
[SerializeField]
|
||||
private Corner _placement = Corner.TopRight;
|
||||
/// <summary>
|
||||
/// rue to show outgoing data bytes.
|
||||
/// </summary>
|
||||
[Tooltip("True to show outgoing data bytes.")]
|
||||
[SerializeField]
|
||||
private bool _showOutgoing = true;
|
||||
|
||||
/// <summary>
|
||||
/// Sets ShowOutgoing value.
|
||||
/// </summary>
|
||||
/// <param name = "value"></param>
|
||||
public void SetShowOutgoing(bool value) => _showOutgoing = value;
|
||||
|
||||
/// <summary>
|
||||
/// True to show incoming data bytes.
|
||||
/// </summary>
|
||||
[Tooltip("True to show incoming data bytes.")]
|
||||
[SerializeField]
|
||||
private bool _showIncoming = true;
|
||||
|
||||
/// <summary>
|
||||
/// Sets ShowIncoming value.
|
||||
/// </summary>
|
||||
/// <param name = "value"></param>
|
||||
public void SetShowIncoming(bool value) => _showIncoming = value;
|
||||
#endregion
|
||||
|
||||
#if UNITY_EDITOR || !UNITY_SERVER
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Style for drawn ping.
|
||||
/// </summary>
|
||||
private readonly GUIStyle _style = new();
|
||||
/// <summary>
|
||||
/// Text to show for client in/out data.
|
||||
/// </summary>
|
||||
private string _clientText;
|
||||
/// <summary>
|
||||
/// Text to show for server in/out data.
|
||||
/// </summary>
|
||||
private string _serverText;
|
||||
/// <summary>
|
||||
/// First found NetworkTrafficStatistics.
|
||||
/// </summary>
|
||||
private NetworkTrafficStatistics _networkTrafficStatistics;
|
||||
/// <summary>
|
||||
/// Next time the server text can be updated.
|
||||
/// </summary>
|
||||
private float _nextServerTextUpdateTime;
|
||||
/// <summary>
|
||||
/// Next time the server text can be updated.
|
||||
/// </summary>
|
||||
private float _nextClientTextUpdateTime;
|
||||
/// <summary>
|
||||
/// True if component is initialized.
|
||||
/// </summary>
|
||||
private bool _initialized;
|
||||
#endregion
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Requires a UI, so exit if server build.
|
||||
#if UNITY_SERVER
|
||||
return;
|
||||
#endif
|
||||
// If release build, check if able to run in release.
|
||||
#if !DEVELOPMENT_BUILD && !UNITY_EDITOR
|
||||
if (!_runInRelease)
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Not enabled.
|
||||
if (!InstanceFinder.NetworkManager.StatisticsManager.TryGetNetworkTrafficStatistics(out _networkTrafficStatistics))
|
||||
return;
|
||||
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
SetSecondsAveraged(_secondsAveraged);
|
||||
|
||||
_networkTrafficStatistics.OnNetworkTraffic += NetworkTrafficStatistics_OnNetworkTraffic;
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_networkTrafficStatistics != null)
|
||||
_networkTrafficStatistics.OnNetworkTraffic -= NetworkTrafficStatistics_OnNetworkTraffic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new number of seconds to average from.
|
||||
/// </summary>
|
||||
public void SetSecondsAveraged(byte seconds)
|
||||
{
|
||||
// Get to ticks.
|
||||
NetworkManager manager = InstanceFinder.NetworkManager;
|
||||
if (manager == null)
|
||||
return;
|
||||
|
||||
if (seconds <= 0)
|
||||
seconds = 1;
|
||||
|
||||
//Convert to milliseconds.
|
||||
long ms = seconds * 1000;
|
||||
|
||||
uint ticks = manager.TimeManager.TimeToTicks(ms, TickRounding.RoundUp);
|
||||
|
||||
// Should not ever be possible.
|
||||
if (ticks <= 0)
|
||||
ticks = 60;
|
||||
|
||||
ClientAverages = new((int)ticks);
|
||||
ServerAverages = new((int)ticks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when new traffic statistics are received.
|
||||
/// </summary>
|
||||
private void NetworkTrafficStatistics_OnNetworkTraffic(uint tick, BidirectionalNetworkTraffic serverTraffic, BidirectionalNetworkTraffic clientTraffic)
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
ServerAverages.AddIn(serverTraffic.InboundTraffic.Bytes);
|
||||
ServerAverages.AddOut(serverTraffic.OutboundTraffic.Bytes);
|
||||
|
||||
ClientAverages.AddIn(clientTraffic.InboundTraffic.Bytes);
|
||||
|
||||
ClientAverages.AddOut(clientTraffic.OutboundTraffic.Bytes);
|
||||
|
||||
if (Time.time < _nextServerTextUpdateTime)
|
||||
return;
|
||||
|
||||
_nextServerTextUpdateTime = Time.time + _updateInterval;
|
||||
|
||||
string nl = System.Environment.NewLine;
|
||||
string result = string.Empty;
|
||||
|
||||
if (_showIncoming)
|
||||
result += $"Server In: {NetworkTrafficStatistics.FormatBytesToLargest(ServerAverages.GetTotal(inBuffer: true))}/s{nl}";
|
||||
if (_showOutgoing)
|
||||
result += $"Server Out: {NetworkTrafficStatistics.FormatBytesToLargest(ServerAverages.GetTotal(inBuffer: false))}/s{nl}";
|
||||
|
||||
_serverText = result;
|
||||
|
||||
result = string.Empty;
|
||||
|
||||
if (_showIncoming)
|
||||
result += $"Client In: {NetworkTrafficStatistics.FormatBytesToLargest(ClientAverages.GetTotal(inBuffer: true))}/s{nl}";
|
||||
if (_showOutgoing)
|
||||
result += $"Client Out: {NetworkTrafficStatistics.FormatBytesToLargest(ClientAverages.GetTotal(inBuffer: false))}/s{nl}";
|
||||
|
||||
_clientText = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when client network traffic is updated.
|
||||
/// </summary>
|
||||
private void NetworkTraffic_OnClientNetworkTraffic(BidirectionalNetworkTraffic traffic)
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
ClientAverages.AddIn(traffic.InboundTraffic.Bytes);
|
||||
ClientAverages.AddOut(traffic.OutboundTraffic.Bytes);
|
||||
|
||||
if (Time.time < _nextClientTextUpdateTime)
|
||||
return;
|
||||
_nextClientTextUpdateTime = Time.time + _updateInterval;
|
||||
|
||||
string nl = System.Environment.NewLine;
|
||||
string result = string.Empty;
|
||||
|
||||
if (_showIncoming)
|
||||
result += $"Client In: {NetworkTrafficStatistics.FormatBytesToLargest(ClientAverages.GetAverage(inBuffer: true))}/s{nl}";
|
||||
if (_showOutgoing)
|
||||
result += $"Client Out: {NetworkTrafficStatistics.FormatBytesToLargest(ClientAverages.GetAverage(inBuffer: false))}/s{nl}";
|
||||
|
||||
_clientText = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when server network traffic is updated.
|
||||
/// </summary>
|
||||
private void NetworkTraffic_OnServerNetworkTraffic(BidirectionalNetworkTraffic traffic)
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
ServerAverages.AddIn(traffic.InboundTraffic.Bytes);
|
||||
ServerAverages.AddOut(traffic.OutboundTraffic.Bytes);
|
||||
|
||||
if (Time.time < _nextServerTextUpdateTime)
|
||||
return;
|
||||
_nextServerTextUpdateTime = Time.time + _updateInterval;
|
||||
|
||||
string nl = System.Environment.NewLine;
|
||||
string result = string.Empty;
|
||||
|
||||
if (_showIncoming)
|
||||
result += $"Server In: {NetworkTrafficStatistics.FormatBytesToLargest(ServerAverages.GetAverage(inBuffer: true))}/s{nl}";
|
||||
if (_showOutgoing)
|
||||
result += $"Server Out: {NetworkTrafficStatistics.FormatBytesToLargest(ServerAverages.GetAverage(inBuffer: 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 (!_initialized)
|
||||
return;
|
||||
|
||||
if (forServer)
|
||||
{
|
||||
_serverText = string.Empty;
|
||||
ServerAverages.ResetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
_clientText = string.Empty;
|
||||
ClientAverages.ResetState();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user