Skip to content

Commit 152178b

Browse files
jeffreyrainy0xFA11
andauthored
feat: snapshot. MTT-1088 Snapshot acknowledgment gaps (#1083)
* feat: snapshot. Attempt at applying snapshot spawn branch over new message queue * feat: snapshot. Reusing spawn entries. * feat: snapshot fix, prefab handling * feat: snapshot. reduce log spam * snapshot: merge preparation. Removing old acks, removing unused variables * feat: snapshot. merge preparation. Removing old acks, removing unused variables * feat: snapshot. more reasonable sentinel checks around snapshot send/receive * feat: snapshot. small clenaup * feat: snapshot. Moving configuration items to NetworkConfig * feat: snapshot. Adjusting to code standard * refactor: moved code from SpawnInternal into separate SendToSnapshot function. This will allow reusing it from NetworkShow. But as-is, no impact * refactor: calling networkShow(NetworkObject) code in networkshow(List<NetworkObject>) instead of calling a deeper-nested function. Make the code more uniform and prepares for incoming snapshot changes * style: whitespace * refactor: also using NetworkHide(NetworkObject) to implement NetworkHide(List<NetworkObject>). Same reasons as for NetworkShow * feat: snapshot. Safer access to Connection RTT structures * feat: snapshot. placeholder comment for code to come later * feat: snapshot. Snapshot fully working for spawn test. Keeps reference to proper network manager. Allows sending spawn to individual instances * feat: snapshot. despawn going via snapshot. snapshot message size limitation. snapshot message packing in a LRU fashion * feat: snapshot. Incrementing the local span and despawn store (amortized linear) when game code demands too much spawn/despawns * feat: snapshot. Using different list to keep track of spawn despawn times. Probably not needed, might be revert in the future, but eases debugging. Also debug prints, obviously temporary * feat: snapshot. extra logging for debugging * fix: snapshot. MTT-1056 despawn loss * feat: snapshot. Proper test for length used in snapshot message. Removed redundant condition for target client list * fix: snapshot. NetworkHide in snapshot mode was incorrectly hiding the object on all machines * feat: snapshot. Keeping snapshot disabled for this release * style: coding standards fix * test: making the networkshow/hide test more resistant to timing difference on the test machine, waiting half a second instead of a tenth. * test: removing Sleep() code in NetworkShowHide that attempted to deal with timing. Used explicit timeout instead. Less flaky * feat: snapshot, computing the mask of past received messages. Not used yet. * feat: snapshot. First pass at PR code review * feat: snapshot. Adjusting to int ticks * feat: snapshot. Reducing the amount of logging, as this is getting merged to develop * feat: snapshot. MTT-1088. Acknowledgement for multiple messages, not just the latest * feat: snapshot. Enabling snapshot in this branch, by default * feat: snapshot. Fixing a bad merge from develop * feat: snapshot. prep for PR, MTT-1088, plus packet loss for testing * feat: snapshot. prep 2 for PR, MTT-1088 * feat: snapshot. prep 3 for PR, MTT-1088. disbaling snapshot, and whitespace fix Co-authored-by: M. Fatih MAR <mfatihmar@gmail.com>
1 parent fd44d53 commit 152178b

File tree

1 file changed

+51
-14
lines changed

1 file changed

+51
-14
lines changed

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

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -472,10 +472,30 @@ internal void ReadSpawns(NetworkReader reader)
472472
}
473473
}
474474

475-
internal ushort ReadAcks(ulong clientId, ClientData clientData, NetworkReader reader)
475+
internal void ReadAcks(ulong clientId, ClientData clientData, NetworkReader reader, ConnectionRtt connection)
476476
{
477477
ushort ackSequence = reader.ReadUInt16();
478+
ushort seqMask = reader.ReadUInt16();
478479

480+
// process the latest acknowledgment
481+
ProcessSingleAck(ackSequence, clientId, clientData, connection);
482+
483+
// for each bit in the mask, acknowledge one message before
484+
while (seqMask != 0)
485+
{
486+
ackSequence--;
487+
// extract least bit
488+
if (seqMask % 2 == 1)
489+
{
490+
ProcessSingleAck(ackSequence, clientId, clientData, connection);
491+
}
492+
// move to next bit
493+
seqMask >>= 1;
494+
}
495+
}
496+
497+
internal void ProcessSingleAck(ushort ackSequence, ulong clientId, ClientData clientData, ConnectionRtt connection)
498+
{
479499
// look through the spawns sent
480500
foreach (var sent in clientData.SentSpawns)
481501
{
@@ -529,7 +549,8 @@ internal ushort ReadAcks(ulong clientId, ClientData clientData, NetworkReader re
529549
}
530550
}
531551

532-
return ackSequence;
552+
// keep track of RTTs, using the sequence number acknowledgement as a marker
553+
connection.NotifyAck(ackSequence, Time.unscaledTime);
533554
}
534555

535556
/// <summary>
@@ -564,6 +585,7 @@ internal struct SentSpawn // this struct also stores Despawns, not just Spawns
564585

565586
internal ushort SequenceNumber = 0; // the next sequence number to use for this client
566587
internal ushort LastReceivedSequence = 0; // the last sequence number received by this client
588+
internal ushort ReceivedSequenceMask = 0; // bitmask of the messages before the last one that we received.
567589

