Skip to content

Commit 0d51492

Browse files
committed
Rework serialization to work with distributed authority
1 parent a34ec5a commit 0d51492

File tree

7 files changed

+181
-148
lines changed

7 files changed

+181
-148
lines changed

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

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,6 +2876,7 @@ public struct TransformData : INetworkSerializeByMemcpy
28762876

28772877
public int NetworkSceneHandle;
28782878

2879+
internal int SynchronizationDataSize;
28792880

28802881
public void Serialize(FastBufferWriter writer)
28812882
{
@@ -2937,15 +2938,29 @@ public void Serialize(FastBufferWriter writer)
29372938
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
29382939
}
29392940

2941+
// write placeholder for serialized data size.
2942+
// Can't be bitpacked because we don't know the value until we calculate it later
2943+
var positionBeforeSynchronizing = writer.Position;
2944+
writer.WriteValueSafe(0);
2945+
var sizeToSkipCalculationPosition = writer.Position;
2946+
29402947
if (HasInstantiationData)
29412948
{
2942-
BytePacker.WriteValuePacked(writer, OwnerObject.InstantiationData.Length);
2943-
writer.WriteBytesSafe(OwnerObject.InstantiationData);
2949+
writer.WriteValueSafe(OwnerObject.InstantiationData);
29442950
}
29452951

29462952
// Synchronize NetworkVariables and NetworkBehaviours
29472953
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
29482954
OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
2955+
2956+
var currentPosition = writer.Position;
2957+
// Write the total number of bytes written for synchronization data.
2958+
writer.Seek(positionBeforeSynchronizing);
2959+
// We want the size of everything after our size to skip calculation position
2960+
var size = currentPosition - sizeToSkipCalculationPosition;
2961+
writer.WriteValueSafe(size);
2962+
// seek back to the head of the writer.
2963+
writer.Seek(currentPosition);
29492964
}
29502965

29512966
public void Deserialize(FastBufferReader reader)
@@ -3001,6 +3016,10 @@ public void Deserialize(FastBufferReader reader)
30013016
// The NetworkSceneHandle is the server-side relative
30023017
// scene handle that the NetworkObject resides in.
30033018
reader.ReadValue(out NetworkSceneHandle);
3019+
3020+
// Read the size of the remaining synchronization data
3021+
// This data will be read in AddSceneObject()
3022+
reader.ReadValueSafe(out SynchronizationDataSize);
30043023
}
30053024
}
30063025

