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
2 changes: 2 additions & 0 deletions com.unity.multiplayer.mlapi/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ public static void NetworkShow(List<NetworkObject> networkObjects, ulong clientI

networkManager.MessageSender.Send(clientId, NetworkConstants.ADD_OBJECTS, NetworkChannel.Internal, buffer);
}
// TODO: add metric for spawn objects here
Comment thread
josiemessa marked this conversation as resolved.
}

/// <summary>
Expand Down Expand Up @@ -401,6 +402,7 @@ public static void NetworkHide(List<NetworkObject> networkObjects, ulong clientI

networkManager.MessageSender.Send(clientId, NetworkConstants.DESTROY_OBJECTS, NetworkChannel.Internal, buffer);
}
// TODO: add metric for destroy objects here
}

private void OnDestroy()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ public void HandleAddObject(ulong clientId, Stream stream)
BufferManager.RecycleConsumedBufferedMessage(message);
}
}

m_NetworkManager.NetworkMetrics.TrackObjectSpawnReceived(clientId, networkObject.NetworkObjectId, networkObject.name, (ulong)stream.Length);
}
}

Expand All @@ -196,7 +198,7 @@ public void HandleDestroyObject(ulong clientId, Stream stream)
Debug.LogWarning($"Trying to destroy object {networkId} but it doesn't seem to exist anymore!");
return;
}

m_NetworkManager.NetworkMetrics.TrackObjectDestroyReceived(clientId, networkId, networkObject.name, (ulong)stream.Length);
NetworkManager.SpawnManager.OnDespawnObject(networkObject, true);
}
}
Expand Down
14 changes: 12 additions & 2 deletions com.unity.multiplayer.mlapi/Runtime/Metrics/INetworkMetrics.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;

namespace MLAPI.Metrics
{
Expand All @@ -22,6 +22,16 @@ public interface INetworkMetrics

void TrackNetworkVariableDeltaReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName,string variableName, ulong bytesCount);

void TrackObjectSpawnSent(ulong receiverClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount);

void TrackObjectSpawnReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount);

void TrackObjectDestroySent(ulong receiverClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount);

void TrackObjectDestroySent(IReadOnlyCollection<ulong> receiverClientIds, ulong networkObjectId, string gameObjectName, ulong bytesCount);

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

void DispatchFrame();
}
}
}
40 changes: 38 additions & 2 deletions com.unity.multiplayer.mlapi/Runtime/Metrics/NetworkMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class NetworkMetrics : INetworkMetrics
readonly EventMetric<NetworkVariableEvent> m_NetworkVariableDeltaSentEvent = new EventMetric<NetworkVariableEvent>(MetricNames.NetworkVariableDeltaSent);
readonly EventMetric<NetworkVariableEvent> m_NetworkVariableDeltaReceivedEvent = new EventMetric<NetworkVariableEvent>(MetricNames.NetworkVariableDeltaReceived);

private readonly EventMetric<ObjectSpawnedEvent> m_ObjectSpawnSentEvent = new EventMetric<ObjectSpawnedEvent>(MetricNames.ObjectSpawnedSent);
private readonly EventMetric<ObjectSpawnedEvent> m_ObjectSpawnReceivedEvent = new EventMetric<ObjectSpawnedEvent>(MetricNames.ObjectSpawnedReceived);
private readonly EventMetric<ObjectDestroyedEvent> m_ObjectDestroySentEvent = new EventMetric<ObjectDestroyedEvent>(MetricNames.ObjectDestroyedSent);
private readonly EventMetric<ObjectDestroyedEvent> m_ObjectDestroyReceivedEvent = new EventMetric<ObjectDestroyedEvent>(MetricNames.ObjectDestroyedReceived);

private Dictionary<ulong, NetworkObjectIdentifier> m_NetworkGameObjects = new Dictionary<ulong, NetworkObjectIdentifier>();

public NetworkMetrics(NetworkManager networkManager)
Expand All @@ -29,8 +34,10 @@ public NetworkMetrics(NetworkManager networkManager)
.WithMetricEvents(m_NamedMessageSentEvent, m_NamedMessageReceivedEvent)
.WithMetricEvents(m_UnnamedMessageSentEvent, m_UnnamedMessageReceivedEvent)
.WithMetricEvents(m_NetworkVariableDeltaSentEvent, m_NetworkVariableDeltaReceivedEvent)
.WithMetricEvents(m_ObjectSpawnSentEvent, m_ObjectSpawnReceivedEvent)
.WithMetricEvents(m_ObjectDestroySentEvent, m_ObjectDestroyReceivedEvent)
.Build();

