[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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bc8f0363ddc75946a958043c5e49a83
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,251 @@
|
||||
using FishNet.Connection;
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Managing.Scened;
|
||||
using FishNet.Transporting;
|
||||
using FishNet.Utility;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using GameKit.Dependencies.Utilities.Types;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Serialization;
|
||||
using UnitySceneManager = UnityEngine.SceneManagement.SceneManager;
|
||||
|
||||
namespace FishNet.Component.Scenes
|
||||
{
|
||||
/// <summary>
|
||||
/// Add to a NetworkManager object to change between Online and Offline scene based on connection states of the server or client.
|
||||
/// </summary>
|
||||
[AddComponentMenu("FishNet/Component/DefaultScene")]
|
||||
public class DefaultScene : MonoBehaviour
|
||||
{
|
||||
#region Serialized.
|
||||
[Tooltip("True to load the online scene as global, false to load it as connection.")]
|
||||
[SerializeField]
|
||||
private bool _enableGlobalScenes = true;
|
||||
/// <summary>
|
||||
/// True to replace all scenes with the offline scene immediately.
|
||||
/// </summary>
|
||||
[Tooltip("True to replace all scenes with the offline scene immediately.")]
|
||||
[SerializeField]
|
||||
private bool _startInOffline;
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[Tooltip("Scene to load when disconnected. Server and client will load this scene.")]
|
||||
[SerializeField]
|
||||
[Scene]
|
||||
private string _offlineScene;
|
||||
|
||||
/// <summary>
|
||||
/// Sets which offline scene to use.
|
||||
/// </summary>
|
||||
/// <param name = "sceneName">Scene name to use as the offline scene.</param>
|
||||
public void SetOfflineScene(string sceneName) => _offlineScene = sceneName;
|
||||
|
||||
/// <summary>
|
||||
/// Scene to load when disconnected. Server and client will load this scene.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetOfflineScene() => _offlineScene;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[Tooltip("Scene to load when connected. Server and client will load this scene.")]
|
||||
[SerializeField]
|
||||
[Scene]
|
||||
private string _onlineScene;
|
||||
|
||||
/// <summary>
|
||||
/// Sets which online scene to use.
|
||||
/// </summary>
|
||||
/// <param name = "sceneName">Scene name to use as the online scene.</param>
|
||||
public void SetOnlineScene(string sceneName) => _onlineScene = sceneName;
|
||||
|
||||
/// <summary>
|
||||
/// Scene to load when connected. Server and client will load this scene.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetOnlineScene() => _onlineScene;
|
||||
|
||||
/// <summary>
|
||||
/// Which scenes to replace when loading into OnlineScene.
|
||||
/// </summary>
|
||||
[Tooltip("Which scenes to replace when loading into OnlineScene.")]
|
||||
[SerializeField]
|
||||
private ReplaceOption _replaceScenes = ReplaceOption.All;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// NetworkManager for this component.
|
||||
/// </summary>
|
||||
private NetworkManager _networkManager;
|
||||
#endregion
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Deinitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this script for use.
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
_networkManager = GetComponentInParent<NetworkManager>();
|
||||
if (_networkManager == null)
|
||||
{
|
||||
_networkManager.LogError($"NetworkManager not found on {gameObject.name} or any parent objects. DefaultScene will not work.");
|
||||
return;
|
||||
}
|
||||
// A NetworkManager won't be initialized if it's being destroyed.
|
||||
if (!_networkManager.Initialized)
|
||||
return;
|
||||
if (_onlineScene == string.Empty || _offlineScene == string.Empty)
|
||||
{
|
||||
_networkManager.LogWarning("Online or Offline scene is not specified. Default scenes will not load.");
|
||||
return;
|
||||
}
|
||||
|
||||
_networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState;
|
||||
_networkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState;
|
||||
_networkManager.SceneManager.OnLoadEnd += SceneManager_OnLoadEnd;
|
||||
_networkManager.ServerManager.OnAuthenticationResult += ServerManager_OnAuthenticationResult;
|
||||
if (_startInOffline)
|
||||
LoadOfflineScene();
|
||||
}
|
||||
|
||||
private void Deinitialize()
|
||||
{
|
||||
if (!ApplicationState.IsQuitting() && _networkManager != null && _networkManager.Initialized)
|
||||
{
|
||||
_networkManager.ClientManager.OnClientConnectionState -= ClientManager_OnClientConnectionState;
|
||||
_networkManager.ServerManager.OnServerConnectionState -= ServerManager_OnServerConnectionState;
|
||||
_networkManager.SceneManager.OnLoadEnd -= SceneManager_OnLoadEnd;
|
||||
_networkManager.ServerManager.OnAuthenticationResult -= ServerManager_OnAuthenticationResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a scene load ends.
|
||||
/// </summary>
|
||||
private void SceneManager_OnLoadEnd(SceneLoadEndEventArgs obj)
|
||||
{
|
||||
bool onlineLoaded = false;
|
||||
foreach (Scene s in obj.LoadedScenes)
|
||||
{
|
||||
if (s.name == GetSceneName(_onlineScene))
|
||||
{
|
||||
onlineLoaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If online scene was loaded then unload offline.
|
||||
if (onlineLoaded)
|
||||
UnloadOfflineScene();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after the local server connection state changes.
|
||||
/// </summary>
|
||||
private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj)
|
||||
{
|
||||
/* When server starts load online scene as global.
|
||||
* Since this is a global scene clients will automatically
|
||||
* join it when connecting. */
|
||||
if (obj.ConnectionState == LocalConnectionState.Started)
|
||||
{
|
||||
/* If not exactly one server is started then
|
||||
* that means either none are started, which isnt true because
|
||||
* we just got a started callback, or two+ are started.
|
||||
* When a server has already started there's no reason to load
|
||||
* scenes again. */
|
||||
if (!_networkManager.ServerManager.IsOnlyOneServerStarted())
|
||||
return;
|
||||
|
||||
// If here can load scene.
|
||||
SceneLoadData sld = new(GetSceneName(_onlineScene));
|
||||
sld.ReplaceScenes = _replaceScenes;
|
||||
if (_enableGlobalScenes)
|
||||
_networkManager.SceneManager.LoadGlobalScenes(sld);
|
||||
else
|
||||
_networkManager.SceneManager.LoadConnectionScenes(sld);
|
||||
}
|
||||
// When server stops load offline scene.
|
||||
else if (obj.ConnectionState == LocalConnectionState.Stopped && !_networkManager.ServerManager.IsAnyServerStarted())
|
||||
{
|
||||
LoadOfflineScene();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after the local client connection state changes.
|
||||
/// </summary>
|
||||
private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj)
|
||||
{
|
||||
if (obj.ConnectionState == LocalConnectionState.Stopped)
|
||||
{
|
||||
// Only load offline scene if not also server.
|
||||
if (!_networkManager.IsServerStarted)
|
||||
LoadOfflineScene();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a client completes authentication.
|
||||
/// </summary>
|
||||
private void ServerManager_OnAuthenticationResult(NetworkConnection arg1, bool authenticated)
|
||||
{
|
||||
/* This is only for loading connection scenes.
|
||||
* If using global there is no need to continue. */
|
||||
if (_enableGlobalScenes)
|
||||
return;
|
||||
if (!authenticated)
|
||||
return;
|
||||
|
||||
SceneLoadData sld = new(GetSceneName(_onlineScene));
|
||||
_networkManager.SceneManager.LoadConnectionScenes(arg1, sld);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads offlineScene as single.
|
||||
/// </summary>
|
||||
private void LoadOfflineScene()
|
||||
{
|
||||
// Already in offline scene.
|
||||
if (UnitySceneManager.GetActiveScene().name == GetSceneName(_offlineScene))
|
||||
return;
|
||||
// Only use scene manager if networking scenes. I may add something in later to do both local and networked.
|
||||
UnitySceneManager.LoadScene(_offlineScene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the offline scene.
|
||||
/// </summary>
|
||||
private void UnloadOfflineScene()
|
||||
{
|
||||
Scene s = UnitySceneManager.GetSceneByName(GetSceneName(_offlineScene));
|
||||
if (string.IsNullOrEmpty(s.name))
|
||||
return;
|
||||
|
||||
UnitySceneManager.UnloadSceneAsync(s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a scene name from fullPath.
|
||||
/// </summary>
|
||||
/// <param name = "fullPath"></param>
|
||||
/// <returns></returns>
|
||||
private string GetSceneName(string fullPath)
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57ce8bbb58966cb45a7140f32da5327a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,251 @@
|
||||
using FishNet.Managing;
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Managing.Timing;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Prediction;
|
||||
using FishNet.Utility.Extension;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Component.Transforming
|
||||
{
|
||||
/// <summary>
|
||||
/// Detaches the object which this component resides and follows another.
|
||||
/// </summary>
|
||||
public class DetachableNetworkTickSmoother : NetworkBehaviour
|
||||
{
|
||||
#region Serialized.
|
||||
/// <summary>
|
||||
/// True to attach the object to it's original parent when OnStopClient is called.
|
||||
/// </summary>
|
||||
[Tooltip("True to attach the object to it's original parent when OnStopClient is called.")]
|
||||
[SerializeField]
|
||||
private bool _attachOnStop = true;
|
||||
/// <summary>
|
||||
/// Object to follow, and smooth towards.
|
||||
/// </summary>
|
||||
[Tooltip("Object to follow, and smooth towards.")]
|
||||
[SerializeField]
|
||||
private Transform _followObject;
|
||||
/// <summary>
|
||||
/// How many ticks to interpolate over.
|
||||
/// </summary>
|
||||
[Tooltip("How many ticks to interpolate over.")]
|
||||
[Range(1, byte.MaxValue)]
|
||||
[SerializeField]
|
||||
private byte _interpolation = 1;
|
||||
/// <summary>
|
||||
/// True to enable teleport threshhold.
|
||||
/// </summary>
|
||||
[Tooltip("True to enable teleport threshold.")]
|
||||
[SerializeField]
|
||||
private bool _enableTeleport;
|
||||
/// <summary>
|
||||
/// How far the object must move between ticks to teleport rather than smooth.
|
||||
/// </summary>
|
||||
[Tooltip("How far the object must move between ticks to teleport rather than smooth.")]
|
||||
[Range(0f, ushort.MaxValue)]
|
||||
[SerializeField]
|
||||
private float _teleportThreshold;
|
||||
/// <summary>
|
||||
/// True to synchronize the position of the followObject.
|
||||
/// </summary>
|
||||
[Tooltip("True to synchronize the position of the followObject.")]
|
||||
[SerializeField]
|
||||
private bool _synchronizePosition = true;
|
||||
/// <summary>
|
||||
/// True to synchronize the rotation of the followObject.
|
||||
/// </summary>
|
||||
[Tooltip("True to synchronize the rotation of the followObject.")]
|
||||
[SerializeField]
|
||||
private bool _synchronizeRotation;
|
||||
/// <summary>
|
||||
/// True to synchronize the scale of the followObject.
|
||||
/// </summary>
|
||||
[Tooltip("True to synchronize the scale of the followObject.")]
|
||||
[SerializeField]
|
||||
private bool _synchronizeScale;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// TimeManager subscribed to.
|
||||
/// </summary>
|
||||
private TimeManager _timeManager;
|
||||
/// <summary>
|
||||
/// Parent of the object prior to detaching.
|
||||
/// </summary>
|
||||
private Transform _parent;
|
||||
/// <summary>
|
||||
/// Local properties of the graphical during instantation.
|
||||
/// </summary>
|
||||
private TransformProperties _transformInstantiatedLocalProperties;
|
||||
/// <summary>
|
||||
/// World properties of the followObject during post tick.
|
||||
/// </summary>
|
||||
private TransformProperties _postTickFollowObjectWorldProperties;
|
||||
/// <summary>
|
||||
/// How quickly to move towards target.
|
||||
/// </summary>
|
||||
private MoveRates _moveRates = new(MoveRates.INSTANT_VALUE);
|
||||
/// <summary>
|
||||
/// True if initialized.
|
||||
/// </summary>
|
||||
private bool _initialized;
|
||||
/// <summary>
|
||||
/// Cached TickDelta of the TimeManager.
|
||||
/// </summary>
|
||||
private float _tickDelta;
|
||||
|
||||
private static readonly ProfilerMarker _pm_OnPostTick = new("DetachableNetworkTickSmoother._timeManager_OnPostTick()");
|
||||
#endregion
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_transformInstantiatedLocalProperties = transform.GetLocalProperties();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ChangeSubscription(false);
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
bool error = false;
|
||||
if (transform.parent == null)
|
||||
{
|
||||
NetworkManager.LogError($"{GetType().Name} on gameObject {gameObject.name} requires a parent to detach from.");
|
||||
error = true;
|
||||
}
|
||||
if (_followObject == null)
|
||||
{
|
||||
NetworkManager.LogError($"{GetType().Name} on gameObject {gameObject}, root {transform.root} requires followObject to be set.");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
_parent = transform.parent;
|
||||
transform.SetParent(null);
|
||||
|
||||
SetTimeManager(TimeManager);
|
||||
// Unsub first in the rare chance we already subbed such as a stop callback issue.
|
||||
ChangeSubscription(false);
|
||||
ChangeSubscription(true);
|
||||
|
||||
_postTickFollowObjectWorldProperties = _followObject.GetWorldProperties();
|
||||
_tickDelta = (float)TimeManager.TickDelta;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (ApplicationState.IsQuitting())
|
||||
return;
|
||||
#endif
|
||||
// Reattach to parent.
|
||||
if (_attachOnStop && _parent != null)
|
||||
{
|
||||
// Reparent
|
||||
transform.SetParent(_parent);
|
||||
// Set to instantiated local values.
|
||||
transform.SetLocalProperties(_transformInstantiatedLocalProperties);
|
||||
}
|
||||
|
||||
_postTickFollowObjectWorldProperties.ResetState();
|
||||
ChangeSubscription(false);
|
||||
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
[Client(Logging = LoggingType.Off)]
|
||||
private void Update()
|
||||
{
|
||||
MoveTowardsFollowTarget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after a tick completes.
|
||||
/// </summary>
|
||||
private void _timeManager_OnPostTick()
|
||||
{
|
||||
using (_pm_OnPostTick.Auto())
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
_postTickFollowObjectWorldProperties.Update(_followObject);
|
||||
// Unset values if not following the transform property.
|
||||
if (!_synchronizePosition)
|
||||
_postTickFollowObjectWorldProperties.Position = transform.position;
|
||||
if (!_synchronizeRotation)
|
||||
_postTickFollowObjectWorldProperties.Rotation = transform.rotation;
|
||||
if (!_synchronizeScale)
|
||||
_postTickFollowObjectWorldProperties.Scale = transform.localScale;
|
||||
SetMoveRates();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new PredictionManager to use.
|
||||
/// </summary>
|
||||
/// <param name = "tm"></param>
|
||||
private void SetTimeManager(TimeManager tm)
|
||||
{
|
||||
if (tm == _timeManager)
|
||||
return;
|
||||
|
||||
// Unsub from current.
|
||||
ChangeSubscription(false);
|
||||
// Sub to newest.
|
||||
_timeManager = tm;
|
||||
ChangeSubscription(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the subscription to the TimeManager.
|
||||
/// </summary>
|
||||
private void ChangeSubscription(bool subscribe)
|
||||
{
|
||||
if (_timeManager == null)
|
||||
return;
|
||||
|
||||
if (subscribe)
|
||||
_timeManager.OnPostTick += _timeManager_OnPostTick;
|
||||
else
|
||||
_timeManager.OnPostTick -= _timeManager_OnPostTick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves towards targetObject.
|
||||
/// </summary>
|
||||
private void MoveTowardsFollowTarget()
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
_moveRates.Move(transform, _postTickFollowObjectWorldProperties, Time.deltaTime, useWorldSpace: true);
|
||||
}
|
||||
|
||||
private void SetMoveRates()
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
float duration = _tickDelta * _interpolation;
|
||||
/* If interpolation is 1 then add on a tiny amount
|
||||
* of more time to compensate for frame time, so that
|
||||
* the smoothing does not complete before the next tick,
|
||||
* as this would result in jitter. */
|
||||
if (_interpolation == 1)
|
||||
duration += Mathf.Max(Time.deltaTime, 1f / 50f);
|
||||
|
||||
float teleportT = _enableTeleport ? _teleportThreshold : MoveRates.UNSET_VALUE;
|
||||
_moveRates = MoveRates.GetWorldMoveRates(transform, _followObject, duration, teleportT);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c631fa10037fa844292bacd140d7c7f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edb137ec1a2c56540a187929d6b97b54
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
#if UNITY_EDITOR
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using UnityEditor;
|
||||
using LayoutTools = GameKit.Dependencies.Utilities.EditorGuiLayoutTools;
|
||||
|
||||
namespace FishNet.Component.Transforming.Editing
|
||||
{
|
||||
[CustomEditor(typeof(DetachableNetworkTickSmoother), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class DetachableNetworkTickSmootherEditor : Editor
|
||||
{
|
||||
private SerializedProperty _attachOnStop;
|
||||
private SerializedProperty _followObject;
|
||||
private SerializedProperty _interpolation;
|
||||
private SerializedProperty _enableTeleport;
|
||||
private SerializedProperty _teleportThreshold;
|
||||
private SerializedProperty _synchronizePosition;
|
||||
private SerializedProperty _synchronizeRotation;
|
||||
private SerializedProperty _synchronizeScale;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_attachOnStop = serializedObject.FindProperty(nameof(_attachOnStop));
|
||||
_followObject = serializedObject.FindProperty(nameof(_followObject));
|
||||
_interpolation = serializedObject.FindProperty(nameof(_interpolation));
|
||||
_enableTeleport = serializedObject.FindProperty(nameof(_enableTeleport));
|
||||
_teleportThreshold = serializedObject.FindProperty(nameof(_teleportThreshold));
|
||||
_synchronizePosition = serializedObject.FindProperty(nameof(_synchronizePosition));
|
||||
_synchronizeRotation = serializedObject.FindProperty(nameof(_synchronizeRotation));
|
||||
_synchronizeScale = serializedObject.FindProperty(nameof(_synchronizeScale));
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
LayoutTools.AddObjectField("Script:", MonoScript.FromMonoBehaviour((DetachableNetworkTickSmoother)target), typeof(DetachableNetworkTickSmoother), false, EditorLayoutEnableType.Disabled);
|
||||
|
||||
EditorGUILayout.HelpBox("This component will be obsoleted soon. Use NetworkTickSmoother or OfflineTickSmoother.", MessageType.Warning);
|
||||
// Misc.
|
||||
EditorGUILayout.LabelField("Misc", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_attachOnStop);
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Smoothing.
|
||||
EditorGUILayout.LabelField("Smoothing", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_followObject);
|
||||
EditorGUILayout.PropertyField(_interpolation);
|
||||
|
||||
EditorGUILayout.PropertyField(_enableTeleport);
|
||||
if (_enableTeleport.boolValue)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_teleportThreshold);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.Space();
|
||||
|
||||
//Authority.
|
||||
EditorGUILayout.LabelField("Synchronizing", EditorStyles.boldLabel);
|
||||
EditorGUI.indentLevel++;
|
||||
EditorGUILayout.PropertyField(_synchronizePosition);
|
||||
EditorGUILayout.PropertyField(_synchronizeRotation);
|
||||
EditorGUILayout.PropertyField(_synchronizeScale);
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 342594fe005d75a4985e1a8ca218f822
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,164 @@
|
||||
using FishNet.Managing.Logging;
|
||||
using FishNet.Managing.Timing;
|
||||
using FishNet.Object;
|
||||
using FishNet.Object.Prediction;
|
||||
using GameKit.Dependencies.Utilities;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace FishNet.Component.Transforming
|
||||
{
|
||||
/// <summary>
|
||||
/// Smoothes an object between ticks.
|
||||
/// This can be used on objects without NetworkObject components.
|
||||
/// </summary>
|
||||
public class MonoTickSmoother : MonoBehaviour
|
||||
{
|
||||
// Lazy way to display obsolete message w/o using a custom editor.
|
||||
[Header("This component will be obsoleted soon.")]
|
||||
[Header("Use NetworkTickSmoother or OfflineTickSmoother.")]
|
||||
[Header(" ")]
|
||||
|
||||
#region Serialized.
|
||||
/// <summary>
|
||||
/// True to use InstanceFinder to locate the TimeManager. When false specify which TimeManager to use by calling SetTimeManager.
|
||||
/// </summary>
|
||||
[Tooltip("True to use InstanceFinder to locate the TimeManager. When false specify which TimeManager to use by calling SetTimeManager.")]
|
||||
[SerializeField]
|
||||
private bool _useInstanceFinder = true;
|
||||
/// <summary>
|
||||
/// GraphicalObject you wish to smooth.
|
||||
/// </summary>
|
||||
[Tooltip("GraphicalObject you wish to smooth.")]
|
||||
[SerializeField]
|
||||
private Transform _graphicalObject;
|
||||
/// <summary>
|
||||
/// True to enable teleport threshhold.
|
||||
/// </summary>
|
||||
[Tooltip("True to enable teleport threshold.")]
|
||||
[SerializeField]
|
||||
private bool _enableTeleport;
|
||||
/// <summary>
|
||||
/// How far the object must move between ticks to teleport rather than smooth.
|
||||
/// </summary>
|
||||
[Tooltip("How far the object must move between ticks to teleport rather than smooth.")]
|
||||
[Range(0f, ushort.MaxValue)]
|
||||
[SerializeField]
|
||||
private float _teleportThreshold;
|
||||
#endregion
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// TimeManager subscribed to.
|
||||
/// </summary>
|
||||
private TimeManager _timeManager;
|
||||
/// <summary>
|
||||
/// BasicTickSmoother for this script.
|
||||
/// </summary>
|
||||
private LocalTransformTickSmoother _tickSmoother;
|
||||
#endregion
|
||||
|
||||
#region Private Profiler Markers
|
||||
|
||||
private static readonly ProfilerMarker _pm_OnPreTick = new("MonoTickSmoother._timeManager_OnPreTick()");
|
||||
private static readonly ProfilerMarker _pm_OnPostTick = new("MonoTickSmoother._timeManager_OnPostTick()");
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_tickSmoother.ResetState();
|
||||
ChangeSubscription(false);
|
||||
ObjectCaches<LocalTransformTickSmoother>.StoreAndDefault(ref _tickSmoother);
|
||||
}
|
||||
|
||||
[Client(Logging = LoggingType.Off)]
|
||||
private void Update()
|
||||
{
|
||||
_tickSmoother?.Update();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this script for use.
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
_tickSmoother = ObjectCaches<LocalTransformTickSmoother>.Retrieve();
|
||||
if (_useInstanceFinder)
|
||||
{
|
||||
_timeManager = InstanceFinder.TimeManager;
|
||||
ChangeSubscription(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new PredictionManager to use.
|
||||
/// </summary>
|
||||
/// <param name = "tm"></param>
|
||||
public void SetTimeManager(TimeManager tm)
|
||||
{
|
||||
if (tm == _timeManager)
|
||||
return;
|
||||
|
||||
// Unsub from current.
|
||||
ChangeSubscription(false);
|
||||
// Sub to newest.
|
||||
_timeManager = tm;
|
||||
ChangeSubscription(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the subscription to the TimeManager.
|
||||
/// </summary>
|
||||
private void ChangeSubscription(bool subscribe)
|
||||
{
|
||||
if (_timeManager == null)
|
||||
return;
|
||||
|
||||
if (subscribe)
|
||||
{
|
||||
if (_tickSmoother != null)
|
||||
{
|
||||
float tDistance = _enableTeleport ? _teleportThreshold : MoveRates.UNSET_VALUE;
|
||||
_tickSmoother.InitializeOnce(_graphicalObject, tDistance, (float)_timeManager.TickDelta, 1);
|
||||
}
|
||||
_timeManager.OnPreTick += _timeManager_OnPreTick;
|
||||
_timeManager.OnPostTick += _timeManager_OnPostTick;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timeManager.OnPreTick -= _timeManager_OnPreTick;
|
||||
_timeManager.OnPostTick -= _timeManager_OnPostTick;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before a tick starts.
|
||||
/// </summary>
|
||||
private void _timeManager_OnPreTick()
|
||||
{
|
||||
using (_pm_OnPreTick.Auto())
|
||||
{
|
||||
_tickSmoother.OnPreTick();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after a tick completes.
|
||||
/// </summary>
|
||||
private void _timeManager_OnPostTick()
|
||||
{
|
||||
using (_pm_OnPostTick.Auto())
|
||||
{
|
||||
_tickSmoother.OnPostTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3c0f5e921f9d784986ee1c8d311ccba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs
|
||||
uploadId: 866910
|
||||
@@ -0,0 +1,108 @@
|
||||
using FishNet.Managing.Timing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FishNet.Component.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Add to any object to display current ping(round trip time).
|
||||
/// </summary>
|
||||
[AddComponentMenu("FishNet/Component/PingDisplay")]
|
||||
public class PingDisplay : MonoBehaviour
|
||||
{
|
||||
#region Types.
|
||||
private enum Corner
|
||||
{
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Serialized.
|
||||
/// <summary>
|
||||
/// Color for text.
|
||||
/// </summary>
|
||||
[Tooltip("Color for text.")]
|
||||
[SerializeField]
|
||||
private Color _color = Color.white;
|
||||
/// <summary>
|
||||
/// Which corner to display ping in.
|
||||
/// </summary>
|
||||
[Tooltip("Which corner to display ping in.")]
|
||||
[SerializeField]
|
||||
private Corner _placement = Corner.TopRight;
|
||||
/// <summary>
|
||||
/// True to show the real ping. False to include tick rate latency within the ping.
|
||||
/// </summary>
|
||||
[Tooltip("True to show the real ping. False to include tick rate latency within the ping.")]
|
||||
[SerializeField]
|
||||
private bool _hideTickRate = true;
|
||||
#endregion
|
||||
|
||||
#if UNITY_EDITOR || !UNITY_SERVER
|
||||
|
||||
#region Private.
|
||||
/// <summary>
|
||||
/// Style for drawn ping.
|
||||
/// </summary>
|
||||
private GUIStyle _style = new();
|
||||
#endregion
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
// Only clients can see pings.
|
||||
if (!InstanceFinder.IsClientStarted)
|
||||
return;
|
||||
|
||||
_style.normal.textColor = _color;
|
||||
_style.fontSize = 15;
|
||||
float width = 85f;
|
||||
float height = 15f;
|
||||
float edge = 10f;
|
||||
|
||||
float horizontal;
|
||||
float vertical;
|
||||
|
||||
if (_placement == Corner.TopLeft)
|
||||
{
|
||||
horizontal = 10f;
|
||||
vertical = 10f;
|
||||
}
|
||||
else if (_placement == Corner.TopRight)
|
||||
{
|
||||
horizontal = Screen.width - width - edge;
|
||||
vertical = 10f;
|
||||
}
|
||||
else if (_placement == Corner.BottomLeft)
|
||||
{
|
||||
horizontal = 10f;
|
||||
vertical = Screen.height - height - edge;
|
||||
}
|
||||
else
|
||||
{
|
||||
horizontal = Screen.width - width - edge;
|
||||
vertical = Screen.height - height - edge;
|
||||
}
|
||||
|
||||
long ping;
|
||||
TimeManager tm = InstanceFinder.TimeManager;
|
||||
if (tm == null)
|
||||
{
|
||||
ping = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ping = tm.RoundTripTime;
|
||||
long deduction = 0;
|
||||
if (_hideTickRate)
|
||||
deduction = (long)(tm.TickDelta * 2000d);
|
||||
|
||||
ping = (long)Mathf.Max(1, ping - deduction);
|
||||
}
|
||||
|
||||
GUI.Label(new(horizontal, vertical, width, height), $"Ping: {ping}ms", _style);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9b6b565cd9533c4ebc18003f0fc18a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 207815
|
||||
packageName: 'FishNet: Networking Evolved'
|
||||
packageVersion: 4.6.22R
|
||||
assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs
|
||||
uploadId: 866910
|
||||
Reference in New Issue
Block a user