Skip to content

Commit 6de550b

Browse files
chore: merge develop 2 0 0 updates with SessionOwner permissions (#3176)
* fix: clamp spawntimeout to recommended range (#3174) * update Clamping spawntimeout * update improving parenting failed message when either the child or parent NetworkObject is not spawnd. * update Update the local SceneEventData.SceneEventType on the authority side for SceneLoadComplete. * style removing whitespaces * feat: Add a SessionOwner ObjectStatus and allow InScenePlaced network objects to be distributed (#3175) * Initial pass on SessionOwner ownership flag * feat: Add SessionOwner OwnershipStatus flag * update removing additional session owner accessor. * Add OwnershipPermissions tests * fix client connect * Revert "fix client connect" This reverts commit 3c3b354. * update object distribution for in-scene placed NetworkObjects needs to use the InScenePlacedSourceGlobalObjectIdHash when building an object distribution list. * Add changelog * Remove unnecessary change * Remove Settings.json * Reword CHANGELOG * fix DAHost should not distribute session owner permission NetworkObjects. When client is promoted to session owner, for now newly promoted client takes ownership of NetworkObjects that have the SessionOwner permission set. Only prevent non-session owners from taking ownership of a NetworkObject with the SessionOwner permission set. * test fix Avoid the RemoveOwnership client-server only method. * style Visual studio code cleanup likes to sort by alpha... fixing for our standards. * update Adding check for session owner trying to change ownership to a non-session owner client. * test Adding an additional validation that a non-session owner cannot change ownership and that a session owner cannot change ownership to a non-session owner when the NetworkObject in question has the SessionOwner permissions set. --------- Co-authored-by: NoelStephensUnity <[email protected]> --------- Co-authored-by: Emma <[email protected]>
1 parent 658ef01 commit 6de550b

File tree

10 files changed

+231
-80
lines changed

10 files changed

+231
-80
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1111

1212
### Added
1313

14+
- Added `NetworkObject.OwnershipStatus.SessionOwner` to allow Network Objects to be distributable and only owned by the Session Owner. This flag will override all other `OwnershipStatus` flags. (#3175)
1415
- Added `UnityTransport.GetEndpoint` method to provide a way to obtain `NetworkEndpoint` information of a connection via client identifier. (#3130)
1516
- Added `NetworkTransport.OnEarlyUpdate` and `NetworkTransport.OnPostLateUpdate` methods to provide more control over handling transport related events at the start and end of each frame. (#3113)
1617

@@ -31,6 +32,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
3132

3233
### Changed
3334

35+
- In-scene placed `NetworkObject`s have been made distributable when balancing object distribution after a connection event. (#3175)
3436
- Optimised `NetworkVariable` and `NetworkTransform` related packets when in Distributed Authority mode.
3537
- The Debug Simulator section of the Unity Transport component was removed. This section was not functional anymore and users are now recommended to use the more featureful [Network Simulator](https://docs-multiplayer.unity3d.com/tools/current/tools-network-simulator/) tool from the Multiplayer Tools package instead. (#3121)
3638

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

+4
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ public override void OnInspectorGUI()
115115
EditorGUI.BeginChangeCheck();
116116
serializedObject.UpdateIfRequiredOrScript();
117117
DrawPropertiesExcluding(serializedObject, k_HiddenFields);
118+
if (m_NetworkObject.IsOwnershipSessionOwner)
119+
{
120+
m_NetworkObject.Ownership = NetworkObject.OwnershipStatus.SessionOwner;
121+
}
118122
serializedObject.ApplyModifiedProperties();
119123
EditorGUI.EndChangeCheck();
120124

com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs

+25
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ namespace Unity.Netcode
1313
[Serializable]
1414
public class NetworkConfig
1515
{
16+
// Clamp spawn time outs to prevent dropping messages during scene events
17+
// Note: The legacy versions of NGO defaulted to 1s which was too low. As
18+
// well, the SpawnTimeOut is now being clamped to within this recommended
19+
// range both via UI and when NetworkManager is validated.
20+
internal const float MinSpawnTimeout = 10.0f;
21+
// Clamp spawn time outs to no more than 1 hour (really that is a bit high)
22+
internal const float MaxSpawnTimeout = 3600.0f;
23+
1624
/// <summary>
1725
/// The protocol version. Different versions doesn't talk to each other.
1826
/// </summary>
@@ -132,6 +140,8 @@ public class NetworkConfig
132140
/// The amount of time a message will be held (deferred) if the destination NetworkObject needed to process the message doesn't exist yet. If the NetworkObject is not spawned within this time period, all deferred messages for that NetworkObject will be dropped.
133141
/// </summary>
134142
[Tooltip("The amount of time a message will be held (deferred) if the destination NetworkObject needed to process the message doesn't exist yet. If the NetworkObject is not spawned within this time period, all deferred messages for that NetworkObject will be dropped.")]
143+
144+
[Range(MinSpawnTimeout, MaxSpawnTimeout)]
135145
public float SpawnTimeout = 10f;
136146

137147
/// <summary>
@@ -176,6 +186,21 @@ public class NetworkConfig
176186
[Tooltip("Enable (default) if you want to profile network messages with development builds and defaults to being disabled in release builds. When disabled, network messaging profiling will be disabled in development builds.")]
177187
public bool NetworkProfilingMetrics = true;
178188

189+
/// <summary>
190+
/// Invoked by <see cref="NetworkManager"/> when it is validated.
191+
/// </summary>
192+
/// <remarks>
193+
/// Used to check for potential legacy values that have already been serialized and/or
194+
/// runtime modifications to a property outside of the recommended range.
195+
/// For each property checked below, provide a brief description of the reason.
196+
/// </remarks>
197+
internal void OnValidate()
198+
{
199+
// Legacy NGO versions defaulted this value to 1 second that has since been determiend
200+
// any range less than 10 seconds can lead to dropped messages during scene events.
201+
SpawnTimeout = Mathf.Clamp(SpawnTimeout, MinSpawnTimeout, MaxSpawnTimeout);
202+
}
203+
179204
/// <summary>
180205
/// Returns a base64 encoded version of the configuration
181206
/// </summary>

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ internal void OnClientDisconnectFromServer(ulong clientId)
12121212
{
12131213
// Only NetworkObjects that have the OwnershipStatus.Distributable flag set and no parent
12141214
// (ownership is transferred to all children) will have their ownership redistributed.
1215-
if (ownedObject.IsOwnershipDistributable && ownedObject.GetCachedParent() == null)
1215+
if (ownedObject.IsOwnershipDistributable && ownedObject.GetCachedParent() == null && !ownedObject.IsOwnershipSessionOwner)
12161216
{
12171217
if (ownedObject.IsOwnershipLocked)
12181218
{
@@ -1249,6 +1249,11 @@ internal void OnClientDisconnectFromServer(ulong clientId)
12491249
childObject.SetOwnershipLock(false);
12501250
}
12511251

1252+
// Ignore session owner marked objects
1253+
if (childObject.IsOwnershipSessionOwner)
1254+
{
1255+
continue;
1256+
}
12521257
NetworkManager.SpawnManager.ChangeOwnership(childObject, targetOwner, true);
12531258
if (EnableDistributeLogging)
12541259
{

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -225,11 +225,7 @@ internal void SetSessionOwner(ulong sessionOwner)
225225
foreach (var networkObjectEntry in SpawnManager.SpawnedObjects)
226226
{
227227
var networkObject = networkObjectEntry.Value;
228-
if (networkObject.IsSceneObject == null || !networkObject.IsSceneObject.Value)
229-
{
230-
continue;
231-
}
232-
if (networkObject.OwnerClientId != LocalClientId)
228+
if (networkObject.IsOwnershipSessionOwner && LocalClient.IsSessionOwner)
233229
{
234230
SpawnManager.ChangeOwnership(networkObject, LocalClientId, true);
235231
}
@@ -955,6 +951,9 @@ internal void OnValidate()
955951
return; // May occur when the component is added
956952
}
957953

954+
// Do a validation pass on NetworkConfig properties
955+
NetworkConfig.OnValidate();
956+
958957
if (GetComponentInChildren<NetworkObject>() != null)
959958
{
960959
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)

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

+54-14
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,13 @@ public void DeferDespawn(int tickOffset, bool destroy = true)
439439
/// </remarks>
440440
public bool IsOwnershipDistributable => Ownership.HasFlag(OwnershipStatus.Distributable);
441441

442+
/// <summary>
443+
/// When true, the <see cref="NetworkObject"/> can only be owned by the current Session Owner.
444+
/// To set <see cref="OwnershipStatus.SessionOwner"/> during runtime, use <see cref="ChangeOwnership(ulong)"/> to ensure the session owner owns the object.
445+
/// Once the session owner owns the object, then use <see cref="SetOwnershipStatus(OwnershipStatus, bool, OwnershipLockActions)"/>.
446+
/// </summary>
447+
public bool IsOwnershipSessionOwner => Ownership.HasFlag(OwnershipStatus.SessionOwner);
448+
442449
/// <summary>
443450
/// Returns true if the <see cref="NetworkObject"/> is has ownership locked.
444451
/// When locked, the <see cref="NetworkObject"/> cannot be redistributed nor can it be transferred by another client.
@@ -481,7 +488,8 @@ public void DeferDespawn(int tickOffset, bool destroy = true)
481488
/// <see cref="None"/>: If nothing is set, then ownership is considered "static" and cannot be redistributed, requested, or transferred (i.e. a Player would have this).
482489
/// <see cref="Distributable"/>: When set, this instance will be automatically redistributed when a client joins (if not locked or no request is pending) or leaves.
483490
/// <see cref="Transferable"/>: When set, a non-owner can obtain ownership immediately (without requesting and as long as it is not locked).
484-
/// <see cref="RequestRequired"/>: When set, When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred).
491+
/// <see cref="RequestRequired"/>: When set, a non-owner must request ownership from the owner (will always get locked once ownership is transferred).
492+
/// <see cref="SessionOwner"/>: When set, only the current session owner may have ownership over this object.
485493
/// </summary>
486494
// Ranges from 1 to 8 bits
487495
[Flags]
@@ -491,6 +499,7 @@ public enum OwnershipStatus
491499
Distributable = 1 << 0,
492500
Transferable = 1 << 1,
493501
RequestRequired = 1 << 2,
502+
SessionOwner = 1 << 3,
494503
}
495504

496505
/// <summary>
@@ -549,7 +558,7 @@ public bool SetOwnershipLock(bool lockOwnership = true)
549558
}
550559

551560
// If we don't have the Transferable flag set and it is not a player object, then it is the same as having a static lock on ownership
552-
if (!IsOwnershipTransferable && !IsPlayerObject)
561+
if (!(IsOwnershipTransferable || IsPlayerObject) || IsOwnershipSessionOwner)
553562
{
554563
NetworkLog.LogWarning($"Trying to add or remove ownership lock on [{name}] which does not have the {nameof(OwnershipStatus.Transferable)} flag set!");
555564
return false;
@@ -582,13 +591,15 @@ public bool SetOwnershipLock(bool lockOwnership = true)
582591
/// <see cref="RequestRequired"/>: The <see cref="NetworkObject"/> requires an ownership request via <see cref="RequestOwnership"/>.
583592
/// <see cref="RequestInProgress"/>: The <see cref="NetworkObject"/> is already processing an ownership request and ownership cannot be acquired at this time.
584593
/// <see cref="NotTransferrable"/>: The <see cref="NetworkObject"/> does not have the <see cref="OwnershipStatus.Transferable"/> flag set and ownership cannot be acquired.
594+
/// <see cref="SessionOwnerOnly"/>: The <see cref="NetworkObject"/> has the <see cref="OwnershipStatus.SessionOwner"/> flag set and ownership cannot be acquired.
585595
/// </summary>
586596
public enum OwnershipPermissionsFailureStatus
587597
{
588598
Locked,
589599
RequestRequired,
590600
RequestInProgress,
591-
NotTransferrable
601+
NotTransferrable,
602+
SessionOwnerOnly
592603
}
593604

594605
/// <summary>
@@ -610,6 +621,7 @@ public enum OwnershipPermissionsFailureStatus
610621
/// <see cref="RequestRequiredNotSet"/>: The <see cref="OwnershipStatus.RequestRequired"/> flag is not set on this <see cref="NetworkObject"/>
611622
/// <see cref="Locked"/>: The current owner has locked ownership which means requests are not available at this time.
612623
/// <see cref="RequestInProgress"/>: There is already a known request in progress. You can scan for ownership changes and try upon
624+
/// <see cref="SessionOwnerOnly"/>: This object is marked as SessionOwnerOnly and therefore cannot be requested
613625
/// a change in ownership or just try again after a specific period of time or no longer attempt to request ownership.
614626
/// </summary>
615627
public enum OwnershipRequestStatus
@@ -619,6 +631,7 @@ public enum OwnershipRequestStatus
619631
RequestRequiredNotSet,
620632
Locked,
621633
RequestInProgress,
634+
SessionOwnerOnly,
622635
}
623636

624637
/// <summary>
@@ -631,6 +644,7 @@ public enum OwnershipRequestStatus
631644
/// <see cref="OwnershipRequestStatus.RequestRequiredNotSet"/>: The <see cref="OwnershipStatus.RequestRequired"/> flag is not set on this <see cref="NetworkObject"/>
632645
/// <see cref="OwnershipRequestStatus.Locked"/>: The current owner has locked ownership which means requests are not available at this time.
633646
/// <see cref="OwnershipRequestStatus.RequestInProgress"/>: There is already a known request in progress. You can scan for ownership changes and try upon
647+
/// <see cref="OwnershipRequestStatus.SessionOwnerOnly"/>: This object can only belong the the session owner and so cannot be requested
634648
/// a change in ownership or just try again after a specific period of time or no longer attempt to request ownership.
635649
/// </remarks>
636650
/// <returns><see cref="OwnershipRequestStatus"/></returns>
@@ -660,6 +674,12 @@ public OwnershipRequestStatus RequestOwnership()
660674
return OwnershipRequestStatus.RequestInProgress;
661675
}
662676

677+
// Exit early if it has the SessionOwner flag
678+
if (IsOwnershipSessionOwner)
679+
{
680+
return OwnershipRequestStatus.SessionOwnerOnly;
681+
}
682+
663683
// Otherwise, send the request ownership message
664684
var changeOwnership = new ChangeOwnershipMessage
665685
{
@@ -716,7 +736,7 @@ internal void OwnershipRequest(ulong clientRequestingOwnership)
716736
{
717737
response = OwnershipRequestResponseStatus.RequestInProgress;
718738
}
719-
else if (!IsOwnershipRequestRequired && !IsOwnershipTransferable)
739+
else if (!(IsOwnershipRequestRequired || IsOwnershipTransferable) || IsOwnershipSessionOwner)
720740
{
721741
response = OwnershipRequestResponseStatus.CannotRequest;
722742
}
@@ -836,6 +856,12 @@ public enum OwnershipLockActions
836856
/// </remarks>
837857
public bool SetOwnershipStatus(OwnershipStatus status, bool clearAndSet = false, OwnershipLockActions lockAction = OwnershipLockActions.None)
838858
{
859+
if (status.HasFlag(OwnershipStatus.SessionOwner) && !NetworkManager.LocalClient.IsSessionOwner)
860+
{
861+
NetworkLog.LogWarning("Only the session owner is allowed to set the ownership status to session owner only.");
862+
return false;
863+
}
864+
839865
// If it already has the flag do nothing
840866
if (!clearAndSet && Ownership.HasFlag(status))
841867
{
@@ -847,13 +873,25 @@ public bool SetOwnershipStatus(OwnershipStatus status, bool clearAndSet = false,
847873
Ownership = OwnershipStatus.None;
848874
}
849875

850-
// Faster to just OR a None status than to check
851-
// if it is !None before "OR'ing".
852-
Ownership |= status;
853-
854-
if (lockAction != OwnershipLockActions.None)
876+
if (status.HasFlag(OwnershipStatus.SessionOwner))
855877
{
856-
SetOwnershipLock(lockAction == OwnershipLockActions.SetAndLock);
878+
Ownership = OwnershipStatus.SessionOwner;
879+
}
880+
else if (Ownership.HasFlag(OwnershipStatus.SessionOwner))
881+
{
882+
NetworkLog.LogWarning("No other ownership statuses may be set while SessionOwner is set.");
883+
return false;
884+
}
885+
else
886+
{
887+
// Faster to just OR a None status than to check
888+
// if it is !None before "OR'ing".
889+
Ownership |= status;
890+
891+
if (lockAction != OwnershipLockActions.None)
892+
{
893+
SetOwnershipLock(lockAction == OwnershipLockActions.SetAndLock);
894+
}
857895
}
858896

859897
SendOwnershipStatusUpdate();
@@ -1629,7 +1667,7 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla
16291667
// DANGO-TODO: Review over don't destroy with owner being set but DistributeOwnership not being set
16301668
if (NetworkManager.LogLevel == LogLevel.Developer)
16311669
{
1632-
NetworkLog.LogWarning("DANGO-TODO: Review over don't destroy with owner being set but DistributeOwnership not being set. For now, if the NetworkObject does not destroy with the owner it will automatically set DistributeOwnership.");
1670+
NetworkLog.LogWarning("DANGO-TODO: Review over don't destroy with owner being set but DistributeOwnership not being set. For now, if the NetworkObject does not destroy with the owner it will set ownership to SessionOwner.");
16331671
}
16341672
}
16351673
}
@@ -2007,12 +2045,14 @@ public bool TrySetParent(NetworkObject parent, bool worldPositionStays = true)
20072045

20082046
internal bool InternalTrySetParent(NetworkObject parent, bool worldPositionStays = true)
20092047
{
2010-
if (parent != null && (IsSpawned ^ parent.IsSpawned))
2048+
if (parent != null && (IsSpawned ^ parent.IsSpawned) && NetworkManager != null && !NetworkManager.ShutdownInProgress)
20112049
{
2012-
if (NetworkManager != null && !NetworkManager.ShutdownInProgress)
2050+
if (NetworkManager.LogLevel <= LogLevel.Developer)
20132051
{
2014-
return false;
2052+
var nameOfNotSpawnedObject = IsSpawned ? $" the parent ({parent.name})" : $"the child ({name})";
2053+
NetworkLog.LogWarning($"Parenting failed because {nameOfNotSpawnedObject} is not spawned!");
20152054
}
2055+
return false;
20162056
}
20172057

20182058
m_CachedWorldPositionStays = worldPositionStays;

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -1903,10 +1903,12 @@ private void OnSessionOwnerLoadedScene(uint sceneEventId, Scene scene)
19031903
SendSceneEventData(sceneEventData.SceneEventId, NetworkManager.ConnectedClientsIds.Where(c => c != sessionOwner).ToArray());
19041904

19051905
m_IsSceneEventActive = false;
1906+
1907+
sceneEventData.SceneEventType = SceneEventType.LoadComplete;
19061908
//First, notify local server that the scene was loaded
19071909
OnSceneEvent?.Invoke(new SceneEvent()
19081910
{
1909-
SceneEventType = SceneEventType.LoadComplete,
1911+
SceneEventType = sceneEventData.SceneEventType,
19101912
LoadSceneMode = sceneEventData.LoadSceneMode,
19111913
SceneName = SceneNameFromHash(sceneEventData.SceneHash),
19121914
ClientId = NetworkManager.LocalClientId,

0 commit comments

Comments
 (0)