#if MULTIPLAYER_TOOLS using System; using System.Collections.Generic; using Unity.Multiplayer.Tools; using Unity.Multiplayer.Tools.MetricTypes; using Unity.Multiplayer.Tools.NetStats; using Unity.Profiling; namespace Unity.Netcode { internal class NetworkMetrics : INetworkMetrics { private const ulong k_MaxMetricsPerFrame = 1000L; private static Dictionary s_SceneEventTypeNames; private static ProfilerMarker s_FrameDispatch = new ProfilerMarker($"{nameof(NetworkMetrics)}.DispatchFrame"); static NetworkMetrics() { s_SceneEventTypeNames = new Dictionary(); foreach (SceneEventType type in Enum.GetValues(typeof(SceneEventType))) { s_SceneEventTypeNames[(uint)type] = type.ToString(); } } private static string GetSceneEventTypeName(uint typeCode) { if (!s_SceneEventTypeNames.TryGetValue(typeCode, out string name)) { name = "Unknown"; } return name; } private readonly Counter m_TransportBytesSent = new Counter(NetworkMetricTypes.TotalBytesSent.Id) { ShouldResetOnDispatch = true, }; private readonly Counter m_TransportBytesReceived = new Counter(NetworkMetricTypes.TotalBytesReceived.Id) { ShouldResetOnDispatch = true, }; private readonly EventMetric m_NetworkMessageSentEvent = new EventMetric(NetworkMetricTypes.NetworkMessageSent.Id); private readonly EventMetric m_NetworkMessageReceivedEvent = new EventMetric(NetworkMetricTypes.NetworkMessageReceived.Id); private readonly EventMetric m_NamedMessageSentEvent = new EventMetric(NetworkMetricTypes.NamedMessageSent.Id); private readonly EventMetric m_NamedMessageReceivedEvent = new EventMetric(NetworkMetricTypes.NamedMessageReceived.Id); private readonly EventMetric m_UnnamedMessageSentEvent = new EventMetric(NetworkMetricTypes.UnnamedMessageSent.Id); private readonly EventMetric m_UnnamedMessageReceivedEvent = new EventMetric(NetworkMetricTypes.UnnamedMessageReceived.Id); private readonly EventMetric m_NetworkVariableDeltaSentEvent = new EventMetric(NetworkMetricTypes.NetworkVariableDeltaSent.Id); private readonly EventMetric m_NetworkVariableDeltaReceivedEvent = new EventMetric(NetworkMetricTypes.NetworkVariableDeltaReceived.Id); private readonly EventMetric m_OwnershipChangeSentEvent = new EventMetric(NetworkMetricTypes.OwnershipChangeSent.Id); private readonly EventMetric m_OwnershipChangeReceivedEvent = new EventMetric(NetworkMetricTypes.OwnershipChangeReceived.Id); private readonly EventMetric m_ObjectSpawnSentEvent = new EventMetric(NetworkMetricTypes.ObjectSpawnedSent.Id); private readonly EventMetric m_ObjectSpawnReceivedEvent = new EventMetric(NetworkMetricTypes.ObjectSpawnedReceived.Id); private readonly EventMetric m_ObjectDestroySentEvent = new EventMetric(NetworkMetricTypes.ObjectDestroyedSent.Id); private readonly EventMetric m_ObjectDestroyReceivedEvent = new EventMetric(NetworkMetricTypes.ObjectDestroyedReceived.Id); private readonly EventMetric m_RpcSentEvent = new EventMetric(NetworkMetricTypes.RpcSent.Id); private readonly EventMetric m_RpcReceivedEvent = new EventMetric(NetworkMetricTypes.RpcReceived.Id); private readonly EventMetric m_ServerLogSentEvent = new EventMetric(NetworkMetricTypes.ServerLogSent.Id); private readonly EventMetric m_ServerLogReceivedEvent = new EventMetric(NetworkMetricTypes.ServerLogReceived.Id); private readonly EventMetric m_SceneEventSentEvent = new EventMetric(NetworkMetricTypes.SceneEventSent.Id); private readonly EventMetric m_SceneEventReceivedEvent = new EventMetric(NetworkMetricTypes.SceneEventReceived.Id); #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 private readonly Counter m_PacketSentCounter = new Counter(NetworkMetricTypes.PacketsSent.Id) { ShouldResetOnDispatch = true, }; private readonly Counter m_PacketReceivedCounter = new Counter(NetworkMetricTypes.PacketsReceived.Id) { ShouldResetOnDispatch = true, }; private readonly Gauge m_RttToServerGauge = new Gauge(NetworkMetricTypes.RttToServer.Id) { ShouldResetOnDispatch = true, }; private readonly Gauge m_NetworkObjectsGauge = new Gauge(NetworkMetricTypes.NetworkObjects.Id) { ShouldResetOnDispatch = true, }; private readonly Gauge m_ConnectionsGauge = new Gauge(NetworkMetricTypes.ConnectedClients.Id) { ShouldResetOnDispatch = true, }; private readonly Gauge m_PacketLossGauge = new Gauge(NetworkMetricTypes.PacketLoss.Id); #endif private ulong m_NumberOfMetricsThisFrame; public NetworkMetrics() { Dispatcher = new MetricDispatcherBuilder() .WithCounters(m_TransportBytesSent, m_TransportBytesReceived) .WithMetricEvents(m_NetworkMessageSentEvent, m_NetworkMessageReceivedEvent) .WithMetricEvents(m_NamedMessageSentEvent, m_NamedMessageReceivedEvent) .WithMetricEvents(m_UnnamedMessageSentEvent, m_UnnamedMessageReceivedEvent) .WithMetricEvents(m_NetworkVariableDeltaSentEvent, m_NetworkVariableDeltaReceivedEvent) .WithMetricEvents(m_OwnershipChangeSentEvent, m_OwnershipChangeReceivedEvent) .WithMetricEvents(m_ObjectSpawnSentEvent, m_ObjectSpawnReceivedEvent) .WithMetricEvents(m_ObjectDestroySentEvent, m_ObjectDestroyReceivedEvent) .WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent) .WithMetricEvents(m_ServerLogSentEvent, m_ServerLogReceivedEvent) .WithMetricEvents(m_SceneEventSentEvent, m_SceneEventReceivedEvent) #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 .WithCounters(m_PacketSentCounter, m_PacketReceivedCounter) .WithGauges(m_RttToServerGauge) .WithGauges(m_NetworkObjectsGauge) .WithGauges(m_ConnectionsGauge) .WithGauges(m_PacketLossGauge) #endif .Build(); Dispatcher.RegisterObserver(NetcodeObserver.Observer); } internal IMetricDispatcher Dispatcher { get; } private bool CanSendMetrics => m_NumberOfMetricsThisFrame < k_MaxMetricsPerFrame; public void SetConnectionId(ulong connectionId) { Dispatcher.SetConnectionId(connectionId); } public void TrackTransportBytesSent(long bytesCount) { m_TransportBytesSent.Increment(bytesCount); } public void TrackTransportBytesReceived(long bytesCount) { m_TransportBytesReceived.Increment(bytesCount); } public void TrackNetworkMessageSent(ulong receivedClientId, string messageType, long bytesCount) { if (!CanSendMetrics) { return; } m_NetworkMessageSentEvent.Mark(new NetworkMessageEvent(new ConnectionInfo(receivedClientId), messageType, bytesCount)); IncrementMetricCount(); } public void TrackNetworkMessageReceived(ulong senderClientId, string messageType, long bytesCount) { if (!CanSendMetrics) { return; } m_NetworkMessageReceivedEvent.Mark(new NetworkMessageEvent(new ConnectionInfo(senderClientId), messageType, bytesCount)); IncrementMetricCount(); } public void TrackNamedMessageSent(ulong receiverClientId, string messageName, long bytesCount) { if (!CanSendMetrics) { return; } m_NamedMessageSentEvent.Mark(new NamedMessageEvent(new ConnectionInfo(receiverClientId), messageName, bytesCount)); IncrementMetricCount(); } public void TrackNamedMessageSent(IReadOnlyCollection receiverClientIds, string messageName, long bytesCount) { foreach (var receiver in receiverClientIds) { TrackNamedMessageSent(receiver, messageName, bytesCount); } } public void TrackNamedMessageReceived(ulong senderClientId, string messageName, long bytesCount) { if (!CanSendMetrics) { return; } m_NamedMessageReceivedEvent.Mark(new NamedMessageEvent(new ConnectionInfo(senderClientId), messageName, bytesCount)); IncrementMetricCount(); } public void TrackUnnamedMessageSent(ulong receiverClientId, long bytesCount) { if (!CanSendMetrics) { return; } m_UnnamedMessageSentEvent.Mark(new UnnamedMessageEvent(new ConnectionInfo(receiverClientId), bytesCount)); IncrementMetricCount(); } public void TrackUnnamedMessageSent(IReadOnlyCollection receiverClientIds, long bytesCount) { foreach (var receiverClientId in receiverClientIds) { TrackUnnamedMessageSent(receiverClientId, bytesCount); } } public void TrackUnnamedMessageReceived(ulong senderClientId, long bytesCount) { if (!CanSendMetrics) { return; } m_UnnamedMessageReceivedEvent.Mark(new UnnamedMessageEvent(new ConnectionInfo(senderClientId), bytesCount)); IncrementMetricCount(); } public void TrackNetworkVariableDeltaSent( ulong receiverClientId, NetworkObject networkObject, string variableName, string networkBehaviourName, long bytesCount) { if (!CanSendMetrics) { return; } m_NetworkVariableDeltaSentEvent.Mark( new NetworkVariableEvent( new ConnectionInfo(receiverClientId), GetObjectIdentifier(networkObject), variableName, networkBehaviourName, bytesCount)); IncrementMetricCount(); } public void TrackNetworkVariableDeltaReceived( ulong senderClientId, NetworkObject networkObject, string variableName, string networkBehaviourName, long bytesCount) { if (!CanSendMetrics) { return; } m_NetworkVariableDeltaReceivedEvent.Mark( new NetworkVariableEvent( new ConnectionInfo(senderClientId), GetObjectIdentifier(networkObject), variableName, networkBehaviourName, bytesCount)); IncrementMetricCount(); } public void TrackOwnershipChangeSent(ulong receiverClientId, NetworkObject networkObject, long bytesCount) { if (!CanSendMetrics) { return; } m_OwnershipChangeSentEvent.Mark(new OwnershipChangeEvent(new ConnectionInfo(receiverClientId), GetObjectIdentifier(networkObject), bytesCount)); IncrementMetricCount(); } public void TrackOwnershipChangeReceived(ulong senderClientId, NetworkObject networkObject, long bytesCount) { if (!CanSendMetrics) { return; } m_OwnershipChangeReceivedEvent.Mark(new OwnershipChangeEvent(new ConnectionInfo(senderClientId), GetObjectIdentifier(networkObject), bytesCount)); IncrementMetricCount(); } public void TrackObjectSpawnSent(ulong receiverClientId, NetworkObject networkObject, long bytesCount) { if (!CanSendMetrics) { return; } m_ObjectSpawnSentEvent.Mark(new ObjectSpawnedEvent(new ConnectionInfo(receiverClientId), GetObjectIdentifier(networkObject), bytesCount)); IncrementMetricCount(); } public void TrackObjectSpawnReceived(ulong senderClientId, NetworkObject networkObject, long bytesCount) { if (!CanSendMetrics) { return; } m_ObjectSpawnReceivedEvent.Mark(new ObjectSpawnedEvent(new ConnectionInfo(senderClientId), GetObjectIdentifier(networkObject), bytesCount)); IncrementMetricCount(); } public void TrackObjectDestroySent(ulong receiverClientId, NetworkObject networkObject, long bytesCount) { if (!CanSendMetrics) { return; } m_ObjectDestroySentEvent.Mark(new ObjectDestroyedEvent(new ConnectionInfo(receiverClientId), GetObjectIdentifier(networkObject), bytesCount)); IncrementMetricCount(); } public void TrackObjectDestroyReceived(ulong senderClientId, NetworkObject networkObject, long bytesCount) { if (!CanSendMetrics) { return; } m_ObjectDestroyReceivedEvent.Mark(new ObjectDestroyedEvent(new ConnectionInfo(senderClientId), GetObjectIdentifier(networkObject), bytesCount)); IncrementMetricCount(); } public void TrackRpcSent( ulong receiverClientId, NetworkObject networkObject, string rpcName, string networkBehaviourName, long bytesCount) { if (!CanSendMetrics) { return; } m_RpcSentEvent.Mark( new RpcEvent( new ConnectionInfo(receiverClientId), GetObjectIdentifier(networkObject), rpcName, networkBehaviourName, bytesCount)); IncrementMetricCount(); } public void TrackRpcSent( ulong[] receiverClientIds, NetworkObject networkObject, string rpcName, string networkBehaviourName, long bytesCount) { foreach (var receiverClientId in receiverClientIds) { TrackRpcSent(receiverClientId, networkObject, rpcName, networkBehaviourName, bytesCount); } } public void TrackRpcReceived( ulong senderClientId, NetworkObject networkObject, string rpcName, string networkBehaviourName, long bytesCount) { if (!CanSendMetrics) { return; } m_RpcReceivedEvent.Mark( new RpcEvent(new ConnectionInfo(senderClientId), GetObjectIdentifier(networkObject), rpcName, networkBehaviourName, bytesCount)); IncrementMetricCount(); } public void TrackServerLogSent(ulong receiverClientId, uint logType, long bytesCount) { if (!CanSendMetrics) { return; } m_ServerLogSentEvent.Mark(new ServerLogEvent(new ConnectionInfo(receiverClientId), (Multiplayer.Tools.MetricTypes.LogLevel)logType, bytesCount)); IncrementMetricCount(); } public void TrackServerLogReceived(ulong senderClientId, uint logType, long bytesCount) { if (!CanSendMetrics) { return; } m_ServerLogReceivedEvent.Mark(new ServerLogEvent(new ConnectionInfo(senderClientId), (Multiplayer.Tools.MetricTypes.LogLevel)logType, bytesCount)); IncrementMetricCount(); } public void TrackSceneEventSent(IReadOnlyList receiverClientIds, uint sceneEventType, string sceneName, long bytesCount) { foreach (var receiverClientId in receiverClientIds) { TrackSceneEventSent(receiverClientId, sceneEventType, sceneName, bytesCount); } } public void TrackSceneEventSent(ulong receiverClientId, uint sceneEventType, string sceneName, long bytesCount) { if (!CanSendMetrics) { return; } m_SceneEventSentEvent.Mark(new SceneEventMetric(new ConnectionInfo(receiverClientId), GetSceneEventTypeName(sceneEventType), sceneName, bytesCount)); IncrementMetricCount(); } public void TrackSceneEventReceived(ulong senderClientId, uint sceneEventType, string sceneName, long bytesCount) { if (!CanSendMetrics) { return; } m_SceneEventReceivedEvent.Mark(new SceneEventMetric(new ConnectionInfo(senderClientId), GetSceneEventTypeName(sceneEventType), sceneName, bytesCount)); IncrementMetricCount(); } public void TrackPacketSent(uint packetCount) { #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 if (!CanSendMetrics) { return; } m_PacketSentCounter.Increment(packetCount); IncrementMetricCount(); #endif } public void TrackPacketReceived(uint packetCount) { #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 if (!CanSendMetrics) { return; } m_PacketReceivedCounter.Increment(packetCount); IncrementMetricCount(); #endif } public void UpdateRttToServer(int rttMilliseconds) { #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 if (!CanSendMetrics) { return; } var rttSeconds = rttMilliseconds * 1e-3; m_RttToServerGauge.Set(rttSeconds); #endif } public void UpdateNetworkObjectsCount(int count) { #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 if (!CanSendMetrics) { return; } m_NetworkObjectsGauge.Set(count); #endif } public void UpdateConnectionsCount(int count) { #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 if (!CanSendMetrics) { return; } m_ConnectionsGauge.Set(count); #endif } public void UpdatePacketLoss(float packetLoss) { #if MULTIPLAYER_TOOLS_1_0_0_PRE_7 if (!CanSendMetrics) { return; } m_PacketLossGauge.Set(packetLoss); #endif } public void DispatchFrame() { s_FrameDispatch.Begin(); Dispatcher.Dispatch(); s_FrameDispatch.End(); m_NumberOfMetricsThisFrame = 0; } private void IncrementMetricCount() { m_NumberOfMetricsThisFrame++; } private static NetworkObjectIdentifier GetObjectIdentifier(NetworkObject networkObject) { return new NetworkObjectIdentifier(networkObject.GetNameForMetrics(), networkObject.NetworkObjectId); } } internal class NetcodeObserver { public static IMetricObserver Observer { get; } = MetricObserverFactory.Construct(); } } #endif