@@ -3015,12 +3034,7 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
30153034
{
30163035
if (serializer.IsWriter)
30173036
{
3018-
// write placeholder int.
3019-
// Can't be bitpacked because we don't know the value until we calculate it later
30203037
var writer = serializer.GetFastBufferWriter();
3021-
var positionBeforeSynchronizing = writer.Position;
3022-
writer.WriteValueSafe(0);
3023-
var sizeToSkipCalculationPosition = writer.Position;
30243038

30253039
// Synchronize NetworkVariables
30263040
foreach (var behavior in ChildNetworkBehaviours)
@@ -3046,12 +3060,6 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
30463060
}
30473061

30483062
var currentPosition = writer.Position;
3049-
// Write the total number of bytes written for NetworkVariable and NetworkBehaviour
3050-
// synchronization.
3051-
writer.Seek(positionBeforeSynchronizing);
3052-
// We want the size of everything after our size to skip calculation position
3053-
var size = currentPosition - sizeToSkipCalculationPosition;
3054-
writer.WriteValueSafe(size);
30553063
// Write the number of NetworkBehaviours synchronized
30563064
writer.Seek(networkBehaviourCountPosition);
30573065
writer.WriteValueSafe(synchronizationCount);
@@ -3061,41 +3069,26 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
30613069
}
30623070
else
30633071
{
3064-
var seekToEndOfSynchData = 0;
30653072
var reader = serializer.GetFastBufferReader();
3066-
try
3067-
{
3068-
reader.ReadValueSafe(out int sizeOfSynchronizationData);
3069-
seekToEndOfSynchData = reader.Position + sizeOfSynchronizationData;
30703073

3071-
// Apply the network variable synchronization data
3072-
foreach (var behaviour in ChildNetworkBehaviours)
3073-
{
3074-
behaviour.InitializeVariables();
3075-
behaviour.SetNetworkVariableData(reader, targetClientId);
3076-
}
3074+
// Apply the network variable synchronization data
3075+
foreach (var behaviour in ChildNetworkBehaviours)
3076+
{
3077+
behaviour.InitializeVariables();
3078+
behaviour.SetNetworkVariableData(reader, targetClientId);
3079+
}
30773080

3078-
// Read the number of NetworkBehaviours to synchronize
3079-
reader.ReadValueSafe(out byte numberSynchronized);
3081+
// Read the number of NetworkBehaviours to synchronize
3082+
reader.ReadValueSafe(out byte numberSynchronized);
30803083

3081-
// If a NetworkBehaviour writes synchronization data, it will first
3082-
// write its NetworkBehaviourId so when deserializing the client-side
3083-
// can find the right NetworkBehaviour to deserialize the synchronization data.
3084-
for (int i = 0; i < numberSynchronized; i++)
3085-
{
3086-
reader.ReadValueSafe(out ushort networkBehaviourId);
3087-
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
3088-
networkBehaviour.Synchronize(ref serializer, targetClientId);
3089-
}
3090-
3091-
if (seekToEndOfSynchData != reader.Position)
3092-
{
3093-
Debug.LogWarning($"[Size mismatch] Expected: {seekToEndOfSynchData} Currently At: {reader.Position}!");
3094-
}
3095-
}
3096-
catch
3084+
// If a NetworkBehaviour writes synchronization data, it will first
3085+
// write its NetworkBehaviourId so when deserializing the client-side
3086+
// can find the right NetworkBehaviour to deserialize the synchronization data.
3087+
for (int i = 0; i < numberSynchronized; i++)
30973088
{
3098-
reader.Seek(seekToEndOfSynchData);
3089+
reader.ReadValueSafe(out ushort networkBehaviourId);
3090+
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
3091+
networkBehaviour.Synchronize(ref serializer, targetClientId);
30993092
}
31003093
}
31013094
}
@@ -3185,15 +3178,18 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
31853178
/// <returns>The deserialized NetworkObject or null if deserialization failed</returns>
31863179
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager, bool invokedByMessage = false)
31873180
{
3188-
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3181+
var endOfSynchronizationData = reader.Position + sceneObject.SynchronizationDataSize;
3182+
3183+
byte[] instantiationData = null;
3184+
if (sceneObject.HasInstantiationData)
3185+
{
3186+
reader.ReadValueSafe(out instantiationData);
3187+
}
31893188

3190-
//Synchronize the instantiation data if needed
3191-
FastBufferReader instantiationDataReader = sceneObject.HasInstantiationData ? networkManager.PrefabHandler.GetInstantiationDataReader(sceneObject.Hash, reader) : default;
31923189

3193-
//Attempt to create a local NetworkObject
3194-
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject, instantiationDataReader);
3190+
// Attempt to create a local NetworkObject
3191+
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject, instantiationData);
31953192

3196-
instantiationDataReader.Dispose();
31973193

