Skip to content

Commit 8b56bd6

Browse files
author
Jean-Sébastien Fauteux
authored
feat: RPC Event Net stat reporting (#954)
* RPC Event implementation with test * Update based on comments * Fixed RPC byte size
1 parent 78160ae commit 8b56bd6

File tree

13 files changed

+274
-8
lines changed

13 files changed

+274
-8
lines changed

com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,19 @@ internal void __endSendServerRpc(NetworkSerializer serializer, uint rpcMethodId,
9191
}
9292

9393
var rpcQueueContainer = NetworkManager.RpcQueueContainer;
94+
var rpcMessageSize = 0L;
9495
if (IsHost)
9596
{
96-
rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage);
97+
rpcMessageSize = rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Inbound, serverRpcParams.Send.UpdateStage);
9798
}
9899
else
99100
{
100-
rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
101+
rpcMessageSize = rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
102+
}
103+
104+
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
105+
{
106+
NetworkManager.NetworkMetrics.TrackRpcSent(NetworkManager.ServerClientId, NetworkObjectId, rpcMethodName, (ulong)rpcMessageSize);
101107
}
102108
}
103109

@@ -183,7 +189,8 @@ internal void __endSendClientRpc(NetworkSerializer serializer, uint rpcMethodId,
183189
}
184190

185191
var rpcQueueContainer = NetworkManager.RpcQueueContainer;
186-
192+
var messageSize = 0L;
193+
string rpcMethodName;
187194
if (IsHost)
188195
{
189196
ulong[] clientIds = clientRpcParams.Send.TargetClientIds ?? NetworkManager.ConnectedClientsList.Select(c => c.ClientId).ToArray();
@@ -195,12 +202,23 @@ internal void __endSendClientRpc(NetworkSerializer serializer, uint rpcMethodId,
195202
var containsServerClientId = clientIds.Contains(NetworkManager.ServerClientId);
196203
if (containsServerClientId && clientIds.Length == 1)
197204
{
198-
rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Inbound, clientRpcParams.Send.UpdateStage);
205+
messageSize = rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Inbound, clientRpcParams.Send.UpdateStage);
206+
207+
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out rpcMethodName))
208+
{
209+
NetworkManager.NetworkMetrics.TrackRpcSent(clientIds, NetworkObjectId, rpcMethodName, (ulong)messageSize);
210+
}
211+
199212
return;
200213
}
201214
}
202215

203-
rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
216+
messageSize = rpcQueueContainer.EndAddQueueItemToFrame(serializer.Writer, RpcQueueHistoryFrame.QueueFrameType.Outbound, NetworkUpdateStage.PostLateUpdate);
217+
218+
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out rpcMethodName))
219+
{
220+
NetworkManager.NetworkMetrics.TrackRpcSent(NetworkManager.ServerClientId, NetworkObjectId, rpcMethodName, (ulong)messageSize);
221+
}
204222
}
205223

206224
/// <summary>

com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,11 @@ internal void InvokeRpc(RpcFrameQueueItem queueItem)
14591459
}
14601460

14611461
__rpc_func_table[networkRpcMethodId](networkBehaviour, new NetworkSerializer(queueItem.NetworkReader), rpcParams);
1462+
1463+
if (__rpc_name_table.TryGetValue(networkRpcMethodId, out var rpcMethodName))
1464+
{
1465+
NetworkMetrics.TrackRpcReceived(queueItem.NetworkId, networkObjectId, rpcMethodName, (ulong)queueItem.MessageData.Count);
1466+
}
14621467
}
14631468

14641469
#if DEVELOPMENT_BUILD || UNITY_EDITOR

