diff --git a/Adapters/AssemblyInfo.cs b/Adapters/AssemblyInfo.cs index 5944d6a..93cd3eb 100644 --- a/Adapters/AssemblyInfo.cs +++ b/Adapters/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; // Adapters +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.MockNgo")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Ngo1")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Utp2")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Ngo1WithUtp2")] @@ -10,12 +11,14 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Runtime")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Configuration")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.UI")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.Visualization")] // Test assemblies -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Tests.Editor")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] - #if UNITY_INCLUDE_TESTS -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Tests.Runtime.NetworkSimulator")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Tests.Runtime")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] #endif diff --git a/Adapters/Components/Actions/IHandleNetworkParameters.cs b/Adapters/Components/Actions/IHandleNetworkParameters.cs index 84c836e..9e8a58a 100644 --- a/Adapters/Components/Actions/IHandleNetworkParameters.cs +++ b/Adapters/Components/Actions/IHandleNetworkParameters.cs @@ -15,4 +15,4 @@ class NetworkParameters public int PacketLossPercent { get; set; } } -} \ No newline at end of file +} diff --git a/Adapters/Components/Actions/ISimulateDisconnect.cs b/Adapters/Components/Actions/ISimulateDisconnect.cs index aedd5dc..bd05104 100644 --- a/Adapters/Components/Actions/ISimulateDisconnect.cs +++ b/Adapters/Components/Actions/ISimulateDisconnect.cs @@ -4,4 +4,4 @@ interface ISimulateDisconnect : IAdapterComponent { void SimulateDisconnect(); } -} \ No newline at end of file +} diff --git a/Adapters/Components/Actions/ISimulateDisconnectAndReconnect.cs b/Adapters/Components/Actions/ISimulateDisconnectAndReconnect.cs index 4ba4d56..93fc685 100644 --- a/Adapters/Components/Actions/ISimulateDisconnectAndReconnect.cs +++ b/Adapters/Components/Actions/ISimulateDisconnectAndReconnect.cs @@ -3,4 +3,4 @@ namespace Unity.Multiplayer.Tools.Adapters interface ISimulateDisconnectAndReconnect : IAdapterComponent, ISimulateDisconnect, ISimulateReconnect { } -} \ No newline at end of file +} diff --git a/Adapters/Components/Actions/ISimulateReconnect.cs b/Adapters/Components/Actions/ISimulateReconnect.cs index f47fd4b..b82b8ec 100644 --- a/Adapters/Components/Actions/ISimulateReconnect.cs +++ b/Adapters/Components/Actions/ISimulateReconnect.cs @@ -4,4 +4,4 @@ interface ISimulateReconnect : IAdapterComponent { void SimulateReconnect(); } -} \ No newline at end of file +} diff --git a/Adapters/Components/Events/IGetConnectedClients.cs b/Adapters/Components/Events/IGetConnectedClients.cs new file mode 100644 index 0000000..9499891 --- /dev/null +++ b/Adapters/Components/Events/IGetConnectedClients.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Unity.Multiplayer.Tools.Adapters +{ + interface IGetConnectedClients : IAdapterComponent + { + IReadOnlyList ConnectedClients { get; } + event Action ClientConnectionEvent; + event Action ClientDisconnectionEvent; + } +} \ No newline at end of file diff --git a/Adapters/Components/Events/IGetConnectedClients.cs.meta b/Adapters/Components/Events/IGetConnectedClients.cs.meta new file mode 100644 index 0000000..e7159df --- /dev/null +++ b/Adapters/Components/Events/IGetConnectedClients.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2b11a672b58b4af6869de0959404b3ee +timeCreated: 1677506994 \ No newline at end of file diff --git a/Adapters/Components/Events/IMetricCollectionEvent.cs b/Adapters/Components/Events/IMetricCollectionEvent.cs index bf2975e..bc28346 100644 --- a/Adapters/Components/Events/IMetricCollectionEvent.cs +++ b/Adapters/Components/Events/IMetricCollectionEvent.cs @@ -1,5 +1,4 @@ using System; - using Unity.Multiplayer.Tools.NetStats; namespace Unity.Multiplayer.Tools.Adapters diff --git a/Adapters/Components/Queries/IGetBandwidth.cs b/Adapters/Components/Queries/IGetBandwidth.cs index 2ef1be5..3071e64 100644 --- a/Adapters/Components/Queries/IGetBandwidth.cs +++ b/Adapters/Components/Queries/IGetBandwidth.cs @@ -1,10 +1,40 @@ +using System; +using Unity.Multiplayer.Tools.Common; + namespace Unity.Multiplayer.Tools.Adapters { /// - /// Interface to get object bandwidth in bytes this frame + /// Interface to get the bandwidth in bytes of an object this frame /// interface IGetBandwidth : IAdapterComponent { - int GetBandwidthBytes(ObjectId objectId); + /// + /// Returns the types of bandwidth that are supported by this adapter + /// + BandwidthTypes SupportedBandwidthTypes { get; } + + /// + /// Returns the amount of bandwidth related to this object, filtered by bandwidth type and direction. + /// + /// + /// If any of the bandwidth type flags are not supported by this adapter then they will be ignored + /// and will not contribute to the total bandwidth returned. If none of the bandwidth flags are + /// supported by this adapter then the bandwidth returned will be zero. + /// + /// + /// may be thrown if this method is called without any subscribers to . + /// This is to ensure that object bandwidth is computed and stored only if required. + /// + float GetBandwidthBytes( + ObjectId objectId, + BandwidthTypes bandwidthTypes = BandwidthTypes.All, + NetworkDirection networkDirection = NetworkDirection.SentAndReceived); + + /// + /// Event that is triggered when new object bandwidth data is available. + /// If there are no subscribers, then bandwidth may not be computed and stored + /// by the underlying implementation. + /// + event Action OnBandwidthUpdated; } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetGameObject.cs b/Adapters/Components/Queries/IGetGameObject.cs index a128ba7..dea3914 100644 --- a/Adapters/Components/Queries/IGetGameObject.cs +++ b/Adapters/Components/Queries/IGetGameObject.cs @@ -9,4 +9,4 @@ interface IGetGameObject : IAdapterComponent { GameObject GetGameObject(ObjectId objectId); } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetInterest.cs b/Adapters/Components/Queries/IGetInterest.cs index 2378d82..b8672fb 100644 --- a/Adapters/Components/Queries/IGetInterest.cs +++ b/Adapters/Components/Queries/IGetInterest.cs @@ -7,4 +7,4 @@ interface IGetInterest : IAdapterComponent { bool GetInterest(ObjectId objectId); } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetObjectIds.cs b/Adapters/Components/Queries/IGetObjectIds.cs index fbc4d1c..0d63db2 100644 --- a/Adapters/Components/Queries/IGetObjectIds.cs +++ b/Adapters/Components/Queries/IGetObjectIds.cs @@ -7,6 +7,6 @@ namespace Unity.Multiplayer.Tools.Adapters /// interface IGetObjectIds : IAdapterComponent { - IEnumerable ObjectIds { get; } + IReadOnlyList ObjectIds { get; } } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetOwnership.cs b/Adapters/Components/Queries/IGetOwnership.cs index 435bceb..fe01d3b 100644 --- a/Adapters/Components/Queries/IGetOwnership.cs +++ b/Adapters/Components/Queries/IGetOwnership.cs @@ -7,4 +7,4 @@ interface IGetOwnership : IAdapterComponent { ClientId GetOwner(ObjectId objectId); } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetPriority.cs b/Adapters/Components/Queries/IGetPriority.cs index c6dfc0f..28c7645 100644 --- a/Adapters/Components/Queries/IGetPriority.cs +++ b/Adapters/Components/Queries/IGetPriority.cs @@ -7,4 +7,4 @@ interface IGetPriority : IAdapterComponent { int GetPriority(ObjectId objectId); } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetRpcCount.cs b/Adapters/Components/Queries/IGetRpcCount.cs index f4fd7b0..cb9ec3a 100644 --- a/Adapters/Components/Queries/IGetRpcCount.cs +++ b/Adapters/Components/Queries/IGetRpcCount.cs @@ -1,3 +1,5 @@ +using System; + namespace Unity.Multiplayer.Tools.Adapters { /// @@ -5,6 +7,17 @@ namespace Unity.Multiplayer.Tools.Adapters /// interface IGetRpcCount : IAdapterComponent { + /// + /// may be thrown if this method is called without any subscribers to . + /// This is to ensure that object bandwidth is computed and stored only if required. + /// int GetRpcCount(ObjectId objectId); + + /// + /// Event that is triggered when new object RPC counts are available. + /// If there are no subscribers, then RPC counts may not be computed and stored + /// by the underlying implementation. + /// + event Action OnRpcCountUpdated; } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetTransformLastKnown.cs b/Adapters/Components/Queries/IGetTransformLastKnown.cs index 0e23e48..921675d 100644 --- a/Adapters/Components/Queries/IGetTransformLastKnown.cs +++ b/Adapters/Components/Queries/IGetTransformLastKnown.cs @@ -7,4 +7,4 @@ interface IGetLastKnownObjectTransform : IAdapterComponent { } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetTransformPredicted.cs b/Adapters/Components/Queries/IGetTransformPredicted.cs index 6af7540..38bcd9b 100644 --- a/Adapters/Components/Queries/IGetTransformPredicted.cs +++ b/Adapters/Components/Queries/IGetTransformPredicted.cs @@ -4,4 +4,4 @@ interface IGetObjectTransformPredicted : IAdapterComponent { } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/IGetTransformWithoutArtificialLatency.cs b/Adapters/Components/Queries/IGetTransformWithoutArtificialLatency.cs index f6f5d20..062f1a4 100644 --- a/Adapters/Components/Queries/IGetTransformWithoutArtificialLatency.cs +++ b/Adapters/Components/Queries/IGetTransformWithoutArtificialLatency.cs @@ -4,4 +4,4 @@ interface IGetTransformWithoutArtificialLatency : IAdapterComponent { } -} \ No newline at end of file +} diff --git a/Adapters/Components/Queries/INetworkAvailability.cs b/Adapters/Components/Queries/INetworkAvailability.cs index a72f595..8aba1e7 100644 --- a/Adapters/Components/Queries/INetworkAvailability.cs +++ b/Adapters/Components/Queries/INetworkAvailability.cs @@ -4,4 +4,4 @@ interface INetworkAvailability : IAdapterComponent { bool IsConnected { get; } } -} \ No newline at end of file +} diff --git a/Adapters/DataTypes/ObjectId.cs b/Adapters/DataTypes/ObjectId.cs index 3fca244..b776c57 100644 --- a/Adapters/DataTypes/ObjectId.cs +++ b/Adapters/DataTypes/ObjectId.cs @@ -13,4 +13,4 @@ enum ObjectId : long { // No members on purpose, this is a wrapper struct for strong typing } -} \ No newline at end of file +} diff --git a/Adapters/Exceptions.meta b/Adapters/Exceptions.meta new file mode 100644 index 0000000..b902a4b --- /dev/null +++ b/Adapters/Exceptions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2d5ecb1af654151a4af63a64a7560a5 +timeCreated: 1659121614 \ No newline at end of file diff --git a/Adapters/Exceptions/NoSubscribersException.cs b/Adapters/Exceptions/NoSubscribersException.cs new file mode 100644 index 0000000..1fd3144 --- /dev/null +++ b/Adapters/Exceptions/NoSubscribersException.cs @@ -0,0 +1,18 @@ +using System; + +namespace Unity.Multiplayer.Tools.Adapters +{ + /// + /// Exception for accessing information from the adapter + /// when there are no subscribers for this piece of information. + /// + class NoSubscribersException : Exception + { + public NoSubscribersException(string resourceName, string subscriptionMethodName) + : base( + $"Attempt to use {resourceName} without any subscribers. " + + $"Subscribe using {subscriptionMethodName}, so that the " + + $"adapter knows to compute and store this information.") + {} + } +} diff --git a/Common/Tests/Data/ExponentialMovingAverageTests.cs.meta b/Adapters/Exceptions/NoSubscribersException.cs.meta similarity index 83% rename from Common/Tests/Data/ExponentialMovingAverageTests.cs.meta rename to Adapters/Exceptions/NoSubscribersException.cs.meta index c9bfa71..13aca78 100644 --- a/Common/Tests/Data/ExponentialMovingAverageTests.cs.meta +++ b/Adapters/Exceptions/NoSubscribersException.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6f54d9b18a9af432db08c5859ca06525 +guid: 98cb296cccf3a40bdbefa331c15910da MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Adapters/IAdapterComponent.cs b/Adapters/IAdapterComponent.cs index fde2595..3fb72f5 100644 --- a/Adapters/IAdapterComponent.cs +++ b/Adapters/IAdapterComponent.cs @@ -8,4 +8,4 @@ interface IAdapterComponent { } -} \ No newline at end of file +} diff --git a/Adapters/INetworkAdapter.cs b/Adapters/INetworkAdapter.cs index c6f18f5..327bb7c 100644 --- a/Adapters/INetworkAdapter.cs +++ b/Adapters/INetworkAdapter.cs @@ -9,4 +9,4 @@ interface INetworkAdapter [CanBeNull] T GetComponent() where T : class, IAdapterComponent; } -} \ No newline at end of file +} diff --git a/Adapters/Metadata/AdapterMetadata.cs b/Adapters/Metadata/AdapterMetadata.cs index 70a7a6e..516a8ce 100644 --- a/Adapters/Metadata/AdapterMetadata.cs +++ b/Adapters/Metadata/AdapterMetadata.cs @@ -4,4 +4,4 @@ struct AdapterMetadata { public PackageInfo PackageInfo { get; set; } }; -} \ No newline at end of file +} diff --git a/Adapters/Metadata/PackageInfo.cs b/Adapters/Metadata/PackageInfo.cs index 8643628..9ca3984 100644 --- a/Adapters/Metadata/PackageInfo.cs +++ b/Adapters/Metadata/PackageInfo.cs @@ -5,4 +5,4 @@ struct PackageInfo public string PackageName { get; set; } public PackageVersion Version { get; set; } }; -} \ No newline at end of file +} diff --git a/Adapters/Metadata/PackageVersion.cs b/Adapters/Metadata/PackageVersion.cs index 93b215d..bd10434 100644 --- a/Adapters/Metadata/PackageVersion.cs +++ b/Adapters/Metadata/PackageVersion.cs @@ -7,4 +7,4 @@ struct PackageVersion public int Patch { get; set; } public string PreRelease { get; set; } } -} \ No newline at end of file +} diff --git a/Common/Tests.meta b/Adapters/MockNgo.meta similarity index 77% rename from Common/Tests.meta rename to Adapters/MockNgo.meta index e6e4335..65397bf 100644 --- a/Common/Tests.meta +++ b/Adapters/MockNgo.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 226aaa1e2f2b045d4afbf014a2508bb4 +guid: 3449872f1c8314bf7bf71258e445af6f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Adapters/MockNgo/Constants.cs b/Adapters/MockNgo/Constants.cs new file mode 100644 index 0000000..561dfe3 --- /dev/null +++ b/Adapters/MockNgo/Constants.cs @@ -0,0 +1,17 @@ +namespace Unity.Multiplayer.Tools.Adapters.MockNgo +{ + class Constants + { + public const int k_DefaultSpawnMessageByteCount = 36; + + public const int k_DefaultDespawnMessageByteCount = 8; + + public const int k_DefaultOwnershipChangeByteCount = 16; + + public const int k_DefaultPositionChangeByteCount = 12; + + public const int k_DefaultRotationChangeByteCount = 12; + + public const int k_DefaultScaleChangeByteCount = 12; + } +} diff --git a/Adapters/MockNgo/Constants.cs.meta b/Adapters/MockNgo/Constants.cs.meta new file mode 100644 index 0000000..61bda4f --- /dev/null +++ b/Adapters/MockNgo/Constants.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2719f4f800c44dcc99659c5435f671dd +timeCreated: 1675384662 \ No newline at end of file diff --git a/Adapters/MockNgo/MockNetworkManager.cs b/Adapters/MockNgo/MockNetworkManager.cs new file mode 100644 index 0000000..e6343f5 --- /dev/null +++ b/Adapters/MockNgo/MockNetworkManager.cs @@ -0,0 +1,79 @@ +using System; +using UnityEngine; + +namespace Unity.Multiplayer.Tools.Adapters.MockNgo +{ + /// + /// A mock network manager containing the mock client ID of the local instance + /// and parameters of the underlying mock network solution (like how many bytes + /// must be sent or received to update an object's position) + /// + /// + /// There should be only one of these. + /// + [AddComponentMenu("MP Tools Dev/" + nameof(MockNetworkManager), 1000)] + public class MockNetworkManager : MonoBehaviour + { + internal MockNgoAdapter Adapter { get; private set; } + + void Awake() + { + Adapter = new(() => (ClientId)LocalClientID); + } + + void LateUpdate() + { + Adapter.OnLateUpdate(); + } + + void OnDestroy() + { + Adapter.Dispose(); + } + + /// + /// The client ID of the local instance + /// + public int LocalClientID { get; set; } = 0; + + /// + /// The number of bytes of network traffic incurred when an object is spawned. + /// + [field:Min(0)] + [field:SerializeField] + public int SpawnMessageByteCount { get; set; } = Constants.k_DefaultSpawnMessageByteCount; + + /// + /// The number of bytes of network traffic incurred when an object is despawned. + /// + [field:Min(0)] + [field:SerializeField] + public int DespawnMessageByteCount { get; set; } = Constants.k_DefaultDespawnMessageByteCount; + + /// + /// The number of bytes of network traffic incurred when an object's owner is changed + /// + [field:Min(0)] + [field:SerializeField] + public int OwnershipChangeByteCount { get; set; } = Constants.k_DefaultOwnershipChangeByteCount; + + /// + /// The number of bytes of network traffic incurred when an object's position changes. + /// + [field:Min(0)] + [field:SerializeField] + public int PositionUpdateByteCount { get; set; } = Constants.k_DefaultPositionChangeByteCount; + + /// + /// The number of bytes of network traffic incurred when an object's rotation changes. + /// + [field:Min(0)] + [field:SerializeField] + public int RotationUpdateByteCount { get; set; } = Constants.k_DefaultRotationChangeByteCount; + + /// The number of bytes of network traffic incurred when an object's scale changes. + [field:Min(0)] + [field:SerializeField] + public int ScaleUpdateByteCount { get; set; } = Constants.k_DefaultScaleChangeByteCount; + } +} diff --git a/Adapters/MockNgo/MockNetworkManager.cs.meta b/Adapters/MockNgo/MockNetworkManager.cs.meta new file mode 100644 index 0000000..df44a08 --- /dev/null +++ b/Adapters/MockNgo/MockNetworkManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d41971b808c241588ee1ae520b327008 +timeCreated: 1675383586 \ No newline at end of file diff --git a/Adapters/MockNgo/MockNetworkObject.cs b/Adapters/MockNgo/MockNetworkObject.cs new file mode 100644 index 0000000..da93091 --- /dev/null +++ b/Adapters/MockNgo/MockNetworkObject.cs @@ -0,0 +1,106 @@ +using System; +using UnityEngine; + +namespace Unity.Multiplayer.Tools.Adapters.MockNgo +{ + [AddComponentMenu("MP Tools Dev/" + nameof(MockNetworkObject), 1000)] + public class MockNetworkObject : MonoBehaviour + { + // Public properties + // -------------------------------------------------------------------- + [field:SerializeField] + public int OwnerClientId { get; set; } + + ClientId Owner + { + get => (ClientId)OwnerClientId; + set => OwnerClientId = (int)value; + } + + public int ObjectId { get; private set; } = -1; + + // Cached references + // -------------------------------------------------------------------- + MockNetworkManager NetworkManager { get; set; } + MockNgoAdapter Adapter => NetworkManager.Adapter; + + // Private fields + // -------------------------------------------------------------------- + int m_PreviousOwner = 0; + ClientId PreviousOwner + { + get => (ClientId)m_PreviousOwner; + set => m_PreviousOwner = (int)value; + } + Vector3 m_PreviousPosition; + Quaternion m_PreviousRotation; + Vector3 m_PreviousScale; + + // Methods to record network traffic + // -------------------------------------------------------------------- + public void RecordRpcCall(int byteCount) + { + Adapter.RecordRpcCall((ObjectId)ObjectId, byteCount); + } + + public void RecordNetworkVariableUpdate(int byteCount) + { + Adapter.RecordNetworkVariableUpdate((ObjectId)ObjectId, byteCount); + } + + void RecordSpawn(int byteCount) + { + PreviousOwner = Owner; + ObjectId = (int)Adapter.RecordObjectSpawn(gameObject, Owner, byteCount); + } + + void RecordDepawn(int byteCount) + { + Adapter.RecordObjectDespawn((ObjectId)ObjectId, Owner, byteCount); + } + + void RecordOwnershipChange(ClientId newOwner, int byteCount) + { + Adapter.RecordOwnershipChange((ObjectId)ObjectId, newOwner, byteCount); + } + + // Event method implementations to record any network traffic that + // would have been incurred if the object was really networked + // -------------------------------------------------------------------- + void Start() + { + NetworkManager = FindAnyObjectByType(); + RecordSpawn(NetworkManager.SpawnMessageByteCount); + } + + void OnDestroy() + { + RecordDepawn(NetworkManager.DespawnMessageByteCount); + } + + void Update() + { + if (PreviousOwner != Owner) + { + RecordOwnershipChange(Owner, NetworkManager.OwnershipChangeByteCount); + PreviousOwner = Owner; + } + + if (m_PreviousPosition != transform.position) + { + RecordNetworkVariableUpdate(NetworkManager.PositionUpdateByteCount); + m_PreviousPosition = transform.position; + } + if (m_PreviousRotation != transform.rotation) + { + RecordNetworkVariableUpdate(NetworkManager.RotationUpdateByteCount); + m_PreviousRotation = transform.rotation; + } + if (m_PreviousScale != transform.localScale) + { + RecordNetworkVariableUpdate(NetworkManager.ScaleUpdateByteCount); + m_PreviousScale = transform.localScale; + } + } + } +} diff --git a/Adapters/MockNgo/MockNetworkObject.cs.meta b/Adapters/MockNgo/MockNetworkObject.cs.meta new file mode 100644 index 0000000..7559780 --- /dev/null +++ b/Adapters/MockNgo/MockNetworkObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c9362ec51a5a49349d38ff43822ef27a +timeCreated: 1675381603 \ No newline at end of file diff --git a/Adapters/MockNgo/MockNgoAdapter.cs b/Adapters/MockNgo/MockNgoAdapter.cs new file mode 100644 index 0000000..c8c63cd --- /dev/null +++ b/Adapters/MockNgo/MockNgoAdapter.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using Unity.Multiplayer.Tools.Common; +using UnityEngine; + +namespace Unity.Multiplayer.Tools.Adapters.MockNgo +{ + class MockNgoAdapter + + : INetworkAdapter + + , IDisposable + + // Events + // -------------------------------------------------------------------- + , IGetConnectedClients + + // Queries + // ---------------------------------------------------------------------------------------- + , IGetBandwidth + , IGetClientId + , IGetGameObject + , IGetObjectIds + , IGetOwnership + , IGetRpcCount + { + public delegate ClientId ClientIdProvider(); + readonly ClientIdProvider m_ClientIdProvider; + public MockNgoAdapter(ClientIdProvider clientIdProvider) + { + m_ClientIdProvider = clientIdProvider; + NetworkAdapters.AddAdapter(this); + } + public void Dispose() + { + NetworkAdapters.RemoveAdapter(this); + } + + public AdapterMetadata Metadata { get; } = new AdapterMetadata + { + PackageInfo = new PackageInfo + { + PackageName = "com.unity.multiplayer.tools.mock", + Version = new PackageVersion + { + Major = 0, + Minor = 0, + Patch = 0, + PreRelease = "" + } + } + }; + public T GetComponent() where T : class, IAdapterComponent + { + return this as T; + } + + // Fields for recording network traffic from MockNetworkObjects + // ---------------------------------------------------------------------------------------- + bool IsServer => LocalClientId == ServerClientId; + + int m_NextValidObjectID = 0; + + readonly List m_ClientIds = new(); + readonly List m_ObjectIds = new(); + Dictionary GameObjects { get; } = new(); + Dictionary ObjectOwners { get; } = new(); + Dictionary ObjectsPerOwner { get; } = new(); + + Dictionary RpcCallCounts { get; } = new(); + Dictionary RpcCallBandwidth { get; } = new(); + Dictionary NetVarBandwidth { get; } = new(); + + /// + /// Additional bandwidth that is not from RPC calls or Network Variable Updates + /// + Dictionary AdditionalBandwidth { get; } = new(); + + int m_LastFrameCount = 0; + void ClearOldData() + { + var frameCount = Time.frameCount; + if (frameCount <= m_LastFrameCount) + { + return; + } + m_LastFrameCount = frameCount; + RpcCallCounts.Clear(); + RpcCallBandwidth.Clear(); + NetVarBandwidth.Clear(); + AdditionalBandwidth.Clear(); + } + + // Internal interface for receiving data from MockNetworkObjects + // ---------------------------------------------------------------------------------------- + void RecordNetworkTraffic( + Dictionary networkTrafficCache, + ObjectId objectId, + int byteCount) + { + ClearOldData(); + var owner = ObjectOwners[objectId]; + var isObjectOwner = owner == LocalClientId; + var bytesSent = (IsServer || isObjectOwner) ? byteCount : 0; + var bytesReceived = isObjectOwner ? 0 : byteCount; + var bytesSentAndReceived = new BytesSentAndReceived(bytesSent, bytesReceived); + if (networkTrafficCache.TryGetValue(objectId, out BytesSentAndReceived existingValue)) + { + networkTrafficCache[objectId] = bytesSentAndReceived + existingValue; + } + else + { + networkTrafficCache[objectId] = bytesSentAndReceived; + } + } + + internal ObjectId RecordObjectSpawn(GameObject gameObject, ClientId owner, int byteCount) + { + var objectId = (ObjectId)m_NextValidObjectID++; + GameObjects[objectId] = gameObject; + ObjectOwners[objectId] = owner; + RecordNetworkTraffic(AdditionalBandwidth, objectId, byteCount); + IncrementOwnerObjectCount(owner); + + m_ObjectIds.Add(objectId); + + return objectId; + } + + internal void RecordObjectDespawn(ObjectId objectId, ClientId owner, int byteCount) + { + GameObjects.Remove(objectId); + ObjectOwners.Remove(objectId); + + m_ObjectIds.Remove(objectId); + + DecrementOwnerObjectCount(owner); + } + + internal void RecordOwnershipChange(ObjectId objectId, ClientId newOwner, int byteCount) + { + if (ObjectOwners.TryGetValue(objectId, out var previousOwner) && previousOwner != newOwner) + { + DecrementOwnerObjectCount(previousOwner); + IncrementOwnerObjectCount(newOwner); + } + + ObjectOwners[objectId] = newOwner; + + RecordNetworkTraffic(AdditionalBandwidth, objectId, byteCount); + } + + internal void RecordRpcCall(ObjectId objectId, int byteCount) + { + RpcCallCounts.TryGetValue(objectId, out var existingRpcCount); + RpcCallCounts[objectId] = existingRpcCount + 1; + RecordNetworkTraffic(RpcCallBandwidth, objectId, byteCount); + } + + internal void RecordNetworkVariableUpdate(ObjectId objectId, int byteCount) + { + RecordNetworkTraffic(NetVarBandwidth, objectId, byteCount); + } + + void IncrementOwnerObjectCount(ClientId owner) + { + if (!ObjectsPerOwner.TryGetValue(owner, out var count) || count == 0) + { + ObjectsPerOwner[owner] = 1; + OnClientConnected(owner); + } + else + { + ObjectsPerOwner[owner] = ++count; + } + } + + void DecrementOwnerObjectCount(ClientId owner) + { + if (ObjectsPerOwner.TryGetValue(owner, out var count) && count > 0) + { + ObjectsPerOwner[owner] = --count; + if (count == 0) + { + OnClientDisconnected(owner); + } + } + } + + void OnClientConnected(ClientId client) + { + if (!m_ClientIds.Contains(client)) + { + m_ClientIds.Add(client); + } + ClientConnectionEvent?.Invoke(client); + } + + void OnClientDisconnected(ClientId client) + { + m_ClientIds.RemoveAll(id => id == client); + ClientDisconnectionEvent?.Invoke(client); + } + + // IGetBandwidth + // ---------------------------------------------------------------------------------------- + public void OnLateUpdate() + { + ClearOldData(); + OnBandwidthUpdated?.Invoke(); + OnRpcCountUpdated?.Invoke(); + } + + public BandwidthTypes SupportedBandwidthTypes => + BandwidthTypes.Other | BandwidthTypes.NetVar | BandwidthTypes.Rpc; + + public float GetBandwidthBytes( + ObjectId objectId, + BandwidthTypes bandwidthTypes = BandwidthTypes.All, + NetworkDirection networkDirection = NetworkDirection.SentAndReceived) + { + if (OnBandwidthUpdated == null) + { + // Although it is not required for implementations of IGetBandwidth to throw + // NoSubscriberException in the event that there are no subscribers, it is + // documented as a possibility in the interface. In order to catch cases in + // which our tools may fail to subscribe before calling GetBandwidthBytes + // we should throw this exception, even though the information is available + // without subscribers. + throw new NoSubscribersException(nameof(IGetBandwidth), nameof(OnBandwidthUpdated)); + } + var total = new BytesSentAndReceived(); + if (bandwidthTypes.ContainsAny(BandwidthTypes.Other) && + AdditionalBandwidth.TryGetValue(objectId, out var additionalBandwidth)) + { + total += additionalBandwidth; + } + if (bandwidthTypes.ContainsAny(BandwidthTypes.NetVar) && + NetVarBandwidth.TryGetValue(objectId, out var netVarBandwidth)) + { + total += netVarBandwidth; + } + if (bandwidthTypes.ContainsAny(BandwidthTypes.Rpc) && + RpcCallBandwidth.TryGetValue(objectId, out var rpcCallBandwidth)) + { + total += rpcCallBandwidth; + } + return total[networkDirection]; + } + public event Action OnBandwidthUpdated; + + // IGetClientId + // ---------------------------------------------------------------------------------------- + public ClientId LocalClientId => m_ClientIdProvider(); + public ClientId ServerClientId => 0; + + // IGetGameObject + // ---------------------------------------------------------------------------------------- + public GameObject GetGameObject(ObjectId objectId) => GameObjects.GetValueOrDefault(objectId); + + // IGetObjectIds + // ---------------------------------------------------------------------------------------- + public IReadOnlyList ObjectIds => m_ObjectIds; + + // IGetOwnership + // ---------------------------------------------------------------------------------------- + public ClientId GetOwner(ObjectId objectId) => ObjectOwners.GetValueOrDefault(objectId); + + // IGetRpcCount + // ---------------------------------------------------------------------------------------- + public int GetRpcCount(ObjectId objectId) => RpcCallCounts.GetValueOrDefault(objectId); + public event Action OnRpcCountUpdated; + + // IClientConnectionEvents + // ---------------------------------------------------------------------------------------- + public IReadOnlyList ConnectedClients => m_ClientIds; + public event Action ClientConnectionEvent; + public event Action ClientDisconnectionEvent; + } +} diff --git a/Adapters/MockNgo/MockNgoAdapter.cs.meta b/Adapters/MockNgo/MockNgoAdapter.cs.meta new file mode 100644 index 0000000..b8811dc --- /dev/null +++ b/Adapters/MockNgo/MockNgoAdapter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a2f3b4cf14d746cf9de3f7743f960cba +timeCreated: 1675380976 \ No newline at end of file diff --git a/Adapters/MockNgo/Unity.Multiplayer.Tools.Adapters.MockNgo.asmdef b/Adapters/MockNgo/Unity.Multiplayer.Tools.Adapters.MockNgo.asmdef new file mode 100644 index 0000000..8177fe9 --- /dev/null +++ b/Adapters/MockNgo/Unity.Multiplayer.Tools.Adapters.MockNgo.asmdef @@ -0,0 +1,9 @@ +{ + "name": "Unity.Multiplayer.Tools.Adapters.MockNgo", + "rootNamespace": "Unity.Multiplayer.Tools.Adapters.MockNgo", + "references": [ + "Unity.Multiplayer.Tools.Adapters", + "Unity.Multiplayer.Tools.Common" + ], + "defineConstraints": ["UNITY_MP_TOOLS_MOCK_NETWORK_ADAPTER"] +} \ No newline at end of file diff --git a/Adapters/MockNgo/Unity.Multiplayer.Tools.Adapters.MockNgo.asmdef.meta b/Adapters/MockNgo/Unity.Multiplayer.Tools.Adapters.MockNgo.asmdef.meta new file mode 100644 index 0000000..ef038b0 --- /dev/null +++ b/Adapters/MockNgo/Unity.Multiplayer.Tools.Adapters.MockNgo.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1c8c2bb6a09b42c08e22de97e4e243ff +timeCreated: 1675380871 \ No newline at end of file diff --git a/Adapters/NetworkAdapters.cs b/Adapters/NetworkAdapters.cs index 18dd4b2..50a2e26 100644 --- a/Adapters/NetworkAdapters.cs +++ b/Adapters/NetworkAdapters.cs @@ -90,4 +90,4 @@ void UnsubscribeFromAllAdapters() return UnsubscribeFromAllAdapters; } } -} \ No newline at end of file +} diff --git a/Adapters/Ngo1/AssemblyInfo.cs b/Adapters/Ngo1/AssemblyInfo.cs deleted file mode 100644 index 54a9c39..0000000 --- a/Adapters/Ngo1/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -using System.Runtime.CompilerServices; - diff --git a/Adapters/Ngo1/Ngo1Adapter.cs b/Adapters/Ngo1/Ngo1Adapter.cs index c72c590..62997fa 100644 --- a/Adapters/Ngo1/Ngo1Adapter.cs +++ b/Adapters/Ngo1/Ngo1Adapter.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; -using System.Linq; -using UnityEngine; - +using System.Diagnostics.CodeAnalysis; +using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.MetricEvents; using Unity.Multiplayer.Tools.NetStats; using Unity.Netcode; +using UnityEngine; namespace Unity.Multiplayer.Tools.Adapters.Ngo1 { @@ -14,6 +15,7 @@ class Ngo1Adapter // Events // -------------------------------------------------------------------- + , IGetConnectedClients , IMetricCollectionEvent // Queries @@ -25,11 +27,66 @@ class Ngo1Adapter , IGetOwnership , IGetRpcCount { - // TODO: Get reference via OnSingletonReady when we have internal access to NGO - NetworkManager NetworkManager => NetworkManager.Singleton; - NetworkSpawnManager SpawnManager => NetworkManager.SpawnManager; + [NotNull] + readonly NetworkManager m_NetworkManager; + + [MaybeNull] + NetworkSpawnManager SpawnManager => m_NetworkManager.SpawnManager; + + [MaybeNull] + Dictionary SpawnedObjects => SpawnManager?.SpawnedObjects; + + public Ngo1Adapter([NotNull] NetworkManager networkManager) + { + DebugUtil.TraceMethodName(); + + Debug.Assert(networkManager != null, $"The parameter {nameof(networkManager)} can't be null."); + + m_NetworkManager = networkManager; + m_NetworkManager.OnClientConnectedCallback += OnClientConnected; + m_NetworkManager.OnClientDisconnectCallback += OnClientDisconnected; + m_NetworkManager.NetworkTickSystem.Tick += OnTick; + + MetricEventPublisher.OnMetricsReceived += OnMetricsReceived; + } + + readonly List m_ClientIds = new(); + readonly List m_ObjectIds = new(); + + void OnTick() + { + RefreshClientIds(); + RefreshObjectIds(); + } + + void RefreshClientIds() + { + m_ClientIds.Clear(); + + var clientIds = m_NetworkManager.ConnectedClientsIds; + foreach (var clientId in clientIds) + { + m_ClientIds.Add((ClientId)clientId); + } + } + + void RefreshObjectIds() + { + m_ObjectIds.Clear(); + + var spawnedObjects = SpawnManager?.SpawnedObjectsList; + if (spawnedObjects == null) + { + return; + } + foreach (var spawnedObject in spawnedObjects) + { + m_ObjectIds.Add((ObjectId)spawnedObject.NetworkObjectId); + } + } - public AdapterMetadata Metadata { get; } = new AdapterMetadata{ + public AdapterMetadata Metadata { get; } = new AdapterMetadata + { PackageInfo = new PackageInfo { PackageName = "com.unity.netcode.gameobjects", @@ -50,34 +107,133 @@ public T GetComponent() where T : class, IAdapterComponent // Events // -------------------------------------------------------------------- + public IReadOnlyList ConnectedClients => m_ClientIds; + public event Action ClientConnectionEvent; + void OnClientConnected(ulong clientId) + { + var typedClientId = (ClientId)clientId; + if (!m_ClientIds.Contains(typedClientId)) + { + m_ClientIds.Add(typedClientId); + } + ClientConnectionEvent?.Invoke(typedClientId); + } + + public event Action ClientDisconnectionEvent; + void OnClientDisconnected(ulong clientId) + { + var typedClientId = (ClientId)clientId; + m_ClientIds.RemoveAll(id => id == typedClientId); + ClientDisconnectionEvent?.Invoke(typedClientId); + } + public event Action MetricCollectionEvent; - internal void OnMetricsReceived(MetricCollection metricCollection) + void OnMetricsReceived(MetricCollection metricCollection) { + UpdateNetworkTrafficCaches(metricCollection); MetricCollectionEvent?.Invoke(metricCollection); } - // Queries + // Simple Queries // -------------------------------------------------------------------- - public int GetBandwidthBytes(ObjectId objectId) + public ClientId LocalClientId => (ClientId)m_NetworkManager.LocalClientId; + public ClientId ServerClientId => (ClientId)NetworkManager.ServerClientId; + + public IReadOnlyList ObjectIds => m_ObjectIds; + + public GameObject GetGameObject(ObjectId objectId) { - throw new System.NotImplementedException(); + var spawnedObjects = SpawnedObjects; + if (spawnedObjects.TryGetValue((ulong)objectId, out var networkObject)) + { + return networkObject.gameObject; + } + return null; } - public ClientId LocalClientId => (ClientId)NetworkManager.LocalClientId; - public ClientId ServerClientId => (ClientId)NetworkManager.ServerClientId; + public ClientId GetOwner(ObjectId objectId) + { + var spawnedObjects = SpawnedObjects; + if (spawnedObjects.TryGetValue((ulong)objectId, out var networkObject)) + { + return (ClientId)networkObject.OwnerClientId; + } + return 0; + } + + // Cached Queries + // -------------------------------------------------------------------- + ObjectBandwidthCache m_BandwidthCache; + ObjectRpcCountCache m_RpcCountCache; + + void UpdateNetworkTrafficCaches(MetricCollection metricCollection) + { + if (m_OnBandwidthUpdated != null) + { + m_BandwidthCache.Update(metricCollection); + m_OnBandwidthUpdated.Invoke(); + } - public GameObject GetGameObject(ObjectId objectId) => - SpawnManager.SpawnedObjects[(ulong)objectId].gameObject; + if (m_OnRpcCountUpdated != null) + { + m_RpcCountCache.Update(metricCollection); + m_OnRpcCountUpdated.Invoke(); + } + } - public IEnumerable ObjectIds => - SpawnManager.SpawnedObjects.Keys.Select(ulongId => (ObjectId)ulongId); + // IGetBandwidth + // -------------------------------------------------------------------- + event Action m_OnBandwidthUpdated; - public ClientId GetOwner(ObjectId objectId) => - (ClientId)SpawnManager.SpawnedObjects[(ulong)objectId].OwnerClientId; + public BandwidthTypes SupportedBandwidthTypes => + BandwidthTypes.Other | BandwidthTypes.Rpc | BandwidthTypes.NetVar; - public int GetRpcCount(ObjectId objectId) + public event Action OnBandwidthUpdated { - throw new System.NotImplementedException(); + add + { + m_BandwidthCache ??= new(); + m_OnBandwidthUpdated += value; + } + remove + { + m_OnBandwidthUpdated -= value; + if (m_OnBandwidthUpdated == null) + { + m_BandwidthCache = null; + } + } } + + public float GetBandwidthBytes( + ObjectId objectId, + BandwidthTypes bandwidthTypes = BandwidthTypes.All, + NetworkDirection networkDirection = NetworkDirection.SentAndReceived) + => m_BandwidthCache?.GetBandwidth(objectId, bandwidthTypes, networkDirection) + ?? throw new NoSubscribersException(nameof(IGetBandwidth), nameof(OnBandwidthUpdated)); + + // IGetRpcCount + // -------------------------------------------------------------------- + event Action m_OnRpcCountUpdated; + public event Action OnRpcCountUpdated + { + add + { + m_RpcCountCache ??= new(); + m_OnRpcCountUpdated += value; + } + remove + { + m_OnRpcCountUpdated -= value; + if (m_OnRpcCountUpdated == null) + { + m_RpcCountCache = null; + } + } + } + + public int GetRpcCount(ObjectId objectId) + => m_RpcCountCache?.GetRpcCount(objectId) + ?? throw new NoSubscribersException(nameof(IGetRpcCount), nameof(OnRpcCountUpdated)); } -} \ No newline at end of file +} diff --git a/Adapters/Ngo1/Ngo1AdapterInitializer.cs b/Adapters/Ngo1/Ngo1AdapterInitializer.cs index b60ccf9..fd0c47e 100644 --- a/Adapters/Ngo1/Ngo1AdapterInitializer.cs +++ b/Adapters/Ngo1/Ngo1AdapterInitializer.cs @@ -1,3 +1,6 @@ +using System.Threading.Tasks; +using Unity.Multiplayer.Tools.Common; +using Unity.Netcode; using UnityEngine; namespace Unity.Multiplayer.Tools.Adapters.Ngo1 @@ -7,9 +10,24 @@ static class Ngo1AdapterInitializer [RuntimeInitializeOnLoadMethod] static void InitializeAdapter() { - var ngo1Adapter = new Ngo1Adapter(); - MetricEvents.MetricEventPublisher.OnMetricsReceived += ngo1Adapter.OnMetricsReceived; + InitializeAdapterAsync().Forget(); + } + + static async Task InitializeAdapterAsync() + { + var networkManager = await GetNetworkManagerAsync(); + var ngo1Adapter = new Ngo1Adapter(networkManager); NetworkAdapters.AddAdapter(ngo1Adapter); } + + static async Task GetNetworkManagerAsync() + { + while (NetworkManager.Singleton == null || NetworkManager.Singleton.NetworkTickSystem == null) + { + await Task.Yield(); + } + + return NetworkManager.Singleton; + } } -} \ No newline at end of file +} diff --git a/Adapters/Ngo1/ObjectBandwidthCache.cs b/Adapters/Ngo1/ObjectBandwidthCache.cs new file mode 100644 index 0000000..2806a51 --- /dev/null +++ b/Adapters/Ngo1/ObjectBandwidthCache.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.MetricTypes; +using Unity.Multiplayer.Tools.NetStats; + +namespace Unity.Multiplayer.Tools.Adapters.Ngo1 +{ + class ObjectBandwidthCache + { + readonly Dictionary m_OtherBandwidth = new(); + readonly Dictionary m_NetVarBandwidth = new(); + readonly Dictionary m_RpcBandwidth = new(); + + public float GetBandwidth( + ObjectId objectId, + BandwidthTypes bandwidthTypes, + NetworkDirection networkDirection) + { + var total = new BytesSentAndReceived(); + if (bandwidthTypes.ContainsAny(BandwidthTypes.Other) && + m_OtherBandwidth.TryGetValue(objectId, out var otherBandwidth)) + { + total += otherBandwidth; + } + if (bandwidthTypes.ContainsAny(BandwidthTypes.NetVar) && + m_NetVarBandwidth.TryGetValue(objectId, out var netVarBandwidth)) + { + total += netVarBandwidth; + } + if (bandwidthTypes.ContainsAny(BandwidthTypes.Rpc) && + m_RpcBandwidth.TryGetValue(objectId, out var rpcBandwidth)) + { + total += rpcBandwidth; + } + return total[networkDirection]; + } + + static readonly NetworkDirection[] k_SentAndReceived = { NetworkDirection.Sent, NetworkDirection.Received }; + + public void Update(MetricCollection collection) + { + m_OtherBandwidth.Clear(); + m_NetVarBandwidth.Clear(); + m_RpcBandwidth.Clear(); + + foreach (var direction in k_SentAndReceived) + { + LookupAndCountBytes(collection, direction, MetricType.Rpc, m_RpcBandwidth); + LookupAndCountBytes(collection, direction, MetricType.NetworkVariableDelta, m_NetVarBandwidth); + LookupAndCountBytes(collection, direction, MetricType.ObjectSpawned, m_OtherBandwidth); + LookupAndCountBytes(collection, direction, MetricType.ObjectDestroyed, m_OtherBandwidth); + LookupAndCountBytes(collection, direction, MetricType.OwnershipChange, m_OtherBandwidth); + } + } + + static void LookupAndCountBytes( + MetricCollection collection, + NetworkDirection direction, + MetricType metricType, + Dictionary bandwidthBuffer) + where TEvent : INetworkMetricEvent, INetworkObjectEvent + { + var directedMetric = metricType.GetDirectedMetric(direction); + var metricId = MetricId.Create(directedMetric); + var events = collection.GetEventValues(metricId); + CountEventBytesForObjects(events, direction, bandwidthBuffer); + } + + static void CountEventBytesForObjects( + IReadOnlyList events, + NetworkDirection direction, + Dictionary bandwidthBuffer) + where TEvent : INetworkMetricEvent, INetworkObjectEvent + { + foreach (var objectEvent in events) + { + var objectId = (ObjectId)objectEvent.NetworkId.NetworkId; + var bytesSentAndReceived = new BytesSentAndReceived(objectEvent.BytesCount, direction); + if (bandwidthBuffer.TryGetValue(objectId, out var value)) + { + bandwidthBuffer[objectId] = value + bytesSentAndReceived; + } + else + { + bandwidthBuffer[objectId] = bytesSentAndReceived; + } + } + } + } +} diff --git a/Adapters/Ngo1/ObjectBandwidthCache.cs.meta b/Adapters/Ngo1/ObjectBandwidthCache.cs.meta new file mode 100644 index 0000000..0e2a6ce --- /dev/null +++ b/Adapters/Ngo1/ObjectBandwidthCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f9566f254d16422aa958ec862516afed +timeCreated: 1659051345 \ No newline at end of file diff --git a/Adapters/Ngo1/ObjectRpcCountCache.cs b/Adapters/Ngo1/ObjectRpcCountCache.cs new file mode 100644 index 0000000..59051eb --- /dev/null +++ b/Adapters/Ngo1/ObjectRpcCountCache.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using Unity.Multiplayer.Tools.MetricTypes; +using Unity.Multiplayer.Tools.NetStats; + +namespace Unity.Multiplayer.Tools.Adapters.Ngo1 +{ + class ObjectRpcCountCache + { + Dictionary m_MostRecentRpcCount = new Dictionary(); + + public int GetRpcCount(ObjectId objectId) => + m_MostRecentRpcCount.TryGetValue(objectId, out var bandwidth) ? bandwidth : 0; + + public void Update(MetricCollection collection) + { + m_MostRecentRpcCount.Clear(); + + LookupAndCountRpcs(collection, DirectedMetricType.RpcSent); + LookupAndCountRpcs(collection, DirectedMetricType.RpcReceived); + } + + void LookupAndCountRpcs(MetricCollection collection, DirectedMetricType metricType) + { + var metricId = MetricId.Create(metricType); + var events = collection.GetEventValues(metricId); + CountRpcs(events); + } + + void CountRpcs(IReadOnlyList rpcs) + { + foreach (var rpc in rpcs) + { + var objectId = (ObjectId)rpc.NetworkId.NetworkId; + if (m_MostRecentRpcCount.ContainsKey(objectId)) + { + m_MostRecentRpcCount[objectId] += 1; + } + else + { + m_MostRecentRpcCount[objectId] = 1; + } + } + } + } +} diff --git a/Adapters/Ngo1/ObjectRpcCountCache.cs.meta b/Adapters/Ngo1/ObjectRpcCountCache.cs.meta new file mode 100644 index 0000000..4babc73 --- /dev/null +++ b/Adapters/Ngo1/ObjectRpcCountCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 10e9fb3e233248c19c5783cbc7d46b9c +timeCreated: 1659051353 \ No newline at end of file diff --git a/Adapters/Ngo1/Unity.Multiplayer.Tools.Adapters.Ngo1.asmdef b/Adapters/Ngo1/Unity.Multiplayer.Tools.Adapters.Ngo1.asmdef index 57a9660..bded413 100644 --- a/Adapters/Ngo1/Unity.Multiplayer.Tools.Adapters.Ngo1.asmdef +++ b/Adapters/Ngo1/Unity.Multiplayer.Tools.Adapters.Ngo1.asmdef @@ -4,7 +4,9 @@ "references": [ "com.unity.netcode.runtime", "Unity.Multiplayer.Tools.Adapters", + "Unity.Multiplayer.Tools.Common", "Unity.Multiplayer.Tools.MetricEvents", + "Unity.Multiplayer.Tools.MetricTypes", "Unity.Multiplayer.Tools.NetStats", "Unity.Netcode.Runtime" ], diff --git a/Adapters/Ngo1WithUtp2/AssemblyInfo.cs b/Adapters/Ngo1WithUtp2/AssemblyInfo.cs index 1064f27..383a215 100644 --- a/Adapters/Ngo1WithUtp2/AssemblyInfo.cs +++ b/Adapters/Ngo1WithUtp2/AssemblyInfo.cs @@ -1,4 +1,6 @@ using System.Runtime.CompilerServices; // Test assemblies -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Tests")] +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Tests.Editor")] +#endif diff --git a/Adapters/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializer.cs b/Adapters/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializer.cs index 4dbe45a..cdc246e 100644 --- a/Adapters/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializer.cs +++ b/Adapters/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializer.cs @@ -38,4 +38,4 @@ static void RemoveAdapter(int instanceId) } } } -} \ No newline at end of file +} diff --git a/Adapters/Tests.meta b/Adapters/Tests.meta deleted file mode 100644 index 51d9f77..0000000 --- a/Adapters/Tests.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: d350108529234d12ade70c5eb522d27b -timeCreated: 1664982544 \ No newline at end of file diff --git a/Adapters/Tests/Ngo1WithUtp2.meta b/Adapters/Tests/Ngo1WithUtp2.meta deleted file mode 100644 index 3fe5263..0000000 --- a/Adapters/Tests/Ngo1WithUtp2.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: c0571cf1beba4411bca5e16db54f3e2f -timeCreated: 1664982696 \ No newline at end of file diff --git a/Adapters/Tests/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializerTests.cs b/Adapters/Tests/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializerTests.cs deleted file mode 100644 index 3b13770..0000000 --- a/Adapters/Tests/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializerTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections; -using System.Linq; -using NUnit.Framework; -using Unity.Multiplayer.Tools.Adapters.Utp2; -using Unity.Multiplayer.Tools.Adapters.Ngo1WithUtp2; -using Unity.Netcode; -using Unity.Netcode.Transports.UTP; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Unity.Multiplayer.Tools.Adapters.Tests.Ngo1WithUtp2 -{ - public class Ngo1WithUtp2AdapterInitializerTests - { - [UnityTest] - public IEnumerator Ngo1WithUtp2AdapterInitializer_WhenNgo1AndUtp2AreInitialized_RegistersAdapter() - { - Ngo1WithUtp2AdapterInitializer.InitializeAdapter(); - - var gameObject = new GameObject(); - var networkManager = gameObject.AddComponent(); - var transport = gameObject.AddComponent(); - - networkManager.NetworkConfig = new NetworkConfig - { - NetworkTransport = transport, - }; - - networkManager.StartHost(); - - yield return null; - - var adapter = Ngo1WithUtp2AdapterInitializer.s_Adapters.Values.FirstOrDefault(); - Assert.IsNotNull(adapter); - Assert.IsInstanceOf(adapter); - } - } -} \ No newline at end of file diff --git a/Adapters/Tests/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializerTests.cs.meta b/Adapters/Tests/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializerTests.cs.meta deleted file mode 100644 index c1a3f68..0000000 --- a/Adapters/Tests/Ngo1WithUtp2/Ngo1WithUtp2AdapterInitializerTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5287b6152d704cc396aae3839b048773 -timeCreated: 1664982740 \ No newline at end of file diff --git a/Adapters/Tests/Unity.Multiplayer.Tools.Adapters.Tests.asmdef b/Adapters/Tests/Unity.Multiplayer.Tools.Adapters.Tests.asmdef deleted file mode 100644 index 9e22f42..0000000 --- a/Adapters/Tests/Unity.Multiplayer.Tools.Adapters.Tests.asmdef +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "Unity.Multiplayer.Tools.Adapters.Tests", - "rootNamespace": "Unity.Multiplayer.Tools.Adapters.Tests", - "references": [ - "Unity.Multiplayer.Tools.Adapters", - "Unity.Multiplayer.Tools.Adapters.Utp2", - "Unity.Multiplayer.Tools.Adapters.Ngo1WithUtp2", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner", - "Unity.Netcode.Runtime" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "autoReferenced": false, - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "defineConstraints": [ - "UNITY_2022_1_OR_NEWER", - "UTP_TRANSPORT_2_0_ABOVE", - "UNITY_NETCODE_GAMEOBJECTS_1_0" - ], - "noEngineReferences": false -} \ No newline at end of file diff --git a/Adapters/Tests/Unity.Multiplayer.Tools.Adapters.Tests.asmdef.meta b/Adapters/Tests/Unity.Multiplayer.Tools.Adapters.Tests.asmdef.meta deleted file mode 100644 index aebb98b..0000000 --- a/Adapters/Tests/Unity.Multiplayer.Tools.Adapters.Tests.asmdef.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: b5a91c385085445694373dc8dd18b312 -timeCreated: 1664982554 \ No newline at end of file diff --git a/Adapters/Utp2/AssemblyInfo.cs b/Adapters/Utp2/AssemblyInfo.cs index 65aeae8..70bbb38 100644 --- a/Adapters/Utp2/AssemblyInfo.cs +++ b/Adapters/Utp2/AssemblyInfo.cs @@ -4,4 +4,6 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Ngo1WithUtp2")] // Test assemblies -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Tests")] +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Tests.Editor")] +#endif diff --git a/CHANGELOG.future.md b/CHANGELOG.future.md new file mode 100644 index 0000000..f7d9b08 --- /dev/null +++ b/CHANGELOG.future.md @@ -0,0 +1,28 @@ +# Future Changelog + +This file (CHANGELOG.future.md) is used to record changelog entries for future releases. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +### Purpose of this file + +This file allows us to include changelog entries for changes targeting future versions in the PRs containing these changes, without causing PR validation failures. + +Such PR validation failures occur because package validation does not allow inclusion of entries for future releases in CHANGELOG.md. + + +### How to use this file + +When you are making changes in develop that are targeting a future release that is greater than the current package version number, record the changelog entries for these changes in this file. +1. You should follow the same format for changelog entries as CHANGELOG.md +2. If you know the specific version number of the release these changes will be included in, use that version number. +3. If you don't know the specific version number of the release these changes will be included in, just put them under "Next Release" + +Then, when we have a new release branch and version number, we can move these changelog entries from this file into CHANGELOG.md under the next version number, and delete this file from the release branch as needed. + + +## Next Release + +### *Runtime Net Stats Monitor* +- Fix an index out of range exception that could occur when adding a new RNSM graph at runtime diff --git a/MetricTypes/Tests/Editor/Unity.Multiplayer.Tools.MetricTypes.Tests.Editor.asmdef.meta b/CHANGELOG.future.md.meta similarity index 59% rename from MetricTypes/Tests/Editor/Unity.Multiplayer.Tools.MetricTypes.Tests.Editor.asmdef.meta rename to CHANGELOG.future.md.meta index 363250a..a9ceeb9 100644 --- a/MetricTypes/Tests/Editor/Unity.Multiplayer.Tools.MetricTypes.Tests.Editor.asmdef.meta +++ b/CHANGELOG.future.md.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: c1f1c40d79d16dd4a8fe5f249be33893 -AssemblyDefinitionImporter: +guid: 8c40358f66b73451595bfe4ab8c0893e +TextScriptImporter: externalObjects: {} userData: assetBundleName: diff --git a/CHANGELOG.md b/CHANGELOG.md index 783c0f6..b59ea32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,22 +4,29 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [1.1.0] - 2022-11-07 +## [2.0.0-pre.2] - 2023-05-02 -### *Metrics* -- Improve the warning message for throttling, and increase the threshold for throttling a metric from 100 to 1,000 recorded events per frame +### *General* +- Dropped support for Unity 2020.3; the next supported version is Unity 2021.3 +- Fixed ``Failed to load type initialization for assembly Unity.Multiplayer.Tools.MetricTypes`` runtime exception when building using Managed Stripping level set to high. -### *Misc* -- Fixed compilation warning related to unsupported build targets +### *Network Scene Visualization* -### *Network Simulator* +This release adds the Network Scene Visualization to the Multiplayer Tools Package. This tool allows users to visualize networking information (like bandwidth and ownership) on a per-object basis in the scene view using a number of visualizations, including mesh shading and a text overlay. + +### *Runtime Net Stats Monitor* +- Fixed an issue that prevented using the ``RuntimeNetStatsMonitor.AddCustomValue`` API for stats that are only sampled per second. +- Switched to a new color-blind friendly color-palette for default variable colors in graphs, which will provide increased contrast and more default values. This new color palette is the same one used in the new Network Scene Visualization tool. +- Reduced the maximum sample count in Graphs and Simple Moving Average counters from 4096 to 512. Sample counts higher than 512 are no longer needed since per-second sampling was introduced in 1.1.0. +- Deprecated public methods that could be used to control the conditional compilation of the RNSM. Conditional compilation of the RNSM will be removed in a future major release. -This release adds the Network Simulator to the Multiplayer Tools Package. -This tool offers a configurable component to simulate adverse network condition. -Packet delay, jitter, packet loss and loss interval are all parameters that can be configured to simulate different kind of networks. -A set of built-in network scenarios are provided to simulate more complex scenarios. User-defined scenarios are also supported. +## [1.1.0] - 2022-09-22 -For more information about the Network Simulator, please see the [tools documentation](https://docs-multiplayer.unity3d.com/tools/current/install-tools/index.html). +### *Metrics* +- Improve the warning message for throttling, and increase the threshold for throttling a metric from 100 to 1,000 recorded events per frame. + +### *Misc* +- Fixed compilation warning related to unsupported build targets ### *Runtime Net Stats Monitor* - Graphs and Simple Moving Average counters can now be configured to be sampled per-second rather than per-frame diff --git a/Common/Runtime/AssemblyInfo.cs b/Common/Runtime/AssemblyInfo.cs index e190e54..9743125 100644 --- a/Common/Runtime/AssemblyInfo.cs +++ b/Common/Runtime/AssemblyInfo.cs @@ -1,6 +1,8 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.MockNgo")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Ngo1")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Common.Tests")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Initialization")] @@ -11,11 +13,21 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Runtime")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Configuration")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.UI")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.Visualization")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.DependencyInjection")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.DependencyInjection.UIElements")] +// Test assemblies #if UNITY_INCLUDE_TESTS -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Tests.Runtime.NetworkSimulator")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Common.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Tests.Runtime")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Tests.Editor")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] #endif diff --git a/Common/Runtime/Compiler/IsExternalInit.cs b/Common/Runtime/Compiler/IsExternalInit.cs index a40369c..5cefd5d 100644 --- a/Common/Runtime/Compiler/IsExternalInit.cs +++ b/Common/Runtime/Compiler/IsExternalInit.cs @@ -13,4 +13,4 @@ internal static class IsExternalInit { } -} \ No newline at end of file +} diff --git a/Common/Runtime/Constants/StringConstants.cs b/Common/Runtime/Constants/StringConstants.cs index 640b01d..4473a62 100644 --- a/Common/Runtime/Constants/StringConstants.cs +++ b/Common/Runtime/Constants/StringConstants.cs @@ -12,4 +12,4 @@ internal static class StringConstants public const string k_ResourcePrefixRnsm = k_ResourcePrefix + "Rnsm"; public const string k_ResourcePrefixRnsmDefault = k_ResourcePrefixRnsm + "Default"; } -} \ No newline at end of file +} diff --git a/Common/Runtime/Context/Context.cs b/Common/Runtime/Context/Context.cs index d239e39..65cbabe 100644 --- a/Common/Runtime/Context/Context.cs +++ b/Common/Runtime/Context/Context.cs @@ -40,7 +40,7 @@ protected enum ContextStatus EnabledInEditor, EnabledInRuntime } - + protected ContextStatus Status { get; private set; } void IEditorSetupHandler.EditorSetup() diff --git a/Common/Runtime/Data/ContinuousExponentialMovingAverage.cs b/Common/Runtime/Data/ContinuousExponentialMovingAverage.cs index 9b345b4..4ae045e 100644 --- a/Common/Runtime/Data/ContinuousExponentialMovingAverage.cs +++ b/Common/Runtime/Data/ContinuousExponentialMovingAverage.cs @@ -17,11 +17,12 @@ namespace Unity.Multiplayer.Tools.Common /// statistician Andreas Eckner describes something similar here:
/// http://www.eckner.com/research.html /// - internal class ContinuousExponentialMovingAverage + class ContinuousExponentialMovingAverage { const double k_DefaultInitialTime = double.NegativeInfinity; public static readonly double k_ln2 = Math.Log(2); + public static readonly float k_ln2F = MathF.Log(2); /// The decay constant is λ = ln(2) / HalfLife. A larger decay constant will results in a faster, /// more responsive CEMA with less smoothing, whereas a smaller decay constant will result in a slower, @@ -42,6 +43,11 @@ public static ContinuousExponentialMovingAverage CreateWithHalfLife(double halfL /// less responsive CEMA with more smoothing. public static double GetDecayConstantForHalfLife(double halfLife) => k_ln2 / halfLife; + /// The decay constant is λ = ln(2) / HalfLife. A larger decay constant will results in a faster, + /// more responsive CEMA with less smoothing, whereas a smaller decay constant will result in a slower, + /// less responsive CEMA with more smoothing. + public static float GetDecayConstantForHalfLife(float halfLife) => k_ln2F / halfLife; + public ContinuousExponentialMovingAverage(double decayConstant, double value = 0d, double time = k_DefaultInitialTime) { if (decayConstant < 0) @@ -118,4 +124,4 @@ public double GetCounterValue(double time) return LastValue * oldValueWeight; } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Data/EnumMap.cs b/Common/Runtime/Data/EnumMap.cs index b70567b..ccf2e7d 100644 --- a/Common/Runtime/Data/EnumMap.cs +++ b/Common/Runtime/Data/EnumMap.cs @@ -47,15 +47,7 @@ public EnumMap() public EnumMap(TValue value) : this() { -#if UNITY_2021_3_OR_NEWER Array.Fill(m_Values, value); -#else - // Array.Fill doesn't appear to be available in Unity < 2021.3 - for (var i = 0; i < m_Values.Length; ++i) - { - m_Values[i] = value; - } -#endif } public EnumMap(TValue[] values) @@ -66,8 +58,8 @@ public EnumMap(TValue[] values) public TValue this[TEnum key] { - get => m_Values[CastEnumToInt(key)]; - set => m_Values[CastEnumToInt(key)] = value; + get => m_Values[key.UnsafeCastToInt()]; + set => m_Values[key.UnsafeCastToInt()] = value; } public int Count => s_Count; @@ -76,36 +68,14 @@ public TValue this[TEnum key] public void Add(TEnum key, TValue value) { - m_Values[CastEnumToInt(key)] = value; - } - - /// - /// Convert.ToInt32 is orders of magnitude slower than this method, - /// and allocates with each call, so this method is needed in order - /// for this class to perform better than a Dictionary. - ///
- /// Using Convert.ToInt32 this class will perform a little worse - /// (maybe 20-30% worse) than a Dictionary, and will allocate with - /// each read and write. - ///
- /// Using the unsafe CastEnumToInt method, this class performs - /// ~3.6 times faster than a dictionary, and allocates 8 times less. - ///
- static unsafe int CastEnumToInt(TEnum enumValue) - { - return *(int*)(&enumValue); - } - - static unsafe TEnum CastIntToEnum(int value) - { - return *(TEnum*)(&value); + m_Values[key.UnsafeCastToInt()] = value; } public IEnumerator> GetEnumerator() { for (int i = 0; i < s_Count; ++i) { - yield return new KeyValuePair(CastIntToEnum(i), m_Values[i]); + yield return new KeyValuePair(i.UnsafeCastToEnum(), m_Values[i]); } } @@ -119,7 +89,7 @@ static class EnumContinuity { public static (int min, int max, int uniqueValueCount) GetMinMaxAndUniqueValueCount() - where TEnum : Enum + where TEnum : unmanaged, Enum { var enumValues = EnumUtil.GetValues(); diff --git a/Common/Runtime/Data/EnumMap.cs.meta b/Common/Runtime/Data/EnumMap.cs.meta index c9564e2..33da8cf 100644 --- a/Common/Runtime/Data/EnumMap.cs.meta +++ b/Common/Runtime/Data/EnumMap.cs.meta @@ -1,3 +1,11 @@ fileFormatVersion: 2 -guid: 3e24e6e87de94630b57fecdfe43d8e56 -timeCreated: 1665604304 \ No newline at end of file +guid: ffee27ff34e924b299d4c5ff73a2ae8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Common/Runtime/Data/ExponentialMovingAverage.cs b/Common/Runtime/Data/ExponentialMovingAverage.cs index 3d476ed..f090a53 100644 --- a/Common/Runtime/Data/ExponentialMovingAverage.cs +++ b/Common/Runtime/Data/ExponentialMovingAverage.cs @@ -57,4 +57,4 @@ public void AddSample(float x) Value = Parameter * x + (1f - Parameter) * Value; } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Data/LogNormalRandomWalk.cs b/Common/Runtime/Data/LogNormalRandomWalk.cs index 96292c0..b4e6688 100644 --- a/Common/Runtime/Data/LogNormalRandomWalk.cs +++ b/Common/Runtime/Data/LogNormalRandomWalk.cs @@ -45,4 +45,4 @@ public void Repeat(Random random, Action action) } } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Data/MinAndMax.cs b/Common/Runtime/Data/MinAndMax.cs index 8304977..2d42639 100644 --- a/Common/Runtime/Data/MinAndMax.cs +++ b/Common/Runtime/Data/MinAndMax.cs @@ -10,4 +10,4 @@ public MinAndMax(float min, float max) public float Min { get; set; } public float Max { get; set; } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Data/RingBuffer.cs b/Common/Runtime/Data/RingBuffer.cs index 1272749..79f3257 100644 --- a/Common/Runtime/Data/RingBuffer.cs +++ b/Common/Runtime/Data/RingBuffer.cs @@ -118,12 +118,10 @@ bool ContainsIndex(int index) return 0 <= index && index < Length; } -#if UNITY_2021_2_OR_NEWER // Index and ^i syntax are not available in lower versions bool ContainsIndex(Index index) { return ContainsIndex(index.IsFromEnd ? index.Value - 1 : index.Value); } -#endif /// Thrown if the index is out of range void ThrowIfIndexOutOfRange(int index) @@ -143,7 +141,6 @@ void ThrowIfCapacityLessThanZero(int capacity) } } -#if UNITY_2021_2_OR_NEWER // Index and ^i syntax are not available in lower versions /// Thrown if the index is out of range void ThrowIfIndexOutOfRange(Index index) { @@ -152,7 +149,6 @@ void ThrowIfIndexOutOfRange(Index index) throw new IndexOutOfRangeException($"Index {index} is out of range [0, {Length})"); } } -#endif /// Translates a logical index into an index in the underlying buffer. int GetBufferIndex(int index) @@ -166,7 +162,6 @@ int GetBufferIndexFromEnd(int index) return GetBufferIndex(Length - 1 - index); } -#if UNITY_2021_2_OR_NEWER // Index and ^i syntax are not available in lower versions /// Translates a logical index into an index in the underlying buffer. int GetBufferIndex(Index index) { @@ -179,7 +174,6 @@ int GetBufferIndex(Index index) return GetBufferIndex(index.Value); } } -#endif /// Ring buffer is indexed from least recent (beginning at 0) to most recent (ending at length - 1). /// @@ -197,7 +191,6 @@ public T this[int index] } } -#if UNITY_2021_2_OR_NEWER // Index and ^i syntax are not available in lower versions /// Ring buffer is indexed from least recent (beginning at 0) to most recent (ending at length - 1). /// public T this[Index index] @@ -213,7 +206,6 @@ public T this[Index index] m_Buffer![GetBufferIndex(index)] = value; } } -#endif public T GetValueOrDefault(int index) { @@ -224,7 +216,6 @@ public T GetValueOrDefault(int index) return default; } -#if UNITY_2021_2_OR_NEWER // Index and ^i syntax are not available in lower versions public T GetValueOrDefault(Index index) { if (ContainsIndex(index)) @@ -233,17 +224,14 @@ public T GetValueOrDefault(Index index) } return default; } -#endif /// Throws IndexOutOfRange if length is 0 public T LeastRecent => this[0]; public T LeastRecentOrDefault => Length > 0 ? LeastRecent : default; -#if UNITY_2021_2_OR_NEWER // Index and ^i syntax are not available in lower versions /// Throws IndexOutOfRange if length is 0 public T MostRecent => this[^1]; public T MostRecentOrDefault => Length > 0 ? MostRecent : default; -#endif /// Enumerator from least to most recent public IEnumerator GetEnumerator() @@ -260,4 +248,4 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Data/RingBufferExtensions.cs b/Common/Runtime/Data/RingBufferExtensions.cs index ea05341..d08fb2a 100644 --- a/Common/Runtime/Data/RingBufferExtensions.cs +++ b/Common/Runtime/Data/RingBufferExtensions.cs @@ -131,4 +131,4 @@ public static float Average(this RingBuffer ring) return sum / ring.Length; } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Extensions/DebugUtils.cs b/Common/Runtime/Extensions/DebugUtils.cs index 00599a7..1b7bc84 100644 --- a/Common/Runtime/Extensions/DebugUtils.cs +++ b/Common/Runtime/Extensions/DebugUtils.cs @@ -1,18 +1,71 @@ using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; using Debug = UnityEngine.Debug; namespace Unity.Multiplayer.Tools.Common { static class DebugUtil { + /// - /// Use #define UNITY_MP_TOOLS_DEBUG_TRACE at the call site in order to enable logging. + /// Define UNITY_MP_TOOLS_DEBUG_TRACE, either at the call site or in project settings, to enable logging /// /// [Conditional("UNITY_MP_TOOLS_DEBUG_TRACE")] public static void Trace(string message) { Debug.Log(message); - } + } + +#if UNITY_MP_TOOLS_DEBUG_TRACE + static string FormatMethodName(string typeName, string methodName) + { + const string k_ConstructorMethodName = ".ctor"; + const string k_StaticConstructorMethodName = ".cctor"; + return methodName switch + { + k_ConstructorMethodName => $"{typeName}()", + k_StaticConstructorMethodName => $"static {typeName}()", + _ => $"{typeName}.{methodName}()", + }; + } +#endif + + /// + /// Logs the name of the calling method as <FileName>.<MethodName> + /// + /// + /// This approach is ~25x faster than , + /// but will be less clear in cases in which the typename does not match the file name, + /// or in which there are multiple constructors or methods of the same name belonging to + /// different types in the same file. + ///
+ /// In such cases you can use for a less ambiguous alternative. + ///
+ [Conditional("UNITY_MP_TOOLS_DEBUG_TRACE")] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void TraceMethodName( + [CallerFilePath] string filepath = "", + [CallerMemberName] string methodName = "") + { +#if UNITY_MP_TOOLS_DEBUG_TRACE + var filenameWithoutExtension = Path.GetFileNameWithoutExtension(filepath); + Debug.Log(FormatMethodName(filenameWithoutExtension, methodName)); +#endif + } + + [Conditional("UNITY_MP_TOOLS_DEBUG_TRACE")] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void TraceMethodNameUsingStackFrame() + { +#if UNITY_MP_TOOLS_DEBUG_TRACE + var sf = new StackFrame(1); + var method = sf.GetMethod(); + var typeName = method.DeclaringType!.Name; + var methodName = method.Name; + Debug.Log(FormatMethodName(typeName, methodName)); +#endif + } } } diff --git a/Common/Runtime/Extensions/DictionaryExtensions.cs b/Common/Runtime/Extensions/DictionaryExtensions.cs new file mode 100644 index 0000000..a37cc2f --- /dev/null +++ b/Common/Runtime/Extensions/DictionaryExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Unity.Multiplayer.Tools.Common +{ + public static class DictionaryExtensions + { + public static TValue GetValueOrCreateNew(this Dictionary dictionary, TKey key) + where TValue : new() + { + if (dictionary.TryGetValue(key, out var value)) + return value; + + value = new TValue(); + dictionary.Add(key, value); + return value; + } + } +} diff --git a/Common/Runtime/Extensions/DictionaryExtensions.cs.meta b/Common/Runtime/Extensions/DictionaryExtensions.cs.meta new file mode 100644 index 0000000..33389ad --- /dev/null +++ b/Common/Runtime/Extensions/DictionaryExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f59f63848a4c4cdb8978bee0a20a4337 +timeCreated: 1659232460 \ No newline at end of file diff --git a/Common/Runtime/Extensions/EnumUtil.cs b/Common/Runtime/Extensions/EnumUtil.cs index faa018f..80b8e8d 100644 --- a/Common/Runtime/Extensions/EnumUtil.cs +++ b/Common/Runtime/Extensions/EnumUtil.cs @@ -1,10 +1,232 @@ using System; +using System.Collections.Generic; +using System.Linq; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Tools.Common { - internal static class EnumUtil { - public static T[] GetValues() { + static class EnumUtil + { + public static T[] GetValues() where T : unmanaged, Enum + { return (T[])Enum.GetValues(typeof(T)); } + + public static string[] GetNames() where T : unmanaged, Enum + { + return Enum.GetNames(typeof(T)); + } + + public static IEnumerable<(string name, T value)> GetValuesAndNames(params T[] skip) where T : unmanaged, Enum + { + foreach (var name in GetNames()) + { + var value = Enum.Parse(name); + + if (skip != null && skip.Contains(value)) + { + continue; + } + yield return (name, value); + } + } + + /// + /// Warning: this cast is unsafe. It does not perform any type checking you. + /// You must ensure that the underlying type is correct. + /// + /// + /// Convert.ToInt32 is orders of magnitude slower than this method, + /// and allocates with each call, so this method is needed in order + /// for this to perform better than a Dictionary. + ///
+ /// Using Convert.ToInt32 this class will perform a little worse + /// (maybe 20-30% worse) than a Dictionary, and will allocate with + /// each read and write. + ///
+ /// Using the unsafe CastEnumToInt method, this class performs + /// ~3.6 times faster than a dictionary, and allocates 8 times less. + ///
+ public static unsafe TUnderlying UnsafeCastToUnderlying(this TEnum enumValue) + where TEnum: unmanaged, Enum + where TUnderlying: unmanaged + { + return *(TUnderlying*)(&enumValue); + } + + /// + /// Warning: this cast is unsafe. It does not perform any type checking for you. + /// You must ensure that the underlying type is correct. + /// + /// + /// Convert.ToInt32 is orders of magnitude slower than this method, + /// and allocates with each call, so this method is needed in order + /// for this to perform better than a Dictionary. + ///
+ /// Using Convert.ToInt32 this class will perform a little worse + /// (maybe 20-30% worse) than a Dictionary, and will allocate with + /// each read and write. + ///
+ /// Using the unsafe CastEnumToInt method, this class performs + /// ~3.6 times faster than a dictionary, and allocates 8 times less. + ///
+ public static unsafe int UnsafeCastToInt(this TEnum enumValue) + where TEnum: unmanaged, Enum + { + return *(int*)(&enumValue); + } + + /// + /// Warning: this cast is unsafe. It does not perform any type checking for you. + /// You must ensure that the underlying type is correct. + /// + public static unsafe TEnum UnsafeCastToEnum(this int value) + where TEnum: unmanaged, Enum + { + return *(TEnum*)(&value); + } + + /// + /// Warning: this cast is unsafe. It does not perform any type checking for you. + /// You must ensure that the underlying type is correct. + /// + public static unsafe TEnum UnsafeCastToEnum(this TUnderlying value) + where TUnderlying: unmanaged + where TEnum: unmanaged, Enum + { + return *(TEnum*)(&value); + } + + /// + /// True if this enum value contains one or more of the flags in b and false otherwise + /// + /// + /// Is thrown if TEnum does not have the FlagsAttribute + /// + /// + /// Is thrown if TEnum does not have the FlagsAttribute + /// + public static bool ContainsAny(this TEnum a, TEnum b) + where TEnum: unmanaged, Enum + { + return IntFlagEnumUtils.ContainsAny(a, b); + } + + /// + /// True if this enum value contains all of the flags in b and false otherwise + /// + /// + /// Is thrown if TEnum does not have the FlagsAttribute + /// + public static bool ContainsAll(this TEnum a, TEnum b) + where TEnum: unmanaged, Enum + { + return IntFlagEnumUtils.ContainsAll(a, b); + } + + /// + /// Is thrown if TEnum does not have the FlagsAttribute + /// + public static TEnum SetFlags(this TEnum a, TEnum b, bool value) + where TEnum: unmanaged, Enum + { + return IntFlagEnumUtils.SetFlags(a, b, value); + } + + /// + /// Is thrown if TEnum does not have the FlagsAttribute + /// + public static void SetFlagsInPlace(ref this TEnum a, TEnum b, bool value) + where TEnum: unmanaged, Enum + { + a = IntFlagEnumUtils.SetFlags(a, b, value); + } + } + + /// + /// This static class is used to enforce the constraint (via its static constructor) + /// that the generic enum parameter passed has an underlying type of TUnderlying. + /// + static class CheckedEnumUtils + where TEnum : unmanaged, Enum + where TUnderlying : unmanaged + { + static CheckedEnumUtils() + { + var type = typeof(TEnum); + if (Enum.GetUnderlyingType(type) != typeof(TUnderlying)) + { + throw new UnhandledEnumUnderlyingTypeException(); + } + } + + /// + /// Returns the unsafe value, but the static constructor ensures an exception + /// is thrown if this is called anywhere on an enum that does not have an + /// underlying type of TUnderlying. + /// + public static TUnderlying CheckedCastToUnderlying(TEnum value) + { + return value.UnsafeCastToUnderlying(); + } + } + + /// + /// This static class is used to enforce the constraint (via its static constructor) + /// that the generic parameter passed to it has the flags attribute. + /// + static class IntFlagEnumUtils where TEnum : unmanaged, Enum + { + /// + /// Ensure that TEnum has the flags attribute + /// + static IntFlagEnumUtils() + { + var type = typeof(TEnum); + if (type.GetCustomAttributes(typeof(FlagsAttribute), true).Length <= 0) + { + throw new EnumWithoutFlagsAttributeException(); + } + if (Enum.GetUnderlyingType(type) != typeof(int)) + { + throw new UnhandledEnumUnderlyingTypeException(); + } + } + + public static bool ContainsAny(TEnum a, TEnum b) + { + return (a.UnsafeCastToInt() & b.UnsafeCastToInt()) != 0; + } + + public static bool ContainsAll(TEnum a, TEnum b) + { + return (a.UnsafeCastToInt() & b.UnsafeCastToInt()) == b.UnsafeCastToInt(); + } + + public static TEnum SetFlags(TEnum a, TEnum b, bool value) + { + return (value + ? a.UnsafeCastToInt() | b.UnsafeCastToInt() + : a.UnsafeCastToInt() & ~b.UnsafeCastToInt()).UnsafeCastToEnum(); + } + } + + class EnumWithoutFlagsAttributeException : Exception + where TEnum : unmanaged, Enum + { + public EnumWithoutFlagsAttributeException() + : base($"Cannot use {nameof(EnumUtil.ContainsAny)}, {nameof(EnumUtil.SetFlags)}, or {nameof(EnumUtil.SetFlagsInPlace)} " + + $"on enum {nameof(TEnum)}, as it does not have the {nameof(FlagsAttribute)} attribute") + {} + } + + class UnhandledEnumUnderlyingTypeException : Exception + where TEnum : unmanaged, Enum + { + public UnhandledEnumUnderlyingTypeException() + : base($"Cannot use {nameof(EnumUtil.ContainsAny)}, {nameof(EnumUtil.SetFlags)}, or {nameof(EnumUtil.SetFlagsInPlace)} " + + $"on enum {nameof(TEnum)}, because its underlying type {typeof(TEnum).UnderlyingSystemType} is not the required" + + $" underlying type {typeof(TRequiredUnderlyingType)}") + {} } -} \ No newline at end of file +} diff --git a/Common/Runtime/Extensions/ListUtil.cs b/Common/Runtime/Extensions/ListUtil.cs index 6fb5778..63e5608 100644 --- a/Common/Runtime/Extensions/ListUtil.cs +++ b/Common/Runtime/Extensions/ListUtil.cs @@ -48,4 +48,4 @@ public static void Resize(this List list, int size, Func generator) } } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Extensions/ProfilerUtilities.cs b/Common/Runtime/Extensions/ProfilerUtilities.cs new file mode 100644 index 0000000..4204854 --- /dev/null +++ b/Common/Runtime/Extensions/ProfilerUtilities.cs @@ -0,0 +1,22 @@ +using UnityEngine.Profiling; + +namespace Unity.Multiplayer.Tools.Common +{ + public ref struct ProfilerScope + { + public static ProfilerScope BeginSample(string name) + { + return new ProfilerScope(name); + } + + ProfilerScope(string name) + { + Profiler.BeginSample(name); + } + + public void Dispose() + { + Profiler.EndSample(); + } + } +} diff --git a/Common/Runtime/Extensions/ProfilerUtilities.cs.meta b/Common/Runtime/Extensions/ProfilerUtilities.cs.meta new file mode 100644 index 0000000..73bb7e4 --- /dev/null +++ b/Common/Runtime/Extensions/ProfilerUtilities.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 751b4c745efa4bfd8589f884128ccc03 +timeCreated: 1659055020 \ No newline at end of file diff --git a/Common/Runtime/Extensions/ReadOnlyListExtensions.cs b/Common/Runtime/Extensions/ReadOnlyListExtensions.cs index ee10910..07e3234 100644 --- a/Common/Runtime/Extensions/ReadOnlyListExtensions.cs +++ b/Common/Runtime/Extensions/ReadOnlyListExtensions.cs @@ -21,4 +21,4 @@ public static int IndexOf(this IReadOnlyList list, T elementToFind ) return -1; } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Extensions/StringUtil.cs b/Common/Runtime/Extensions/StringUtil.cs index adc9e6c..24b58a2 100644 --- a/Common/Runtime/Extensions/StringUtil.cs +++ b/Common/Runtime/Extensions/StringUtil.cs @@ -14,4 +14,4 @@ internal static string RemoveSpaces(string s) return s.Replace(" ", ""); } } -} \ No newline at end of file +} diff --git a/Common/Runtime/Extensions/VisualElementExtensions.cs b/Common/Runtime/Extensions/VisualElementExtensions.cs index 5be935a..b6a2125 100644 --- a/Common/Runtime/Extensions/VisualElementExtensions.cs +++ b/Common/Runtime/Extensions/VisualElementExtensions.cs @@ -1,7 +1,4 @@ -using System; -using System.Runtime.CompilerServices; -using UnityEngine; -using UnityEngine.UIElements; +using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.Common { @@ -12,6 +9,11 @@ public static void AddEventLifecycle( EventCallback onAttach, EventCallback onDetach) { + // If the element is already attached, fire the onAttach callback immediately + if (visualElement.hierarchy.parent != null) + { + onAttach(default); + } visualElement.RegisterCallback(onAttach); visualElement.RegisterCallback(onDetach); } @@ -41,5 +43,31 @@ public static void SetInclude(this VisualElement visualElement, bool includeInLa /// public static bool GetInclude(this VisualElement visualElement) => visualElement.style.display.value == DisplayStyle.Flex; + + /// + /// Registers the specified callback for every element matched in the query and automatically Unregisters the callback when + /// the element is detached. + /// + /// Root VisualElement on which the selector will be applied. + /// The callback that will be registered for each child matching the query. + /// /// If specified, will select elements with this name. + /// If specified, will select elements with the given class (not to be confused with Type). + /// Type of the event to register the callback. + public static void QueryRegisterCallback( + this VisualElement visualElement, + EventCallback callback, + string name = null, + string className = null) + where TEventType : EventBase, new() + { + visualElement.Query(name, className).Build().ForEach(AddLifecycleEvents); + + void AddLifecycleEvents(VisualElement element) + { + element.AddEventLifecycle(OnAttach, OnDetach); + void OnAttach(AttachToPanelEvent evt) => element.RegisterCallback(callback); + void OnDetach(DetachFromPanelEvent evt) => element.UnregisterCallback(callback); + } + } } } diff --git a/Common/Runtime/Helpers.meta b/Common/Runtime/Helpers.meta new file mode 100644 index 0000000..10a9d30 --- /dev/null +++ b/Common/Runtime/Helpers.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 474bbee6df934e18aca2467a929dbd2f +timeCreated: 1659633563 \ No newline at end of file diff --git a/Common/Runtime/Helpers/RuntimeUpdaterBehaviour.cs b/Common/Runtime/Helpers/RuntimeUpdaterBehaviour.cs new file mode 100644 index 0000000..23a2c41 --- /dev/null +++ b/Common/Runtime/Helpers/RuntimeUpdaterBehaviour.cs @@ -0,0 +1,272 @@ +using System; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Unity.Multiplayer.Tools.Common +{ + public interface IRuntimeUpdater + { + event Action OnStart; + event Action OnAwake; + event Action OnUpdate; + event Action OnFixedUpdate; + event Action OnLateUpdate; + event Action OnDestroyed; + } + + class RuntimeUpdater : IRuntimeUpdater + { + RuntimeUpdaterBehaviour m_Component; + + event Action m_OnAwake; + event Action m_OnStart; + event Action m_OnUpdate; + event Action m_OnFixedUpdate; + event Action m_OnLateUpdate; + event Action m_OnDestroyed; + + // TODO: Encapsulate those similar access pattern into a separate class. + + public event Action OnAwake + { + add + { + if (m_Component != null) + { + m_Component.OnAwake += value; + } + + // Keep tracks of events even when the gameObject wasn't instantiated yet: + m_OnAwake += value; + } + remove + { + if (m_Component != null) + { + m_Component.OnAwake -= value; + } + m_OnAwake -= value; + } + } + + public event Action OnStart + { + add + { + if (m_Component != null) + { + m_Component.OnStart += value; + } + + // Keep tracks of events even when the gameObject wasn't instantiated yet: + m_OnStart += value; + } + remove + { + if (m_Component != null) + { + m_Component.OnStart -= value; + } + m_OnStart -= value; + } + } + + public event Action OnUpdate + { + add + { + if (m_Component != null) + { + m_Component.OnUpdate += value; + } + + // Keep tracks of events even when the gameObject wasn't instantiated yet: + m_OnUpdate += value; + } + remove + { + if (m_Component != null) + { + m_Component.OnUpdate -= value; + } + m_OnUpdate -= value; + } + } + + public event Action OnFixedUpdate + { + add + { + if (m_Component != null) + { + m_Component.OnFixedUpdate += value; + } + + // Keep tracks of events even when the gameObject wasn't instantiated yet: + m_OnFixedUpdate += value; + } + remove + { + if (m_Component != null) + { + m_Component.OnFixedUpdate -= value; + } + m_OnFixedUpdate -= value; + } + } + + public event Action OnLateUpdate + { + add + { + if (m_Component != null) + { + m_Component.OnLateUpdate += value; + } + + // Keep tracks of events even when the gameObject wasn't instantiated yet: + m_OnLateUpdate += value; + } + remove + { + if (m_Component != null) + { + m_Component.OnLateUpdate -= value; + } + m_OnLateUpdate -= value; + } + } + + public event Action OnDestroyed + { + add + { + if (m_Component != null) + { + m_Component.OnDestroyed += value; + } + + // Keep tracks of events even when the gameObject wasn't instantiated yet: + m_OnDestroyed += value; + } + remove + { + if (m_Component != null) + { + m_Component.OnDestroyed -= value; + } + m_OnDestroyed -= value; + } + } + + public RuntimeUpdater() + { +#if UNITY_EDITOR + // Handling Edit Time behavior: + void OnEditorApplicationOnplayModeStateChanged(PlayModeStateChange change) + { + switch (change) + { + case PlayModeStateChange.EnteredPlayMode: + { + CreateInstance(); + break; + } + case PlayModeStateChange.ExitingPlayMode: + { + Object.Destroy(m_Component); + break; + } + } + } + + EditorApplication.playModeStateChanged += OnEditorApplicationOnplayModeStateChanged; +#else + CreateInstance(); +#endif + } + + void CreateInstance() + { + Debug.Assert(m_Component == null, m_Component); + + m_Component = new GameObject($"[{nameof(RuntimeUpdaterBehaviour)}]").AddComponent(); + m_Component.gameObject.hideFlags = HideFlags.HideAndDontSave; + m_Component.ReplaceCallbacks(m_OnAwake, m_OnStart, m_OnUpdate, m_OnFixedUpdate, m_OnLateUpdate, m_OnDestroyed); + Object.DontDestroyOnLoad(m_Component.gameObject); + } + } + + /// + /// Generic Updater for hooking into Update, FixedUpdate and LateUpdate callbacks. + /// + class RuntimeUpdaterBehaviour : MonoBehaviour, IRuntimeUpdater + { + public event Action OnAwake; + public event Action OnStart; + public event Action OnUpdate; + public event Action OnFixedUpdate; + public event Action OnLateUpdate; + public event Action OnDestroyed; + + static string RuntimeErrorMessage => $"{nameof(RuntimeUpdaterBehaviour)} can only be called at runtime."; + + internal void ReplaceCallbacks( + Action onAwake, + Action onStart, + Action onUpdate, + Action onFixedUpdate, + Action onLateUpdate, + Action onDestroyed) + { + OnAwake = onAwake; + OnStart = onStart; + OnUpdate = onUpdate; + OnFixedUpdate = onFixedUpdate; + OnLateUpdate = onLateUpdate; + OnDestroyed = onDestroyed; + } + + void Awake() + { + DebugUtil.TraceMethodNameUsingStackFrame(); + OnAwake?.Invoke(); + } + + void Start() + { + DebugUtil.TraceMethodNameUsingStackFrame(); + OnStart?.Invoke(); + } + + void Update() + { + Debug.Assert(Application.isPlaying, RuntimeErrorMessage); + OnUpdate?.Invoke(); + } + + void FixedUpdate() + { + Debug.Assert(Application.isPlaying, RuntimeErrorMessage); + OnFixedUpdate?.Invoke(); + } + + void LateUpdate() + { + Debug.Assert(Application.isPlaying, RuntimeErrorMessage); + OnLateUpdate?.Invoke(); + } + + void OnDestroy() + { + DebugUtil.Trace($"{nameof(RuntimeUpdaterBehaviour)} Destroyed"); + OnDestroyed?.Invoke(); + OnAwake = null; + OnStart = null; + OnUpdate = null; + OnFixedUpdate = null; + OnLateUpdate = null; + } + } +} diff --git a/Common/Runtime/Helpers/RuntimeUpdaterBehaviour.cs.meta b/Common/Runtime/Helpers/RuntimeUpdaterBehaviour.cs.meta new file mode 100644 index 0000000..4b08746 --- /dev/null +++ b/Common/Runtime/Helpers/RuntimeUpdaterBehaviour.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 63896f9cea444ec9978c3e711dcad9cd +timeCreated: 1659228467 \ No newline at end of file diff --git a/Common/Runtime/NetworkTypes.meta b/Common/Runtime/NetworkTypes.meta new file mode 100644 index 0000000..27b0faf --- /dev/null +++ b/Common/Runtime/NetworkTypes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 146dd8c29e5e4ee18bc1bed77d5ec4e6 +timeCreated: 1674250863 \ No newline at end of file diff --git a/Common/Runtime/NetworkTypes/BandwidthTypes.cs b/Common/Runtime/NetworkTypes/BandwidthTypes.cs new file mode 100644 index 0000000..7ab3f42 --- /dev/null +++ b/Common/Runtime/NetworkTypes/BandwidthTypes.cs @@ -0,0 +1,43 @@ +using System; + +using static Unity.Multiplayer.Tools.Common.BandwidthTypes; + +namespace Unity.Multiplayer.Tools.Common +{ + [Flags] + enum BandwidthTypes + { + None = 0, + + /// + /// Bandwidth that doesn't fall into any of the other categories + /// + Other = 1 << 0, + NetVar = 1 << 1, + Rpc = 1 << 2, + + All = Other | NetVar | Rpc + } + + static class BandwidthTypesExtensions + { + public static string DisplayName(this BandwidthTypes direction) + { + return direction switch + { + None => "None", + All => "All", + Other => "Other", + NetVar => "NetVars", + Rpc => "RPCs", + + Other | NetVar => "NetVars and Other", + Other | Rpc => "RPCs and Other", + NetVar | Rpc => "NetVars and RPCs", + + _ => throw new ArgumentOutOfRangeException($"Unknow {nameof(NetworkDirection)} {direction}") + }; + } + } +} + diff --git a/Common/Runtime/NetworkTypes/BandwidthTypes.cs.meta b/Common/Runtime/NetworkTypes/BandwidthTypes.cs.meta new file mode 100644 index 0000000..d2a049f --- /dev/null +++ b/Common/Runtime/NetworkTypes/BandwidthTypes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a5d9b3155805475fb142dc482d629d4a +timeCreated: 1677273946 \ No newline at end of file diff --git a/NetworkProfiler/Runtime/Models/Profiler/BytesSentAndReceived.cs b/Common/Runtime/NetworkTypes/BytesSentAndReceived.cs similarity index 57% rename from NetworkProfiler/Runtime/Models/Profiler/BytesSentAndReceived.cs rename to Common/Runtime/NetworkTypes/BytesSentAndReceived.cs index 84bdc07..e0000ee 100644 --- a/NetworkProfiler/Runtime/Models/Profiler/BytesSentAndReceived.cs +++ b/Common/Runtime/NetworkTypes/BytesSentAndReceived.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; -using Unity.Multiplayer.Tools.MetricTypes; -namespace Unity.Multiplayer.Tools.NetworkProfiler.Runtime +namespace Unity.Multiplayer.Tools.Common { [Serializable] - internal struct BytesSentAndReceived : IEquatable + struct BytesSentAndReceived : IEquatable { public BytesSentAndReceived(long sent = 0, long received = 0) { @@ -13,8 +12,25 @@ public BytesSentAndReceived(long sent = 0, long received = 0) Received = received; } - public long Sent { get; } - public long Received { get; } + public BytesSentAndReceived(long count, NetworkDirection direction) + { + Sent = ((direction & NetworkDirection.Sent) != NetworkDirection.None) ? count : 0; + Received = ((direction & NetworkDirection.Received) != NetworkDirection.None) ? count : 0; + } + + public long Sent { get; set; } + public long Received { get; set; } + + /// + /// Indexes this BytesSentAndReceived by direction. This works as follows: + /// 1. Returns the sent field if the direction is Sent. + /// 2. Returns the received field if the direction is Received. + /// 3. Returns the sum if the direction is SentAndReceived. + /// 4. Returns 0 if the direction is None. + /// + public long this[NetworkDirection direction] => + ((direction & NetworkDirection.Sent) != NetworkDirection.None ? Sent : 0) + + ((direction & NetworkDirection.Received) != NetworkDirection.None ? Received : 0); public NetworkDirection Direction => (Sent > 0f ? NetworkDirection.Sent : NetworkDirection.None) | @@ -39,13 +55,7 @@ public override bool Equals(object obj) a.Sent + b.Sent, a.Received + b.Received); - public override int GetHashCode() - { - unchecked - { - return (Sent.GetHashCode() * 397) ^ Received.GetHashCode(); - } - } + public override int GetHashCode() => HashCode.Combine(Sent, Received); public override string ToString() { diff --git a/NetworkProfiler/Runtime/Models/Profiler/BytesSentAndReceived.cs.meta b/Common/Runtime/NetworkTypes/BytesSentAndReceived.cs.meta similarity index 100% rename from NetworkProfiler/Runtime/Models/Profiler/BytesSentAndReceived.cs.meta rename to Common/Runtime/NetworkTypes/BytesSentAndReceived.cs.meta diff --git a/MetricTypes/Runtime/MetricTypes/NetworkDirection.cs b/Common/Runtime/NetworkTypes/NetworkDirection.cs similarity index 57% rename from MetricTypes/Runtime/MetricTypes/NetworkDirection.cs rename to Common/Runtime/NetworkTypes/NetworkDirection.cs index d496edd..dd59e3d 100644 --- a/MetricTypes/Runtime/MetricTypes/NetworkDirection.cs +++ b/Common/Runtime/NetworkTypes/NetworkDirection.cs @@ -1,6 +1,6 @@ using System; -namespace Unity.Multiplayer.Tools.MetricTypes +namespace Unity.Multiplayer.Tools.Common { [Flags] enum NetworkDirection @@ -22,11 +22,16 @@ static class NetworkDirectionConstants static class NetworkDirectionExtensions { - public static string DisplayString(this NetworkDirection direction) + public static string DisplayName(this NetworkDirection direction) { - return direction == NetworkDirection.None - ? "" - : direction.ToString(); + return direction switch + { + NetworkDirection.None => "None", + NetworkDirection.Received => "Received", + NetworkDirection.Sent => "Sent", + NetworkDirection.SentAndReceived => "Sent And Received", + _ => throw new ArgumentOutOfRangeException($"Unknow {nameof(NetworkDirection)} {direction}") + }; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/NetworkDirection.cs.meta b/Common/Runtime/NetworkTypes/NetworkDirection.cs.meta similarity index 100% rename from MetricTypes/Runtime/MetricTypes/NetworkDirection.cs.meta rename to Common/Runtime/NetworkTypes/NetworkDirection.cs.meta diff --git a/Common/Runtime/Profiling.meta b/Common/Runtime/Profiling.meta new file mode 100644 index 0000000..a5fa819 --- /dev/null +++ b/Common/Runtime/Profiling.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cc931f47d39b4403b631ab609cfd1f59 +timeCreated: 1679440374 \ No newline at end of file diff --git a/Common/Runtime/Profiling/MultiSampleTimer.cs b/Common/Runtime/Profiling/MultiSampleTimer.cs new file mode 100644 index 0000000..e514318 --- /dev/null +++ b/Common/Runtime/Profiling/MultiSampleTimer.cs @@ -0,0 +1,87 @@ +#if UNITY_MP_TOOLS_DEV +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace Unity.Multiplayer.Tools.Common +{ + class MultiSampleTimer + { + const double k_Milli = 1000; + static readonly double k_SecondsPerTick = 1d / Stopwatch.Frequency; + static readonly double k_MilliSecondsPerTick = k_Milli * k_SecondsPerTick; + + const long k_SampleSize = 1000; + + readonly List m_TickCounts = new(); + readonly Stopwatch m_Stopwatch = new(); + readonly string m_ScopeName; + + long SampleCount => m_TickCounts.Count; + + public MultiSampleTimer(string scopeName) + { + m_ScopeName = scopeName; + } + + void Reset() + { + m_TickCounts.Clear(); + m_Stopwatch.Reset(); + } + + public void BeginSample() + { + m_Stopwatch.Start(); + } + + public void EndSample() + { + m_Stopwatch.Stop(); + var sample = m_Stopwatch.ElapsedTicks; + m_TickCounts.Add(sample); + m_Stopwatch.Reset(); + + if (SampleCount >= k_SampleSize) + { + LogStatsToConsole(); + Reset(); + } + } + + public void LogStatsToConsole() + { + var totalTicks = 0L; + var minTicks = long.MaxValue; + var maxTicks = long.MinValue; + var sampleCount = m_TickCounts.Count; + for (var i = 0; i < sampleCount; ++i) + { + var sample = m_TickCounts[i]; + totalTicks += m_TickCounts[i]; + minTicks = Math.Min(minTicks, sample); + maxTicks = Math.Max(maxTicks, sample); + } + var totalSeconds = totalTicks * k_SecondsPerTick; + + m_TickCounts.Sort(); + var medianTicks = m_TickCounts[sampleCount / 2]; + + var average_ms = k_Milli * totalSeconds / sampleCount; + var median_ms = medianTicks * k_MilliSecondsPerTick; + var min_ms = minTicks * k_MilliSecondsPerTick; + var max_ms = maxTicks * k_MilliSecondsPerTick; + + UnityEngine.Debug.Log( + $"{m_ScopeName} executed {sampleCount} times in {totalSeconds} seconds. " + + $"Average: {average_ms} ms, median: {median_ms} ms, min: {min_ms}, max: {max_ms}"); + } + + public void LogSamplesToFile(string filepath) + { + File.WriteAllText(filepath, string.Join(",\n", m_TickCounts)); + } + } +} +#endif // UNITY_MP_TOOLS_DEV diff --git a/Common/Runtime/Profiling/MultiSampleTimer.cs.meta b/Common/Runtime/Profiling/MultiSampleTimer.cs.meta new file mode 100644 index 0000000..6ea3e76 --- /dev/null +++ b/Common/Runtime/Profiling/MultiSampleTimer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 023325256c5c401aab55158e703f2d06 +timeCreated: 1679440385 \ No newline at end of file diff --git a/Common/Runtime/Visualization.meta b/Common/Runtime/Visualization.meta new file mode 100644 index 0000000..b2147c1 --- /dev/null +++ b/Common/Runtime/Visualization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a41eb8582fac48e1a0faf2ce966e1b16 +timeCreated: 1677718668 \ No newline at end of file diff --git a/Common/Runtime/Visualization/CategoricalColorPalette.cs b/Common/Runtime/Visualization/CategoricalColorPalette.cs new file mode 100644 index 0000000..1b36f2e --- /dev/null +++ b/Common/Runtime/Visualization/CategoricalColorPalette.cs @@ -0,0 +1,94 @@ +using UnityEngine; + +namespace Unity.Multiplayer.Tools.Common.Visualization +{ + /// + /// A color-blind-friendly color palette for visualization of categorical data + /// + // [InitializeOnLoad] + static class CategoricalColorPalette + { + /// + /// The number of available colors in the palette + /// + public static int ColorCount => k_ColorPalette.Length; + + /// + /// The color output by TryGetColor when the index exceeds ColorCount + /// + public static Color k_AbsentColor = Color.clear; + + public static Color GetColor(int index) => k_ColorPalette[index]; + + /// + /// If index is in range [0, ColorCount) returns true and outputs a color in the palette. + ///
+ /// If index is out of range returns false and outputs k_AbsentColor + ///
+ public static bool TryGetColor(int index, out Color color) + { + if (0 < index && index < ColorCount) + { + color = k_ColorPalette[index]; + return true; + } + color = k_AbsentColor; + return false; + } + + /// + /// List generated using + /// + static readonly Color[] k_ColorPalette = + { + new( 0 / 255f, 180 / 255f, 8 / 255f), // kelly green + new(142 / 255f, 6 / 255f, 205 / 255f), // french violet + new( 0 / 255f, 235 / 255f, 193 / 255f), // vivid opal + new(134 / 255f, 8 / 255f, 28 / 255f), // hot chile + new( 0 / 255f, 194 / 255f, 249 / 255f), // capri + new(216 / 255f, 13 / 255f, 123 / 255f), // magenta + new(175 / 255f, 255 / 255f, 42 / 255f), // lime + new( 70 / 255f, 11 / 255f, 112 / 255f), // christalle + new(255 / 255f, 46 / 255f, 149 / 255f), // persian rose + new( 0 / 255f, 119 / 255f, 2 / 255f), // bilbao + new(255 / 255f, 163 / 255f, 252 / 255f), // violet + new( 0 / 255f, 87 / 255f, 69 / 255f), // deep opal + new(255 / 255f, 135 / 255f, 53 / 255f), // burning orange + new( 0 / 255f, 121 / 255f, 250 / 255f), // azure + new(255 / 255f, 215 / 255f, 225 / 255f), // azalea + new( 0 / 255f, 64 / 255f, 2 / 255f), // british racing green + new( 0 / 255f, 159 / 255f, 250 / 255f), // bleu de france + new(171 / 255f, 13 / 255f, 97 / 255f), // jazberry jam + new( 0 / 255f, 244 / 255f, 7 / 255f), // radioactive green + new(107 / 255f, 6 / 255f, 159 / 255f), // purple heart + new( 0 / 255f, 203 / 255f, 167 / 255f), // aquamarine + new(222 / 255f, 13 / 255f, 46 / 255f), // amaranth red + new(124 / 255f, 255 / 255f, 250 / 255f), // electric blue + new( 90 / 255f, 10 / 255f, 51 / 255f), // mulberry + new(255 / 255f, 66 / 255f, 53 / 255f), // carmine + new( 0 / 255f, 95 / 255f, 204 / 255f), // royal blue + new(255 / 255f, 172 / 255f, 198 / 255f), // amaranth pink + new( 0 / 255f, 90 / 255f, 1 / 255f), // san felix + new(255 / 255f, 102 / 255f, 253 / 255f), // fuchsia + new( 0 / 255f, 145 / 255f, 117 / 255f), // elf green + new(255 / 255f, 226 / 255f, 57 / 255f), // gargoyle gas + new( 0 / 255f, 48 / 255f, 111 / 255f), // madison + new( 0 / 255f, 175 / 255f, 142 / 255f), // jeepers creepers + new(178 / 255f, 7 / 255f, 37 / 255f), // alabama crimson + new( 0 / 255f, 229 / 255f, 248 / 255f), // aqua blue + new(129 / 255f, 13 / 255f, 73 / 255f), // french plum + new( 0 / 255f, 211 / 255f, 2 / 255f), // vivid harlequin + new(180 / 255f, 10 / 255f, 252 / 255f), // electric purple + new(134 / 255f, 255 / 255f, 222 / 255f), // light turquoise + new( 95 / 255f, 9 / 255f, 20 / 255f), // rosewood + new(237 / 255f, 13 / 255f, 253 / 255f), // psychedelic purple + new( 0 / 255f, 115 / 255f, 92 / 255f), // robin hood + new(255 / 255f, 185 / 255f, 53 / 255f), // frenzee + new( 0 / 255f, 72 / 255f, 158 / 255f), // tory blue + new(255 / 255f, 120 / 255f, 173 / 255f), // barbie pink + new( 0 / 255f, 149 / 255f, 3 / 255f), // india green + new(255 / 255f, 213 / 255f, 253 / 255f), // pale mauve + new( 0 / 255f, 61 / 255f, 48 / 255f), // sherwood green + }; + } +} diff --git a/Common/Runtime/Visualization/CategoricalColorPalette.cs.meta b/Common/Runtime/Visualization/CategoricalColorPalette.cs.meta new file mode 100644 index 0000000..9b31e80 --- /dev/null +++ b/Common/Runtime/Visualization/CategoricalColorPalette.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aac5a36d520d4738a214a66d2b459555 +timeCreated: 1677718686 \ No newline at end of file diff --git a/Common/Runtime/Visualization/CategoricalColorPaletteVisualizer.cs b/Common/Runtime/Visualization/CategoricalColorPaletteVisualizer.cs new file mode 100644 index 0000000..d29e979 --- /dev/null +++ b/Common/Runtime/Visualization/CategoricalColorPaletteVisualizer.cs @@ -0,0 +1,415 @@ +#if UNITY_EDITOR +#if UNITY_MP_TOOLS_DEV +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEditor; +using UnityEngine; +using UnityEngine.UIElements; + +namespace Unity.Multiplayer.Tools.Common.Visualization +{ + /// + /// This class provides functionality for visualizing and generating color-blind friendly color palettes. + /// + /// + /// We have a good list of color-blind friendly colors from the 24-color palette here: + /// http://mkweb.bcgsc.ca/colorblind/palettes.mhtml. + /// + /// However, these colors are ordered in such a way that there is little contrast between adjacent colors in the palette. + /// Thus this original ordering is really bad for our use cases, as we never know in advance how many different colors will be used, + /// as this depends on the number of clients, variables, etc. that the user is visualizing. + /// + /// When visualizing N categories of data we will only use the first N colors, so we want to reorder these colors to + /// get as much contrast out of the first N colors as possible, including for users with color blindness. + /// + class CategoricalColorPaletteVisualizer : EditorWindow + { + [MenuItem("Window/Mp Tools/Color Palette")] + static void ShowColorPaletteGenerator() + { + var window = EditorWindow.GetWindow(); + window.titleContent = new GUIContent("MP Tools Color Palette Visualizer"); + + // Can become very hard to find a window on a multi-monitor setup if it accidentally becomes 1x1 + window.minSize = new Vector2(200, 200); + window.maxSize = new Vector2(3840, 2160); + } + + /// + /// Color names in their original order from the 24-color palette here: http://mkweb.bcgsc.ca/colorblind/palettes.mhtml + /// + public static readonly string[] k_ColorNames_OriginalOrder = + { + "sherwood green", + "mulberry", + "deep opal", + "french plum", + "robin hood", + "jazberry jam", + "elf green", + "magenta", + "jeepers creepers", + "persian rose", + "aquamarine", + "barbie pink", + "vivid opal", + "amaranth pink", + "light turquoise", + "azalea", + + "madison", + "christalle", + "tory blue", + "purple heart", + "royal blue", + "french violet", + "azure", + "electric purple", + "bleu de france", + "psychedelic purple", + "capri", + "fuchsia", + "aqua blue", + "violet", + "electric blue", + "pale mauve", + + "british racing green", + "rosewood", + "san felix", + "hot chile", + "bilbao", + "alabama crimson", + "india green", + "amaranth red", + "kelly green", + "carmine", + "vivid harlequin", + "burning orange", + "radioactive green", + "frenzee", + "lime", + "gargoyle gas", + }; + + /// + /// Colors in their original order from the 24-color palette here: http://mkweb.bcgsc.ca/colorblind/palettes.mhtml + /// + public static readonly Color[] k_ColorPalette_OriginalOrder = + { + new( 0 / 255f, 61 / 255f, 48 / 255f), // sherwood green + new( 90 / 255f, 10 / 255f, 51 / 255f), // mulberry + new( 0 / 255f, 87 / 255f, 69 / 255f), // deep opal + new(129 / 255f, 13 / 255f, 73 / 255f), // french plum + new( 0 / 255f, 115 / 255f, 92 / 255f), // robin hood + new(171 / 255f, 13 / 255f, 97 / 255f), // jazberry jam + new( 0 / 255f, 145 / 255f, 117 / 255f), // elf green + new(216 / 255f, 13 / 255f, 123 / 255f), // magenta + new( 0 / 255f, 175 / 255f, 142 / 255f), // jeepers creepers + new(255 / 255f, 46 / 255f, 149 / 255f), // persian rose + new( 0 / 255f, 203 / 255f, 167 / 255f), // aquamarine + new(255 / 255f, 120 / 255f, 173 / 255f), // barbie pink + new( 0 / 255f, 235 / 255f, 193 / 255f), // vivid opal + new(255 / 255f, 172 / 255f, 198 / 255f), // amaranth pink + new(134 / 255f, 255 / 255f, 222 / 255f), // light turquoise + new(255 / 255f, 215 / 255f, 225 / 255f), // azalea + new( 0 / 255f, 48 / 255f, 111 / 255f), // madison + new( 70 / 255f, 11 / 255f, 112 / 255f), // christalle + new( 0 / 255f, 72 / 255f, 158 / 255f), // tory blue + new(107 / 255f, 6 / 255f, 159 / 255f), // purple heart + new( 0 / 255f, 95 / 255f, 204 / 255f), // royal blue + new(142 / 255f, 6 / 255f, 205 / 255f), // french violet + new( 0 / 255f, 121 / 255f, 250 / 255f), // azure + new(180 / 255f, 10 / 255f, 252 / 255f), // electric purple + new( 0 / 255f, 159 / 255f, 250 / 255f), // bleu de france + new(237 / 255f, 13 / 255f, 253 / 255f), // psychedelic purple + new( 0 / 255f, 194 / 255f, 249 / 255f), // capri + new(255 / 255f, 102 / 255f, 253 / 255f), // fuchsia + new( 0 / 255f, 229 / 255f, 248 / 255f), // aqua blue + new(255 / 255f, 163 / 255f, 252 / 255f), // violet + new(124 / 255f, 255 / 255f, 250 / 255f), // electric blue + new(255 / 255f, 213 / 255f, 253 / 255f), // pale mauve + new( 0 / 255f, 64 / 255f, 2 / 255f), // british racing green + new( 95 / 255f, 9 / 255f, 20 / 255f), // rosewood + new( 0 / 255f, 90 / 255f, 1 / 255f), // san felix + new(134 / 255f, 8 / 255f, 28 / 255f), // hot chile + new( 0 / 255f, 119 / 255f, 2 / 255f), // bilbao + new(178 / 255f, 7 / 255f, 37 / 255f), // alabama crimson + new( 0 / 255f, 149 / 255f, 3 / 255f), // india green + new(222 / 255f, 13 / 255f, 46 / 255f), // amaranth red + new( 0 / 255f, 180 / 255f, 8 / 255f), // kelly green + new(255 / 255f, 66 / 255f, 53 / 255f), // carmine + new( 0 / 255f, 211 / 255f, 2 / 255f), // vivid harlequin + new(255 / 255f, 135 / 255f, 53 / 255f), // burning orange + new( 0 / 255f, 244 / 255f, 7 / 255f), // radioactive green + new(255 / 255f, 185 / 255f, 53 / 255f), // frenzee + new(175 / 255f, 255 / 255f, 42 / 255f), // lime + new(255 / 255f, 226 / 255f, 57 / 255f), // gargoyle gas + }; + + public static int ColorCount => k_ColorPalette_OriginalOrder.Length; + + enum ColorOrder + { + /// + /// Default color ordering from http://mkweb.bcgsc.ca/colorblind/palettes.mhtml + /// + Original, + + /// + /// Zig-zag ordering to try to get more contrast out of the first N colors + /// + ZigZag, + } + static readonly string[] k_ColorOrderNames = EnumUtil.GetNames(); + + static int GetColorIndex(ColorOrder order, int index) + { + return order switch + { + ColorOrder.Original => index, + ColorOrder.ZigZag => ZigZagOrdering(index), + _ => throw new ArgumentOutOfRangeException(nameof(order), order, null) + }; + } + + /// + /// Method to reorder the indices to maximize the contrast between the first N entries, + /// unlike the original order in which adjacent colors are very similar. + /// + static int ZigZagOrdering(int indexInNewOrder) + { + var i = (ColorCount - 1) - indexInNewOrder; + + var series = i % 3; + + var redOrGreenShift = ((i & 8) != 0 ? 1 : 0); + + var redOrGreen = (i + redOrGreenShift) & 1; + + var next = -i; + + var lightness = + ((next & 0b001) != 0 ? 0b100 : 0) | + ((next & 0b010) != 0 ? 0b010 : 0) | + ((next & 0b100) != 0 ? 0b001 : 0); + + var indexInOriginalOrder = series * 16 + lightness * 2 + redOrGreen; + + return indexInOriginalOrder; + } + + enum ColorVisionType + { + /// + /// Normal vision + /// + Normal, + + /// + /// Deuteranomaly and deuteranopia: type of color blindness that affects 6% of males, + /// missing or anomalous cones to see green light + /// + Deuteranopia, + + /// + /// Protanomaly and protanopia: type of color blindness that affects 2% of males; + /// missing or anomalous cones to see red light + /// + Protanopia, + + /// + /// Monochromacy: total color blindness, incredibly rare but included for comparison. + /// Also how a color palette or visualization would look if printed out in greyscale. + /// + Monochromacy, + + Count, + } + + static string ColorVisionDescription(ColorVisionType visionType) + { + return visionType switch + { + ColorVisionType.Normal => "Normal vision", + ColorVisionType.Deuteranopia => "Affecting green cones", + ColorVisionType.Protanopia => "Affecting red cones", + ColorVisionType.Monochromacy => "Total color blindness", + _ => throw new ArgumentOutOfRangeException(nameof(visionType), visionType, null) + }; + } + + static Color ColorTransform(ColorVisionType visionType, Color inputColor) + { + return visionType switch + { + ColorVisionType.Normal => inputColor, + ColorVisionType.Deuteranopia => DeuteranopiaColorTransform(inputColor), + ColorVisionType.Protanopia => ProtanopiaColorTransform(inputColor), + ColorVisionType.Monochromacy => MonochromacyColorTransform(inputColor), + _ => throw new ArgumentOutOfRangeException(nameof(visionType), visionType, null) + }; + } + + /// + /// Simulation of color blindness missing cone to see green light + /// + /// + /// Transform is from here: http://mkweb.bcgsc.ca/colorblind/math.mhtml + /// + static Color DeuteranopiaColorTransform(Color inputColor) + { + var c = inputColor; + return new Color( + 0.33066007f * c.r + 0.66933993f * c.g + 0f * c.b, + 0.33066007f * c.r + 0.66933993f * c.g + 0f * c.b, + -0.02785538f * c.r + 0.02785538f * c.g + 1f * c.b); + } + + /// + /// Simulation of color blindness missing cone to see red light + /// + /// + /// Transform is from here: http://mkweb.bcgsc.ca/colorblind/math.mhtml + /// + static Color ProtanopiaColorTransform(Color inputColor) + { + var c = inputColor; + return new Color( + 0.170556992f * c.r + 0.829443014f * c.g + 0f * c.b, + 0.170556991f * c.r + 0.829443008f * c.g + 0f * c.b, + -0.004517144f * c.r + 0.004517144f * c.g + 1f * c.b); + } + + /// + /// Simulation of total color blindness + /// + /// + /// Transform is from here: http://mkweb.bcgsc.ca/colorblind/math.mhtml + /// + static Color MonochromacyColorTransform(Color inputColor) + { + var c = inputColor; + return new Color( + 0.2126f * c.r + 0.7152f * c.g + 0.0722f * c.b, + 0.2126f * c.r + 0.7152f * c.g + 0.0722f * c.b, + 0.2126f * c.r + 0.7152f * c.g + 0.0722f * c.b); + } + + ColorOrder m_Order = ColorOrder.ZigZag; + + [NonSerialized] + VisualElement m_ColorVisualizationContainer; + + public void CreateGUI() + { + var root = rootVisualElement; + + var windowTitle = new Label("MP Tools Categorical Color Palette"); + windowTitle.style.unityFontStyleAndWeight = FontStyle.Bold; + windowTitle.style.fontSize = 24; + root.Add(windowTitle); + + var windowDescription = new Label( + "Window for visualizing the color palette used in the Multiplayer Tools package as it would appear " + + "with various kinds of color blindness."); + root.Add(windowDescription); + + var colorOrderDropdown = new DropdownField( + label: "Color Order", + choices: k_ColorOrderNames.ToList(), + defaultIndex: (int)m_Order); + root.Add(colorOrderDropdown); + colorOrderDropdown.style.alignSelf = Align.FlexStart; + colorOrderDropdown.RegisterCallback>(evt => + { + m_Order = (ColorOrder)colorOrderDropdown.index; + RegenerateColorPaletteVisualization(); + }); + + var outputColorsToConsoleButton = new Button(() => LogColorsToConsole(m_Order)); + outputColorsToConsoleButton.text = "Output Colors to Console"; + root.Add(outputColorsToConsoleButton); + + outputColorsToConsoleButton.style.alignSelf = Align.FlexStart; + + root.Add(m_ColorVisualizationContainer ??= new()); + RegenerateColorPaletteVisualization(); + } + + void RegenerateColorPaletteVisualization() + { + m_ColorVisualizationContainer.Clear(); + m_ColorVisualizationContainer.Add(CreateColorPaletteVisualization(m_Order)); + } + + static VisualElement CreateColorPaletteVisualization(ColorOrder order) + { + var colorColumns = new VisualElement(); + colorColumns.style.flexDirection = FlexDirection.Row; + + for (var visionType = ColorVisionType.Normal; + (int)visionType < (int)ColorVisionType.Count; + visionType = (ColorVisionType)((int)visionType + 1)) + { + var column = new VisualElement(); + column.style.flexDirection = FlexDirection.Column; + colorColumns.Add(column); + + var columnTitle = new Label(visionType.ToString()); + columnTitle.style.unityFontStyleAndWeight = FontStyle.Bold; + columnTitle.style.fontSize = 16; + column.Add(columnTitle); + + var description = new Label($"({ColorVisionDescription(visionType)})"); + column.Add(description); + + for (var i = 0; i < ColorCount; ++i) + { + var index = GetColorIndex(order, i); + var colorName = k_ColorNames_OriginalOrder[index]; + var color = k_ColorPalette_OriginalOrder[index]; + var colorSimulated = ColorTransform(visionType, color); + var label = new Label(colorName); + label.style.color = Color.white; + label.style.backgroundColor = colorSimulated; + label.style.paddingLeft = 10; + label.style.paddingBottom = 2; + label.style.paddingTop = 2; + label.style.unityFontStyleAndWeight = FontStyle.Bold; + column.Add(label); + } + } + return colorColumns; + } + + static void LogColorsToConsole(ColorOrder order) + { + byte Round(float x) => (byte)Math.Round(255f * x); + + HashSet existingEntries = new(); + + StringBuilder colors = new(); + for (var i = 0; i < CategoricalColorPalette.ColorCount; ++i) + { + var index = GetColorIndex(order, i); + + var name = k_ColorNames_OriginalOrder[index]; + var color = k_ColorPalette_OriginalOrder[index]; + + if (existingEntries.Contains(index)) + { + Debug.LogWarning($"Color order {order} contains duplicate entries for color {name} corresponding to index {index}"); + } + existingEntries.Add(index); + + colors.AppendLine($"new({Round(color.r),3} / 255f, {Round(color.g),3} / 255f, {Round(color.b),3} / 255f), // {name}"); + } + Debug.Log(colors.ToString()); + } + } +} +#endif // UNITY_MP_TOOLS_DEV +#endif // UNITY_EDITOR \ No newline at end of file diff --git a/Common/Runtime/Visualization/CategoricalColorPaletteVisualizer.cs.meta b/Common/Runtime/Visualization/CategoricalColorPaletteVisualizer.cs.meta new file mode 100644 index 0000000..46f85a9 --- /dev/null +++ b/Common/Runtime/Visualization/CategoricalColorPaletteVisualizer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b131b357a08f47658db4eb5ec54baceb +timeCreated: 1678479127 \ No newline at end of file diff --git a/Common/Tests/Data/ContinuousExponentialMovingAverageTests.cs b/Common/Tests/Data/ContinuousExponentialMovingAverageTests.cs deleted file mode 100644 index 744d391..0000000 --- a/Common/Tests/Data/ContinuousExponentialMovingAverageTests.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.Common.Tests -{ - internal class ContinuousExponentialMovingAverageTests - { - [TestCase(0.3)] - [TestCase(0.5)] - [TestCase(0.7)] - [TestCase(0.3)] - public void InitializedCorrectlyWithOneArgument(double decayConstant) - { - var ema = new ContinuousExponentialMovingAverage(decayConstant); - Assert.AreEqual(decayConstant, ema.DecayConstant); - Assert.AreEqual(0f, ema.LastValue); - Assert.AreEqual(Double.NegativeInfinity, ema.LastTime); - } - - [TestCase(0.5)] - [TestCase(0.7, -342.74)] - [TestCase(0.3, 49.63)] - public void InitializedCorrectlyWithTwoArguments( - double decayConstant, - double initialValue = 0f) - { - var ema = new ContinuousExponentialMovingAverage(decayConstant, initialValue); - Assert.AreEqual(decayConstant, ema.DecayConstant); - Assert.AreEqual(initialValue, ema.LastValue); - } - - [TestCase(0.5)] - [TestCase(0.7, -342.74)] - [TestCase(0.3, 49.63)] - [TestCase(0.3, 49.63, 0)] - [TestCase(0.3, 147.2, -7)] - [TestCase(0.3, 147.2, 38)] - public void InitializedCorrectlyWithThreeArguments( - double decayConstant, - double initialValue = 0, - double initialTime = Double.MinValue) - { - var ema = new ContinuousExponentialMovingAverage(decayConstant, initialValue, initialTime); - Assert.AreEqual(decayConstant, ema.DecayConstant); - Assert.AreEqual(initialValue, ema.LastValue); - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(3)] - [TestCase(4)] - [TestCase(5)] - [TestCase(6)] - [TestCase(7)] - [TestCase(8)] - [TestCase(87)] - [TestCase(128)] - [TestCase(256)] - [TestCase(4967)] - public void GetDecayConstantForHalfLifeIsWorkingCorrectly(double halfLife) - { - var decayConstant = ContinuousExponentialMovingAverage.GetDecayConstantForHalfLife(halfLife); - Assert.AreEqual(ContinuousExponentialMovingAverage.k_ln2 / halfLife, decayConstant); - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(3)] - [TestCase(4)] - [TestCase(5)] - [TestCase(6)] - [TestCase(7)] - [TestCase(8)] - [TestCase(87)] - [TestCase(128)] - [TestCase(256)] - [TestCase(4967)] - public void CreateWithHalfLifeIsWorkingCorrectly(double halfLife) - { - var cema = ContinuousExponentialMovingAverage.CreateWithHalfLife(halfLife); - Assert.AreEqual(ContinuousExponentialMovingAverage.k_ln2 / halfLife, cema.DecayConstant); - Assert.AreEqual(0d, cema.LastValue); - Assert.AreEqual(Double.NegativeInfinity, cema.LastTime); - } - - [TestCase(-89.30, 4.7)] - [TestCase(-12.80, 16.37)] - [TestCase( 0.00, -148.6)] - [TestCase( 0.47, 1.23)] - [TestCase(111.70, 785.0)] - public void ConstructedWithDefaultTimeAssumesNextInstantaneousSample(double time, double value) - { - var cema = ContinuousExponentialMovingAverage.CreateWithHalfLife(0.5); - Assert.AreEqual(0, cema.LastValue); - Assert.AreEqual(Double.NegativeInfinity, cema.LastTime); - - cema.AddSampleForGauge(value, time); - Assert.AreEqual(value, cema.LastValue); - Assert.AreEqual(value, cema.GetCounterValue(time)); - } - - [TestCase(0, 0, 0, 0)] - [TestCase(0, 0, 0, 1)] - [TestCase(0, 0, 1, 0)] - [TestCase(0, 0, 1, 1)] - [TestCase(0, 1, 0, 0)] - [TestCase(0, 1, 0, 1)] - [TestCase(0, 1, 1, 0)] - [TestCase(0, 1, 1, 1)] - [TestCase(0.003, -485, 0.005, 184.5)] - [TestCase(0.004, 485, 0.078, -184.5)] - [TestCase(0.003, 485, 10089, 184.5)] - [TestCase(0.004, 485, 1e9, -184.5)] - public void ConstructedWithInitialTimeDoesNotAssumeNextInstantaneousSample( - double t0, // Time 0 - double v0, // Value at time 0 - double t1, // Time 1 - double v1) // Value at time 1 - { - var decayConstant = ContinuousExponentialMovingAverage.GetDecayConstantForHalfLife(2); - var cema = new ContinuousExponentialMovingAverage(decayConstant, v0, t0); - Assert.AreEqual(v0, cema.LastValue); - Assert.AreEqual(t0, cema.LastTime); - Assert.AreEqual(cema.LastValue, cema.GetCounterValue(t0)); - - cema.AddSampleForGauge(v1, t1); - Assert.AreEqual(t1, cema.LastTime); - Assert.AreEqual(cema.LastValue, cema.GetCounterValue(t1)); - - // After adding a new sample, the result should be between the min and max sample values - var minValue = Math.Min(v0, v1); - var maxValue = Math.Max(v0, v1); - - Assert.LessOrEqual(minValue, cema.LastValue); - Assert.LessOrEqual(cema.LastValue, maxValue); - } - - [TestCase(4.89, 0, 1, 0.1)] - [TestCase(-1178000, -964, 258, 100)] - public void ValueRemainsConstantWithConstantInput( - double value, - double startTime, - double endTime, - double timeStep) - { - var cema = ContinuousExponentialMovingAverage.CreateWithHalfLife(7.5); - for (double time = startTime; time < endTime; time += timeStep) - { - cema.AddSampleForGauge(value, time); - Assert.AreEqual(time, cema.LastTime); - Assert.AreEqual(value, cema.LastValue); - Assert.AreEqual(value, cema.GetCounterValue(time)); - } - } - - [TestCase(100, 0, 1, 0.1)] - [TestCase(487.5, -192, 289, 34)] - [TestCase(-487.5, -192, 289, 34)] - public void ValueDecaysFromInitialValueWithZeroInput( - double initialValue, - double startTime, - double endTime, - double timeStep) - { - var decayConstant = ContinuousExponentialMovingAverage.GetDecayConstantForHalfLife(timeStep); - var cema = new ContinuousExponentialMovingAverage(decayConstant, initialValue, startTime); - - Assert.AreEqual(startTime, cema.LastTime); - Assert.AreEqual(initialValue, cema.LastValue); - Assert.AreEqual(initialValue, cema.GetCounterValue(startTime)); - - for (double time = startTime + timeStep; time < endTime; time += timeStep) - { - var lastValue = cema.LastValue; - cema.AddSampleForGauge(0, time); - Assert.AreEqual(time, cema.LastTime); - Assert.Less(Math.Abs(cema.LastValue), Math.Abs(lastValue)); - Assert.Less(0, Math.Abs(cema.LastValue)); - } - } - - [TestCase(100, 0, 1, 0.1)] - [TestCase(487.5, -192, 289, 34)] - [TestCase(-487.5, -192, 289, 34)] - public void ValueRisesToMeetConstantInputWhenStartingFromZero( - double constantValue, - double startTime, - double endTime, - double timeStep) - { - var decayConstant = ContinuousExponentialMovingAverage.GetDecayConstantForHalfLife(timeStep); - var cema = new ContinuousExponentialMovingAverage(decayConstant, 0d, startTime); - - Assert.AreEqual(startTime, cema.LastTime); - Assert.AreEqual(0d, cema.LastValue); - Assert.AreEqual(0d, cema.GetCounterValue(startTime)); - - for (double time = startTime + timeStep; time < endTime; time += timeStep) - { - var lastValue = cema.LastValue; - cema.AddSampleForGauge(constantValue, time); - Assert.AreEqual(time, cema.LastTime); - - // The value is "charging" like a capacitor from zero to the constant value, - // so will always be larger than the previous value but also always smaller - // than the target value - Assert.Less(Math.Abs(lastValue), Math.Abs(cema.LastValue)); - Assert.Less(Math.Abs(cema.LastValue), Math.Abs(constantValue)); - } - } - - [TestCase(7)] - [TestCase(1847)] - [TestCase(4485741)] - public void VerifyContinuousExponentialMovingAverageOfRandomSamples(int seed) - { - var random = new Random(); - var totalTime = random.NextDouble() * 5 + 5; - var halfLife = random.NextDouble() * 99 + 1; - var cema = ContinuousExponentialMovingAverage.CreateWithHalfLife(halfLife); - - var expectedResult = 0d; - for (var time = 0d; time < totalTime; time += random.NextDouble() + 0.1) - { - var deltaTime = time - cema.LastTime; - - var newSample = random.NextDouble() * 100 - 50; - cema.AddSampleForGauge(newSample, time); - - var blendFactor = 1d - Math.Exp(-deltaTime * cema.DecayConstant); - var remainingDecayTime = totalTime - time; - expectedResult += newSample * blendFactor * Math.Exp(-remainingDecayTime * cema.DecayConstant); - - Assert.AreEqual(expectedResult, cema.GetCounterValue(totalTime), 1e-7); - } - } - - [TestCase(7)] - [TestCase(1847)] - [TestCase(4485741)] - public void VerifyContinuousExponentialMovingAverageOfRandomSamplesOverTime(int seed) - { - var random = new Random(); - var totalTime = random.NextDouble() * 5 + 5; - var halfLife = random.NextDouble() * 99 + 1; - var cema = ContinuousExponentialMovingAverage.CreateWithHalfLife(halfLife); - - var expectedResult = 0d; - for (var time = 0d; time < totalTime; time += random.NextDouble() + 0.1) - { - var deltaTime = time - cema.LastTime; - - var newSample = random.NextDouble() * 100 - 50; - cema.AddSampleForCounter(newSample, time); - - var blendFactor = 1d - Math.Exp(-deltaTime * cema.DecayConstant); - var remainingDecayTime = totalTime - time; - - expectedResult += (newSample / deltaTime) - * blendFactor - * Math.Exp(-remainingDecayTime * cema.DecayConstant); - - Assert.AreEqual(expectedResult, cema.GetCounterValue(totalTime), 1e-7); - } - } - } -} \ No newline at end of file diff --git a/Common/Tests/Data/EnumMapTests.cs b/Common/Tests/Data/EnumMapTests.cs deleted file mode 100644 index 7d166ed..0000000 --- a/Common/Tests/Data/EnumMapTests.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; - - -namespace Unity.Multiplayer.Tools.Common.Tests -{ - using static Unity.Multiplayer.Tools.Common.Tests.ContinuousEnum; - - enum EmptyEnum { } - - enum SingleValueEnum - { - A = 0, - } - enum ContinuousEnum - { - A, B, C, D, E, F, G, H, - } - enum ContinuousEnum_U8 : Byte - { - A, B, C, D, E, F, G, H, - } - enum ContinuousEnum_I16 : Int16 - { - A, B, C, D, E, F, G, H, - } - enum ContinuousEnum_U32 : UInt32 - { - A, B, C, D, E, F, G, H, - } - enum ContinuousEnum_I64 : Int64 - { - A, B, C, D, E, F, G, H, - } - enum ContinuousEnumShiftedUp - { - A = 7, B, C, D, E, F, G, H, - } - enum ContinuousEnumShiftedDown - { - A = -5, B, C, D, E, F, G, H, - } - enum EnumWithDuplicates - { - A = B, B = 0, - C = D, D = 1, - E = F, F = 2, - G = H, H = 3, - } - enum EnumWithHoles - { - A = 0, - C = 2, - E = 4, - G = 6 - } - enum EnumWithHolesAndDuplicates - { - A = B, B = 0, - C = D, D = 2, - E = F, F = 4, - G = H, H = 6 - } - enum EnumWithFlags - { - None = 0, - A = 1 << 0, - B = 1 << 1, - C = 1 << 2, - D = 1 << 3, - E = 1 << 4, - F = 1 << 5, - G = 1 << 6, - H = 1 << 7, - } - - [TestFixture] - class EnumMapTests - { - [TestCase((EmptyEnum)0 , int.MaxValue, int.MinValue, 0)] - [TestCase(SingleValueEnum.A , 0, 0, 1)] - [TestCase(ContinuousEnum.A , 0, 7, 8)] - [TestCase(ContinuousEnumShiftedUp.A , 7, 14, 8)] - [TestCase(ContinuousEnumShiftedDown.A , -5, 2, 8)] - [TestCase(EnumWithDuplicates.A , 0, 3, 4)] - [TestCase(EnumWithHoles.A , 0, 6, 4)] - [TestCase(EnumWithHolesAndDuplicates.A , 0, 6, 4)] - [TestCase(EnumWithFlags.A , 0, 128, 9)] - public void TestEnumCounts(TEnum _, int min, int max, int unique) - where TEnum : Enum - { - var (resultMin, resultMax, resultUnique) = EnumContinuity.GetMinMaxAndUniqueValueCount(); - Assert.AreEqual(min, resultMin); - Assert.AreEqual(max, resultMax); - Assert.AreEqual(unique, resultUnique); - } - - public enum ExpectedExceptionType - { - None, - UnhandledBackingType, - EmptyEnum, - NonZeroEnumMinimumValue, - DiscontinuousEnum, - } - - [TestCase((EmptyEnum)0 , ExpectedExceptionType.EmptyEnum)] - [TestCase(SingleValueEnum.A , ExpectedExceptionType.None)] - [TestCase(ContinuousEnum.A , ExpectedExceptionType.None)] - [TestCase(ContinuousEnum_U8.A , ExpectedExceptionType.UnhandledBackingType)] - [TestCase(ContinuousEnum_I16.A , ExpectedExceptionType.UnhandledBackingType)] - [TestCase(ContinuousEnum_U32.A , ExpectedExceptionType.UnhandledBackingType)] - [TestCase(ContinuousEnum_I64.A , ExpectedExceptionType.UnhandledBackingType)] - [TestCase(ContinuousEnumShiftedUp.A , ExpectedExceptionType.NonZeroEnumMinimumValue)] - [TestCase(ContinuousEnumShiftedDown.A , ExpectedExceptionType.NonZeroEnumMinimumValue)] - [TestCase(EnumWithDuplicates.A , ExpectedExceptionType.None)] - [TestCase(EnumWithHoles.A , ExpectedExceptionType.DiscontinuousEnum)] - [TestCase(EnumWithHolesAndDuplicates.A , ExpectedExceptionType.DiscontinuousEnum)] - [TestCase(EnumWithFlags.A , ExpectedExceptionType.DiscontinuousEnum)] - public void TestEnumContinuity(TEnum _, ExpectedExceptionType expectedExceptionType) - where TEnum : unmanaged, Enum - { - switch (expectedExceptionType) - { - case ExpectedExceptionType.UnhandledBackingType: - { - Assert.Throws>(() => - { - EnumContinuity.ValidateEnumForEnumMap(); - }); - break; - } - case ExpectedExceptionType.EmptyEnum: - { - Assert.Throws>(() => - { - EnumContinuity.ValidateEnumForEnumMap(); - }); - break; - } - case ExpectedExceptionType.NonZeroEnumMinimumValue: - { - Assert.Throws>(() => - { - EnumContinuity.ValidateEnumForEnumMap(); - }); - break; - } - case ExpectedExceptionType.DiscontinuousEnum: - { - Assert.Throws>(() => - { - EnumContinuity.ValidateEnumForEnumMap(); - }); - break; - } - default: - Assert.DoesNotThrow(() => - { - EnumContinuity.ValidateEnumForEnumMap(); - }); - break; - } - } - - [Test] - public void EnumMapUsageTest() - { - var map = new EnumMap(); - Assert.Zero(map[A]); - Assert.Zero(map[B]); - Assert.Zero(map[C]); - Assert.Zero(map[D]); - Assert.Zero(map[E]); - Assert.Zero(map[F]); - Assert.Zero(map[G]); - Assert.Zero(map[H]); - - map[A] = 1; - map[B] = 2; - map[C] = map[A] + map[B]; - map[D] = map[B] + map[C]; - map[E] = map[C] + map[D]; - map[F] = map[D] + map[E]; - map[G] = map[E] + map[F]; - map[H] = map[F] + map[G]; - - Assert.AreEqual( 1, map[A]); - Assert.AreEqual( 2, map[B]); - Assert.AreEqual( 3, map[C]); - Assert.AreEqual( 5, map[D]); - Assert.AreEqual( 8, map[E]); - Assert.AreEqual(13, map[F]); - Assert.AreEqual(21, map[G]); - Assert.AreEqual(34, map[H]); - } - - [Test] - public void CollectionLiteralTest() - { - var map = new EnumMap - { - { A, 1 }, - { B, 2 }, - { C, 3 }, - { D, 5 }, - { E, 8 }, - { F, 13 }, - { G, 21 }, - { H, 34 }, - }; - Assert.AreEqual( 1, map[A]); - Assert.AreEqual( 2, map[B]); - Assert.AreEqual( 3, map[C]); - Assert.AreEqual( 5, map[D]); - Assert.AreEqual( 8, map[E]); - Assert.AreEqual(13, map[F]); - Assert.AreEqual(21, map[G]); - Assert.AreEqual(34, map[H]); - } - - [Test] - public void IteratorTest() - { - var map = new EnumMap(); - map[A] = 1; - map[B] = 2; - map[C] = 3; - map[D] = 5; - map[E] = 8; - map[F] = 13; - map[G] = 21; - map[H] = 34; - Assert.AreEqual( 1, map[A]); - Assert.AreEqual( 2, map[B]); - Assert.AreEqual( 3, map[C]); - Assert.AreEqual( 5, map[D]); - Assert.AreEqual( 8, map[E]); - Assert.AreEqual(13, map[F]); - Assert.AreEqual(21, map[G]); - Assert.AreEqual(34, map[H]); - CollectionAssert.AreEquivalent(new KeyValuePair[] - { - // In C# >= 9 this can be much more concise with Target-typed new, - // but until we drop support for Unity 2020.3 this must remain this way - new KeyValuePair(A, 1), - new KeyValuePair(B, 2), - new KeyValuePair(C, 3), - new KeyValuePair(D, 5), - new KeyValuePair(E, 8), - new KeyValuePair(F, 13), - new KeyValuePair(G, 21), - new KeyValuePair(H, 34), - }, map); - } - } -} \ No newline at end of file diff --git a/Common/Tests/Data/EnumMapTests.cs.meta b/Common/Tests/Data/EnumMapTests.cs.meta deleted file mode 100644 index 4eab5e0..0000000 --- a/Common/Tests/Data/EnumMapTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5ed4e67da3ea4c7c944da7b6b67b45bb -timeCreated: 1665609867 \ No newline at end of file diff --git a/Common/Tests/Data/ExponentialMovingAverageTests.cs b/Common/Tests/Data/ExponentialMovingAverageTests.cs deleted file mode 100644 index 4c86fa5..0000000 --- a/Common/Tests/Data/ExponentialMovingAverageTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.Common.Tests -{ - [Obsolete("This test class has been marked obsolete because it was not intended to be a public API", false)] - public class ExponentialMovingAverageTests - { - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void InitializedCorrectlyWithOneArgument(float parameter = 0f) { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void InitializedCorrectlyWithTwoArguments(float parameter = 0f, float initialValue = 0f) { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void ValueRemainsConstantWithConstantInput() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void ValueDecaysFromInitialValueWithZeroInput() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void ValueRisesToMeetConstantInputWhenStartingFromZero() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void ValueIsComputedCorrectlyInResponseToVaryingInput() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void ApproximatingSimpleMovingAverageIsCorrect(int sampleCount) {} - } -} \ No newline at end of file diff --git a/Common/Tests/Data/ExponentialMovingAverageTestsTmp.cs b/Common/Tests/Data/ExponentialMovingAverageTestsTmp.cs deleted file mode 100644 index 339c6d9..0000000 --- a/Common/Tests/Data/ExponentialMovingAverageTestsTmp.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.Common.Tests -{ - class ExponentialMovingAverageTestsTmp - { - - [TestCase(0.3f)] - [TestCase(0.5f)] - [TestCase(0.7f)] - [TestCase(0.3f)] - public void InitializedCorrectlyWithOneArgument(float parameter) - { - var ema = new ExponentialMovingAverage(parameter); - Assert.AreEqual(ema.Parameter, parameter); - Assert.AreEqual(ema.Value, 0f); - } - - [TestCase(0.5f)] - [TestCase(0.7f, -342.74f)] - [TestCase(0.3f, 49.63f)] - public void InitializedCorrectlyWithTwoArguments( - float parameter, - float initialValue = 0f) - { - var ema = new ExponentialMovingAverage(parameter, initialValue); - Assert.AreEqual(ema.Parameter, parameter); - Assert.AreEqual(ema.Value, initialValue); - } - - [Test] - public void ValueRemainsConstantWithConstantInput() - { - var ema = new ExponentialMovingAverage(0.5f, 4.8f); - Assert.AreEqual(4.8f, ema.Value); - - ema.AddSample(4.8f); - Assert.AreEqual(4.8f, ema.Value); - - ema.AddSample(4.8f); - Assert.AreEqual(4.8f, ema.Value); - - ema.AddSample(4.8f); - Assert.AreEqual(4.8f, ema.Value); - - ema.AddSample(4.8f); - Assert.AreEqual(4.8f, ema.Value); - } - - [Test] - public void ValueDecaysFromInitialValueWithZeroInput() - { - var ema = new ExponentialMovingAverage(0.5f, 1f); - Assert.AreEqual(1f, ema.Value, delta: 1e-7f); - - ema.AddSample(0f); - Assert.AreEqual(0.5f, ema.Value, delta: 1e-7f); - - ema.AddSample(0f); - Assert.AreEqual(0.25f, ema.Value, delta: 1e-7f); - - ema.AddSample(0f); - Assert.AreEqual(0.125f, ema.Value, delta: 1e-7f); - - ema.AddSample(0f); - Assert.AreEqual(0.0625f, ema.Value, delta: 1e-7f); - } - - [Test] - public void ValueRisesToMeetConstantInputWhenStartingFromZero() - { - var ema = new ExponentialMovingAverage(0.5f, 0f); - Assert.AreEqual(0f, ema.Value); - - ema.AddSample(16f); - Assert.AreEqual(8f, ema.Value); - - ema.AddSample(16f); - Assert.AreEqual(12f, ema.Value); - - ema.AddSample(16f); - Assert.AreEqual(14f, ema.Value); - - ema.AddSample(16f); - Assert.AreEqual(15f, ema.Value); - } - - [Test] - public void ValueIsComputedCorrectlyInResponseToVaryingInput() - { - var ema = new ExponentialMovingAverage(parameter: 0.5f, value: 0f); - Assert.AreEqual(0.5f, ema.Parameter); - Assert.AreEqual(0f, ema.Value); - - // With a parameter of 0.5, the moving average after each new value is the - // average of the most recent value and the previous moving average - - ema.AddSample(10f); - Assert.AreEqual(5f, ema.Value); - - ema.AddSample(13f); - Assert.AreEqual(9f, ema.Value); - - ema.AddSample(9f); - Assert.AreEqual(9f, ema.Value); - - ema.AddSample(11f); - Assert.AreEqual(10f, ema.Value); - - ema.AddSample(7f); - Assert.AreEqual(8.5f, ema.Value); - - ema.AddSample(7f); - Assert.AreEqual(7.75f, ema.Value); - - ema.AddSample(8.25f); - Assert.AreEqual(8f, ema.Value); - - ema.AddSample(4f); - Assert.AreEqual(6f, ema.Value); - - ema.AddSample(4f); - Assert.AreEqual(5f, ema.Value); - - ema.AddSample(4f); - Assert.AreEqual(4.5f, ema.Value); - - ema.AddSample(2.5f); - Assert.AreEqual(3.5f, ema.Value); - } - - /// Just some tests that the formula in ApproximatingSimpleMovingAverage are working correctly. - /// For more information, see that functions doc-comment and attached link - [TestCase(1)] - [TestCase(2)] - [TestCase(3)] - [TestCase(4)] - [TestCase(5)] - [TestCase(6)] - [TestCase(7)] - [TestCase(8)] - [TestCase(87)] - [TestCase(128)] - [TestCase(256)] - [TestCase(4967)] - public void ApproximatingSimpleMovingAverageIsCorrect(int sampleCount) - { - var ema = ExponentialMovingAverage.ApproximatingSimpleMovingAverage(sampleCount); - Assert.AreEqual(2f / (sampleCount + 1), ema.Parameter); - } - } -} \ No newline at end of file diff --git a/Common/Tests/Data/ExponentialMovingAverageTestsTmp.cs.meta b/Common/Tests/Data/ExponentialMovingAverageTestsTmp.cs.meta deleted file mode 100644 index 97f3414..0000000 --- a/Common/Tests/Data/ExponentialMovingAverageTestsTmp.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 33e9a06369e041a0a13b0108dfbe5d19 -timeCreated: 1664202141 \ No newline at end of file diff --git a/Common/Tests/Data/RingBufferExtensionsTests.cs b/Common/Tests/Data/RingBufferExtensionsTests.cs deleted file mode 100644 index bbf08bf..0000000 --- a/Common/Tests/Data/RingBufferExtensionsTests.cs +++ /dev/null @@ -1,89 +0,0 @@ -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.Common.Tests -{ - internal class RingBufferExtensionTests - { - [Test] - public void EmptyRingBufferSumIsZero() - { - var ring = new RingBuffer(13); - Assert.AreEqual(0, ring.Sum()); - } - - [Test] - public void EmptyRingBufferAverageIsNan() - { - var ring = new RingBuffer(13); - Assert.IsTrue(float.IsNaN(ring.Average())); - } - - [Test] - public void RingBufferSumIsCorrect() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - Assert.AreEqual(20, ring.Sum()); - } - - [Test] - public void RingBufferAverageIsCorrect() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - Assert.AreEqual(20f / 6f, ring.Average()); - } - - [Test] - public void RingBufferSumIsCorrectAfterOverflow() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - ring.PushBack(13); - ring.PushBack(21); - ring.PushBack(34); - ring.PushBack(55); - ring.PushBack(89); - - Assert.AreEqual( - 5 + 8 + 13 + 21 + 34 + 55 + 89, - ring.Sum()); - } - - [Test] - public void RingBufferAverageIsCorrectAfterOverflow() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - ring.PushBack(13); - ring.PushBack(21); - ring.PushBack(34); - ring.PushBack(55); - ring.PushBack(89); - - Assert.AreEqual( - (float)(5 + 8 + 13 + 21 + 34 + 55 + 89) / 7, - ring.Average()); - } - } -} \ No newline at end of file diff --git a/Common/Tests/Data/RingBufferTests.cs b/Common/Tests/Data/RingBufferTests.cs deleted file mode 100644 index a68577d..0000000 --- a/Common/Tests/Data/RingBufferTests.cs +++ /dev/null @@ -1,549 +0,0 @@ -// Index and ^i syntax are not available in Unity < 2021.2 -// so for simplicity's sake, only include these tests in higher versions -#if UNITY_2021_2_OR_NEWER - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.Common.Tests -{ - internal class RingBufferTests - { - static void NoOp(T t) - { - // No-op function: turns expression into statements to test exceptions - } - static void AssertIndexOutOfRange(RingBuffer ring, int i) - { - Assert.Throws(() => NoOp(ring[i])); - } - static void AssertIndexOutOfRange(RingBuffer ring, Index i) - { - Assert.Throws(() => NoOp(ring[i])); - } - - [Test] - public void RingBufferConstructedWithNegativeCapacityThrowsArgumentException() - { - Assert.Throws(() => NoOp(new RingBuffer(-1))); - Assert.Throws(() => NoOp(new RingBuffer(-7))); - } - - [Test] - public void RingBufferConstructedWithZeroCapacityDoesNotStoreValues() - { - var ring = new RingBuffer(0); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - AssertIndexOutOfRange(ring, 1); - AssertIndexOutOfRange(ring, -1); - - ring.PushBack(0); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - AssertIndexOutOfRange(ring, 1); - AssertIndexOutOfRange(ring, -1); - - ring.PushBack(7); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - AssertIndexOutOfRange(ring, 1); - AssertIndexOutOfRange(ring, -1); - - ring.PushBack(16); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - AssertIndexOutOfRange(ring, 1); - AssertIndexOutOfRange(ring, -1); - } - - [Test] - public void EmptyRingBufferThrowsIndexOutOfRangeExceptionsWhenIndexed() - { - var ring = new RingBuffer(13); - AssertIndexOutOfRange(ring, 0); - AssertIndexOutOfRange(ring, 1); - AssertIndexOutOfRange(ring, 3); - AssertIndexOutOfRange(ring, -7); - AssertIndexOutOfRange(ring, ^3); - } - - [Test] - public void RingBufferCanPushValues() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - } - - [Test] - public void RingBufferReportsLengthCorrectly() - { - var ring = new RingBuffer(7); - Assert.AreEqual(ring.Length, 0); - ring.PushBack(1); - Assert.AreEqual(ring.Length, 1); - ring.PushBack(1); - Assert.AreEqual(ring.Length, 2); - ring.PushBack(2); - Assert.AreEqual(ring.Length, 3); - ring.PushBack(3); - Assert.AreEqual(ring.Length, 4); - ring.PushBack(5); - Assert.AreEqual(ring.Length, 5); - ring.PushBack(8); - Assert.AreEqual(ring.Length, 6); - } - - [Test] - public void RingBufferCanPushAndRead() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - AssertIndexOutOfRange(ring, -1); - Assert.AreEqual(ring[0], 1); - Assert.AreEqual(ring[1], 1); - Assert.AreEqual(ring[2], 2); - Assert.AreEqual(ring[3], 3); - Assert.AreEqual(ring[4], 5); - Assert.AreEqual(ring[5], 8); - AssertIndexOutOfRange(ring, 6); - Assert.AreEqual(ring[^1], 8); - Assert.AreEqual(ring[^2], 5); - Assert.AreEqual(ring[^3], 3); - Assert.AreEqual(ring[^4], 2); - Assert.AreEqual(ring[^5], 1); - Assert.AreEqual(ring[^6], 1); - AssertIndexOutOfRange(ring, ^0); - AssertIndexOutOfRange(ring, ^7); - } - - [Test] - public void RingBufferCanPushAndWriteAndRead() - { - var ring = new RingBuffer(7); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring[0] = 1; - ring[^5] = 1; - ring[2] = 2; - ring[^3] = 3; - ring[4] = 5; - ring[^1] = 8; - AssertIndexOutOfRange(ring, -1); - Assert.AreEqual(ring[0], 1); - Assert.AreEqual(ring[1], 1); - Assert.AreEqual(ring[2], 2); - Assert.AreEqual(ring[3], 3); - Assert.AreEqual(ring[4], 5); - Assert.AreEqual(ring[5], 8); - AssertIndexOutOfRange(ring, 6); - Assert.AreEqual(ring[^1], 8); - Assert.AreEqual(ring[^2], 5); - Assert.AreEqual(ring[^3], 3); - Assert.AreEqual(ring[^4], 2); - Assert.AreEqual(ring[^5], 1); - Assert.AreEqual(ring[^6], 1); - AssertIndexOutOfRange(ring, ^0); - AssertIndexOutOfRange(ring, ^7); - } - - [Test] - public void RingBufferAtCapacityCanBePushed() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - ring.PushBack(13); - ring.PushBack(21); - ring.PushBack(34); - ring.PushBack(55); - ring.PushBack(89); - } - - [Test] - public void RingBufferAtCapacityReportsLengthCorrectly() - { - var ring = new RingBuffer(7); - Assert.AreEqual(ring.Length, 0); - ring.PushBack(1); - Assert.AreEqual(ring.Length, 1); - ring.PushBack(1); - Assert.AreEqual(ring.Length, 2); - ring.PushBack(2); - Assert.AreEqual(ring.Length, 3); - ring.PushBack(3); - Assert.AreEqual(ring.Length, 4); - ring.PushBack(5); - Assert.AreEqual(ring.Length, 5); - ring.PushBack(8); - Assert.AreEqual(ring.Length, 6); - ring.PushBack(13); - Assert.AreEqual(ring.Length, 7); - ring.PushBack(21); - Assert.AreEqual(ring.Length, 7); - ring.PushBack(34); - Assert.AreEqual(ring.Length, 7); - ring.PushBack(55); - Assert.AreEqual(ring.Length, 7); - ring.PushBack(89); - Assert.AreEqual(ring.Length, 7); - } - - [Test] - public void RingBufferAtCapacityCanPushAndRead() - { - var ring = new RingBuffer(7); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - ring.PushBack(13); - ring.PushBack(21); - ring.PushBack(34); - ring.PushBack(55); - ring.PushBack(89); - AssertIndexOutOfRange(ring, -1); - Assert.AreEqual(ring[0], 5); - Assert.AreEqual(ring[1], 8); - Assert.AreEqual(ring[2], 13); - Assert.AreEqual(ring[3], 21); - Assert.AreEqual(ring[4], 34); - Assert.AreEqual(ring[5], 55); - Assert.AreEqual(ring[6], 89); - AssertIndexOutOfRange(ring, 7); - Assert.AreEqual(ring[^1], 89); - Assert.AreEqual(ring[^2], 55); - Assert.AreEqual(ring[^3], 34); - Assert.AreEqual(ring[^4], 21); - Assert.AreEqual(ring[^5], 13); - Assert.AreEqual(ring[^6], 8); - Assert.AreEqual(ring[^7], 5); - AssertIndexOutOfRange(ring, ^0); - AssertIndexOutOfRange(ring, ^8); - } - - [Test] - public void RingBufferAtCapacityCanPushAndWriteAndRead() - { - var ring = new RingBuffer(7); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - ring.PushBack(0); - - ring[0] = 5; - ring[^6] = 8; - ring[2] = 13; - ring[^4] = 21; - ring[4] = 34; - ring[^2] = 55; - ring[6] = 89; - - AssertIndexOutOfRange(ring, -1); - Assert.AreEqual(ring[0], 5); - Assert.AreEqual(ring[1], 8); - Assert.AreEqual(ring[2], 13); - Assert.AreEqual(ring[3], 21); - Assert.AreEqual(ring[4], 34); - Assert.AreEqual(ring[5], 55); - Assert.AreEqual(ring[6], 89); - AssertIndexOutOfRange(ring, 7); - Assert.AreEqual(ring[^1], 89); - Assert.AreEqual(ring[^2], 55); - Assert.AreEqual(ring[^3], 34); - Assert.AreEqual(ring[^4], 21); - Assert.AreEqual(ring[^5], 13); - Assert.AreEqual(ring[^6], 8); - Assert.AreEqual(ring[^7], 5); - AssertIndexOutOfRange(ring, ^0); - AssertIndexOutOfRange(ring, ^8); - } - - [Test] - public void CanIterateOverZeroCapacityRingBuffer() - { - var ring = new RingBuffer(0); - ring.PushBack(0); - ring.PushBack(7); - Assert.IsEmpty(ring); - } - - [Test] - public void CanIterateOverEmptyRingBuffer() - { - var ring = new RingBuffer(8); - Assert.IsEmpty(ring); - } - - [Test] - public void CanIterateOverRingBuffer() - { - var ring = new RingBuffer(8); - ring.PushBack(1); - Assert.AreEqual( - new List { 1 }, - ring.ToList()); - - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - Assert.AreEqual( - new List { 1, 1, 2, 3, 5 }, - ring.ToList()); - } - - [Test] - public void CanIterateOverRingBufferAtCapacity() - { - var ring = new RingBuffer(8); - ring.PushBack(1); - ring.PushBack(1); - ring.PushBack(2); - ring.PushBack(3); - ring.PushBack(5); - ring.PushBack(8); - ring.PushBack(13); - ring.PushBack(21); - ring.PushBack(34); - ring.PushBack(55); - ring.PushBack(89); - Assert.AreEqual( - new List { 3, 5, 8, 13, 21, 34, 55, 89 }, - ring.ToList()); - } - - [Test] - public void RingBufferCapacityCanBeChanged() - { - var ring = new RingBuffer(0); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - - ring.Capacity = 4; - Assert.AreEqual(ring.Capacity, 4); - Assert.AreEqual(ring.Length, 0); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 1); - ring.PushBack(2); - Assert.AreEqual(ring.Length, 2); - ring.PushBack(3); - Assert.AreEqual(ring.Length, 3); - ring.PushBack(5); - Assert.AreEqual(ring.Length, 4); - ring.PushBack(8); - Assert.AreEqual(ring.Length, 4); - - ring.Capacity = 2; - Assert.AreEqual(ring.Capacity, 2); - } - - [Test] - public void RingBufferHasNoValuesAfterCapacitySetToZero() - { - var ring = new RingBuffer(0); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - - ring.Capacity = 4; - Assert.AreEqual(ring.Capacity, 4); - Assert.AreEqual(ring.Length, 0); - Assert.AreEqual(new List { }, ring.ToList()); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 1); - ring.PushBack(2); - Assert.AreEqual(ring.Length, 2); - ring.PushBack(3); - Assert.AreEqual(ring.Length, 3); - ring.PushBack(5); - Assert.AreEqual(ring.Length, 4); - ring.PushBack(8); - Assert.AreEqual(ring.Length, 4); - Assert.AreEqual(new List { 2, 3, 5, 8 }, ring.ToList()); - - ring.Capacity = 0; - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - Assert.AreEqual(new List { }, ring.ToList()); - } - - [Test] - public void RingBufferPreservesValuesWhenCapacityIsIncreased() - { - var ring = new RingBuffer(0); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - - ring.Capacity = 4; - Assert.AreEqual(ring.Capacity, 4); - Assert.AreEqual(ring.Length, 0); - Assert.AreEqual(new List { }, ring.ToList()); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 1); - ring.PushBack(2); - Assert.AreEqual(ring.Length, 2); - ring.PushBack(3); - Assert.AreEqual(ring.Length, 3); - ring.PushBack(5); - Assert.AreEqual(ring.Length, 4); - ring.PushBack(8); - Assert.AreEqual(ring.Length, 4); - Assert.AreEqual( - new List { 2, 3, 5, 8 }, - ring.ToList()); - - ring.Capacity = 8; - Assert.AreEqual(ring.Capacity, 8); - Assert.AreEqual(ring.Length, 4); - Assert.AreEqual( - new List { 2, 3, 5, 8 }, - ring.ToList()); - - ring.PushBack(13); - Assert.AreEqual(ring.Length, 5); - ring.PushBack(21); - Assert.AreEqual(ring.Length, 6); - ring.PushBack(34); - Assert.AreEqual(ring.Length, 7); - ring.PushBack(55); - Assert.AreEqual(ring.Length, 8); - ring.PushBack(89); - Assert.AreEqual(ring.Length, 8); - Assert.AreEqual( - new List { 3, 5, 8, 13, 21, 34, 55, 89 }, - ring.ToList()); - } - - [Test] - public void RingBufferPreservesValuesWhenCapacityIsDecreased() - { - var ring = new RingBuffer(0); - Assert.AreEqual(ring.Capacity, 0); - Assert.AreEqual(ring.Length, 0); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 0); - AssertIndexOutOfRange(ring, 0); - - ring.Capacity = 6; - Assert.AreEqual(ring.Capacity, 6); - Assert.AreEqual(ring.Length, 0); - Assert.AreEqual(new List { }, ring.ToList()); - - ring.PushBack(1); - Assert.AreEqual(ring.Length, 1); - ring.PushBack(2); - Assert.AreEqual(ring.Length, 2); - ring.PushBack(3); - Assert.AreEqual(ring.Length, 3); - ring.PushBack(5); - Assert.AreEqual(ring.Length, 4); - ring.PushBack(8); - Assert.AreEqual(ring.Length, 5); - ring.PushBack(13); - Assert.AreEqual(ring.Length, 6); - ring.PushBack(21); - Assert.AreEqual(ring.Length, 6); - Assert.AreEqual( - new List { 2, 3, 5, 8, 13, 21 }, - ring.ToList()); - - ring.Capacity = 4; - Assert.AreEqual(ring.Capacity, 4); - Assert.AreEqual(ring.Length, 4); - Assert.AreEqual(new List { 5, 8, 13, 21 }, ring.ToList()); - - ring.PushBack(34); - Assert.AreEqual(ring.Capacity, 4); - Assert.AreEqual(ring.Length, 4); - Assert.AreEqual(new List { 8, 13, 21, 34 }, ring.ToList()); - } - - [Test] - public void RingBufferMostAndLeastRecentAreCorrect() - { - var ring = new RingBuffer(4); - - ring.PushBack(7); // => 7 - Assert.AreEqual(7, ring.MostRecent); - Assert.AreEqual(7, ring.LeastRecent); - - ring.PushBack(3); // => 3 7 - Assert.AreEqual(3, ring.MostRecent); - Assert.AreEqual(7, ring.LeastRecent); - - ring.PushBack(5); // => 5 3 7 - Assert.AreEqual(5, ring.MostRecent); - Assert.AreEqual(7, ring.LeastRecent); - - ring.PushBack(11); // => 11 5 3 7 - Assert.AreEqual(11, ring.MostRecent); - Assert.AreEqual(7, ring.LeastRecent); - - ring.PushBack(14); // => 14 11 5 3 - Assert.AreEqual(14, ring.MostRecent); - Assert.AreEqual(3, ring.LeastRecent); - - ring.PushBack(6); // => 6 14 11 5 - Assert.AreEqual(6, ring.MostRecent); - Assert.AreEqual(5, ring.LeastRecent); - - ring.PushBack(12); // => 12 6 14 11 - Assert.AreEqual(12, ring.MostRecent); - Assert.AreEqual(11, ring.LeastRecent); - - ring.PushBack(4); // => 4 12 6 14 - Assert.AreEqual(4, ring.MostRecent); - Assert.AreEqual(14, ring.LeastRecent); - } - } -} -#endif diff --git a/Common/Tests/Data/RingBufferTests.cs.meta b/Common/Tests/Data/RingBufferTests.cs.meta deleted file mode 100644 index e682302..0000000 --- a/Common/Tests/Data/RingBufferTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b6be88a387d7e41a0830fea8d41a9799 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Common/Tests/Unity.Multiplayer.Tools.Common.Tests.asmdef b/Common/Tests/Unity.Multiplayer.Tools.Common.Tests.asmdef deleted file mode 100644 index 61a3309..0000000 --- a/Common/Tests/Unity.Multiplayer.Tools.Common.Tests.asmdef +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "Unity.Multiplayer.Tools.Common.Tests", - "rootNamespace": "Unity.Multiplayer.Tools.Common.Tests", - "references": [ - "UnityEngine.TestRunner", - "UnityEditor.TestRunner", - "Unity.Multiplayer.Tools.Common" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "autoReferenced": false, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} \ No newline at end of file diff --git a/DependencyInjection.meta b/DependencyInjection.meta new file mode 100644 index 0000000..4532027 --- /dev/null +++ b/DependencyInjection.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a2444d679d34dba8a61de8c1fad8e37 +timeCreated: 1678800253 \ No newline at end of file diff --git a/DependencyInjection/AssemblyInfo.cs b/DependencyInjection/AssemblyInfo.cs new file mode 100644 index 0000000..361cce3 --- /dev/null +++ b/DependencyInjection/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.DependencyInjection.UIElements")] + +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.UI")] diff --git a/DependencyInjection/AssemblyInfo.cs.meta b/DependencyInjection/AssemblyInfo.cs.meta new file mode 100644 index 0000000..5b8d3a9 --- /dev/null +++ b/DependencyInjection/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 77733246a5a2456c97d6d2da5998a222 +timeCreated: 1678815279 \ No newline at end of file diff --git a/DependencyInjection/Container.cs b/DependencyInjection/Container.cs new file mode 100644 index 0000000..6b786ba --- /dev/null +++ b/DependencyInjection/Container.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +namespace Unity.Multiplayer.Tools.DependencyInjection +{ + class Container + { + readonly List m_DependencyDefinitions = new List(); + + internal IReadOnlyCollection DependencyDefinitions => m_DependencyDefinitions; + + public Container AddTransient() + where TImplementation : class + { + return AddTransient(); + } + + public Container AddTransient() + where TImplementation : class, TDependency + { + m_DependencyDefinitions.Add(DependencyDefinition.Transient()); + + return this; + } + + public Container AddTransient(Func instanceFactory) + where TImplementation : class + { + return AddTransient(instanceFactory); + } + + public Container AddTransient(Func instanceFactory) + where TImplementation : class, TDependency + { + m_DependencyDefinitions.Add(DependencyDefinition.Transient(instanceFactory)); + + return this; + } + + public Container AddSingleton() + where TImplementation : class + { + return AddSingleton(); + } + + public Container AddSingleton() + where TImplementation : class, TDependency + { + m_DependencyDefinitions.Add(DependencyDefinition.Singleton()); + + return this; + } + + public Container AddSingleton(TImplementation instance) + where TImplementation : class + { + return AddSingleton(instance); + } + + public Container AddSingleton(TImplementation instance) + where TImplementation : class, TDependency + { + m_DependencyDefinitions.Add(DependencyDefinition.Singleton(instance)); + + return this; + } + } +} diff --git a/DependencyInjection/Container.cs.meta b/DependencyInjection/Container.cs.meta new file mode 100644 index 0000000..ffb3dd1 --- /dev/null +++ b/DependencyInjection/Container.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e2dbdf304da64b72b434dd7ba0c43e6d +timeCreated: 1678801215 \ No newline at end of file diff --git a/DependencyInjection/Context.cs b/DependencyInjection/Context.cs new file mode 100644 index 0000000..50dd68f --- /dev/null +++ b/DependencyInjection/Context.cs @@ -0,0 +1,9 @@ +namespace Unity.Multiplayer.Tools.DependencyInjection +{ + class Context + { + public static Container Current { get; } = new Container(); + + public static IDependencyResolver Resolver { get; } = new DependencyResolver(Current); + } +} diff --git a/DependencyInjection/Context.cs.meta b/DependencyInjection/Context.cs.meta new file mode 100644 index 0000000..9756f39 --- /dev/null +++ b/DependencyInjection/Context.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 87df61c1aa7a45d7be9c2456354e7c4a +timeCreated: 1678814882 \ No newline at end of file diff --git a/DependencyInjection/DependencyDefinition.cs b/DependencyInjection/DependencyDefinition.cs new file mode 100644 index 0000000..02ac0a9 --- /dev/null +++ b/DependencyInjection/DependencyDefinition.cs @@ -0,0 +1,73 @@ +using System; +using JetBrains.Annotations; + +namespace Unity.Multiplayer.Tools.DependencyInjection +{ + class DependencyDefinition + { + DependencyDefinition( + Type dependencyType, + Type implementationType, + Lifetime lifetime, + object implementationInstance = null, + Func implementationFactory = null) + { + DependencyType = dependencyType; + ImplementationType = implementationType; + Lifetime = lifetime; + ImplementationInstance = implementationInstance; + ImplementationFactory = implementationFactory; + } + + public Type DependencyType { get; } + + public Type ImplementationType { get; } + + public Lifetime Lifetime { get; } + + [CanBeNull] + public object ImplementationInstance { get; } + + [CanBeNull] + public Func ImplementationFactory { get; } + + public static DependencyDefinition Transient() + where TImplementation : class, TDependency + { + return Define(typeof(TDependency), typeof(TImplementation), Lifetime.Transient); + } + + public static DependencyDefinition Transient(Func implementationFactory) + where TImplementation : class, TDependency + { + return Define(typeof(TDependency), typeof(TImplementation), Lifetime.Transient, implementationFactory); + } + + public static DependencyDefinition Singleton() + where TImplementation : class, TDependency + { + return Define(typeof(TDependency), typeof(TImplementation), Lifetime.Singleton); + } + + public static DependencyDefinition Singleton(TImplementation implementationInstance) + where TImplementation : class, TDependency + { + return Define(typeof(TDependency), typeof(TImplementation), Lifetime.Singleton, implementationInstance); + } + + static DependencyDefinition Define(Type dependencyType, Type implementationType, Lifetime lifetime) + { + return new DependencyDefinition(dependencyType, implementationType, lifetime); + } + + static DependencyDefinition Define(Type dependencyType, Type implementationType, Lifetime lifetime, object implementationInstance) + { + return new DependencyDefinition(dependencyType, implementationType, lifetime, implementationInstance); + } + + static DependencyDefinition Define(Type dependencyType, Type implementationType, Lifetime lifetime, Func implementationFactory) + { + return new DependencyDefinition(dependencyType, implementationType, lifetime, implementationFactory); + } + } +} diff --git a/DependencyInjection/DependencyDefinition.cs.meta b/DependencyInjection/DependencyDefinition.cs.meta new file mode 100644 index 0000000..685a3a2 --- /dev/null +++ b/DependencyInjection/DependencyDefinition.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b867d996a3c340128f30a4c123e907c4 +timeCreated: 1678800530 \ No newline at end of file diff --git a/DependencyInjection/DependencyResolver.cs b/DependencyInjection/DependencyResolver.cs new file mode 100644 index 0000000..c02c388 --- /dev/null +++ b/DependencyInjection/DependencyResolver.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Unity.Multiplayer.Tools.DependencyInjection +{ + interface IDependencyResolver + { + object Resolve(Type type); + } + + class DependencyResolver : IDependencyResolver + { + readonly Container m_Container; + readonly Dictionary m_SingletonInstances = new Dictionary(); + + public DependencyResolver(Container container) + { + m_Container = container; + } + + public object Resolve(Type type) + { + var resolved = TryResolve(type, out var dependency); + if (!resolved) + { + throw new MissingDependencyException(type); + } + + return dependency; + } + + bool TryResolve(Type type, out object dependency) + { + dependency = default; + + var singletonDefinitionCandidate = m_Container.DependencyDefinitions + .LastOrDefault(x => x.DependencyType == type && + x.Lifetime == Lifetime.Singleton); + if (singletonDefinitionCandidate != default) + { + if (m_SingletonInstances.TryGetValue(type, out var singletonInstance)) + { + dependency = singletonInstance; + } + else + { + dependency = Instantiate(singletonDefinitionCandidate); + m_SingletonInstances[type] = dependency; + } + + return true; + } + + var transientDefinitionCandidate = m_Container.DependencyDefinitions + .LastOrDefault(x => x.DependencyType == type && + x.Lifetime == Lifetime.Transient); + if (transientDefinitionCandidate != default) + { + dependency = Instantiate(transientDefinitionCandidate); + return true; + } + + return false; + } + + static object Instantiate(DependencyDefinition definition) + { + if (definition.ImplementationInstance != null) + { + return definition.ImplementationInstance; + } + + if (definition.ImplementationFactory != null) + { + return definition.ImplementationFactory.Invoke(); + } + + var parameterlessConstructor = definition.ImplementationType.GetConstructor(Type.EmptyTypes); + if (parameterlessConstructor != null) + { + return parameterlessConstructor.Invoke(Array.Empty()); + } + + throw new DependencyInstantiationException(definition.ImplementationType); + } + + class MissingDependencyException : Exception + { + public MissingDependencyException(Type type) + : base($"Could not resolve dependency for type '{type.AssemblyQualifiedName}'.") + { + } + } + + class DependencyInstantiationException : Exception + { + public DependencyInstantiationException(Type type) + : base($"Could not instantiate dependency of type '{type.AssemblyQualifiedName}'.") + { + + } + } + } +} diff --git a/DependencyInjection/DependencyResolver.cs.meta b/DependencyInjection/DependencyResolver.cs.meta new file mode 100644 index 0000000..148eff4 --- /dev/null +++ b/DependencyInjection/DependencyResolver.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ebffdddb4275435c8ec512fde221b9d6 +timeCreated: 1678801516 \ No newline at end of file diff --git a/DependencyInjection/InjectAttribute.cs b/DependencyInjection/InjectAttribute.cs new file mode 100644 index 0000000..5a21784 --- /dev/null +++ b/DependencyInjection/InjectAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Unity.Multiplayer.Tools.DependencyInjection +{ + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] + sealed class InjectAttribute : Attribute + { + } +} diff --git a/DependencyInjection/InjectAttribute.cs.meta b/DependencyInjection/InjectAttribute.cs.meta new file mode 100644 index 0000000..bc89828 --- /dev/null +++ b/DependencyInjection/InjectAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a92a4ebc417e404dbbad7180e8fd3033 +timeCreated: 1678800300 \ No newline at end of file diff --git a/DependencyInjection/Lifetime.cs b/DependencyInjection/Lifetime.cs new file mode 100644 index 0000000..bbef079 --- /dev/null +++ b/DependencyInjection/Lifetime.cs @@ -0,0 +1,8 @@ +namespace Unity.Multiplayer.Tools.DependencyInjection +{ + enum Lifetime + { + Singleton, + Transient + } +} diff --git a/DependencyInjection/Lifetime.cs.meta b/DependencyInjection/Lifetime.cs.meta new file mode 100644 index 0000000..e01fcf7 --- /dev/null +++ b/DependencyInjection/Lifetime.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e8d65940aa954cc0ad8abc4052b2df40 +timeCreated: 1678800645 \ No newline at end of file diff --git a/DependencyInjection/README.md b/DependencyInjection/README.md new file mode 100644 index 0000000..878410b --- /dev/null +++ b/DependencyInjection/README.md @@ -0,0 +1,108 @@ +# Unity.Multiplayer.Tools.DependencyInjection + +## What is dependency injection +Dependency injection is a design pattern used in software development to improve the flexibility and maintainability of code. +In a nutshell, it allows classes to be created and configured externally, and then passed into other classes as dependencies +rather than being directly instantiated within those classes. This enables a separation of concerns, making it easier to manage +dependencies and modify the behavior of an application without needing to modify its core code. + +The goal of this library is to provide a simple and easy-to-use implementation of dependency injection for the +different tools assemblies. + +## Registering dependencies + +Dependency configuration is typically centralized for each different tool in a Context or Module class. Dependencies should be +abstracted behind interfaces, and registration allows you to inform the application which implementation of that interface to use +wherever it is needed. + +For example, given the following components: + +```csharp +public interface IDependency +{ + void DoSomething(); +} +``` + +```csharp +public class Dependency +{ + public void DoSomething() + { + Debug.Log("Hello world!"); + } +} +``` + +```csharp +public class TestDependency +{ + public int NumberOfTimesDoSomethingWasCalled { get; } + + public void DoSomething() + { + DoSomethingWasCalled++; + } +} +``` + +A regular application could register the type `Dependency` to be injected in classes that need `IDependency`, but a different registration +could be used in the testing suite of that application to make sure that `TestDependency` is used instead. + +Dependency registration also allows us to configure the lifetime of each dependencies. For example, we might want to ensure that a +singleton instead is used for a particular dependency. A dependency configured this way is said to have a `Singleton` lifetime. Conversely, we might want a new dependency instantiated every time a consumer +needs it. A dependency configured this way is said to have a `Transient` lifetime. + +The component responsible for holding this information is called the `Container`. Dependency registration can be configured on the +container directly. + +```csharp + // Register MyDependency for classes requiring an instance of IDependency, constrained to a singleton instance + Container.AddSingleton(); + + // Same as singleton above, but we specify which instance to use + Container.AddSingleton(new MyDependency()); + + // Same as singleton above, but classes can use MyDependency directly + Container.AddSingleton(); + Container.AddSingleton(new MyDependency()); +``` + +```csharp + // Register MyDependency for classes requiring an instance of IDependency, constructing a new instance each time + Container.AddTransient(); + + // Same as singleton above, but we specify how to construct that instance + Container.AddTransient(() => new MyDependency()); + + // Same as transient above, but classes can use MyDependency directly + Container.AddTransient(); + Container.AddTransient(new MyDependency()); +``` + +## Resolving dependencies + +Dependencies are resolved using the `DependencyResolver` class. + +Dependencies are resolved with the following priority: +1. If the last dependency definition for the required type is defined as singleton, and an instance has already been constructed, use +that instance +2. If the last dependency definition for the required type is defined as singleton, and an instance has not yet been constructed, +construct that dependency and use that instance +3. If the last dependency definition for the required type is defined as transient, construct that dependency and use that instance +4. If the dependency has no other usable dependency definition, but has a parameterless constructor, construct and use it + +## Assorted notes +- Dependency injection breaks down when the application "knows" it's using dependency injection. Thus, it's best used when registration +logic is well constrained to app/module/context initialization, and entirely absent from other parts of the application. Domain-level +classes should not have direct access to the container or the dependency resolver. +- We currently have an implementation of a class that gets its dependencies injected that derives from `VisualElement` in the +`Unity.Multiplayer.Tools.DependencyInjection.UIElements` assembly. This works well for views, but a more generic approach needs to be +implemented if we want to use dependency injection in a non-UI Elements context. However, UI Elements views are the entry point of +most of our applications, this might not be necessary in the short-term. +- Ideally, we'd be using constructors for dependency injection. This allows for a lot of different advantages, the main one being easier +to use in a context where dependency injection is not used / required. The primary example of this is in tests. For now, we can override +dependencies in the container if we need to test dependency-injected classes. +- This DI mini-framework is custom, but heavily inspired by more fully-fledged DI frameworks out there. It builds on well established +existing principles and behavior. Before adding any functionalities or changing the way the framework works, make sure you review +existing work. This utility should feel very familiar to work with for anyone who has used DI in an application previously. diff --git a/DependencyInjection/README.md.meta b/DependencyInjection/README.md.meta new file mode 100644 index 0000000..e9d9b23 --- /dev/null +++ b/DependencyInjection/README.md.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dab89a38996b4d1dba9c12bff54359db +timeCreated: 1679409542 \ No newline at end of file diff --git a/DependencyInjection/UIElements.meta b/DependencyInjection/UIElements.meta new file mode 100644 index 0000000..ffe8501 --- /dev/null +++ b/DependencyInjection/UIElements.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 79329c89272a42038cefda714ab10271 +timeCreated: 1678805162 \ No newline at end of file diff --git a/DependencyInjection/UIElements/AssemblyInfo.cs b/DependencyInjection/UIElements/AssemblyInfo.cs new file mode 100644 index 0000000..946f237 --- /dev/null +++ b/DependencyInjection/UIElements/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.UI")] diff --git a/DependencyInjection/UIElements/AssemblyInfo.cs.meta b/DependencyInjection/UIElements/AssemblyInfo.cs.meta new file mode 100644 index 0000000..5b96706 --- /dev/null +++ b/DependencyInjection/UIElements/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e4cc9dff1e8a4c1597e23fec1529175a +timeCreated: 1678979937 \ No newline at end of file diff --git a/DependencyInjection/UIElements/InjectedVisualElement.cs b/DependencyInjection/UIElements/InjectedVisualElement.cs new file mode 100644 index 0000000..ac12a87 --- /dev/null +++ b/DependencyInjection/UIElements/InjectedVisualElement.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.Editor; +using UnityEditor; +using UnityEngine.UIElements; + +namespace Unity.Multiplayer.Tools.DependencyInjection.UIElements +{ + /// + /// We have discussed making the dependency injection more generic and not bound to visual elements only. + /// We feel like the logic is easy enough to extract and implement in a mechanism that will suit our needs + /// when we get to that point. As of now, the capabilities that this allows in VisualElement classes is enough. + /// + abstract class InjectedVisualElement : VisualElement + { + protected InjectedVisualElement() + { + InitializeAsync().Forget(); + } + + async Task InitializeAsync() + { + InjectDependencies(); + await LoadUxmlAsync(); + InjectQueryFields(); + Initialized(); + } + + void InjectDependencies() + { + foreach (var field in InjectedVisualElementCache.GetInjectFields()) + { + var value = Context.Resolver.Resolve(field.FieldType); + field.SetValue(this, value); + } + } + + async Task LoadUxmlAsync() + { + var uxmlPath = (LoadUxmlViewAttribute)Attribute.GetCustomAttribute(typeof(TView), typeof(LoadUxmlViewAttribute)); + if (uxmlPath == null || string.IsNullOrWhiteSpace(uxmlPath.RootPath)) + { + return; + } + + var viewName = string.IsNullOrWhiteSpace(uxmlPath.ViewName) ? typeof(TView).Name : uxmlPath.ViewName; + var filePath = $"{uxmlPath.RootPath}{viewName}.uxml"; + var loadAssetAtPath = await AssetDatabaseHelper.LoadAssetAtPathAsync(filePath); + loadAssetAtPath.CloneTree(this); + } + + void InjectQueryFields() + { + foreach (var field in InjectedVisualElementCache.GetQueryFields()) + { + var attribute = (UxmlQueryAttribute)field.GetCustomAttribute(typeof(UxmlQueryAttribute)); + var queryName = string.IsNullOrWhiteSpace(attribute.Name) ? field.Name : attribute.Name; + var element = this.Q(queryName); + field.SetValue(this, element); + } + } + + protected virtual void Initialized() + { + } + } + + /// + /// Leveraging InitializeOnLoad allows us to compute this when the domain is reloaded. The alternative is getting + /// the fields we need whenever the view is first created, which results in a noticeable hang. + /// + [InitializeOnLoad] + static class InjectedVisualElementCache + { + static readonly Dictionary> s_InjectAttributeFields; + static readonly Dictionary> s_QueryAttributeFields; + + static InjectedVisualElementCache() + { + s_InjectAttributeFields = TypeCache.GetFieldsWithAttribute() + .GroupBy(x => x.ReflectedType) + .ToDictionary(x => x.Key, x => x.ToList()); + s_QueryAttributeFields = TypeCache.GetFieldsWithAttribute() + .GroupBy(x => x.ReflectedType) + .ToDictionary(x => x.Key, x => x.ToList()); + } + + public static IReadOnlyCollection GetInjectFields() + { + if (!s_InjectAttributeFields.TryGetValue(typeof(TView), out var fields)) + { + fields = new List(); + } + + return fields; + } + + public static IReadOnlyCollection GetQueryFields() + { + if (!s_QueryAttributeFields.TryGetValue(typeof(TView), out var fields)) + { + fields = new List(); + } + + return fields; + } + } +} diff --git a/DependencyInjection/UIElements/InjectedVisualElement.cs.meta b/DependencyInjection/UIElements/InjectedVisualElement.cs.meta new file mode 100644 index 0000000..0144d07 --- /dev/null +++ b/DependencyInjection/UIElements/InjectedVisualElement.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b98894e3e29d47a18ebab6d1f7612704 +timeCreated: 1678800415 \ No newline at end of file diff --git a/DependencyInjection/UIElements/LoadUxmlViewAttribute.cs b/DependencyInjection/UIElements/LoadUxmlViewAttribute.cs new file mode 100644 index 0000000..fb6f07d --- /dev/null +++ b/DependencyInjection/UIElements/LoadUxmlViewAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Unity.Multiplayer.Tools.DependencyInjection.UIElements +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + sealed class LoadUxmlViewAttribute : Attribute + { + public LoadUxmlViewAttribute(string rootPath) + { + RootPath = rootPath; + } + + public string RootPath { get; } + + public string ViewName { get; set; } + } +} diff --git a/DependencyInjection/UIElements/LoadUxmlViewAttribute.cs.meta b/DependencyInjection/UIElements/LoadUxmlViewAttribute.cs.meta new file mode 100644 index 0000000..2ba821b --- /dev/null +++ b/DependencyInjection/UIElements/LoadUxmlViewAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f23a684bf2b648659fa46a50f3f1a655 +timeCreated: 1678891276 \ No newline at end of file diff --git a/DependencyInjection/UIElements/UxmlQueryAttribute.cs b/DependencyInjection/UIElements/UxmlQueryAttribute.cs new file mode 100644 index 0000000..fe8e03e --- /dev/null +++ b/DependencyInjection/UIElements/UxmlQueryAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Unity.Multiplayer.Tools.DependencyInjection.UIElements +{ + sealed class UxmlQueryAttribute : Attribute + { + public string Name; + } +} diff --git a/DependencyInjection/UIElements/UxmlQueryAttribute.cs.meta b/DependencyInjection/UIElements/UxmlQueryAttribute.cs.meta new file mode 100644 index 0000000..d69cce1 --- /dev/null +++ b/DependencyInjection/UIElements/UxmlQueryAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4503353617f3493fb4be06cc46d3a6d6 +timeCreated: 1678892174 \ No newline at end of file diff --git a/DependencyInjection/Unity.Multiplayer.Tools.DependencyInjection.asmdef b/DependencyInjection/Unity.Multiplayer.Tools.DependencyInjection.asmdef new file mode 100644 index 0000000..c8c1c88 --- /dev/null +++ b/DependencyInjection/Unity.Multiplayer.Tools.DependencyInjection.asmdef @@ -0,0 +1,16 @@ +{ + "name": "Unity.Multiplayer.Tools.DependencyInjection", + "rootNamespace": "Unity.Multiplayer.Tools.DependencyInjection", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/DependencyInjection/Unity.Multiplayer.Tools.DependencyInjection.asmdef.meta b/DependencyInjection/Unity.Multiplayer.Tools.DependencyInjection.asmdef.meta new file mode 100644 index 0000000..0a8cac2 --- /dev/null +++ b/DependencyInjection/Unity.Multiplayer.Tools.DependencyInjection.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c3ec2c80206a4301a9d16f546ff886f9 +timeCreated: 1678800266 \ No newline at end of file diff --git a/DevTools~/RemoveTrailingWhitespace.py b/DevTools~/RemoveTrailingWhitespace.py new file mode 100644 index 0000000..9da1092 --- /dev/null +++ b/DevTools~/RemoveTrailingWhitespace.py @@ -0,0 +1,9 @@ +from pathlib import Path + +PROJECT_ROOT_RELATIVE = "../" + +for path in Path(PROJECT_ROOT_RELATIVE).rglob('*.cs'): + with open(path, 'r') as file: + new = [line.rstrip() for line in file] + with open(path, 'w') as file: + [file.write('%s\n' % line) for line in new] diff --git a/Documentation~/index.md b/Documentation~/index.md index 36cf4d6..0c48843 100644 --- a/Documentation~/index.md +++ b/Documentation~/index.md @@ -17,11 +17,11 @@ See guides below to install the Multiplayer Tools package and get started with u This version of Multiplayer Tools is compatible with the following Unity versions and platforms: -* 2020.3 and later +* 2021.3 and later * Windows, Mac, Linux platforms ## Document revision history |Date|Reason| |---|---| -|October 19, 2021|Document created. Matches package version 1.0.0| \ No newline at end of file +|October 19, 2021|Document created. Matches package version 1.0.0| diff --git a/Editor/AssemblyInfo.cs b/Editor/AssemblyInfo.cs index f0dad55..20d14ab 100644 --- a/Editor/AssemblyInfo.cs +++ b/Editor/AssemblyInfo.cs @@ -1,3 +1,8 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Tests.Editor")] \ No newline at end of file +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.BuildSettings.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.DependencyInjection.UIElements")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.UI")] +#endif diff --git a/Editor/AssetDatabaseHelper.cs b/Editor/AssetDatabaseHelper.cs new file mode 100644 index 0000000..7587914 --- /dev/null +++ b/Editor/AssetDatabaseHelper.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +namespace Unity.Multiplayer.Tools.Editor +{ + static class AssetDatabaseHelper + { + /// + /// At the moment there's an existent issue with the AssetDatabase that in certain situations the asset isn't fully loaded, + /// for instance, when using Overlays, if load an asset from CreatePanelContent method, the asset might not be available yet. + /// This method waits for the asset to be fully loaded asynchronously. + /// + /// + /// + /// + public static async Task LoadAssetAtPathAsync(string filePath) where T : Object + { + var asset = AssetDatabase.LoadAssetAtPath(filePath); + + while (asset == null) + { + await Task.Yield(); + asset = AssetDatabase.LoadAssetAtPath(filePath); + } + + return asset; + } + } +} \ No newline at end of file diff --git a/Editor/AssetDatabaseHelper.cs.meta b/Editor/AssetDatabaseHelper.cs.meta new file mode 100644 index 0000000..0830ce5 --- /dev/null +++ b/Editor/AssetDatabaseHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7daee6e1891a491a96462d925f1d4026 +timeCreated: 1682640100 \ No newline at end of file diff --git a/Editor/GenerateLinkXml.cs b/Editor/GenerateLinkXml.cs deleted file mode 100644 index 17cca4c..0000000 --- a/Editor/GenerateLinkXml.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.IO; -using UnityEditor.Build; -using UnityEditor.Build.Reporting; -using UnityEditor.UnityLinker; - -namespace Unity.Multiplayer.Tools.Editor -{ - // More Info: - // https://docs.unity3d.com/2020.1/Documentation/ScriptReference/Build.IUnityLinkerProcessor.GenerateAdditionalLinkXmlFile.html - // https://github.com/jilleJr/Newtonsoft.Json-for-Unity/wiki/Reference-link.xml - // https://forum.unity.com/threads/the-current-state-of-link-xml-in-packages.995848/ - class GenerateLinkXml : IUnityLinkerProcessor - { - const string k_LinkXmlPath = "Packages/com.unity.multiplayer.tools/Editor/link.xml"; - - public int callbackOrder => 0; - - public string GenerateAdditionalLinkXmlFile(BuildReport report, UnityLinkerBuildPipelineData data) - { - return Path.GetFullPath(k_LinkXmlPath); - } - } -} \ No newline at end of file diff --git a/Editor/GenerateLinkXml.cs.meta b/Editor/GenerateLinkXml.cs.meta deleted file mode 100644 index 1e38cc3..0000000 --- a/Editor/GenerateLinkXml.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 4d62babbb7aa4eaf8a4537bb486fa374 -timeCreated: 1667783600 \ No newline at end of file diff --git a/Editor/Settings/BuildSymbol.cs b/Editor/Settings/BuildSymbol.cs index ac89b48..c1bd30b 100644 --- a/Editor/Settings/BuildSymbol.cs +++ b/Editor/Settings/BuildSymbol.cs @@ -51,7 +51,7 @@ static string GetBuildSymbolSubString(BuildSymbol symbol) throw new ArgumentOutOfRangeException(nameof(symbol), symbol, null); } } - + public static string GetBuildSymbolString( Tool tool, BuildSymbol symbol) diff --git a/Editor/Settings/NetStatsMonitor/NetStatsMonitorBuildSettings.cs b/Editor/Settings/NetStatsMonitor/NetStatsMonitorBuildSettings.cs index 086e069..310ea7f 100644 --- a/Editor/Settings/NetStatsMonitor/NetStatsMonitorBuildSettings.cs +++ b/Editor/Settings/NetStatsMonitor/NetStatsMonitorBuildSettings.cs @@ -2,12 +2,12 @@ namespace Unity.Multiplayer.Tools.Editor { - /// /// Methods to control whether the Runtime Net Stats Monitor is included in the build. /// When making automated builds of your project, you can use this to dynamically /// control whether the monitor is included in release or development builds. /// + [Obsolete("This class is deprecated as the benefits of allowing the RNSM implementation to be conditionally compiled out of a build are too small. It will be removed in future.")] public static class NetStatsMonitorBuildSettings { // NOTE: These four public, parameterless methods are needed because our CI can only call @@ -16,6 +16,7 @@ public static class NetStatsMonitorBuildSettings /// /// Enables the RNSM in development builds for all build targets /// + [Obsolete("This method is deprecated as the benefits of allowing the RNSM implementation to be conditionally compiled out of a build are too small. It will be removed in future.")] public static void EnableInDevelopForAllBuildTargets() { BuildSettings.SetSymbolForAllBuildTargets(Tool.RuntimeNetStatsMonitor, BuildSymbol.DisableInDevelop, false); @@ -24,6 +25,7 @@ public static void EnableInDevelopForAllBuildTargets() /// /// Disables the RNSM in development builds for all build targets /// + [Obsolete("This method is deprecated as the benefits of allowing the RNSM implementation to be conditionally compiled out of a build are too small. It will be removed in future.")] public static void DisableInDevelopForAllBuildTargets() { BuildSettings.SetSymbolForAllBuildTargets(Tool.RuntimeNetStatsMonitor, BuildSymbol.DisableInDevelop, true); @@ -32,6 +34,7 @@ public static void DisableInDevelopForAllBuildTargets() /// /// Enables the RNSM in release builds for all build targets /// + [Obsolete("This method is deprecated as the benefits of allowing the RNSM implementation to be conditionally compiled out of a build are too small. It will be removed in future.")] public static void EnableInReleaseForAllBuildTargets() { BuildSettings.SetSymbolForAllBuildTargets(Tool.RuntimeNetStatsMonitor, BuildSymbol.EnableInRelease, true); @@ -40,6 +43,7 @@ public static void EnableInReleaseForAllBuildTargets() /// /// Disables the RNSM in release builds for all build targets /// + [Obsolete("This method is deprecated as the benefits of allowing the RNSM implementation to be conditionally compiled out of a build are too small. It will be removed in future.")] public static void DisableInReleaseForAllBuildTargets() { BuildSettings.SetSymbolForAllBuildTargets(Tool.RuntimeNetStatsMonitor, BuildSymbol.EnableInRelease, false); diff --git a/Editor/Settings/ProjectSettingsMenu.cs b/Editor/Settings/ProjectSettingsMenu.cs index 621b0e3..a63cef0 100644 --- a/Editor/Settings/ProjectSettingsMenu.cs +++ b/Editor/Settings/ProjectSettingsMenu.cs @@ -44,4 +44,4 @@ internal ProjectSettingsMenu() contents.Add(netSimSettings); } } -} \ No newline at end of file +} diff --git a/Editor/Settings/ProjectSettingsProvider.cs b/Editor/Settings/ProjectSettingsProvider.cs index 2f0cc22..e8b9a48 100644 --- a/Editor/Settings/ProjectSettingsProvider.cs +++ b/Editor/Settings/ProjectSettingsProvider.cs @@ -41,4 +41,4 @@ public static SettingsProvider CreateProjectSettingsMenuProvider() return provider; } } -} \ No newline at end of file +} diff --git a/Editor/Settings/ToolList.cs b/Editor/Settings/ToolList.cs index 8ebf02a..893d43f 100644 --- a/Editor/Settings/ToolList.cs +++ b/Editor/Settings/ToolList.cs @@ -1,5 +1,3 @@ -using System; - namespace Unity.Multiplayer.Tools.Editor { enum Tool diff --git a/Editor/Settings/UssClassNames.cs b/Editor/Settings/UssClassNames.cs index 247cf5e..4992270 100644 --- a/Editor/Settings/UssClassNames.cs +++ b/Editor/Settings/UssClassNames.cs @@ -9,4 +9,4 @@ internal static class UssClassNames public const string k_SettingsSubMenuContents = "unity-mp-tools-settings-sub-menu-contents"; public const string k_Setting = "unity-mp-tools-setting"; } -} \ No newline at end of file +} diff --git a/Editor/Tests/BuildSettings/BuildSettingsTests.cs b/Editor/Tests/BuildSettings/BuildSettingsTests.cs deleted file mode 100644 index 2ee9e22..0000000 --- a/Editor/Tests/BuildSettings/BuildSettingsTests.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using NUnit.Framework; -using Unity.Multiplayer.Tools.Editor; -using UnityEditor; -using UnityEditor.Build; -using Tool = Unity.Multiplayer.Tools.Editor.Tool; - -namespace Unity.Multiplayer.Tools.Tests.Editor -{ - [TestFixture] - [Explicit("Running tests with this fixture modifies compile flags and can trigger a recompile, " + - "so should only be run when needed. " + - "They are also pretty simple so don't need to be run at a all times.")] - class BuildSettingsTests - { - readonly Dictionary m_BuildSettingsPerTarget - = new Dictionary(); - - [OneTimeSetUp] - public void SaveScriptingDefineSymbols() - { - m_BuildSettingsPerTarget.Clear(); - foreach (var target in BuildSettings.k_AllBuildTargets) - { - PlayerSettings.GetScriptingDefineSymbols(target, out string[] symbols); - m_BuildSettingsPerTarget[target] = symbols; - } - } - - [OneTimeTearDown] - public void RestoreScriptingDefineSymbols() - { - foreach (var (target, symbols) in m_BuildSettingsPerTarget) - { - PlayerSettings.SetScriptingDefineSymbols(target, symbols); - } - } - - [Test(Description = "This test is there to make sure that we have the correct amount of tests and testing all build symbols" - + " for every tools.")] - public void When_TestingToolsBuildSettings_HasCorrectCount() - { - const int ExpectedToolCount = 2; - var actualToolCount = Enum.GetNames(typeof(Tool)).Length; - Assert.AreEqual(ExpectedToolCount, actualToolCount); - } - - [Test] - public void When_ToolIsEnabled_ItIsEnabled() - { - foreach (var symbol in BuildTestsConstants.k_AllBuildSymbols) - { - foreach (var tool in BuildTestsConstants.k_AllTools) - { - BuildSettings.AddSymbolToAllBuildTargets( - tool, symbol); - Assert.IsTrue(BuildSettings.GetEnabledForAnyBuildTargets( - tool, symbol)); - Assert.IsTrue(BuildSettings.GetSymbolInAllBuildTargets( - tool, symbol)); - } - } - } - - [Test] - public void When_ToolIsDisabled_ItIsDisabled() - { - foreach (var symbol in BuildTestsConstants.k_AllBuildSymbols) - { - foreach (var tool in BuildTestsConstants.k_AllTools) - { - BuildSettings.RemoveSymbolFromAllBuildTargets( - tool, symbol); - Assert.IsFalse(BuildSettings.GetEnabledForAnyBuildTargets( - tool, symbol)); - Assert.IsFalse(BuildSettings.GetSymbolInAllBuildTargets( - tool, symbol)); - } - } - } - - [Test] - public void When_ToolIsEnabledThenDisabled_ItIsDisabled() - { - foreach (var symbol in BuildTestsConstants.k_AllBuildSymbols) - { - foreach (var tool in BuildTestsConstants.k_AllTools) - { - BuildSettings.AddSymbolToAllBuildTargets( - tool, symbol); - BuildSettings.RemoveSymbolFromAllBuildTargets( - tool, symbol); - - Assert.IsFalse(BuildSettings.GetEnabledForAnyBuildTargets( - tool, symbol)); - Assert.IsFalse(BuildSettings.GetSymbolInAllBuildTargets( - tool, symbol)); - } - } - } - - [Test] - public void When_ToolIsEnabledThenDisabledThenEnabled_ItIsEnabled() - { - foreach (var symbol in BuildTestsConstants.k_AllBuildSymbols) - { - foreach (var tool in BuildTestsConstants.k_AllTools) - { - BuildSettings.AddSymbolToAllBuildTargets( - tool, symbol); - BuildSettings.RemoveSymbolFromAllBuildTargets( - tool, symbol); - BuildSettings.AddSymbolToAllBuildTargets( - tool, symbol); - - Assert.IsTrue(BuildSettings.GetEnabledForAnyBuildTargets( - tool, symbol)); - Assert.IsTrue(BuildSettings.GetSymbolInAllBuildTargets( - tool, symbol)); - } - } - } - } -} diff --git a/Editor/Tests/BuildSettings/BuildSettingsTests.cs.meta b/Editor/Tests/BuildSettings/BuildSettingsTests.cs.meta deleted file mode 100644 index 806f902..0000000 --- a/Editor/Tests/BuildSettings/BuildSettingsTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 26bec3e6ae1348679b719a545e27db8c -timeCreated: 1658240365 \ No newline at end of file diff --git a/Editor/Tests/BuildSettings/BuildSymbolTests.cs b/Editor/Tests/BuildSettings/BuildSymbolTests.cs deleted file mode 100644 index aa6c748..0000000 --- a/Editor/Tests/BuildSettings/BuildSymbolTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using Unity.Multiplayer.Tools.Editor; - -namespace Unity.Multiplayer.Tools.Tests.Editor -{ - public class BuildSymbolTests - { - readonly IReadOnlyDictionary< - Tool, - IReadOnlyDictionary> m_BuildSymbols = new Dictionary> - { - //RNSM Build Symbols - { - Tool.RuntimeNetStatsMonitor, - new Dictionary - { - { - BuildSymbol.DisableInDevelop, - "UNITY_MP_TOOLS_NET_STATS_MONITOR_DISABLED_IN_DEVELOP" - }, - { - BuildSymbol.EnableInRelease, - "UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE" - }, - { - BuildSymbol.OverrideEnabled, - "UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED" - } - } - }, - //Network Simulator Build Symbols - { - Tool.NetworkSimulator, - new Dictionary - { - { - BuildSymbol.DisableInDevelop, - "UNITY_MP_TOOLS_NETSIM_DISABLED_IN_DEVELOP" - }, - { - BuildSymbol.EnableInRelease, - "UNITY_MP_TOOLS_NETSIM_ENABLED_IN_RELEASE" - }, - { - BuildSymbol.OverrideEnabled, - "UNITY_MP_TOOLS_NETSIM_IMPLEMENTATION_ENABLED" - } - } - } - }; - - [Test] - public void When_GettingToolBuildSymbol_IsExpectedString() - { - foreach (var tool in BuildTestsConstants.k_AllTools) - { - foreach (var symbol in BuildTestsConstants.k_AllBuildSymbols) - { - var expectedString = m_BuildSymbols[tool][symbol]; - var actualString = BuildSymbolStrings.GetBuildSymbolString(tool, symbol); - Assert.AreEqual(expectedString, actualString); - } - } - } - } -} diff --git a/Editor/Tests/BuildSettings/BuildSymbolTests.cs.meta b/Editor/Tests/BuildSettings/BuildSymbolTests.cs.meta deleted file mode 100644 index 19d793a..0000000 --- a/Editor/Tests/BuildSettings/BuildSymbolTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: c122dd84a0224f549db6d8a26dc0b691 -timeCreated: 1658418347 \ No newline at end of file diff --git a/Editor/Tests/BuildSettings/BuildTestsConstants.cs b/Editor/Tests/BuildSettings/BuildTestsConstants.cs deleted file mode 100644 index bb4d486..0000000 --- a/Editor/Tests/BuildSettings/BuildTestsConstants.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using Unity.Multiplayer.Tools.Editor; - -namespace Unity.Multiplayer.Tools.Tests.Editor -{ - internal static class BuildTestsConstants - { - internal static readonly IReadOnlyList k_AllBuildSymbols = new List - { - BuildSymbol.DisableInDevelop, - BuildSymbol.EnableInRelease, - }; - - internal static readonly IReadOnlyList k_AllTools = new List - { - Tool.RuntimeNetStatsMonitor, - Tool.NetworkSimulator - }; - } -} diff --git a/Editor/Tests/BuildSettings/BuildTestsConstants.cs.meta b/Editor/Tests/BuildSettings/BuildTestsConstants.cs.meta deleted file mode 100644 index 59e3afd..0000000 --- a/Editor/Tests/BuildSettings/BuildTestsConstants.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: b5ec33b16319438f8ecdfee272c055ce -timeCreated: 1658423952 \ No newline at end of file diff --git a/Editor/Tests/Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Editor.asmdef b/Editor/Tests/Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Editor.asmdef deleted file mode 100644 index 4ad3afb..0000000 --- a/Editor/Tests/Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Editor.asmdef +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "Unity.Multiplayer.Tools.Tests.Editor", - "rootNamespace": "Unity.Multiplayer.Tools.Tests.Editor", - "references": [ - "UnityEditor.TestRunner", - "UnityEngine.TestRunner", - "Unity.Multiplayer.Tools.Editor" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "autoReferenced": false, - "defineConstraints": [ - "UNITY_2021_2_OR_NEWER" - ], - "versionDefines": [], - "noEngineReferences": false -} \ No newline at end of file diff --git a/Editor/Unity.Multiplayer.Tools.Editor.asmdef b/Editor/Unity.Multiplayer.Tools.Editor.asmdef index 608a68d..68841a4 100644 --- a/Editor/Unity.Multiplayer.Tools.Editor.asmdef +++ b/Editor/Unity.Multiplayer.Tools.Editor.asmdef @@ -12,9 +12,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "UNITY_2021_2_OR_NEWER" - ], + "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } \ No newline at end of file diff --git a/Editor/link.xml b/Editor/link.xml deleted file mode 100644 index c664870..0000000 --- a/Editor/link.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/Editor/link.xml.meta b/Editor/link.xml.meta deleted file mode 100644 index 8798d6f..0000000 --- a/Editor/link.xml.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: d7f4234e1d5d48399c36b37e8edbc78e -timeCreated: 1667611772 \ No newline at end of file diff --git a/Initialization.meta b/Initialization.meta index bbe2bcb..db2738b 100644 --- a/Initialization.meta +++ b/Initialization.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 42bd4a19cf73446fb42cf476fb4a2e73 +guid: c35e298b141ee4b00a931206059655fc timeCreated: 1660169971 \ No newline at end of file diff --git a/Initialization/ContextsDefinition.cs b/Initialization/ContextsDefinition.cs index 2eef915..0f3f06d 100644 --- a/Initialization/ContextsDefinition.cs +++ b/Initialization/ContextsDefinition.cs @@ -1,13 +1,34 @@ -using Unity.Multiplayer.Tools.Common; +using System.Collections.Generic; +using Unity.Multiplayer.Tools.Common; namespace Unity.Multiplayer.Tools.Context { static class ContextsDefinition { - internal static IContext[] Get() => new IContext[] + internal static IContext[] Contexts { get; } + + static ContextsDefinition() + { + var contexts = new List(); + + IRuntimeUpdater runtimeUpdater = new RuntimeUpdater(); + + InitializeNetVisContexts(runtimeUpdater, contexts); + + Contexts = contexts.ToArray(); + } + + static void InitializeNetVisContexts(IRuntimeUpdater runtimeUpdater, List contexts) { - // Example: new NetVisContext(), - // Other tool contexts - }; +#if UNITY_2023_2_OR_NEWER && UNITY_EDITOR + var netVisRuntimeContext = NetVis.Editor.Visualization.NetVisContext.InitializeInstance(runtimeUpdater); + contexts.Add(netVisRuntimeContext); + var netVisPresentationContext = NetVis.Editor.UI.PresentationContext.InitializeInstance( + netVisRuntimeContext.ConfigurationWithEvents, + netVisRuntimeContext.BandwidthStats, + netVisRuntimeContext.ConnectedClients); + contexts.Add(netVisPresentationContext); +#endif // UNITY_2023_2_OR_NEWER && UNITY_EDITOR + } } } diff --git a/Initialization/ContextsInitializer.cs b/Initialization/ContextsInitializer.cs index acafd52..e463a39 100644 --- a/Initialization/ContextsInitializer.cs +++ b/Initialization/ContextsInitializer.cs @@ -5,11 +5,8 @@ // Runtime Contexts are correctly running or if Editor Only Contexts are disabled as expected. // #define UNITY_MP_TOOLS_SIMULATE_BUILD -using System.Diagnostics; -using System.Runtime.CompilerServices; using Unity.Multiplayer.Tools.Common; using UnityEngine; -using Debug = UnityEngine.Debug; namespace Unity.Multiplayer.Tools.Context { @@ -27,11 +24,11 @@ static class ContextsInitializer static ContextsInitializer() { - TraceCall(); + DebugUtil.TraceMethodName(); Application.quitting += DisableRuntimeContexts; - s_Contexts = ContextsDefinition.Get(); + s_Contexts = ContextsDefinition.Contexts; #if UNITY_EDITOR && !UNITY_MP_TOOLS_SIMULATE_BUILD EnableEditorContexts(); @@ -40,7 +37,7 @@ static ContextsInitializer() static void EnableEditorContexts() { - TraceCall(); + DebugUtil.TraceMethodName(); foreach (var context in s_Contexts) { @@ -54,7 +51,7 @@ static void EnableEditorContexts() [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void EnableRuntimeContexts() { - TraceCall(); + DebugUtil.TraceMethodName(); foreach (var context in s_Contexts) { @@ -67,7 +64,7 @@ static void EnableRuntimeContexts() static void DisableRuntimeContexts() { - TraceCall(); + DebugUtil.TraceMethodName(); foreach (var context in s_Contexts) { @@ -77,11 +74,5 @@ static void DisableRuntimeContexts() } } } - - [Conditional("UNITY_MP_TOOLS_CONTEXT_TRACE_CALLS")] - static void TraceCall([CallerMemberName] string methodName = "") - { - Debug.Log($"{nameof(ContextsInitializer)}.{methodName}"); - } } } diff --git a/Initialization/ContextsInitializer.cs.meta b/Initialization/ContextsInitializer.cs.meta index 288d1bb..1b201c1 100644 --- a/Initialization/ContextsInitializer.cs.meta +++ b/Initialization/ContextsInitializer.cs.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 0570333af2d44f6aa7a46eeaf9b8cd8d +guid: 760c488faaf234803a8e32e6819a8ab0 timeCreated: 1660170162 \ No newline at end of file diff --git a/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef b/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef index 4628864..9cee7ca 100644 --- a/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef +++ b/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef @@ -3,7 +3,10 @@ "rootNamespace": "Unity.Multiplayer.Tools.Context", "references": [ "Unity.Multiplayer.Tools.Common", - "Unity.Multiplayer.Tools.NetVis.Shared" + "Unity.Multiplayer.Tools.NetVis.Configuration", + "Unity.Multiplayer.Tools.NetVis.Editor.UI", + "Unity.Multiplayer.Tools.NetVis.Editor.Visualization", + "Unity.Multiplayer.Tools.Adapters" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef.meta b/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef.meta index 371275f..4f18265 100644 --- a/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef.meta +++ b/Initialization/Unity.Multiplayer.Tools.Initializationt.asmdef.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 80dd7f8108eb4391904f5f877f20cf55 +guid: b809aad57883d47ef8c06c1e3fa1ca37 timeCreated: 1660170011 \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 74b9b11..d6a559c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,5 +1,5 @@ -Multiplayer Tools copyright © 2021 Unity Technologies +Multiplayer Tools copyright © 2023 Unity Technologies -Licensed under the Unity Package Distribution License (see https://unity3d.com/legal/licenses/Unity_Package_Distribution_License ). +Licensed under the Unity Package Distribution License (see https://unity3d.com/legal/licenses/Unity_Package_Distribution_License). -Unless expressly provided otherwise, the software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. \ No newline at end of file +Unless expressly provided otherwise, the software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. diff --git a/MetricEvents/Runtime/AssemblyInfo.cs b/MetricEvents/Runtime/AssemblyInfo.cs index 5026878..4d7c7d0 100644 --- a/MetricEvents/Runtime/AssemblyInfo.cs +++ b/MetricEvents/Runtime/AssemblyInfo.cs @@ -12,4 +12,6 @@ // Listeners (tests) // should only include the NGO 1.0 Adapter. // Individual tools should listen through the adapter, rather than using this assembly directly. -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +#endif diff --git a/MetricEvents/Runtime/MetricEvents.cs b/MetricEvents/Runtime/MetricEvents.cs index 7b06e62..15b46a9 100644 --- a/MetricEvents/Runtime/MetricEvents.cs +++ b/MetricEvents/Runtime/MetricEvents.cs @@ -14,4 +14,4 @@ public static void RaiseOnMetricsReceived(MetricCollection metricCollection) OnMetricsReceived?.Invoke(metricCollection); } } -} \ No newline at end of file +} diff --git a/MetricTestData/Runtime/AssemblyInfo.cs b/MetricTestData/Runtime/AssemblyInfo.cs index 7e0a19c..c4a8cbb 100644 --- a/MetricTestData/Runtime/AssemblyInfo.cs +++ b/MetricTestData/Runtime/AssemblyInfo.cs @@ -1,3 +1,6 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +#endif diff --git a/MetricTestData/Runtime/Definitions/Adjectives.cs b/MetricTestData/Runtime/Definitions/Adjectives.cs index d2ff8bf..4e36baa 100644 --- a/MetricTestData/Runtime/Definitions/Adjectives.cs +++ b/MetricTestData/Runtime/Definitions/Adjectives.cs @@ -908,4 +908,4 @@ static class Adjectives } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/Definitions/Nouns.cs b/MetricTestData/Runtime/Definitions/Nouns.cs index adbae56..9f3e81a 100644 --- a/MetricTestData/Runtime/Definitions/Nouns.cs +++ b/MetricTestData/Runtime/Definitions/Nouns.cs @@ -8807,4 +8807,4 @@ static class Nouns } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/Definitions/Verbs.cs b/MetricTestData/Runtime/Definitions/Verbs.cs index ba4c6dc..c13392f 100644 --- a/MetricTestData/Runtime/Definitions/Verbs.cs +++ b/MetricTestData/Runtime/Definitions/Verbs.cs @@ -637,4 +637,4 @@ static class Verbs } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/Dispatch/ITestDataTracker.cs b/MetricTestData/Runtime/Dispatch/ITestDataTracker.cs index bc38f74..bf8d0bc 100644 --- a/MetricTestData/Runtime/Dispatch/ITestDataTracker.cs +++ b/MetricTestData/Runtime/Dispatch/ITestDataTracker.cs @@ -65,4 +65,4 @@ interface ITestDataTracker void UpdatePacketLoss(float count); } -} \ No newline at end of file +} diff --git a/MetricTestData/Runtime/Dispatch/TestDataTracker.cs b/MetricTestData/Runtime/Dispatch/TestDataTracker.cs index 645d565..9702902 100644 --- a/MetricTestData/Runtime/Dispatch/TestDataTracker.cs +++ b/MetricTestData/Runtime/Dispatch/TestDataTracker.cs @@ -229,4 +229,4 @@ public void UpdatePacketLoss(float count) m_PacketLoss.Set(count); } } -} \ No newline at end of file +} diff --git a/MetricTestData/Runtime/TestData.meta b/MetricTestData/Runtime/TestData.meta deleted file mode 100644 index be6664f..0000000 --- a/MetricTestData/Runtime/TestData.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 748815dfee6644187aed19cfe3363f6d -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/MetricTestData/Runtime/TestData/Definitions.meta b/MetricTestData/Runtime/TestData/Definitions.meta deleted file mode 100644 index 71164a2..0000000 --- a/MetricTestData/Runtime/TestData/Definitions.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: fe9014272bea64ad5b9b9a96bf3f0a80 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/MetricTestData/Runtime/TestDataDefinition.cs b/MetricTestData/Runtime/TestDataDefinition.cs index d055103..2bd34e8 100644 --- a/MetricTestData/Runtime/TestDataDefinition.cs +++ b/MetricTestData/Runtime/TestDataDefinition.cs @@ -1,9 +1,9 @@ #if UNITY_EDITOR +using System; using System.Collections.Generic; using System.Linq; using Unity.Multiplayer.Tools.MetricTestData.Definitions; -using Random = System.Random; namespace Unity.Multiplayer.Tools.MetricTestData { @@ -83,4 +83,4 @@ static string Capitalize(string input) } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/TestDataDispatcher.cs b/MetricTestData/Runtime/TestDataDispatcher.cs index a63e4cd..3884752 100644 --- a/MetricTestData/Runtime/TestDataDispatcher.cs +++ b/MetricTestData/Runtime/TestDataDispatcher.cs @@ -281,4 +281,4 @@ public void DispatchServerFrame(uint nbClients = 2) } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/TestDataGenerator.cs b/MetricTestData/Runtime/TestDataGenerator.cs index ffced01..57b40bb 100644 --- a/MetricTestData/Runtime/TestDataGenerator.cs +++ b/MetricTestData/Runtime/TestDataGenerator.cs @@ -129,4 +129,4 @@ public IEnumerable GenerateSceneEvent(string prefix, params Co } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/TestDataGeneratorComponent.cs b/MetricTestData/Runtime/TestDataGeneratorComponent.cs index 95d18c7..a37b16e 100644 --- a/MetricTestData/Runtime/TestDataGeneratorComponent.cs +++ b/MetricTestData/Runtime/TestDataGeneratorComponent.cs @@ -52,4 +52,4 @@ internal void Update() } } -#endif \ No newline at end of file +#endif diff --git a/MetricTestData/Runtime/TestDataTrends.cs b/MetricTestData/Runtime/TestDataTrends.cs index 89e8902..b751332 100644 --- a/MetricTestData/Runtime/TestDataTrends.cs +++ b/MetricTestData/Runtime/TestDataTrends.cs @@ -57,4 +57,4 @@ class TestDataTrends } } -#endif \ No newline at end of file +#endif diff --git a/MetricTypes/Runtime/AssemblyInfo.cs b/MetricTypes/Runtime/AssemblyInfo.cs index 057feb3..c95a41b 100644 --- a/MetricTypes/Runtime/AssemblyInfo.cs +++ b/MetricTypes/Runtime/AssemblyInfo.cs @@ -3,12 +3,11 @@ [assembly: InternalsVisibleTo("Unity.Netcode.Runtime")] [assembly: InternalsVisibleTo("Unity.Netcode.RuntimeTests")] [assembly: InternalsVisibleTo("Unity.Netcode.EditorTests")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Ngo1")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricTestData")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricTypes.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStats")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Editor")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")] @@ -17,3 +16,9 @@ #if UNITY_EDITOR [assembly: InternalsVisibleTo("TestProject.EditorTests")] #endif + +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricTypes.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Tests.Editor")] +#endif diff --git a/MetricTypes/Runtime/MetricTypes/ConnectionInfo.cs b/MetricTypes/Runtime/MetricTypes/ConnectionInfo.cs index dcf57bc..138a496 100644 --- a/MetricTypes/Runtime/MetricTypes/ConnectionInfo.cs +++ b/MetricTypes/Runtime/MetricTypes/ConnectionInfo.cs @@ -33,4 +33,4 @@ public override int GetHashCode() return Id.GetHashCode(); } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/DirectedMetricTypeExtensions.cs b/MetricTypes/Runtime/MetricTypes/DirectedMetricTypeExtensions.cs index 285f86e..4d725f4 100644 --- a/MetricTypes/Runtime/MetricTypes/DirectedMetricTypeExtensions.cs +++ b/MetricTypes/Runtime/MetricTypes/DirectedMetricTypeExtensions.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using Unity.Multiplayer.Tools.NetStats; using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.NetStats; namespace Unity.Multiplayer.Tools.MetricTypes { @@ -63,4 +63,4 @@ internal static string GetDisplayName(this DirectedMetricType directedMetric) return directedMetric.ToString(); } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/DirectionalMetricInfo.cs b/MetricTypes/Runtime/MetricTypes/DirectionalMetricInfo.cs index 5841cc3..5d3351a 100644 --- a/MetricTypes/Runtime/MetricTypes/DirectionalMetricInfo.cs +++ b/MetricTypes/Runtime/MetricTypes/DirectionalMetricInfo.cs @@ -1,6 +1,5 @@ -using System; -using System.Linq; - + +using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; namespace Unity.Multiplayer.Tools.MetricTypes @@ -33,4 +32,4 @@ public DirectionalMetricInfo(MetricType metricType, NetworkDirection networkDire internal string DisplayName => DirectedMetricType.GetDisplayName(); } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/INetworkMetricEvent.cs b/MetricTypes/Runtime/MetricTypes/INetworkMetricEvent.cs index 2c3736c..2e8e6d0 100644 --- a/MetricTypes/Runtime/MetricTypes/INetworkMetricEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/INetworkMetricEvent.cs @@ -6,4 +6,4 @@ interface INetworkMetricEvent long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/MetricType.cs b/MetricTypes/Runtime/MetricTypes/MetricType.cs index 92f3c66..059250e 100644 --- a/MetricTypes/Runtime/MetricTypes/MetricType.cs +++ b/MetricTypes/Runtime/MetricTypes/MetricType.cs @@ -1,7 +1,7 @@ using Unity.Multiplayer.Tools.NetStats; using MT = Unity.Multiplayer.Tools.MetricTypes.MetricType; -using ND = Unity.Multiplayer.Tools.MetricTypes.NetworkDirection; -using NDC = Unity.Multiplayer.Tools.MetricTypes.NetworkDirectionConstants; +using ND = Unity.Multiplayer.Tools.Common.NetworkDirection; +using NDC = Unity.Multiplayer.Tools.Common.NetworkDirectionConstants; namespace Unity.Multiplayer.Tools.MetricTypes { @@ -202,4 +202,4 @@ public enum DirectedMetricType DisplayAsPercentage = true)] PacketLoss = (MT.PacketLoss << NDC.k_BitWidth) | ND.Received, } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/MetricTypeExtensions.cs b/MetricTypes/Runtime/MetricTypes/MetricTypeExtensions.cs index 3290cdf..9f0c574 100644 --- a/MetricTypes/Runtime/MetricTypes/MetricTypeExtensions.cs +++ b/MetricTypes/Runtime/MetricTypes/MetricTypeExtensions.cs @@ -1,5 +1,5 @@ -using Unity.Multiplayer.Tools.MetricTypes; -using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.MetricTypes; namespace Unity.Multiplayer.Tools.NetStats { @@ -25,4 +25,4 @@ internal static string GetTypeNameString(this MetricType metricType) return GetTypeNameString(metricType.ToString()); } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/NamedMessageEvent.cs b/MetricTypes/Runtime/MetricTypes/NamedMessageEvent.cs index 747ae7c..5285ad0 100644 --- a/MetricTypes/Runtime/MetricTypes/NamedMessageEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/NamedMessageEvent.cs @@ -25,4 +25,4 @@ public NamedMessageEvent(ConnectionInfo connection, FixedString64Bytes name, lon public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/NetworkMessageEvent.cs b/MetricTypes/Runtime/MetricTypes/NetworkMessageEvent.cs index dacfbc0..76193a7 100644 --- a/MetricTypes/Runtime/MetricTypes/NetworkMessageEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/NetworkMessageEvent.cs @@ -25,4 +25,4 @@ public NetworkMessageEvent(ConnectionInfo connection, FixedString64Bytes name, l public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/NetworkMetricTypes.cs b/MetricTypes/Runtime/MetricTypes/NetworkMetricTypes.cs index 09b4491..05a8f0d 100644 --- a/MetricTypes/Runtime/MetricTypes/NetworkMetricTypes.cs +++ b/MetricTypes/Runtime/MetricTypes/NetworkMetricTypes.cs @@ -8,76 +8,48 @@ /// static class NetworkMetricTypes { - public static readonly DirectionalMetricInfo NetworkMessageSent = - new DirectionalMetricInfo(DirectedMetricType.NetworkMessageSent); - public static readonly DirectionalMetricInfo NetworkMessageReceived = - new DirectionalMetricInfo(DirectedMetricType.NetworkMessageReceived); + public static readonly DirectionalMetricInfo NetworkMessageSent = new(DirectedMetricType.NetworkMessageSent); + public static readonly DirectionalMetricInfo NetworkMessageReceived = new(DirectedMetricType.NetworkMessageReceived); - public static readonly DirectionalMetricInfo TotalBytesSent = - new DirectionalMetricInfo(DirectedMetricType.TotalBytesSent); - public static readonly DirectionalMetricInfo TotalBytesReceived = - new DirectionalMetricInfo(DirectedMetricType.TotalBytesReceived); + public static readonly DirectionalMetricInfo TotalBytesSent = new(DirectedMetricType.TotalBytesSent); + public static readonly DirectionalMetricInfo TotalBytesReceived = new(DirectedMetricType.TotalBytesReceived); - public static readonly DirectionalMetricInfo RpcSent = - new DirectionalMetricInfo(DirectedMetricType.RpcSent); - public static readonly DirectionalMetricInfo RpcReceived = - new DirectionalMetricInfo(DirectedMetricType.RpcReceived); + public static readonly DirectionalMetricInfo RpcSent = new(DirectedMetricType.RpcSent); + public static readonly DirectionalMetricInfo RpcReceived = new(DirectedMetricType.RpcReceived); - public static readonly DirectionalMetricInfo NamedMessageSent = - new DirectionalMetricInfo(DirectedMetricType.NamedMessageSent); - public static readonly DirectionalMetricInfo NamedMessageReceived = - new DirectionalMetricInfo(DirectedMetricType.NamedMessageReceived); + public static readonly DirectionalMetricInfo NamedMessageSent = new (DirectedMetricType.NamedMessageSent); + public static readonly DirectionalMetricInfo NamedMessageReceived = new (DirectedMetricType.NamedMessageReceived); - public static readonly DirectionalMetricInfo UnnamedMessageSent = - new DirectionalMetricInfo(DirectedMetricType.UnnamedMessageSent); - public static readonly DirectionalMetricInfo UnnamedMessageReceived = - new DirectionalMetricInfo(DirectedMetricType.UnnamedMessageReceived); + public static readonly DirectionalMetricInfo UnnamedMessageSent = new (DirectedMetricType.UnnamedMessageSent); + public static readonly DirectionalMetricInfo UnnamedMessageReceived = new (DirectedMetricType.UnnamedMessageReceived); - public static readonly DirectionalMetricInfo NetworkVariableDeltaSent = - new DirectionalMetricInfo(DirectedMetricType.NetworkVariableDeltaSent); - public static readonly DirectionalMetricInfo NetworkVariableDeltaReceived = - new DirectionalMetricInfo(DirectedMetricType.NetworkVariableDeltaReceived); + public static readonly DirectionalMetricInfo NetworkVariableDeltaSent = new (DirectedMetricType.NetworkVariableDeltaSent); + public static readonly DirectionalMetricInfo NetworkVariableDeltaReceived = new (DirectedMetricType.NetworkVariableDeltaReceived); - public static readonly DirectionalMetricInfo ObjectSpawnedSent = - new DirectionalMetricInfo(DirectedMetricType.ObjectSpawnedSent); - public static readonly DirectionalMetricInfo ObjectSpawnedReceived = - new DirectionalMetricInfo(DirectedMetricType.ObjectSpawnedReceived); + public static readonly DirectionalMetricInfo ObjectSpawnedSent = new (DirectedMetricType.ObjectSpawnedSent); + public static readonly DirectionalMetricInfo ObjectSpawnedReceived = new (DirectedMetricType.ObjectSpawnedReceived); - public static readonly DirectionalMetricInfo ObjectDestroyedSent = - new DirectionalMetricInfo(DirectedMetricType.ObjectDestroyedSent); - public static readonly DirectionalMetricInfo ObjectDestroyedReceived = - new DirectionalMetricInfo(DirectedMetricType.ObjectDestroyedReceived); + public static readonly DirectionalMetricInfo ObjectDestroyedSent = new (DirectedMetricType.ObjectDestroyedSent); + public static readonly DirectionalMetricInfo ObjectDestroyedReceived = new (DirectedMetricType.ObjectDestroyedReceived); - public static readonly DirectionalMetricInfo OwnershipChangeSent = - new DirectionalMetricInfo(DirectedMetricType.OwnershipChangeSent); - public static readonly DirectionalMetricInfo OwnershipChangeReceived = - new DirectionalMetricInfo(DirectedMetricType.OwnershipChangeReceived); + public static readonly DirectionalMetricInfo OwnershipChangeSent = new (DirectedMetricType.OwnershipChangeSent); + public static readonly DirectionalMetricInfo OwnershipChangeReceived = new (DirectedMetricType.OwnershipChangeReceived); - public static readonly DirectionalMetricInfo ServerLogSent = - new DirectionalMetricInfo(DirectedMetricType.ServerLogSent); - public static readonly DirectionalMetricInfo ServerLogReceived = - new DirectionalMetricInfo(DirectedMetricType.ServerLogReceived); + public static readonly DirectionalMetricInfo ServerLogSent = new (DirectedMetricType.ServerLogSent); + public static readonly DirectionalMetricInfo ServerLogReceived = new (DirectedMetricType.ServerLogReceived); - public static readonly DirectionalMetricInfo SceneEventSent = - new DirectionalMetricInfo(DirectedMetricType.SceneEventSent); - public static readonly DirectionalMetricInfo SceneEventReceived = - new DirectionalMetricInfo(DirectedMetricType.SceneEventReceived); + public static readonly DirectionalMetricInfo SceneEventSent = new (DirectedMetricType.SceneEventSent); + public static readonly DirectionalMetricInfo SceneEventReceived = new (DirectedMetricType.SceneEventReceived); - public static readonly DirectionalMetricInfo PacketsSent = - new DirectionalMetricInfo(DirectedMetricType.PacketsSent); - public static readonly DirectionalMetricInfo PacketsReceived = - new DirectionalMetricInfo(DirectedMetricType.PacketsReceived); + public static readonly DirectionalMetricInfo PacketsSent = new (DirectedMetricType.PacketsSent); + public static readonly DirectionalMetricInfo PacketsReceived = new (DirectedMetricType.PacketsReceived); - public static readonly DirectionalMetricInfo RttToServer = - new DirectionalMetricInfo(DirectedMetricType.RttToServer); + public static readonly DirectionalMetricInfo RttToServer = new (DirectedMetricType.RttToServer); - public static readonly DirectionalMetricInfo NetworkObjects = - new DirectionalMetricInfo(DirectedMetricType.NetworkObjects); + public static readonly DirectionalMetricInfo NetworkObjects = new (DirectedMetricType.NetworkObjects); - public static readonly DirectionalMetricInfo ConnectedClients = - new DirectionalMetricInfo(DirectedMetricType.Connections); + public static readonly DirectionalMetricInfo ConnectedClients = new (DirectedMetricType.Connections); - public static readonly DirectionalMetricInfo PacketLoss = - new DirectionalMetricInfo(DirectedMetricType.PacketLoss); + public static readonly DirectionalMetricInfo PacketLoss = new (DirectedMetricType.PacketLoss); } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/NetworkObjectIdentifier.cs b/MetricTypes/Runtime/MetricTypes/NetworkObjectIdentifier.cs index 07730ea..1900ac7 100644 --- a/MetricTypes/Runtime/MetricTypes/NetworkObjectIdentifier.cs +++ b/MetricTypes/Runtime/MetricTypes/NetworkObjectIdentifier.cs @@ -22,4 +22,4 @@ public NetworkObjectIdentifier(FixedString64Bytes name, ulong networkId) public ulong NetworkId { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/NetworkVariableEvent.cs b/MetricTypes/Runtime/MetricTypes/NetworkVariableEvent.cs index 3409920..94c49ce 100644 --- a/MetricTypes/Runtime/MetricTypes/NetworkVariableEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/NetworkVariableEvent.cs @@ -26,9 +26,9 @@ public NetworkVariableEvent(ConnectionInfo connection, NetworkObjectIdentifier n public NetworkObjectIdentifier NetworkId { get; } public FixedString64Bytes Name { get; } - + public FixedString64Bytes NetworkBehaviourName { get; } public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/ObjectDestroyedEvent.cs b/MetricTypes/Runtime/MetricTypes/ObjectDestroyedEvent.cs index e6025cb..7cb82c4 100644 --- a/MetricTypes/Runtime/MetricTypes/ObjectDestroyedEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/ObjectDestroyedEvent.cs @@ -18,4 +18,4 @@ public ObjectDestroyedEvent(ConnectionInfo connection, NetworkObjectIdentifier n public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/ObjectSpawnedEvent.cs b/MetricTypes/Runtime/MetricTypes/ObjectSpawnedEvent.cs index 7ca3a40..54fbd43 100644 --- a/MetricTypes/Runtime/MetricTypes/ObjectSpawnedEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/ObjectSpawnedEvent.cs @@ -18,4 +18,4 @@ public ObjectSpawnedEvent(ConnectionInfo connection, NetworkObjectIdentifier net public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/OwnershipChangeEvent.cs b/MetricTypes/Runtime/MetricTypes/OwnershipChangeEvent.cs index c0b013f..c64dae6 100644 --- a/MetricTypes/Runtime/MetricTypes/OwnershipChangeEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/OwnershipChangeEvent.cs @@ -18,4 +18,4 @@ public OwnershipChangeEvent(ConnectionInfo connection, NetworkObjectIdentifier n public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/RpcEvent.cs b/MetricTypes/Runtime/MetricTypes/RpcEvent.cs index 72a96bf..ac277d8 100644 --- a/MetricTypes/Runtime/MetricTypes/RpcEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/RpcEvent.cs @@ -26,9 +26,9 @@ public RpcEvent(ConnectionInfo connection, NetworkObjectIdentifier networkId, Fi public NetworkObjectIdentifier NetworkId { get; } public FixedString64Bytes Name { get; } - + public FixedString64Bytes NetworkBehaviourName { get; } public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/SceneEventMetric.cs b/MetricTypes/Runtime/MetricTypes/SceneEventMetric.cs index 5ffc4fb..3565732 100644 --- a/MetricTypes/Runtime/MetricTypes/SceneEventMetric.cs +++ b/MetricTypes/Runtime/MetricTypes/SceneEventMetric.cs @@ -28,4 +28,4 @@ public SceneEventMetric(ConnectionInfo connection, FixedString64Bytes sceneEvent public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/ServerLogEvent.cs b/MetricTypes/Runtime/MetricTypes/ServerLogEvent.cs index 388e07b..e23afd8 100644 --- a/MetricTypes/Runtime/MetricTypes/ServerLogEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/ServerLogEvent.cs @@ -27,4 +27,4 @@ internal enum LogLevel Error, None } -} \ No newline at end of file +} diff --git a/MetricTypes/Runtime/MetricTypes/StringConversionUtility.cs b/MetricTypes/Runtime/MetricTypes/StringConversionUtility.cs index 82c0295..8d0b50b 100644 --- a/MetricTypes/Runtime/MetricTypes/StringConversionUtility.cs +++ b/MetricTypes/Runtime/MetricTypes/StringConversionUtility.cs @@ -15,7 +15,7 @@ public static FixedString64Bytes ConvertToFixedString(string value) { return string.Empty; } - + if (FixedString64Bytes.UTF8MaxLengthInBytes < value.Length) { var fixedString = new FixedString64Bytes(); diff --git a/MetricTypes/Runtime/MetricTypes/UnnamedMessageEvent.cs b/MetricTypes/Runtime/MetricTypes/UnnamedMessageEvent.cs index 901f5d1..e8b95cb 100644 --- a/MetricTypes/Runtime/MetricTypes/UnnamedMessageEvent.cs +++ b/MetricTypes/Runtime/MetricTypes/UnnamedMessageEvent.cs @@ -15,4 +15,4 @@ public UnnamedMessageEvent(ConnectionInfo connection, long bytesCount) public long BytesCount { get; } } -} \ No newline at end of file +} diff --git a/MetricTypes/Tests.meta b/MetricTypes/Tests.meta deleted file mode 100644 index 8dbd1b3..0000000 --- a/MetricTypes/Tests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8b781a5ea3d213b47b02b2a3dba21865 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/MetricTypes/Tests/Editor.meta b/MetricTypes/Tests/Editor.meta deleted file mode 100644 index 21f4e70..0000000 --- a/MetricTypes/Tests/Editor.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 591a439f8d0055d46b2604ee918ffec8 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/MetricTypes/Tests/Editor/MetricTypeStringTests.cs b/MetricTypes/Tests/Editor/MetricTypeStringTests.cs deleted file mode 100644 index 92afa2b..0000000 --- a/MetricTypes/Tests/Editor/MetricTypeStringTests.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Collections; - -namespace Unity.Multiplayer.Tools.MetricTypes.Tests -{ - [Obsolete("This test class has been marked obsolete because it was not intended to be a public API", false)] - public class MetricTypeStringTests - { - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void RpcEvent_RpcName() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void RpcEvent_BehaviourName() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void NamedMessageEvent_Name() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void NetworkMessageEvent_Name() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void NetworkObjectIdentifier_Name() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void NetworkVariableEvent_VariableName() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void NetworkVariableEvent_BehaviourName() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void SceneEvent_SceneEventType() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void SceneEvent_SceneName() { } - } -} \ No newline at end of file diff --git a/MetricTypes/Tests/Editor/MetricTypeStringTests.cs.meta b/MetricTypes/Tests/Editor/MetricTypeStringTests.cs.meta deleted file mode 100644 index 9c3f8b6..0000000 --- a/MetricTypes/Tests/Editor/MetricTypeStringTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 76b013dca1e25e74cb6ddc6a031fd99c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/MetricTypes/Tests/Editor/MetricTypeStringTestsTmp.cs b/MetricTypes/Tests/Editor/MetricTypeStringTestsTmp.cs deleted file mode 100644 index 558c403..0000000 --- a/MetricTypes/Tests/Editor/MetricTypeStringTestsTmp.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Collections; - -namespace Unity.Multiplayer.Tools.MetricTypes.Tests -{ - class MetricTypeStringTestsTmp - { - const string k_ValidString = "abc"; - - static readonly string k_LongString = "65CharString65CharString65CharString65CharString65CharString65Cha"; - static readonly FixedString64Bytes k_LongStringExpected = k_LongString.Substring(0, FixedString64Bytes.UTF8MaxLengthInBytes); - - static void AssertLongStringCase( - string parameterName, - Func constructor, - Func publicAccessor) - { - T obj = default; - Assert.DoesNotThrow(() => obj = constructor(k_LongString), $"Exception thrown when constructing {typeof(T).Name} with a very long string sent to parameter {parameterName}"); - var publicValue = publicAccessor(obj); - Assert.AreEqual(k_LongStringExpected, publicValue); - } - - static void AssertEmptyStringCase( - string parameterName, - Func constructor, - Func publicAccessor) - { - T obj = default; - Assert.DoesNotThrow(()=> obj = constructor(string.Empty), $"Exception thrown when constructing {typeof(T).Name} with an empty string sent to parameter {parameterName}"); - var publicValue = publicAccessor(obj); - Assert.AreEqual(0, publicValue.Length); - } - - static void AssertNullStringCase( - string parameterName, - Func constructor, - Func publicAccessor) - { - T obj = default; - Assert.DoesNotThrow(()=> obj = constructor(null), $"Exception thrown when constructing {typeof(T).Name} with a null string sent to parameter {parameterName}"); - var publicValue = publicAccessor(obj); - Assert.AreEqual(0, publicValue.Length); - } - - static void AssertRegularStringCase( - string parameterName, - Func constructor, - Func publicAccessor) - { - const string regularString = "RegularString"; - T obj = default; - Assert.DoesNotThrow(()=> obj = constructor(regularString), $"Exception thrown when constructing {typeof(T).Name} with a regular string sent to parameter {parameterName}"); - var publicValue = publicAccessor(obj); - Assert.AreEqual(regularString, publicValue); - } - - static void AssertStringParameterCorrectness( - string parameterName, - Func constructor, - Func publicAccessor) - { - AssertLongStringCase(parameterName, constructor, publicAccessor); - AssertEmptyStringCase(parameterName, constructor, publicAccessor); - AssertNullStringCase(parameterName, constructor, publicAccessor); - AssertRegularStringCase(parameterName, constructor, publicAccessor); - } - - [Test] - public void RpcEvent_RpcName() - { - AssertStringParameterCorrectness( - nameof(RpcEvent.Name), - str => new RpcEvent(default, default, str, k_ValidString, default), - evt => evt.Name); - } - - [Test] - public void RpcEvent_BehaviourName() - { - AssertStringParameterCorrectness( - nameof(RpcEvent.NetworkBehaviourName), - str => new RpcEvent(default, default, k_ValidString, str, default), - evt => evt.NetworkBehaviourName); - } - - - [Test] - public void NamedMessageEvent_Name() - { - AssertStringParameterCorrectness( - nameof(NamedMessageEvent.Name), - str => new NamedMessageEvent(default, str, default), - evt => evt.Name); - } - - [Test] - public void NetworkMessageEvent_Name() - { - AssertStringParameterCorrectness( - nameof(NetworkMessageEvent.Name), - str => new NetworkMessageEvent(default, str, default), - evt => evt.Name); - } - - [Test] - public void NetworkObjectIdentifier_Name() - { - AssertStringParameterCorrectness( - nameof(NetworkObjectIdentifier.Name), - str => new NetworkObjectIdentifier(str, default), - obj => obj.Name); - } - - [Test] - public void NetworkVariableEvent_VariableName() - { - AssertStringParameterCorrectness( - nameof(NetworkVariableEvent.Name), - str => new NetworkVariableEvent(default, default, str, k_ValidString, default), - evt => evt.Name); - } - - [Test] - public void NetworkVariableEvent_BehaviourName() - { - AssertStringParameterCorrectness( - nameof(NetworkVariableEvent.NetworkBehaviourName), - str => new NetworkVariableEvent(default, default, k_ValidString, str, default), - evt => evt.NetworkBehaviourName); - } - - [Test] - public void SceneEvent_SceneEventType() - { - AssertStringParameterCorrectness( - nameof(SceneEventMetric.SceneEventType), - str => new SceneEventMetric(default, str, k_ValidString, default), - evt => evt.SceneEventType); - } - - [Test] - public void SceneEvent_SceneName() - { - AssertStringParameterCorrectness( - nameof(SceneEventMetric.SceneName), - str => new SceneEventMetric(default, k_ValidString, str, default), - evt => evt.SceneName); - } - } -} \ No newline at end of file diff --git a/MetricTypes/Tests/Editor/MetricTypeStringTestsTmp.cs.meta b/MetricTypes/Tests/Editor/MetricTypeStringTestsTmp.cs.meta deleted file mode 100644 index 7170797..0000000 --- a/MetricTypes/Tests/Editor/MetricTypeStringTestsTmp.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 85ceaa9936c44a448272c3041a364118 -timeCreated: 1664202618 \ No newline at end of file diff --git a/MetricTypes/Tests/Editor/Unity.Multiplayer.Tools.MetricTypes.Tests.Editor.asmdef b/MetricTypes/Tests/Editor/Unity.Multiplayer.Tools.MetricTypes.Tests.Editor.asmdef deleted file mode 100644 index 56b710b..0000000 --- a/MetricTypes/Tests/Editor/Unity.Multiplayer.Tools.MetricTypes.Tests.Editor.asmdef +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "Unity.Multiplayer.Tools.MetricTypes.Tests.Editor", - "rootNamespace": "Unity.Multiplayer.Tools.MetricTypes.Tests", - "references": [ - "Unity.Multiplayer.Tools.MetricTypes", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner", - "Unity.Collections" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "autoReferenced": false, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} \ No newline at end of file diff --git a/NetStats/CodeGen/AssemblyProcessorMain.cs b/NetStats/CodeGen/AssemblyProcessorMain.cs index 84a6486..461b080 100644 --- a/NetStats/CodeGen/AssemblyProcessorMain.cs +++ b/NetStats/CodeGen/AssemblyProcessorMain.cs @@ -1,11 +1,10 @@ using System; -using System.IO; using System.Collections.Generic; +using System.IO; using Mono.Cecil; using Mono.Cecil.Cil; using Unity.CompilationPipeline.Common.Diagnostics; using Unity.CompilationPipeline.Common.ILPostProcessing; -using UnityEngine; namespace Unity.Multiplayer.Tools.NetStats.CodeGen { diff --git a/NetStats/CodeGen/CodeGenHelpers.cs b/NetStats/CodeGen/CodeGenHelpers.cs index 901c654..54cf773 100644 --- a/NetStats/CodeGen/CodeGenHelpers.cs +++ b/NetStats/CodeGen/CodeGenHelpers.cs @@ -4,20 +4,19 @@ using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; using Unity.CompilationPipeline.Common.Diagnostics; using Unity.CompilationPipeline.Common.ILPostProcessing; -using UnityEngine; +using UnityEngine.Scripting; namespace Unity.Multiplayer.Tools.NetStats.CodeGen { static class CodeGenHelpers { const string k_NetStatsAssemblyName = "Unity.Multiplayer.Tools.NetStats"; - + public static bool AssemblyDependsOnNetStats(ICompiledAssembly compiledAssembly) => compiledAssembly.References.Any(reference => reference.Contains(k_NetStatsAssemblyName)); - + public static void AddError(this List diagnostics, string message) { diagnostics.AddError((SequencePoint)null, message); @@ -39,7 +38,7 @@ static void AddError(this List diagnostics, SequencePoint seq MessageData = $" - {message}" }); } - + public static AssemblyDefinition AssemblyDefinitionFor(ICompiledAssembly compiledAssembly, out PostProcessorAssemblyResolver assemblyResolver) { assemblyResolver = new PostProcessorAssemblyResolver(compiledAssembly); @@ -57,14 +56,14 @@ public static AssemblyDefinition AssemblyDefinitionFor(ICompiledAssembly compile return assemblyDefinition; } - + public static void InjectTypeRegistration( AssemblyDefinition assembly, ModuleDefinition module, Func> instructionsFactory) { var type = module.Types.FirstOrDefault(t => t.FullName == TypeRegistration.k_ClassName); - + MethodDefinition method; if (type != null) { @@ -83,26 +82,29 @@ public static void InjectTypeRegistration( MethodAttributes.Static, module.TypeSystem.Void); method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); - + type.Methods.Add(method); module.Types.Add(type); - - var assemblyContainsNetStatsTypesAttribute = module.ImportReference( - typeof(AssemblyRequiresTypeRegistrationAttribute) - .GetConstructor(Type.EmptyTypes)); - assembly.CustomAttributes.Add( - new CustomAttribute( - assemblyContainsNetStatsTypesAttribute)); + + var preserveConstructor = typeof(PreserveAttribute).GetConstructor(Type.EmptyTypes); + var typeRegistrationConstructor = typeof(AssemblyRequiresTypeRegistrationAttribute) + .GetConstructor(Type.EmptyTypes); + + var preserveAttribute = module.ImportReference(preserveConstructor); + var typeRegistrationAttribute = module.ImportReference(typeRegistrationConstructor); + + method.CustomAttributes.Add(new CustomAttribute(preserveAttribute)); + assembly.CustomAttributes.Add(new CustomAttribute(typeRegistrationAttribute)); } - + var processor = method.Body.GetILProcessor(); var instructions = instructionsFactory.Invoke(processor); instructions.ForEach(instruction => processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 1, instruction)); } public static GenericInstanceMethod CreateStaticGenericMethod( - ModuleDefinition module, - TypeReference staticType, + ModuleDefinition module, + TypeReference staticType, MethodReference staticGenericMethod, TypeReference genericTypeArgument) { diff --git a/NetStats/CodeGen/EventPayloadRegistrationAssemblyProcessor.cs b/NetStats/CodeGen/EventPayloadRegistrationAssemblyProcessor.cs index 92820ee..a556e5c 100644 --- a/NetStats/CodeGen/EventPayloadRegistrationAssemblyProcessor.cs +++ b/NetStats/CodeGen/EventPayloadRegistrationAssemblyProcessor.cs @@ -1,6 +1,6 @@ using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; @@ -25,13 +25,13 @@ public bool ImportReferences( typeof(EventMetricFactory).GetMethod( nameof(EventMetricFactory.RegisterType), BindingFlags.Static | BindingFlags.NonPublic)); - + return true; } - + public bool ProcessAssembly( - ICompiledAssembly compiledAssembly, - AssemblyDefinition assemblyDefinition, + ICompiledAssembly compiledAssembly, + AssemblyDefinition assemblyDefinition, ModuleDefinition mainModule, IAssemblyProcessingLogger logger) { @@ -62,7 +62,7 @@ public bool ProcessAssembly( { CodeGenHelpers.InjectTypeRegistration( assemblyDefinition, - mainModule, + mainModule, processor => { var instructions = new List(); diff --git a/NetStats/CodeGen/IAssemblyProcesor.cs b/NetStats/CodeGen/IAssemblyProcesor.cs index 43abcfc..6a06563 100644 --- a/NetStats/CodeGen/IAssemblyProcesor.cs +++ b/NetStats/CodeGen/IAssemblyProcesor.cs @@ -1,7 +1,5 @@ -using System; -using Mono.Cecil; +using Mono.Cecil; using Unity.CompilationPipeline.Common.ILPostProcessing; -using UnityEngine; namespace Unity.Multiplayer.Tools.NetStats.CodeGen { diff --git a/NetStats/CodeGen/MetricIdTypeRegistrationAssemblyProcessor.cs b/NetStats/CodeGen/MetricIdTypeRegistrationAssemblyProcessor.cs index a703804..1e8a9df 100644 --- a/NetStats/CodeGen/MetricIdTypeRegistrationAssemblyProcessor.cs +++ b/NetStats/CodeGen/MetricIdTypeRegistrationAssemblyProcessor.cs @@ -1,11 +1,10 @@ using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; using Unity.CompilationPipeline.Common.ILPostProcessing; -using UnityEngine; namespace Unity.Multiplayer.Tools.NetStats.CodeGen { @@ -28,10 +27,10 @@ public bool ImportReferences( BindingFlags.Static | BindingFlags.Public)); return true; } - + public bool ProcessAssembly( - ICompiledAssembly compiledAssembly, - AssemblyDefinition assemblyDefinition, + ICompiledAssembly compiledAssembly, + AssemblyDefinition assemblyDefinition, ModuleDefinition mainModule, IAssemblyProcessingLogger logger) { diff --git a/NetStats/Runtime/AssemblyInfo.cs b/NetStats/Runtime/AssemblyInfo.cs index e60095f..d13b4b8 100644 --- a/NetStats/Runtime/AssemblyInfo.cs +++ b/NetStats/Runtime/AssemblyInfo.cs @@ -8,20 +8,26 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricEvents")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricTestData")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricTypes")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStats.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Editor")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStats.CodeGen")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetVis.Editor.Visualization")] [assembly: InternalsVisibleTo("Unity.Netcode.TestHelpers.Runtime")] [assembly: InternalsVisibleTo("TestProject.RuntimeTests")] [assembly: InternalsVisibleTo("TestProject.ToolsIntegration.RuntimeTests")] + #if UNITY_EDITOR [assembly: InternalsVisibleTo("TestProject.EditorTests")] #endif + +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStats.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +#endif \ No newline at end of file diff --git a/NetStats/Runtime/Dispatch/IMetricDispatcher.cs b/NetStats/Runtime/Dispatch/IMetricDispatcher.cs index 76b6ced..b5ab024 100644 --- a/NetStats/Runtime/Dispatch/IMetricDispatcher.cs +++ b/NetStats/Runtime/Dispatch/IMetricDispatcher.cs @@ -8,4 +8,4 @@ interface IMetricDispatcher void Dispatch(); } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Dispatch/IMetricObserver.cs b/NetStats/Runtime/Dispatch/IMetricObserver.cs index 1de4ff4..01b9b49 100644 --- a/NetStats/Runtime/Dispatch/IMetricObserver.cs +++ b/NetStats/Runtime/Dispatch/IMetricObserver.cs @@ -4,4 +4,4 @@ interface IMetricObserver { void Observe(MetricCollection collection); } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Dispatch/MetricCollectionBuilder.cs b/NetStats/Runtime/Dispatch/MetricCollectionBuilder.cs index 76768e6..2f393bf 100644 --- a/NetStats/Runtime/Dispatch/MetricCollectionBuilder.cs +++ b/NetStats/Runtime/Dispatch/MetricCollectionBuilder.cs @@ -50,4 +50,4 @@ public MetricCollection Build() new ReadOnlyDictionary(m_PayloadEvents.ToDictionary(x => x.Id, x => x))); } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Dispatch/MetricDispatcher.cs b/NetStats/Runtime/Dispatch/MetricDispatcher.cs index e4e9b40..de81fb8 100644 --- a/NetStats/Runtime/Dispatch/MetricDispatcher.cs +++ b/NetStats/Runtime/Dispatch/MetricDispatcher.cs @@ -69,4 +69,4 @@ public void Dispatch() } } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Dispatch/MetricDispatcherBuilder.cs b/NetStats/Runtime/Dispatch/MetricDispatcherBuilder.cs index 6042163..0e397bf 100644 --- a/NetStats/Runtime/Dispatch/MetricDispatcherBuilder.cs +++ b/NetStats/Runtime/Dispatch/MetricDispatcherBuilder.cs @@ -75,4 +75,4 @@ public IMetricDispatcher Build() m_PayloadEvents.Values.ToList()); } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/Counter.cs b/NetStats/Runtime/Metrics/Counter.cs index 6e0b88e..5a12378 100644 --- a/NetStats/Runtime/Metrics/Counter.cs +++ b/NetStats/Runtime/Metrics/Counter.cs @@ -14,7 +14,7 @@ public void Increment(long increment = 1) { Value += increment; } - + public override MetricContainerType MetricContainerType => MetricContainerType.Counter; } } diff --git a/NetStats/Runtime/Metrics/EventMetricFactory.cs b/NetStats/Runtime/Metrics/EventMetricFactory.cs index 3a2a7a8..1fabc37 100644 --- a/NetStats/Runtime/Metrics/EventMetricFactory.cs +++ b/NetStats/Runtime/Metrics/EventMetricFactory.cs @@ -29,7 +29,7 @@ static EventMetricFactory() { TypeRegistration.RunIfNeeded(); } - + internal static void RegisterType() where T : unmanaged { if(k_TypeNames.ContainsKey(typeof(T))) diff --git a/NetStats/Runtime/Metrics/IEventMetric.cs b/NetStats/Runtime/Metrics/IEventMetric.cs index b632a4e..4bd0063 100644 --- a/NetStats/Runtime/Metrics/IEventMetric.cs +++ b/NetStats/Runtime/Metrics/IEventMetric.cs @@ -34,4 +34,4 @@ interface IEventMetric : IEventMetric { IReadOnlyList Values { get; } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/IMetricFactory.cs b/NetStats/Runtime/Metrics/IMetricFactory.cs index 2d55b20..ba8ba4e 100644 --- a/NetStats/Runtime/Metrics/IMetricFactory.cs +++ b/NetStats/Runtime/Metrics/IMetricFactory.cs @@ -1,6 +1,4 @@ -using System; - -namespace Unity.Multiplayer.Tools.NetStats +namespace Unity.Multiplayer.Tools.NetStats { interface IMetricFactory { diff --git a/NetStats/Runtime/Metrics/IResettable.cs b/NetStats/Runtime/Metrics/IResettable.cs index 18816f3..80ae8f1 100644 --- a/NetStats/Runtime/Metrics/IResettable.cs +++ b/NetStats/Runtime/Metrics/IResettable.cs @@ -6,4 +6,4 @@ interface IResettable void Reset(); } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/MetricId/AssemblyRequiresTypeRegistrationAttribute.cs b/NetStats/Runtime/Metrics/MetricId/AssemblyRequiresTypeRegistrationAttribute.cs index e2f209b..214aa6a 100644 --- a/NetStats/Runtime/Metrics/MetricId/AssemblyRequiresTypeRegistrationAttribute.cs +++ b/NetStats/Runtime/Metrics/MetricId/AssemblyRequiresTypeRegistrationAttribute.cs @@ -11,4 +11,4 @@ namespace Unity.Multiplayer.Tools.NetStats public class AssemblyRequiresTypeRegistrationAttribute : Attribute { } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/MetricId/MetricId.cs b/NetStats/Runtime/Metrics/MetricId/MetricId.cs index 1ea73d4..1a83c1d 100644 --- a/NetStats/Runtime/Metrics/MetricId/MetricId.cs +++ b/NetStats/Runtime/Metrics/MetricId/MetricId.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using JetBrains.Annotations; +using Unity.Multiplayer.Tools.Common; using UnityEngine; namespace Unity.Multiplayer.Tools.NetStats @@ -53,10 +54,10 @@ internal MetricId(Type enumType, int enumValue) /// An enum with the /// public static MetricId Create(T value) - where T: struct, IConvertible + where T : unmanaged, Enum { var enumType = typeof(T); - var enumValue = value.ToInt32(CultureInfo.InvariantCulture); + var enumValue = CheckedEnumUtils.CheckedCastToUnderlying(value); return new MetricId(enumType, enumValue); } @@ -86,10 +87,7 @@ public override bool Equals(object obj) /// Serves as the default hash function. /// /// A hash code for the current . - public override int GetHashCode() - { - return 173 * TypeIndex + 13 * EnumValue; - } + public override int GetHashCode() => HashCode.Combine(TypeIndex, EnumValue); /// /// Returns a string that represents the current . diff --git a/NetStats/Runtime/Metrics/MetricId/MetricKind.cs b/NetStats/Runtime/Metrics/MetricId/MetricKind.cs index 1452ce3..3f2997c 100644 --- a/NetStats/Runtime/Metrics/MetricId/MetricKind.cs +++ b/NetStats/Runtime/Metrics/MetricId/MetricKind.cs @@ -18,4 +18,4 @@ public enum MetricKind /// Gauge, } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/MetricId/MetricMetadataAttribute.cs b/NetStats/Runtime/Metrics/MetricId/MetricMetadataAttribute.cs index c882674..e73d445 100644 --- a/NetStats/Runtime/Metrics/MetricId/MetricMetadataAttribute.cs +++ b/NetStats/Runtime/Metrics/MetricId/MetricMetadataAttribute.cs @@ -1,5 +1,4 @@ using System; -using JetBrains.Annotations; namespace Unity.Multiplayer.Tools.NetStats { @@ -33,4 +32,4 @@ public class MetricMetadataAttribute : Attribute /// public bool DisplayAsPercentage { get; set; } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/MetricId/MetricTypeEnumAttribute.cs b/NetStats/Runtime/Metrics/MetricId/MetricTypeEnumAttribute.cs index 8598b53..46a1fde 100644 --- a/NetStats/Runtime/Metrics/MetricId/MetricTypeEnumAttribute.cs +++ b/NetStats/Runtime/Metrics/MetricId/MetricTypeEnumAttribute.cs @@ -24,4 +24,4 @@ public class MetricTypeEnumAttribute : Attribute /// from the inspector that are only intended for internal MP Tools testing. [AttributeUsage(AttributeTargets.Enum)] internal class MetricTypeEnumHideInInspectorAttribute : Attribute {} -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/MetricId/MetricTypeSortPriorityAttribue.cs b/NetStats/Runtime/Metrics/MetricId/MetricTypeSortPriorityAttribue.cs index 862594b..be61d70 100644 --- a/NetStats/Runtime/Metrics/MetricId/MetricTypeSortPriorityAttribue.cs +++ b/NetStats/Runtime/Metrics/MetricId/MetricTypeSortPriorityAttribue.cs @@ -19,4 +19,4 @@ internal class MetricTypeSortPriorityAttribute : Attribute { public SortPriority SortPriority { get; set; } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Metrics/MetricIdTypeLibrary.cs b/NetStats/Runtime/Metrics/MetricIdTypeLibrary.cs index bfa57aa..ba64756 100644 --- a/NetStats/Runtime/Metrics/MetricIdTypeLibrary.cs +++ b/NetStats/Runtime/Metrics/MetricIdTypeLibrary.cs @@ -186,7 +186,7 @@ internal static bool GetDisplayAsPercentage(int typeIndex, int enumValue) static T GetEnumMetadata(List data, int typeIndex, int enumValue) { - if (typeIndex > k_EnumValues.Count) + if (typeIndex >= k_EnumValues.Count) { return default(T); } diff --git a/NetStats/Runtime/Metrics/TypeRegistration.cs b/NetStats/Runtime/Metrics/TypeRegistration.cs index 4317c71..8e6afa2 100644 --- a/NetStats/Runtime/Metrics/TypeRegistration.cs +++ b/NetStats/Runtime/Metrics/TypeRegistration.cs @@ -46,4 +46,4 @@ public static void RunIfNeeded() } } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Serialization/Cloned/BitWriter.cs b/NetStats/Runtime/Serialization/Cloned/BitWriter.cs index f515f82..1050753 100644 --- a/NetStats/Runtime/Serialization/Cloned/BitWriter.cs +++ b/NetStats/Runtime/Serialization/Cloned/BitWriter.cs @@ -60,7 +60,7 @@ public void Dispose() /// When you know you will be writing multiple fields back-to-back and you know the total size, /// you can call TryBeginWriteBits() once on the total size, and then follow it with calls to /// WriteBit() or WriteBits(). - /// + /// /// Bitwise write operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using TryBeginWriteBits(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following diff --git a/NetStats/Runtime/Serialization/Cloned/BufferSerializer.cs b/NetStats/Runtime/Serialization/Cloned/BufferSerializer.cs index 36fd85f..cfc1944 100644 --- a/NetStats/Runtime/Serialization/Cloned/BufferSerializer.cs +++ b/NetStats/Runtime/Serialization/Cloned/BufferSerializer.cs @@ -2,7 +2,7 @@ namespace Unity.Multiplayer.Tools.NetStats { /// /// Two-way serializer wrapping FastBufferReader or FastBufferWriter. - /// + /// /// Implemented as a ref struct for two reasons: /// 1. The BufferSerializer cannot outlive the FBR/FBW it wraps or using it will cause a crash /// 2. The BufferSerializer must always be passed by reference and can't be copied @@ -60,7 +60,7 @@ public FastBufferWriter GetFastBufferWriter() /// /// Serialize an INetworkSerializable - /// + /// /// Throws OverflowException if the end of the buffer has been reached. /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. /// @@ -74,7 +74,7 @@ public FastBufferWriter GetFastBufferWriter() /// Serialize a string. /// /// Note: Will ALWAYS allocate a new string when reading. - /// + /// /// Throws OverflowException if the end of the buffer has been reached. /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. /// @@ -94,11 +94,11 @@ public void SerializeValue(ref string s, bool oneByteChars = false) /// Note: Will ALWAYS allocate a new array when reading. /// If you have a statically-sized array that you know is large enough, it's recommended to /// serialize the size yourself and iterate serializing array members. - /// + /// /// (This is because C# doesn't allow setting an array's length value, so deserializing /// into an existing array of larger size would result in an array that doesn't have as many values /// as its Length indicates it should.) - /// + /// /// Throws OverflowException if the end of the buffer has been reached. /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. /// @@ -110,7 +110,7 @@ public void SerializeValue(ref T[] array) where T : unmanaged /// /// Serialize a single byte - /// + /// /// Throws OverflowException if the end of the buffer has been reached. /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. /// @@ -123,7 +123,7 @@ public void SerializeValue(ref byte value) /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. - /// + /// /// Throws OverflowException if the end of the buffer has been reached. /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. /// @@ -139,7 +139,7 @@ public void SerializeValue(ref T value) where T : unmanaged /// you can call PreCheck() once on the total size, and then follow it with calls to /// SerializeValuePreChecked() for faster serialization. Write buffers will grow during PreCheck() /// if needed. - /// + /// /// PreChecked serialization operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using PreCheck(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using PreCheck is to avoid bounds checking in the following @@ -157,7 +157,7 @@ public bool PreCheck(int amount) /// /// Serialize a string. - /// + /// /// Note: Will ALWAYS allocate a new string when reading. /// /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only @@ -180,7 +180,7 @@ public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) /// Note: Will ALWAYS allocate a new array when reading. /// If you have a statically-sized array that you know is large enough, it's recommended to /// serialize the size yourself and iterate serializing array members. - /// + /// /// (This is because C# doesn't allow setting an array's length value, so deserializing /// into an existing array of larger size would result in an array that doesn't have as many values /// as its Length indicates it should.) diff --git a/NetStats/Runtime/Serialization/Cloned/FastBufferReader.cs b/NetStats/Runtime/Serialization/Cloned/FastBufferReader.cs index 4b8953d..79f8223 100644 --- a/NetStats/Runtime/Serialization/Cloned/FastBufferReader.cs +++ b/NetStats/Runtime/Serialization/Cloned/FastBufferReader.cs @@ -76,7 +76,7 @@ internal unsafe void CommitBitwiseReads(int amount) /// /// Create a FastBufferReader from a NativeArray. - /// + /// /// A new buffer will be created using the given allocator and the value will be copied in. /// FastBufferReader will then own the data. /// @@ -98,7 +98,7 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in /// /// Create a FastBufferReader from an ArraySegment. - /// + /// /// A new buffer will be created using the given allocator and the value will be copied in. /// FastBufferReader will then own the data. /// @@ -123,7 +123,7 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i /// /// Create a FastBufferReader from an existing byte array. - /// + /// /// A new buffer will be created using the given allocator and the value will be copied in. /// FastBufferReader will then own the data. /// @@ -148,7 +148,7 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = /// /// Create a FastBufferReader from an existing byte buffer. - /// + /// /// A new buffer will be created using the given allocator and the value will be copied in. /// FastBufferReader will then own the data. /// @@ -170,7 +170,7 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in /// /// Create a FastBufferReader from a FastBufferWriter. - /// + /// /// A new buffer will be created using the given allocator and the value will be copied in. /// FastBufferReader will then own the data. /// @@ -250,7 +250,7 @@ public unsafe BitReader EnterBitwiseContext() /// When you know you will be reading multiple fields back-to-back and you know the total size, /// you can call TryBeginRead() once on the total size, and then follow it with calls to /// ReadValue() instead of ReadValueSafe() for faster serialization. - /// + /// /// Unsafe read operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following @@ -284,7 +284,7 @@ public unsafe bool TryBeginRead(int bytes) /// When you know you will be reading multiple fields back-to-back and you know the total size, /// you can call TryBeginRead() once on the total size, and then follow it with calls to /// ReadValue() instead of ReadValueSafe() for faster serialization. - /// + /// /// Unsafe read operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following diff --git a/NetStats/Runtime/Serialization/Cloned/FastBufferWriter.cs b/NetStats/Runtime/Serialization/Cloned/FastBufferWriter.cs index f2a52a7..d3b1d7e 100644 --- a/NetStats/Runtime/Serialization/Cloned/FastBufferWriter.cs +++ b/NetStats/Runtime/Serialization/Cloned/FastBufferWriter.cs @@ -201,7 +201,7 @@ internal unsafe void Grow(int additionalSizeRequired) /// When you know you will be writing multiple fields back-to-back and you know the total size, /// you can call TryBeginWrite() once on the total size, and then follow it with calls to /// WriteValue() instead of WriteValueSafe() for faster serialization. - /// + /// /// Unsafe write operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following @@ -247,7 +247,7 @@ public unsafe bool TryBeginWrite(int bytes) /// When you know you will be writing multiple fields back-to-back and you know the total size, /// you can call TryBeginWrite() once on the total size, and then follow it with calls to /// WriteValue() instead of WriteValueSafe() for faster serialization. - /// + /// /// Unsafe write operations will throw OverflowException in editor and development builds if you /// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following diff --git a/NetStats/Runtime/Units/BaseUnit.cs b/NetStats/Runtime/Units/BaseUnit.cs index 244f9f2..4cf549b 100644 --- a/NetStats/Runtime/Units/BaseUnit.cs +++ b/NetStats/Runtime/Units/BaseUnit.cs @@ -26,4 +26,4 @@ public static string GetSymbol(this BaseUnit unit) } } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Units/BaseUnits.cs b/NetStats/Runtime/Units/BaseUnits.cs index df93d7c..02d232e 100644 --- a/NetStats/Runtime/Units/BaseUnits.cs +++ b/NetStats/Runtime/Units/BaseUnits.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using UnityEngine; namespace Unity.Multiplayer.Tools.NetStats { @@ -35,18 +33,7 @@ public override bool Equals(object obj) return obj is BaseUnits other && Equals(other); } - public override int GetHashCode() - { - // Note: System.Hashcode functionality isn't available in Unity < 2021.2, - // so this custom hashing can be replaced when versions < 2021.2 no longer - // need to be supported - - // By virtue of the fact that all four fields combined are less than 32 bits - // we can pack them all in a 32 bit HashCode, and don't even need to hash. - return - ((byte)BytesExponent << 8) | - (byte)SecondsExponent; - } + public override int GetHashCode() => HashCode.Combine(BytesExponent, SecondsExponent); internal sbyte GetExponent(BaseUnit unit) { @@ -124,4 +111,4 @@ internal string DisplayString public override string ToString() => DisplayString; } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Units/MetricPrefix.cs b/NetStats/Runtime/Units/MetricPrefix.cs index 30b018c..a1aa996 100644 --- a/NetStats/Runtime/Units/MetricPrefix.cs +++ b/NetStats/Runtime/Units/MetricPrefix.cs @@ -91,4 +91,4 @@ public static float GetValueFloat(this MetricPrefix prefix) } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Units/UnitExtensions.cs b/NetStats/Runtime/Units/UnitExtensions.cs index 2b78853..bf2abe7 100644 --- a/NetStats/Runtime/Units/UnitExtensions.cs +++ b/NetStats/Runtime/Units/UnitExtensions.cs @@ -28,4 +28,4 @@ internal static BaseUnits GetBaseUnits(this Units units) } } } -} \ No newline at end of file +} diff --git a/NetStats/Runtime/Units/Units.cs b/NetStats/Runtime/Units/Units.cs index 1972da1..2a5dd55 100644 --- a/NetStats/Runtime/Units/Units.cs +++ b/NetStats/Runtime/Units/Units.cs @@ -28,4 +28,4 @@ public enum Units /// Hertz, } -} \ No newline at end of file +} diff --git a/NetStats/Tests.meta b/NetStats/Tests.meta deleted file mode 100644 index 75a10b6..0000000 --- a/NetStats/Tests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 9b3fbfca23ae97a4fbb8d70b095f5885 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor.meta b/NetStats/Tests/Editor.meta deleted file mode 100644 index 8299c50..0000000 --- a/NetStats/Tests/Editor.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4c5ee8c3cb6e11842b908f2ad664c644 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/AssemblyInfo.cs b/NetStats/Tests/Editor/AssemblyInfo.cs deleted file mode 100644 index b50da24..0000000 --- a/NetStats/Tests/Editor/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkProfiler.Tests.Editor")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] \ No newline at end of file diff --git a/NetStats/Tests/Editor/AssemblyInfo.cs.meta b/NetStats/Tests/Editor/AssemblyInfo.cs.meta deleted file mode 100644 index b8e49df..0000000 --- a/NetStats/Tests/Editor/AssemblyInfo.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c833092833eb49ab9b99b4f2e1162c53 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Dispatch.meta b/NetStats/Tests/Editor/Dispatch.meta deleted file mode 100644 index 9132202..0000000 --- a/NetStats/Tests/Editor/Dispatch.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4c44451c1e75e804f82b15b8da8faa85 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Dispatch/MetricDispatcherTests.cs b/NetStats/Tests/Editor/Dispatch/MetricDispatcherTests.cs deleted file mode 100644 index 186c310..0000000 --- a/NetStats/Tests/Editor/Dispatch/MetricDispatcherTests.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; -using UnityEngine; -using UnityEngine.TestTools; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - sealed class MetricDispatcherTests - { - readonly MetricId MetricId = MetricId.Create(DirectedMetricType.RpcSent); - - [Test] - public void Dispatch_WhenCounterIsRegistered_DispatchesCounterValueToObservers() - { - // Arrange - var counter = new Counter(MetricId); - var metricDispatcher = new MetricDispatcherBuilder() - .WithCounters(counter) - .Build(); - - metricDispatcher.RegisterObserver(new TestObserver(snapshot => - { - // Assert - var found = snapshot.TryGetCounter(MetricId, out var metric); - Assert.IsTrue(found); - Assert.NotNull(metric); - Assert.AreEqual(10, metric.Value); - })); - - counter.Increment(10); - - // Act - metricDispatcher.Dispatch(); - } - - [Test] - public void Dispatch_WhenGaugeIsRegistered_DispatchesGaugeValueToObservers() - { - // Arrange - var gauge = new Gauge(MetricId); - var metricDispatcher = new MetricDispatcherBuilder() - .WithGauges(gauge) - .Build(); - - metricDispatcher.RegisterObserver(new TestObserver(snapshot => - { - // Assert - var found = snapshot.TryGetGauge(MetricId, out var metric); - Assert.IsTrue(found); - Assert.NotNull(metric); - Assert.AreEqual(10, metric.Value); - })); - - gauge.Set(10); - - // Act - metricDispatcher.Dispatch(); - } - - [Test] - public void Dispatch_WhenTimerIsRegistered_DispatchesTimerValueToObservers() - { - // Arrange - var timer = new Timer(MetricId); - var metricDispatcher = new MetricDispatcherBuilder() - .WithTimers(timer) - .Build(); - - metricDispatcher.RegisterObserver(new TestObserver(snapshot => - { - // Assert - var found = snapshot.TryGetTimer(MetricId, out var metric); - Assert.IsTrue(found); - Assert.NotNull(metric); - Assert.AreEqual(TimeSpan.FromHours(1), metric.Value); - })); - - timer.Set(TimeSpan.FromHours(1)); - - // Act - metricDispatcher.Dispatch(); - } - - [Test] - public void Dispatch_WhenEventIsRegistered_DispatchesEventValueToObservers() - { - // Arrange - var metricEvent = new EventMetric(MetricId); - var metricDispatcher = new MetricDispatcherBuilder() - .WithMetricEvents(metricEvent) - .Build(); - - var value = 1234; - - metricDispatcher.RegisterObserver(new TestObserver(snapshot => - { - // Assert - var found = snapshot.TryGetEvent(MetricId, out var metric); - Assert.IsTrue(found); - Assert.NotNull(metric); - Assert.NotNull(metric.Values); - Assert.AreEqual(1, metric.Values.Count); - Assert.AreEqual(value, metric.Values.First()); - })); - - metricEvent.Mark(value); - - // Act - metricDispatcher.Dispatch(); - } - - [Test] - public void Dispatch_WhenTypedEventIsRegistered_DispatchesEventValueToObservers() - { - // Arrange - var metricEvent = new EventMetric(MetricId); - var metricDispatcher = new MetricDispatcherBuilder() - .WithMetricEvents(metricEvent) - .Build(); - - var value = 1234; - - metricDispatcher.RegisterObserver(new TestObserver(snapshot => - { - // Assert - var found = snapshot.TryGetEvent(MetricId, out var metric); - Assert.IsTrue(found); - Assert.NotNull(metric); - Assert.NotNull(metric.Values); - Assert.AreEqual(1, metric.Values.Count); - Assert.NotNull(metric.Values.First()); - Assert.AreEqual(value, metric.Values.First().Value); - })); - - metricEvent.Mark(new TestMetricEvent(value)); - - // Act - metricDispatcher.Dispatch(); - } - - [Test] - public void Dispatch_WhenMetricIsResetOnDispatch_ResetsMetric() - { - // Arrange - var counter = new Counter(MetricId) - { - ShouldResetOnDispatch = true, - }; - - var metricDispatcher = new MetricDispatcherBuilder() - .WithCounters(counter) - .Build(); - - counter.Increment(10); - - // Act - metricDispatcher.Dispatch(); - - // Assert - Assert.AreEqual(0, counter.Value); - } - - [Test] - public void Dispatch_WhenMetricIsNotResetOnDispatch_DoesNotResetMetric() - { - // Arrange - var counter = new Counter(MetricId) - { - ShouldResetOnDispatch = false, - }; - - var metricDispatcher = new MetricDispatcherBuilder() - .WithCounters(counter) - .Build(); - - counter.Increment(10); - - // Act - metricDispatcher.Dispatch(); - - // Assert - Assert.AreEqual(10, counter.Value); - } - - [Test] - public void Dispatch_WhenMetricWentOverLimit_LogsWarning() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.RpcReceived); - var metric = new EventMetric(id) - { - MaxNumberOfValues = 0, - }; - - var metricDispatcher = new MetricDispatcherBuilder() - .WithMetricEvents(metric) - .Build(); - - metric.Mark(1234); - - // Act & Assert - Assert.True(metric.WentOverLimit()); - const string k_RegexWhitespaceZeroOrMore = "\\s*"; - LogAssert.Expect( - LogType.Warning, - new Regex(Regex.Escape(metric.WentOverLimitMessage()) + k_RegexWhitespaceZeroOrMore)); - metricDispatcher.Dispatch(); - } - - [Serializable] - public struct TestMetricEvent - { - public TestMetricEvent(int value) - { - Value = value; - } - - public int Value { get; } - } - - class TestObserver : IMetricObserver - { - private readonly Action m_Assertion; - - public TestObserver(Action assertion) - { - m_Assertion = assertion; - } - - public void Observe(MetricCollection collection) - { - m_Assertion.Invoke(collection); - } - } - } -} diff --git a/NetStats/Tests/Editor/Dispatch/MetricDispatcherTests.cs.meta b/NetStats/Tests/Editor/Dispatch/MetricDispatcherTests.cs.meta deleted file mode 100644 index d77f081..0000000 --- a/NetStats/Tests/Editor/Dispatch/MetricDispatcherTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 04398912741cb7e418840149943bdd37 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics.meta b/NetStats/Tests/Editor/Metrics.meta deleted file mode 100644 index e33858b..0000000 --- a/NetStats/Tests/Editor/Metrics.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 9defd396e5e83944e8a3944472859b3c -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/CounterTests.cs b/NetStats/Tests/Editor/Metrics/CounterTests.cs deleted file mode 100644 index 637f999..0000000 --- a/NetStats/Tests/Editor/Metrics/CounterTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - sealed class CounterTests - { - [Test] - public void Increment_Always_IncrementsUnderlyingValue() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.RpcSent); - var counter = new Counter(id, 10); - - // Act - counter.Increment(); - - // Assert - Assert.AreEqual(id, counter.Id); - Assert.AreEqual(11, counter.Value); - } - - [TestCase(10, 1, 11)] - [TestCase(10, -1, 9)] - public void Increment_Always_IncrementsUnderlyingValueBySpecifiedAmount(long initial, long increment, long expected) - { - // Arrange - var id = MetricId.Create(DirectedMetricType.UnnamedMessageSent); - var counter = new Counter(id, initial); - - // Act - counter.Increment(increment); - - // Assert - Assert.AreEqual(id, counter.Id); - Assert.AreEqual(expected, counter.Value); - } - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/CounterTests.cs.meta b/NetStats/Tests/Editor/Metrics/CounterTests.cs.meta deleted file mode 100644 index 324661f..0000000 --- a/NetStats/Tests/Editor/Metrics/CounterTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 61937e9a36f143f44917366de4aea624 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/GaugeTests.cs b/NetStats/Tests/Editor/Metrics/GaugeTests.cs deleted file mode 100644 index 34e1208..0000000 --- a/NetStats/Tests/Editor/Metrics/GaugeTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - sealed class GaugeTests - { - [Test] - public void Set_Always_SetsUnderlyingValueToSpecifiedAmount() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.NetworkVariableDeltaReceived); - var gauge = new Gauge(id); - var value = new Random().NextDouble(); - - // Act - gauge.Set(value); - - // Assert - Assert.AreEqual(id, gauge.Id); - Assert.AreEqual(value, gauge.Value); - } - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/GaugeTests.cs.meta b/NetStats/Tests/Editor/Metrics/GaugeTests.cs.meta deleted file mode 100644 index 27cd9b8..0000000 --- a/NetStats/Tests/Editor/Metrics/GaugeTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b4cd75d1b76df5b488e8f0b8aae55bb2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/MetricCollectionTestUtility.cs b/NetStats/Tests/Editor/Metrics/MetricCollectionTestUtility.cs deleted file mode 100644 index 6de7d84..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricCollectionTestUtility.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - static class MetricCollectionTestUtility - { - public static MetricCollection ConstructFromMetrics( - IReadOnlyCollection metrics, - ulong localConnectionId = ulong.MaxValue) - { - static MetricId ById(IMetric metric) => metric.Id; - var metricCollection = new MetricCollection( - metrics.OfType>().ToDictionary(ById), - metrics.OfType>().ToDictionary(ById), - metrics.OfType>().ToDictionary(ById), - metrics.OfType().ToDictionary(ById)); - metricCollection.ConnectionId = localConnectionId; - - return metricCollection; - } - } -} diff --git a/NetStats/Tests/Editor/Metrics/MetricCollectionTestUtility.cs.meta b/NetStats/Tests/Editor/Metrics/MetricCollectionTestUtility.cs.meta deleted file mode 100644 index be0072a..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricCollectionTestUtility.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 7b69805fa0914494b3e494f0507fbed6 -timeCreated: 1630598431 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/MetricEventTests.cs b/NetStats/Tests/Editor/Metrics/MetricEventTests.cs deleted file mode 100644 index 4108723..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricEventTests.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - sealed class MetricEventTests - { - const int k_MetricValue1 = 1234; - const int k_MetricValue2 = 5678; - - [TestCase(4596, 9)] - [TestCase(-1, 7)] - [TestCase(347, 11)] - public void Constructor_WhenTypeIndexIsOutOfRange_ThrowsException(int typeIndex, int value) - { - Assert.Throws(() => new EventMetric( - new MetricId(typeIndex, value))); - } - - [Test] - public void ResetStringMetricEvent_Always_ClearsUnderlyingCollection() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.RpcReceived); - var metric = new EventMetric(id); - metric.Mark(k_MetricValue1); - - // Act - metric.Reset(); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.IsEmpty(metric.Values); - } - - [Test] - public void MarkStringMetricEvent_Always_AddsValueToUnderlyingCollection() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.RpcReceived); - var metric = new EventMetric(id); - - // Act - metric.Mark(k_MetricValue1); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.AreEqual(1, metric.Values.Count); - Assert.AreEqual(k_MetricValue1, metric.Values.FirstOrDefault()); - } - - [Test] - public void MarkStringMetricEvent_WithExistingItemsInCollection_KeepsUnderlyingItemsInOrder() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.SceneEventSent); - var metric = new EventMetric(id); - var values = new[] { 1, 2, 3, 4, 5 }; - - // Act - foreach (var value in values) - { - metric.Mark(value); - } - - // Assert - Assert.AreEqual(id, metric.Id); - CollectionAssert.AreEquivalent(values, metric.Values); - } - - [Test] - public void ResetCustomStructMetricEvent_Always_ClearsUnderlyingCollection() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.ObjectSpawnedReceived); - var metric = new EventMetric(id); - metric.Mark(new CustomEvent()); - - // Act - metric.Reset(); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.IsEmpty(metric.Values); - } - - [Test] - public void MarkCustomStructMetricEvent_Always_AddsValueToUnderlyingCollection() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.TotalBytesSent); - var metric = new EventMetric(id); - var value = new CustomEvent(k_MetricValue1); - - // Act - metric.Mark(value); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.AreEqual(1, metric.Values.Count); - Assert.AreEqual(value, metric.Values.FirstOrDefault()); - } - - [Test] - public void MarkCustomStructMetricEvent_WithExistingItemsInCollection_KeepsUnderlyingItemsInOrder() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.ObjectDestroyedReceived); - var metric = new EventMetric(id); - var values = new[] { new CustomEvent(k_MetricValue1), new CustomEvent(k_MetricValue2) }; - - // Act - foreach (var value in values) - { - metric.Mark(value); - } - - // Assert - Assert.AreEqual(id, metric.Id); - CollectionAssert.AreEquivalent(values, metric.Values); - } - - [Test] - public void MarkMetricEvent_WhenNumberOfValuesDoesNotGoOverLimit_DoesNotSetWentOverLimitFlag() - { - // Arrange - var metricLimit = 10; - var id = MetricId.Create(DirectedMetricType.NetworkVariableDeltaReceived); - var metric = new EventMetric(id) - { - MaxNumberOfValues = metricLimit, - }; - - // Act - foreach (var _ in Enumerable.Range(0, (int)metricLimit)) - { - metric.Mark(k_MetricValue1); - } - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.False(metric.WentOverLimit()); - } - - [Test] - public void MarkMetricEvent_WhenNumberOfValuesGoesOverLimit_SetsWentOverLimitFlag() - { - // Arrange - var metricLimit = 10; - var id = MetricId.Create(DirectedMetricType.RpcSent); - var metric = new EventMetric(id) - { - MaxNumberOfValues = metricLimit, - }; - - // Act - foreach (var _ in Enumerable.Range(0, (int)metricLimit + 1)) - { - metric.Mark(k_MetricValue1); - } - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.True(metric.WentOverLimit()); - } - - [Test] - public void Reset_Always_ResetsWentOverLimitFlag() - { - // Arrange - var metricLimit = 10; - var id = MetricId.Create(DirectedMetricType.TotalBytesReceived); - var metric = new EventMetric(id) - { - MaxNumberOfValues = metricLimit, - }; - - // Act - foreach (var _ in Enumerable.Range(0, (int)metricLimit + 1)) - { - metric.Mark(k_MetricValue1); - } - - Assert.True(metric.WentOverLimit()); - - // Act - metric.Reset(); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.False(metric.WentOverLimit()); - } - - [Serializable] - public struct CustomEvent - { - public CustomEvent(int id) - { - Id = id; - } - - public int Id { get; } - } - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/MetricEventTests.cs.meta b/NetStats/Tests/Editor/Metrics/MetricEventTests.cs.meta deleted file mode 100644 index 2886411..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricEventTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 29c1dcd8ed398e843b0aa4b92f5a1358 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/MetricIdTests.cs b/NetStats/Tests/Editor/Metrics/MetricIdTests.cs deleted file mode 100644 index 56eb9b0..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricIdTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using NUnit.Framework; - -using Unity.Multiplayer.Tools.MetricTypes; -using UnityEngine; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - internal class MetricIdTests - { - [TestCase(TestMetric.BytesCounter)] - [TestCase(TestMetric.BytesGauge)] - [TestCase(TestMetric.SecondsCounter)] - [TestCase(TestMetric.SecondsGauge)] - [TestCase(TestMetric.UnitlessCounter)] - [TestCase(TestMetric.UnitlessGauge)] - public void GivenMetricId_WhenToString_ReturnsCorrectValue(TestMetric value) - { - var metricId = MetricId.Create(value); - - var stringValue = metricId.ToString(); - - Assert.AreEqual(typeof(TestMetric), metricId.EnumType); - Assert.AreEqual((int)value, metricId.EnumValue); - Assert.AreEqual(value.ToString(), stringValue); - } - } -} diff --git a/NetStats/Tests/Editor/Metrics/MetricIdTests.cs.meta b/NetStats/Tests/Editor/Metrics/MetricIdTests.cs.meta deleted file mode 100644 index 9a9036c..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricIdTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f7c41f068d16449aebb48ec042292bcb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTests.cs b/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTests.cs deleted file mode 100644 index b49946a..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - [Obsolete("This test class has been marked obsolete because it was not intended to be a public API", false)] - public class MetricIdTypeLibraryTests - { - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void VerifyTestMetricsIncludedInLibrary() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void VerifyCorrectDisplayName() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void VerifyCorrectUnits() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void VerifyCorrectMetricKind() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void VerifyCorrectName() { } - } -} diff --git a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTests.cs.meta b/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTests.cs.meta deleted file mode 100644 index a1306d3..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 1c498eb15d44412c8bbf2249e6ec60fe -timeCreated: 1641245628 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTestsTmp.cs b/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTestsTmp.cs deleted file mode 100644 index 15d2901..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTestsTmp.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - class MetricIdTypeLibraryTestsTmp - { - [Test] - // This test validates that the test metric is included in the type library - // which is an outcome of the Assembly processing code, - // which is difficult to test on its own so we just verify that the outcome is correct - // If this test fails, the problem is most likely in `MetricIdTypeRegistrationAssemblyProcessor.cs` - public void VerifyTestMetricsIncludedInLibrary() - { - Assert.IsTrue(MetricIdTypeLibrary.ContainsType(typeof(TestMetricId))); - } - - [Test] - public void VerifyCorrectDisplayName() - { - Assert.AreEqual( - MetricIdTypeLibrary.GetEnumDisplayName( - MetricIdTypeLibrary.GetTypeIndex(typeof(TestMetricId)), - TestMetricIdConstants.Test1Value), - TestMetricIdConstants.Test1DisplayName); - } - - [Test] - public void VerifyCorrectUnits() - { - Assert.AreEqual( - MetricIdTypeLibrary.GetEnumUnit( - MetricIdTypeLibrary.GetTypeIndex(typeof(TestMetricId)), - TestMetricIdConstants.Test1Value), - TestMetricIdConstants.Test1Units.GetBaseUnits()); - } - - [Test] - public void VerifyCorrectMetricKind() - { - Assert.AreEqual( - MetricIdTypeLibrary.GetEnumMetricKind( - MetricIdTypeLibrary.GetTypeIndex(typeof(TestMetricId)), - TestMetricIdConstants.Test1Value), - TestMetricIdConstants.Test1MetricKind); - } - - [Test] - public void VerifyCorrectName() - { - Assert.AreEqual( - MetricIdTypeLibrary.GetEnumName( - MetricIdTypeLibrary.GetTypeIndex(typeof(TestMetricId)), - TestMetricIdConstants.Test1Value), - TestMetricIdConstants.Test1Name); - } - } -} diff --git a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTestsTmp.cs.meta b/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTestsTmp.cs.meta deleted file mode 100644 index ad8f654..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricIdTypeLibraryTestsTmp.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 207e58cd1d6947878ce1561f6c0ddb39 -timeCreated: 1664202878 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/MetricTests.cs b/NetStats/Tests/Editor/Metrics/MetricTests.cs deleted file mode 100644 index 5af6d8c..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - sealed class MetricTests - { - [TestCase(4957, 18)] - [TestCase(-487, 22)] - [TestCase(312, 3)] - public void Constructor_WhenMetricIdIsOutOfRange_ThrowsException(int typeIndex, int enumValue) - { - Assert.Throws(() => new Counter( - new MetricId(typeIndex: typeIndex, enumValue: enumValue))); - } - - [Test] - public void Constructor_Always_SetsNameAndDefaultValue() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.OwnershipChangeReceived); - var value = new Random().Next(); - - // Act - var metric = new TestMetric(id, value); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.AreEqual(value, metric.Value); - } - - [Test] - public void Reset_Always_SetsUnderlyingValueToDefaultValue() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.TotalBytesSent); - var value = new Random().Next(); - - var metric = new TestMetric(id, value); - metric.SetValue(100); - - // Act - metric.Reset(); - - // Assert - Assert.AreEqual(id, metric.Id); - Assert.AreEqual(value, metric.Value); - } - - [Serializable] - internal class TestMetric : Metric - { - public TestMetric(MetricId metricId, int defaultValue = default) - : base(metricId, defaultValue) - { - } - - public void SetValue(int value) - { - Value = value; - } - - public override MetricContainerType MetricContainerType => MetricContainerType.Counter; - } - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/MetricTests.cs.meta b/NetStats/Tests/Editor/Metrics/MetricTests.cs.meta deleted file mode 100644 index 29828eb..0000000 --- a/NetStats/Tests/Editor/Metrics/MetricTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9e73273eecf26c14ba77e05bc659f129 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/TestMetric.cs b/NetStats/Tests/Editor/Metrics/TestMetric.cs deleted file mode 100644 index f45c6dd..0000000 --- a/NetStats/Tests/Editor/Metrics/TestMetric.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ -#if !UNITY_MP_TOOLS_DEV - [MetricTypeEnumHideInInspector] -#endif - [MetricTypeEnum] - [MetricTypeSortPriority(SortPriority = SortPriority.VeryLow)] - internal enum TestMetric - { - [MetricMetadata(Units = Units.Bytes, MetricKind = MetricKind.Counter)] - BytesCounter, - - [MetricMetadata(Units = Units.Bytes, MetricKind = MetricKind.Counter)] - BytesCounter2, - - [MetricMetadata(Units = Units.Bytes, MetricKind = MetricKind.Counter)] - BytesCounter3, - - [MetricMetadata(Units = Units.Bytes, MetricKind = MetricKind.Counter)] - BytesCounter4, - - [MetricMetadata(Units = Units.Bytes, MetricKind = MetricKind.Gauge)] - BytesGauge, - - [MetricMetadata(Units = Units.Seconds, MetricKind = MetricKind.Counter)] - SecondsCounter, - [MetricMetadata(Units = Units.Seconds, MetricKind = MetricKind.Gauge)] - SecondsGauge, - - [MetricMetadata(Units = Units.None, MetricKind = MetricKind.Counter)] - UnitlessCounter, - [MetricMetadata(Units = Units.None, MetricKind = MetricKind.Gauge)] - UnitlessGauge, - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/TestMetric.cs.meta b/NetStats/Tests/Editor/Metrics/TestMetric.cs.meta deleted file mode 100644 index 22b21fd..0000000 --- a/NetStats/Tests/Editor/Metrics/TestMetric.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 483ad192dd2aa497e9ba14bb192dbf34 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Metrics/TestMetricId.cs b/NetStats/Tests/Editor/Metrics/TestMetricId.cs deleted file mode 100644 index f9508a9..0000000 --- a/NetStats/Tests/Editor/Metrics/TestMetricId.cs +++ /dev/null @@ -1,26 +0,0 @@ -using static Unity.Multiplayer.Tools.NetStats.Tests.TestMetricIdConstants; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - static class TestMetricIdConstants - { - public const int Test1Value = 1; - public const string Test1Name = nameof(TestMetricId.Test1); - public const string Test1DisplayName = "3E6C45D1-3C1D-4AEE-A32A-1C80F23FA6E9"; - public const Units Test1Units = Units.Hertz; - public const MetricKind Test1MetricKind = MetricKind.Gauge; - } - - [MetricTypeEnum] - [MetricTypeEnumHideInInspector] - [MetricTypeSortPriority(SortPriority = SortPriority.VeryLow)] - enum TestMetricId - { - [MetricMetadata( - DisplayName = Test1DisplayName, - Units = Test1Units, - MetricKind = Test1MetricKind)] - Test1 = Test1Value, - Test2 - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/TestMetricId.cs.meta b/NetStats/Tests/Editor/Metrics/TestMetricId.cs.meta deleted file mode 100644 index 37280e3..0000000 --- a/NetStats/Tests/Editor/Metrics/TestMetricId.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 825e22f4258a40e0a03642001ddab9e3 -timeCreated: 1641245607 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/TimerTests.cs b/NetStats/Tests/Editor/Metrics/TimerTests.cs deleted file mode 100644 index f0786fa..0000000 --- a/NetStats/Tests/Editor/Metrics/TimerTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Threading; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - sealed class TimerTests - { - [Test] - public void Set_Always_SetsUnderlyingValueToSpecifiedTimeSpan() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.ServerLogReceived); - var timer = new Timer(id); - var duration = TimeSpan.FromDays(1); - - // Act - timer.Set(duration); - - // Assert - Assert.AreEqual(id, timer.Id); - Assert.AreEqual(duration, timer.Value); - } - - [Test] - public void Time_Always_SetsUnderlyingValueToScopeExecutionDuration() - { - // Arrange - var id = MetricId.Create(DirectedMetricType.ServerLogSent); - var timer = new Timer(id); - - // Act - using (timer.Time()) - { - Thread.Sleep(110); - } - - // Assert - Assert.AreEqual(id, timer.Id); - Assert.Greater(timer.Value, TimeSpan.FromMilliseconds(100)); - } - } -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Metrics/TimerTests.cs.meta b/NetStats/Tests/Editor/Metrics/TimerTests.cs.meta deleted file mode 100644 index 399ba83..0000000 --- a/NetStats/Tests/Editor/Metrics/TimerTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f5d2b3ad4a64b7a4cb3d324ff55a9d15 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Serialization.meta b/NetStats/Tests/Editor/Serialization.meta deleted file mode 100644 index cc213f0..0000000 --- a/NetStats/Tests/Editor/Serialization.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 9ee29b0978614d5b8c82f7b18ae2e83a -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStats/Tests/Editor/Serialization/MetricFactoryTests.cs b/NetStats/Tests/Editor/Serialization/MetricFactoryTests.cs deleted file mode 100644 index ba2d09d..0000000 --- a/NetStats/Tests/Editor/Serialization/MetricFactoryTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Collections; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - [Obsolete("This test class has been marked obsolete because it was not intended to be a public API", false)] - public class MetricFactoryTests - { - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void SetUp() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void GivenCounterHeader_WhenTryConstructCalled_CounterMetricReturned() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void WhenTryConstructCalled_MetricHasCorrectName() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void GivenGaugeHeader_WhenTryConstructCalled_GaugeMetricReturned() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void GivenTimerHeader_WhenTryConstructCalled_TimerMetricReturned() { } - - [Obsolete("This test method has been marked obsolete because it was not intended to be part of the public API", false)] - public void GivenEventHeader_WhenTryConstructCalled_EventMetricReturned() { } - } -} diff --git a/NetStats/Tests/Editor/Serialization/MetricFactoryTests.cs.meta b/NetStats/Tests/Editor/Serialization/MetricFactoryTests.cs.meta deleted file mode 100644 index a897373..0000000 --- a/NetStats/Tests/Editor/Serialization/MetricFactoryTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: fe0c348c0cf742b2910d227fa5a6b32f -timeCreated: 1637603899 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Serialization/MetricFactoryTestsTmp.cs b/NetStats/Tests/Editor/Serialization/MetricFactoryTestsTmp.cs deleted file mode 100644 index 20bc855..0000000 --- a/NetStats/Tests/Editor/Serialization/MetricFactoryTestsTmp.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Collections; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - class MetricFactoryTestsTmp - { - MetricFactory m_TestObj; - - static MetricHeader GetCounterMetricHeader() => new MetricHeader - { - MetricContainerType = MetricContainerType.Counter, - - MetricId = MetricId.Create(DirectedMetricType.NamedMessageReceived) - }; - - static MetricHeader GetGaugeMetricHeader() => new MetricHeader - { - MetricContainerType = MetricContainerType.Gauge, - MetricId = MetricId.Create(DirectedMetricType.TotalBytesSent) - }; - - static MetricHeader GetTimerMetricHeader() => new MetricHeader - { - MetricContainerType = MetricContainerType.Timer, - MetricId = MetricId.Create(DirectedMetricType.ObjectDestroyedReceived) - }; - - static MetricHeader GetEventMetricHeader(FixedString128Bytes eventFactoryTypeName) => new MetricHeader - { - MetricContainerType = MetricContainerType.Event, - MetricId = MetricId.Create(DirectedMetricType.ObjectDestroyedReceived), - EventFactoryTypeName = eventFactoryTypeName - }; - - [SetUp] - public void SetUp() - { - m_TestObj = new MetricFactory(); - } - - [Test] - public void GivenCounterHeader_WhenTryConstructCalled_CounterMetricReturned() - { - var header = GetCounterMetricHeader(); - - var result = m_TestObj.TryConstruct(header, out var metric); - - Assert.IsTrue(result); - Assert.IsInstanceOf(metric); - } - - [Test] - public void WhenTryConstructCalled_MetricHasCorrectName() - { - var metricId = MetricId.Create(DirectedMetricType.ObjectDestroyedReceived); - - var header = GetCounterMetricHeader(); - header.MetricId = metricId; - - var result = m_TestObj.TryConstruct(header, out var metric); - - Assert.AreEqual(metric.Id, metricId); - } - - [Test] - public void GivenGaugeHeader_WhenTryConstructCalled_GaugeMetricReturned() - { - var header = GetGaugeMetricHeader(); - - var result = m_TestObj.TryConstruct(header, out var metric); - - Assert.IsTrue(result); - Assert.IsInstanceOf(metric); - } - - [Test] - public void GivenTimerHeader_WhenTryConstructCalled_TimerMetricReturned() - { - var header = GetTimerMetricHeader(); - - var result = m_TestObj.TryConstruct(header, out var metric); - - Assert.IsTrue(result); - Assert.IsInstanceOf(metric); - } - - [Test] - public void GivenEventHeader_WhenTryConstructCalled_EventMetricReturned() - { - Assert.IsTrue(EventMetricFactory.TryGetFactoryTypeName(typeof(TestEventData), out var eventFactoryTypeName)); - - var header = GetEventMetricHeader(eventFactoryTypeName); - - var result = m_TestObj.TryConstruct(header, out var metric); - - Assert.IsTrue(result); - Assert.IsInstanceOf>(metric); - } - } -} diff --git a/NetStats/Tests/Editor/Serialization/MetricFactoryTestsTmp.cs.meta b/NetStats/Tests/Editor/Serialization/MetricFactoryTestsTmp.cs.meta deleted file mode 100644 index c669915..0000000 --- a/NetStats/Tests/Editor/Serialization/MetricFactoryTestsTmp.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: e9fcb10c253441c5ad684c76b9ec0150 -timeCreated: 1664203032 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Serialization/NetStatSerializerTests.cs b/NetStats/Tests/Editor/Serialization/NetStatSerializerTests.cs deleted file mode 100644 index acd1ae7..0000000 --- a/NetStats/Tests/Editor/Serialization/NetStatSerializerTests.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTypes; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - internal class NetStatSerializerTests - { - static MetricCollection SerializeDeserializeMetrics(List metrics) - { - var collection = MetricCollectionTestUtility.ConstructFromMetrics(metrics); - - var serialized = new NetStatSerializer().Serialize(collection); - var deserialized = new NetStatSerializer().Deserialize(serialized); - return deserialized; - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(10)] - public void GivenCountersInCollection_WhenSerializedDeserialized_CounterValuesCorrect(int numCounters) - { - var counters = new List(); - for (int i = 0; i < numCounters; ++i) - { - var id = MetricId.Create((DirectedMetricType)i); - counters.Add(new Counter(id, i)); - } - - var result = SerializeDeserializeMetrics(counters.Cast().ToList()); - - for (int i = 0; i < numCounters; ++i) - { - Assert.IsTrue(result.TryGetCounter(counters[i].Id, out var deserializedCounter)); - Assert.AreEqual(deserializedCounter.Value, counters[i].Value); - } - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(10)] - public void GivenGaugesInCollection_WhenSerializedDeserialized_GaugeValuesCorrect(int numGauges) - { - var gauges = new List(); - for (int i = 0; i < numGauges; ++i) - { - var id = MetricId.Create((DirectedMetricType)i); - gauges.Add(new Gauge(id, i * 0.5f)); - } - - var result = SerializeDeserializeMetrics(gauges.Cast().ToList()); - - for (int i = 0; i < numGauges; ++i) - { - Assert.IsTrue(result.TryGetGauge(gauges[i].Id, out var deserializedGauge)); - Assert.IsTrue(Math.Abs(deserializedGauge.Value - gauges[i].Value) < float.Epsilon); - } - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(10)] - public void GivenEventsInCollection_WhenSerializedDeserialized_EventValuesCorrect(int numEvents) - { - var events = new List>(); - for (int i = 0; i < numEvents; ++i) - { - var id = MetricId.Create((DirectedMetricType)i); - var evt = new EventMetric(id); - for (int j = 0; j < numEvents; ++j) - { - evt.Mark(j); - } - - events.Add(evt); - } - - var result = SerializeDeserializeMetrics(events.Cast().ToList()); - - for (int i = 0; i < numEvents; ++i) - { - Assert.IsTrue(result.TryGetEvent(events[i].Id, out var deserializedEvent)); - Assert.AreEqual(events[i].Values.Count, deserializedEvent.Values.Count); - Assert.IsTrue(events[i].Values.SequenceEqual(deserializedEvent.Values)); - } - } - - [TestCase(1)] - [TestCase(2)] - [TestCase(10)] - public void GivenGenericEventsInCollection_WhenSerializedDeserialized_EventValuesCorrect(int numEvents) - { - var events = new List>(); - for (int i = 0; i < numEvents; ++i) - { - var id = MetricId.Create((DirectedMetricType)i); - var evt = new EventMetric(id); - for (int j = 0; j < numEvents; ++j) - { - evt.Mark(new TestEventData() - { - Int1 = j * 10, - Bool1 = j % 2 == 0 - }); - } - - events.Add(evt); - } - - var result = SerializeDeserializeMetrics(events.Cast().ToList()); - - for (int i = 0; i < numEvents; ++i) - { - Assert.IsTrue(result.TryGetEvent(events[i].Id, out var deserializedEvent)); - Assert.AreEqual(events[i].Values.Count, deserializedEvent.Values.Count); - - var eventValues = events[i].Values.ToList(); - var deserializedEventValues = deserializedEvent.Values.ToList(); - for (int j = 0; j < eventValues.Count; ++j) - { - deserializedEventValues[j].AssertEquals(eventValues[j]); - } - } - } - } -} diff --git a/NetStats/Tests/Editor/Serialization/NetStatSerializerTests.cs.meta b/NetStats/Tests/Editor/Serialization/NetStatSerializerTests.cs.meta deleted file mode 100644 index fd905c6..0000000 --- a/NetStats/Tests/Editor/Serialization/NetStatSerializerTests.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 581a7975602f4d7e9cd602eeac18a2b4 -timeCreated: 1630454417 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Serialization/TestEventData.cs b/NetStats/Tests/Editor/Serialization/TestEventData.cs deleted file mode 100644 index a32a08f..0000000 --- a/NetStats/Tests/Editor/Serialization/TestEventData.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NUnit.Framework; - -namespace Unity.Multiplayer.Tools.NetStats.Tests -{ - struct TestEventData - { - public int Int1; - public bool Bool1; - - public void AssertEquals(TestEventData other) - { - Assert.AreEqual(Int1, other.Int1); - Assert.AreEqual(Bool1, other.Bool1); - } - } - - class TestEventDataUsage - { - public EventMetric eventData; - } -} diff --git a/NetStats/Tests/Editor/Serialization/TestEventData.cs.meta b/NetStats/Tests/Editor/Serialization/TestEventData.cs.meta deleted file mode 100644 index 9026ea3..0000000 --- a/NetStats/Tests/Editor/Serialization/TestEventData.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: d9fcb9c80cfe4fbfacd810fbf926b77c -timeCreated: 1637604837 \ No newline at end of file diff --git a/NetStats/Tests/Editor/Unity.Multiplayer.Tools.NetStats.Tests.Editor.asmdef b/NetStats/Tests/Editor/Unity.Multiplayer.Tools.NetStats.Tests.Editor.asmdef deleted file mode 100644 index 26f1b05..0000000 --- a/NetStats/Tests/Editor/Unity.Multiplayer.Tools.NetStats.Tests.Editor.asmdef +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "Unity.Multiplayer.Tools.NetStats.Tests.Editor", - "rootNamespace": "Unity.Multiplayer.Tools.NetStats.Tests", - "references": [ - "Unity.Multiplayer.Tools.NetStats", - "Unity.Multiplayer.Tools.MetricTypes", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner", - "Unity.Collections" - ], - "includePlatforms": [ - "Editor" - ], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": true, - "precompiledReferences": [ - "nunit.framework.dll" - ], - "autoReferenced": false, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} \ No newline at end of file diff --git a/NetStats/Tests/Editor/Unity.Multiplayer.Tools.NetStats.Tests.Editor.asmdef.meta b/NetStats/Tests/Editor/Unity.Multiplayer.Tools.NetStats.Tests.Editor.asmdef.meta deleted file mode 100644 index 97f8e86..0000000 --- a/NetStats/Tests/Editor/Unity.Multiplayer.Tools.NetStats.Tests.Editor.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: fb06fd2eadf40594d9ba0083020502b7 -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Component/AssemblyInfo.cs b/NetStatsMonitor/Component/AssemblyInfo.cs index c5f220b..978cdef 100644 --- a/NetStatsMonitor/Component/AssemblyInfo.cs +++ b/NetStatsMonitor/Component/AssemblyInfo.cs @@ -2,5 +2,9 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Editor")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Interface")] + +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Interface.Tests.Editor")] +#endif \ No newline at end of file diff --git a/NetStatsMonitor/Component/CustomTestDataGenerator.cs b/NetStatsMonitor/Component/CustomTestDataGenerator.cs index ae5036f..c6e688b 100644 --- a/NetStatsMonitor/Component/CustomTestDataGenerator.cs +++ b/NetStatsMonitor/Component/CustomTestDataGenerator.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using Unity.Multiplayer.Tools.NetStats; using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.NetStats; using UnityEngine; namespace Unity.Multiplayer.Tools.NetStatsMonitor @@ -44,7 +44,11 @@ internal class CustomTestDataGenerator : MonoBehaviour void Start() { +#if UNITY_2023_1_OR_NEWER + m_Rnsm = FindFirstObjectByType(); +#else m_Rnsm = FindObjectOfType(); +#endif } void Update() diff --git a/NetStatsMonitor/Component/RuntimeNetStatsMonitor.cs b/NetStatsMonitor/Component/RuntimeNetStatsMonitor.cs index 52c130f..6600ea7 100644 --- a/NetStatsMonitor/Component/RuntimeNetStatsMonitor.cs +++ b/NetStatsMonitor/Component/RuntimeNetStatsMonitor.cs @@ -8,12 +8,11 @@ // --------------------------------------------------------------------------------------------------------------------- -using JetBrains.Annotations; using System; +using JetBrains.Annotations; using Unity.Multiplayer.Tools.NetStats; using UnityEngine; using UnityEngine.UIElements; - #if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation; #endif diff --git a/NetStatsMonitor/Component/Unity.Multiplayer.Tools.NetStatsMonitor.Component.asmdef b/NetStatsMonitor/Component/Unity.Multiplayer.Tools.NetStatsMonitor.Component.asmdef index 49d1bf3..70aa9b7 100644 --- a/NetStatsMonitor/Component/Unity.Multiplayer.Tools.NetStatsMonitor.Component.asmdef +++ b/NetStatsMonitor/Component/Unity.Multiplayer.Tools.NetStatsMonitor.Component.asmdef @@ -13,9 +13,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "UNITY_2021_2_OR_NEWER" - ], + "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } \ No newline at end of file diff --git a/NetStatsMonitor/Configuration/AssemblyInfo.cs b/NetStatsMonitor/Configuration/AssemblyInfo.cs index e6ced74..0f40fee 100644 --- a/NetStatsMonitor/Configuration/AssemblyInfo.cs +++ b/NetStatsMonitor/Configuration/AssemblyInfo.cs @@ -3,5 +3,9 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Editor")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Interface")] + +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Interface.Tests.Editor")] +#endif \ No newline at end of file diff --git a/NetStatsMonitor/Configuration/ConfigurationLimits.cs b/NetStatsMonitor/Configuration/ConfigurationLimits.cs index 8afba41..44c5c3d 100644 --- a/NetStatsMonitor/Configuration/ConfigurationLimits.cs +++ b/NetStatsMonitor/Configuration/ConfigurationLimits.cs @@ -3,10 +3,12 @@ namespace Unity.Multiplayer.Tools.NetStatsMonitor static class ConfigurationLimits { internal const int k_GraphSampleMin = 8; - internal const int k_GraphSampleMax = 4096; + internal const int k_GraphSampleDefault = 256; + internal const int k_GraphSampleMax = 512; internal const int k_CounterSampleMin = 8; - internal const int k_CounterSampleMax = 4096; + internal const int k_CounterSampleDefault = 64; + internal const int k_CounterSampleMax = 512; // Limited to 7 for now // Informed by the approximate base 10 precision of 32-bit floating point diff --git a/NetStatsMonitor/Configuration/Counters/AggregationMethod.cs b/NetStatsMonitor/Configuration/Counters/AggregationMethod.cs index 28a8c5c..e0912b7 100644 --- a/NetStatsMonitor/Configuration/Counters/AggregationMethod.cs +++ b/NetStatsMonitor/Configuration/Counters/AggregationMethod.cs @@ -21,4 +21,4 @@ public enum AggregationMethod // Max, // Median, } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Counters/CounterConfiguration.cs b/NetStatsMonitor/Configuration/Counters/CounterConfiguration.cs index cd5ace0..5c6c371 100644 --- a/NetStatsMonitor/Configuration/Counters/CounterConfiguration.cs +++ b/NetStatsMonitor/Configuration/Counters/CounterConfiguration.cs @@ -112,4 +112,4 @@ internal int ComputeHashCode() SimpleMovingAverageParams.ComputeHashCode()); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Counters/ExponentialMovingAverageParams.cs b/NetStatsMonitor/Configuration/Counters/ExponentialMovingAverageParams.cs index ab52480..1410606 100644 --- a/NetStatsMonitor/Configuration/Counters/ExponentialMovingAverageParams.cs +++ b/NetStatsMonitor/Configuration/Counters/ExponentialMovingAverageParams.cs @@ -32,4 +32,4 @@ internal int ComputeHashCode() return HalfLife.GetHashCode(); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Counters/SimpleMovingAverageParams.cs b/NetStatsMonitor/Configuration/Counters/SimpleMovingAverageParams.cs index ac7318b..162bf2f 100644 --- a/NetStatsMonitor/Configuration/Counters/SimpleMovingAverageParams.cs +++ b/NetStatsMonitor/Configuration/Counters/SimpleMovingAverageParams.cs @@ -13,20 +13,19 @@ public sealed class SimpleMovingAverageParams /// The number of samples that are maintained for the purpose of smoothing. /// /// - /// The value is clamped to the range [8, 4096]. + /// The value is clamped to the range [8, 512]. /// [field: SerializeField] - [field: Min(1)] [field: Tooltip("The number of samples that are maintained for the purpose of smoothing." + - "The value is clamped to the range [8, 4096].")] + "The value is clamped to the range [8, 512].")] [field: Range(ConfigurationLimits.k_CounterSampleMin, ConfigurationLimits.k_CounterSampleMax)] - int m_SampleCount = 64; + int m_SampleCount = ConfigurationLimits.k_CounterSampleDefault; /// /// The number of samples that are maintained for the purpose of smoothing. /// /// - /// The value is clamped to the range [8, 4096]. + /// The value is clamped to the range [8, 512]. /// public int SampleCount { @@ -60,4 +59,4 @@ internal int ComputeHashCode() return HashCode.Combine(SampleCount, SampleRate); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Counters/SmoothingMethod.cs b/NetStatsMonitor/Configuration/Counters/SmoothingMethod.cs index a6f7911..10294db 100644 --- a/NetStatsMonitor/Configuration/Counters/SmoothingMethod.cs +++ b/NetStatsMonitor/Configuration/Counters/SmoothingMethod.cs @@ -1,5 +1,3 @@ -using System; - namespace Unity.Multiplayer.Tools.NetStatsMonitor { /// @@ -20,4 +18,4 @@ public enum SmoothingMethod /// SimpleMovingAverage, } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/DisplayElementConfiguration.cs b/NetStatsMonitor/Configuration/DisplayElementConfiguration.cs index 375f3e7..27b1b12 100644 --- a/NetStatsMonitor/Configuration/DisplayElementConfiguration.cs +++ b/NetStatsMonitor/Configuration/DisplayElementConfiguration.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; - -using UnityEngine; using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; using Unity.Multiplayer.Tools.NetStatsMonitor.Configuration; +using UnityEngine; namespace Unity.Multiplayer.Tools.NetStatsMonitor { @@ -325,7 +324,6 @@ public void OnAfterDeserialize() } } -#if UNITY_2021_2_OR_NEWER // HashCode isn't defined in Unity < 2021.2 internal int ComputeStatsHashCode() { var hash = 0; @@ -353,6 +351,5 @@ internal int ComputeHashCode() } return hash; } -#endif } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/DisplayElementType.cs b/NetStatsMonitor/Configuration/DisplayElementType.cs index 0728430..4440c91 100644 --- a/NetStatsMonitor/Configuration/DisplayElementType.cs +++ b/NetStatsMonitor/Configuration/DisplayElementType.cs @@ -18,4 +18,4 @@ public enum DisplayElementType /// StackedAreaGraph, } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Graphs/GraphConfiguration.cs b/NetStatsMonitor/Configuration/Graphs/GraphConfiguration.cs index b708557..3539e40 100644 --- a/NetStatsMonitor/Configuration/Graphs/GraphConfiguration.cs +++ b/NetStatsMonitor/Configuration/Graphs/GraphConfiguration.cs @@ -15,19 +15,19 @@ public sealed class GraphConfiguration /// The number of samples that are maintained for the purpose of graphing. /// /// - /// The value is clamped to the range [8, 4096]. + /// The value is clamped to the range [8, 512]. /// [field: SerializeField] [field: Tooltip("The number of samples that are maintained for the purpose of graphing. " + - "The value is clamped to the range [8, 4096].")] + "The value is clamped to the range [8, 512].")] [field: Range(ConfigurationLimits.k_GraphSampleMin, ConfigurationLimits.k_GraphSampleMax)] - int m_SampleCount = 256; + int m_SampleCount = ConfigurationLimits.k_GraphSampleDefault; /// /// The number of samples that are maintained for the purpose of graphing. /// /// - /// The value is clamped to the range [8, 4096]. + /// The value is clamped to the range [8, 512]. /// public int SampleCount { @@ -90,4 +90,4 @@ internal int ComputeHashCode() return hash; } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Graphs/GraphXAxisType.cs b/NetStatsMonitor/Configuration/Graphs/GraphXAxisType.cs index 7a60ecf..7c5dca8 100644 --- a/NetStatsMonitor/Configuration/Graphs/GraphXAxisType.cs +++ b/NetStatsMonitor/Configuration/Graphs/GraphXAxisType.cs @@ -14,4 +14,4 @@ public enum GraphXAxisType /// Time, } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Graphs/LineGraphConfiguration.cs b/NetStatsMonitor/Configuration/Graphs/LineGraphConfiguration.cs index 4e57182..bd6d3b3 100644 --- a/NetStatsMonitor/Configuration/Graphs/LineGraphConfiguration.cs +++ b/NetStatsMonitor/Configuration/Graphs/LineGraphConfiguration.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using UnityEngine; namespace Unity.Multiplayer.Tools.NetStatsMonitor @@ -35,4 +34,4 @@ internal int ComputeHashCode() return LineThickness.GetHashCode(); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/NetStatsMonitorConfiguration.cs b/NetStatsMonitor/Configuration/NetStatsMonitorConfiguration.cs index 6b24594..8bbb35c 100644 --- a/NetStatsMonitor/Configuration/NetStatsMonitorConfiguration.cs +++ b/NetStatsMonitor/Configuration/NetStatsMonitorConfiguration.cs @@ -60,4 +60,4 @@ internal void RecomputeConfigurationHash() ConfigurationHash = hashCode; } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/PositionConfiguration.cs b/NetStatsMonitor/Configuration/PositionConfiguration.cs index c850139..aa54ec9 100644 --- a/NetStatsMonitor/Configuration/PositionConfiguration.cs +++ b/NetStatsMonitor/Configuration/PositionConfiguration.cs @@ -69,4 +69,4 @@ public float PositionTopToBottom [SerializeField] float m_PositionTopToBottom; } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.asmdef b/NetStatsMonitor/Configuration/Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.asmdef index a220071..02b4439 100644 --- a/NetStatsMonitor/Configuration/Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.asmdef +++ b/NetStatsMonitor/Configuration/Unity.Multiplayer.Tools.NetStatsMonitor.Configuration.asmdef @@ -12,9 +12,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "UNITY_2021_2_OR_NEWER" - ], + "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } \ No newline at end of file diff --git a/NetStatsMonitor/Configuration/UssClassNames.cs b/NetStatsMonitor/Configuration/UssClassNames.cs index 311213f..c022de9 100644 --- a/NetStatsMonitor/Configuration/UssClassNames.cs +++ b/NetStatsMonitor/Configuration/UssClassNames.cs @@ -35,4 +35,4 @@ internal static class UssClassNames internal const string k_NoDataReceived = "rnsm-no-data-received"; internal const string k_NoDataReceivedLabel = "rnsm-no-data-received-label"; } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Configuration/Util/LabelGeneration.cs b/NetStatsMonitor/Configuration/Util/LabelGeneration.cs index f424861..6f60e8a 100644 --- a/NetStatsMonitor/Configuration/Util/LabelGeneration.cs +++ b/NetStatsMonitor/Configuration/Util/LabelGeneration.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Unity.Multiplayer.Tools.MetricTypes; +using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Configuration @@ -92,4 +92,4 @@ public static string GenerateLabel(List stats) } } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/AssemblyInfo.cs b/NetStatsMonitor/Editor/AssemblyInfo.cs index 2a45dc7..921e37e 100644 --- a/NetStatsMonitor/Editor/AssemblyInfo.cs +++ b/NetStatsMonitor/Editor/AssemblyInfo.cs @@ -1,3 +1,6 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Editor")] \ No newline at end of file +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Editor")] +#endif \ No newline at end of file diff --git a/NetStatsMonitor/Editor/Inspector/CounterConfigurationDrawer.cs b/NetStatsMonitor/Editor/Inspector/CounterConfigurationDrawer.cs index fdb1aa3..c853e35 100644 --- a/NetStatsMonitor/Editor/Inspector/CounterConfigurationDrawer.cs +++ b/NetStatsMonitor/Editor/Inspector/CounterConfigurationDrawer.cs @@ -83,4 +83,4 @@ void UpdateFieldVisibility() }); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/CustomTestDataGeneratorEditor.cs b/NetStatsMonitor/Editor/Inspector/CustomTestDataGeneratorEditor.cs index 70940bf..a1b2959 100644 --- a/NetStatsMonitor/Editor/Inspector/CustomTestDataGeneratorEditor.cs +++ b/NetStatsMonitor/Editor/Inspector/CustomTestDataGeneratorEditor.cs @@ -14,4 +14,4 @@ public override VisualElement CreateInspectorGUI() return root; } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/DisplayElementConfigurationDrawer.cs b/NetStatsMonitor/Editor/Inspector/DisplayElementConfigurationDrawer.cs index 57a1c21..28d3235 100644 --- a/NetStatsMonitor/Editor/Inspector/DisplayElementConfigurationDrawer.cs +++ b/NetStatsMonitor/Editor/Inspector/DisplayElementConfigurationDrawer.cs @@ -124,4 +124,4 @@ int GetDisplayElementCount(SerializedProperty property) return (property.serializedObject.targetObject as NetStatsMonitorConfiguration)?.DisplayElements?.Count ?? 0; } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/GraphConfigurationDrawer.cs b/NetStatsMonitor/Editor/Inspector/GraphConfigurationDrawer.cs index 1c6fa21..d8a2e50 100644 --- a/NetStatsMonitor/Editor/Inspector/GraphConfigurationDrawer.cs +++ b/NetStatsMonitor/Editor/Inspector/GraphConfigurationDrawer.cs @@ -75,4 +75,4 @@ public void OnTypeChanged(DisplayElementType type) m_LineGraphField.SetInclude(type == DisplayElementType.LineGraph); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/NetStatsMonitorConfigurationEditor.cs b/NetStatsMonitor/Editor/Inspector/NetStatsMonitorConfigurationEditor.cs index a246d07..eaeb3e3 100644 --- a/NetStatsMonitor/Editor/Inspector/NetStatsMonitorConfigurationEditor.cs +++ b/NetStatsMonitor/Editor/Inspector/NetStatsMonitorConfigurationEditor.cs @@ -14,4 +14,4 @@ public override VisualElement CreateInspectorGUI() return root; } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/PositionConfigurationDrawer.cs b/NetStatsMonitor/Editor/Inspector/PositionConfigurationDrawer.cs index 58f51aa..bfe66d9 100644 --- a/NetStatsMonitor/Editor/Inspector/PositionConfigurationDrawer.cs +++ b/NetStatsMonitor/Editor/Inspector/PositionConfigurationDrawer.cs @@ -2,7 +2,6 @@ using System.Reflection; using Unity.Multiplayer.Tools.Common; using UnityEditor; -using UnityEditor.UIElements; using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Editor @@ -70,4 +69,4 @@ void UpdateFieldVisibility() }); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/RuntimeNetStatsMonitorEditor.cs b/NetStatsMonitor/Editor/Inspector/RuntimeNetStatsMonitorEditor.cs index 13cd3f0..0c7b5d8 100644 --- a/NetStatsMonitor/Editor/Inspector/RuntimeNetStatsMonitorEditor.cs +++ b/NetStatsMonitor/Editor/Inspector/RuntimeNetStatsMonitorEditor.cs @@ -14,4 +14,4 @@ public override VisualElement CreateInspectorGUI() return root; } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Inspector/Util/CustomInspectorUtils.cs b/NetStatsMonitor/Editor/Inspector/Util/CustomInspectorUtils.cs index 8d60ab5..a89e607 100644 --- a/NetStatsMonitor/Editor/Inspector/Util/CustomInspectorUtils.cs +++ b/NetStatsMonitor/Editor/Inspector/Util/CustomInspectorUtils.cs @@ -33,4 +33,4 @@ internal static SerializedProperty GetParent(this SerializedProperty property) return obj.FindProperty(parentPath); } } -} \ No newline at end of file +} diff --git a/NetStatsMonitor/Editor/Unity.Multiplayer.Tools.NetStatsMonitor.Editor.asmdef b/NetStatsMonitor/Editor/Unity.Multiplayer.Tools.NetStatsMonitor.Editor.asmdef index f241758..3daeb9d 100644 --- a/NetStatsMonitor/Editor/Unity.Multiplayer.Tools.NetStatsMonitor.Editor.asmdef +++ b/NetStatsMonitor/Editor/Unity.Multiplayer.Tools.NetStatsMonitor.Editor.asmdef @@ -15,9 +15,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "UNITY_2021_2_OR_NEWER" - ], + "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } \ No newline at end of file diff --git a/NetStatsMonitor/Implementation/AssemblyInfo.cs b/NetStatsMonitor/Implementation/AssemblyInfo.cs index f4bc9d7..dcc7a35 100644 --- a/NetStatsMonitor/Implementation/AssemblyInfo.cs +++ b/NetStatsMonitor/Implementation/AssemblyInfo.cs @@ -2,4 +2,8 @@ [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.MetricEvents")] [assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] -[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation")] + +// Test assemblies +#if UNITY_INCLUDE_TESTS +[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")] +#endif \ No newline at end of file diff --git a/NetStatsMonitor/Implementation/Component/RnsmComponentImplementation.cs b/NetStatsMonitor/Implementation/Component/RnsmComponentImplementation.cs index dab311a..77a7a62 100644 --- a/NetStatsMonitor/Implementation/Component/RnsmComponentImplementation.cs +++ b/NetStatsMonitor/Implementation/Component/RnsmComponentImplementation.cs @@ -344,7 +344,7 @@ public void AddCustomValue(MetricId metricId, float value) { // There is no display element configured to display // this stat, so there is no point in storing it. - return; + continue; } // This additional call to CollectStatsIfEnoughTimeHasElapsed is beneficial in the following scenario: @@ -362,4 +362,4 @@ public void AddCustomValue(MetricId metricId, float value) } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/Configuration/Extensions/DisplayElementConfiguration.cs b/NetStatsMonitor/Implementation/Configuration/Extensions/DisplayElementConfiguration.cs index e288366..63aa464 100644 --- a/NetStatsMonitor/Implementation/Configuration/Extensions/DisplayElementConfiguration.cs +++ b/NetStatsMonitor/Implementation/Configuration/Extensions/DisplayElementConfiguration.cs @@ -29,4 +29,4 @@ internal static int GetHistoryRequirementsHash(this DisplayElementConfiguration } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/Configuration/Extensions/NetStatsMonitorConfiguration.cs b/NetStatsMonitor/Implementation/Configuration/Extensions/NetStatsMonitorConfiguration.cs index 418d63e..ce7f2a2 100644 --- a/NetStatsMonitor/Implementation/Configuration/Extensions/NetStatsMonitorConfiguration.cs +++ b/NetStatsMonitor/Implementation/Configuration/Extensions/NetStatsMonitorConfiguration.cs @@ -29,4 +29,4 @@ internal static int GetHistoryRequirementsHash(this NetStatsMonitorConfiguration } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/Graphing/GraphColorUtils.cs b/NetStatsMonitor/Implementation/Graphing/GraphColorUtils.cs deleted file mode 100644 index a2098e0..0000000 --- a/NetStatsMonitor/Implementation/Graphing/GraphColorUtils.cs +++ /dev/null @@ -1,82 +0,0 @@ -// RNSM Implementation compilation boilerplate -// All references to UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED should be defined in the same way, -// as any discrepancies are likely to result in build failures -// --------------------------------------------------------------------------------------------------------------------- -#if UNITY_EDITOR || ((DEVELOPMENT_BUILD && !UNITY_MP_TOOLS_NET_STATS_MONITOR_DISABLED_IN_DEVELOP) || (!DEVELOPMENT_BUILD && UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE)) - #define UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED -#endif -// --------------------------------------------------------------------------------------------------------------------- - -#if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED - -using UnityEngine; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Graphing -{ - internal static class GraphColorUtils - { - /// Set of colors for labelling quantitative data from www.colorbrewer2.org, - /// that can be distinguished by those with red-green color blindness - static readonly Color32[] k_ColorSeries_ColorBlindSafe = new[] - { - new Color32(166, 206, 227, 255), - new Color32(031, 120, 180, 255), - new Color32(178, 223, 138, 255), - new Color32(051, 160, 044, 255), - }; - - /// A larger series of colors which can display more values from www.colorbrewer2.org, - /// but may not be easily distinguishable for those with color blindness. - /// Users can customize the colors on the plot so if this series isn't - /// suitable for their purposes they can create their own. - static readonly Color32[] k_ColorSeries_Larger = new[] - { - new Color32(141, 211, 199, 255), - new Color32(255, 255, 179, 255), - new Color32(190, 186, 218, 255), - new Color32(251, 128, 114, 255), - new Color32(128, 177, 211, 255), - new Color32(253, 180, 098, 255), - new Color32(179, 222, 105, 255), - new Color32(252, 205, 229, 255), - new Color32(217, 217, 217, 255), - new Color32(188, 128, 189, 255), - new Color32(204, 235, 197, 255), - new Color32(255, 237, 111, 255), - }; - - const float k_ColorDefaultSaturation = 0.8f; - - /// Value chosen to match the average value of k_ColorSeriesLarger - /// so that any random colors needed beyond the first 12 won't be - /// jarringly different - const float k_ColorDefaultValue = 0.72f; - - static Color32 GetRandomColorForIndex(int statIndex) - { - const int k_randomPrime0 = 17; - const int k_randomPrime1 = 67; - const int k_randomPrime2 = 419; - var hue = (((statIndex + k_randomPrime0) * k_randomPrime1) % k_randomPrime2) / k_randomPrime2; - return Color.HSVToRGB(hue, k_ColorDefaultSaturation, k_ColorDefaultValue); - } - - public static Color32 GetColorForIndex(int statIndex, int totalStatCount) - { - if (totalStatCount <= k_ColorSeries_ColorBlindSafe.Length) - { - return k_ColorSeries_ColorBlindSafe[statIndex]; - } - - // Fall back on the larger list if we don't have enough color-blind safe colors to support their data set - if (statIndex < k_ColorSeries_Larger.Length) - { - return k_ColorSeries_Larger[statIndex]; - } - - // Fall back on random colors if we don't have enough colors in any series to support their data set - return GetRandomColorForIndex(statIndex); - } - } -} -#endif \ No newline at end of file diff --git a/NetStatsMonitor/Implementation/Graphing/GraphColorUtils.cs.meta b/NetStatsMonitor/Implementation/Graphing/GraphColorUtils.cs.meta deleted file mode 100644 index aad36a0..0000000 --- a/NetStatsMonitor/Implementation/Graphing/GraphColorUtils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: aa85082a765dc4bf5bde969b1aac19ab -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Implementation/Graphing/GraphScalingUtils.cs b/NetStatsMonitor/Implementation/Graphing/GraphScalingUtils.cs index 5402e08..682e9d4 100644 --- a/NetStatsMonitor/Implementation/Graphing/GraphScalingUtils.cs +++ b/NetStatsMonitor/Implementation/Graphing/GraphScalingUtils.cs @@ -66,4 +66,4 @@ public static MantissaAndExponent NextLargestRoundNumber(float value) } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/Stats/MultiStatHistory.cs b/NetStatsMonitor/Implementation/Stats/MultiStatHistory.cs index 13a3b2c..bf7fdd3 100644 --- a/NetStatsMonitor/Implementation/Stats/MultiStatHistory.cs +++ b/NetStatsMonitor/Implementation/Stats/MultiStatHistory.cs @@ -13,7 +13,6 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; using UnityEngine.Assertions; @@ -186,4 +185,4 @@ internal double TimeSpanOfLastNSamples(SampleRate sampleRate, int sampleCount) } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/Stats/StatHistory.cs b/NetStatsMonitor/Implementation/Stats/StatHistory.cs index 3893897..1ba7fa9 100644 --- a/NetStatsMonitor/Implementation/Stats/StatHistory.cs +++ b/NetStatsMonitor/Implementation/Stats/StatHistory.cs @@ -9,9 +9,7 @@ #if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED -using System; using System.Linq; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; @@ -30,10 +28,6 @@ class StatHistory /// Entries may be null or empty if not required. public EnumMap> SampleBuffers { get; } = new(); - /// Ring buffer of most recent values. - /// Will have capacity zero and not store any values if the number of past values required is zero - // public RingBuffer RecentValues { get; } - public StatHistory(StatHistoryRequirements requirements) { ContinuousExponentialMovingAverages = requirements diff --git a/NetStatsMonitor/Implementation/Stats/StatsAccumulator.cs b/NetStatsMonitor/Implementation/Stats/StatsAccumulator.cs index b5f9ba1..be4a441 100644 --- a/NetStatsMonitor/Implementation/Stats/StatsAccumulator.cs +++ b/NetStatsMonitor/Implementation/Stats/StatsAccumulator.cs @@ -13,7 +13,6 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; -using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation diff --git a/NetStatsMonitor/Implementation/Stats/StatsAggregator.cs b/NetStatsMonitor/Implementation/Stats/StatsAggregator.cs index e255482..0421ac1 100644 --- a/NetStatsMonitor/Implementation/Stats/StatsAggregator.cs +++ b/NetStatsMonitor/Implementation/Stats/StatsAggregator.cs @@ -10,7 +10,6 @@ #if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED using System; -using System.Collections.Generic; using Unity.Multiplayer.Tools.NetStats; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation diff --git a/NetStatsMonitor/Implementation/UI/Graphs/GraphBoundsTransformer.cs b/NetStatsMonitor/Implementation/UI/Graphs/GraphBoundsTransformer.cs index bb33e85..d5a0bcd 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/GraphBoundsTransformer.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/GraphBoundsTransformer.cs @@ -135,4 +135,4 @@ LinearTransform ComputeYAxisTransform( } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/UI/Graphs/GraphBuffers.cs b/NetStatsMonitor/Implementation/UI/Graphs/GraphBuffers.cs index a1e68e2..b4173b3 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/GraphBuffers.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/GraphBuffers.cs @@ -10,7 +10,7 @@ #if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED using System; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Graphing; +using Unity.Multiplayer.Tools.Common.Visualization; using UnityEngine; using UnityEngine.UIElements; @@ -126,13 +126,13 @@ void SetVertexColors(int statCount, int pointsPerStat, Color[] variableColors) for (var statIndex = 0; statIndex < statCount; ++statIndex) { var statVerticesBegin = statIndex * verticesPerStat; - Color32 statColor = (variableColors != null && statIndex < variableColors.Length) + var statColor = (variableColors != null && statIndex < variableColors.Length) ? variableColors[statIndex] - : GraphColorUtils.GetColorForIndex(statIndex, statCount); + : CategoricalColorPalette.GetColor(statIndex); for (var vertexIndex = 0; vertexIndex < verticesPerStat; ++vertexIndex) { var vertexIndexAbsolute = statVerticesBegin + vertexIndex; - Vertices[vertexIndexAbsolute].tint = statColor; + Vertices[vertexIndexAbsolute].tint = (Color32)statColor; } } } diff --git a/NetStatsMonitor/Implementation/UI/Graphs/GraphContent.cs b/NetStatsMonitor/Implementation/UI/Graphs/GraphContent.cs index 20a85b4..56514e4 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/GraphContent.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/GraphContent.cs @@ -10,12 +10,10 @@ #if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED using System.Collections.Generic; - -using UnityEngine; -using UnityEngine.UIElements; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; +using UnityEngine; +using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { diff --git a/NetStatsMonitor/Implementation/UI/Graphs/GraphInputSynchronizer.cs b/NetStatsMonitor/Implementation/UI/Graphs/GraphInputSynchronizer.cs index 777b923..4c32906 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/GraphInputSynchronizer.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/GraphInputSynchronizer.cs @@ -77,4 +77,4 @@ public int ComputeNumberOfPointsToAdvance( } } } -#endif \ No newline at end of file +#endif diff --git a/NetStatsMonitor/Implementation/UI/Graphs/GraphLegend.cs b/NetStatsMonitor/Implementation/UI/Graphs/GraphLegend.cs index 65dd278..181d79b 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/GraphLegend.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/GraphLegend.cs @@ -11,13 +11,11 @@ using System.Collections.Generic; using System.Linq; - +using Unity.Multiplayer.Tools.Common; +using Unity.Multiplayer.Tools.Common.Visualization; using UnityEngine; using UnityEngine.UIElements; -using Unity.Multiplayer.Tools.Common; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Graphing; - namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { public class GraphLegend : VisualElement @@ -50,9 +48,9 @@ public void UpdateConfiguration(DisplayElementConfiguration configuration) { var legendKey = m_LegendKeys[i]; var stat = stats[i]; - Color32 color = (variableColors != null && i < variableColors.Count) + Color color = (variableColors != null && i < variableColors.Count) ? variableColors[i] - : GraphColorUtils.GetColorForIndex(i, stats.Count); + : CategoricalColorPalette.GetColor(i); legendKey.Update(stat.ToString(), color); if (i >= childrenCount) diff --git a/NetStatsMonitor/Implementation/UI/Graphs/GraphVisualElement.cs b/NetStatsMonitor/Implementation/UI/Graphs/GraphVisualElement.cs index 26e9dc2..3a6bbd1 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/GraphVisualElement.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/GraphVisualElement.cs @@ -11,11 +11,9 @@ using System; using System.Collections.Generic; - -using UnityEngine.UIElements; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; +using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { diff --git a/NetStatsMonitor/Implementation/UI/Graphs/IGraphRenderer.cs b/NetStatsMonitor/Implementation/UI/Graphs/IGraphRenderer.cs index 9b30065..29e34f8 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/IGraphRenderer.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/IGraphRenderer.cs @@ -10,11 +10,9 @@ #if UNITY_MP_TOOLS_NET_STATS_MONITOR_IMPLEMENTATION_ENABLED using System.Collections.Generic; - -using UnityEngine.UIElements; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; +using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { diff --git a/NetStatsMonitor/Implementation/UI/Graphs/LegendKey.cs b/NetStatsMonitor/Implementation/UI/Graphs/LegendKey.cs index 1c246af..19b7c57 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/LegendKey.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/LegendKey.cs @@ -40,7 +40,7 @@ internal LegendKey(string name, Color32 color) UpdateColor(color); } - internal void Update(string name, Color32 color) + internal void Update(string name, Color color) { UpdateName(name); UpdateColor(color); @@ -52,9 +52,9 @@ internal void UpdateName(string name) m_KeyLabel.text = name; } - internal void UpdateColor(Color32 color) + internal void UpdateColor(Color color) { - m_Swatch.style.backgroundColor = (Color)color; + m_Swatch.style.backgroundColor = color; } } } diff --git a/NetStatsMonitor/Implementation/UI/Graphs/LineGraphRenderer.cs b/NetStatsMonitor/Implementation/UI/Graphs/LineGraphRenderer.cs index d9ff91b..d61020d 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/LineGraphRenderer.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/LineGraphRenderer.cs @@ -12,12 +12,10 @@ using System; using System.Collections.Generic; - -using UnityEngine; -using UnityEngine.UIElements; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; +using UnityEngine; +using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { @@ -247,7 +245,7 @@ void UpdateMaxPointValues( m_MaxPointValues.PushBack(0); } - var firstNewPointIndex = graphWidthPoints - pointsToAdvance; + var firstNewPointIndex = Math.Max(graphWidthPoints - pointsToAdvance, 0); foreach (var stat in stats) { var pointValues = sampler.PointValues[stat]; diff --git a/NetStatsMonitor/Implementation/UI/Graphs/StackedAreaGraphRenderer.cs b/NetStatsMonitor/Implementation/UI/Graphs/StackedAreaGraphRenderer.cs index 5cf385e..566184f 100644 --- a/NetStatsMonitor/Implementation/UI/Graphs/StackedAreaGraphRenderer.cs +++ b/NetStatsMonitor/Implementation/UI/Graphs/StackedAreaGraphRenderer.cs @@ -11,12 +11,10 @@ using System; using System.Collections.Generic; - -using UnityEngine; -using UnityEngine.UIElements; - using Unity.Multiplayer.Tools.Common; using Unity.Multiplayer.Tools.NetStats; +using UnityEngine; +using UnityEngine.UIElements; namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { diff --git a/NetStatsMonitor/Implementation/Util/Constants.cs b/NetStatsMonitor/Implementation/Util/Constants.cs index 1f26f00..4a19c16 100644 --- a/NetStatsMonitor/Implementation/Util/Constants.cs +++ b/NetStatsMonitor/Implementation/Util/Constants.cs @@ -1,5 +1,3 @@ -using System; - namespace Unity.Multiplayer.Tools.NetStatsMonitor.Implementation { static class Constants diff --git a/NetStatsMonitor/Implementation/Util/NumericUtils.cs b/NetStatsMonitor/Implementation/Util/NumericUtils.cs index 03004d2..6320d1a 100644 --- a/NetStatsMonitor/Implementation/Util/NumericUtils.cs +++ b/NetStatsMonitor/Implementation/Util/NumericUtils.cs @@ -137,12 +137,12 @@ public static (string leadingNumber, string prefixSymbol) GetLeadingNumberAndPre return (inputBase1000.Mantissa >= 0 ? "+∞" : "-∞", ""); } var prefixSymbol = metricPrefix.GetSymbol(); - + var leadingNumber = roundedValue.ToString("N" + digitsBelowDecimal, CultureInfo.CurrentCulture); return (leadingNumber, prefixSymbol); } - + /// Returns (string LeadingNumber, string PrefixSymbol) public static (string leadingNumber, string prefixSymbol) GetLeadingNumberAndPrefixSymbol( MantissaAndExponent inputBase1000, @@ -158,7 +158,7 @@ public static (string leadingNumber, string prefixSymbol) GetLeadingNumberAndPre var digitsAboveDecimal = GetDigitsAboveDecimal(inputBase1000, false); var digitsBelowDecimal = GetDigitsBelowDecimal(significantDigits, digitsAboveDecimal); - + var roundedValue = RoundToSignificantDigits( inputBase1000.Mantissa, significantDigits, @@ -181,7 +181,7 @@ public static string Base1000ToEngineeringNotation( + (unitsNumerator == "" ? prefixSymbol : $"{k_SmallSpace}{prefixSymbol}{unitsNumerator}") + (unitsDenominator == "" ? "" : $"{k_DivisionSlash}{unitsDenominator}"); } - + public static string Base1000ToEngineeringNotation( MantissaAndExponent inputBase1000, int significantDigits, @@ -220,7 +220,7 @@ public static string Base10ToPercentageNotation( + (unitsNumerator == "" ? "" : $"{k_SmallSpace}{unitsNumerator}") + (unitsDenominator == "" ? "" : $"{k_DivisionSlash}{unitsDenominator}"); } - + public static string Base10ToPercentageNotation( MantissaAndExponent inputBase10, int significantDigits, @@ -259,10 +259,10 @@ public static bool Approximately(double a, double b, double tolerance) { return Math.Abs(a - b) < tolerance; } - + public static int GetDigitsAboveDecimal(MantissaAndExponent inputBase1000, bool displayAsPercentage) { - return displayAsPercentage ? 1 : + return displayAsPercentage ? 1 : inputBase1000.Mantissa >= 100f ? 3 : inputBase1000.Mantissa >= 10f ? 2 : 1; diff --git a/NetStatsMonitor/Tests.meta b/NetStatsMonitor/Tests.meta deleted file mode 100644 index 78cc453..0000000 --- a/NetStatsMonitor/Tests.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 48cf0a11f3eef4d84be248b293245514 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation.meta b/NetStatsMonitor/Tests/Implementation.meta deleted file mode 100644 index 0763a05..0000000 --- a/NetStatsMonitor/Tests/Implementation.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 02f47306a0fb14d97a378d47185db4cb -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Component.meta b/NetStatsMonitor/Tests/Implementation/Component.meta deleted file mode 100644 index d158807..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 5f177a66a5bf42fcbd46103d0194c504 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmCustomDataDisplayUpdateTests.cs b/NetStatsMonitor/Tests/Implementation/Component/RnsmCustomDataDisplayUpdateTests.cs deleted file mode 100644 index 4874663..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmCustomDataDisplayUpdateTests.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using Unity.Multiplayer.Tools.NetStats; -using Unity.Multiplayer.Tools.NetStats.Tests; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests -{ - /// These tests are virtually identical to RnsmDisplayUpdateTests only using the custom data API - /// rather than the TestDataGenerator - class RnsmCustomDataDisplayUpdateTests - { - static readonly List k_CustomStatsDisplayed = new List - { - MetricId.Create(TestMetric.BytesCounter), - MetricId.Create(TestMetric.BytesCounter3), - MetricId.Create(TestMetric.BytesCounter4), - }; - - [Test] - public void EnsureRnsmDisplayOnlyUpdatesWhenNewDataIsReceived() - { - RnsmTestHarness rnsmTestHarness = new RnsmTestHarness( - rnsmRefreshRate: 1f, - statsDisplayed: k_CustomStatsDisplayed); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - customData: new Dictionary - { - // BytesGauge is not configured to be displayed so will not - // trigger a display update - { MetricId.Create(TestMetric.BytesGauge), 17 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 1 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - customData: null, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 2 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - customData: new Dictionary - { - // SecondsCounter and BytesCounter2 are not configured to be displayed so will not - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter2), 87623 }, - { MetricId.Create(TestMetric.SecondsCounter), 224 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 3 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - { MetricId.Create(TestMetric.SecondsGauge), 117 }, - }, - updateRnsm: false, - expectedUpdateStatus: RnsmDisplayUpdateStatus.UpdateNotCalled); - - // T == 4 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - customData: null, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled); - - // T == 5 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - customData: null, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - rnsmTestHarness.Teardown(); - } - - [Test] - public void EnsureRnsmDisplayOnlyUpdatesWhenEnoughTimeHasElapsed() - { - RnsmTestHarness rnsmTestHarness = new RnsmTestHarness( - rnsmRefreshRate: 1f, - statsDisplayed: k_CustomStatsDisplayed); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - customData: null, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedNoTimeElapsedUpdateCalledFirstTime); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - customData: new Dictionary - { - // BytesCounter3 is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter3), 419 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoTimeElapsed); - - // T == 0.2 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.2f, - customData: new Dictionary - { - // BytesCounter4 is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter4), 419 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 0.8 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.6f, - customData: new Dictionary - { - // BytesCounter3 is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter3), 419 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 0.95 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.15f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 1.05 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.1f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - { MetricId.Create(TestMetric.SecondsGauge), 117 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled); - - // T == 1.75 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.7f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - { MetricId.Create(TestMetric.SecondsGauge), 117 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 1.85 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.1f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - { MetricId.Create(TestMetric.SecondsGauge), 117 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 2.2 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.35f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - { MetricId.Create(TestMetric.SecondsGauge), 117 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled); - - // T == 2.4 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.2f, - customData: new Dictionary - { - // BytesCounter is configured to be displayed so will - // trigger a display update - { MetricId.Create(TestMetric.BytesCounter), 419 }, - { MetricId.Create(TestMetric.SecondsGauge), 117 }, - }, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - rnsmTestHarness.Teardown(); - } - - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmCustomDataDisplayUpdateTests.cs.meta b/NetStatsMonitor/Tests/Implementation/Component/RnsmCustomDataDisplayUpdateTests.cs.meta deleted file mode 100644 index 5bf2b49..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmCustomDataDisplayUpdateTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0de1da66c8e3842efb2999c4b7c53545 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateStatus.cs b/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateStatus.cs deleted file mode 100644 index cf967ed..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateStatus.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests -{ - enum RnsmDisplayUpdateStatus - { - /// No new data received; display should not be updated - NoDataReceived, - - /// No time has elapsed since last update; display should not be updated - NoTimeElapsed, - - /// Not enough time has elapsed; display should not be updated - NotEnoughTimeElapsed, - - /// Update was not called; display should not be updated - UpdateNotCalled, - - /// The RNSM display is refreshed the first time it receives data, even if no time has elapsed - DataReceivedNoTimeElapsedUpdateCalledFirstTime, - - // New data received, time elapsed, and RNSM update has been called; display should be updated - DataReceivedTimeElapsedUpdateCalled, - } - - static class RnsmDisplayUpdateStatusExtensions - { - internal static bool UpdateExpected(this RnsmDisplayUpdateStatus status) - { - switch (status) - { - case RnsmDisplayUpdateStatus.DataReceivedNoTimeElapsedUpdateCalledFirstTime: - case RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled: - return true; - default: - return false; - } - } - internal static string AssertMessage(this RnsmDisplayUpdateStatus status) - { - switch (status) - { - case RnsmDisplayUpdateStatus.NoDataReceived: - return "No new data received; display should not be updated"; - - case RnsmDisplayUpdateStatus.NoTimeElapsed: - return "No time has elapsed since last update; display should not be updated"; - - case RnsmDisplayUpdateStatus.NotEnoughTimeElapsed: - return "Not enough time has elapsed; display should not be updated"; - - case RnsmDisplayUpdateStatus.UpdateNotCalled: - return "Update was not called; display should not be updated"; - - case RnsmDisplayUpdateStatus.DataReceivedNoTimeElapsedUpdateCalledFirstTime: - return "The RNSM display is refreshed the first time it receives data, even if no time has elapsed"; - - case RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled: - return - "New data received, time elapsed, and RNSM update has been called; display should be updated"; - default: - throw new ArgumentException($"Unhandled {nameof(RnsmDisplayUpdateStatus)} {status}"); - } - } - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateStatus.cs.meta b/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateStatus.cs.meta deleted file mode 100644 index 586c6bb..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateStatus.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 986d67fca193f47299354827a97716ff -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateTests.cs b/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateTests.cs deleted file mode 100644 index 65df32e..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateTests.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using NUnit.Framework; - -using Unity.Multiplayer.Tools.Adapters; -using Unity.Multiplayer.Tools.NetStats; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests -{ - class RnsmDisplayUpdateTests - { - MockNgo1Adapter m_MockNgo1Adapter; - [OneTimeSetUp] - public void Setup() - { - m_MockNgo1Adapter = new MockNgo1Adapter(); - } - - [OneTimeTearDown] - public void Teardown() - { - m_MockNgo1Adapter?.Uninitialize(); - m_MockNgo1Adapter = null; - } - - [Test] - public void EnsureRnsmDisplayOnlyUpdatesWhenNewDataIsReceived() - { - RnsmTestHarness rnsmTestHarness = new RnsmTestHarness(rnsmRefreshRate: 1f); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - generateNewData: false, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 1 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - generateNewData: false, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 2 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - generateNewData: false, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 3 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - generateNewData: true, - updateRnsm: false, - expectedUpdateStatus: RnsmDisplayUpdateStatus.UpdateNotCalled); - - // T == 4 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - generateNewData: false, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled); - - // T == 5 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 1f, - generateNewData: false, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - rnsmTestHarness.Teardown(); - } - - [Test] - public void EnsureRnsmDisplayOnlyUpdatesWhenEnoughTimeHasElapsed() - { - RnsmTestHarness rnsmTestHarness = new RnsmTestHarness(rnsmRefreshRate: 1f); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - generateNewData: false, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoDataReceived); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedNoTimeElapsedUpdateCalledFirstTime); - - // T == 0 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NoTimeElapsed); - - // T == 0.2 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.2f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 0.8 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.6f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 0.95 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.15f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 1.05 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.1f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled); - - // T == 1.75 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.7f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 1.85 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.1f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - // T == 2.2 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.35f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.DataReceivedTimeElapsedUpdateCalled); - - // T == 2.4 - rnsmTestHarness.SimulateFrameAndVerifyDisplayUpdate( - timeElapsed: 0.2f, - generateNewData: true, - updateRnsm: true, - expectedUpdateStatus: RnsmDisplayUpdateStatus.NotEnoughTimeElapsed); - - rnsmTestHarness.Teardown(); - } - } - - class MockNgo1Adapter : INetworkAdapter, IMetricCollectionEvent - { - public MockNgo1Adapter() - { - MetricEvents.MetricEventPublisher.OnMetricsReceived += OnMetricsReceived; - NetworkAdapters.AddAdapter(this); - } - - public void Uninitialize() - { - MetricEvents.MetricEventPublisher.OnMetricsReceived -= OnMetricsReceived; - NetworkAdapters.RemoveAdapter(this); - } - - void OnMetricsReceived(MetricCollection metricCollection) - { - MetricCollectionEvent?.Invoke(metricCollection); - } - - public AdapterMetadata Metadata { get; } - public T GetComponent() where T : class, IAdapterComponent - { - return this as T; - } - - public event Action MetricCollectionEvent; - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateTests.cs.meta b/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateTests.cs.meta deleted file mode 100644 index 6088851..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmDisplayUpdateTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5e3041bf43af74e9a85f7f46e385472d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmTestHarness.cs b/NetStatsMonitor/Tests/Implementation/Component/RnsmTestHarness.cs deleted file mode 100644 index 2d79019..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmTestHarness.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using Unity.Multiplayer.Tools.MetricTestData; -using Unity.Multiplayer.Tools.NetStats; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation; -using UnityEngine; -using UnityEngine.UIElements; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests -{ - internal class RnsmTestHarness - { - NetStatsMonitorConfiguration m_Configuration = ScriptableObject.CreateInstance(); - - RnsmComponentImplementation m_Rnsm = new(); - float m_RnsmRefreshRate = 1f; - - GameObject m_TestDataGeneratorObject; - TestDataGeneratorComponent m_TestDataGenerator; - - double m_CurrentTime = 0; - int m_DisplayUpdateCount = 0; - int m_ExpectedDisplayUpdateCount = 0; - - internal RnsmTestHarness(float rnsmRefreshRate, List statsDisplayed = null) - { - if (statsDisplayed != null) - { - m_Configuration.DisplayElements.Add(new DisplayElementConfiguration - { - Label = "Stats", - Stats = statsDisplayed, - }); - } - var position = new PositionConfiguration(); - var styleSheet = Resources.Load("rnsmStyleSheet"); - - // Mocking time for RNSM - m_Rnsm.GetCurrentTime = () => m_CurrentTime; - - // Callback to record when the display is updated - m_Rnsm.OnDisplayUpdate += () => { ++m_DisplayUpdateCount; }; - - m_Rnsm.SetupAndConfigure(m_Configuration, position, styleSheet, null, m_RnsmRefreshRate); - } - - internal void Teardown() - { - m_Rnsm.Teardown(); - m_Rnsm = null; - Object.DestroyImmediate(m_TestDataGeneratorObject); - } - - /// Simulates a frame and validates that display updates occur - /// in response to new data or elapsed time - internal void SimulateFrameAndVerifyDisplayUpdate( - double timeElapsed = 0, - bool generateNewData = false, - Dictionary customData = null, - bool updateRnsm = true, - RnsmDisplayUpdateStatus expectedUpdateStatus = RnsmDisplayUpdateStatus.NoDataReceived) - { - m_CurrentTime += timeElapsed; - if (customData != null) - { - foreach (var (metric, value) in customData) - { - m_Rnsm.AddCustomValue(metric, value); - } - } - if (generateNewData) - { - if (m_TestDataGeneratorObject == null) - { - m_TestDataGeneratorObject = new GameObject(); - } - if (m_TestDataGenerator == null) - { - m_TestDataGenerator = m_TestDataGeneratorObject - .AddComponent(); - } - m_TestDataGenerator.Update(); - } - if (updateRnsm) - { - m_Rnsm.Update(m_Configuration, m_RnsmRefreshRate); - } - if (expectedUpdateStatus.UpdateExpected()) - { - ++m_ExpectedDisplayUpdateCount; - } - Assert.AreEqual(m_ExpectedDisplayUpdateCount, m_DisplayUpdateCount, expectedUpdateStatus.AssertMessage()); - } - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmTestHarness.cs.meta b/NetStatsMonitor/Tests/Implementation/Component/RnsmTestHarness.cs.meta deleted file mode 100644 index 1fa243c..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmTestHarness.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3f09cf2a324174039b0ec367796c5333 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmVisibilityTests.cs b/NetStatsMonitor/Tests/Implementation/Component/RnsmVisibilityTests.cs deleted file mode 100644 index 1e0e8ed..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmVisibilityTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Collections; -using NUnit.Framework; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation; -using UnityEngine; -using UnityEngine.TestTools; -using UnityEngine.UIElements; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests -{ - static class RnsmTestExtensions - { - internal static UIDocument GetUiDoc(this RuntimeNetStatsMonitor rnsm) - => rnsm.Implementation?.UiDoc; - - internal static VisualElement GetRootVisualElement(this RuntimeNetStatsMonitor rnsm) - => rnsm.Implementation?.UiDoc.rootVisualElement; - - internal static RnsmVisualElement GetRnsmVisualElement(this RuntimeNetStatsMonitor rnsm) - => rnsm.GetRootVisualElement()?.Q(); - - internal static bool ImplementationExists(this RuntimeNetStatsMonitor rnsm) - => rnsm.Implementation != null; - - internal static bool UiDocExists(this RuntimeNetStatsMonitor rnsm) - => rnsm.GetUiDoc() != null; - - internal static bool RootVisualElementExists(this RuntimeNetStatsMonitor rnsm) - => rnsm.GetRootVisualElement() != null; - - internal static bool RnsmVisualElementExists(this RuntimeNetStatsMonitor rnsm) - => rnsm.GetRnsmVisualElement() != null; - - internal static bool RnsmVisualElementVisible(this RuntimeNetStatsMonitor rnsm) - => rnsm.GetRnsmVisualElement()?.visible ?? false; - } - - class RnsmVisibilityTests - { - [Test] - public void EnsureVisibilityIsAppliedToTheRootVisualElement() - { - var go = new GameObject(); - var rnsm = go.AddComponent(); - rnsm.Setup(); - - rnsm.Visible = false; - TestVisibility(rnsm, false); - - rnsm.Visible = true; - TestVisibility(rnsm, true); - - rnsm.Visible = false; - TestVisibility(rnsm, false); - - rnsm.Visible = true; - TestVisibility(rnsm, true); - - rnsm.Teardown(); - Object.DestroyImmediate(go); - } - - internal static void TestVisibility(RuntimeNetStatsMonitor rnsm, bool expectedVisibility) - { - Assert.IsTrue(rnsm.ImplementationExists(), "rnsm.ImplementationExists()"); - Assert.IsTrue(rnsm.UiDocExists(), "rnsm.UiDocExists()"); - Assert.IsTrue(rnsm.RootVisualElementExists(), "rnsm.RootVisualElementExists()"); - Assert.IsTrue(rnsm.RnsmVisualElementExists(), "rnsm.RnsmVisualElementExists()"); - if (expectedVisibility) - { - Assert.IsTrue(rnsm.Visible, "rnsm.Visible"); - Assert.IsTrue(rnsm.RnsmVisualElementVisible(), "rnsm.RnsmVisualElementVisible()"); - } - else - { - Assert.IsFalse(rnsm.Visible, "rnsm.Visible"); - Assert.IsFalse(rnsm.RnsmVisualElementVisible(), "rnsm.RnsmVisualElementVisible()"); - } - } - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Component/RnsmVisibilityTests.cs.meta b/NetStatsMonitor/Tests/Implementation/Component/RnsmVisibilityTests.cs.meta deleted file mode 100644 index 797e593..0000000 --- a/NetStatsMonitor/Tests/Implementation/Component/RnsmVisibilityTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 049424bb45fa04bf78ca7314eda1c274 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Graphing.meta b/NetStatsMonitor/Tests/Implementation/Graphing.meta deleted file mode 100644 index 28b489b..0000000 --- a/NetStatsMonitor/Tests/Implementation/Graphing.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: c54ed47693a74dfbb043306dce17660a -timeCreated: 1645744707 \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Graphing/GraphScalingUtilsTests.cs b/NetStatsMonitor/Tests/Implementation/Graphing/GraphScalingUtilsTests.cs deleted file mode 100644 index e68449c..0000000 --- a/NetStatsMonitor/Tests/Implementation/Graphing/GraphScalingUtilsTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using NUnit.Framework; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation.Graphing -{ - internal static class GraphScalingUtilsTests - { - - // NextHighestRoundNumberTests, numbers in [0..10] - // -------------------------------------------------------------------- - // The "round" numbers are 1, 1.5, 2, 3, 4, 5, 6, 8, and 10, - // multiplied by any power of 10 - - [TestCase(0f , 0f , 0)] - - [TestCase(0.85f , 1f , 0)] - [TestCase(0.97f , 1f , 0)] - [TestCase(1f , 1f , 0)] - - [TestCase(1.000001f, 1.5f, 0)] - [TestCase(1.32f , 1.5f, 0)] - [TestCase(1.45f , 1.5f, 0)] - [TestCase(1.49f , 1.5f, 0)] - [TestCase(1.499999f, 1.5f, 0)] - [TestCase(1.5f , 1.5f, 0)] - - [TestCase(1.500001f, 2f , 0)] - [TestCase(1.5001f , 2f , 0)] - [TestCase(1.75f , 2f , 0)] - [TestCase(1.99999f , 2f , 0)] - [TestCase(2f , 2f , 0)] - - [TestCase(2.000001f, 3f , 0)] - [TestCase(2.4879f , 3f , 0)] - [TestCase(2.4879f , 3f , 0)] - [TestCase(2.5f , 3f , 0)] - [TestCase(2.763912f, 3f , 0)] - [TestCase(2.999999f, 3f , 0)] - [TestCase(3f , 3f , 0)] - - [TestCase(3.000001f, 4f , 0)] - [TestCase(3.5f , 4f , 0)] - [TestCase(3.999999f, 4f , 0)] - [TestCase(4f , 4f , 0)] - - [TestCase(4.000001f, 5f , 0)] - [TestCase(4.5f , 5f , 0)] - [TestCase(4.999999f, 5f , 0)] - [TestCase(5f , 5f , 0)] - - [TestCase(5.000001f, 6f , 0)] - [TestCase(5.5f , 6f , 0)] - [TestCase(5.999999f, 6f , 0)] - [TestCase(6f , 6f , 0)] - - [TestCase(6.000001f, 8f , 0)] - [TestCase(6.5f , 8f , 0)] - [TestCase(6.999999f, 8f , 0)] - [TestCase(7f , 8f , 0)] - [TestCase(7.000001f, 8f , 0)] - [TestCase(7.5f , 8f , 0)] - [TestCase(7.999999f, 8f , 0)] - [TestCase(8f , 8f , 0)] - - [TestCase(8.000001f, 1f , 1)] - [TestCase(8.5f , 1f , 1)] - [TestCase(8.999999f, 1f , 1)] - [TestCase(9f , 1f , 1)] - [TestCase(9.000001f, 1f , 1)] - [TestCase(9.5f , 1f , 1)] - [TestCase(9.999999f, 1f , 1)] - - public static void NextHighestRoundNumber_AtVariousOrdersOfMagnitude_Test( - float input, - float expectedMantissa, - int expectedExponentOf10) - { - // Try various exponents automatically, to avoid needing to write additional tests by hand - for (int i = -9; i <= 9; ++i) - { - var powerOf10 = MathF.Pow(10, i); - var actual = GraphScalingUtils.NextLargestRoundNumber(input * powerOf10); - Assert.AreEqual(expectedMantissa, actual.Mantissa); - if (expectedMantissa != 0) - { - Assert.AreEqual(expectedExponentOf10 + i, actual.Exponent); - } - } - } - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Graphing/GraphScalingUtilsTests.cs.meta b/NetStatsMonitor/Tests/Implementation/Graphing/GraphScalingUtilsTests.cs.meta deleted file mode 100644 index 4af48dc..0000000 --- a/NetStatsMonitor/Tests/Implementation/Graphing/GraphScalingUtilsTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e97394075c05e42f99aea9d0077a60c9 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Stats.meta b/NetStatsMonitor/Tests/Implementation/Stats.meta deleted file mode 100644 index 732c486..0000000 --- a/NetStatsMonitor/Tests/Implementation/Stats.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bba7719d21a44b3aa9651241452c6fd3 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/Stats/StatHistoryRequirementsTests.cs b/NetStatsMonitor/Tests/Implementation/Stats/StatHistoryRequirementsTests.cs deleted file mode 100644 index 8ea84b3..0000000 --- a/NetStatsMonitor/Tests/Implementation/Stats/StatHistoryRequirementsTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -using NUnit.Framework; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Stats -{ - class StatHistoryRequirementsTests - { - [Test] - public void ExponentialMovingAverageParamsSetDoesNotContainDuplicates() - { - var requirements = new StatHistoryRequirements(); - var decayConstants = requirements.DecayConstants; - - decayConstants.Add(0.1); - decayConstants.Add(0.1); - decayConstants.Add(0.1); - decayConstants.Add(0.25); - decayConstants.Add(0.25); - decayConstants.Add(0.25); - decayConstants.Add(0.33); - decayConstants.Add(0.33); - decayConstants.Add(0.5); - decayConstants.Add(0.6874); - decayConstants.Add(0.6874); - decayConstants.Add(0.687434); - decayConstants.Add(0.687434); - decayConstants.Add(0.687434); - decayConstants.Add(0.68743453786895); - decayConstants.Add(0.68743453786895); - decayConstants.Add(0.68743453786895); - decayConstants.Add(0.68743453786895); - - Assert.AreEqual(decayConstants.Count, 7); - } - } -} \ No newline at end of file diff --git a/NetStatsMonitor/Tests/Implementation/Stats/StatHistoryRequirementsTests.cs.meta b/NetStatsMonitor/Tests/Implementation/Stats/StatHistoryRequirementsTests.cs.meta deleted file mode 100644 index c00c077..0000000 --- a/NetStatsMonitor/Tests/Implementation/Stats/StatHistoryRequirementsTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c142d5b3069014940a42799ccec7dfa8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/UI.meta b/NetStatsMonitor/Tests/Implementation/UI.meta deleted file mode 100644 index 4919dd4..0000000 --- a/NetStatsMonitor/Tests/Implementation/UI.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8bd4e807df1b4ba4b9c4df1dc70f5b7f -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/NetStatsMonitor/Tests/Implementation/UI/CounterVisualElementTests.cs b/NetStatsMonitor/Tests/Implementation/UI/CounterVisualElementTests.cs deleted file mode 100644 index 3452d00..0000000 --- a/NetStatsMonitor/Tests/Implementation/UI/CounterVisualElementTests.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using NUnit.Framework; -using Unity.Multiplayer.Tools.NetStats; -using Unity.Multiplayer.Tools.NetStats.Tests; -using Unity.Multiplayer.Tools.NetStatsMonitor.Implementation; -using UnityEngine.UIElements; - -namespace Unity.Multiplayer.Tools.NetStatsMonitor.Tests.Implementation.UI -{ - internal class CounterVisualElementTests - { - static readonly MetricId k_UnitlessGauge - = MetricId.Create(TestMetric.UnitlessGauge); - - [TestCase("Total Bytes Sent")] - [TestCase("totalbytesreceived")] - [TestCase("TOTAL_MESSAGES_SENT")] - [TestCase("total-messages-received")] - [TestCase("148.2")] - [TestCase("-9810")] - [TestCase("with random numbers 0987134 and symbols )(*&# 818483)(*&$%")] - [TestCase("\nars\tt0;834\n\t0193874")] - public void CounterDisplaysCorrectLabel(string counterLabel) - { - var configuration = MakeEmaCounterConfiguration(counterLabel, new List{k_UnitlessGauge}); - var counter = new CounterVisualElement(); - counter.UpdateConfiguration(configuration); - - var label = counter.Q