LagCompensationSystem.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. using Content.Server.Movement.Components;
  2. using Robust.Server.Player;
  3. using Robust.Shared.Map;
  4. using Robust.Shared.Player;
  5. using Robust.Shared.Timing;
  6. namespace Content.Server.Movement.Systems;
  7. /// <summary>
  8. /// Stores a buffer of previous positions of the relevant entity.
  9. /// Can be used to check the entity's position at a recent point in time.
  10. /// </summary>
  11. public sealed class LagCompensationSystem : EntitySystem
  12. {
  13. [Dependency] private readonly IGameTiming _timing = default!;
  14. // I figured 500 ping is max, so 1.5 is 750.
  15. // Max ping I've had is 350ms from aus to spain.
  16. public static readonly TimeSpan BufferTime = TimeSpan.FromMilliseconds(750);
  17. public override void Initialize()
  18. {
  19. base.Initialize();
  20. Log.Level = LogLevel.Info;
  21. SubscribeLocalEvent<LagCompensationComponent, MoveEvent>(OnLagMove);
  22. }
  23. public override void Update(float frameTime)
  24. {
  25. base.Update(frameTime);
  26. var curTime = _timing.CurTime;
  27. var earliestTime = curTime - BufferTime;
  28. // Cull any old ones from active updates
  29. // Probably fine to include ignored.
  30. var query = AllEntityQuery<LagCompensationComponent>();
  31. while (query.MoveNext(out var comp))
  32. {
  33. while (comp.Positions.TryPeek(out var pos))
  34. {
  35. if (pos.Item1 < earliestTime)
  36. {
  37. comp.Positions.Dequeue();
  38. continue;
  39. }
  40. break;
  41. }
  42. }
  43. }
  44. private void OnLagMove(EntityUid uid, LagCompensationComponent component, ref MoveEvent args)
  45. {
  46. if (!args.NewPosition.EntityId.IsValid())
  47. return; // probably being sent to nullspace for deletion.
  48. component.Positions.Enqueue((_timing.CurTime, args.NewPosition, args.NewRotation));
  49. }
  50. public (EntityCoordinates Coordinates, Angle Angle) GetCoordinatesAngle(EntityUid uid, ICommonSession? pSession,
  51. TransformComponent? xform = null)
  52. {
  53. if (!Resolve(uid, ref xform))
  54. return (EntityCoordinates.Invalid, Angle.Zero);
  55. if (pSession == null || !TryComp<LagCompensationComponent>(uid, out var lag) || lag.Positions.Count == 0)
  56. return (xform.Coordinates, xform.LocalRotation);
  57. var angle = Angle.Zero;
  58. var coordinates = EntityCoordinates.Invalid;
  59. var ping = pSession.Channel.Ping;
  60. // Use 1.5 due to the trip buffer.
  61. var sentTime = _timing.CurTime - TimeSpan.FromMilliseconds(ping * 1.5);
  62. foreach (var pos in lag.Positions)
  63. {
  64. coordinates = pos.Item2;
  65. angle = pos.Item3;
  66. if (pos.Item1 >= sentTime)
  67. break;
  68. }
  69. if (coordinates == default)
  70. {
  71. Log.Debug($"No long comp coords found, using {xform.Coordinates}");
  72. coordinates = xform.Coordinates;
  73. angle = xform.LocalRotation;
  74. }
  75. else
  76. {
  77. Log.Debug($"Actual coords is {xform.Coordinates} and got {coordinates}");
  78. }
  79. return (coordinates, angle);
  80. }
  81. public Angle GetAngle(EntityUid uid, ICommonSession? session, TransformComponent? xform = null)
  82. {
  83. var (_, angle) = GetCoordinatesAngle(uid, session, xform);
  84. return angle;
  85. }
  86. public EntityCoordinates GetCoordinates(EntityUid uid, ICommonSession? session, TransformComponent? xform = null)
  87. {
  88. var (coordinates, _) = GetCoordinatesAngle(uid, session, xform);
  89. return coordinates;
  90. }
  91. }