Skip to content

Commit b712d1c

Browse files
fix: NetworkSceneManager does not synchronize despawned in-scene placed NetworkObjects [MTT-2924] (#1898)
* fix MTT-2924 This is the first pass at solving the issue where late joining clients are not synchronized properly with despawned in-scene NetworkObjects. * Update CHANGELOG.md * Update CHANGELOG.md * fix This allows for respawning of in-scene placed NetworkObjects without having to register them with the NetworkManager. * Update CHANGELOG.md * style * Update CHANGELOG.md * style whitespace fixes. * fix Passing the entire SceneObject structure as opposed to adding more parameters to the NetworkSpawnManager.HasPrefab method in order to be able to extract the NetworkSceneHandle and include the in-scene placed NetworkObjects as part of the "prefabs to search for". * update This is some work towards integration tests where clients might be able to actually load scenes as opposed to fake loading them * fix Fixed some minor issues with tests. Updated HiddenVariableTests as it needed some timing related adjustments. * Update NetworkSceneManagerEventDataPoolTest.cs * style * style * test - integration test clients now load scenes This update allows for clients to load scenes and synchronize in-scene placed NetworkObjects during integration testing. * test - fix Now that clients are loading scenes, needed to make some minor adjustments. * test - fix Realized I don't need to simulate client loading delay unless the client is blocked from loading. Also had to adjust whether the metrics tests waited for the client or not when loading. * fix Fixes an issue that can occur where this becomes enabled in test runner and will throw an exception if target is null. * fix - NetworkSceneManager.GetSceneRelativeInSceneNetworkObject GetSceneRelativeInSceneNetworkObject needed to get the sceneHandle before checking for it existing in the ScenePlacedObjects. * fix - AddDespawnedInSceneNetworkObjects AddDespawnedInSceneNetworkObjects needed to only return despawned NetworkObjects belonging to the NetworkManager. * test - fix - Integration test client scene loading This fixes some of the last edge case scenarios such as: - Not loading scenes for clients when a non-NetcodeIntegrationTest based integration test is running since this does require some additional management and cleanup. - Assuring that in-scene NetworkObjects have the right NetworkManager assigned when loaded - Added a check to assure that it didn't time out while waiting for all clients to connect - Increased the NetcodeIntegrationTestHelpers default timeout to 2s vs 1s (clients loading scenes can slow down when things happen) - NetcodeIntegrationTests now unload all scenes loaded (but the test runner scene) during teardown and onetimeteardown. * test update Just adding timeout checks in both NetworkVariableTests and TimeMultiInstanceTest * test assets Updating some of the assets used for integration testing now that clients are loading scenes too. * test fix Changing the scenes being loaded to avoid integration test specific SDK architecture related issues. * test fix An early shutdown was not needed here * test fix Disabling the audio listener in the EmptyScene to reduce console spam during integration tests. * test fix Fixing edge case timing issue with MessageOrdering. * test InScenePlacedNetworkObjects This is the integration test for this PR. It tests late joining client synchronization with despawned NetworkObjects and tests that in-scene placed NetworkObjects do not need to be registered with the NetworkManager to be spawned. * fix Don't throw an exception when calling GetObservers or IsNetworkVisibleTo when the NetworkObject is not spawned. * fix fixing typo/mistake for the scene to be unloaded. * style * Update ObjectNameIdentifier.cs * style * style updating comments and removing LFs and some commented out code. * update and style updating comments and minor final tweaks. * update minor changes suggested by Jeffrey R. * update removing scene management specific code from places where scene management is disabled. * style updated some comments * fix Fixing an issue where CheckValueOnClient was crashing. Integration test timing did change slightly now that clients load scenes, so it was possible that the key would not exist by the time it was being checked. * fix consoles reducing the number of clients from 9 to 4 in some of the NetworkSceneManager related tests as I think it impacted performance enough to cause clients to take longer than the default timeout of 2 seconds. * fix Hopefully this will fix the issue with Xbox Scarlett * test - fix Removing m_CanCheckLoadUnloadEvents as that was a sanity check while I was testing clients loading scenes. This could possibly fix an edge case scenario issue where a message might get ignored. * update removing NetworkSceneHandle from NetworkObject as it was not needed. * Update CHANGELOG.md * update Removing wait for clients to unload scenes and unloading them via SceneManager as the test has already completed and this is more clean up than anything else. * test update Removed the crazy code in ObjectNameIdentifier for adding tag information and simplified it by storing the original name and using that each time it needs to regenerate the tag information. Moved where the NetworkManager name and tag information is applied to get the right client id. Added additional debug information. * style * test Testing with verbose mode to see why this passes on Windows 10 in a stand alone build and in the editor but fails on Yamato desktops. * test adding more debug info. * test fix Looks like the scene isn't really loaded when it says it is loaded. Moving when the NetworkSceneManager's callback is invoked and adding delay before invoking. * test fix Making some additional modifications to see if it was when the NetworkSceneManager scene Load/Unload event action is invoked and reducing the number of clients to 1. * Test Fix Minor tweaks and merging develop into this branch. * test fix Found a bug in some recent changes that would cause a crash under specific scenarios. * fix * test and style Went back through some of the changed files and removed some code no longer needed, extended some of the verbose debug info for any future debugging purposes, and updated some of the comments. * style removing commented out code.
1 parent 4a5c86f commit b712d1c

40 files changed

+1687
-349
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ Additional documentation and release notes are available at [Multiplayer Documen
1818
### Changed
1919

2020
- `unmanaged` structs are no longer universally accepted as RPC parameters because some structs (i.e., structs with pointers in them, such as `NativeList<T>`) can't be supported by the default memcpy struct serializer. Structs that are intended to be serialized across the network must add `INetworkSerializeByMemcpy` to the interface list (i.e., `struct Foo : INetworkSerializeByMemcpy`). This interface is empty and just serves to mark the struct as compatible with memcpy serialization. For external structs you can't edit, you can pass them to RPCs by wrapping them in `ForceNetworkSerializeByMemcpy<T>`. (#1901)
21+
- Changed requirement to register in-scene placed NetworkObjects with `NetworkManager` in order to respawn them. In-scene placed NetworkObjects are now automatically tracked during runtime and no longer need to be registered as a NetworkPrefab. (#1898)
2122

2223
### Removed
2324
- Removed `SIPTransport` (#1870)
2425

2526
- Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs).
2627

2728
### Fixed
28-
29+
- Fixed issue where `NetworkSceneManager` did not synchronize despawned in-scene placed NetworkObjects. (#1898)
2930
- Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890)
3031
- Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884)
3132
- Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883)
@@ -36,6 +37,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
3637
## [1.0.0-pre.7] - 2022-04-06
3738

3839
### Added
40+
3941
- Added editor only check prior to entering into play mode if the currently open and active scene is in the build list and if not displays a dialog box asking the user if they would like to automatically add it prior to entering into play mode. (#1828)
4042
- Added `UnityTransport` implementation and `com.unity.transport` package dependency (#1823)
4143
- Added `NetworkVariableWritePermission` to `NetworkVariableBase` and implemented `Owner` client writable netvars. (#1762)

com.unity.netcode.gameobjects/Editor/NetworkBehaviourEditor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ public override void OnInspectorGUI()
218218
/// </summary>
219219
private void OnEnable()
220220
{
221+
// This can be null and throw an exception when running test runner in the editor
222+
if (target == null)
223+
{
224+
return;
225+
}
221226
// When we first add a NetworkBehaviour this editor will be enabled
222227
// so we go ahead and check for an already existing NetworkObject here
223228
CheckForNetworkObject((target as NetworkBehaviour).gameObject);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1838,7 +1838,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint?
18381838

18391839
if (createPlayerObject)
18401840
{
1841-
var networkObject = SpawnManager.CreateLocalNetworkObject(false, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash, ownerClientId, null, position, rotation);
1841+
var networkObject = SpawnManager.CreateLocalNetworkObject(false, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent<NetworkObject>().GlobalObjectIdHash, ownerClientId, null, null, position, rotation);
18421842
SpawnManager.SpawnNetworkObjectLocally(networkObject, SpawnManager.GetNetworkObjectId(), false, true, ownerClientId, false);
18431843

18441844
ConnectedClients[ownerClientId].PlayerObject = networkObject;

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

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ internal string GetNameForMetrics()
151151
#endif
152152
}
153153

154+
private readonly HashSet<ulong> m_EmptyULongHashSet = new HashSet<ulong>();
154155
/// <summary>
155156
/// Returns Observers enumerator
156157
/// </summary>
@@ -159,7 +160,7 @@ public HashSet<ulong>.Enumerator GetObservers()
159160
{
160161
if (!IsSpawned)
161162
{
162-
throw new SpawnStateException("Object is not spawned");
163+
return m_EmptyULongHashSet.GetEnumerator();
163164
}
164165

165166
return Observers.GetEnumerator();
@@ -174,9 +175,8 @@ public bool IsNetworkVisibleTo(ulong clientId)
174175
{
175176
if (!IsSpawned)
176177
{
177-
throw new SpawnStateException("Object is not spawned");
178+
return false;
178179
}
179-
180180
return Observers.Contains(clientId);
181181
}
182182

@@ -372,7 +372,7 @@ private void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool play
372372
throw new NotServerException($"Only server can spawn {nameof(NetworkObject)}s");
373373
}
374374

375-
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), false, playerObject, ownerClientId, destroyWithScene);
375+
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene);
376376

377377
for (int i = 0; i < NetworkManager.ConnectedClientsList.Count; i++)
378378
{
@@ -865,16 +865,17 @@ public struct TransformData : INetworkSerializeByMemcpy
865865
public NetworkObject OwnerObject;
866866
public ulong TargetClientId;
867867

868+
public int NetworkSceneHandle;
869+
868870
public unsafe void Serialize(FastBufferWriter writer)
869871
{
870-
if (!writer.TryBeginWrite(
871-
sizeof(HeaderData) +
872-
(Header.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) +
873-
(Header.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) +
874-
(Header.IsReparented
875-
? FastBufferWriter.GetWriteSize(IsLatestParentSet) +
876-
(IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0)
877-
: 0)))
872+
var writeSize = sizeof(HeaderData);
873+
writeSize += Header.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0;
874+
writeSize += Header.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0;
875+
writeSize += Header.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + (IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0) : 0;
876+
writeSize += Header.IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
877+
878+
if (!writer.TryBeginWrite(writeSize))
878879
{
879880
throw new OverflowException("Could not serialize SceneObject: Out of buffer space.");
880881
}
@@ -900,6 +901,16 @@ public unsafe void Serialize(FastBufferWriter writer)
900901
}
901902
}
902903

904+
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
905+
// NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique
906+
// handle, it provides us with a unique and persistent "scene prefab asset" instance.
907+
// This is only set on in-scene placed NetworkObjects to reduce the over-all packet
908+
// sizes for dynamically spawned NetworkObjects.
909+
if (Header.IsSceneObject)
910+
{
911+
writer.WriteValue(OwnerObject.gameObject.scene.handle);
912+
}
913+
903914
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
904915
}
905916

@@ -910,10 +921,12 @@ public unsafe void Deserialize(FastBufferReader reader)
910921
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
911922
}
912923
reader.ReadValue(out Header);
913-
if (!reader.TryBeginRead(
914-
(Header.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0) +
915-
(Header.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0) +
916-
(Header.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) : 0)))
924+
var readSize = Header.HasParent ? FastBufferWriter.GetWriteSize(ParentObjectId) : 0;
925+
readSize += Header.HasTransform ? FastBufferWriter.GetWriteSize(Transform) : 0;
926+
readSize += Header.IsReparented ? FastBufferWriter.GetWriteSize(IsLatestParentSet) + (IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0) : 0;
927+
readSize += Header.IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
928+
929+
if (!reader.TryBeginRead(readSize))
917930
{
918931
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
919932
}
@@ -937,6 +950,16 @@ public unsafe void Deserialize(FastBufferReader reader)
937950
LatestParent = latestParent;
938951
}
939952
}
953+
954+
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
955+
// NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique
956+
// handle, it provides us with a unique and persistent "scene prefab asset" instance.
957+
// Client-side NetworkSceneManagers use this to locate their local instance of the
958+
// NetworkObject instance.
959+
if (Header.IsSceneObject)
960+
{
961+
reader.ReadValue(out NetworkSceneHandle);
962+
}
940963
}
941964
}
942965

