Skip to content

Commit 96b7af6

Browse files
committed
Added buffer safety and Tests
1 parent 0fab0a6 commit 96b7af6

4 files changed

Lines changed: 244 additions & 7 deletions

File tree

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2981,8 +2981,7 @@ public void Serialize(FastBufferWriter writer)
29812981

29822982
if (HasInstantiationPayload)
29832983
{
2984-
if (NetworkManager.Singleton.PrefabHandler.TryGetHandlerWithData(Hash, out INetworkPrefabInstanceHandlerWithData synchronizer))
2985-
synchronizer.OnSynchronizeInstantiationData(ref bufferSerializer);
2984+
OwnerObject.NetworkManager.PrefabHandler.SynchronizeInstantiationData(Hash, ref bufferSerializer);
29862985
}
29872986

29882987
OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
@@ -3232,11 +3231,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
32323231
//Synchronize the instantiation payload if needed
32333232
if (sceneObject.HasInstantiationPayload)
32343233
{
3235-
if (NetworkManager.Singleton.PrefabHandler.TryGetHandlerWithData(sceneObject.Hash, out INetworkPrefabInstanceHandlerWithData handler))
3236-
{
3237-
var instantiationPayloadBufferReader = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
3238-
handler.OnSynchronizeInstantiationData(ref instantiationPayloadBufferReader);
3239-
}
3234+
networkManager.PrefabHandler.SynchronizeInstantiationData(sceneObject.Hash, ref bufferSerializer);
32403235
}
32413236

32423237
//Attempt to create a local NetworkObject

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,77 @@ internal bool TryGetHandlerWithData(uint objectHash, out INetworkPrefabInstanceH
253253
return false;
254254
}
255255

