Skip to content

Commit 7b48cb7

Browse files
Merge branch 'develop-2.0.0' into chore/2.x/migrate-2-4-2-back-to-develop-2-0-0
2 parents 8cb3541 + d84313f commit 7b48cb7

11 files changed

+461
-108
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
4444

4545
- Added `SinglePlayerTransport` that provides the ability to start as a host for a single player network session. (#3473)
4646
- When using UnityTransport >=2.4 and Unity >= 6000.1.0a1, SetConnectionData will accept a fully qualified hostname instead of an IP as a connect address on the client side. (#3441)
47+
- Added `NetworkPrefabInstanceHandlerWithData<T>`, a variant of `INetworkPrefabInstanceHandler` that provides access to custom instantiation data directly within the `Instantiate()` method. (#3430)
4748

4849
### Fixed
4950

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1593,8 +1593,9 @@ internal void ShutdownInternal()
15931593
// Completely reset the NetworkClient
15941594
ConnectionManager.LocalClient = new NetworkClient();
15951595

1596-
// This cleans up the internal prefabs list
1596+
// Clean up the internal prefabs data
15971597
NetworkConfig?.Prefabs?.Shutdown();
1598+
PrefabHandler.Shutdown();
15981599

15991600
// Reset the configuration hash for next session in the event
16001601
// that the prefab list changes

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

Lines changed: 85 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public uint PrefabIdHash
5858
}
5959
}
6060

61+
/// <summary>
62+
/// InstantiationData sent during the instantiation process.
63+
/// Available to read as T parameter to <see cref="NetworkPrefabInstanceHandlerWithData{T}.Instantiate(ulong, Vector3, Quaternion, T)"/> for custom handling by user code.
64+
/// </summary>
65+
internal byte[] InstantiationData;
66+
6167
/// <summary>
6268
/// All <see cref="NetworkTransform"/> component instances associated with a <see cref="NetworkObject"/> component instance.
6369
/// </summary>
@@ -2857,6 +2863,12 @@ public bool SpawnWithObservers
28572863
set => ByteUtility.SetBit(ref m_BitField, 10, value);
28582864
}
28592865

2866+
public bool HasInstantiationData
2867+
{
2868+
get => ByteUtility.GetBit(m_BitField, 11);
2869+
set => ByteUtility.SetBit(ref m_BitField, 11, value);
2870+
}
2871+
28602872
// When handling the initial synchronization of NetworkObjects,
28612873
// this will be populated with the known observers.
28622874
public ulong[] Observers;
@@ -2884,6 +2896,7 @@ public struct TransformData : INetworkSerializeByMemcpy
28842896

28852897
public int NetworkSceneHandle;
28862898

2899+
internal int SynchronizationDataSize;
28872900

28882901
public void Serialize(FastBufferWriter writer)
28892902
{
@@ -2945,9 +2958,29 @@ public void Serialize(FastBufferWriter writer)
29452958
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
29462959
}
29472960

2961+
// write placeholder for serialized data size.
2962+
// Can't be bitpacked because we don't know the value until we calculate it later
2963+
var positionBeforeSynchronizing = writer.Position;
2964+
writer.WriteValueSafe(0);
2965+
var sizeToSkipCalculationPosition = writer.Position;
2966+
2967+
if (HasInstantiationData)
2968+
{
2969+
writer.WriteValueSafe(OwnerObject.InstantiationData);
2970+
}
2971+
29482972
// Synchronize NetworkVariables and NetworkBehaviours
29492973
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
29502974
OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
2975+
2976+
var currentPosition = writer.Position;
2977+
// Write the total number of bytes written for synchronization data.
2978+
writer.Seek(positionBeforeSynchronizing);
2979+
// We want the size of everything after our size to skip calculation position
2980+
var size = currentPosition - sizeToSkipCalculationPosition;
2981+
writer.WriteValueSafe(size);
2982+
// seek back to the head of the writer.
2983+
writer.Seek(currentPosition);
29512984
}
29522985

29532986
public void Deserialize(FastBufferReader reader)
@@ -3003,6 +3036,10 @@ public void Deserialize(FastBufferReader reader)
30033036
// The NetworkSceneHandle is the server-side relative
30043037
// scene handle that the NetworkObject resides in.
30053038
reader.ReadValue(out NetworkSceneHandle);
3039+
3040+
// Read the size of the remaining synchronization data
3041+
// This data will be read in AddSceneObject()
3042+
reader.ReadValueSafe(out SynchronizationDataSize);
30063043
}
30073044
}
30083045

