Skip to content

Commit 0e475bc

Browse files
authored
fix: Never distribute an object to a client who is not an observer (#3323)
<!-- Replace this block with what this PR does and why. Describe what you'd like reviewers to know, how you applied the engineering principles, and any interesting tradeoffs made. Delete bullet points below that don't apply, and update the changelog section as appropriate. --> <!-- Add short version of the JIRA ticket to the PR title (e.g. "feat: new shiny feature [MTT-123]") --> [MTTB-1041](https://jira.unity3d.com/browse/MTTB-1041) <!-- Add RFC link here if applicable. --> ## Changelog - Fixed: ChangeOwnership should never distribute to a client that is not an observer of that object. ## Testing and Documentation - No tests have been added. <!-- Uncomment and mark items off with a * if this PR deprecates any API: ### Deprecated API - [ ] An `[Obsolete]` attribute was added along with a `(RemovedAfter yyyy-mm-dd)` entry. - [ ] An [api updater] was added. - [ ] Deprecation of the API is explained in the CHANGELOG. - [ ] The users can understand why this API was removed and what they should use instead. --> Closes #3299
1 parent dc13db4 commit 0e475bc

File tree

4 files changed

+139
-68
lines changed

4 files changed

+139
-68
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1515

1616
### Fixed
1717

18+
- Fixed `ChangeOwnership` changing ownership to clients that are not observers. This also happened with automated object distribution. (#3323)
1819
- Fixed issue where `AnticipatedNetworkVariable` previous value returned by `AnticipatedNetworkVariable.OnAuthoritativeValueChanged` is updated correctly on the non-authoritative side. (#3306)
1920
- Fixed `OnClientConnectedCallback` passing incorrect `clientId` when scene management is disabled. (#3312)
2021
- Fixed issue where the `NetworkObject.Ownership` custom editor did not take the default "Everything" flag into consideration. (#3305)

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

+73-53
Original file line numberDiff line numberDiff line change
@@ -1122,18 +1122,16 @@ internal void OnClientDisconnectFromServer(ulong clientId)
11221122
{
11231123
if (!playerObject.DontDestroyWithOwner)
11241124
{
1125-
// DANGO-TODO: This is something that would be best for CMB Service to handle as it is part of the disconnection process
11261125
// If a player NetworkObject is being despawned, make sure to remove all children if they are marked to not be destroyed
11271126
// with the owner.
1128-
if (NetworkManager.DistributedAuthorityMode && NetworkManager.DAHost)
1127+
if (NetworkManager.DAHost)
11291128
{
11301129
// Remove any children from the player object if they are not going to be destroyed with the owner
11311130
var childNetworkObjects = playerObject.GetComponentsInChildren<NetworkObject>();
11321131
foreach (var child in childNetworkObjects)
11331132
{
1134-
// TODO: We have always just removed all children, but we might think about changing this to preserve the nested child
1135-
// hierarchy.
1136-
if (child.DontDestroyWithOwner && child.transform.transform.parent != null)
1133+
// TODO: We have always just removed all children, but we might think about changing this to preserve the nested child hierarchy.
1134+
if (child.DontDestroyWithOwner && child.transform.transform.parent)
11371135
{
11381136
// If we are here, then we are running in DAHost mode and have the authority to remove the child from its parent
11391137
child.AuthorityAppliedParenting = true;
@@ -1158,10 +1156,7 @@ internal void OnClientDisconnectFromServer(ulong clientId)
11581156
}
11591157
else if (!NetworkManager.ShutdownInProgress)
11601158
{
1161-
if (!NetworkManager.ShutdownInProgress)
1162-
{
1163-
playerObject.RemoveOwnership();
1164-
}
1159+
playerObject.RemoveOwnership();
11651160
}
11661161
}
11671162

@@ -1175,7 +1170,7 @@ internal void OnClientDisconnectFromServer(ulong clientId)
11751170
for (int i = clientOwnedObjects.Count - 1; i >= 0; i--)
11761171
{
11771172
var ownedObject = clientOwnedObjects[i];
1178-
if (ownedObject != null)
1173+
if (ownedObject)
11791174
{
11801175
// If destroying with owner, then always despawn and destroy (or defer destroying to prefab handler)
11811176
if (!ownedObject.DontDestroyWithOwner)
@@ -1196,68 +1191,93 @@ internal void OnClientDisconnectFromServer(ulong clientId)
11961191
}
11971192
else if (!NetworkManager.ShutdownInProgress)
11981193
{
1194+
// DANGO-TODO: We will want to match how the CMB service handles this. For now, we just try to evenly distribute
1195+
// ownership.
11991196
// NOTE: All of the below code only handles ownership transfer.
12001197
// For client-server, we just remove the ownership.
12011198
// For distributed authority, we need to change ownership based on parenting
12021199
if (NetworkManager.DistributedAuthorityMode)
12031200
{
1204-
// Only NetworkObjects that have the OwnershipStatus.Distributable flag set and no parent
1205-
// (ownership is transferred to all children) will have their ownership redistributed.
1206-
if (ownedObject.IsOwnershipDistributable && ownedObject.GetCachedParent() == null && !ownedObject.IsOwnershipSessionOwner)
1201+
// Only NetworkObjects that have the OwnershipStatus.Distributable flag set and are not OwnershipSessionOwner are distributed.
1202+
// If the object has a parent - skip it for now, it will be distributed when its root parent is distributed.
1203+
if (!ownedObject.IsOwnershipDistributable || ownedObject.IsOwnershipSessionOwner || ownedObject.GetCachedParent())
1204+
{
1205+
continue;
1206+
}
1207+
1208+
if (ownedObject.IsOwnershipLocked)
1209+
{
1210+
ownedObject.SetOwnershipLock(false);
1211+
}
1212+
1213+
var targetOwner = NetworkManager.ServerClientId;
1214+
// Cycle through the full count of clients to find
1215+
// the next viable owner. If none are found, then
1216+
// the DAHost defaults to the owner.
1217+
for (int j = 0; j < remainingClients.Count; j++)
12071218
{
1208-
if (ownedObject.IsOwnershipLocked)
1219+
clientCounter++;
1220+
clientCounter = clientCounter % predictedClientCount;
1221+
if (ownedObject.Observers.Contains(remainingClients[clientCounter].ClientId))
12091222
{
1210-
ownedObject.SetOwnershipLock(false);
1223+
targetOwner = remainingClients[clientCounter].ClientId;
1224+
break;
12111225
}
1226+
}
1227+
if (EnableDistributeLogging)
1228+
{
1229+
Debug.Log($"[Disconnected][Client-{clientId}][NetworkObjectId-{ownedObject.NetworkObjectId} Distributed to Client-{targetOwner}");
1230+
}
12121231

1213-
// DANGO-TODO: We will want to match how the CMB service handles this. For now, we just try to evenly distribute
1214-
// ownership.
1215-
var targetOwner = NetworkManager.ServerClientId;
1216-
if (predictedClientCount > 1)
1232+
NetworkManager.SpawnManager.ChangeOwnership(ownedObject, targetOwner, true);
1233+
1234+
// Ownership gets passed down to all children that have the same owner.
1235+
var childNetworkObjects = ownedObject.GetComponentsInChildren<NetworkObject>();
1236+
foreach (var childObject in childNetworkObjects)
1237+
{
1238+
// We already changed ownership for this
1239+
if (childObject == ownedObject)
12171240
{
1218-
clientCounter++;
1219-
clientCounter = clientCounter % predictedClientCount;
1220-
targetOwner = remainingClients[clientCounter].ClientId;
1241+
continue;
12211242
}
1222-
if (EnableDistributeLogging)
1243+
// If the client owner disconnected, it is ok to unlock this at this point in time.
1244+
if (childObject.IsOwnershipLocked)
12231245
{
1224-
Debug.Log($"[Disconnected][Client-{clientId}][NetworkObjectId-{ownedObject.NetworkObjectId} Distributed to Client-{targetOwner}");
1246+
childObject.SetOwnershipLock(false);
12251247
}
1226-
NetworkManager.SpawnManager.ChangeOwnership(ownedObject, targetOwner, true);
1227-
// DANGO-TODO: Should we try handling inactive NetworkObjects?
1228-
// Ownership gets passed down to all children that have the same owner.
1229-
var childNetworkObjects = ownedObject.GetComponentsInChildren<NetworkObject>();
1230-
foreach (var childObject in childNetworkObjects)
1248+
1249+
// Ignore session owner marked objects
1250+
if (childObject.IsOwnershipSessionOwner)
12311251
{
1232-
// We already changed ownership for this
1233-
if (childObject == ownedObject)
1234-
{
1235-
continue;
1236-
}
1237-
// If the client owner disconnected, it is ok to unlock this at this point in time.
1238-
if (childObject.IsOwnershipLocked)
1239-
{
1240-
childObject.SetOwnershipLock(false);
1241-
}
1252+
continue;
1253+
}
12421254

1243-
// Ignore session owner marked objects
1244-
if (childObject.IsOwnershipSessionOwner)
1245-
{
1246-
continue;
1247-
}
1255+
// If the child's owner is not the client disconnected and the objects are marked with either distributable or transferable, then
1256+
// do not change ownership.
1257+
if (childObject.OwnerClientId != clientId && (childObject.IsOwnershipDistributable || childObject.IsOwnershipTransferable))
1258+
{
1259+
continue;
1260+
}
12481261

1249-
// If the child's owner is not the client disconnected and the objects are marked with either distributable or transferable, then
1250-
// do not change ownership.
1251-
if (childObject.OwnerClientId != clientId && (childObject.IsOwnershipDistributable || childObject.IsOwnershipTransferable))
1262+
var childOwner = targetOwner;
1263+
if (!childObject.Observers.Contains(childOwner))
1264+
{
1265+
for (int j = 0; j < remainingClients.Count; j++)
12521266
{
1253-
continue;
1267+
clientCounter++;
1268+
clientCounter = clientCounter % predictedClientCount;
1269+
if (ownedObject.Observers.Contains(remainingClients[clientCounter].ClientId))
1270+
{
1271+
childOwner = remainingClients[clientCounter].ClientId;
1272+
break;
1273+
}
12541274
}
1275+
}
12551276

1256-
NetworkManager.SpawnManager.ChangeOwnership(childObject, targetOwner, true);
1257-
if (EnableDistributeLogging)
1258-
{
1259-
Debug.Log($"[Disconnected][Client-{clientId}][Child of {ownedObject.NetworkObjectId}][NetworkObjectId-{ownedObject.NetworkObjectId} Distributed to Client-{targetOwner}");
1260-
}
1277+
NetworkManager.SpawnManager.ChangeOwnership(childObject, childOwner, true);
1278+
if (EnableDistributeLogging)
1279+
{
1280+
Debug.Log($"[Disconnected][Client-{clientId}][Child of {ownedObject.NetworkObjectId}][NetworkObjectId-{ownedObject.NetworkObjectId} Distributed to Client-{targetOwner}");
12611281
}
12621282
}
12631283
}

0 commit comments

Comments
 (0)