Skip to content

fix: snapshot system. last fixes for release #1129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7f0ab63
feat: snapshot. MTT-1087 first part. separating the side effect (stor…
jeffreyrainy Aug 30, 2021
0ef806d
feat: snapshot. MTT-1087 second part. Writing SnapshotMessage that fi…
jeffreyrainy Aug 30, 2021
c6584ab
feat: snapshot. MTT-1087 part 3. Writing spawns after acks, because s…
jeffreyrainy Aug 30, 2021
5b1046d
feat: snapshot. MTT-1087 part 4. Using max spawn message size from Ne…
jeffreyrainy Aug 30, 2021
f12b1c2
Merge remote-tracking branch 'origin/develop' into experimental/snaps…
jeffreyrainy Aug 30, 2021
1a11320
feat: snapshot. mtt-1087. Forgotten , for performance
jeffreyrainy Aug 31, 2021
70bded6
Merge remote-tracking branch 'origin/develop' into experimental/snaps…
jeffreyrainy Aug 31, 2021
e257812
Merge branch 'develop' into experimental/snapshot-system-spawn4
jeffreyrainy Aug 31, 2021
71aba63
Merge branch 'develop' into experimental/snapshot-system-spawn4
jeffreyrainy Aug 31, 2021
d8f3588
Merge branch 'develop' into experimental/snapshot-system-spawn4
jeffreyrainy Aug 31, 2021
93508a1
feat: snapshot. MTT-1089 pessimistic resend control. spawn part
jeffreyrainy Aug 31, 2021
322e8a1
Merge remote-tracking branch 'origin/develop' into experimental/snaps…
jeffreyrainy Aug 31, 2021
ec8e4e3
feat: snapshot. MTT-1089 pessimistic resend control. despawn part
jeffreyrainy Sep 1, 2021
db29c2e
fix: snapshot. Cleaning up our clientData.SentSpawns data structure. …
jeffreyrainy Sep 2, 2021
2f77277
feat: snapshot. Last fixes for release. Prepares for MTT-1089, but le…
jeffreyrainy Sep 2, 2021
7576a29
Merge remote-tracking branch 'origin/develop' into experimental/snaps…
jeffreyrainy Sep 2, 2021
73b6dbf
feat: snapshot. Last fixes for release. PR review comments
jeffreyrainy Sep 2, 2021
811438d
style: snapshot. Coding standards
jeffreyrainy Sep 2, 2021
bd1d60b
style: snapshot. Coding standards
jeffreyrainy Sep 2, 2021
c35af65
Merge branch 'develop' into experimental/snapshot-system-spawn4
jeffreyrainy Sep 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,17 +394,15 @@ private void OnDestroy()

private SnapshotDespawnCommand GetDespawnCommand()
{
SnapshotDespawnCommand command;
var command = new SnapshotDespawnCommand();
command.NetworkObjectId = NetworkObjectId;
command.TickWritten = default; // value will be set internally by SnapshotSystem
command.TargetClientIds = default;

return command;
}

private SnapshotSpawnCommand GetSpawnCommand()
{
SnapshotSpawnCommand command;
var command = new SnapshotSpawnCommand();
command.NetworkObjectId = NetworkObjectId;
command.OwnerClientId = OwnerClientId;
command.IsPlayerObject = IsPlayerObject;
Expand All @@ -426,8 +424,6 @@ private SnapshotSpawnCommand GetSpawnCommand()
command.ObjectPosition = transform.position;
command.ObjectRotation = transform.rotation;
command.ObjectScale = transform.localScale;
command.TickWritten = default; // value will be set internally by SnapshotSystem
command.TargetClientIds = default;

return command;
}
Expand Down
73 changes: 62 additions & 11 deletions com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal struct SnapshotDespawnCommand
// snapshot internal
internal int TickWritten;
internal List<ulong> TargetClientIds;
internal int TimesWritten;
}

internal struct SnapshotSpawnCommand
Expand All @@ -57,6 +58,7 @@ internal struct SnapshotSpawnCommand
// snapshot internal
internal int TickWritten;
internal List<ulong> TargetClientIds;
internal int TimesWritten;
}

// A table of NetworkVariables that constitutes a Snapshot.
Expand Down Expand Up @@ -191,6 +193,11 @@ internal void AddSpawn(SnapshotSpawnCommand command)
command.TargetClientIds = GetClientList();
}

// todo: store, for each client, the spawn not ack'ed yet,
// to prevent sending despawns to them.
// for clientData in client list
// clientData.SpawnSet.Add(command.NetworkObjectId);

// todo:
// this 'if' might be temporary, but is needed to help in debugging
// or maybe it stays
Expand Down Expand Up @@ -302,7 +309,7 @@ internal Entry ReadEntry(NetworkReader reader)

internal SnapshotSpawnCommand ReadSpawn(NetworkReader reader)
{
SnapshotSpawnCommand command;
var command = new SnapshotSpawnCommand();

command.NetworkObjectId = reader.ReadUInt64Packed();
command.GlobalObjectIdHash = (uint)reader.ReadUInt64Packed();
Expand All @@ -315,18 +322,16 @@ internal SnapshotSpawnCommand ReadSpawn(NetworkReader reader)
command.ObjectScale = reader.ReadVector3();

command.TickWritten = reader.ReadInt32Packed();
command.TargetClientIds = default;

return command;
}

