Skip to content

Commit 06118b8

Browse files
fix: ChangeOwnership sending own clientId and added NetworkSceneHandle struct (#3647)
* chore: Update the OwnershipPermissionsTests to work with rust server * Remove TestHelpers change * Ensure we don't send CreateObject messages to the cmb service when there are no valid receivers * dotnet-fix * Ensure the DAHost complains if the senderId is included in the ClientIds list * Refactor and unify the ChangeOwnership message sending * Update the NetworkObjectOwnershipPropertiesTests to work with rust * Add placeholder SceneHandle object * Add serializer for the SceneHandle * Use SceneHandle in place of int * Fix ulong to int conversions * Ensure the spawn manager has the SceneHandle object in scope * Fix implicit conversions in the tests * Fix remaining implicit conversions in DistributedAuthorityCodecTests * Fix more ulong to int conversions * Simplify the IntegrationTestSceneHandler some * Update the testproject asmdef so the sceneEventDataTests compile * fix whitespace * fix whitespace part 2 electric boogaloo * Fix codestyle violations * Fix import ordering * fix FastBuffer tests * Remove serialization changes * Use NetworkSceneHandle wrapper class * Remove unneeded changes * Remove accidental int conversion * Ensure the tests use a specific constructor * Use the correct syntax * Fix whitespace errors * Fix the xml doc * fix This resolves the issue with the new edge case where a scene could be unloaded but a spawned object referencing the scene handle could still be spawned (pending being de-spawned within a few frames). --------- Co-authored-by: Noel Stephens <[email protected]>
1 parent 6626762 commit 06118b8

19 files changed

+491
-305
lines changed

.yamato/cmb-service-standalone-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
cmb_service_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ editor }}:
3535
name : CMB Service Test - NGO {{ project.name }} - [{{ platform.name }}, {{ editor }}, {{ backend }}]
3636
agent:
37-
type: {{ platform.type }}::GPU
37+
type: {{ platform.type }}
3838
image: {{ platform.image }}
3939
flavor: {{ platform.flavor }}
4040