Dispatcher.RegisterObserver(MLAPIObserver.Observer);
}

Expand Down Expand Up @@ -79,19 +86,48 @@ public void TrackUnnamedMessageReceived(ulong senderClientId, ulong bytesCount)
{
m_UnnamedMessageReceivedEvent.Mark(new UnnamedMessageEvent(new ConnectionInfo(senderClientId), bytesCount));
}

public void TrackNetworkVariableDeltaSent(ulong receiverClientId, ulong networkObjectId, string gameObjectName, string variableName, ulong bytesCount)
{
variableName = PrettyPrintVariableName(variableName);
m_NetworkVariableDeltaSentEvent.Mark(new NetworkVariableEvent(new ConnectionInfo(receiverClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), variableName, bytesCount));
}


public void TrackNetworkVariableDeltaReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, string variableName, ulong bytesCount)
{
variableName = PrettyPrintVariableName(variableName);
m_NetworkVariableDeltaReceivedEvent.Mark(new NetworkVariableEvent(new ConnectionInfo(senderClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), variableName, bytesCount));
}

public void TrackObjectSpawnSent(ulong receiverClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
m_ObjectSpawnSentEvent.Mark(new ObjectSpawnedEvent(new ConnectionInfo(receiverClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), bytesCount));
}

public void TrackObjectSpawnReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
m_ObjectSpawnReceivedEvent.Mark(new ObjectSpawnedEvent(new ConnectionInfo(senderClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), bytesCount));
}

public void TrackObjectDestroySent(ulong receiverClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
m_ObjectDestroySentEvent.Mark(new ObjectDestroyedEvent(new ConnectionInfo(receiverClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), bytesCount));
}

public void TrackObjectDestroySent(IReadOnlyCollection<ulong> receiverClientIds, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
foreach (var receiverClientId in receiverClientIds)
{
TrackObjectDestroySent(receiverClientId, networkObjectId, gameObjectName, bytesCount);
}
}

public void TrackObjectDestroyReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
m_ObjectDestroyReceivedEvent.Mark(new ObjectDestroyedEvent(new ConnectionInfo(senderClientId), new NetworkObjectIdentifier(gameObjectName, networkObjectId), bytesCount));
}

