ServerCube.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. namespace Mirror.Examples.SnapshotInterpolationDemo
  4. {
  5. public class ServerCube : MonoBehaviour
  6. {
  7. [Header("Components")]
  8. public ClientCube client;
  9. [Header("Movement")]
  10. public float distance = 10;
  11. public float speed = 3;
  12. Vector3 start;
  13. [Header("Snapshot Interpolation")]
  14. [Tooltip("Send N snapshots per second. Multiples of frame rate make sense.")]
  15. public int sendRate = 30; // in Hz. easier to work with as int for EMA. easier to display '30' than '0.333333333'
  16. public float sendInterval => 1f / sendRate;
  17. float lastSendTime;
  18. [Header("Latency Simulation")]
  19. [Tooltip("Latency in seconds")]
  20. public float latency = 0.05f; // 50 ms
  21. [Tooltip("Latency jitter, randomly added to latency.")]
  22. [Range(0, 1)] public float jitter = 0.05f;
  23. [Tooltip("Packet loss in %")]
  24. [Range(0, 1)] public float loss = 0.1f;
  25. [Tooltip("Scramble % of unreliable messages, just like over the real network. Mirror unreliable is unordered.")]
  26. [Range(0, 1)] public float scramble = 0.1f;
  27. // random
  28. // UnityEngine.Random.value is [0, 1] with both upper and lower bounds inclusive
  29. // but we need the upper bound to be exclusive, so using System.Random instead.
  30. // => NextDouble() is NEVER < 0 so loss=0 never drops!
  31. // => NextDouble() is ALWAYS < 1 so loss=1 always drops!
  32. System.Random random = new System.Random();
  33. // hold on to snapshots for a little while before delivering
  34. // <deliveryTime, snapshot>
  35. List<(double, Snapshot3D)> queue = new List<(double, Snapshot3D)>();
  36. // latency simulation:
  37. // always a fixed value + some jitter.
  38. float SimulateLatency() => latency + Random.value * jitter;
  39. void Start()
  40. {
  41. start = transform.position;
  42. }
  43. void Update()
  44. {
  45. // move on XY plane
  46. float x = Mathf.PingPong(Time.time * speed, distance);
  47. transform.position = new Vector3(start.x + x, start.y, start.z);
  48. // broadcast snapshots every interval
  49. if (Time.time >= lastSendTime + sendInterval)
  50. {
  51. Send(transform.position);
  52. lastSendTime = Time.time;
  53. }
  54. Flush();
  55. }
  56. void Send(Vector3 position)
  57. {
  58. // create snapshot
  59. // Unity 2019 doesn't have Time.timeAsDouble yet
  60. Snapshot3D snap = new Snapshot3D(NetworkTime.localTime, 0, position);
  61. // simulate packet loss
  62. bool drop = random.NextDouble() < loss;
  63. if (!drop)
  64. {
  65. // simulate scramble (Random.Next is < max, so +1)
  66. bool doScramble = random.NextDouble() < scramble;
  67. int last = queue.Count;
  68. int index = doScramble ? random.Next(0, last + 1) : last;
  69. // simulate latency
  70. float simulatedLatency = SimulateLatency();
  71. // Unity 2019 doesn't have Time.timeAsDouble yet
  72. double deliveryTime = NetworkTime.localTime + simulatedLatency;
  73. queue.Insert(index, (deliveryTime, snap));
  74. }
  75. }
  76. void Flush()
  77. {
  78. // flush ready snapshots to client
  79. for (int i = 0; i < queue.Count; ++i)
  80. {
  81. (double deliveryTime, Snapshot3D snap) = queue[i];
  82. // Unity 2019 doesn't have Time.timeAsDouble yet
  83. if (NetworkTime.localTime >= deliveryTime)
  84. {
  85. client.OnMessage(snap);
  86. queue.RemoveAt(i);
  87. --i;
  88. }
  89. }
  90. }
  91. }
  92. }