Skip to content

Commit c26b42e

Browse files
fix: Exception thrown when player prefab is null (#3965)
* fix Fixes the issue where if the player prefab is null it will throw an exception. * update Adding changelog entry. * test Adding test to validate this PR.
1 parent 4aaf266 commit c26b42e

5 files changed

Lines changed: 108 additions & 3 deletions

File tree

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2222

2323
### Fixed
2424

25+
- Fixed issue where if the `NetworkManager` player prefab is not assigned an exception is thrown upon starting a host and/or when a client joins. (#3965)
2526

2627
### Security
2728

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,37 @@ internal void ProcessClientsToDisconnect()
946946
m_ClientsToDisconnect.Clear();
947947
}
948948

949+
/// <summary>
950+
/// The checks to find the right GlobalObjectIdHash value
951+
/// are complex enough to deserve a method that includes
952+
/// an easy to follow logical flow.
953+
/// This also makes it a quick check to determine if there
954+
/// even is a player prefab to spawn (it is valid to not
955+
/// have any player spawned upon connection).
956+
/// </summary>
957+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
958+
private (bool IsValid, uint GlobalObjectIdHash) GetPlayerPrefabHash(uint? playerPrefabHash)
959+
{
960+
if (playerPrefabHash != null && playerPrefabHash.HasValue)
961+
{
962+
return (true, playerPrefabHash.Value);
963+
}
964+
else
965+
if (NetworkManager.NetworkConfig.PlayerPrefab != null)
966+
{
967+
var networkObject = NetworkManager.NetworkConfig.PlayerPrefab.GetComponent<NetworkObject>();
968+
if (networkObject != null)
969+
{
970+
return (true, networkObject.GlobalObjectIdHash);
971+
}
972+
else
973+
{
974+
NetworkManager.Log.Error(new Logging.Context(LogLevel.Error, $"Player prefab {NetworkManager.NetworkConfig.PlayerPrefab.name} has no {nameof(NetworkObject)}!"));
975+
}
976+
}
977+
return (false, 0);
978+
}
979+
949980
/// <summary>
950981
/// Server Side: Handles the approval of a client
951982
/// </summary>
@@ -972,10 +1003,10 @@ internal void HandleConnectionApproval(ulong ownerClientId, bool createPlayerObj
9721003
}
9731004

9741005
// Server-side spawning (only if there is a prefab hash or player prefab provided)
975-
var idHashToSpawn = playerPrefabHash ?? NetworkManager.NetworkConfig.PlayerPrefab?.GetComponent<NetworkObject>()?.GlobalObjectIdHash;
976-
if (!NetworkManager.DistributedAuthorityMode && createPlayerObject && idHashToSpawn.HasValue)
1006+
var idHashToSpawn = GetPlayerPrefabHash(playerPrefabHash);
1007+
if (!NetworkManager.DistributedAuthorityMode && createPlayerObject && idHashToSpawn.IsValid)
9771008
{
978-
var playerObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(idHashToSpawn.Value, ownerClientId, playerPosition, playerRotation);
1009+
var playerObject = NetworkManager.SpawnManager.GetNetworkObjectToSpawn(idHashToSpawn.GlobalObjectIdHash, ownerClientId, playerPosition, playerRotation);
9791010

9801011
if (playerObject == null)
9811012
{
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using Unity.Netcode.TestHelpers.Runtime;
4+
using UnityEngine.TestTools;
5+
6+
namespace Unity.Netcode.RuntimeTests
7+
{
8+
/// <summary>
9+
/// Generic <see cref="NetworkManager"/> test to validate clients can connect
10+
/// without having set a player prefab.
11+
/// </summary>
12+
[TestFixture(HostOrServer.Server)]
13+
[TestFixture(HostOrServer.Host)]
14+
[TestFixture(HostOrServer.DAHost)]
15+
internal class NetworkManagerPlayerPrefab : NetcodeIntegrationTest
16+
{
17+
protected override int NumberOfClients => 1;
18+
19+
public NetworkManagerPlayerPrefab(HostOrServer hostOrServer) : base(hostOrServer)
20+
{
21+
}
22+
23+
/// <summary>
24+
/// Assure no player prefab is assigned.
25+
/// </summary>
26+
protected override void OnServerAndClientsCreated()
27+
{
28+
foreach (var networkManager in m_NetworkManagers)
29+
{
30+
networkManager.NetworkConfig.PlayerPrefab = null;
31+
}
32+
base.OnServerAndClientsCreated();
33+
}
34+
35+
protected override void OnNewClientCreated(NetworkManager networkManager)
36+
{
37+
networkManager.NetworkConfig.PlayerPrefab = null;
38+
base.OnNewClientCreated(networkManager);
39+
}
40+
41+
/// <summary>
42+
/// Do not wait for spawned players as there are none.
43+
/// </summary>
44+
/// <returns></returns>
45+
protected override bool ShouldCheckForSpawnedPlayers()
46+
{
47+
return false;
48+
}
49+
50+
/// <summary>
51+
/// Validates NetworkManager can start as a host and/or clients
52+
/// can join when there is no player prefab assigned to the
53+
/// NetworkManager.
54+
/// </summary>
55+
[UnityTest]
56+
public IEnumerator VerifyNetworkManagerHandlesNoPlayerPrefab()
57+
{
58+
// If we make it to here, then the 1st client and the authority
59+
// connected with no exceptions.
60+
// Now just late join a 2nd client.
61+
yield return CreateAndStartNewClient();
62+
// If it makes it to here without an exception then the test passes.
63+
}
64+
}
65+
}

com.unity.netcode.gameobjects/Tests/Runtime/NetworkManagerPlayerPrefab.cs.meta

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

com.unity.netcode.gameobjects/Tests/Runtime/TestHelpers/NetcodeIntegrationTest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,6 +1165,12 @@ protected virtual void OnTimeTravelServerAndClientsConnected()
11651165
private void ClientNetworkManagerPostStart(NetworkManager networkManager)
11661166
{
11671167
networkManager.name = $"NetworkManager - Client - {networkManager.LocalClientId}";
1168+
1169+
// Always make sure we have a player to check.
1170+
if (!ShouldCheckForSpawnedPlayers())
1171+
{
1172+
return;
1173+
}
11681174
Assert.NotNull(networkManager.LocalClient.PlayerObject, $"{nameof(StartServerAndClients)} detected that client {networkManager.LocalClientId} does not have an assigned player NetworkObject!");
11691175

11701176
// Go ahead and create an entry for this new client

0 commit comments

Comments
 (0)