256+
/// <summary>
257+
/// Handles the instantiation data for a given <see cref="NetworkObject.GlobalObjectIdHash"/>
258+
/// </summary>
259+
/// <typeparam name="T"></typeparam>
260+
/// <param name="objectHash"></param>
261+
/// <param name="serializer"></param>
262+
internal void SynchronizeInstantiationData<T>(uint objectHash, ref BufferSerializer<T> serializer) where T : IReaderWriter
263+
{
264+
if (!TryGetHandlerWithData(objectHash, out INetworkPrefabInstanceHandlerWithData synchronizableHandler))
265+
{
266+
return;
267+
}
268+
269+
if (serializer.IsWriter)
270+
{
271+
// Reserves space to write the size later
272+
FastBufferWriter fastBufferWriter = serializer.GetFastBufferWriter();
273+
int sizePos = fastBufferWriter.Position;
274+
fastBufferWriter.WriteValueSafe(0); // placeholder for size, correctly writen at the end
275+
276+
int payloadStartPos = fastBufferWriter.Position;
277+
try
278+
{
279+
synchronizableHandler.OnSynchronizeInstantiationData(ref serializer);
280+
}
281+
catch (Exception ex)
282+
{
283+
// Resets to start position to avoid writing corrupted data
284+
fastBufferWriter.Seek(sizePos);
285+
NetworkLog.LogError($"[InstantiationPayload] Handler failed during synchronization (Write): {ex.Message}");
286+
return;
287+
}
288+
289+
// Compute and write actual payload size
290+
int payloadEndPos = fastBufferWriter.Position;
291+
int payloadSize = payloadEndPos - payloadStartPos;
292+
// Goes back and write the real size
293+
fastBufferWriter.Seek(sizePos);
294+
fastBufferWriter.WriteValueSafe(payloadSize);
295+
// Restores to end
296+
fastBufferWriter.Seek(payloadEndPos);
297+
}
298+
else
299+
{
300+
FastBufferReader fastBufferReader = serializer.GetFastBufferReader();
301+
// Reads the expected size of the payload
302+
fastBufferReader.ReadValueSafe(out int payloadSize);
303+
int payloadStartPos = fastBufferReader.Position;
304+
305+
try
306+
{
307+
synchronizableHandler.OnSynchronizeInstantiationData(ref serializer);
308+
}
309+
catch (Exception ex)
310+
{
311+
// Skips the unread payload bytes
312+
fastBufferReader.Seek(payloadStartPos + payloadSize);
313+
NetworkLog.LogError($"[InstantiationPayload] Handler failed during synchronization (Read): {ex.Message}");
314+
return;
315+
}
316+
317+
// Validates if expected number of bytes were read
318+
int payloadEndPos = fastBufferReader.Position;
319+
if (payloadEndPos != payloadStartPos + payloadSize)
320+
{
321+
NetworkLog.LogWarning($"[Payload] Read {payloadEndPos - payloadStartPos} bytes, expected {payloadSize}");
322+
fastBufferReader.Seek(payloadStartPos + payloadSize);
323+
}
324+
}
325+
}
326+
256327
/// <summary>
257328
/// Returns the source NetworkPrefab's <see cref="NetworkObject.GlobalObjectIdHash"/>
258329
/// </summary>
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
using System;
2+
using System.Collections;
3+
using System.Linq;
4+
using NUnit.Framework;
5+
using Unity.Netcode.TestHelpers.Runtime;
6+
using UnityEngine;
7+
using UnityEngine.TestTools;
8+
9+
namespace Unity.Netcode.RuntimeTests
10+
{
11+
internal class NetworkPrefabHandlerWithDataTests
12+
{
13+
private GameObject _prefab;
14+
15+
private NetworkManager server;
16+
private NetworkManager[] clients;
17+
const int k_clientCount = 4;
18+
19+
private PrefabInstanceHandlerWithData server_handler;
20+
private PrefabInstanceHandlerWithData[] client_handlers;
21+
22+
private const string k_TestPrefabObjectName = "NetworkPrefabTestObject";
23+
private uint m_ObjectId = 1;
24+
private GameObject MakeValidNetworkPrefab()
25+
{
26+
Guid baseObjectID = NetworkManagerHelper.AddGameNetworkObject(k_TestPrefabObjectName + m_ObjectId.ToString());
27+
NetworkObject validPrefab = NetworkManagerHelper.InstantiatedNetworkObjects[baseObjectID];
28+
NetcodeIntegrationTestHelpers.MakeNetworkObjectTestPrefab(validPrefab);
29+
m_ObjectId++;
30+
return validPrefab.gameObject;
31+
}
32+
33+
[SetUp]
34+
public void Setup()
35+
{
36+
NetcodeIntegrationTestHelpers.Create(k_clientCount, out server, out clients);
37+
38+
_prefab = MakeValidNetworkPrefab();
39+
40+
NetworkPrefab networkPrefab = new NetworkPrefab() { Prefab = _prefab };
41+
42+
server.NetworkConfig.Prefabs.Add(networkPrefab);
43+
server_handler = new PrefabInstanceHandlerWithData(_prefab);
44+
server.PrefabHandler.AddHandler(_prefab, server_handler);
45+
46+
client_handlers = new PrefabInstanceHandlerWithData[clients.Length];
47+
for (int i = 0; i < clients.Length; i++)
48+
{
49+
client_handlers[i] = new PrefabInstanceHandlerWithData(_prefab);
50+
clients[i].NetworkConfig.Prefabs.Add(networkPrefab);
51+
clients[i].PrefabHandler.AddHandler(_prefab, client_handlers[i]);
52+
}
53+
}
54+
55+
[TearDown]
56+
public void Teardown()
57+
{
58+
for (int i = 0; i < clients.Length; i++)
59+
{
60+
clients[i].PrefabHandler.RemoveHandler(_prefab);
61+
clients[i].NetworkConfig.Prefabs.Remove(_prefab);
62+
clients[i].Shutdown();
63+
}
64+
65+
server.PrefabHandler.RemoveHandler(_prefab);
66+
server.NetworkConfig.Prefabs.Remove(_prefab);
67+
server.Shutdown();
68+
69+
UnityEngine.Object.DestroyImmediate(_prefab);
70+
}
71+
72+
[UnityTest]
73+
public IEnumerator InstantiationPayload_SyncsCorrectly()
74+
{
75+
// Start the instances
76+
if (!NetcodeIntegrationTestHelpers.Start(true, server, clients))
77+
{
78+
Debug.LogError("Failed to start instances");
79+
Assert.Fail("Failed to start instances");
80+
}
81+
82+
// [Client-Side] Wait for a connection to the server
83+
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnected(clients, null, 512);
84+
85+
// [Host-Side] Check to make sure all clients are connected
86+
yield return NetcodeIntegrationTestHelpers.WaitForClientsConnectedToServer(server, clients.Length + 1, null, 512);
87+
88+
//Sets the values to synchronize
89+
server_handler.ValueToSynchronize = 48;
90+
server_handler.networksSerializableToSynchronize = new NetworkSerializableTest() { Value = 12, Value2 = 3.14f };
91+
92+
// Spawn the prefab on the server
93+
var spawned = server.SpawnManager.InstantiateAndSpawn(_prefab.GetComponent<NetworkObject>());
94+
Assert.NotNull(spawned);
95+
96+
// wait for the clients to receive the instantiation payload
97+
var timeoutHelper = new TimeoutHelper();
98+
yield return NetcodeIntegrationTest.WaitForConditionOrTimeOut(() => client_handlers.All(handler => handler.ValueToSynchronize == server_handler.ValueToSynchronize));
99+
Assert.False(timeoutHelper.TimedOut, "Did not successfully sync all handlers");
100+
101+
// Check that the values are synchronized
102+
for (int i = 0; i < client_handlers.Length; i++)
103+
{
104+
Assert.IsTrue(client_handlers[i].IsSynchronizedWith(server_handler), "Client handler " + i + " is not synchronized with server handler");
105+
}
106+
}
107+
108+
private class PrefabInstanceHandlerWithData : INetworkPrefabInstanceHandlerWithData
109+
{
110+
public GameObject Prefab;
111+
public int ValueToSynchronize;
112+
public NetworkSerializableTest networksSerializableToSynchronize;
113+
114+
public PrefabInstanceHandlerWithData(GameObject prefab)
115+
{
116+
Prefab = prefab;
117+
}
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)
126+
{
127+
var instance = GameObject.Instantiate(Prefab, position, rotation).GetComponent<NetworkObject>();
128+
return instance;
129+
}
130+
131+
public void Destroy(NetworkObject networkObject)
132+
{
133+
GameObject.DestroyImmediate(networkObject.gameObject);
134+
}
135+
136+
public bool IsSynchronizedWith(PrefabInstanceHandlerWithData other)
137+
{
138+
if (other == null)
139+
return false;
140+
141+
bool isSynchronized = true;
142+
isSynchronized &= ValueToSynchronize == other.ValueToSynchronize;
143+
isSynchronized &= networksSerializableToSynchronize.Value == other.networksSerializableToSynchronize.Value;
144+
isSynchronized &= networksSerializableToSynchronize.Value2 == other.networksSerializableToSynchronize.Value2;
145+
return isSynchronized;
146+
}
147+
}
148+
149+
struct NetworkSerializableTest : INetworkSerializable
150+
{
151+
public int Value;
152+
public float Value2;
153+
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
154+
{
155+
serializer.SerializeValue(ref Value);
156+
serializer.SerializeValue(ref Value2);
157+
}
158+
}
159+
}
160+
}

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

Lines changed: 11 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)