.yamato/desktop-standalone-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
desktop_standalone_build_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ editor }}:
3737
name : Standalone Build - NGO {{ project.name }} - [{{ platform.name }}, {{ editor }}, {{ backend }}]
3838
agent:
39-
type: {% if platform.name == "mac" %} {{ platform.type }} {% else %} {{ platform.type }}::GPU {% endif %}
39+
type: {% if platform.name == "mac" %} {{ platform.type }} {% else %} {{ platform.type }} {% endif %}
4040
image: {{ platform.image }}
4141
flavor: {{ platform.flavor }}
4242
{% if platform.name == "mac" %}
@@ -70,7 +70,7 @@ desktop_standalone_build_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{
7070
desktop_standalone_test_{{ project.name }}_{{ platform.name }}_{{ backend }}_{{ editor }}:
7171
name : Standalone Test - NGO {{ project.name }} - [{{ platform.name }}, {{ editor }}, {{ backend }}]
7272
agent:
73-
type: {% if platform.name == "mac" %} {{ platform.type }} {% else %} {{ platform.type }}::GPU {% endif %}
73+
type: {% if platform.name == "mac" %} {{ platform.type }} {% else %} {{ platform.type }} {% endif %}
7474
image: {{ platform.image }}
7575
flavor: {{ platform.flavor }}
7676
{% if platform.name == "mac" %}

com.unity.netcode.gameobjects/CHANGELOG.md

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

1111
### Added
1212

13+
- `NetworkSceneManager` as an internal wrapper for the `SceneManager.Scene.handle` and swapped all places that use an `int` to represent a `Scene.handle` to instead use the `NetworkSceneManager`. (#3647)
1314
- Clicking on the Help icon in the inspector will now redirect to the relevant documentation. (#3663)
1415
- Added a `Set` function onto `NetworkList` that takes an optional parameter that forces an update to be processed even if the current value is equal to the previous value. (#3690)
1516

@@ -26,6 +27,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2627

2728
### Fixed
2829

30+
- Distributed authority clients no longer send themselves in the `ClientIds` list when sending a `ChangeOwnershipMessage`. (#3687)
2931
- Made a variety of small performance improvements. (#3683)
3032

3133
### Security

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

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,12 +1367,12 @@ public bool IsNetworkVisibleTo(ulong clientId)
13671367
/// the most important part to uniquely identify in-scene
13681368
/// placed NetworkObjects
13691369
/// </summary>
1370-
internal int SceneOriginHandle = 0;
1370+
internal NetworkSceneHandle SceneOriginHandle;
13711371

13721372
/// <summary>
13731373
/// The server-side scene origin handle
13741374
/// </summary>
1375-
internal int NetworkSceneHandle = 0;
1375+
internal NetworkSceneHandle NetworkSceneHandle;
13761376

13771377
private Scene m_SceneOrigin;
13781378
/// <summary>
@@ -1393,7 +1393,7 @@ internal Scene SceneOrigin
13931393
{
13941394
// The scene origin should only be set once.
13951395
// Once set, it should never change.
1396-
if (SceneOriginHandle == 0 && value.IsValid() && value.isLoaded)
1396+
if (SceneOriginHandle.IsEmpty() && value.IsValid() && value.isLoaded)
13971397
{
13981398
m_SceneOrigin = value;
13991399
SceneOriginHandle = value.handle;
@@ -1405,13 +1405,13 @@ internal Scene SceneOrigin
14051405
/// Helper method to return the correct scene handle
14061406
/// Note: Do not use this within NetworkSpawnManager.SpawnNetworkObjectLocallyCommon
14071407
/// </summary>
1408-
internal int GetSceneOriginHandle()
1408+
internal NetworkSceneHandle GetSceneOriginHandle()
14091409
{
1410-
if (SceneOriginHandle == 0 && IsSpawned && IsSceneObject != false)
1410+
if (SceneOriginHandle.IsEmpty() && IsSpawned && IsSceneObject != false)
14111411
{
14121412
throw new Exception($"{nameof(GetSceneOriginHandle)} called when {nameof(SceneOriginHandle)} is still zero but the {nameof(NetworkObject)} is already spawned!");
14131413
}
1414-
return SceneOriginHandle != 0 ? SceneOriginHandle : gameObject.scene.handle;
1414+
return !SceneOriginHandle.IsEmpty() ? SceneOriginHandle : gameObject.scene.handle;
14151415
}
14161416

14171417
/// <summary>
@@ -2907,7 +2907,7 @@ public struct TransformData : INetworkSerializeByMemcpy
29072907
public NetworkObject OwnerObject;
29082908
public ulong TargetClientId;
29092909

2910-
public int NetworkSceneHandle;
2910+
public NetworkSceneHandle NetworkSceneHandle;
29112911

29122912
internal int SynchronizationDataSize;
29132913

@@ -3420,15 +3420,14 @@ internal void SceneChangedUpdate(Scene scene, bool notify = false)
34203420
{
34213421
// Since the authority is the source of truth for the NetworkSceneHandle,
34223422
// the NetworkSceneHandle is the same as the SceneOriginHandle.
3423-
if (NetworkManager.DistributedAuthorityMode)
3423+
if (NetworkManager.DistributedAuthorityMode && NetworkManager.SceneManager.ClientSceneHandleToServerSceneHandle.ContainsKey(SceneOriginHandle))
34243424
{
34253425
NetworkSceneHandle = NetworkManager.SceneManager.ClientSceneHandleToServerSceneHandle[SceneOriginHandle];
34263426
}
34273427
else
34283428
{
34293429
NetworkSceneHandle = SceneOriginHandle;
34303430
}
3431-
34323431
}
34333432
else // Otherwise, the client did not find the client to server scene handle
34343433
if (NetworkManager.LogLevel == LogLevel.Developer)

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

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System.Linq;
12
using System.Runtime.CompilerServices;
3+
using UnityEngine;
24

35
namespace Unity.Netcode
46
{
@@ -143,42 +145,47 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
143145
public void Handle(ref NetworkContext context)
144146
{
145147
var networkManager = (NetworkManager)context.SystemOwner;
148+
var hasObject = networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out var networkObject);
146149

147150
// If we are the DAHost then forward this message
148151
if (networkManager.DAHost)
149152
{
150-
var shouldProcessLocally = HandleDAHostMessageForwarding(ref networkManager, context.SenderId);
153+
var shouldProcessLocally = HandleDAHostMessageForwarding(ref networkManager, context.SenderId, hasObject, ref networkObject);
151154
if (!shouldProcessLocally)
152155
{
153156
return;
154157
}
155158
}
156159

160+
if (!hasObject)
161+
{
162+
if (networkManager.LogLevel <= LogLevel.Normal)
163+
{
164+
NetworkLog.LogError("Ownership change received for an unknown network object. This should not happen.");
165+
}
166+
return;
167+
}
168+
157169
// If ownership is changing (either a straight change or a request approval), then run through the ownership changed sequence
158170
// Note: There is some extended ownership script at the bottom of HandleOwnershipChange
159171
// If not in distributed authority mode, ChangeMessageType will always be OwnershipChanging.
160172
if (ChangeMessageType == ChangeType.OwnershipChanging || ChangeMessageType == ChangeType.RequestApproved || !networkManager.DistributedAuthorityMode)
161173
{
162-
HandleOwnershipChange(ref context);
174+
HandleOwnershipChange(ref context, ref networkManager, ref networkObject);
163175
}
164176
else if (networkManager.DistributedAuthorityMode)
165177
{
166178
// Otherwise, we handle and extended ownership update
167-
HandleExtendedOwnershipUpdate(ref context);
179+
HandleExtendedOwnershipUpdate(ref context, ref networkObject);
168180
}
169181
}
170182

171183
/// <summary>
172184
/// Handle the extended distributed authority ownership updates
173185
/// </summary>
174186
[MethodImpl(MethodImplOptions.AggressiveInlining)]
175-
private void HandleExtendedOwnershipUpdate(ref NetworkContext context)
187+
private void HandleExtendedOwnershipUpdate(ref NetworkContext context, ref NetworkObject networkObject)
176188
{
177-
var networkManager = (NetworkManager)context.SystemOwner;
178-
179-
// Handle the extended ownership message types
180-
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
181-
182189
if (ChangeMessageType == ChangeType.OwnershipFlagsUpdate)
183190
{
184191
// Just update the ownership flags
@@ -199,10 +206,8 @@ private void HandleExtendedOwnershipUpdate(ref NetworkContext context)
199206
/// Handle the traditional change in ownership message type logic
200207
/// </summary>
201208
[MethodImpl(MethodImplOptions.AggressiveInlining)]
202-
private void HandleOwnershipChange(ref NetworkContext context)
209+
private void HandleOwnershipChange(ref NetworkContext context, ref NetworkManager networkManager, ref NetworkObject networkObject)
203210
{
204-
var networkManager = (NetworkManager)context.SystemOwner;
205-
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
206211
var distributedAuthorityMode = networkManager.DistributedAuthorityMode;
207212

208213
// Sanity check that we are not sending duplicated change ownership messages
@@ -260,12 +265,12 @@ private void HandleOwnershipChange(ref NetworkContext context)
260265
/// </summary>
261266
/// <param name="networkManager">The current NetworkManager from the NetworkContext</param>
262267
/// <param name="senderId">The sender of the current message from the NetworkContext</param>
268+
/// <param name="hasObject">Whether the local client has this object spawned</param>
269+
/// <param name="networkObject">The networkObject we are changing ownership on. Will be null if hasObject is false.</param>
263270
/// <returns>true if this message should also be processed locally; false if the message should only be forwarded</returns>
264271
[MethodImpl(MethodImplOptions.AggressiveInlining)]
265-
private bool HandleDAHostMessageForwarding(ref NetworkManager networkManager, ulong senderId)
272+
private bool HandleDAHostMessageForwarding(ref NetworkManager networkManager, ulong senderId, bool hasObject, ref NetworkObject networkObject)
266273
{
267-
var clientList = ClientIdCount > 0 ? ClientIds : networkManager.ConnectedClientsIds;
268-
269274
var message = new ChangeOwnershipMessage()
270275
{
271276
NetworkObjectId = NetworkObjectId,
@@ -302,20 +307,44 @@ private bool HandleDAHostMessageForwarding(ref NetworkManager networkManager, ul
302307
}
303308
else
304309
{
310+
var clientList = ClientIds;
311+
var errorOnSender = true;
312+
313+
// OwnershipFlagsUpdate doesn't populate the ClientIds list.
314+
if (ChangeMessageType == ChangeType.OwnershipFlagsUpdate)
315+
{
316+
// if the DAHost can see this object, forward the message to all observers.
317+
// if the DAHost can't see the object, forward the message to everyone.
318+
clientList = hasObject ? networkObject.Observers.ToArray() : networkManager.ConnectedClientsIds.ToArray();
319+
320+
// Both clientList arrays will have the local client so we can not throw an error.
321+
errorOnSender = false;
322+
}
323+
305324
foreach (var clientId in clientList)
306325
{
307326
// Don't forward to self or originating client
308-
if (clientId == networkManager.LocalClientId || clientId == senderId)
327+
if (clientId == networkManager.LocalClientId)
309328
{
310329
continue;
311330
}
312331

332+
if (clientId == senderId)
333+
{
334+
if (errorOnSender)
335+
{
336+
Debug.LogError($"client-{senderId} sent a ChangeOwnershipMessage with themself inside the ClientIds list.");
337+
}
338+
339+
continue;
340+
}
341+
313342
networkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.Reliable, clientId);
314343
}
315344
}
316345

317346
// Return whether to process the message on the DAHost itself (only if object is spawned).
318-
return networkManager.SpawnManager.SpawnedObjects.ContainsKey(NetworkObjectId);
347+
return hasObject;
319348
}
320349
}
321350
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal struct SceneEntry
2020
}
2121
public bool IsIntegrationTest() { return false; }
2222

23-
internal Dictionary<string, Dictionary<int, SceneEntry>> SceneNameToSceneHandles = new Dictionary<string, Dictionary<int, SceneEntry>>();
23+
internal Dictionary<string, Dictionary<NetworkSceneHandle, SceneEntry>> SceneNameToSceneHandles = new();
2424

2525
public AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode loadSceneMode, SceneEventProgress sceneEventProgress)
2626
{
@@ -47,7 +47,7 @@ public void ClearSceneTracking(NetworkManager networkManager)
4747
/// <summary>
4848
/// Stops tracking a specific scene
4949
/// </summary>
50-
public void StopTrackingScene(int handle, string name, NetworkManager networkManager)
50+
public void StopTrackingScene(NetworkSceneHandle handle, string name, NetworkManager networkManager)
5151
{
5252
if (SceneNameToSceneHandles.ContainsKey(name))
5353
{
@@ -69,7 +69,7 @@ public void StartTrackingScene(Scene scene, bool assigned, NetworkManager networ
6969
{
7070
if (!SceneNameToSceneHandles.ContainsKey(scene.name))
7171
{
72-
SceneNameToSceneHandles.Add(scene.name, new Dictionary<int, SceneEntry>());
72+
SceneNameToSceneHandles.Add(scene.name, new Dictionary<NetworkSceneHandle, SceneEntry>());
7373
}
7474

7575
if (!SceneNameToSceneHandles[scene.name].ContainsKey(scene.handle))
@@ -168,7 +168,7 @@ public Scene GetSceneFromLoadedScenes(string sceneName, NetworkManager networkMa
168168
/// same application instance is still running, the same scenes are still loaded on the client, and
169169
/// upon reconnecting the client doesn't have to unload the scenes and then reload them)
170170
/// </summary>
171-
public void PopulateLoadedScenes(ref Dictionary<int, Scene> scenesLoaded, NetworkManager networkManager)
171+
public void PopulateLoadedScenes(ref Dictionary<NetworkSceneHandle, Scene> scenesLoaded, NetworkManager networkManager)
172172
{
173173
SceneNameToSceneHandles.Clear();
174174
var sceneCount = SceneManager.sceneCount;
@@ -177,7 +177,7 @@ public void PopulateLoadedScenes(ref Dictionary<int, Scene> scenesLoaded, Networ
177177
var scene = SceneManager.GetSceneAt(i);
178178
if (!SceneNameToSceneHandles.ContainsKey(scene.name))
179179
{
180-
SceneNameToSceneHandles.Add(scene.name, new Dictionary<int, SceneEntry>());
180+
SceneNameToSceneHandles.Add(scene.name, new Dictionary<NetworkSceneHandle, SceneEntry>());
181181
}
182182

183183
if (!SceneNameToSceneHandles[scene.name].ContainsKey(scene.handle))

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ internal interface ISceneManagerHandler
1414

1515
AsyncOperation UnloadSceneAsync(Scene scene, SceneEventProgress sceneEventProgress);
1616

17-
void PopulateLoadedScenes(ref Dictionary<int, Scene> scenesLoaded, NetworkManager networkManager = null);
17+
void PopulateLoadedScenes(ref Dictionary<NetworkSceneHandle, Scene> scenesLoaded, NetworkManager networkManager = null);
1818
Scene GetSceneFromLoadedScenes(string sceneName, NetworkManager networkManager = null);
1919

2020
bool DoesSceneHaveUnassignedEntry(string sceneName, NetworkManager networkManager = null);
2121

22-
void StopTrackingScene(int handle, string name, NetworkManager networkManager = null);
22+
void StopTrackingScene(NetworkSceneHandle handle, string name, NetworkManager networkManager = null);
2323

2424
void StartTrackingScene(Scene scene, bool assigned, NetworkManager networkManager = null);
2525

0 commit comments

Comments
 (0)