Skip to content

Commit 8f99d40

Browse files
chore: optimize network object (#3820)
* chore: NetworkObject optimization * chore: rename pass SceneObject -- > SerializedObject GetMessageSceneObject --> Serialize AddSceneObject --> Deserialize * Address changeog feedback from #3810 PR * Add changelog entry + address PR review feedback * Remove changelog trailing spaces * Fix whitespaces + remove tab spaces
1 parent 8d88f19 commit 8f99d40

File tree

8 files changed

+153
-146
lines changed

8 files changed

+153
-146
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1313

1414
### Changed
1515

16+
- Improve performance of `NetworkObject`. (#3820)
1617
- If the Unity Transport Disconnect Timeout is set to 0 in the Editor, the timeout will be entirely disabled. (#3810)
1718

1819
### Deprecated
@@ -23,7 +24,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
2324

2425
### Fixed
2526

26-
- Fixed issue where maxCapacity calculation overflows if a developer sets a very, very high (large) m_DisconnectTimeoutMS in the Editor for Unity Transport. (#3810)
27+
- Fixed an integer overflow that occurred when configuring a large disconnect timeout with Unity Transport. (#3810)
2728

2829
### Security
2930

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash)
11191119

11201120
var message = new CreateObjectMessage
11211121
{
1122-
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key),
1122+
ObjectInfo = ConnectedClients[clientId].PlayerObject.Serialize(clientPair.Key),
11231123
IncludesSerializedObject = true,
11241124
};
11251125

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

Lines changed: 82 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2842,87 +2842,91 @@ public NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index)
28422842
return ChildNetworkBehaviours[index];
28432843
}
28442844

2845-
internal struct SceneObject
2845+
/// <summary>
2846+
/// The serialized representation of a NetworkObject.
2847+
/// Used for synchronizing clients on NetworkObject spawn.
2848+
/// </summary>
2849+
internal struct SerializedObject
28462850
{
2847-
private ushort m_BitField;
28482851
public uint Hash;
28492852
public ulong NetworkObjectId;
28502853
public ulong OwnerClientId;
28512854
public ushort OwnershipFlags;
28522855

2853-
public bool IsPlayerObject
2854-
{
2855-
get => ByteUtility.GetBit(m_BitField, 0);
2856-
set => ByteUtility.SetBit(ref m_BitField, 0, value);
2857-
}
2858-
public bool HasParent
2859-
{
2860-
get => ByteUtility.GetBit(m_BitField, 1);
2861-
set => ByteUtility.SetBit(ref m_BitField, 1, value);
2862-
}
2863-
public bool IsSceneObject
2864-
{
2865-
get => ByteUtility.GetBit(m_BitField, 2);
2866-
set => ByteUtility.SetBit(ref m_BitField, 2, value);
2867-
}
2868-
public bool HasTransform
2869-
{
2870-
get => ByteUtility.GetBit(m_BitField, 3);
2871-
set => ByteUtility.SetBit(ref m_BitField, 3, value);
2872-
}
2856+
private const ushort k_IsPlayerObject = 0x001;
2857+
private const ushort k_HasParent = 0x002;
2858+
private const ushort k_IsSceneObject = 0x004;
2859+
private const ushort k_HasTransform = 0x008;
2860+
private const ushort k_IsLatestParentSet = 0x010;
2861+
private const ushort k_WorldPositionStays = 0x020;
2862+
private const ushort k_DestroyWithScene = 0x040;
2863+
private const ushort k_DontDestroyWithOwner = 0x080;
2864+
private const ushort k_HasOwnershipFlags = 0x100;
2865+
private const ushort k_SyncObservers = 0x200;
2866+
private const ushort k_SpawnWithObservers = 0x400;
2867+
private const ushort k_HasInstantiationData = 0x800;
28732868

2874-
public bool IsLatestParentSet
2875-
{
2876-
get => ByteUtility.GetBit(m_BitField, 4);
2877-
set => ByteUtility.SetBit(ref m_BitField, 4, value);
2878-
}
2869+
public bool IsPlayerObject;
2870+
public bool HasParent;
2871+
public bool IsSceneObject;
2872+
public bool HasTransform;
28792873

2880-
public bool WorldPositionStays
2881-
{
2882-
get => ByteUtility.GetBit(m_BitField, 5);
2883-
set => ByteUtility.SetBit(ref m_BitField, 5, value);
2884-
}
2874+
public bool IsLatestParentSet;
2875+
2876+
public bool WorldPositionStays;
28852877

28862878
/// <summary>
28872879
/// Even though the server sends notifications for NetworkObjects that get
28882880
/// destroyed when a scene is unloaded, we want to synchronize this so
28892881
/// the client side can use it as part of a filter for automatically migrating
28902882
/// to the current active scene when its scene is unloaded. (only for dynamically spawned)
28912883
/// </summary>
2892-
public bool DestroyWithScene
2893-
{
2894-
get => ByteUtility.GetBit(m_BitField, 6);
2895-
set => ByteUtility.SetBit(ref m_BitField, 6, value);
2896-
}
2884+
public bool DestroyWithScene;
28972885

2898-
public bool DontDestroyWithOwner
2899-
{
2900-
get => ByteUtility.GetBit(m_BitField, 7);
2901-
set => ByteUtility.SetBit(ref m_BitField, 7, value);
2902-
}
2886+
public bool DontDestroyWithOwner;
29032887

2904-
public bool HasOwnershipFlags
2905-
{
2906-
get => ByteUtility.GetBit(m_BitField, 8);
2907-
set => ByteUtility.SetBit(ref m_BitField, 8, value);
2908-
}
2888+
public bool HasOwnershipFlags;
29092889

2910-
public bool SyncObservers
2911-
{
2912-
get => ByteUtility.GetBit(m_BitField, 9);
2913-
set => ByteUtility.SetBit(ref m_BitField, 9, value);
2914-
}
2890+
public bool SyncObservers;
29152891

2916-
public bool SpawnWithObservers
2892+
public bool SpawnWithObservers;
2893+
2894+
public bool HasInstantiationData;
2895+
2896+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2897+
internal ushort GetBitsetRepresentation()
29172898
{
2918-
get => ByteUtility.GetBit(m_BitField, 10);
2919-
set => ByteUtility.SetBit(ref m_BitField, 10, value);
2899+
ushort bitset = 0;
2900+
if (IsPlayerObject) { bitset |= k_IsPlayerObject; };
2901+
if (HasParent) { bitset |= k_HasParent; };
2902+
if (IsSceneObject) { bitset |= k_IsSceneObject; };
2903+
if (HasTransform) { bitset |= k_HasTransform; };
2904+
if (IsLatestParentSet) { bitset |= k_IsLatestParentSet; };
2905+
if (WorldPositionStays) { bitset |= k_WorldPositionStays; };
2906+
if (DestroyWithScene) { bitset |= k_DestroyWithScene; };
2907+
if (DontDestroyWithOwner) { bitset |= k_DontDestroyWithOwner; };
2908+
if (HasOwnershipFlags) { bitset |= k_HasOwnershipFlags; };
2909+
if (SyncObservers) { bitset |= k_SyncObservers; };
2910+
if (SpawnWithObservers) { bitset |= k_SpawnWithObservers; };
2911+
if (HasInstantiationData) { bitset |= k_HasInstantiationData; };
2912+
return bitset;
29202913
}
29212914

2922-
public bool HasInstantiationData
2915+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2916+
internal void SetStateFromBitset(ushort bitset)
29232917
{
2924-
get => ByteUtility.GetBit(m_BitField, 11);
2925-
set => ByteUtility.SetBit(ref m_BitField, 11, value);
2918+
IsPlayerObject = (bitset & k_IsPlayerObject) != 0;
2919+
HasParent = (bitset & k_HasParent) != 0;
2920+
IsSceneObject = (bitset & k_IsSceneObject) != 0;
2921+
HasTransform = (bitset & k_HasTransform) != 0;
2922+
IsLatestParentSet = (bitset & k_IsLatestParentSet) != 0;
2923+
WorldPositionStays = (bitset & k_WorldPositionStays) != 0;
2924+
DestroyWithScene = (bitset & k_DestroyWithScene) != 0;
2925+
DontDestroyWithOwner = (bitset & k_DontDestroyWithOwner) != 0;
2926+
HasOwnershipFlags = (bitset & k_HasOwnershipFlags) != 0;
2927+
SyncObservers = (bitset & k_SyncObservers) != 0;
2928+
SpawnWithObservers = (bitset & k_SpawnWithObservers) != 0;
2929+
HasInstantiationData = (bitset & k_HasInstantiationData) != 0;
29262930
}
29272931

29282932
// When handling the initial synchronization of NetworkObjects,
@@ -2961,7 +2965,8 @@ public void Serialize(FastBufferWriter writer)
29612965
HasOwnershipFlags = true;
29622966
SpawnWithObservers = OwnerObject.SpawnWithObservers;
29632967
}
2964-
writer.WriteValueSafe(m_BitField);
2968+
2969+
writer.WriteValueSafe(GetBitsetRepresentation());
29652970
writer.WriteValueSafe(Hash);
29662971
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
29672972
BytePacker.WriteValueBitPacked(writer, OwnerClientId);
@@ -3041,7 +3046,8 @@ public void Serialize(FastBufferWriter writer)
30413046

30423047
public void Deserialize(FastBufferReader reader)
30433048
{
3044-
reader.ReadValueSafe(out m_BitField);
3049+
reader.ReadValueSafe(out ushort bitset);
3050+
SetStateFromBitset(bitset);
30453051
reader.ReadValueSafe(out Hash);
30463052
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
30473053
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
@@ -3169,9 +3175,9 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
31693175
}
31703176
}
31713177

