Skip to content

fix: Client RPCs always reporting all RPCs as going to all clients, even when limited by ClientRpcParams [MTT-4468] #2144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
1 change: 1 addition & 0 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Fixed

- Fixed ClientRpcs always reporting in the profiler view as going to all clients, even when limited to a subset of clients by ClientRpcParams. (#2144)
- Fixed RPC codegen failing to choose the correct extension methods for FastBufferReader and FastBufferWriter when the parameters were a generic type (i.e., List<int>) and extensions for multiple instantiations of that type have been defined (i.e., List<int> and List<string>) (#2142)
- Fixed throwing an exception in OnNetworkUpdate causing other OnNetworkUpdate calls to not be executed. (#1739)

Expand Down
42 changes: 35 additions & 7 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,42 @@ internal void __endSendClientRpc(ref FastBufferWriter bufferWriter, uint rpcMeth
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (NetworkManager.__rpc_name_table.TryGetValue(rpcMethodId, out var rpcMethodName))
{
foreach (var client in NetworkManager.ConnectedClients)
if (clientRpcParams.Send.TargetClientIds != null)
{
NetworkManager.NetworkMetrics.TrackRpcSent(
client.Key,
NetworkObject,
rpcMethodName,
__getTypeName(),
rpcWriteSize);
foreach (var targetClientId in clientRpcParams.Send.TargetClientIds)
{
NetworkManager.NetworkMetrics.TrackRpcSent(
targetClientId,
NetworkObject,
rpcMethodName,
__getTypeName(),
rpcWriteSize);
}
}
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
{
foreach (var targetClientId in clientRpcParams.Send.TargetClientIdsNativeArray)
{
NetworkManager.NetworkMetrics.TrackRpcSent(
targetClientId,
NetworkObject,
rpcMethodName,
__getTypeName(),
rpcWriteSize);
}
}
else
{
var observerEnumerator = NetworkObject.Observers.GetEnumerator();
while (observerEnumerator.MoveNext())
{
NetworkManager.NetworkMetrics.TrackRpcSent(
observerEnumerator.Current,
NetworkObject,
rpcMethodName,
__getTypeName(),
rpcWriteSize);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the clientRpcParams.Send.TargetClientIdsNativeArray didn't cause Boxing allocation I would suggest using something like:

IEnumerable<ulong> enumerable;

if (clientRpcParams.Send.TargetClientIds != null)
{
    enumerable = clientRpcParams.Send.TargetClientIds;
}
else if (clientRpcParams.Send.TargetClientIdsNativeArray != null)
{
    enumerable = clientRpcParams.Send.TargetClientIdsNativeArray;
}
else
{
    enumerable = NetworkObject.Observers;
}

foreach (var networkObject in enumerable)
{
    NetworkManager.NetworkMetrics.TrackRpcSent(
        networkObject,
        NetworkObject,
        methodName,
        __getTypeName(),
        rpcWriteSize);
}

But in this case, it seems there's no other way to achieve the most efficient code without sacrificing a bit of readability.

So I believe your code is the best choice here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was my thought, too.

}
}
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public void MyServerRpc()
}

[ClientRpc]
public void MyClientRpc()
public void MyClientRpc(ClientRpcParams rpcParams = default)
{
OnClientRpcAction?.Invoke();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
using System.Collections;
using System.Linq;
using NUnit.Framework;
using Unity.Collections;
using Unity.Multiplayer.Tools.MetricTypes;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime.Metrics;

namespace Unity.Netcode.RuntimeTests.Metrics
{
internal class RpcMetricsTests : SingleClientMetricTestBase
internal class RpcMetricsTests : DualClientMetricTestBase
{
protected override void OnCreatePlayerPrefab()
{
Expand All @@ -17,30 +18,79 @@ protected override void OnCreatePlayerPrefab()
}

[UnityTest]
public IEnumerator TrackRpcSentMetricOnServer()
public IEnumerator TrackRpcSentMetricOnServerToOnlyOneClientWithArray()
{
var waitForMetricValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcSent);

m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc(new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIds = new []{FirstClient.LocalClientId}
}
});

yield return waitForMetricValues.WaitForMetricsReceived();

var serverRpcSentValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(2, serverRpcSentValues.Count); // Server will receive this, since it's host
Assert.AreEqual(1, serverRpcSentValues.Count);

Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.Name == nameof(RpcTestComponent.MyClientRpc)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.NetworkBehaviourName == nameof(RpcTestComponent)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.BytesCount != 0));
Assert.AreEqual(FirstClient.LocalClientId, serverRpcSentValues.First().Connection.Id);
}

[UnityTest]
public IEnumerator TrackRpcSentMetricOnServerToOnlyOneClientWithNativeArray()
{
var waitForMetricValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcSent);

m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc(new ClientRpcParams
{
Send = new ClientRpcSendParams
{
TargetClientIdsNativeArray = new NativeArray<ulong>(new []{FirstClient.LocalClientId}, Allocator.Temp)
}
});

yield return waitForMetricValues.WaitForMetricsReceived();

var serverRpcSentValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, serverRpcSentValues.Count);

Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.Name == nameof(RpcTestComponent.MyClientRpc)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.NetworkBehaviourName == nameof(RpcTestComponent)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.BytesCount != 0));
Assert.AreEqual(FirstClient.LocalClientId, serverRpcSentValues.First().Connection.Id);
}

