Skip to content

Commit 096c85c

Browse files
committed
New stateless approach working flawless
1 parent c1d6503 commit 096c85c

4 files changed

Lines changed: 69 additions & 40 deletions

File tree

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

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,8 +1821,6 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla
18211821
}
18221822
}
18231823

1824-
InjectInstantiationData();
1825-
18261824
NetworkManager.SpawnManager.SpawnNetworkObjectLocally(this, NetworkManager.SpawnManager.GetNetworkObjectId(), IsSceneObject.HasValue && IsSceneObject.Value, playerObject, ownerClientId, destroyWithScene);
18271825

18281826
if ((NetworkManager.DistributedAuthorityMode && NetworkManager.DAHost) || (!NetworkManager.DistributedAuthorityMode && NetworkManager.IsServer))
@@ -1855,24 +1853,27 @@ internal void SpawnInternal(bool destroyWithScene, ulong ownerClientId, bool pla
18551853
}
18561854

18571855
/// <summary>
1858-
/// Injects the instantiation data into the <see cref="InstantiationData"/> if available. This is used to synchronize
1856+
/// Serializes the given instantiation data and stores it to be used during the spawn process
1857+
/// by a compatible <see cref="INetworkPrefabInstanceHandlerWithData{T}"/>
18591858
/// </summary>
1860-
internal void InjectInstantiationData()
1859+
public void InjectInstantiationData<T>(T data) where T : struct, INetworkSerializable
18611860
{
1862-
if (NetworkManager.PrefabHandler.TryGetHandlerWithData(this.GlobalObjectIdHash, out var prefabHandler))
1861+
if (!NetworkManager.PrefabHandler.TryGetHandlerWithData(GlobalObjectIdHash, out var prefabHandler) || !prefabHandler.HandlesDataType<T>())
18631862
{
1864-
var serializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(new FastBufferWriter(4, Collections.Allocator.Temp, int.MaxValue)));
1865-
try
1866-
{
1867-
prefabHandler.OnSynchronizeInstantiationData(ref serializer);
1868-
InstantiationData = serializer.GetFastBufferWriter().ToArray();
1869-
}
1870-
catch (Exception ex)
1871-
{
1872-
serializer.GetFastBufferWriter().Dispose();
1873-
NetworkLog.LogError($"[InstantiationData] Handler failed during synchronization for injection (Write): {ex.Message}");
1874-
return;
1875-
}
1863+
throw new Exception("[InstantiationData] Cannot inject data: no compatible handler found for the specified data type.");
1864+
}
1865+
1866+
using var writer = new FastBufferWriter(4, Collections.Allocator.Temp, int.MaxValue);
1867+
var serializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
1868+
1869+
try
1870+
{
1871+
data.NetworkSerialize(serializer);
1872+
InstantiationData = writer.ToArray();
1873+
}
1874+
catch (Exception ex)
1875+
{
1876+
NetworkLog.LogError($"[InstantiationData] Failed to serialize instantiation data for {nameof(NetworkObject)} '{gameObject.name}': {ex}");
18761877
}
18771878
}
18781879

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,50 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
15
namespace Unity.Netcode
26
{
37
/// <summary>
4-
/// Specialized version of <see cref="INetworkPrefabInstanceHandler"/> that supports synchronizing
5-
/// custom data prior to the instantiation of a <see cref="NetworkObject"/>.
8+
/// Specialized version of <see cref="INetworkPrefabInstanceHandler"/> that receives
9+
/// custom instantiation data injected by the server before spawning.
10+
/// </summary>
11+
public interface INetworkPrefabInstanceHandlerWithData<T> : INetworkPrefabInstanceHandlerWithData where T : struct, INetworkSerializable
12+
{
13+
static readonly Dictionary<INetworkPrefabInstanceHandlerWithData, T> _table = new();
14+
15+
NetworkObject INetworkPrefabInstanceHandler.Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation) => Instantiate(ownerClientId, position, rotation, _table[this]);
16+
void INetworkPrefabInstanceHandlerWithData.RemoveDataEntry(INetworkPrefabInstanceHandlerWithData instance) => _table.Remove(instance);
17+
bool INetworkPrefabInstanceHandlerWithData.HandlesDataType<U>() => typeof(T) == typeof(U);
18+
void INetworkPrefabInstanceHandlerWithData.OnReadInstantiationData<RW>(ref BufferSerializer<RW> serializer)
19+
{
20+
_table.TryGetValue(this, out var value);
21+
serializer.SerializeValue(ref value);
22+
_table[this] = value;
23+
}
24+
25+
NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, T instantiationData);
26+
}
27+
28+
/// <summary>
29+
/// Internal use only. Do not implement directly. Use <see cref="INetworkPrefabInstanceHandlerWithData{T}"/> instead.
630
/// </summary>
731
public interface INetworkPrefabInstanceHandlerWithData : INetworkPrefabInstanceHandler
832
{
933
/// <summary>
10-
/// Allows synchronizing custom instantiation data before the object is instantiated. <br/>
11-
/// Called before <see cref="INetworkPrefabInstanceHandler.Instantiate"/>.
34+
/// Invoked during deserialization to read the instantiation data associated with this prefab instance.
35+
/// </summary>
36+
void OnReadInstantiationData<Y>(ref BufferSerializer<Y> serializer) where Y : IReaderWriter;
37+
38+
/// <summary>
39+
/// Removes the data entry for the given instance.
40+
/// Is important to call this when the instance isnt referenced to avoid memory leaks.
41+
/// </summary>
42+
/// <param name="instance"></param>
43+
void RemoveDataEntry(INetworkPrefabInstanceHandlerWithData instance);
44+
45+
/// <summary>
46+
/// Returns true if <typeparamref name="T"/> matches the expected instantiation data type for this handler.
1247
/// </summary>
13-
/// <param name="serializer">The serializer used to synchronize the custom instantiation data.</param>
14-
void OnSynchronizeInstantiationData<T>(ref BufferSerializer<T> serializer) where T : IReaderWriter;
48+
bool HandlesDataType<T>() where T : struct, INetworkSerializable;
1549
}
1650
}

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkPrefabHandler.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public interface INetworkPrefabInstanceHandler
2222
/// via the <see cref="GameObject.SetActive(bool)"/> method.
2323
///
2424
/// 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="INetworkPrefabInstanceHandlerWithData"/> alongside this interface to receive the data before instantiation.
25+
/// implement <see cref="INetworkPrefabInstanceHandlerWithData{T}"/> instead.
2626
/// </summary>
2727
/// <param name="ownerClientId">the owner for the <see cref="NetworkObject"/> to be instantiated</param>
2828
/// <param name="position">the initial/default position for the <see cref="NetworkObject"/> to be instantiated</param>
@@ -212,7 +212,11 @@ public bool RemoveHandler(uint globalObjectIdHash)
212212
m_PrefabInstanceToPrefabAsset.Remove(networkPrefabHashKey);
213213
}
214214