3172-
internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager.ServerClientId, bool syncObservers = false)
3178+
internal SerializedObject Serialize(ulong targetClientId = NetworkManager.ServerClientId, bool syncObservers = false)
31733179
{
3174-
var obj = new SceneObject
3180+
var obj = new SerializedObject
31753181
{
31763182
HasParent = transform.parent != null,
31773183
WorldPositionStays = m_CachedWorldPositionStays,
@@ -3225,7 +3231,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
32253231
syncScaleLocalSpaceRelative = obj.HasParent;
32263232
}
32273233

3228-
obj.Transform = new SceneObject.TransformData
3234+
obj.Transform = new SerializedObject.TransformData
32293235
{
32303236
// If we are parented and we have the m_CachedWorldPositionStays disabled, then use local space
32313237
// values as opposed world space values.
@@ -3244,35 +3250,35 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
32443250
}
32453251

32463252
/// <summary>
3247-
/// Used to deserialize a serialized scene object which occurs
3253+
/// Used to deserialize a serialized <see cref="SerializedObject"/> which occurs
32483254
/// when the client is approved or during a scene transition
32493255
/// </summary>
3250-
/// <param name="sceneObject">Deserialized scene object data</param>
3256+
/// <param name="serializedObject">Deserialized scene object data</param>
32513257
/// <param name="reader">FastBufferReader for the NetworkVariable data</param>
32523258
/// <param name="networkManager">NetworkManager instance</param>
32533259
/// <param name="invokedByMessage">will be true if invoked by CreateObjectMessage</param>
32543260
/// <returns>The deserialized NetworkObject or null if deserialization failed</returns>
3255-
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager, bool invokedByMessage = false)
3261+
internal static NetworkObject Deserialize(in SerializedObject serializedObject, FastBufferReader reader, NetworkManager networkManager, bool invokedByMessage = false)
32563262
{
3257-
var endOfSynchronizationData = reader.Position + sceneObject.SynchronizationDataSize;
3263+
var endOfSynchronizationData = reader.Position + serializedObject.SynchronizationDataSize;
32583264

32593265
byte[] instantiationData = null;
3260-
if (sceneObject.HasInstantiationData)
3266+
if (serializedObject.HasInstantiationData)
32613267
{
32623268
reader.ReadValueSafe(out instantiationData);
32633269
}
32643270

32653271

32663272
// Attempt to create a local NetworkObject
3267-
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject, instantiationData);
3273+
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(serializedObject, instantiationData);
32683274