[UnityTest]
public IEnumerator TrackRpcSentMetricOnServerToAllClients()
{
var waitForMetricValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcSent);

m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();

yield return waitForMetricValues.WaitForMetricsReceived();

var serverRpcSentValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(3, serverRpcSentValues.Count); // Server will receive this, since it's host

Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.Name == nameof(RpcTestComponent.MyClientRpc)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.NetworkBehaviourName == nameof(RpcTestComponent)));
Assert.That(serverRpcSentValues, Has.All.Matches<RpcEvent>(x => x.BytesCount != 0));
Assert.Contains(Server.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
Assert.Contains(Client.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
Assert.Contains(FirstClient.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
Assert.Contains(SecondClient.LocalClientId, serverRpcSentValues.Select(x => x.Connection.Id).ToArray());
}

[UnityTest]
public IEnumerator TrackRpcSentMetricOnClient()
{
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(ClientMetrics.Dispatcher, NetworkMetricTypes.RpcSent);
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.RpcSent);

m_PlayerNetworkObjects[Client.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();
m_PlayerNetworkObjects[FirstClient.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();

yield return waitForClientMetricsValues.WaitForMetricsReceived();

Expand All @@ -58,15 +108,15 @@ public IEnumerator TrackRpcSentMetricOnClient()
public IEnumerator TrackRpcReceivedMetricOnServer()
{
var waitForServerMetricsValues = new WaitForEventMetricValues<RpcEvent>(ServerMetrics.Dispatcher, NetworkMetricTypes.RpcReceived);
m_PlayerNetworkObjects[Client.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();
m_PlayerNetworkObjects[FirstClient.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyServerRpc();

yield return waitForServerMetricsValues.WaitForMetricsReceived();

var serverRpcReceivedValues = waitForServerMetricsValues.AssertMetricValuesHaveBeenFound();
Assert.AreEqual(1, serverRpcReceivedValues.Count);

var rpcReceived = serverRpcReceivedValues.First();
Assert.AreEqual(Client.LocalClientId, rpcReceived.Connection.Id);
Assert.AreEqual(FirstClient.LocalClientId, rpcReceived.Connection.Id);
Assert.AreEqual(nameof(RpcTestComponent.MyServerRpc), rpcReceived.Name);
Assert.AreEqual(nameof(RpcTestComponent), rpcReceived.NetworkBehaviourName);
Assert.AreNotEqual(0, rpcReceived.BytesCount);
Expand All @@ -75,9 +125,9 @@ public IEnumerator TrackRpcReceivedMetricOnServer()
[UnityTest]
public IEnumerator TrackRpcReceivedMetricOnClient()
{
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(ClientMetrics.Dispatcher, NetworkMetricTypes.RpcReceived);
var waitForClientMetricsValues = new WaitForEventMetricValues<RpcEvent>(FirstClientMetrics.Dispatcher, NetworkMetricTypes.RpcReceived);

m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][Client.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();
m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][FirstClient.LocalClientId].GetComponent<RpcTestComponent>().MyClientRpc();

yield return waitForClientMetricsValues.WaitForMetricsReceived();

Expand Down