com.unity.multiplayer.mlapi/Runtime/Messaging/RpcQueue/RpcQueueContainer.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ public PooledNetworkWriter BeginAddQueueItemToFrame(QueueItemType qItemType, flo
433433
/// <param name="writer">NetworkWriter that was used</param>
434434
/// <param name="queueFrameType">type of the queue frame that was used</param>
435435
/// <param name="updateStage">stage the RPC is going to be invoked</param>
436-
public void EndAddQueueItemToFrame(NetworkWriter writer, RpcQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
436+
public long EndAddQueueItemToFrame(NetworkWriter writer, RpcQueueHistoryFrame.QueueFrameType queueFrameType, NetworkUpdateStage updateStage)
437437
{
438438
bool getNextFrame = NetworkManager.IsHost && queueFrameType == RpcQueueHistoryFrame.QueueFrameType.Inbound;
439439

@@ -525,6 +525,8 @@ public void EndAddQueueItemToFrame(NetworkWriter writer, RpcQueueHistoryFrame.Qu
525525

526526
//Add the packed size to the offsets for parsing over various entries
527527
rpcQueueHistoryItem.QueueItemOffsets.Add((uint)rpcQueueHistoryItem.QueueBuffer.Position);
528+
529+
return messageSize;
528530
}
529531

530532
/// <summary>

com.unity.multiplayer.mlapi/Runtime/Metrics/INetworkMetrics.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ public interface INetworkMetrics
3232

3333
void TrackObjectDestroyReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount);
3434

35+
void TrackRpcSent(ulong receiverClientId, ulong networkObjectId, string rpcName, ulong bytesCount);
36+
37+
void TrackRpcSent(ulong[] receiverClientIds, ulong networkObjectId, string rpcName, ulong bytesCount);
38+
39+
void TrackRpcReceived(ulong senderClientId, ulong networkObjectId, string rpcName, ulong bytesCount);
40+
3541
void DispatchFrame();
3642
}
3743
}

com.unity.multiplayer.mlapi/Runtime/Metrics/NetworkMetrics.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public class NetworkMetrics : INetworkMetrics
2626
private readonly EventMetric<ObjectDestroyedEvent> m_ObjectDestroySentEvent = new EventMetric<ObjectDestroyedEvent>(MetricNames.ObjectDestroyedSent);
2727
private readonly EventMetric<ObjectDestroyedEvent> m_ObjectDestroyReceivedEvent = new EventMetric<ObjectDestroyedEvent>(MetricNames.ObjectDestroyedReceived);
2828

29+
readonly EventMetric<RpcEvent> m_RpcSentEvent = new EventMetric<RpcEvent>(MetricNames.RpcSent);
30+
readonly EventMetric<RpcEvent> m_RpcReceivedEvent = new EventMetric<RpcEvent>(MetricNames.RpcReceived);
31+
2932
private Dictionary<ulong, NetworkObjectIdentifier> m_NetworkGameObjects = new Dictionary<ulong, NetworkObjectIdentifier>();
3033

3134
public NetworkMetrics(NetworkManager networkManager)
@@ -37,6 +40,7 @@ public NetworkMetrics(NetworkManager networkManager)
3740
.WithMetricEvents(m_NetworkVariableDeltaSentEvent, m_NetworkVariableDeltaReceivedEvent)
3841
.WithMetricEvents(m_ObjectSpawnSentEvent, m_ObjectSpawnReceivedEvent)
3942
.WithMetricEvents(m_ObjectDestroySentEvent, m_ObjectDestroyReceivedEvent)
43+
.WithMetricEvents(m_RpcSentEvent, m_RpcReceivedEvent)
4044
.Build();
4145

4246
Dispatcher.RegisterObserver(MLAPIObserver.Observer);
@@ -129,6 +133,34 @@ public void TrackObjectDestroyReceived(ulong senderClientId, ulong networkObject
129133
m_ObjectDestroyReceivedEvent.Mark(new ObjectDestroyedEvent(new ConnectionInfo(senderClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), bytesCount));
130134
}
131135