32693275

32703276
if (networkObject == null)
32713277
{
32723278
// Log the error that the NetworkObject failed to construct
32733279
if (networkManager.LogLevel <= LogLevel.Normal)
32743280
{
3275-
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}.");
3281+
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {serializedObject.Hash}.");
32763282
}
32773283

32783284
try
@@ -3293,7 +3299,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
32933299

32943300
// This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning
32953301
// in order to be able to determine which NetworkVariables the client will be allowed to read.
3296-
networkObject.OwnerClientId = sceneObject.OwnerClientId;
3302+
networkObject.OwnerClientId = serializedObject.OwnerClientId;
32973303

32983304
// Special Case: Invoke NetworkBehaviour.OnPreSpawn methods here before SynchronizeNetworkBehaviours
32993305
networkObject.InvokeBehaviourNetworkPreSpawn();
@@ -3321,7 +3327,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
33213327
// being told we do not have a parent, then we want to clear the latest parent so it is not automatically
33223328
// "re-parented" to the original parent. This can happen if not unloading the scene and the parenting of
33233329
// the in-scene placed Networkobject changes several times over different sessions.
3324-
if (sceneObject.IsSceneObject && !sceneObject.HasParent && networkObject.m_LatestParent.HasValue)
3330+
if (serializedObject.IsSceneObject && !serializedObject.HasParent && networkObject.m_LatestParent.HasValue)
33253331
{
33263332
networkObject.m_LatestParent = null;
33273333
}
@@ -3334,19 +3340,19 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
33343340