@@ -3017,12 +3054,7 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
30173054
{
30183055
if (serializer.IsWriter)
30193056
{
3020-
// write placeholder int.
3021-
// Can't be bitpacked because we don't know the value until we calculate it later
30223057
var writer = serializer.GetFastBufferWriter();
3023-
var positionBeforeSynchronizing = writer.Position;
3024-
writer.WriteValueSafe(0);
3025-
var sizeToSkipCalculationPosition = writer.Position;
30263058

30273059
// Synchronize NetworkVariables
30283060
foreach (var behavior in ChildNetworkBehaviours)
@@ -3048,12 +3080,6 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
30483080
}
30493081

30503082
var currentPosition = writer.Position;
3051-
// Write the total number of bytes written for NetworkVariable and NetworkBehaviour
3052-
// synchronization.
3053-
writer.Seek(positionBeforeSynchronizing);
3054-
// We want the size of everything after our size to skip calculation position
3055-
var size = currentPosition - sizeToSkipCalculationPosition;
3056-
writer.WriteValueSafe(size);
30573083
// Write the number of NetworkBehaviours synchronized
30583084
writer.Seek(networkBehaviourCountPosition);
30593085
writer.WriteValueSafe(synchronizationCount);
@@ -3063,41 +3089,26 @@ internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer
30633089
}
30643090
else
30653091
{
3066-
var seekToEndOfSynchData = 0;
30673092
var reader = serializer.GetFastBufferReader();
3068-
try
3069-
{
3070-
reader.ReadValueSafe(out int sizeOfSynchronizationData);
3071-
seekToEndOfSynchData = reader.Position + sizeOfSynchronizationData;
3072-
3073-
// Apply the network variable synchronization data
3074-
foreach (var behaviour in ChildNetworkBehaviours)
3075-
{
3076-
behaviour.InitializeVariables();
3077-
behaviour.SetNetworkVariableData(reader, targetClientId);
3078-
}
30793093

3080-
// Read the number of NetworkBehaviours to synchronize
3081-
reader.ReadValueSafe(out byte numberSynchronized);
3094+
// Apply the network variable synchronization data
3095+
foreach (var behaviour in ChildNetworkBehaviours)
3096+
{
3097+
behaviour.InitializeVariables();
3098+
behaviour.SetNetworkVariableData(reader, targetClientId);
3099+
}
30823100

3083-
// If a NetworkBehaviour writes synchronization data, it will first
3084-
// write its NetworkBehaviourId so when deserializing the client-side
3085-
// can find the right NetworkBehaviour to deserialize the synchronization data.
3086-
for (int i = 0; i < numberSynchronized; i++)
3087-
{
3088-
reader.ReadValueSafe(out ushort networkBehaviourId);
3089-
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
3090-
networkBehaviour.Synchronize(ref serializer, targetClientId);
3091-
}
3101+
// Read the number of NetworkBehaviours to synchronize
3102+
reader.ReadValueSafe(out byte numberSynchronized);
30923103

3093-
if (seekToEndOfSynchData != reader.Position)
3094-
{
3095-
Debug.LogWarning($"[Size mismatch] Expected: {seekToEndOfSynchData} Currently At: {reader.Position}!");
3096-
}
3097-
}
3098-
catch
3104+
// If a NetworkBehaviour writes synchronization data, it will first
3105+
// write its NetworkBehaviourId so when deserializing the client-side
3106+
// can find the right NetworkBehaviour to deserialize the synchronization data.
3107+
for (int i = 0; i < numberSynchronized; i++)
30993108
{
3100-
reader.Seek(seekToEndOfSynchData);
3109+
reader.ReadValueSafe(out ushort networkBehaviourId);
3110+
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
3111+
networkBehaviour.Synchronize(ref serializer, targetClientId);
31013112
}
31023113
}
31033114
}
@@ -3121,7 +3132,8 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
31213132
NetworkSceneHandle = NetworkSceneHandle,
31223133
Hash = CheckForGlobalObjectIdHashOverride(),
31233134
OwnerObject = this,
3124-
TargetClientId = targetClientId
3135+
TargetClientId = targetClientId,
3136+
HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0
31253137
};
31263138

31273139
// Handle Parenting
@@ -3186,8 +3198,18 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager
31863198
/// <returns>The deserialized NetworkObject or null if deserialization failed</returns>
31873199
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager, bool invokedByMessage = false)
31883200
{
3189-
//Attempt to create a local NetworkObject
3190-
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject);
3201+
var endOfSynchronizationData = reader.Position + sceneObject.SynchronizationDataSize;
3202+
3203+
byte[] instantiationData = null;
3204+
if (sceneObject.HasInstantiationData)
3205+
{
3206+
reader.ReadValueSafe(out instantiationData);
3207+
}
3208+
3209+
3210+
// Attempt to create a local NetworkObject
3211+
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject, instantiationData);
3212+
31913213

31923214
if (networkObject == null)
31933215
{
@@ -3200,8 +3222,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
32003222
try
32013223
{
32023224
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
3203-
reader.ReadValueSafe(out int networkBehaviourSynchronizationDataLength);
3204-
reader.Seek(reader.Position + networkBehaviourSynchronizationDataLength);
3225+
reader.Seek(endOfSynchronizationData);
32053226
}
32063227
catch (Exception ex)
32073228
{
@@ -3219,9 +3240,24 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
32193240
// Special Case: Invoke NetworkBehaviour.OnPreSpawn methods here before SynchronizeNetworkBehaviours
32203241
networkObject.InvokeBehaviourNetworkPreSpawn();
32213242

3222-
// Synchronize NetworkBehaviours
3223-
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3224-
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
3243+
// Process the remaining synchronization data from the buffer
3244+
try
3245+
{
3246+
// Synchronize NetworkBehaviours
3247+
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3248+
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
3249+
3250+
// Ensure that the buffer is completely reset
3251+
if (reader.Position != endOfSynchronizationData)
3252+
{
3253+
Debug.LogWarning($"[Size mismatch] Expected: {endOfSynchronizationData} Currently At: {reader.Position}!");
3254+
reader.Seek(endOfSynchronizationData);
3255+
}
3256+
}
3257+
catch
3258+
{
3259+
reader.Seek(endOfSynchronizationData);
3260+
}
32253261

32263262
// If we are an in-scene placed NetworkObject and we originally had a parent but when synchronized we are
32273263
// 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)