Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions com.unity.netcode.gameobjects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Additional documentation and release notes are available at [Multiplayer Documen

### Changed

- Improve performance of `NetworkObject`. (#3820)
- Rename pass:
- SceneObject -- > SerializedObject
- GetMessageSceneObject --> Serialize
- AddSceneObject --> Deserialize (#3820)

### Deprecated

Expand Down Expand Up @@ -42,7 +47,6 @@ Additional documentation and release notes are available at [Multiplayer Documen
### Changed

- Improve performance of `ParentSyncMessage`. (#3814)
- If the Unity Transport Disconnect Timeout is set to 0 in the Editor, the timeout will be entirely disabeled. (#3810)
- Improve performance of `DestroyObjectMessage`. (#3801)
- Improve performance of `CreateObjectMessage`. (#3800)
- First pass of CoreCLR engine API changes. (#3799)
Expand All @@ -54,7 +58,7 @@ Additional documentation and release notes are available at [Multiplayer Documen

- Ensure `NetworkBehaviour.IsSessionOwner` is correctly set when a new session owner is promoted. (#3817)
- Reset extended ownership flags on `NetworkObject` despawn. (#3817)
- Fixed issue where maxCapacity calculation overflows if a developer sets a very, very high (large) m_DisconnectTimeoutMS in the Editor for Unity Transport. (#3810)
- Fixed an integer overflow that occurred when configuring a large disconnect timeout with Unity Transport. (#3810)
- Fixed issues with the "Client-server quickstart for Netcode for GameObjects" script having static methods and properties. (#3787)
- Fixed issue where a warning message was being logged upon a client disconnecting from a server when the log level is set to developer. (#3786)
- Fixed issue where the server or host would no longer have access to the transport id to client id table when processing a transport level client disconnect event. (#3786)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ internal void ApprovedPlayerSpawn(ulong clientId, uint playerPrefabHash)

var message = new CreateObjectMessage
{
ObjectInfo = ConnectedClients[clientId].PlayerObject.GetMessageSceneObject(clientPair.Key),
ObjectInfo = ConnectedClients[clientId].PlayerObject.Serialize(clientPair.Key),
IncludesSerializedObject = true,
};

Expand Down
158 changes: 82 additions & 76 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2842,87 +2842,91 @@ public NetworkBehaviour GetNetworkBehaviourAtOrderIndex(ushort index)
return ChildNetworkBehaviours[index];
}

internal struct SceneObject
/// <summary>
/// The serialized representation of a NetworkObject.
/// Used for synchronizing clients on NetworkObject spawn.
/// </summary>
internal struct SerializedObject
{
private ushort m_BitField;
public uint Hash;
public ulong NetworkObjectId;
public ulong OwnerClientId;
public ushort OwnershipFlags;

public bool IsPlayerObject
{
get => ByteUtility.GetBit(m_BitField, 0);
set => ByteUtility.SetBit(ref m_BitField, 0, value);
}
public bool HasParent
{
get => ByteUtility.GetBit(m_BitField, 1);
set => ByteUtility.SetBit(ref m_BitField, 1, value);
}
public bool IsSceneObject
{
get => ByteUtility.GetBit(m_BitField, 2);
set => ByteUtility.SetBit(ref m_BitField, 2, value);
}
public bool HasTransform
{
get => ByteUtility.GetBit(m_BitField, 3);
set => ByteUtility.SetBit(ref m_BitField, 3, value);
}
private const ushort k_IsPlayerObject = 0x001;
private const ushort k_HasParent = 0x002;
private const ushort k_IsSceneObject = 0x004;
private const ushort k_HasTransform = 0x008;
private const ushort k_IsLatestParentSet = 0x010;
private const ushort k_WorldPositionStays = 0x020;
private const ushort k_DestroyWithScene = 0x040;
private const ushort k_DontDestroyWithOwner = 0x080;
private const ushort k_HasOwnershipFlags = 0x100;
private const ushort k_SyncObservers = 0x200;
private const ushort k_SpawnWithObservers = 0x400;
private const ushort k_HasInstantiationData = 0x800;

public bool IsLatestParentSet
{
get => ByteUtility.GetBit(m_BitField, 4);
set => ByteUtility.SetBit(ref m_BitField, 4, value);
}
public bool IsPlayerObject;
public bool HasParent;
public bool IsSceneObject;
public bool HasTransform;

public bool WorldPositionStays
{
get => ByteUtility.GetBit(m_BitField, 5);
set => ByteUtility.SetBit(ref m_BitField, 5, value);
}
public bool IsLatestParentSet;

public bool WorldPositionStays;

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

public bool DontDestroyWithOwner
{
get => ByteUtility.GetBit(m_BitField, 7);
set => ByteUtility.SetBit(ref m_BitField, 7, value);
}
public bool DontDestroyWithOwner;

public bool HasOwnershipFlags
{
get => ByteUtility.GetBit(m_BitField, 8);
set => ByteUtility.SetBit(ref m_BitField, 8, value);
}
public bool HasOwnershipFlags;

public bool SyncObservers
{
get => ByteUtility.GetBit(m_BitField, 9);
set => ByteUtility.SetBit(ref m_BitField, 9, value);
}
public bool SyncObservers;

public bool SpawnWithObservers
public bool SpawnWithObservers;

public bool HasInstantiationData;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ushort GetBitsetRepresentation()
{
get => ByteUtility.GetBit(m_BitField, 10);
set => ByteUtility.SetBit(ref m_BitField, 10, value);
ushort bitset = 0;
if (IsPlayerObject) { bitset |= k_IsPlayerObject; };
if (HasParent) { bitset |= k_HasParent; };
if (IsSceneObject) { bitset |= k_IsSceneObject; };
if (HasTransform) { bitset |= k_HasTransform; };
if (IsLatestParentSet) { bitset |= k_IsLatestParentSet; };
if (WorldPositionStays) { bitset |= k_WorldPositionStays; };
if (DestroyWithScene) { bitset |= k_DestroyWithScene; };
if (DontDestroyWithOwner) { bitset |= k_DontDestroyWithOwner; };
if (HasOwnershipFlags) { bitset |= k_HasOwnershipFlags; };
if (SyncObservers) { bitset |= k_SyncObservers; };
if (SpawnWithObservers) { bitset |= k_SpawnWithObservers; };
if (HasInstantiationData) { bitset |= k_HasInstantiationData; };
return bitset;
}

public bool HasInstantiationData
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetStateFromBitset(ushort bitset)
{
get => ByteUtility.GetBit(m_BitField, 11);
set => ByteUtility.SetBit(ref m_BitField, 11, value);
IsPlayerObject = (bitset & k_IsPlayerObject) != 0;
HasParent = (bitset & k_HasParent) != 0;
IsSceneObject = (bitset & k_IsSceneObject) != 0;
HasTransform = (bitset & k_HasTransform) != 0;
IsLatestParentSet = (bitset & k_IsLatestParentSet) != 0;
WorldPositionStays = (bitset & k_WorldPositionStays) != 0;
DestroyWithScene = (bitset & k_DestroyWithScene) != 0;
DontDestroyWithOwner = (bitset & k_DontDestroyWithOwner) != 0;
HasOwnershipFlags = (bitset & k_HasOwnershipFlags) != 0;
SyncObservers = (bitset & k_SyncObservers) != 0;
SpawnWithObservers = (bitset & k_SpawnWithObservers) != 0;
HasInstantiationData = (bitset & k_HasInstantiationData) != 0;
}

// When handling the initial synchronization of NetworkObjects,
Expand Down Expand Up @@ -2961,7 +2965,8 @@ public void Serialize(FastBufferWriter writer)
HasOwnershipFlags = true;
SpawnWithObservers = OwnerObject.SpawnWithObservers;
}
writer.WriteValueSafe(m_BitField);

writer.WriteValueSafe(GetBitsetRepresentation());
writer.WriteValueSafe(Hash);
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
BytePacker.WriteValueBitPacked(writer, OwnerClientId);
Expand Down Expand Up @@ -3041,7 +3046,8 @@ public void Serialize(FastBufferWriter writer)

public void Deserialize(FastBufferReader reader)
{
reader.ReadValueSafe(out m_BitField);
reader.ReadValueSafe(out ushort bitset);
SetStateFromBitset(bitset);
reader.ReadValueSafe(out Hash);
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
Expand Down Expand Up @@ -3169,9 +3175,9 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
}
}

internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager.ServerClientId, bool syncObservers = false)
internal SerializedObject Serialize(ulong targetClientId = NetworkManager.ServerClientId, bool syncObservers = false)
{
var obj = new SceneObject
var obj = new SerializedObject
{
HasParent = transform.parent != null,
WorldPositionStays = m_CachedWorldPositionStays,
Expand Down Expand Up @@ -3225,7 +3231,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
syncScaleLocalSpaceRelative = obj.HasParent;
}

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

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

byte[] instantiationData = null;
if (sceneObject.HasInstantiationData)
if (serializedObject.HasInstantiationData)
{
reader.ReadValueSafe(out instantiationData);
}


// Attempt to create a local NetworkObject
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject, instantiationData);
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(serializedObject, instantiationData);


if (networkObject == null)
{
// Log the error that the NetworkObject failed to construct
if (networkManager.LogLevel <= LogLevel.Normal)
{
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}.");
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {serializedObject.Hash}.");
}

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

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

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

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

if (sceneObject.SyncObservers)
if (serializedObject.SyncObservers)
{
foreach (var observer in sceneObject.Observers)
foreach (var observer in serializedObject.Observers)
{
networkObject.Observers.Add(observer);
}
}

if (networkManager.DistributedAuthorityMode)
{
networkObject.SpawnWithObservers = sceneObject.SpawnWithObservers;
networkObject.SpawnWithObservers = serializedObject.SpawnWithObservers;
}

// If this was not invoked by a message handler, we are in distributed authority mode, and we are spawning with observers or
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void Serialize(FastBufferWriter writer, int targetVersion)
{
sobj.Observers.Add(OwnerClientId);
// In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized.
var sceneObject = sobj.GetMessageSceneObject(OwnerClientId, IsDistributedAuthority);
var sceneObject = sobj.Serialize(OwnerClientId, IsDistributedAuthority);
sceneObject.Serialize(writer);
++sceneObjectCount;
}
Expand Down Expand Up @@ -342,9 +342,9 @@ public void Handle(ref NetworkContext context)
// to create a list to hold the data. This is a breach of convention for performance reasons.
for (ushort i = 0; i < sceneObjectCount; i++)
{
var sceneObject = new NetworkObject.SceneObject();
var sceneObject = new NetworkObject.SerializedObject();
sceneObject.Deserialize(m_ReceivedSceneObjectData);
NetworkObject.AddSceneObject(sceneObject, m_ReceivedSceneObjectData, networkManager);
NetworkObject.Deserialize(sceneObject, m_ReceivedSceneObjectData, networkManager);
}

if (networkManager.AutoSpawnPlayerPrefabClientSide)
Expand Down
Loading