diff --git a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs index 5803e79087..d0606897ec 100644 --- a/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs @@ -144,10 +144,18 @@ public class NetworkConfig /// public bool EnableNetworkLogs = true; - // todo: transitional. For the next release, only Snapshot should remain - // The booleans allow iterative development and testing in the meantime + /// + /// Whether or not to enable Snapshot System for variable updates. Currently unsupported. + /// public bool UseSnapshotDelta { get; } = false; + /// + /// Whether or not to enable Snapshot System for spawn and despawn commands. Working but experimental. + /// public bool UseSnapshotSpawn { get; } = false; + /// + /// When Snapshot System spawn is enabled: max size of Snapshot Messages. Meant to fit MTU. + /// + public int SnapshotMaxSpawnUsage { get; } = 1200; public const int RttAverageSamples = 5; // number of RTT to keep an average of (plus one) public const int RttWindowSize = 64; // number of slots to use for RTT computations (max number of in-flight packets) diff --git a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs index b2a509f573..8b5133ea6f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs @@ -243,15 +243,14 @@ internal void WriteEntry(NetworkWriter writer, in Entry entry) writer.WriteUInt16(entry.Length); } - internal void WriteSpawn(ClientData clientData, NetworkWriter writer, in SnapshotSpawnCommand spawn) + internal ClientData.SentSpawn WriteSpawn(in ClientData clientData, NetworkWriter writer, in SnapshotSpawnCommand spawn) { // remember which spawn we sent this connection with which sequence number // that way, upon ack, we can track what is being ack'ed - ClientData.SentSpawn s; - s.ObjectId = spawn.NetworkObjectId; - s.Tick = spawn.TickWritten; - s.SequenceNumber = clientData.SequenceNumber; - clientData.SentSpawns.Add(s); + ClientData.SentSpawn sentSpawn; + sentSpawn.ObjectId = spawn.NetworkObjectId; + sentSpawn.Tick = spawn.TickWritten; + sentSpawn.SequenceNumber = clientData.SequenceNumber; writer.WriteUInt64Packed(spawn.NetworkObjectId); writer.WriteUInt64Packed(spawn.GlobalObjectIdHash); @@ -265,20 +264,23 @@ internal void WriteSpawn(ClientData clientData, NetworkWriter writer, in Snapsho writer.WriteVector3(spawn.ObjectScale); writer.WriteInt32Packed(spawn.TickWritten); + + return sentSpawn; } - internal void WriteDespawn(ClientData clientData, NetworkWriter writer, in SnapshotDespawnCommand despawn) + internal ClientData.SentSpawn WriteDespawn(in ClientData clientData, NetworkWriter writer, in SnapshotDespawnCommand despawn) { // remember which spawn we sent this connection with which sequence number // that way, upon ack, we can track what is being ack'ed - ClientData.SentSpawn s; - s.ObjectId = despawn.NetworkObjectId; - s.Tick = despawn.TickWritten; - s.SequenceNumber = clientData.SequenceNumber; - clientData.SentSpawns.Add(s); + ClientData.SentSpawn sentSpawn; + sentSpawn.ObjectId = despawn.NetworkObjectId; + sentSpawn.Tick = despawn.TickWritten; + sentSpawn.SequenceNumber = clientData.SequenceNumber; writer.WriteUInt64Packed(despawn.NetworkObjectId); writer.WriteInt32Packed(despawn.TickWritten); + + return sentSpawn; } /// /// Read a received Entry @@ -605,8 +607,6 @@ internal class SnapshotSystem : INetworkUpdateSystem, IDisposable internal const ushort SentinelBefore = 0x4246; internal const ushort SentinelAfter = 0x89CE; - private const int k_MaxSpawnUsage = 1000; // max bytes to use for the spawn/despawn part - private NetworkManager m_NetworkManager = default; private Snapshot m_Snapshot = default; @@ -728,8 +728,8 @@ private void SendSnapshot(ulong clientId) writer.WriteUInt16(SentinelBefore); WriteBuffer(buffer); WriteIndex(buffer); - WriteSpawns(buffer, clientId); WriteAcks(buffer, clientId); + WriteSpawns(buffer, clientId); writer.WriteUInt16(SentinelAfter); m_ClientData[clientId].LastReceivedSequence = 0; @@ -745,8 +745,8 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId) { var spawnWritten = 0; var despawnWritten = 0; + var overSize = false; - bool overSize = false; ClientData clientData = m_ClientData[clientId]; // this is needed because spawns being removed may have reduce the size below LRU position @@ -777,35 +777,58 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId) for (var j = 0; j < m_Snapshot.NumSpawns && !overSize; j++) { var index = clientData.NextSpawnIndex; + var savedPosition = writer.GetStream().Position; if (m_Snapshot.Spawns[index].TargetClientIds.Contains(clientId)) { - m_Snapshot.WriteSpawn(clientData, writer, in m_Snapshot.Spawns[index]); - spawnWritten++; - } + var sentSpawn = m_Snapshot.WriteSpawn(clientData, writer, in m_Snapshot.Spawns[index]); - // limit spawn sizes, compare current pos to very first position we wrote to - if (writer.GetStream().Position - positionSpawns > k_MaxSpawnUsage) - { - overSize = true; + // limit spawn sizes, compare current pos to very first position we wrote to + if (writer.GetStream().Position - positionSpawns > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage) + { + overSize = true; + // revert back the position to undo the write + writer.GetStream().Position = savedPosition; + } + else + { + clientData.SentSpawns.Add(sentSpawn); + spawnWritten++; + } } clientData.NextSpawnIndex = (clientData.NextSpawnIndex + 1) % m_Snapshot.NumSpawns; } + // even though we might have a spawn we could not fit, it's possible despawns will fit (they're smaller) + + // todo: this next line is commented for now because there's no check for a spawn command to have been + // ack'ed before sending a despawn for the same object. + // Uncommenting this line would allow some despawn to be sent while spawns are pending. + // As-is it is overly restrictive but allows us to go forward without the spawn/despawn dependency check + + // overSize = false; for (var j = 0; j < m_Snapshot.NumDespawns && !overSize; j++) { var index = clientData.NextDespawnIndex; + var savedPosition = writer.GetStream().Position; if (m_Snapshot.Despawns[index].TargetClientIds.Contains(clientId)) { - m_Snapshot.WriteDespawn(clientData, writer, in m_Snapshot.Despawns[index]); - despawnWritten++; - } - // limit spawn sizes, compare current pos to very first position we wrote to - if (writer.GetStream().Position - positionSpawns > k_MaxSpawnUsage) - { - overSize = true; + var sentDespawn = m_Snapshot.WriteDespawn(clientData, writer, in m_Snapshot.Despawns[index]); + + // limit spawn sizes, compare current pos to very first position we wrote to + if (writer.GetStream().Position - positionSpawns > m_NetworkManager.NetworkConfig.SnapshotMaxSpawnUsage) + { + overSize = true; + // revert back the position to undo the write + writer.GetStream().Position = savedPosition; + } + else + { + clientData.SentSpawns.Add(sentDespawn); + despawnWritten++; + } } clientData.NextDespawnIndex = (clientData.NextDespawnIndex + 1) % m_Snapshot.NumDespawns; } @@ -991,8 +1014,8 @@ internal void ReadSnapshot(ulong clientId, Stream snapshotStream) m_Snapshot.ReadBuffer(reader, snapshotStream); m_Snapshot.ReadIndex(reader); - m_Snapshot.ReadSpawns(reader); m_Snapshot.ReadAcks(clientId, m_ClientData[clientId], reader, GetConnectionRtt(clientId)); + m_Snapshot.ReadSpawns(reader); sentinel = reader.ReadUInt16(); if (sentinel != SentinelAfter)