Skip to content

Commit 1f78274

Browse files
Merge branch 'develop-2.0.0' into fix/networkvariable-collections-can-be-modified-without-write-permissions
2 parents 969db43 + 87d23e9 commit 1f78274

File tree

6 files changed

+146
-7
lines changed

6 files changed

+146
-7
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Additional documentation and release notes are available at [Multiplayer Documen
1010

1111
### Added
1212

13+
- Added a static `NetworkManager.OnInstantiated` event notification to be able to track when a new `NetworkManager` instance has been instantiated. (#3088)
14+
- Added a static `NetworkManager.OnDestroying` event notification to be able to track when an existing `NetworkManager` instance is being destroyed. (#3088)
15+
1316
### Fixed
1417

1518
- Fixed issue where a newly synchronizing client would be synchronized with the current `NetworkVariable` values always which could cause issues with collections if there were any pending state updates. Now, when initially synchronizing a client, if a `NetworkVariable` has a pending state update it will serialize the previously known value(s) to the synchronizing client so when the pending updates are sent they aren't duplicate values on the newly connected client side. (#3081)

com.unity.netcode.gameobjects/Runtime/Connection/NetworkConnectionManager.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,8 +751,8 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne
751751
// Server-side spawning (only if there is a prefab hash or player prefab provided)
752752
if (!NetworkManager.DistributedAuthorityMode && response.CreatePlayerObject && (response.PlayerPrefabHash.HasValue || NetworkManager.NetworkConfig.PlayerPrefab != null))
753753
{
754-
var playerObject = response.PlayerPrefabHash.HasValue ? NetworkManager.SpawnManager.GetNetworkObjectToSpawn(response.PlayerPrefabHash.Value, ownerClientId, response.Position.GetValueOrDefault(), response.Rotation.GetValueOrDefault())
755-
: NetworkManager.SpawnManager.GetNetworkObjectToSpawn(NetworkManager.NetworkConfig.PlayerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash, ownerClientId, response.Position.GetValueOrDefault(), response.Rotation.GetValueOrDefault());
754+
var playerObject = response.PlayerPrefabHash.HasValue ? NetworkManager.SpawnManager.GetNetworkObjectToSpawn(response.PlayerPrefabHash.Value, ownerClientId, response.Position ?? null, response.Rotation ?? null)
755+
: NetworkManager.SpawnManager.GetNetworkObjectToSpawn(NetworkManager.NetworkConfig.PlayerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash, ownerClientId, response.Position ?? null, response.Rotation ?? null);
756756

757757
// Spawn the player NetworkObject locally
758758
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(
@@ -896,15 +896,15 @@ internal void HandleConnectionApproval(ulong ownerClientId, NetworkManager.Conne
896896
/// <summary>
897897
/// Client-Side Spawning in distributed authority mode uses this to spawn the player.
898898
/// </summary>
899-
internal void CreateAndSpawnPlayer(ulong ownerId, Vector3 position = default, Quaternion rotation = default)
899+
internal void CreateAndSpawnPlayer(ulong ownerId)
900900
{
901901
if (NetworkManager.DistributedAuthorityMode && NetworkManager.AutoSpawnPlayerPrefabClientSide)
902902
{
903903
var playerPrefab = NetworkManager.FetchLocalPlayerPrefabToSpawn();
904904
if (playerPrefab != null)
905905
{
906906
var globalObjectIdHash = playerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash;
907-
var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(globalObjectIdHash, ownerId, position, rotation);
907+
var networkObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(globalObjectIdHash, ownerId, playerPrefab.transform.position, playerPrefab.transform.rotation);
908908
networkObject.IsSceneObject = false;
909909
networkObject.SpawnAsPlayerObject(ownerId, networkObject.DestroyWithScene);
910910
}

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ namespace Unity.Netcode
1717
[AddComponentMenu("Netcode/Network Manager", -100)]
1818
public class NetworkManager : MonoBehaviour, INetworkUpdateSystem
1919
{
20+
/// <summary>
21+
/// Subscribe to this static event to get notifications when a <see cref="NetworkManager"/> instance has been instantiated.
22+
/// </summary>
23+
public static event Action<NetworkManager> OnInstantiated;
24+
25+
/// <summary>
26+
/// Subscribe to this static event to get notifications when a <see cref="NetworkManager"/> instance is being destroyed.
27+
/// </summary>
28+
public static event Action<NetworkManager> OnDestroying;
29+
30+
2031
#if UNITY_EDITOR
2132
// Inspector view expand/collapse settings for this derived child class
2233
[HideInInspector]
@@ -1030,6 +1041,8 @@ private void Awake()
10301041
#if UNITY_EDITOR
10311042
EditorApplication.playModeStateChanged += ModeChanged;
10321043
#endif
1044+
// Notify we have instantiated a new instance of NetworkManager.
1045+
OnInstantiated?.Invoke(this);
10331046
}
10341047

10351048
private void OnEnable()
@@ -1632,6 +1645,9 @@ private void OnDestroy()
16321645

16331646
UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= OnSceneUnloaded;
16341647

1648+
// Notify we are destroying NetworkManager
1649+
OnDestroying?.Invoke(this);
1650+
16351651
if (Singleton == this)
16361652
{
16371653
Singleton = null;

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -746,14 +746,14 @@ internal NetworkObject InstantiateAndSpawnNoParameterChecks(NetworkObject networ
746746
/// Gets the right NetworkObject prefab instance to spawn. If a handler is registered or there is an override assigned to the
747747
/// passed in globalObjectIdHash value, then that is what will be instantiated, spawned, and returned.
748748
/// </summary>
749-
internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ownerId, Vector3 position = default, Quaternion rotation = default, bool isScenePlaced = false)
749+
internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ownerId, Vector3? position, Quaternion? rotation, bool isScenePlaced = false)
750750
{
751751
NetworkObject networkObject = null;
752752
// If the prefab hash has a registered INetworkPrefabInstanceHandler derived class
753753
if (NetworkManager.PrefabHandler.ContainsHandler(globalObjectIdHash))
754754
{
755755
// Let the handler spawn the NetworkObject
756-
networkObject = NetworkManager.PrefabHandler.HandleNetworkPrefabSpawn(globalObjectIdHash, ownerId, position, rotation);
756+
networkObject = NetworkManager.PrefabHandler.HandleNetworkPrefabSpawn(globalObjectIdHash, ownerId, position ?? default, rotation ?? default);
757757
networkObject.NetworkManagerOwner = NetworkManager;
758758
}
759759
else
@@ -804,7 +804,9 @@ internal NetworkObject GetNetworkObjectToSpawn(uint globalObjectIdHash, ulong ow
804804
else
805805
{
806806
// Create prefab instance while applying any pre-assigned position and rotation values
807-
networkObject = UnityEngine.Object.Instantiate(networkPrefabReference, position, rotation).GetComponent<NetworkObject>();
807+
networkObject = UnityEngine.Object.Instantiate(networkPrefabReference).GetComponent<NetworkObject>();
808+
networkObject.transform.position = position ?? networkObject.transform.position;
809+
networkObject.transform.rotation = rotation ?? networkObject.transform.rotation;
808810
networkObject.NetworkManagerOwner = NetworkManager;
809811
networkObject.PrefabGlobalObjectIdHash = globalObjectIdHash;
810812
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerEventsTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,60 @@ internal class NetworkManagerEventsTests
1313
private NetworkManager m_ClientManager;
1414
private NetworkManager m_ServerManager;
1515

16+
private NetworkManager m_NetworkManagerInstantiated;
17+
private bool m_Instantiated;
18+
private bool m_Destroyed;
19+
20+
/// <summary>
21+
/// Validates the <see cref="NetworkManager.OnInstantiated"/> and <see cref="NetworkManager.OnDestroying"/> event notifications
22+
/// </summary>
23+
[UnityTest]
24+
public IEnumerator InstantiatedAndDestroyingNotifications()
25+
{
26+
NetworkManager.OnInstantiated += NetworkManager_OnInstantiated;
27+
NetworkManager.OnDestroying += NetworkManager_OnDestroying;
28+
var waitPeriod = new WaitForSeconds(0.01f);
29+
var prefab = new GameObject("InstantiateDestroy");
30+
var networkManagerPrefab = prefab.AddComponent<NetworkManager>();
31+
32+
Assert.IsTrue(m_Instantiated, $"{nameof(NetworkManager)} prefab did not get instantiated event notification!");
33+
Assert.IsTrue(m_NetworkManagerInstantiated == networkManagerPrefab, $"{nameof(NetworkManager)} prefab parameter did not match!");
34+
35+
m_Instantiated = false;
36+
m_NetworkManagerInstantiated = null;
37+
38+
for (int i = 0; i < 3; i++)
39+
{
40+
var instance = Object.Instantiate(prefab);
41+
var networkManager = instance.GetComponent<NetworkManager>();
42+
Assert.IsTrue(m_Instantiated, $"{nameof(NetworkManager)} instance-{i} did not get instantiated event notification!");
43+
Assert.IsTrue(m_NetworkManagerInstantiated == networkManager, $"{nameof(NetworkManager)} instance-{i} parameter did not match!");
44+
Object.DestroyImmediate(instance);
45+
Assert.IsTrue(m_Destroyed, $"{nameof(NetworkManager)} instance-{i} did not get destroying event notification!");
46+
m_Instantiated = false;
47+
m_NetworkManagerInstantiated = null;
48+
m_Destroyed = false;
49+
}
50+
m_NetworkManagerInstantiated = networkManagerPrefab;
51+
Object.Destroy(prefab);
52+
yield return null;
53+
Assert.IsTrue(m_Destroyed, $"{nameof(NetworkManager)} prefab did not get destroying event notification!");
54+
NetworkManager.OnInstantiated -= NetworkManager_OnInstantiated;
55+
NetworkManager.OnDestroying -= NetworkManager_OnDestroying;
56+
}
57+
58+
private void NetworkManager_OnInstantiated(NetworkManager networkManager)
59+
{
60+
m_Instantiated = true;
61+
m_NetworkManagerInstantiated = networkManager;
62+
}
63+
64+
private void NetworkManager_OnDestroying(NetworkManager networkManager)
65+
{
66+
m_Destroyed = true;
67+
Assert.True(m_NetworkManagerInstantiated == networkManager, $"Destroying {nameof(NetworkManager)} and current instance is not a match for the one passed into the event!");
68+
}
69+
1670
[UnityTest]
1771
public IEnumerator OnServerStoppedCalledWhenServerStops()
1872
{

com.unity.netcode.gameobjects/Tests/Runtime/PlayerObjectTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,68 @@ public IEnumerator SpawnWithNoObservers()
120120
}
121121
}
122122
}
123+
124+
/// <summary>
125+
/// This test validates the player position and rotation is correct
126+
/// relative to the prefab's initial settings if no changes are applied.
127+
/// </summary>
128+
[TestFixture(HostOrServer.DAHost)]
129+
[TestFixture(HostOrServer.Host)]
130+
[TestFixture(HostOrServer.Server)]
131+
internal class PlayerSpawnPositionTests : IntegrationTestWithApproximation
132+
{
133+
protected override int NumberOfClients => 2;
134+
135+
public PlayerSpawnPositionTests(HostOrServer hostOrServer) : base(hostOrServer) { }
136+
137+
private Vector3 m_PlayerPosition;
138+
private Quaternion m_PlayerRotation;
139+
140+
protected override void OnCreatePlayerPrefab()
141+
{
142+
var playerNetworkObject = m_PlayerPrefab.GetComponent<NetworkObject>();
143+
m_PlayerPosition = GetRandomVector3(-10.0f, 10.0f);
144+
m_PlayerRotation = Quaternion.Euler(GetRandomVector3(-180.0f, 180.0f));
145+
playerNetworkObject.transform.position = m_PlayerPosition;
146+
playerNetworkObject.transform.rotation = m_PlayerRotation;
147+
base.OnCreatePlayerPrefab();
148+
}
149+
150+
private void PlayerTransformMatches(NetworkObject player)
151+
{
152+
var position = player.transform.position;
153+
var rotation = player.transform.rotation;
154+
Assert.True(Approximately(m_PlayerPosition, position), $"Client-{player.OwnerClientId} position {position} does not match the prefab position {m_PlayerPosition}!");
155+
Assert.True(Approximately(m_PlayerRotation, rotation), $"Client-{player.OwnerClientId} rotation {rotation.eulerAngles} does not match the prefab rotation {m_PlayerRotation.eulerAngles}!");
156+
}
157+
158+
[UnityTest]
159+
public IEnumerator PlayerSpawnPosition()
160+
{
161+
if (m_ServerNetworkManager.IsHost)
162+
{
163+
PlayerTransformMatches(m_ServerNetworkManager.LocalClient.PlayerObject);
164+
165+
foreach (var client in m_ClientNetworkManagers)
166+
{
167+
yield return WaitForConditionOrTimeOut(() => client.SpawnManager.SpawnedObjects.ContainsKey(m_ServerNetworkManager.LocalClient.PlayerObject.NetworkObjectId));
168+
AssertOnTimeout($"Client-{client.LocalClientId} does not contain a player prefab instance for client-{m_ServerNetworkManager.LocalClientId}!");
169+
PlayerTransformMatches(client.SpawnManager.SpawnedObjects[m_ServerNetworkManager.LocalClient.PlayerObject.NetworkObjectId]);
170+
}
171+
}
172+
173+
foreach (var client in m_ClientNetworkManagers)
174+
{
175+
yield return WaitForConditionOrTimeOut(() => m_ServerNetworkManager.SpawnManager.SpawnedObjects.ContainsKey(client.LocalClient.PlayerObject.NetworkObjectId));
176+
AssertOnTimeout($"Client-{m_ServerNetworkManager.LocalClientId} does not contain a player prefab instance for client-{client.LocalClientId}!");
177+
PlayerTransformMatches(m_ServerNetworkManager.SpawnManager.SpawnedObjects[client.LocalClient.PlayerObject.NetworkObjectId]);
178+
foreach (var subClient in m_ClientNetworkManagers)
179+
{
180+
yield return WaitForConditionOrTimeOut(() => subClient.SpawnManager.SpawnedObjects.ContainsKey(client.LocalClient.PlayerObject.NetworkObjectId));
181+
AssertOnTimeout($"Client-{subClient.LocalClientId} does not contain a player prefab instance for client-{client.LocalClientId}!");
182+
PlayerTransformMatches(subClient.SpawnManager.SpawnedObjects[client.LocalClient.PlayerObject.NetworkObjectId]);
183+
}
184+
}
185+
}
186+
}
123187
}

0 commit comments

Comments
 (0)