public void DispatchFrame()
{
Dispatcher.Dispatch();
Expand Down
24 changes: 22 additions & 2 deletions com.unity.multiplayer.mlapi/Runtime/Metrics/NullNetworkMetrics.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;

namespace MLAPI.Metrics
{
Expand Down Expand Up @@ -40,8 +40,28 @@ public void TrackNetworkVariableDeltaReceived(ulong senderClientId, ulong networ
{
}

public void TrackObjectSpawnSent(ulong receiverClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
}

public void TrackObjectSpawnReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
}

public void TrackObjectDestroySent(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
}

public void TrackObjectDestroySent(IReadOnlyCollection<ulong> receiverClientIds, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
}

public void TrackObjectDestroyReceived(ulong senderClientId, ulong networkObjectId, string gameObjectName, ulong bytesCount)
{
}

public void DispatchFrame()
{
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public NetworkObject GetPlayerNetworkObject(ulong clientId)
{
throw new NotServerException("Only the server can find player objects from other clients.");
}

if (NetworkManager.ConnectedClients.TryGetValue(clientId, out NetworkClient networkClient))
{
return networkClient.PlayerObject;
Expand Down Expand Up @@ -223,6 +224,7 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO
{
NetworkLog.LogError($"Failed to create object locally. [{nameof(globalObjectIdHash)}={globalObjectIdHash}]. {nameof(NetworkPrefab)} could not be found. Is the prefab registered with {nameof(NetworkManager)}?");
}

return null;
}

Expand Down Expand Up @@ -253,6 +255,7 @@ internal NetworkObject CreateLocalNetworkObject(bool isSceneObject, uint globalO
{
NetworkLog.LogError($"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure for Hash: {globalObjectIdHash}!");
}

return null;
}

Expand Down Expand Up @@ -377,9 +380,10 @@ internal void SendSpawnCallForObject(ulong clientId, NetworkObject networkObject
NetworkId = 0,
NetworkBuffer = buffer,
NetworkChannel = NetworkChannel.Internal,
ClientNetworkIds = new[] { clientId }
ClientNetworkIds = new[] {clientId}
};
rpcQueueContainer.AddToInternalMLAPISendQueue(queueItem);
NetworkManager.NetworkMetrics.TrackObjectSpawnSent(clientId, networkObject.NetworkObjectId, networkObject.name, (ulong)buffer.Length);
}

internal void WriteSpawnCallForObject(NetworkBuffer buffer, ulong clientId, NetworkObject networkObject, Stream payload)
Expand Down Expand Up @@ -435,7 +439,7 @@ internal void WriteSpawnCallForObject(NetworkBuffer buffer, ulong clientId, Netw

if (payload != null)
{
writer.WriteInt32Packed((int)payload.Length);
writer.WriteInt32Packed((int) payload.Length);
}

if (NetworkManager.NetworkConfig.EnableNetworkVariable)
Expand Down Expand Up @@ -684,17 +688,20 @@ internal void OnDespawnObject(NetworkObject networkObject, bool destroyGameObjec
{
writer.WriteUInt64Packed(networkObject.NetworkObjectId);

var clientIds = NetworkManager.ConnectedClientsList.Select(c => c.ClientId).ToArray();
var queueItem = new RpcFrameQueueItem
{
UpdateStage = NetworkUpdateStage.PostLateUpdate,
QueueItemType = RpcQueueContainer.QueueItemType.DestroyObject,
NetworkId = networkObject.NetworkObjectId,
NetworkBuffer = buffer,
NetworkChannel = NetworkChannel.Internal,
ClientNetworkIds = NetworkManager.ConnectedClientsList.Select(c => c.ClientId).ToArray()
ClientNetworkIds = clientIds,
};

rpcQueueContainer.AddToInternalMLAPISendQueue(queueItem);

NetworkManager.NetworkMetrics.TrackObjectDestroySent(clientIds, networkObject.NetworkObjectId, networkObject.name, (ulong)buffer.Length);
}
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System.Collections;
using System.Linq;
using MLAPI.Metrics;
using NUnit.Framework;
using Unity.Multiplayer.NetworkProfiler;
using Unity.Multiplayer.NetworkProfiler.Models;
using UnityEngine;
using UnityEngine.TestTools;

namespace MLAPI.RuntimeTests.Metrics.NetworkObjects
{
public class NetworkObjectMetricsTests
{
NetworkManager m_Server;
NetworkManager m_Client;
NetworkMetrics m_ClientMetrics;
NetworkMetrics m_ServerMetrics;

private NetworkObject m_NewNetworkObject;
private const string m_NewNetworkObjectName = "TestNetworkObjectToSpawn";

[UnitySetUp]
public IEnumerator SetUp()
{
if (!MultiInstanceHelpers.Create(1, out m_Server, out var clients))
{
Debug.LogError("Failed to create instances");
Assert.Fail("Failed to create instances");
}

var playerPrefab = new GameObject("Player");
var networkObject = playerPrefab.AddComponent<NetworkObject>();

MultiInstanceHelpers.MakeNetworkedObjectTestPrefab(networkObject);

m_Server.NetworkConfig.PlayerPrefab = playerPrefab;

foreach (var client in clients)
{
client.NetworkConfig.PlayerPrefab = playerPrefab;
}

if (!MultiInstanceHelpers.Start(true, m_Server, clients))
{
Debug.LogError("Failed to start instances");
Assert.Fail("Failed to start instances");
}

yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientConnectedToServer(m_Server));

m_Client = clients.First();
m_ClientMetrics = m_Client.NetworkMetrics as NetworkMetrics;
m_ServerMetrics = m_Server.NetworkMetrics as NetworkMetrics;

var gameObject = new GameObject(m_NewNetworkObjectName);
m_NewNetworkObject = gameObject.AddComponent<NetworkObject>();
m_NewNetworkObject.NetworkManagerOwner = m_Server;
}

[UnityTearDown]
public IEnumerator TearDown()
{
MultiInstanceHelpers.Destroy();

yield return null;
}

[UnityTest]
public IEnumerator TrackNetworkObjectSpawnSentMetric()
{
var waitForMetricEvent = new WaitForMetricValues<ObjectSpawnedEvent>(m_ServerMetrics.Dispatcher, MetricNames.ObjectSpawnedSent);

m_NewNetworkObject.Spawn();

yield return waitForMetricEvent.WaitForAFewFrames();

var objectSpawnedSentMetricValues = waitForMetricEvent.EnsureMetricValuesHaveBeenFound();
Assert.AreEqual(1, objectSpawnedSentMetricValues.Count);

var objectSpawned = objectSpawnedSentMetricValues.First();
Assert.AreEqual(m_Client.LocalClientId, objectSpawned.Connection.Id);
Assert.AreEqual(m_NewNetworkObjectName, objectSpawned.NetworkId.Name);
}

[UnityTest]
public IEnumerator TrackNetworkObjectSpawnReceivedMetric()
{
var waitForMetricEvent = new WaitForMetricValues<ObjectSpawnedEvent>(m_ClientMetrics.Dispatcher, MetricNames.ObjectSpawnedReceived);

m_NewNetworkObject.Spawn();

yield return waitForMetricEvent.WaitForAFewFrames();

var objectSpawnedReceivedMetricValues = waitForMetricEvent.EnsureMetricValuesHaveBeenFound();
Assert.AreEqual(1, objectSpawnedReceivedMetricValues.Count);

var objectSpawned = objectSpawnedReceivedMetricValues.First();
Assert.AreEqual(m_Server.LocalClientId, objectSpawned.Connection.Id);
Assert.AreEqual(m_NewNetworkObject.NetworkObjectId, objectSpawned.NetworkId.NetworkId);
// Bug: this should not be the name of the network object
// Assert.AreEqual("Player(Clone)", objectSpawned.NetworkId.Name); // What?
}

[UnityTest]
public IEnumerator TrackNetworkObjectDestroySentMetric()
{
m_NewNetworkObject.Spawn();

var waitForMetricEvent = new WaitForMetricValues<ObjectDestroyedEvent>(m_ServerMetrics.Dispatcher, MetricNames.ObjectDestroyedSent);
// TODO: is there a better way of waiting here?
yield return waitForMetricEvent.WaitForAFewFrames();

m_Server.SpawnManager.OnDespawnObject(m_NewNetworkObject, true);

yield return waitForMetricEvent.WaitForAFewFrames();

var objectDestroyedSentMetricValues = waitForMetricEvent.EnsureMetricValuesHaveBeenFound();
// As there's a client and server, this event is emitted twice.
Assert.AreEqual(2, objectDestroyedSentMetricValues.Count);

var objectDestroyed = objectDestroyedSentMetricValues.Last();
Assert.AreEqual(m_Client.LocalClientId, objectDestroyed.Connection.Id);
Assert.AreEqual(m_NewNetworkObjectName, objectDestroyed.NetworkId.Name);
}

[UnityTest]
public IEnumerator TrackNetworkObjectDestroyReceivedMetric()
{
m_NewNetworkObject.Spawn();
var waitForMetricEvent = new WaitForMetricValues<ObjectDestroyedEvent>(m_ClientMetrics.Dispatcher, MetricNames.ObjectDestroyedReceived);

yield return waitForMetricEvent.WaitForAFewFrames();

m_Server.SpawnManager.OnDespawnObject(m_NewNetworkObject, true);
yield return waitForMetricEvent.Wait(60);

var objectDestroyedReceivedMetricValues = waitForMetricEvent.EnsureMetricValuesHaveBeenFound();
Assert.AreEqual(1, objectDestroyedReceivedMetricValues.Count);

var objectDestroyed = objectDestroyedReceivedMetricValues.First();
Assert.AreEqual(m_Server.LocalClientId, objectDestroyed.Connection.Id);
Assert.AreEqual(m_NewNetworkObject.NetworkObjectId, objectDestroyed.NetworkId.NetworkId);
// Bug: Currently the object name is always "Player Clone"
// Assert.AreEqual(m_NewNetworkObjectName, objectDestroyed.NetworkId.Name);
}
}
}
Loading