136+
public void TrackRpcSent(ulong receiverClientId, ulong networkObjectId, string rpcName, ulong bytesCount)
137+
{
138+
if (!m_NetworkGameObjects.TryGetValue(networkObjectId, out var networkObjectIdentifier))
139+
{
140+
networkObjectIdentifier = new NetworkObjectIdentifier("", networkObjectId);
141+
}
142+
143+
m_RpcSentEvent.Mark(new RpcEvent(new ConnectionInfo(receiverClientId), networkObjectIdentifier, rpcName, bytesCount));
144+
}
145+
146+
public void TrackRpcSent(ulong[] receiverClientIds, ulong networkObjectId, string rpcName, ulong bytesCount)
147+
{
148+
foreach (var receiverClientId in receiverClientIds)
149+
{
150+
TrackRpcSent(receiverClientId, networkObjectId, rpcName, bytesCount);
151+
}
152+
}
153+
154+
public void TrackRpcReceived(ulong senderClientId, ulong networkObjectId, string rpcName, ulong bytesCount)
155+
{
156+
if (!m_NetworkGameObjects.TryGetValue(networkObjectId, out var networkObjectIdentifier))
157+
{
158+
networkObjectIdentifier = new NetworkObjectIdentifier("", networkObjectId);
159+
}
160+
161+
m_RpcReceivedEvent.Mark(new RpcEvent(new ConnectionInfo(senderClientId), networkObjectIdentifier, rpcName, bytesCount));
162+
}
163+
132164
public void DispatchFrame()
133165
{
134166
Dispatcher.Dispatch();

com.unity.multiplayer.mlapi/Runtime/Metrics/NullNetworkMetrics.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ public void TrackObjectDestroyReceived(ulong senderClientId, ulong networkObject
6060
{
6161
}
6262

63+
public void TrackRpcSent(ulong receiverClientId, ulong networkObjectId, string rpcName, ulong bytesCount)
64+
{
65+
}
66+
67+
public void TrackRpcSent(ulong[] receiverClientIds, ulong networkObjectId, string rpcName, ulong bytesCount)
68+
{
69+
}
70+
71+
public void TrackRpcReceived(ulong senderClientId, ulong networkObjectId, string rpcName, ulong bytesCount)
72+
{
73+
}
74+
6375
public void DispatchFrame()
6476
{
6577
}

com.unity.multiplayer.mlapi/Tests/Runtime/Metrics/NetworkVariables/NetworkVariableMetricsReceptionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public IEnumerator SetUp()
2323
Debug.LogError("Failed to create instances");
2424
Assert.Fail("Failed to create instances");
2525
}
26-
26+
2727
var playerPrefab = new GameObject("Player");
2828
var networkObject = playerPrefab.AddComponent<NetworkObject>();
2929
playerPrefab.AddComponent<NetworkVariableComponent>();

com.unity.multiplayer.mlapi/Tests/Runtime/Metrics/RPC.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using MLAPI.Metrics;
6+
using NUnit.Framework;
7+
using Unity.Multiplayer.NetworkProfiler;
8+
using Unity.Multiplayer.NetworkProfiler.Models;
9+
using UnityEngine;
10+
using UnityEngine.TestTools;
11+
12+
namespace MLAPI.RuntimeTests.Metrics.RPC
13+
{
14+
public class NetworkMetricsRpcTests
15+
{
16+
NetworkManager m_Server;
17+
NetworkManager m_Client;
18+
NetworkMetrics m_ClientMetrics;
19+
NetworkMetrics m_ServerMetrics;
20+
21+
[UnitySetUp]
22+
public IEnumerator SetUp()
23+
{
24+
if (!MultiInstanceHelpers.Create(1, out m_Server, out NetworkManager[] clients))
25+
{
26+
Assert.Fail("Failed to create instances");
27+
}
28+
29+
var playerPrefab = new GameObject("Player");
30+
NetworkObject networkObject = playerPrefab.AddComponent<NetworkObject>();
31+
playerPrefab.AddComponent<RpcTestComponent>();
32+
33+
MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);
34+
35+
m_Server.NetworkConfig.PlayerPrefab = playerPrefab;
36+
m_Client = clients.First();
37+
38+
m_Client.NetworkConfig.PlayerPrefab = playerPrefab;
39+
40+
if (!MultiInstanceHelpers.Start(true, m_Server, clients))
41+
{
42+
Assert.Fail("Failed to start instances");
43+
}
44+
45+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));
46+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnectedToServer(m_Server));
47+
48+
m_ClientMetrics = m_Client.NetworkMetrics as NetworkMetrics;
49+
m_ServerMetrics = m_Server.NetworkMetrics as NetworkMetrics;
50+
}
51+
52+
[TearDown]
53+
public void TearDown()
54+
{
55+
MultiInstanceHelpers.Destroy();
56+
}
57+
58+
[UnityTest]
59+
public IEnumerator TrackServerRpcMetrics()
60+
{
61+
var waitForClientMetricsValues = new WaitForMetricValues<RpcEvent>(m_ClientMetrics.Dispatcher, MetricNames.RpcSent);
62+
var waitForServerMetricsValues = new WaitForMetricValues<RpcEvent>(m_ServerMetrics.Dispatcher, MetricNames.RpcReceived);
63+
64+
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
65+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_Client.LocalClientId), m_Server, serverClientPlayerResult));
66+
67+
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
68+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_Client.LocalClientId), m_Client, clientClientPlayerResult));
69+
70+
bool hasReceivedServerRpc = false;
71+
serverClientPlayerResult.Result.GetComponent<RpcTestComponent>().OnServerRpcAction += () =>
72+
{
73+
hasReceivedServerRpc = true;
74+
};
75+
76+
clientClientPlayerResult.Result.GetComponent<RpcTestComponent>().MyServerRpc();
77+
78+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => hasReceivedServerRpc));
79+
80+
// Client Check
81+
yield return waitForClientMetricsValues.WaitForAFewFrames();
82+
83+
var clientMetricSentValues = waitForClientMetricsValues.EnsureMetricValuesHaveBeenFound();
84+
Assert.AreEqual(1, clientMetricSentValues.Count);
85+
86+
var clientMetric = clientMetricSentValues.First();
87+
Assert.AreEqual(m_Server.LocalClientId, clientMetric.Connection.Id);
88+
Assert.AreEqual("MyServerRpc", clientMetric.Name);
89+
90+
// Server Check
91+
yield return waitForServerMetricsValues.WaitForAFewFrames();
92+
93+
var serverMetricReceivedValues = waitForServerMetricsValues.EnsureMetricValuesHaveBeenFound();
94+
Assert.AreEqual(1, serverMetricReceivedValues.Count);
95+
96+
var serverMetric = serverMetricReceivedValues.First();
97+
Assert.AreEqual(m_Client.LocalClientId, serverMetric.Connection.Id);
98+
Assert.AreEqual("MyServerRpc", serverMetric.Name);
99+
}
100+
101+
[UnityTest]
102+
public IEnumerator TrackClientRpcMetrics()
103+
{
104+
var waitForServerMetricsValues = new WaitForMetricValues<RpcEvent>(m_ServerMetrics.Dispatcher, MetricNames.RpcReceived);
105+
106+
var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
107+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_Client.LocalClientId), m_Server, serverClientPlayerResult));
108+
109+
var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper<NetworkObject>();
110+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation((x => x.IsPlayerObject && x.OwnerClientId == m_Client.LocalClientId), m_Client, clientClientPlayerResult));
111+
112+
bool hasReceivedClientRpcOnServer = false;
113+
bool hasReceivedClientRpcRemotely = false;
114+
serverClientPlayerResult.Result.GetComponent<RpcTestComponent>().OnClientRpcAction += () =>
115+
{
116+
hasReceivedClientRpcOnServer = true;
117+
};
118+
clientClientPlayerResult.Result.GetComponent<RpcTestComponent>().OnClientRpcAction += () =>
119+
{
120+
Debug.Log("ClientRpc received on client object");
121+
hasReceivedClientRpcRemotely = true;
122+
};
123+
124+
serverClientPlayerResult.Result.GetComponent<RpcTestComponent>().MyClientRpc();
125+
126+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition(() => hasReceivedClientRpcOnServer && hasReceivedClientRpcRemotely));
127+
128+
yield return waitForServerMetricsValues.WaitForAFewFrames();
129+
130+
var serverMetricReceivedValues = waitForServerMetricsValues.EnsureMetricValuesHaveBeenFound();
131+
Assert.AreEqual(1, serverMetricReceivedValues.Count);
132+
133+
var serverMetric = serverMetricReceivedValues.First();
134+
Assert.AreEqual(m_Server.LocalClientId, serverMetric.Connection.Id);
135+
Assert.AreEqual("MyClientRpc", serverMetric.Name);
136+
}
137+
}
138+
}