internal SnapshotDespawnCommand ReadDespawn(NetworkReader reader)
{
SnapshotDespawnCommand command;
var command = new SnapshotDespawnCommand();

command.NetworkObjectId = reader.ReadUInt64Packed();
command.TickWritten = reader.ReadInt32Packed();
command.TargetClientIds = default;

return command;
}
Expand Down Expand Up @@ -499,8 +504,11 @@ internal void ReadAcks(ulong clientId, ClientData clientData, NetworkReader read
internal void ProcessSingleAck(ushort ackSequence, ulong clientId, ClientData clientData, ConnectionRtt connection)
{
// look through the spawns sent
foreach (var sent in clientData.SentSpawns)
for (int index = 0; index < clientData.SentSpawns.Count; /*no increment*/)
{
// needless copy, but I didn't find a way around
ClientData.SentSpawn sent = clientData.SentSpawns[index];

// for those with the sequence number being ack'ed
if (sent.SequenceNumber == ackSequence)
{
Expand Down Expand Up @@ -548,6 +556,16 @@ internal void ProcessSingleAck(ushort ackSequence, ulong clientId, ClientData cl
}
}
}

// remove current `sent`, by moving last over,
// as it was acknowledged.
// skip incrementing index
clientData.SentSpawns[index] = clientData.SentSpawns[clientData.SentSpawns.Count - 1];
clientData.SentSpawns.RemoveAt(clientData.SentSpawns.Count - 1);
}
else
{
index++;
}
}

Expand Down Expand Up @@ -741,6 +759,38 @@ private void SendSnapshot(ulong clientId)
}
}

// Checks if a given SpawnCommand should be written to a Snapshot Message
// Performs exponential back off. To write a spawn a second time
// two ticks must have gone by. To write it a third time, four ticks, etc...
// This prioritize commands that have been re-sent less than others
private bool ShouldWriteSpawn(in SnapshotSpawnCommand spawnCommand)
{
if (m_CurrentTick < spawnCommand.TickWritten)
{
return false;
}

// 63 as we can't shift more than that.
var diff = Math.Min(63, m_CurrentTick - spawnCommand.TickWritten);

// -1 to make the first resend immediate
return (1 << diff) > (spawnCommand.TimesWritten - 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty cool 😃

}

private bool ShouldWriteDespawn(in SnapshotDespawnCommand despawnCommand)
{
if (m_CurrentTick < despawnCommand.TickWritten)
{
return false;
}

// 63 as we can't shift more than that.
var diff = Math.Min(63, m_CurrentTick - despawnCommand.TickWritten);

// -1 to make the first resend immediate
return (1 << diff) > (despawnCommand.TimesWritten - 1);
}

private void WriteSpawns(NetworkBuffer buffer, ulong clientId)
{
var spawnWritten = 0;
Expand Down Expand Up @@ -779,7 +829,8 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId)
var index = clientData.NextSpawnIndex;
var savedPosition = writer.GetStream().Position;

if (m_Snapshot.Spawns[index].TargetClientIds.Contains(clientId))
// todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns
if (m_Snapshot.Spawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteSpawn(m_Snapshot.Spawns[index])*/)
{
var sentSpawn = m_Snapshot.WriteSpawn(clientData, writer, in m_Snapshot.Spawns[index]);

Expand All @@ -792,6 +843,7 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId)
}
else
{
m_Snapshot.Spawns[index].TimesWritten++;
clientData.SentSpawns.Add(sentSpawn);
spawnWritten++;
}
Expand All @@ -805,15 +857,15 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId)
// 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))
// todo: re-enable ShouldWriteSpawn, once we have a mechanism to not let despawn pass in front of spawns
if (m_Snapshot.Despawns[index].TargetClientIds.Contains(clientId) /*&& ShouldWriteDespawn(m_Snapshot.Despawns[index])*/)
{
var sentDespawn = m_Snapshot.WriteDespawn(clientData, writer, in m_Snapshot.Despawns[index]);

Expand All @@ -826,6 +878,7 @@ private void WriteSpawns(NetworkBuffer buffer, ulong clientId)
}
else
{
m_Snapshot.Despawns[index].TimesWritten++;
clientData.SentSpawns.Add(sentDespawn);
despawnWritten++;
}
Expand Down Expand Up @@ -955,16 +1008,14 @@ internal void ReadSnapshot(ulong clientId, Stream snapshotStream)
clientId = m_NetworkManager.ServerClientId;
}

int snapshotTick = default;

using var reader = PooledNetworkReader.Get(snapshotStream);
// make sure we have a ClientData entry for each client
if (!m_ClientData.ContainsKey(clientId))
{
m_ClientData.Add(clientId, new ClientData());
}

snapshotTick = reader.ReadInt32Packed();
var snapshotTick = reader.ReadInt32Packed();
var sequence = reader.ReadUInt16();

if (sequence >= m_ClientData[clientId].LastReceivedSequence)
Expand Down