Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
## [2.2.2] - 2024-09-17

### *Network Scene Visualization*
- Fixed a bug where NetVis stopped working after the NetworkManager was destroyed and recreated.
- Fixed an issue where NetVis only assigned colors to a limited subset of Network IDs.
- Added custom color setting for host and client ownerships. The custom color settings are synchronized between Multiplayer Play Mode instances.

### *Runtime Network Stats Monitor*
- Added support for entering Play Mode without Domain reload
  • Loading branch information
Unity Technologies committed Sep 17, 2024
1 parent 3093f8f commit ff124a2
Show file tree
Hide file tree
Showing 24 changed files with 435 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .signature
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"timestamp":1727388076,"signature":"Jw1cdy6Ky4HbdLmPiF0Fx/DWyWZYP8Szu1alsvHfIf2DYfkwjh15HOeugyCQSdmGh1kwAQUAeBnEMHxeU+i9wrhnKr19LubMUNJeio5entJLoNQWXDx665DOoRp2WYPNlpD6Nuod7pibZTwnmZQ8MRrvjMrFQxrxKstDSRFlzmQ0fCq+Pnidz+sSf9WooPkb6QRuhctHshi4tSgzhQubmvXwJVh6bXszdoyM5zr5tvjx+q1GDDQm23W69QzxTM2XpER+RuBqVJk4g2IufNgk48ROXcqyKKEeuSThIyS4VrStfVdJL/ByjWusqpQp68V4+vwHEw9dp0CFbqD0n2cHRADffrlKJ2azno+NgfxCJXfezj4S2auOAp1mijHey3ngBoAUJ+vcGDXEU47fkLa0NLggBmY1gwlyurRiT1vtWIPfDbbfYE0dWRFz/RZ9sljxSDihs33iuSVT7U44Jgid7MhgxDrOuLqxdhXG8H65GhCVTb81Y55S3MsgWWkAcEoY","publicKey":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg"}
{"timestamp":1734368505,"signature":"Qxkf7dGuCC3csrlPTCmKjLZ12MdBoI/WAtuR8iuYlwq0cJF6iQxCogevOdGR3h+P5HTr5gGd6KKfLTUXYrI7pVjH0hEVfMAx0Iw2qCk0ij4Qvycxh/ITILLm1CHOVQJdgMAGQt2S6OpJCwIU4n9Abs7CUn6VMVhM85q+4vq7ZwnkqmzjOKN7FfX/YaAYrDcKrpwlRz7SEV0IA9tmUv9+iljSrFRGRMTnEBOwk+0CaRMjVvb3vYPKk7mCfMJm05gBcYv453k98GwjJJg9YackZVHS6P//2ez0ctqWmOqnNLB7s9fVCRRFqiLNNTRwyvybVZ3G7U6KWNwhxo6o1jZiH4OLQPFBUspEc/IwxIS5zsZm+WlLWLx66yWHjxgHcp8K4Rs1hYZ4QkfvFi+9Zrkw6tgKXMNWhAYcbY+Ab8CL/9C9NoKJUghlycgMB9FvVprfe1FSM0KVH90fczjwT2NqMYKY2OlI4ODFA3TUIq1yYHK/h1W1E2rawWEQ9O2etmkc","publicKey":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg"}
2 changes: 2 additions & 0 deletions Adapters/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@
[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation.Tests.Editor")]
[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.NetworkSimulator.Tests.Runtime")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Tests.Editor")]

#endif
6 changes: 6 additions & 0 deletions Adapters/Ngo1/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System.Runtime.CompilerServices;

// Test assemblies
#if UNITY_INCLUDE_TESTS
[assembly: InternalsVisibleTo("Unity.Multiplayer.Tools.Adapters.Tests.Editor")]
#endif
3 changes: 3 additions & 0 deletions Adapters/Ngo1/AssemblyInfo.cs.meta

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

56 changes: 44 additions & 12 deletions Adapters/Ngo1/Ngo1Adapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,30 @@ class Ngo1Adapter
, IGetRpcCount
{
[NotNull]
readonly NetworkManager m_NetworkManager;
NetworkManager m_NetworkManager;

[MaybeNull]
NetworkSpawnManager SpawnManager => m_NetworkManager.SpawnManager;
NetworkSpawnManager SpawnManager => m_NetworkManager?.SpawnManager;

[MaybeNull]
Dictionary<ulong, NetworkObject> SpawnedObjects => SpawnManager?.SpawnedObjects;

public Ngo1Adapter([NotNull] NetworkManager networkManager)
{
DebugUtil.TraceMethodName();
Debug.Assert(networkManager != null, $"The parameter {nameof(networkManager)} can't be null.");
Init(networkManager);
}

internal void ReplaceNetworkManager(NetworkManager networkManager)
{
Debug.Assert(networkManager != null, $"The parameter {nameof(networkManager)} can't be null.");


Deinitialize();
Init(networkManager);
}

void Init(NetworkManager networkManager)
{
m_NetworkManager = networkManager;
m_NetworkManager.OnConnectionEvent += OnConnectionEvent;
m_NetworkManager.NetworkTickSystem.Tick += OnTick;
Expand All @@ -51,7 +61,7 @@ public Ngo1Adapter([NotNull] NetworkManager networkManager)
m_NetworkManager.OnClientStarted += OnServerOrClientStarted;
m_NetworkManager.OnServerStopped += OnServerOrClientStopped;
m_NetworkManager.OnClientStopped += OnServerOrClientStopped;

if (m_NetworkManager.IsConnectedClient || m_NetworkManager.IsServer)
{
OnServerOrClientStarted();
Expand All @@ -60,6 +70,28 @@ public Ngo1Adapter([NotNull] NetworkManager networkManager)
MetricEventPublisher.OnMetricsReceived += OnMetricsReceived;
}

internal void Deinitialize()
{
if (m_NetworkManager != null)
{
m_NetworkManager.OnConnectionEvent -= OnConnectionEvent;

if (m_NetworkManager.NetworkTickSystem != null)
{
m_NetworkManager.NetworkTickSystem.Tick -= OnTick;
}

m_NetworkManager.OnServerStarted -= OnServerOrClientStarted;
m_NetworkManager.OnClientStarted -= OnServerOrClientStarted;
m_NetworkManager.OnServerStopped -= OnServerOrClientStopped;
m_NetworkManager.OnClientStopped -= OnServerOrClientStopped;

m_NetworkManager = null;
}

MetricEventPublisher.OnMetricsReceived -= OnMetricsReceived;
}

readonly List<ClientId> m_ClientIds = new();
readonly List<ObjectId> m_ObjectIds = new();

Expand Down Expand Up @@ -151,15 +183,15 @@ void OnClientDisconnected(ulong clientId)
m_ClientIds.RemoveAll(id => id == typedClientId);
ClientDisconnectionEvent?.Invoke(typedClientId);
}

void OnConnectionEvent(NetworkManager networkManager, ConnectionEventData clientConnectionData)
{
switch (clientConnectionData.EventType)
{
case ConnectionEvent.ClientConnected:
case ConnectionEvent.PeerConnected:
OnClientConnected(clientConnectionData.ClientId);

// Adding clients already existing before we joined
foreach (var peerClientId in clientConnectionData.PeerClientIds)
{
Expand All @@ -175,18 +207,18 @@ void OnConnectionEvent(NetworkManager networkManager, ConnectionEventData client
break;
}
}

public event Action ServerOrClientStarted;
public event Action ServerOrClientStopped;

void OnServerOrClientStarted()
{
// NetworkTickSystem is recreated every time the server or client is (re)started
m_NetworkManager.NetworkTickSystem.Tick -= OnTick;
m_NetworkManager.NetworkTickSystem.Tick += OnTick;
ServerOrClientStarted?.Invoke();
}

void OnServerOrClientStopped(bool isHost)
{
m_NetworkManager.NetworkTickSystem.Tick -= OnTick;
Expand Down Expand Up @@ -214,7 +246,7 @@ public GameObject GetGameObject(ObjectId objectId)
{
return null;
}

return spawnedObjects.TryGetValue((ulong)objectId, out var networkObject) ? networkObject.gameObject : null;
}

Expand Down Expand Up @@ -253,7 +285,7 @@ void UpdateNetworkTrafficCaches(MetricCollection metricCollection)
event Action m_OnBandwidthUpdated;

public bool IsCacheEmpty => m_BandwidthCache == null || m_BandwidthCache.IsCold;

public BandwidthTypes SupportedBandwidthTypes =>
BandwidthTypes.Other | BandwidthTypes.Rpc | BandwidthTypes.NetVar;

Expand Down
32 changes: 31 additions & 1 deletion Adapters/Ngo1/Ngo1AdapterInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Unity.Multiplayer.Tools.Adapters.Ngo1
static class Ngo1AdapterInitializer
{
[RuntimeInitializeOnLoadMethod]
static void InitializeAdapter()
internal static void InitializeAdapter()
{
InitializeAdapterAsync().Forget();
}
Expand All @@ -20,6 +20,36 @@ static async Task InitializeAdapterAsync()
var networkManager = await GetNetworkManagerAsync();
var ngo1Adapter = new Ngo1Adapter(networkManager);
NetworkAdapters.AddAdapter(ngo1Adapter);

#if UNITY_NETCODE_GAMEOBJECTS_2_1_0_ABOVE
// We need the OnInstantiated callback because the NetworkManager could get destroyed and recreated when we change scenes
// OnInstantiated is called in Awake, and the GetNetworkManagerAsync only returns at least after OnEnable
// therefore the initialization is not called twice
NetworkManager.OnInstantiated += async _ =>
{
// We need to wait for the NetworkTickSystem to be ready as well
var newNetworkManager = await GetNetworkManagerAsync();
ngo1Adapter.ReplaceNetworkManager(newNetworkManager);
};
NetworkManager.OnDestroying += _ =>
{
ngo1Adapter.Deinitialize();
};
#endif

#if UNITY_EDITOR
UnityEditor.EditorApplication.playModeStateChanged += OnPlayModeStateChanged;

void OnPlayModeStateChanged(UnityEditor.PlayModeStateChange playModeStateChange)
{
if (playModeStateChange == UnityEditor.PlayModeStateChange.ExitingPlayMode)
{
UnityEditor.EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
ngo1Adapter.Deinitialize();
NetworkAdapters.RemoveAdapter(ngo1Adapter);
}
}
#endif
}

static async Task<NetworkManager> GetNetworkManagerAsync()
Expand Down
5 changes: 5 additions & 0 deletions Adapters/Ngo1/Unity.Multiplayer.Tools.Adapters.Ngo1.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"name": "com.unity.netcode.gameobjects",
"expression": "1.0.0",
"define": "UNITY_NETCODE_GAMEOBJECTS_1_0_ABOVE"
},
{
"name": "com.unity.netcode.gameobjects",
"expression": "2.1.0",
"define": "UNITY_NETCODE_GAMEOBJECTS_2_1_0_ABOVE"
}
],
"noEngineReferences": false
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.future.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog
All notable changes to this package will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [2.2.3] - xxxx-yy-zz
3 changes: 3 additions & 0 deletions CHANGELOG.future.md.meta

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

10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [2.2.2] - 2024-09-17

### *Network Scene Visualization*
- Fixed a bug where NetVis stopped working after the NetworkManager was destroyed and recreated.
- Fixed an issue where NetVis only assigned colors to a limited subset of Network IDs.
- Added custom color setting for host and client ownerships. The custom color settings are synchronized between Multiplayer Play Mode instances.

### *Runtime Network Stats Monitor*
- Added support for entering Play Mode without Domain reload

## [2.2.1] - 2024-09-27

### *Network Scene Visualization*
Expand Down
38 changes: 38 additions & 0 deletions Common/Runtime/Data/SerializedDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using UnityEngine;

namespace Unity.Multiplayer.Tools.Common
{
[Serializable]
internal class SerializedDictionary<K, V> : SortedDictionary<K, V>, ISerializationCallbackReceiver
{
[SerializeField] List<K> m_Keys = new List<K>();
[SerializeField] List<V> m_Values = new List<V>();

public SerializedDictionary() {}
public SerializedDictionary(Comparer<K> comparer) : base(comparer) {}

public void OnBeforeSerialize()
{
m_Keys.Clear();
m_Values.Clear();
foreach (var kvp in this)
{
m_Keys.Add(kvp.Key);
m_Values.Add(kvp.Value);
}
}

public void OnAfterDeserialize()
{
this.Clear();

for (int i = 0; i < m_Keys.Count; i++)
this[m_Keys[i]] = m_Values[i];

m_Keys.Clear();
m_Values.Clear();
}
}
}
3 changes: 3 additions & 0 deletions Common/Runtime/Data/SerializedDictionary.cs.meta

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

70 changes: 70 additions & 0 deletions Common/Runtime/Helpers/SyncedSingleton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#if UNITY_EDITOR
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;

namespace Unity.Multiplayer.Tools.Common
{
// SyncedSingleton helps to synchronize data between Multiplayer Play mode instances through a file on the disk.
// One Editor serializes the data to a file and the other Editors are notified about the file change
// and will reload the data from that file.
internal class SyncedSingleton<T> : ScriptableSingleton<T> where T : ScriptableObject
{
private static bool s_NeedsRegeneration;
public static event Action DataChanged;

// If the file is updated and the editor is not in focus and entered play mode
// before focus is regained (e.g. multiplayer play mode), then the regeneration
// method --called through delayCall-- could be executed after play mode.
// To avoid this, we reload it on entering play mode state change. It's redundant
// but safer.
private static void OnPlayModeStateChanged(PlayModeStateChange state)
{
ReloadIfNeeded();
}

static SyncedSingleton()
{
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}

private static void OnFileChanged(object source, FileSystemEventArgs e)
{
s_NeedsRegeneration = true;
((IDisposable)source).Dispose();
EditorApplication.delayCall += ReloadIfNeeded;
}

private static void ReloadIfNeeded()
{
if (s_NeedsRegeneration)
{
// By deleting the instance the Editor will reload the data from the file.
DestroyImmediate(instance);
s_NeedsRegeneration = false;
DataChanged?.Invoke();
}
}

private FileSystemWatcher m_FileWatcher;

protected void OnEnable()
{
Assert.IsNull(m_FileWatcher);

var directory = Path.GetDirectoryName(ScriptableSingleton<T>.GetFilePath());

if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);

m_FileWatcher = new FileSystemWatcher();
m_FileWatcher.Path = directory;
m_FileWatcher.Changed += OnFileChanged;
m_FileWatcher.Created += OnFileChanged;
m_FileWatcher.EnableRaisingEvents = true;
}
}
}
#endif
2 changes: 2 additions & 0 deletions Common/Runtime/Helpers/SyncedSingleton.cs.meta

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

Loading

0 comments on commit ff124a2

Please sign in to comment.