568590
internal int NextSpawnIndex = 0; // index of the last spawn sent. Used to cycle through spawns (LRU scheme)
569591
internal int NextDespawnIndex = 0; // same as above, but for despawns.
@@ -686,8 +708,6 @@ private void SendSnapshot(ulong clientId)
686708

687709
m_ConnectionRtts[clientId].NotifySend(m_ClientData[clientId].SequenceNumber, Time.unscaledTime);
688710

689-
// Send the entry index and the buffer where the variables are serialized
690-
691711
var context = m_NetworkManager.MessageQueueContainer.EnterInternalCommandContext(
692712
MessageQueueContainer.MessageType.SnapshotData, NetworkChannel.SnapshotExchange,
693713
new[] { clientId }, NetworkUpdateLoop.UpdateStage);
@@ -698,20 +718,27 @@ private void SendSnapshot(ulong clientId)
698718
{
699719
var sequence = m_ClientData[clientId].SequenceNumber;
700720

721+
// write the tick and sequence header
701722
nonNullContext.NetworkWriter.WriteInt32Packed(m_CurrentTick);
702723
nonNullContext.NetworkWriter.WriteUInt16(sequence);
703724

704725
var buffer = (NetworkBuffer)nonNullContext.NetworkWriter.GetStream();
705726

706727
using (var writer = PooledNetworkWriter.Get(buffer))
707728
{
729+
// write the snapshot: buffer, index, spawns, despawns
708730
writer.WriteUInt16(SentinelBefore);
709731
WriteBuffer(buffer);
710732
WriteIndex(buffer);
711733
WriteSpawns(buffer, clientId);
712734
WriteAcks(buffer, clientId);
713735
writer.WriteUInt16(SentinelAfter);
714736

737+
m_ClientData[clientId].LastReceivedSequence = 0;
738+
739+
// todo: this is incorrect (well, sub-optimal)
740+
// we should still continue ack'ing past messages, in case this one is dropped
741+
m_ClientData[clientId].ReceivedSequenceMask = 0;
715742
m_ClientData[clientId].SequenceNumber++;
716743
}
717744
}
@@ -806,7 +833,9 @@ private void WriteAcks(NetworkBuffer buffer, ulong clientId)
806833
{
807834
using (var writer = PooledNetworkWriter.Get(buffer))
808835
{
836+
// todo: revisit whether 16-bit is enough for LastReceivedSequence
809837
writer.WriteUInt16(m_ClientData[clientId].LastReceivedSequence);
838+
writer.WriteUInt16(m_ClientData[clientId].ReceivedSequenceMask);
810839
}
811840
}
812841

@@ -911,12 +940,6 @@ private void WriteVariableToSnapshot(Snapshot snapshot, INetworkVariable network
911940
/// <param name="snapshotStream">The stream to read from</param>
912941
internal void ReadSnapshot(ulong clientId, Stream snapshotStream)
913942
{
914-
// poor man packet loss simulation
915-
//if (Random.Range(0, 10) > 5)
916-
//{
917-
// return;
918-
//}
919-
920943
// todo: temporary hack around bug
921944
if (!m_NetworkManager.IsServer)
922945
{
@@ -937,6 +960,23 @@ internal void ReadSnapshot(ulong clientId, Stream snapshotStream)
937960
var sequence = reader.ReadUInt16();
938961

939962
// todo: check we didn't miss any and deal with gaps
963+
964+
if (m_ClientData[clientId].ReceivedSequenceMask != 0)
965+
{
966+
// since each bit in ReceivedSequenceMask is relative to the last received sequence
967+
// we need to shift all the bits by the difference in sequence
968+
m_ClientData[clientId].ReceivedSequenceMask <<=
969+
(sequence - m_ClientData[clientId].LastReceivedSequence);
970+
}
971+
972+
if (m_ClientData[clientId].LastReceivedSequence != 0)
973+
{
974+
// because the bit we're adding for the previous ReceivedSequenceMask
975+
// was implicit, it needs to be shift by one less
976+
m_ClientData[clientId].ReceivedSequenceMask +=
977+
(ushort)(1 << (ushort)((sequence - 1) - m_ClientData[clientId].LastReceivedSequence));
978+
}
979+
940980
m_ClientData[clientId].LastReceivedSequence = sequence;
941981

942982
var sentinel = reader.ReadUInt16();
@@ -948,10 +988,7 @@ internal void ReadSnapshot(ulong clientId, Stream snapshotStream)
948988
m_Snapshot.ReadBuffer(reader, snapshotStream);
949989
m_Snapshot.ReadIndex(reader);
950990
m_Snapshot.ReadSpawns(reader);
951-
var ackSequence = m_Snapshot.ReadAcks(clientId, m_ClientData[clientId], reader);
952-
953-
// keep track of RTTs, using the sequence number acknowledgement as a marker
954-
GetConnectionRtt(clientId).NotifyAck(ackSequence, Time.unscaledTime);
991+
m_Snapshot.ReadAcks(clientId, m_ClientData[clientId], reader, GetConnectionRtt(clientId));
955992

956993
sentinel = reader.ReadUInt16();
957994
if (sentinel != SentinelAfter)

0 commit comments

Comments
 (0)