Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

namespace Unity.Netcode
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -84,40 +82,18 @@ private int PopLength(in NetworkBuffer messageBuffer)
return len1 + len2 * 256;
}

/// <summary>
/// FillTargetList
/// Fills a list with the ClientId's an item is targeted to
/// </summary>
/// <param name="item">the FrameQueueItem we want targets for</param>
/// <param name="networkIdList">the list to fill</param>
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;
}
}

/// <summary>
/// QueueItem
/// Add a FrameQueueItem to be sent
/// </summary>queueItem
/// <param name="item">the threshold in bytes</param>
public void QueueItem(in MessageFrameItem item, int automaticSendThresholdBytes, SendCallbackType sendCallback)
public void QueueItem(
IReadOnlyCollection<ulong> 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))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ public enum MessageQueueProcessingTypes
Receive,
}

private static readonly IReadOnlyDictionary<int, string> k_MessageTypeNames;

static MessageQueueContainer()
{
var messageTypeNames = new Dictionary<int, string>();
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<MessageQueueHistoryFrame.QueueFrameType, Dictionary<int, Dictionary<NetworkUpdateStage, MessageQueueHistoryFrame>>> m_QueueHistory =
new Dictionary<MessageQueueHistoryFrame.QueueFrameType, Dictionary<int, Dictionary<NetworkUpdateStage, MessageQueueHistoryFrame>>>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;

Expand Down Expand Up @@ -29,6 +30,7 @@ internal class MessageQueueProcessor
private MessageQueueContainer m_MessageQueueContainer;

private readonly NetworkManager m_NetworkManager;
private readonly List<ulong> m_TargetIdBuffer = new List<ulong>();

public void Shutdown()
{
Expand Down Expand Up @@ -217,6 +219,28 @@ internal void ProcessSendQueue(bool isListening)
#endif
}

/// <summary>
/// FillTargetList
/// Fills a list with the ClientId's an item is targeted to
/// </summary>
/// <param name="item">the MessageQueueItem we want targets for</param>
/// <param name="targetList">the list to fill</param>
private static void FillTargetList(in MessageFrameItem item, List<ulong> 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;
}
}

/// <summary>
/// Sends all message queue items in the current outbound frame
/// </summary>
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -293,7 +325,7 @@ private void SendCallback(ulong clientId, MessageBatcher.SendStream sendStream)
/// Sends the Message Queue Item to the specified destination
/// </summary>
/// <param name="item">Information on what to send</param>
private void SendFrameQueueItem(MessageFrameItem item)
private void SendFrameQueueItem(IReadOnlyCollection<ulong> targetIds, in MessageFrameItem item)
{
var channel = item.NetworkChannel;
// If the length is greater than the fragmented threshold, switch to a fragmented channel.
Expand All @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions com.unity.netcode.gameobjects/Runtime/Metrics/NetworkMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal class NetworkMetrics : INetworkMetrics
ShouldResetOnDispatch = true,
};

readonly EventMetric<NetworkMessageEvent> m_NetworkMessageSentEvent = new EventMetric<NetworkMessageEvent>(NetworkMetricTypes.NetworkMessageSent.Id);
readonly EventMetric<NetworkMessageEvent> m_NetworkMessageReceivedEvent = new EventMetric<NetworkMessageEvent>(NetworkMetricTypes.NetworkMessageReceived.Id);
readonly EventMetric<NamedMessageEvent> m_NamedMessageSentEvent = new EventMetric<NamedMessageEvent>(NetworkMetricTypes.NamedMessageSent.Id);
readonly EventMetric<NamedMessageEvent> m_NamedMessageReceivedEvent = new EventMetric<NamedMessageEvent>(NetworkMetricTypes.NamedMessageReceived.Id);
readonly EventMetric<UnnamedMessageEvent> m_UnnamedMessageSentEvent = new EventMetric<UnnamedMessageEvent>(NetworkMetricTypes.UnnamedMessageSent.Id);
Expand All @@ -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)
Expand Down Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public void SendWithThreshold()
MessageType = i % 2 == 0 ? MessageQueueContainer.MessageType.ServerRpc : MessageQueueContainer.MessageType.ClientRpc,
MessageData = new ArraySegment<byte>(randomData, 0, randomData.Length)
};
sendBatcher.QueueItem(queueItem,
sendBatcher.QueueItem(
queueItem.ClientNetworkIds,
queueItem,
k_BatchThreshold,
(networkId, sendStream) =>
{
Expand Down Expand Up @@ -75,7 +77,9 @@ public void SendWithoutThreshold()
MessageType = i % 2 == 0 ? MessageQueueContainer.MessageType.ServerRpc : MessageQueueContainer.MessageType.ClientRpc,
MessageData = new ArraySegment<byte>(randomData, 0, randomData.Length)
};
sendBatcher.QueueItem(queueItem,
sendBatcher.QueueItem(
queueItem.ClientNetworkIds,
queueItem,
k_BatchThreshold,
(networkId, sendStream) =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<NetworkMessageEvent>(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<NetworkMessageEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.NetworkMessageSent);

Server.CustomMessagingManager.SendNamedMessage(messageName, new List<ulong> { 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<NetworkMessageEvent>(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()
{
Expand Down
2 changes: 1 addition & 1 deletion testproject-tools-integration/Packages/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down