31983194
if (networkObject == null)
31993195
{
@@ -3206,8 +3202,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
32063202
try
32073203
{
32083204
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
3209-
reader.ReadValueSafe(out int networkBehaviourSynchronizationDataLength);
3210-
reader.Seek(reader.Position + networkBehaviourSynchronizationDataLength);
3205+
reader.Seek(endOfSynchronizationData);
32113206
}
32123207
catch (Exception ex)
32133208
{
@@ -3225,8 +3220,23 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
32253220
// Special Case: Invoke NetworkBehaviour.OnPreSpawn methods here before SynchronizeNetworkBehaviours
32263221
networkObject.InvokeBehaviourNetworkPreSpawn();
32273222

3228-
// Synchronize NetworkBehaviours
3229-
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
3223+
// Process the remaining synchronization data from the buffer
3224+
try
3225+
{
3226+
// Synchronize NetworkBehaviours
3227+
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3228+
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
3229+
3230+
if (reader.Position != endOfSynchronizationData)
3231+
{
3232+
Debug.LogWarning($"[Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!");
3233+
reader.Seek(endOfSynchronizationData);
3234+
}
3235+
}
3236+
catch
3237+
{
3238+
reader.Seek(endOfSynchronizationData);
3239+
}
32303240

32313241
// If we are an in-scene placed NetworkObject and we originally had a parent but when synchronized we are
32323242
// being told we do not have a parent, then we want to clear the latest parent so it is not automatically
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System.Collections.Generic;
2+
using UnityEngine;
3+
4+
namespace Unity.Netcode
5+
{
6+
/// <summary>
7+
/// Interface for customizing, overriding, spawning, and destroying Network Prefabs
8+
/// Used by <see cref="NetworkPrefabHandler"/>
9+
/// </summary>
10+
public interface INetworkPrefabInstanceHandler
11+
{
12+
/// <summary>
13+
/// Client Side Only
14+
/// Once an implementation is registered with the <see cref="NetworkPrefabHandler"/>, this method will be called every time
15+
/// a Network Prefab associated <see cref="NetworkObject"/> is spawned on clients
16+
///
17+
/// Note On Hosts: Use the <see cref="NetworkPrefabHandler.RegisterHostGlobalObjectIdHashValues(GameObject, List{T})"/>
18+
/// method to register all targeted NetworkPrefab overrides manually since the host will be acting as both a server and client.
19+
///
20+
/// Note on Pooling: If you are using a NetworkObject pool, don't forget to make the NetworkObject active
21+
/// via the <see cref="GameObject.SetActive(bool)"/> method.
22+
/// </summary>
23+
/// <remarks>
24+
/// If you need to pass custom data at instantiation time (e.g., selecting a variant, setting initialization parameters, or choosing a pre-instantiated object),
25+
/// implement <see cref="NetworkPrefabInstanceHandlerWithData{T}"/> instead.
26+
/// </remarks>
27+
/// <param name="ownerClientId">the owner for the <see cref="NetworkObject"/> to be instantiated</param>
28+
/// <param name="position">the initial/default position for the <see cref="NetworkObject"/> to be instantiated</param>
29+
/// <param name="rotation">the initial/default rotation for the <see cref="NetworkObject"/> to be instantiated</param>
30+
/// <returns>The instantiated NetworkObject instance. Returns null if instantiation fails.</returns>
31+
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation);
32+
33+
/// <summary>
34+
/// Invoked on Client and Server
35+
/// Once an implementation is registered with the <see cref="NetworkPrefabHandler"/>, this method will be called when
36+
/// a Network Prefab associated <see cref="NetworkObject"/> is:
37+
///
38+
/// Server Side: destroyed or despawned with the destroy parameter equal to true
39+
/// If <see cref="NetworkObject.Despawn(bool)"/> is invoked with the default destroy parameter (i.e. false) then this method will NOT be invoked!
40+
///
41+
/// Client Side: destroyed when the client receives a destroy object message from the server or host.
42+
///
43+
/// Note on Pooling: When this method is invoked, you do not need to destroy the NetworkObject as long as you want your pool to persist.
44+
/// The most common approach is to make the <see cref="NetworkObject"/> inactive by calling <see cref="GameObject.SetActive(bool)"/>.
45+
/// </summary>
46+
/// <param name="networkObject">The <see cref="NetworkObject"/> being destroyed</param>
47+
public void Destroy(NetworkObject networkObject);
48+
}
49+
}

com.unity.netcode.gameobjects/Runtime/Spawning/INetworkPrefabInstanceHandler.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)