@@ -1006,6 +1029,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
10061029
Vector3? position = null;
10071030
Quaternion? rotation = null;
10081031
ulong? parentNetworkId = null;
1032+
int? networkSceneHandle = null;
10091033

10101034
if (sceneObject.Header.HasTransform)
10111035
{
@@ -1018,10 +1042,15 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
10181042
parentNetworkId = sceneObject.ParentObjectId;
10191043
}
10201044

1045+
if (sceneObject.Header.IsSceneObject)
1046+
{
1047+
networkSceneHandle = sceneObject.NetworkSceneHandle;
1048+
}
1049+
10211050
//Attempt to create a local NetworkObject
10221051
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(
10231052
sceneObject.Header.IsSceneObject, sceneObject.Header.Hash,
1024-
sceneObject.Header.OwnerClientId, parentNetworkId, position, rotation, sceneObject.Header.IsReparented);
1053+
sceneObject.Header.OwnerClientId, parentNetworkId, networkSceneHandle, position, rotation, sceneObject.Header.IsReparented);
10251054

10261055
networkObject?.SetNetworkParenting(sceneObject.Header.IsReparented, sceneObject.LatestParent);
10271056

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/CreateObjectMessage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
1919
}
2020

2121
ObjectInfo.Deserialize(reader);
22-
if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo.Header.IsSceneObject, ObjectInfo.Header.Hash))
22+
if (!networkManager.NetworkConfig.ForceSamePrefabs && !networkManager.SpawnManager.HasPrefab(ObjectInfo))
2323
{
2424
networkManager.DeferredMessageManager.DeferMessage(IDeferredMessageManager.TriggerType.OnAddPrefab, ObjectInfo.Header.Hash, reader, ref context);
2525
return false;

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/DestroyObjectMessage.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace Unity.Netcode
33
internal struct DestroyObjectMessage : INetworkMessage, INetworkSerializeByMemcpy
44
{
55
public ulong NetworkObjectId;
6+
public bool DestroyGameObject;
67

78
public void Serialize(FastBufferWriter writer)
89
{
@@ -37,7 +38,7 @@ public void Handle(ref NetworkContext context)
3738
}
3839

3940
networkManager.NetworkMetrics.TrackObjectDestroyReceived(context.SenderId, networkObject, context.MessageSize);
40-
networkManager.SpawnManager.OnDespawnObject(networkObject, true);
41+
networkManager.SpawnManager.OnDespawnObject(networkObject, DestroyGameObject);
4142
}
4243
}
4344
}

com.unity.netcode.gameobjects/Runtime/SceneManagement/ISceneManagerHandler.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@ struct SceneEventAction
1515
{
1616
internal uint SceneEventId;
1717
internal Action<uint> EventAction;
18+
/// <summary>
19+
/// Used server-side for integration testing in order to
20+
/// invoke the SceneEventProgress once done loading
21+
/// </summary>
22+
internal Action Completed;
1823
internal void Invoke()
1924
{
25+
Completed?.Invoke();
2026
EventAction.Invoke(SceneEventId);
2127
}
2228
}

0 commit comments

Comments
 (0)