mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
remove SnapshotBuffer. use list directly for easier interpolation.
This commit is contained in:
parent
4ed9c6443f
commit
063ae9553a
@ -3,6 +3,8 @@
|
|||||||
// Base class for NetworkTransform and NetworkTransformChild.
|
// Base class for NetworkTransform and NetworkTransformChild.
|
||||||
// => simple unreliable sync without any interpolation for now.
|
// => simple unreliable sync without any interpolation for now.
|
||||||
// => which means we don't need teleport detection either
|
// => which means we don't need teleport detection either
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace Mirror.Experimental
|
namespace Mirror.Experimental
|
||||||
@ -44,9 +46,26 @@ public abstract class OumuamuaBase : NetworkBehaviour
|
|||||||
public int bufferTimeMultiplier = 3;
|
public int bufferTimeMultiplier = 3;
|
||||||
public float bufferTime => sendInterval * bufferTimeMultiplier;
|
public float bufferTime => sendInterval * bufferTimeMultiplier;
|
||||||
|
|
||||||
// snapshot buffers
|
// snapshots sorted by timestamp
|
||||||
SnapshotBuffer serverBuffer = new SnapshotBuffer();
|
// in the original article, glenn fiedler drops any snapshots older than
|
||||||
SnapshotBuffer clientBuffer = new SnapshotBuffer();
|
// the last received snapshot.
|
||||||
|
// -> instead, we insert into a sorted buffer
|
||||||
|
// -> the higher the buffer information density, the better
|
||||||
|
// -> we still drop anything older than the first element in the buffer
|
||||||
|
SortedList<float, Snapshot> serverBuffer = new SortedList<float, Snapshot>();
|
||||||
|
SortedList<float, Snapshot> clientBuffer = new SortedList<float, Snapshot>();
|
||||||
|
|
||||||
|
// insert into snapshot buffer if newer than first entry
|
||||||
|
static void InsertIfNewEnough(Snapshot snapshot, SortedList<float, Snapshot> buffer)
|
||||||
|
{
|
||||||
|
// drop it if it's older than the first snapshot
|
||||||
|
if (buffer.Count > 0 &&
|
||||||
|
buffer.Values[0].timestamp > snapshot.timestamp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// otherwise sort it into the list
|
||||||
|
buffer.Add(snapshot.timestamp, snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
// local authority client sends sync message to server for broadcasting
|
// local authority client sends sync message to server for broadcasting
|
||||||
[Command(channel = Channels.Unreliable)]
|
[Command(channel = Channels.Unreliable)]
|
||||||
@ -56,7 +75,7 @@ void CmdClientToServerSync(Snapshot snapshot)
|
|||||||
if (clientAuthority)
|
if (clientAuthority)
|
||||||
{
|
{
|
||||||
// add to buffer (or drop if older than first element)
|
// add to buffer (or drop if older than first element)
|
||||||
serverBuffer.InsertIfNewEnough(snapshot);
|
InsertIfNewEnough(snapshot, serverBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +87,7 @@ void RpcServerToClientSync(Snapshot snapshot)
|
|||||||
if (!IsClientWithAuthority)
|
if (!IsClientWithAuthority)
|
||||||
{
|
{
|
||||||
// add to buffer (or drop if older than first element)
|
// add to buffer (or drop if older than first element)
|
||||||
clientBuffer.InsertIfNewEnough(snapshot);
|
InsertIfNewEnough(snapshot, clientBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +114,7 @@ void ApplySnapshot(Snapshot snapshot)
|
|||||||
// helper function to apply snapshots.
|
// helper function to apply snapshots.
|
||||||
// we use the same one on server and client.
|
// we use the same one on server and client.
|
||||||
// => called every Update() depending on authority.
|
// => called every Update() depending on authority.
|
||||||
void ApplySnapshots(ref float remoteTime, SnapshotBuffer buffer)
|
void ApplySnapshots(ref float remoteTime, SortedList<float, Snapshot> buffer)
|
||||||
{
|
{
|
||||||
Debug.Log($"{name} snapshotbuffer={buffer.Count}");
|
Debug.Log($"{name} snapshotbuffer={buffer.Count}");
|
||||||
|
|
||||||
@ -120,8 +139,9 @@ void ApplySnapshots(ref float remoteTime, SnapshotBuffer buffer)
|
|||||||
if (remoteTime == 0)
|
if (remoteTime == 0)
|
||||||
{
|
{
|
||||||
// then set it to first snapshot received (if any)
|
// then set it to first snapshot received (if any)
|
||||||
if (buffer.Peek(out Snapshot first))
|
if (buffer.Count > 0)
|
||||||
{
|
{
|
||||||
|
Snapshot first = buffer.Values[0];
|
||||||
remoteTime = first.timestamp;
|
remoteTime = first.timestamp;
|
||||||
Debug.LogWarning("remoteTime initialized to " + first.timestamp);
|
Debug.LogWarning("remoteTime initialized to " + first.timestamp);
|
||||||
}
|
}
|
||||||
@ -134,8 +154,19 @@ void ApplySnapshots(ref float remoteTime, SnapshotBuffer buffer)
|
|||||||
// (probably need to speed this up based on buffer size later)
|
// (probably need to speed this up based on buffer size later)
|
||||||
remoteTime += Time.deltaTime;
|
remoteTime += Time.deltaTime;
|
||||||
|
|
||||||
if (buffer.DequeueIfOldEnough(remoteTime, bufferTime, out Snapshot snapshot))
|
// apply first snapshot if old enough
|
||||||
ApplySnapshot(snapshot);
|
if (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
// snapshot needs to be older than currentTime - bufferTime
|
||||||
|
float threshold = remoteTime - bufferTime;
|
||||||
|
Snapshot first = buffer.Values[0];
|
||||||
|
if (first.timestamp <= threshold)
|
||||||
|
{
|
||||||
|
ApplySnapshot(first);
|
||||||
|
// remove it, now that we have applied it
|
||||||
|
buffer.RemoveAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
// the snapshot buffer's job is to hold for example 100ms worth of snapshots.
|
|
||||||
//
|
|
||||||
// from the article:
|
|
||||||
// "What we do is instead of immediately rendering snapshot data received is
|
|
||||||
// that we buffer snapshots for a short amount of time in an interpolation
|
|
||||||
// buffer. This interpolation buffer holds on to snapshots for a period of time
|
|
||||||
// such that you have not only the snapshot you want to render but also,
|
|
||||||
// statistically speaking, you are very likely to have the next snapshot as
|
|
||||||
// well."
|
|
||||||
//
|
|
||||||
// SnapshotBuffer simply wraps a sorted list with some time functions.
|
|
||||||
// it's nothing special and we could do the time math in NetworkTransform,
|
|
||||||
// but this way we shield ourselves from life's complexities and it's testable!
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Mirror.Experimental
|
|
||||||
{
|
|
||||||
public class SnapshotBuffer
|
|
||||||
{
|
|
||||||
// snapshots sorted by timestamp
|
|
||||||
// in the original article, glenn fiedler drops any snapshots older than
|
|
||||||
// the last received snapshot.
|
|
||||||
// -> instead, we insert into a sorted buffer
|
|
||||||
// -> the higher the buffer information density, the better
|
|
||||||
// -> we still drop anything older than the first element in the buffer
|
|
||||||
SortedList<float, Snapshot> list = new SortedList<float, Snapshot>();
|
|
||||||
|
|
||||||
// insert a snapshot if it's new enough.
|
|
||||||
// sorts it into the right position.
|
|
||||||
public void InsertIfNewEnough(Snapshot snapshot)
|
|
||||||
{
|
|
||||||
// drop it if it's older than the first snapshot
|
|
||||||
if (list.Count > 0 &&
|
|
||||||
list.Values[0].timestamp > snapshot.timestamp)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise sort it into the list
|
|
||||||
list.Add(snapshot.timestamp, snapshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
// dequeue the first snapshot if it's older enough.
|
|
||||||
// for example, currentTime = 100, bufferInterval = 0.3
|
|
||||||
// so any snapshot before time = 99.7
|
|
||||||
public bool DequeueIfOldEnough(float currentTime, float bufferInterval, out Snapshot snapshot)
|
|
||||||
{
|
|
||||||
if (list.Count > 0)
|
|
||||||
{
|
|
||||||
// snapshot needs to be older than currentTime - bufferTime
|
|
||||||
float thresholdTime = currentTime - bufferInterval;
|
|
||||||
|
|
||||||
// compare time of first entry (oldest snapshot)
|
|
||||||
Snapshot first = list.Values[0];
|
|
||||||
if (first.timestamp <= thresholdTime)
|
|
||||||
{
|
|
||||||
snapshot = first;
|
|
||||||
list.RemoveAt(0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
snapshot = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// peek
|
|
||||||
public bool Peek(out Snapshot snapshot)
|
|
||||||
{
|
|
||||||
if (list.Count > 0)
|
|
||||||
{
|
|
||||||
snapshot = list.Values[0];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
snapshot = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// count queue size independent of time
|
|
||||||
public int Count => list.Count;
|
|
||||||
|
|
||||||
// get all snapshots. useful for testing.
|
|
||||||
public IList<Snapshot> All() => list.Values;
|
|
||||||
|
|
||||||
public void Clear() => list.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 889afb8fc95244b0a38f4eefffa7e643
|
|
||||||
timeCreated: 1615886688
|
|
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5eb7a798032d4ae8b5bf22d14a60bcd9
|
|
||||||
timeCreated: 1615958523
|
|
@ -1,106 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Mirror.Experimental;
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace Mirror.Tests
|
|
||||||
{
|
|
||||||
public class SnapshotBufferTests
|
|
||||||
{
|
|
||||||
SnapshotBuffer buffer;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void SetUp()
|
|
||||||
{
|
|
||||||
buffer = new SnapshotBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Insert_Empty()
|
|
||||||
{
|
|
||||||
buffer.InsertIfNewEnough(new Snapshot());
|
|
||||||
Assert.That(buffer.Count, Is.EqualTo(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Insert_NotNewEnough()
|
|
||||||
{
|
|
||||||
// insert snapshot at time = 1
|
|
||||||
buffer.InsertIfNewEnough(new Snapshot{timestamp = 1});
|
|
||||||
Assert.That(buffer.Count, Is.EqualTo(1));
|
|
||||||
|
|
||||||
// insert snapshot at time = 0.5 (too old, should be dropped)
|
|
||||||
buffer.InsertIfNewEnough(new Snapshot{timestamp = 0.5f});
|
|
||||||
Assert.That(buffer.Count, Is.EqualTo(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Insert_NewEnough()
|
|
||||||
{
|
|
||||||
// insert snapshot at time = 1
|
|
||||||
buffer.InsertIfNewEnough(new Snapshot{timestamp = 1});
|
|
||||||
Assert.That(buffer.Count, Is.EqualTo(1));
|
|
||||||
|
|
||||||
// insert snapshot at time = 1.5 (newer than first = ok)
|
|
||||||
buffer.InsertIfNewEnough(new Snapshot{timestamp = 1.5f});
|
|
||||||
Assert.That(buffer.Count, Is.EqualTo(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Insert_Inbetween()
|
|
||||||
{
|
|
||||||
// insert snapshot at time = 1
|
|
||||||
Snapshot first = new Snapshot{timestamp = 1};
|
|
||||||
buffer.InsertIfNewEnough(first);
|
|
||||||
|
|
||||||
// insert snapshot at time = 2
|
|
||||||
Snapshot last = new Snapshot{timestamp = 2};
|
|
||||||
buffer.InsertIfNewEnough(last);
|
|
||||||
|
|
||||||
// insert snapshot at time = 1.5 (inbetween)
|
|
||||||
Snapshot between = new Snapshot{timestamp = 1.5f};
|
|
||||||
buffer.InsertIfNewEnough(between);
|
|
||||||
|
|
||||||
// check if sorted properly
|
|
||||||
IList<Snapshot> all = buffer.All();
|
|
||||||
Assert.That(all.Count, Is.EqualTo(3));
|
|
||||||
Assert.That(all[0], Is.EqualTo(first));
|
|
||||||
Assert.That(all[1], Is.EqualTo(between));
|
|
||||||
Assert.That(all[2], Is.EqualTo(last));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Dequeue_NotOldEnough()
|
|
||||||
{
|
|
||||||
// add snapshot at time=1
|
|
||||||
Snapshot snapshot = new Snapshot{timestamp = 1};
|
|
||||||
buffer.InsertIfNewEnough(snapshot);
|
|
||||||
|
|
||||||
// dequeue at time = 2 with buffer time = 1.5
|
|
||||||
// in other words, anything older than 0.5 should dequeue (nothing)
|
|
||||||
bool result = buffer.DequeueIfOldEnough(2, 1.5f, out Snapshot value);
|
|
||||||
Assert.That(result, Is.False);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Dequeue_OldEnough()
|
|
||||||
{
|
|
||||||
// add snapshot at time=1
|
|
||||||
Snapshot snapshot = new Snapshot{timestamp = 1};
|
|
||||||
buffer.InsertIfNewEnough(snapshot);
|
|
||||||
|
|
||||||
// dequeue at time = 2 with buffer time = 0.5
|
|
||||||
// in other words, anything older than 1.5 should dequeue
|
|
||||||
bool result = buffer.DequeueIfOldEnough(2, 0.5f, out Snapshot value);
|
|
||||||
Assert.That(result, Is.True);
|
|
||||||
Assert.That(value, Is.EqualTo(snapshot));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
buffer.InsertIfNewEnough(new Snapshot());
|
|
||||||
buffer.Clear();
|
|
||||||
Assert.That(buffer.Count, Is.EqualTo(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9df6ec4155e2423e8b95a49f4d07add4
|
|
||||||
timeCreated: 1615958523
|
|
Loading…
Reference in New Issue
Block a user