215-
m_PrefabAssetToPrefabHandlerWithData.Remove(globalObjectIdHash);
215+
if(m_PrefabAssetToPrefabHandlerWithData.TryGetValue(globalObjectIdHash, out var handlerWithData))
216+
{
217+
handlerWithData.RemoveDataEntry(handlerWithData);
218+
m_PrefabAssetToPrefabHandlerWithData.Remove(globalObjectIdHash);
219+
}
216220
return m_PrefabAssetToPrefabHandler.Remove(globalObjectIdHash);
217221
}
218222

@@ -268,7 +272,7 @@ internal void ReadInstantiationData<T>(uint objectHash, ref BufferSerializer<T>
268272

269273
try
270274
{
271-
synchronizableHandler.OnSynchronizeInstantiationData(ref serializer);
275+
synchronizableHandler.OnReadInstantiationData(ref serializer);
272276
}
273277
catch (Exception ex)
274278
{

com.unity.netcode.gameobjects/Tests/Runtime/Prefabs/NetworkPrefabHandlerWithDataTests.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ public IEnumerator InstantiationPayload_SyncsCorrectly()
8686
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1, null, 512);
8787

8888
//Sets the values to synchronize
89-
server_handler.ValueToSynchronize = 48;
9089
server_handler.networksSerializableToSynchronize = new NetworkSerializableTest() { Value = 12, Value2 = 3.14f };
9190

9291
// Spawn the prefab on the server
@@ -95,7 +94,7 @@ public IEnumerator InstantiationPayload_SyncsCorrectly()
9594

9695
// wait for the clients to receive the instantiation payload
9796
var timeoutHelper = new TimeoutHelper();
98-
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => client_handlers.All(handler => handler.ValueToSynchronize == server_handler.ValueToSynchronize));
97+
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => client_handlers.All(handler => (handler.networksSerializableToSynchronize.Value == server_handler.networksSerializableToSynchronize.Value)&& (handler.networksSerializableToSynchronize.Value2 == server_handler.networksSerializableToSynchronize.Value2)));
9998
Assert.False(timeoutHelper.TimedOut, "Did not successfully sync all handlers");
10099

101100
// Check that the values are synchronized
@@ -105,25 +104,17 @@ public IEnumerator InstantiationPayload_SyncsCorrectly()
105104
}
106105
}
107106

108-
private class PrefabInstanceHandlerWithData : INetworkPrefabInstanceHandlerWithData
107+
private class PrefabInstanceHandlerWithData : INetworkPrefabInstanceHandlerWithData<NetworkSerializableTest>
109108
{
110109
public GameObject Prefab;
111-
public int ValueToSynchronize;
112110
public NetworkSerializableTest networksSerializableToSynchronize;
113-
114111
public PrefabInstanceHandlerWithData(GameObject prefab)
115112
{
116113
Prefab = prefab;
117114
}
118-
119-
public virtual void OnSynchronizeInstantiationData<T>(ref BufferSerializer<T> serializer) where T : IReaderWriter
120-
{
121-
serializer.SerializeValue(ref ValueToSynchronize);
122-
serializer.SerializeValue(ref networksSerializableToSynchronize);
123-
}
124-
125-
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
115+
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation, NetworkSerializableTest data)
126116
{
117+
networksSerializableToSynchronize = data;
127118
var instance = GameObject.Instantiate(Prefab, position, rotation).GetComponent<NetworkObject>();
128119
return instance;
129120
}
@@ -139,7 +130,6 @@ public bool IsSynchronizedWith(PrefabInstanceHandlerWithData other)
139130
return false;
140131

141132
bool isSynchronized = true;
142-
isSynchronized &= ValueToSynchronize == other.ValueToSynchronize;
143133
isSynchronized &= networksSerializableToSynchronize.Value == other.networksSerializableToSynchronize.Value;
144134
isSynchronized &= networksSerializableToSynchronize.Value2 == other.networksSerializableToSynchronize.Value2;
145135
return isSynchronized;

0 commit comments

Comments
 (0)