diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
index dc1832df8b..1af4e45b31 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
@@ -1243,6 +1243,7 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel,
{
var messageType = (MessageQueueContainer.MessageType)messageStream.ReadByte();
MessageHandler.MessageReceiveQueueItem(clientId, messageStream, receiveTime, messageType, networkChannel);
+ NetworkMetrics.TrackNetworkMessageReceived(clientId, MessageQueueContainer.GetMessageTypeName(messageType), data.Count);
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
s_HandleIncomingData.End();
@@ -1253,6 +1254,7 @@ private void ReceiveCallback(NetworkBuffer messageBuffer, MessageQueueContainer.
float receiveTime, NetworkChannel receiveChannel)
{
MessageHandler.MessageReceiveQueueItem(clientId, messageBuffer, receiveTime, messageType, receiveChannel);
+ NetworkMetrics.TrackNetworkMessageReceived(clientId, MessageQueueContainer.GetMessageTypeName(messageType), messageBuffer.Length);
}
///
diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs
index 7e27a36d56..978d4fcfa6 100644
--- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageBatcher.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Linq;
using System.Collections.Generic;
namespace Unity.Netcode
@@ -34,8 +33,7 @@ public void Shutdown()
m_SendDict.Clear();
}
- // Used to store targets, internally
- private ulong[] m_TargetList = new ulong[0];
+
// Used to mark longer lengths. Works because we can't have zero-sized messages
private const byte k_LongLenMarker = 0;
@@ -84,40 +82,18 @@ private int PopLength(in NetworkBuffer messageBuffer)
return len1 + len2 * 256;
}
- ///
- /// FillTargetList
- /// Fills a list with the ClientId's an item is targeted to
- ///
- /// the FrameQueueItem we want targets for
- /// the list to fill
- private static void FillTargetList(in MessageFrameItem item, ref ulong[] networkIdList)
- {
- switch (item.MessageType)
- {
- // todo: revisit .resize() and .ToArry() usage, for performance
- case MessageQueueContainer.MessageType.ServerRpc:
- Array.Resize(ref networkIdList, 1);
- networkIdList[0] = item.NetworkId;
- break;
- default:
- // todo: consider the implications of default usage of queueItem.clientIds
- case MessageQueueContainer.MessageType.ClientRpc:
- // copy the list
- networkIdList = item.ClientNetworkIds.ToArray();
- break;
- }
- }
-
///
/// QueueItem
/// Add a FrameQueueItem to be sent
/// queueItem
/// the threshold in bytes
- public void QueueItem(in MessageFrameItem item, int automaticSendThresholdBytes, SendCallbackType sendCallback)
+ public void QueueItem(
+ IReadOnlyCollection targetList,
+ in MessageFrameItem item,
+ int automaticSendThresholdBytes,
+ SendCallbackType sendCallback)
{
- FillTargetList(item, ref m_TargetList);
-
- foreach (ulong clientId in m_TargetList)
+ foreach (ulong clientId in targetList)
{
if (!m_SendDict.ContainsKey(clientId))
{
diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs
index fec17c426e..314ff409cc 100644
--- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs
@@ -44,6 +44,29 @@ public enum MessageQueueProcessingTypes
Receive,
}
+ private static readonly IReadOnlyDictionary k_MessageTypeNames;
+
+ static MessageQueueContainer()
+ {
+ var messageTypeNames = new Dictionary();
+ foreach(var messageType in Enum.GetValues(typeof(MessageType)))
+ {
+ messageTypeNames.Add((int)messageType, messageType.ToString());
+ }
+
+ k_MessageTypeNames = messageTypeNames;
+ }
+
+ public static string GetMessageTypeName(MessageType messageType)
+ {
+ if (!k_MessageTypeNames.TryGetValue((int)messageType, out var messageTypeName))
+ {
+ messageTypeName = string.Empty;
+ }
+
+ return messageTypeName;
+ }
+
// Inbound and Outbound QueueHistoryFrames
private readonly Dictionary>> m_QueueHistory =
new Dictionary>>();
diff --git a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs
index 93666fde25..f60c6a9321 100644
--- a/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
@@ -29,6 +30,7 @@ internal class MessageQueueProcessor
private MessageQueueContainer m_MessageQueueContainer;
private readonly NetworkManager m_NetworkManager;
+ private readonly List m_TargetIdBuffer = new List();
public void Shutdown()
{
@@ -217,6 +219,28 @@ internal void ProcessSendQueue(bool isListening)
#endif
}
+ ///
+ /// FillTargetList
+ /// Fills a list with the ClientId's an item is targeted to
+ ///
+ /// the MessageQueueItem we want targets for
+ /// the list to fill
+ private static void FillTargetList(in MessageFrameItem item, List targetList)
+ {
+ switch (item.MessageType)
+ {
+ case MessageQueueContainer.MessageType.ServerRpc:
+ targetList.Add(item.NetworkId);
+ break;
+ default:
+ // todo: consider the implications of default usage of queueItem.clientIds
+ case MessageQueueContainer.MessageType.ClientRpc:
+ // copy the list
+ targetList.AddRange(item.ClientNetworkIds);
+ break;
+ }
+ }
+
///
/// Sends all message queue items in the current outbound frame
///
@@ -235,13 +259,21 @@ private void MessageQueueSendAndFlush(bool isListening)
advanceFrameHistory = true;
if (isListening)
{
+ m_TargetIdBuffer.Clear();
+ FillTargetList(currentQueueItem, m_TargetIdBuffer);
+
if (m_MessageQueueContainer.IsUsingBatching())
{
- m_MessageBatcher.QueueItem(currentQueueItem, k_BatchThreshold, SendCallback);
+ m_MessageBatcher.QueueItem(m_TargetIdBuffer, currentQueueItem, k_BatchThreshold, SendCallback);
}
else
{
- SendFrameQueueItem(currentQueueItem);
+ SendFrameQueueItem(m_TargetIdBuffer, currentQueueItem);
+ }
+
+ foreach (var target in m_TargetIdBuffer)
+ {
+ m_NetworkManager.NetworkMetrics.TrackNetworkMessageSent(target, MessageQueueContainer.GetMessageTypeName(currentQueueItem.MessageType), currentQueueItem.MessageData.Count);
}
}
@@ -293,7 +325,7 @@ private void SendCallback(ulong clientId, MessageBatcher.SendStream sendStream)
/// Sends the Message Queue Item to the specified destination
///
/// Information on what to send
- private void SendFrameQueueItem(MessageFrameItem item)
+ private void SendFrameQueueItem(IReadOnlyCollection targetIds, in MessageFrameItem item)
{
var channel = item.NetworkChannel;
// If the length is greater than the fragmented threshold, switch to a fragmented channel.
@@ -304,25 +336,11 @@ private void SendFrameQueueItem(MessageFrameItem item)
{
channel = NetworkChannel.Fragmented;
}
- switch (item.MessageType)
- {
- case MessageQueueContainer.MessageType.ServerRpc:
- // TODO: Can we remove this special case for server RPCs?
- {
- m_MessageQueueContainer.NetworkManager.NetworkMetrics.TrackTransportBytesSent(item.MessageData.Count);
- m_MessageQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(item.NetworkId, item.MessageData, channel);
- break;
- }
- default:
- {
- foreach (ulong clientid in item.ClientNetworkIds)
- {
- m_MessageQueueContainer.NetworkManager.NetworkMetrics.TrackTransportBytesSent(item.MessageData.Count);
- m_MessageQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(clientid, item.MessageData, channel);
- }
- break;
- }
+ foreach (var clientId in targetIds)
+ {
+ m_MessageQueueContainer.NetworkManager.NetworkMetrics.TrackTransportBytesSent(item.MessageData.Count);
+ m_MessageQueueContainer.NetworkManager.NetworkConfig.NetworkTransport.Send(clientId, item.MessageData, channel);
}
}
diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/INetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/INetworkMetrics.cs
index 14d7e3275e..05bf0f3098 100644
--- a/com.unity.netcode.gameobjects/Runtime/Metrics/INetworkMetrics.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Metrics/INetworkMetrics.cs
@@ -8,6 +8,10 @@ internal interface INetworkMetrics
void TrackTransportBytesReceived(long bytesCount);
+ void TrackNetworkMessageSent(ulong receivedClientId, string messageType, long bytesCount);
+
+ void TrackNetworkMessageReceived(ulong senderClientId, string messageType, long bytesCount);
+
void TrackNetworkObject(NetworkObject networkObject);
void TrackNamedMessageSent(ulong receiverClientId, string messageName, long bytesCount);
diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs
index 1a816873fa..4d160fce18 100644
--- a/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs
@@ -17,6 +17,8 @@ internal class NetworkMetrics : INetworkMetrics
ShouldResetOnDispatch = true,
};
+ readonly EventMetric m_NetworkMessageSentEvent = new EventMetric(NetworkMetricTypes.NetworkMessageSent.Id);
+ readonly EventMetric m_NetworkMessageReceivedEvent = new EventMetric(NetworkMetricTypes.NetworkMessageReceived.Id);
readonly EventMetric m_NamedMessageSentEvent = new EventMetric(NetworkMetricTypes.NamedMessageSent.Id);
readonly EventMetric m_NamedMessageReceivedEvent = new EventMetric(NetworkMetricTypes.NamedMessageReceived.Id);
readonly EventMetric m_UnnamedMessageSentEvent = new EventMetric(NetworkMetricTypes.UnnamedMessageSent.Id);
@@ -42,6 +44,7 @@ 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)
@@ -76,6 +79,16 @@ public void TrackNetworkObject(NetworkObject networkObject)
}
}
+ public void TrackNetworkMessageSent(ulong receivedClientId, string messageType, long bytesCount)
+ {
+ m_NetworkMessageSentEvent.Mark(new NetworkMessageEvent(new ConnectionInfo(receivedClientId), messageType, bytesCount));
+ }
+
+ public void TrackNetworkMessageReceived(ulong senderClientId, string messageType, long bytesCount)
+ {
+ m_NetworkMessageReceivedEvent.Mark(new NetworkMessageEvent(new ConnectionInfo(senderClientId), messageType, bytesCount));
+ }
+
public void TrackNamedMessageSent(ulong receiverClientId, string messageName, long bytesCount)
{
m_NamedMessageSentEvent.Mark(new NamedMessageEvent(new ConnectionInfo(receiverClientId), messageName, bytesCount));
diff --git a/com.unity.netcode.gameobjects/Runtime/Metrics/NullNetworkMetrics.cs b/com.unity.netcode.gameobjects/Runtime/Metrics/NullNetworkMetrics.cs
index df27bb0857..9a7bd97b18 100644
--- a/com.unity.netcode.gameobjects/Runtime/Metrics/NullNetworkMetrics.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Metrics/NullNetworkMetrics.cs
@@ -11,6 +11,14 @@ public void TrackTransportBytesSent(long bytesCount)
public void TrackTransportBytesReceived(long bytesCount)
{
}
+
+ public void TrackNetworkMessageSent(ulong receivedClientId, string messageType, long bytesCount)
+ {
+ }
+
+ public void TrackNetworkMessageReceived(ulong senderClientId, string messageType, long bytesCount)
+ {
+ }
public void TrackNetworkObject(NetworkObject networkObject)
{
diff --git a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs
index 53ffaa74f0..a88209bdad 100644
--- a/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Editor/MessageBatcherTests.cs
@@ -26,7 +26,9 @@ public void SendWithThreshold()
MessageType = i % 2 == 0 ? MessageQueueContainer.MessageType.ServerRpc : MessageQueueContainer.MessageType.ClientRpc,
MessageData = new ArraySegment(randomData, 0, randomData.Length)
};
- sendBatcher.QueueItem(queueItem,
+ sendBatcher.QueueItem(
+ queueItem.ClientNetworkIds,
+ queueItem,
k_BatchThreshold,
(networkId, sendStream) =>
{
@@ -75,7 +77,9 @@ public void SendWithoutThreshold()
MessageType = i % 2 == 0 ? MessageQueueContainer.MessageType.ServerRpc : MessageQueueContainer.MessageType.ClientRpc,
MessageData = new ArraySegment(randomData, 0, randomData.Length)
};
- sendBatcher.QueueItem(queueItem,
+ sendBatcher.QueueItem(
+ queueItem.ClientNetworkIds,
+ queueItem,
k_BatchThreshold,
(networkId, sendStream) =>
{
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs
index f52488bb14..f721a6157c 100644
--- a/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/Metrics/MessagingMetricsTests.cs
@@ -22,6 +22,73 @@ public class MessagingMetricsTests : DualClientMetricTestBase
protected override int NbClients => 2;
+ [UnityTest]
+ public IEnumerator TrackNetworkMessageSentMetric()
+ {
+ var messageName = Guid.NewGuid().ToString();
+ using var memoryStream = new MemoryStream();
+ using var binaryWriter = new BinaryWriter(memoryStream);
+ binaryWriter.Write(messageName);
+
+ var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent);
+
+ Server.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, memoryStream);
+
+ yield return waitForMetricValues.WaitForMetricsReceived();
+
+ var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
+ Assert.AreEqual(1, networkMessageSentMetricValues.Count);
+
+ var networkMessageEvent = networkMessageSentMetricValues.First();
+ Assert.AreEqual(MessageQueueContainer.GetMessageTypeName(MessageQueueContainer.MessageType.NamedMessage), networkMessageEvent.Name);
+ Assert.AreEqual(FirstClient.LocalClientId, networkMessageEvent.Connection.Id);
+ }
+
+ [UnityTest]
+ public IEnumerator TrackNetworkMessageSentMetricToMultipleClients()
+ {
+ var messageName = Guid.NewGuid().ToString();
+ using var memoryStream = new MemoryStream();
+ using var binaryWriter = new BinaryWriter(memoryStream);
+ binaryWriter.Write(messageName);
+
+ var waitForMetricValues = new WaitForMetricValues(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent);
+
+ Server.CustomMessagingManager.SendNamedMessage(messageName, new List { FirstClient.LocalClientId, SecondClient.LocalClientId }, memoryStream);
+
+ yield return waitForMetricValues.WaitForMetricsReceived();
+
+ var networkMessageSentMetricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
+ Assert.AreEqual(2, networkMessageSentMetricValues.Count(x => x.Name.Equals(MessageQueueContainer.GetMessageTypeName(MessageQueueContainer.MessageType.NamedMessage))));
+ }
+
+ [UnityTest]
+ public IEnumerator TrackNetworkMessageReceivedMetric()
+ {
+ var messageName = Guid.NewGuid().ToString();
+ using var memoryStream = new MemoryStream();
+ using var binaryWriter = new BinaryWriter(memoryStream);
+ binaryWriter.Write(messageName);
+
+ LogAssert.Expect(LogType.Log, $"Received from {Server.LocalClientId}");
+ FirstClient.CustomMessagingManager.RegisterNamedMessageHandler(messageName, (sender, payload) =>
+ {
+ Debug.Log($"Received from {sender}");
+ });
+
+ var waitForMetricValues = new WaitForMetricValues(FirstClientMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageReceived);
+
+ Server.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, memoryStream);
+
+ yield return waitForMetricValues.WaitForMetricsReceived();
+
+ var networkMessageReceivedValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
+ Assert.AreEqual(1, networkMessageReceivedValues.Count(x => x.Name.Equals(MessageQueueContainer.GetMessageTypeName(MessageQueueContainer.MessageType.NamedMessage))));
+
+ var namedMessageReceived = networkMessageReceivedValues.First();
+ Assert.AreEqual(Server.LocalClientId, namedMessageReceived.Connection.Id);
+ }
+
[UnityTest]
public IEnumerator TrackNamedMessageSentMetric()
{
diff --git a/testproject-tools-integration/Packages/manifest.json b/testproject-tools-integration/Packages/manifest.json
index e3ff4cbc20..7d187e5d57 100644
--- a/testproject-tools-integration/Packages/manifest.json
+++ b/testproject-tools-integration/Packages/manifest.json
@@ -3,7 +3,7 @@
"dependencies": {
"com.unity.ide.rider": "3.0.7",
"com.unity.netcode.gameobjects": "file:../../com.unity.netcode.gameobjects",
- "com.unity.multiplayer.tools": "0.0.1-preview.7",
+ "com.unity.multiplayer.tools": "0.0.1-preview.8",
"com.unity.multiplayer.transport.utp": "file:../../com.unity.multiplayer.transport.utp",
"com.unity.test-framework": "1.1.26",
"com.unity.modules.ai": "1.0.0",