com.unity.multiplayer.mlapi/Tests/Runtime/Metrics/RPC/NetworkMetricsRpcTests.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using MLAPI.Messaging;
3+
4+
namespace MLAPI.RuntimeTests.Metrics.RPC
5+
{
6+
public class RpcTestComponent : NetworkBehaviour
7+
{
8+
public event Action OnServerRpcAction;
9+
public event Action OnClientRpcAction;
10+
11+
[ServerRpc]
12+
public void MyServerRpc()
13+
{
14+
OnServerRpcAction?.Invoke();
15+
}
16+
17+
[ClientRpc]
18+
public void MyClientRpc()
19+
{
20+
OnClientRpcAction?.Invoke();
21+
}
22+
}
23+
}

com.unity.multiplayer.mlapi/Tests/Runtime/Metrics/RPC/RpcTestComponent.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

com.unity.multiplayer.mlapi/Tests/Runtime/RpcQueueTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public IEnumerator UpdateStagesInvocation()
6666
}
6767

6868
/// <summary>
69-
/// This tests the RPC Queue outbound and inbound buffer capabilities.
69+
/// This tests the RPC Queue outbound and inbound buffer capabilities.
7070
/// </summary>
7171
/// <returns>IEnumerator</returns>
7272
[UnityTest, Order(2)]

0 commit comments

Comments
 (0)