33353341
// Invoke the non-authority local spawn method
33363342
// (It also invokes post spawn and handles processing derferred messages)
3337-
networkManager.SpawnManager.NonAuthorityLocalSpawn(networkObject, sceneObject, sceneObject.DestroyWithScene);
3343+
networkManager.SpawnManager.NonAuthorityLocalSpawn(networkObject, serializedObject, serializedObject.DestroyWithScene);
33383344

3339-
if (sceneObject.SyncObservers)
3345+
if (serializedObject.SyncObservers)
33403346
{
3341-
foreach (var observer in sceneObject.Observers)
3347+
foreach (var observer in serializedObject.Observers)
33423348
{
33433349
networkObject.Observers.Add(observer);
33443350
}
33453351
}
33463352

33473353
if (networkManager.DistributedAuthorityMode)
33483354
{
3349-
networkObject.SpawnWithObservers = sceneObject.SpawnWithObservers;
3355+
networkObject.SpawnWithObservers = serializedObject.SpawnWithObservers;
33503356
}
33513357

33523358
// If this was not invoked by a message handler, we are in distributed authority mode, and we are spawning with observers or

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ public void Serialize(FastBufferWriter writer, int targetVersion)
160160
{
161161
sobj.Observers.Add(OwnerClientId);
162162
// In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized.
163-
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId, IsDistributedAuthority);
164-
sceneObject.Serialize(writer);
163+
var serializedObject = sobj.Serialize(OwnerClientId, IsDistributedAuthority);
164+
serializedObject.Serialize(writer);
165165
++sceneObjectCount;
166166
}
167167
}
@@ -342,9 +342,9 @@ public void Handle(ref NetworkContext context)
342342
// to create a list to hold the data. This is a breach of convention for performance reasons.
343343
for (ushort i = 0; i < sceneObjectCount; i++)
344344
{
345-
var sceneObject = new NetworkObject.SceneObject();
346-
sceneObject.Deserialize(m_ReceivedSceneObjectData);
347-
NetworkObject.AddSceneObject(sceneObject, m_ReceivedSceneObjectData, networkManager);
345+
var serializedObject = new NetworkObject.SerializedObject();
346+
serializedObject.Deserialize(m_ReceivedSceneObjectData);
347+
NetworkObject.Deserialize(serializedObject, m_ReceivedSceneObjectData, networkManager);
348348
}
349349

350350
if (networkManager.AutoSpawnPlayerPrefabClientSide)

0 commit comments

Comments
 (0)