AtmosDebugOverlaySystem.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. using System.Numerics;
  2. using Content.Server.Atmos.Components;
  3. using Content.Shared.Atmos;
  4. using Content.Shared.Atmos.EntitySystems;
  5. using Content.Shared.CCVar;
  6. using JetBrains.Annotations;
  7. using Robust.Server.GameObjects;
  8. using Robust.Server.Player;
  9. using Robust.Shared.Configuration;
  10. using Robust.Shared.Enums;
  11. using Robust.Shared.Map;
  12. using Robust.Shared.Map.Components;
  13. using Robust.Shared.Player;
  14. namespace Content.Server.Atmos.EntitySystems
  15. {
  16. [UsedImplicitly]
  17. public sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem
  18. {
  19. [Dependency] private readonly IPlayerManager _playerManager = default!;
  20. [Dependency] private readonly IMapManager _mapManager = default!;
  21. [Dependency] private readonly IConfigurationManager _configManager = default!;
  22. [Dependency] private readonly SharedTransformSystem _transform = default!;
  23. [Dependency] private readonly MapSystem _mapSystem = default!;
  24. /// <summary>
  25. /// Players allowed to see the atmos debug overlay.
  26. /// To modify it see <see cref="AddObserver"/> and
  27. /// <see cref="RemoveObserver"/>.
  28. /// </summary>
  29. private readonly HashSet<ICommonSession> _playerObservers = new();
  30. /// <summary>
  31. /// Overlay update ticks per second.
  32. /// </summary>
  33. private float _updateCooldown;
  34. private List<Entity<MapGridComponent>> _grids = new();
  35. public override void Initialize()
  36. {
  37. base.Initialize();
  38. _playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
  39. }
  40. public override void Shutdown()
  41. {
  42. base.Shutdown();
  43. _playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
  44. }
  45. public bool AddObserver(ICommonSession observer)
  46. {
  47. return _playerObservers.Add(observer);
  48. }
  49. public bool HasObserver(ICommonSession observer)
  50. {
  51. return _playerObservers.Contains(observer);
  52. }
  53. public bool RemoveObserver(ICommonSession observer)
  54. {
  55. if (!_playerObservers.Remove(observer))
  56. {
  57. return false;
  58. }
  59. var message = new AtmosDebugOverlayDisableMessage();
  60. RaiseNetworkEvent(message, observer.Channel);
  61. return true;
  62. }
  63. /// <summary>
  64. /// Adds the given observer if it doesn't exist, removes it otherwise.
  65. /// </summary>
  66. /// <param name="observer">The observer to toggle.</param>
  67. /// <returns>true if added, false if removed.</returns>
  68. public bool ToggleObserver(ICommonSession observer)
  69. {
  70. if (HasObserver(observer))
  71. {
  72. RemoveObserver(observer);
  73. return false;
  74. }
  75. AddObserver(observer);
  76. return true;
  77. }
  78. private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
  79. {
  80. if (e.NewStatus != SessionStatus.InGame)
  81. {
  82. RemoveObserver(e.Session);
  83. }
  84. }
  85. private AtmosDebugOverlayData? ConvertTileToData(TileAtmosphere tile)
  86. {
  87. return new AtmosDebugOverlayData(
  88. tile.GridIndices,
  89. tile.Air?.Temperature ?? default,
  90. tile.Air?.Moles,
  91. tile.PressureDirection,
  92. tile.LastPressureDirection,
  93. tile.AirtightData.BlockedDirections,
  94. tile.ExcitedGroup?.GetHashCode(),
  95. tile.Space,
  96. tile.MapAtmosphere,
  97. tile.NoGridTile,
  98. tile.Air?.Immutable ?? false);
  99. }
  100. public override void Update(float frameTime)
  101. {
  102. AccumulatedFrameTime += frameTime;
  103. _updateCooldown = 1 / _configManager.GetCVar(CCVars.NetAtmosDebugOverlayTickRate);
  104. if (AccumulatedFrameTime < _updateCooldown)
  105. {
  106. return;
  107. }
  108. // This is the timer from GasTileOverlaySystem
  109. AccumulatedFrameTime -= _updateCooldown;
  110. // Now we'll go through each player, then through each chunk in range of that player checking if the player is still in range
  111. // If they are, check if they need the new data to send (i.e. if there's an overlay for the gas).
  112. // Afterwards we reset all the chunk data for the next time we tick.
  113. foreach (var session in _playerObservers)
  114. {
  115. if (session.AttachedEntity is not {Valid: true} entity)
  116. continue;
  117. var transform = Transform(entity);
  118. var pos = _transform.GetWorldPosition(transform);
  119. var worldBounds = Box2.CenteredAround(pos,
  120. new Vector2(LocalViewRange, LocalViewRange));
  121. _grids.Clear();
  122. _mapManager.FindGridsIntersecting(transform.MapID, worldBounds, ref _grids);
  123. foreach (var grid in _grids)
  124. {
  125. var uid = grid.Owner;
  126. if (!Exists(uid))
  127. continue;
  128. if (!TryComp(uid, out GridAtmosphereComponent? gridAtmos))
  129. continue;
  130. var entityTile = _mapSystem.GetTileRef(grid, grid, transform.Coordinates).GridIndices;
  131. var baseTile = new Vector2i(entityTile.X - LocalViewRange / 2, entityTile.Y - LocalViewRange / 2);
  132. var debugOverlayContent = new AtmosDebugOverlayData?[LocalViewRange * LocalViewRange];
  133. var index = 0;
  134. for (var y = 0; y < LocalViewRange; y++)
  135. {
  136. for (var x = 0; x < LocalViewRange; x++)
  137. {
  138. var vector = new Vector2i(baseTile.X + x, baseTile.Y + y);
  139. gridAtmos.Tiles.TryGetValue(vector, out var tile);
  140. debugOverlayContent[index++] = tile == null ? null : ConvertTileToData(tile);
  141. }
  142. }
  143. var msg = new AtmosDebugOverlayMessage(GetNetEntity(grid), baseTile, debugOverlayContent);
  144. RaiseNetworkEvent(msg, session.Channel);
  145. }
  146. }
  147. }
  148. }
  149. }