ServerCube.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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. #if !UNITY_2020_3_OR_NEWER
  60. Snapshot3D snap = new Snapshot3D(NetworkTime.localTime, 0, position);
  61. #else
  62. Snapshot3D snap = new Snapshot3D(Time.timeAsDouble, 0, position);
  63. #endif
  64. // simulate packet loss
  65. bool drop = random.NextDouble() < loss;
  66. if (!drop)
  67. {
  68. // simulate scramble (Random.Next is < max, so +1)
  69. bool doScramble = random.NextDouble() < scramble;
  70. int last = queue.Count;
  71. int index = doScramble ? random.Next(0, last + 1) : last;
  72. // simulate latency
  73. float simulatedLatency = SimulateLatency();
  74. #if !UNITY_2020_3_OR_NEWER
  75. double deliveryTime = NetworkTime.localTime + simulatedLatency;
  76. #else
  77. double deliveryTime = Time.timeAsDouble + simulatedLatency;
  78. #endif
  79. queue.Insert(index, (deliveryTime, snap));
  80. }
  81. }
  82. void Flush()
  83. {
  84. // flush ready snapshots to client
  85. for (int i = 0; i < queue.Count; ++i)
  86. {
  87. (double deliveryTime, Snapshot3D snap) = queue[i];
  88. #if !UNITY_2020_3_OR_NEWER
  89. if (NetworkTime.localTime >= deliveryTime)
  90. #else
  91. if (Time.timeAsDouble >= deliveryTime)
  92. #endif
  93. {
  94. client.OnMessage(snap);
  95. queue.RemoveAt(i);
  96. --i;
  97. }
  98. }
  99. }
  100. }
  101. }